정지홍 2023. 1. 12. 23:27

학습규칙이란?

-신경망이 학습할때 결합 강도가 어느 정도로 변화하는지 설명한 것

-헵의 규칙과 델타 규칙이 대표적

-학습목적은 출력값과 정답(데이터의 실제값)을 일치시키는 것

 

헵의 규칙이란?

-앞쪽의 신경세포가 흥분하며 뒤따르는 신경세포의 흥분의 전달 효율이 강화된다는 이런

-장기간 흥분하지 않는다면 뒤쪽의 신경세포 전달 효율도 감소한다.

-△w=r*yi*yj

-△w:가중치의 변화량 , r:상수(yi,yj가 함께 커지면 같이 커진다) , yi:앞쪽 뉴런의 흥분정도 , yj:뒤쪽 뉴런의 흥분정도

 

델타 규칙이란?

-출력값과 정답(데이터의 실제값)의 오차가 커질수록 가중치 수정량도 증가

-입력값이 커질수록 가중치 수정량도 증가

-△w=η*(yj-t)*yi

-η:학습률이며 정답에서 멀어질수록 정답에 다가가기 위해 가중치 수정량이 커진다

-△w:가중치의 변화량

-yi:시냅스 앞쪽 뉴런의 흥분정도

-yj:시냅스 뒤쪽 뉴런의 흥분정도

 

역전파란?

-출력값과 정답의 오차를 네트워크에서 역전파시켜서 가중치와 편향을 최적화 시킨다.

-순전파로 얻은 출력값과 정답의 오차를 하나씩 역으로 올라가서 전파시키며 이때 오차를 이용하여 각 층의 가중치 및 편향값을 수정해 나간다. 이걸 반복하여 오차를 최소화시키도록 최적화 시킨다.

-역전파 5가지 요소->훈련 데이터와 테스트 데이터, 손실함수 , 경사하강법, 최적화 알고리즘, 배치 사이즈

 

원핫 인코딩이란?

-출력값중 1개만 1이고 나머지는 0인 벡터로 표현되는 수치 벡터

 

훈련 데이터와 테스트 데이터?

-훈련데이터는 신경망 학습에 사용되고 테스트 데이터는 학습결과 검증을 위해 사용된다.

-각각의 데이터를 입력값과 정답의 짝으로 구성된다. 이러한 한 쌍을 샘플이라고 부른다.

-보통 훈련데이터가 테스트 데이터보다 많다.

-테스트 데이터에서 결과가 x이면 신경망과 훈련방법에 오류가 있는 것을 의미

 

손실 함수(오차함수 or 비용함수)?

-신경망의 결과로 출력되는 값과 정답의 오차를 정의하는 함수이다.

-오차가 클수록 바람직하지 못하다.

-오차제곱합(출력값과 정답의 차이를 제곱한 후 모두 더한것)을 이용하여 출력값이 정답과 어느 정도 일치하는지 정량화가 가능하다. ----->출력값과 정답이 연속적인 수치인 경우에 잘 맞아서 주로 회귀 문제에서 사용

-교차 엔트로피 오차(출력값의 자연로그값과 정답의 곱을 모두 더한 후에 마이너스 부호를 붙힌다. x가 1일때는 0이며 x가 0에 접근할 수록  -logx는 커진다. 따라서 정답에 가까울 수록 0에 가까우며 정답에서 멀수록 1에 가깝다. 출력값과 정답의 차이가 클수록 학습속도가 빠르다.)

 

 

경사 하강법이란?

-오차를 순차적으로 역전시켜 가중치와 오차를 조금씩 수정하며 오차를 최소화 시키는 방법

-손실함수로 구한 오차값을 기점으로 신경망의 반대 방향으로 w와 bias를 수정해나간다. 이때 수정량을 경사하강법으로 정한다.

-그래프상에서 가로축은 가중치, 세로축은 오차이며 가중치,편향의 변화량은 곡선의 기울기로 결정된다.

