신경망 학습은 저번 시간에 학습했던 신경망에서, 어떤식으로 학습을 하는지에 대한 이론이다.

 

학습

  • 훈련 데이터로부터 가중치의 최적값을 자동으로 획득하는 것

 

학습의 목표

  • 손실 함수의 결과값을 가장 작게 만드는 가중치를 찾는 것
  • 손실 함수 : 신경명이 학습할 수 있도록 해주는 지표

 

신경망 학습에서는 손실 함수 라는 것을 사용한다.

일반적인 패턴을 찾고 손실 함수를 사용해 오차 범위를 줄여 정답을 찾아가는 과정이라고 보겠다.

왜 손실 함수를 사용하는거? 하면, 가중치의 값을 조금만 변화 시켜도 손실 함수의 값이 크게 변하기 때문에, 변화를 쉽게 감지할 수 있고, 가중치를 어떻게 조절해야할지 알 수 있다.

 

손실 함수

  • 회귀 문제 : MSE (Mean squared error)
  • 분류 문제 : Cross-Entropy

 

학습을 위해선 미니배치 학습이라고 하는 방식을 사용하는데, 훈련 데이터 일부를 골라 전체의 근사치로 사용한다.

 

train_size = x_train.shape[0]

batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)

x_batch = x_train[batch_mask]
y_batch = y_train[batch_mask]

 

코드 상에선 이렇게 batch_size 를 두어, 훈련 데이터 셋에서 무작위 10개를 골라내도록 구현한다.

 

손실 함수의 기울기를 통해 학습이 되는지 확인을 할 수 있다고 하는데, 그러기 위해선 수치 미분에 대한 개념을 알아야한다.

 

미분

  • 변수 x 값의 변화량에 관한 함숫값 f(x)의 변화량 비
    • x의 '작은 변화'가 함수 f(x)를 얼마나 변화시키는가

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

def num_diff(f, x):
    h = 1e-4 # 너무 작은 값을 사용하면 컴퓨터에서 계산하지 못한다고 한다. : 1e-50
    return (f(x + h) - f(x)) / h
    
def function_1(x):
    return 0.01 * x ** 2 + 0.1 * x

x = np.arange(0.0, 20.0, 0.1)
y = function_1(x)
plt.xlabel('x')
plt.ylabel('f(x)')
plt.plot(x, y)
plt.show()
    
num_diff( function_1, 5 ) # x=5일 때의 미분 값

num_diff( function_1, 10 ) # x=10일 때의 미분 값

 

y = 0.01x^2 + 0.1x 의 해석적 미분은 df(x)/dx = 0.02x + 0.1 이므로

  • 0.02 * 5 + 0.1 = 0.2
  • 0.02 * 10 + 0.1 = 0.3
  • 오차가 매우 낮다.

 

편미분

  • 파라미터가 2개인 함수
  • 변수가 여럿인 함수에 대한 미분
  • 목표 변수 하나에 초점을 맞추고 다른 변수는 값을 고정하는 방식으로 구현한다.

 

def function_temp1(x0):
    return x0 * x0 + 4.0 ** 2
    
num_diff( function_temp1, 3.0 )

# 결과 : 6.000099999994291

def function_temp2(x1):
    return 3.0 ** 2 + x1 * x1
num_diff( function_temp2, 4.0 )

# 결과 : 8.00009999998963

 

기울기

  • 기울기는 모든 변수의 편미분으로 이루어진 벡터

 

def num_grad(f, x):
    h = 1e-4
    grad = np.zeros_like(x)  # x와 형상이 같고 원소가 모두 0인 배열 생성
    
    for idx in range(x.size):

        # 기존 x 값을 보관
        temp = x[idx]

        # f(x+h) 계산
        x[idx] = temp + h
        fxh1 = f(x)

        # f(x-h) 계산
        x[idx] = temp - h
        fxh2 = f(x)
        
        # 그리고 기울기 계산
        grad[idx] = (fxh1 - fxh2) / (2*h) # 중앙차분 방식을 사용한 계산

        # 다시 x를 원래대로
        x[idx] = temp
        
    return grad
    
    def function_2(x):
    	return np.sum(x ** 2)
    
    print( num_grad( function_2, np.array([3.0, 4.0]) ) )
    # 6 8
    
    print( num_grad( function_2, np.array([0.0, 2.0]) ) )
    # 0 4
    
    print( num_grad( function_2, np.array([3.0, 0.0]) ) )
    # 6 0

 

기울기 하강 (Gradient Descent) - 손실함수가 내려가는 방향으로 이동

  • 신경망은 학습 단계에서 최적의 가중치를 찾아야 함
    • 최적의 가중치 : 손실 함수가 minimum이 되는 가중치
    • 가중치의 공간이 넓기 때문에 어디가 minimum 인지 알기 어려움
    • Gradient decent : 기울기를 활용해서 함수의 minimum 을 찾는 것
    • 기울기가 가리키는 곳에 항사 minumum 이 있다는 보장은 없음
  • Global minimum : 가장 작은 값
  • Local minimum : 국조석인 최솟값
  • Saddle point : 어느 방향에서 maximum, 다른 방향에서는 minimum 이 되는 점
  • Plateau : 평평한 곳, 학습이 진행되지 않음

 

gradient descent 수식

 

η (eta) 는 갱신하는 양 : 학습률

  • 한번의 학습으로 얼마나 갱신할 지 지정
  • 0.01 이나 0.001 등 특정 값을 정하는데 너무 크거나 작으면 학습이 잘 안될 수 있음

 

def gd( f, init_x, lr=0.01, step_num=100 ):
	
    # f : 최적화 하려는 함수
    # init_x : 초기 값
    # lr : 학습률
    # step_num : 반복 횟수
    
    x = init_x
    for i in range(step_num):
        grad = num_grad(f, x)
        x -= lr * grad
        plt.scatter(x[0], x[1])
    return x
    
def function_2(x):
    return x[0]**2 + x[1]**2

init_x = np.array([-3.0, 4.0])

plt.xlim(-5, 5)
plt.ylim(-5, 5)

result = gd( function_2, init_x=init_x, lr=0.1, step_num=100 )

print(result)

plt.show()

# [ -6.11110793e-10   8.14814391e-10]

 

학습률이 너무 큰 경우엔 발산하고, 너무 작으면 갱신이 되지 않는다.

 

 

Hyperparameter 는 사용자가 직접 수동으로 설정해주는 값이며, 여러 후보 값들을 시험하며 가장 잘 학습하는 값을 찾는 과정을 Hyperparameter Tuning 이라고 한다.

 

신경망에서의 기울기는 결국 가중치에 대한 손실함수기울기를 뜻한다.

 

그래서 신경망 학습의 절차를 정리해보면

 

1 단계 - 미니배치

2 단계 - 기울기 산출

3 단계 - 매개변수 갱신

4 단계 - 1~3 단계 반복

 

이런식으로 이루어진다.

이 방식을 SGD (Stochastic Gradient Descent) 라고 하며,

대부분의 딥러닝 프레임워크에서는 SGD 라는 함수를 제공한다고 한다.

 

생각보다 공부해야 될 것이 너무 많았다..

코드 중심은 정말로 다음 시간에..