본문 바로가기
Machine learning

Deep Learning numpy를 통한 기초이론_2. 학습과정

by ahsung 2020. 1. 5.

 

기초이론_1에서 퍼셉트론, 즉 인공신경망의 기본적인 구조와 비선형 구조를 가질 수 있게하는

 

다층 퍼셉트론, 그리고 활성화 함수를 소개하였습니다.

 

사실 활성화함수는 softmax, ReLU, 전 시간에 사용했던 계단 함수 등등 많이 쓰이고 있습니다.

 

활성화함수는 목적에 따라 여러가지 형태로 선택할 수 있습니다.

 

하지만 제 블로그에서는 활성화함수와 이제 곧 배우게될 손실함수(loss function)등 

 

간단하게 원리를 설명하는 것이 목적이기에  각 함수들의 소개나 수학식은 설명하지 않겠습니다.

 

저번 시간에는 인공신경망이 어떻게 컴퓨터의 기초적인 and,or,xor 등을 구현해내는지 보았습니다.

그리고 이들을 구현하였다는 것은 이 게이트들을 섞으면 컴퓨터의 모든 처리를 구현할 수 있다는 것도 알고 있습니다.

(컴퓨터 가장 최소단위인 트렌지스터들은 and, or ,xor을 기반으로 모든 것이 만들어질 수 있다.)

 

하지만 저번 시간처럼 weight와 bias를 일일이 사람히 정한다는 것은 말이 안되는 일이기 때문에

 

컴퓨터가 스스로 정답에 가까운 weight와 bias를 찾아가는  학습과정을 알아 봅시다.

 

 

기초이론_1에서도 설명했듯이 

딥러닝은 결과와 정답의 차이에서 많은 자극을 느낀다고 하였조,

바로 결과와 정답의 차이를 구하는 것이  "손실함수 (loss function) 입니다."

 

손실함수도  평균제곱오차, 교차 엔트로피 오차 등등

 

여러가지 종류의 함수가 있습니다.

 

 

오차에 따른 매개변수를 학습하는 과정

 

저희의 목표는 인공지능 모델이 최대한 정답에 가까운 결과를 내는 것입니다.

 

그렇다는 것은 모델의 결과와 정답의 오차가 가장 작은 것이 좋다는 것이지요

 

그리고 이 오차를 나타내는 것이 손실함수.

 

그리고 손실함수가 이차방정식이라면(평균 제곱 오차) 최저점에서는 미분값이 0이 되는 지점일 것입니다.

 

이런 아이디어를 통해 미분값이 0이 되는 매개변수 값을 찾는 것이 학습과정입니다.!

 

 

1) 초기 weight,와 bias의 값을 랜덤으로 준후 손실 함수의 결과값을 측정합니다.

 

2) weight 매개변수와 bias 매개변수 별로 손실함수에 대한 편미분값을 구합니다.

 

즉 weight가 4개 있다면  1번째 weight 부터 순서대로 나머지 변수들을 상수취급하고 

자신이 현재 증가할 수록 손실함수는 어떤 방향으로 증가하는지  다시말해 미분값을 구하는 것입니다.!!

 

3) 미분값만큼 비례해서 뺌으로서 미분값이 0에 가까워 지는 방향으로 매개변수를 점점 이동시킵니다.

 

이렇게 미분값을 따라 하강하는 듯 찾아간다하여 이렇게 정답을 향해 찾아가는 방식을 "경사하강법" 이라고 합니다. 

(최대값을 찾는 경사 상승법도 있을 수 있겠조..최대에서 미분0을 갖는..)

 

 

하지만 모든 함수가 항상 미분이 0이 되는 지점이 최소또한 아니기 때문에

최대한 이런 두가지 점에 타협할 수 있는 손실 함수를 디자인하고 사용하는 것이 중요합니다.

(4차 방정식은 미분값 0인 구역이 최대 3개 존재할 수 있기 때문에 위 방법으로 구한 미분값 0이 최소라고 확정할 수 없다.)  

 

물론 평균제곱오차는 이차식이므로 일반적으로 이차원 공간에서 미분값 0일 때 최소값을 보장 받을 순 있습니다.

하지만 w와 b의 매개변수 개수가 많아지고 비선형 요소가 섞이게되면 함수의 형태는 더욱 복잡해지고 알 수 없게 되어

단순한 2차 방정식 손실함수로는 한계가 올 수 있습니다.

 

def numdiff_part(f,x):
    h = 1e-4
    grad = np.zeros_like(x)
    
    # x[0 ~ size] 하나씩 그 한개에 대한 +h증가 값을 주어서 미분값 측정,
    # 다른 값들은 증가량이 없이 들어가기 때문에 f(+h)-f(-h) 에서 0으로 사라진다. 
    for i in range(x.size):
        tmp_val = x[i]
        
        # f(x+h) 계산
        x[i] = float(tmp_val) + h
        fxh1 = f(x)
        
        # f(x-h) 계산
        x[i] = float(tmp_val) - h 
        fxh2 = f(x) 
        
        grad[i] = (fxh1 - fxh2) / (2*h)
        x[i] = tmp_val # 값 복원
        
    return grad


f는 손실함수, x는 각 매개변수들을 numpy 배열형태로 나타낸 변수입니다.


def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x #초기 값
    x_history = []

    for i in range(step_num):
        #x값 계속 기록...
        x_history.append( x.copy() )
        
          #x에서 기울기 구하고
        #그 만큼 이동해서 다시 구한다.
        #점점 x는 기울기가 0에 가까운 곳으로 이동,
        # 0.01씩 기울기만큼 100번 학습!
        # 각 변수마다 편미분 값이 벡터로 나온다...
        grad = numerical_gradient(f, x)
        print(x,grad)
        x -= lr * grad
    
    return x, np.array(x_history)

 

이런식으로 점점 미분값만큼 각 매개변수들을 변화시키며 손실합수의 미분값이 0이되는 지점으로 점점 이동합니다.

 

이때 lr은 learning rate로 학습률입니다.

이 값을 곱해주는 이유는 weight의 경우 1이하의 소수자리수 인 경우도 많은데

미분값이 큰 수일경우 상세하게 weight를 조절할 수 없게됩니다.

그렇기 때문에 0.01과 같이 작은 값을 곱해줍니다.

만약 lr이 너무 크면 아에 손실함수안에 이상한 값으로 발산할 수 있고

너무 작으면 학습 시간이 매우 오래걸리거나.. 진행이 진전되지 않을 수 있습니다.

 

 

댓글