-국소적인 최소점에 빠지는 것을 국소 최적해라고 한다. 이때 진짜 최소값을 전역 최적해라고 한다.

-순전파는 출력값을 전파시키지만 역전파는 입력 오차값의 기울기를 전파시킨다.

-회귀문제에서 손실함수는 오차제곱합, 은닉층의 활성화함수는 시그모이드 함수 , 출력층의 활성화함수는 항등함수이다.

-분류문제에서 손실함수는 교처 앤트로피 오차, 은닉층은 시그모이드함수, 출력층은 소프트맥스이다.

 

 

-최적화 알고리즘?

  • 확률적 경사 하강법(수정할때 마다 샘플을 무작위로 선택하는 알고리즘, 기울기 수정할때 마다 훈련 데이터에서 무작위로 샘플을 선택하여 국소적 최적해에 잘 빠지지 않는다.)
  • 모멘텀(확률적 경사 하강법에서 관성을 더한 것. 수정량이 급격히 변화하는 것을 막고 좀 더 부드럽게 수정됨)
  • 아다그라드(수정량이 자동으로 조정되지만 수정량이 감소하여 도중에 수정량이 0이 되면 최적화가 더 이상 진행x)
  • RMSProp(아다그라드의 단점을 개선한 방법)
  • 아담(위의 알고리즘의 장점을 모두 가진 알고리즘)

 

배치사이즈란?

-가중치와 오차의 수정을 묶어서 진행되는데 이때 이 묶음의 크기를 말하며 학습의 효율성에 영향을 준다.

-모든 훈련 데이터를 1회 학습하는 것이 1에포크 라고 한다.

-훈련데이터가 여러개로 묶인 그룹을 배치라고 한다.

-배치사이즈는 이 그룹에 포함되는 샘플의 수이다.

 

배치학습?

-배치학습의 배치 사이즈는 전체 훈련 데이터의 개수이다

-1에포크에 모든 훈련 데이터를 가지고 오차의 평균을 구한뒤에 가중치 및 편향을 수정한다.

-안정된 학습이며 속도가 빠르지만 국소 최적해에 빠지기 쉽다.

 

온라인학습?

-개별 샘플마다 가중치와 편향을 수정한다. 이로 인해서 안전성이 떨어지지만 국소 최적해에 빠지는것을 예방한다.

 

미니배치학습?

-위의 두개의 학습 방법의 중간정도이며 훈련 데이터를 작은 그룹으로 분할하고 그룹마다 가중치,편향을 수정


회귀문제에서 역전파 구현

import numpy as np
import matplotlib.pyplot as plt
#y=sin(x)함수 학습
#x값은 입력 , y값은 출력 , sin(x)는 정답
input_data=np.arange(0,np.pi*2,0.1) #입력값
correct_data=np.sin(input_data) #정답값
input_data=(input_data-np.pi)/np.pi #입력을 -0.1에서 1.0 범위로 수정
n_data=len(correct_data)#데이터 수

n_in=1#입력층의 뉴런수
n_mid=3#은닉층의 뉴런수
n_out=1#출력층의 뉴런수

wb_width=0.01#가중치와 편향설정을 위한 정규분포의 표준편차
eta=0.1 #학습률
epoch=2001#2001에포크
interval=200#경과표시 간격


class OutLayer:#출력츨
    def __init__(self,n_upper,n):#초기 설정 (self,앞층의 뉴런수, 이 층의 뉴런수)
        #위에서 얻은 뉴런수로 가중치와 편향의 초깃값 설정
        self.w=wb_width*np.random.randn(n_upper ,n) #가중치 행렬 생성
        self.b=wb_width*np.random.randn(n)        #편향 벡터 생성

    def forward(self,x):#순전파
        self.x=x
        u=np.dot(x,self.w)+self.b #입력값 x외 가중치를 곱하고 편향을 더하여.....
        self.y=u#출력값인 y를 계산(항등함수 사용)

    def backward(self,t):#역전파(self, 이전에 층에서 얻은 정답값)
        delta=self.y-t#이전에 얻은 정답을 뺀다

        self.grad_w=np.dot(self.x.T , delta)
        self.grad_b=np.sum(delta,axis=0)
        self.grad_x=np.dot(delta, self.w.T)

    def update(self,eta):
        self.w-=eta*self.grad_w
        self.b-=eta*self.grad_b

class MiddleLayer:#은닉층
    def __init__(self,n_upper,n):
        self.w=wb_width*np.random.randn(n_upper,n)
        self.b=wb_width*np.random.randn(n)
   
    def forward(self,x):
        self.x=x
        u=np.dot(x,self.w)+self.b #입력값 x외 가중치를 곱하고 편향을 더하여.....
        self.y=1/(1+np.exp(-u))#출력값인 y를 계산(시그모이드함수 사용)
   
    def backward(self,grad_y):
        delta=grad_y*(1-self.y)*self.y#시그모이드 함수 미분한다
        self.grad_w=np.dot(self.x.T , delta)
        self.grad_b=np.sum(delta,axis=0)
        self.grad_x=np.dot(delta,self.w.T)
   
    def update(self,eta):#가중치와 편향 수정
        self.w-=eta*self.grad_w
        self.b-=eta*self.grad_b

#각 층을 초기화한다.
middle_layer=MiddleLayer(n_in , n_mid)
out_layer=OutLayer(n_mid , n_out)

for i in range(epoch):#학습
    index_random=np.arange(n_data)#데이터의 수만큼의 원소 갯수를 가진 index_random을 생성한 뒤에..
    np.random.shuffle(index_random)#여기에서 순서를 섞어준다
    total_error=0#결과표시할때 사용
    plot_x=[]
    plot_y=[]

    for idx in index_random:#위에서 셔플로 섞어준 넘파이리스트에서 하나씩 꺼낸다
        x=input_data[idx:idx+1]#입력
        t=correct_data[idx:idx+1]#정답

        #순전파
        middle_layer.forward(x.reshape(1,1))
        out_layer.forward(middle_layer.y)
        #역전피
        out_layer.backward(t.reshape(1,1))
        middle_layer.backward(out_layer.grad_x)
        #가중치와 편향 수정
        middle_layer.update(eta)
        out_layer.update(eta)

        if i%interval==0:
            y=out_layer.y.reshape(-1)
            total_error+=1.0/2.0*np.sum(np.square(y-t))
            plot_x.append(x)
            plot_y.append(y)

    if i%interval==0:
        plt.plot(input_data , correct_data , linestyle="dashed")
        plt.scatter(plot_x , plot_y , marker="+")
        plt.show()

        print("epoch: "+str(i)+ "/" +str(epoch) , "Error:"+str(total_error/n_data))        
epoch: 0/2001 Error:0.2859334345101318

epoch: 200/2001 Error:0.01303710291006273
epoch: 400/2001 Error:0.005978601741496623
epoch: 600/2001 Error:0.002343675519545371
epoch: 800/2001 Error:0.0007235721078605079
epoch: 2000/2001 Error:8.914322522327089e-06


분류문제에서 역전파 구현

 

분류에서는 sin곡선이 상단영역에 위치할지 하단에 위치할지 분류시키는 학습을 한다

정답은 원핫 인코딩이다.

import numpy as np
import matplotlib.pyplot as plt

x=np.arange(-1.0 , 1.1 , 0.1)
y=np.arange(-1.0 , 1.1 , 0.1)

input_data=[]#입력 벡터
correct_data=[]#정답 벡터
for X in x:
    for Y in y:
        input_data.append([X,Y])
        if Y<np.sin(np.pi*X):#sin값이 아래에 존재시에 0,1을 넣는다(원핫 인코딩)
            correct_data.append([0,1])
        else:#위에 존재시 1,0 넣는다
            correct_data.append([1,0])
#데이터 수
n_data=len(correct_data)

input_data=np.array(input_data)
correct_data=np.array(correct_data)

n_in=2
n_mid=6
n_out=2

wb_width=0.01
eta=0.1
epoch=101#에포크는 101번
interval=10#구간별 표시 구간은 10으로

class OutLayer:#출력츨
    def __init__(self,n_upper,n):#초기 설정 (self,앞층의 뉴런수, 이 층의 뉴런수)
        #위에서 얻은 뉴런수로 가중치와 편향의 초깃값 설정
        self.w=wb_width*np.random.randn(n_upper ,n) #가중치 행렬 생성
        self.b=wb_width*np.random.randn(n)        #편향 벡터 생성

    def forward(self,x):#순전파
        self.x=x
        u=np.dot(x,self.w)+self.b #입력값 x외 가중치를 곱하고 편향을 더하여.....
        self.y=np.exp(u)/np.sum(np.exp(u),axis=1,keepdims=True)#소프트맥스함수

    def backward(self,t):#역전파(self, 이전에 층에서 얻은 정답값)
        delta=self.y-t#이전에 얻은 정답을 뺀다

        self.grad_w=np.dot(self.x.T , delta)
        self.grad_b=np.sum(delta,axis=0)
        self.grad_x=np.dot(delta, self.w.T)

    def update(self,eta):
        self.w-=eta*self.grad_w
        self.b-=eta*self.grad_b

class MiddleLayer:#은닉층
    def __init__(self,n_upper,n):
        self.w=wb_width*np.random.randn(n_upper,n)
        self.b=wb_width*np.random.randn(n)
   
    def forward(self,x):
        self.x=x
        u=np.dot(x,self.w)+self.b #입력값 x외 가중치를 곱하고 편향을 더하여.....
        self.y=1/(1+np.exp(-u))#출력값인 y를 계산(시그모이드함수 사용)
   
    def backward(self,grad_y):
        delta=grad_y*(1-self.y)*self.y#시그모이드 함수 미분한다
        self.grad_w=np.dot(self.x.T , delta)
        self.grad_b=np.sum(delta,axis=0)
        self.grad_x=np.dot(delta,self.w.T)
   
    def update(self,eta):#가중치와 편향 수정
        self.w-=eta*self.grad_w
        self.b-=eta*self.grad_b

#각 층을 초기화한다.
middle_layer=MiddleLayer(n_in , n_mid)
out_layer=OutLayer(n_mid , n_out)

sin_data=np.sin(np.pi*x)#결과 검증용
for i in range(epoch):
    index_random=np.arange(n_data)
    np.random.shuffle(index_random)
   
    total_error=0
    x_1,y_1,x_2,y_2=[],[],[],[]

    for idx in index_random:
        X=input_data[idx]
        t=correct_data[idx]

        middle_layer.forward(X.reshape(1,2))
        out_layer.forward(middle_layer.y)

        out_layer.backward(t.reshape(1,2))
        middle_layer.backward(out_layer.grad_x)

        middle_layer.update(eta)
        out_layer.update(eta)

        if i%interval==0:
            Y=out_layer.y.reshape(-1)
            total_error+= -np.sum(t*np.log(y+1e-7))

            if Y[0]>Y[1]:
                x_1.append(X[0])
                y_1.append(X[1])
            else:
                x_2.append(X[0])
                y_2.append(X[1])
        if i%interval==0:
            plt.plot(x,sin_data,linestyle="dashed")
            plt.scatter(x_1,y_1,marker="+")
            plt.scatter(x_2,y_2,marker="x")
            plt.show()    

            print("epoch: "+str(i)+ "/" +str(epoch) , "Error:"+str(total_error/n_data))        

Epoch:0/101 Error:0.7226915341273943
Epoch:10/101 Error:0.32361579765015897
Epoch:100/101 Error:0.07152428711763589