deep-learning (3)

MinGeo·2022년 8월 15일
0

deep-learning

목록 보기
3/6

오늘의 주제

오늘은 밑바닥부터 시작하는 딥러닝에서의 chapter 4, 신경망 학습에 대해서 다뤄볼 것이다.

학습 이란?

훈련 데이터로부터 가중치 매개변수의 최적값을 자동으로 획득하는 것을 의미한다. 이번 chapter에서는 신경망이 학습할 수 있도록 해주는 지표인 손실 함수를 사용한다. 이 손실 함수의 결괏값을 가장 작게 만드는 가중치 매개변수를 찾는 것이 학습의 목표이며, 이 결괏값을 가급적 작게 만드는 기법으로 경사법에 대해서 배울 것이다.


출처 : 밑바닥부터 시작하는 딥러닝

위 그림에서 처럼 신경망은 이미지를 있는 그대로 학습한다. 이미지에 포함된 중요한 특징까지도 기계가 스스로 학습한다.

그래서 딥러닝을 데이터에서 목표한 결과를 사람의 개입 없이 얻는다는 뜻을 담은 종단간 기계학습이라고 하기도 한다.

훈련 데이터와 시험 데이터

기계학습 문제에서는 데이터를 훈련 데이터시험 데이터로 나눠 학습과 실험을 수행한다.

먼저 훈련 데이터만 사용하여 학습하면서 최적의 매개변수를 찾고, 시험 데이터를 사용하여 훈련한 모델의 실력을 평가한다. 이렇게 하는 이유는 범용 능력을 제대로 평가하기 위해서 이다.

범용 능력이란 아직 보지 못한 데이터로도 문제를 올바르게 풀어내는 능력이다. 우리가 가진 훈련 데이터만 잘 판별하는 모델이 있을 때, 이를 가진 훈련 데이터로만 평가하면 올바르게 모델의 능력을 평가할 수 없다. 그러므로 데이터셋 하나로만 평가하지 않는다.

오버피팅 : 한 데이터셋에만 지나치게 최적화된 상태

손실 함수

신경망 학습에서 신경망은 하나의 지표를 기준으로 최적의 매개변수 값을 탐색하는데, 이 지표를 손실 함수 라고 한다. 손실 함수는 신경망 성능이 얼마나 나쁜지를 나타내는 지표로, 현재 신경망이 훈련 데이터를 얼마나 잘 처리하지 못하느냐를 나타낸다. 임의의 함수를 사용할 수도 있지만, 일반적으로 오차제곱합교차 엔트로피 오차를 사용한다.

오차제곱합

출처 : 밑바닥부터 시작하는 딥러닝

yk는 신경망의 출력, tk는 정답 레이블, k는 데이터의 차원 수를 나타낸다.
여기서 지난 시간에 신경망에 대해 공부한 것을 떠올려보면, 신경망의 출력 y는 소프트맥스 함수의 출력 값이다. 그러므로 확률로 해석할 수 있다.

>>> y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
>>> t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

위 배열들의 원소는 첫 번째 원소부터 순서대로 0, 1, 2,... 일 떄의 값이다. 정답 레이블인 t는 2 index에만 1이 들어가 있고, 정답을 가르키고 있다. 위 y 배열을 보면 index 2에 있는 0.6이 가장 큰 값임을알 수 있다. 그러므로 정답이 일치하는 것을 알 수 있다.

위 정답 레이블 t 처럼 한 원소만 1로 하고 그 외는 0으로 나타내는 표기법을 원-핫 인코딩이라고 한다.

오차제곱합을 파이썬으로 나타낸 코드이다.

def sum_squares_error(y, t):
	return 0.5 * np.sum((y-t)**2)

이 오차제곱합을 실제로 코드를 작성해서 알아보자

import numpy as np
import matplotlib.pyplot as plt

def sum_squares_error(y, t):
    return 0.5*np.sum((y-t)**2)

t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
print(sum_squares_error(np.array(y), np.array(t)))
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
print(sum_squares_error(np.array(y), np.array(t)))

출력 :
0.09750000000000003
0.5975

첫번째는 index 2일 확률이 가장 높다고 추정했고, 두번째는 index 7일 확률이 가장 높다고 추정했다. 각 결과를 보면 정답과 같은 첫번째 출력이 정답과 다른 두번째 출력이 값이 더 크다. 손실함수 값이 작을 수록 오차가 더 작은 것이므로 첫 번째 추정 결과가 정답에 더 가깝다.

교차 엔트로피 오차

출처 : 밑바닥부터 시작하는 딥러닝

여기서 log는 밑이 e인 자연로그 이며, yk는 신경망 출력, tk는 정답 레이블이다. 여기서도 정답 레이블은 원-핫 인코딩을 사용한다. 그렇기 때문에 사실상 정답일 때의 자연로그만 값이 있게 된다.(tk가 0이면 그냥 0이 나오기 때문)

그래프를 보면 x가 1일 때 y는 0이 되고 x가 0에 가까워질수록 y의 값은 점점 작아진다. 그러므로 정답에 해당하는 출력이 커질수록 0에 가까워지고, 출력이 1일 때 0이 된다. 이 점을 보아 정답일 때의 출력이 작아질 수록 오차는 커진다.

교차 엔트로피 오차를 python코드로 알아보자.

def cross_entropy_error(y, t):
	delta = 1e-7
    return -np.sum(t * np.log(y + delta))

여기서 보면 수식에는 존재하지 않는 delta라는 매우 작은 값이 더해지는 것을 볼 수 있다. 왜냐하면 np.log() 함수에 0을 입력하면 마이너스 무한대를 뜻하는 -inf가 되어 더이상 계산을 진행할 수 없기 떄문이다. 그러므로 작은 값을 더해 마이너스 무한대가 발생하지 않도록 막은 것이다.

교차 엔트로피 오차로 예시를 한번 풀어보자.

import numpy as np
import matplotlib.pyplot as plt

def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))

t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
print(cross_entropy_error(np.array(y), np.array(t)))
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
print(cross_entropy_error(np.array(y), np.array(t)))

출력 :
0.510825457099338
2.302584092994546

첫 번째 예는 정답일 때의 출력이 0.6인 경우이고 두 번째 예는 정답일 때의 출력이 0.1인 경우이다. 두 경우의 오차를 보면 첫 번째 값이 적기 때문에 첫 번째 추정이 더 정답일 가능성이 높다고 판단한 것이다.

미니배치 학습

기계학습 문제는 훈련데이터에 대한 손실 함수의 값을 구하고, 그 값을 최대한 줄여주는 매개변수를 찾아낸다. 이렇게 하려면 모든 훈련 데이터를 대상으로 손실 함수 값을 구해야한다. 위에서 했던 건 훈련 데이터 하나에 대한 값을 구한 것이었고, 훈련 데이터 모두에 대한 손실 함수의 합을 구해보자.

ex) 교차 엔트로피 오차

여기서 tnk는 n번째 데이터의 k번째 값을 의미한다. ynk는 신경망의 출력, tnk는 정답 레이블이다.

앞에서 봤던 손실 함수를 N개의 데이터로 확장한 형태이다. 마지막으로 n으로 나누어서 정규화 함으로써 평균 손실 함수를 구하게 된다.

그런데 데이터가 몇개 안되는 경우에는 괜찮지만, 데이터의 개수가 10000개, 20000개, 100,000개가 넘어가기 시작하면 시간이 많이 걸리는 경우가 있다. 그래서 이런 경우 데이터 일부를 추려 전체의 근사치로 이용한다.

훈련 데이터로 부터 일부만 골라 학습을 수행하는 것을 미니배치 학습 이라고 하며 그 일부를 미니배치라고 한다.

배치용 교차 엔트로피 오차 구현하기

배치 데이터를 지원하는 교차 엔트로피 오차 구현을 위해서 코드를 조금 수정해주어야 한다. 코드는 다음과 같다.

def cross_entropy_error(y, t):
	if y.ndim == 1:
    	t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        
     batch_size = y.shape[0]
     return -np.sum(t * np.log(y + 1e-7)) / batch_size

데이터 하나당 교차 엔트로피 오차를 구하는 경우에는 reshape 함수로 데이터의 형상을 바꿔준다. 그리고 배치 사이즈로 나누어 정규화를 진행해 데이터 하나당 평균의 교차 엔트로피 오차를 계산한다.

손실함수를 설정한 이유

우리의 신경망 학습의 목표는 높은 정확도가 나타나는 매개변수 값을 찾는 것이다. 하지만 왜 정확도를 지표로 하지 않고 손실 함수값을 지표로 할까?

신경망 학습에서는 최적의 매개변수를 탐색할 때 손실 함수의 값을 가능한 작게 하는 매개변수 값을 찾는다. 이때 매개변수의 미분을 계산해서 그 값을 이용해 매개변수의 값을 서서히 갱신한다. 가중치 매개변수의 손실 함수의 미분이란 가중치 매개변수 값이 변화할 때, 손실 함수가 어떻게 변하는지에 대한 의미를 가지고 있다.

정확도를 사용해서는 안되는 이유는, 정확도를 지표로 하는 매개변수의 미분 값은 대부분 장소에서 0이 되기 때문이다.

정확도는 매개변수의 미미한 변화에는 거의 반응을 보이지 않으며, 값이 불연속적으로 갑자기 변화한다. 신경망 학습에서는 이 미분이 0이 아니어야만 제대로 된 신경망 학습이 수행될 수 있다.

수치 미분

미분

미분이란 특정 순간의 변화량을 뜻한다. 수식은 다음과 같다.


출처 : 밑바닥부터 시작하는 딥러닝

위 식에서 우리는 h에 가급적 작은 값을 넣어야 한다. 하지마 너무 작은 숫자가 들어나면 반올림 오차라는 문제가 발생한다. 소수점 8자리 이하의 작은 값이 생략되어 최종 계산 결과에 오차가 생기게 된다.

또 위 식에서는 x + h와 x 사이의 함수 f의 차분을 계산하고 있지만, 이 계산에서는 오차가 있다. 이번 식에서의 미분은 x+h와 x 사이의 기울기에 해당한다. (h를 무한히 0으로 좁히는 것이 불가능하다.)

위 그림처럼 수치 미분에는 오차가 발생한다. 이 오차를 줄이기 위해서 (x+h)와 (x-h)일 때의 함수 f의 차분을 계산하는 방법을 사용하기도 한다. 이 차분은 x를 중심으로 전후의 차분을 계산한다는 의미에서 중심 차분혹은 중앙 차분이라고 한다.

위의 모든 내용을 적용해서 수치 미분을 python으로 구현해 보자.

def numerical diff(f, x):
	h = 1e-4
    return (f(x+h) - f(x-h)) / (2*h)

위에 나온 것 처럼 아주 작은 차분으로 미분하는 것을 수치 미분 이라고 한다.

수치 미분 예

간단한 함수 하나를 미분해 보자

y = 0.01x^2 + 0.1x

위 함수를 python으로 작성해보자

def function_1(x):
	return 0.01*x**2 + 0.1*x

이 함수를 x=5, x=10일 때 아까 만든 수치미분 함수로 계산해보자

import numpy as np

def numerical_diff(f, x):
    h = 1e-4
    return (f(x+h)-f(x-h)) / (2*h)
def function_1(x):
    return 0.01*x**2 + 0.1*x

print(numerical_diff(function_1, 5))
print(numerical_diff(function_1, 10))

출력 :
0.1999999999990898
0.2999999999986347

위 식을 직접 미분해 보면, x가 5일 때는 0.2, x가 10일 때는 0.3이 나온다. 위 출력과 거의 유사한 것을 알 수 있다.

편미분

변수가 여럿인 함수에 대한 미분을 편미분 이라고 한다.

위 함수 수식의 그래프는 다음과 같다.

위 식을 한번 python으로 풀어보자

import numpy as np

def numerical_diff(f, x):
    h = 1e-4
    return (f(x+h)-f(x-h)) / (2*h)
def function_tmp1(x0):
    return x0*x0 + 4.0**2.0

print(numerical_diff(function_tmp1, 3.0))

출력 :
6.00000000000378

위 처럼 편미분은 변수가 하나인 미분과 마찬가지로 특정 장소의 기울기를 구한다. 단, 여러 변수 중 목표 변수 하나에 초점을 맞추고 다른 변수는 값을 고정한다.

기울기

앞에서는 각 변수별로 따로 계산했었다. 양쪽의 편미분을 묶어서 계산해서 벡터로 정리한 것을 기울기라고 한다.

기울기를 구하는 python 함수를 그려보자

def numerical_gradient(f, x):
	h = 1e-4
    grad = np.zeros_like(x)
    for idx in range(x.size):
    	# f(x+h) 계산
    	tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x)
        # f(x-h) 계산
        x[idx] = tmp_val -h
        fxh2 = f(x)
        
        grad[idx] = (fxh1 - fxh2) / (2*h)
        x[idx] = tmp_val
     return grad

위 식을 이용해 각 점에서의 기울기를 계산할 수 있다.

아까 주어진 f(x0, x1) = x0^2 + x1^2의 기울기 그래프를 확인해 보자.

출처 : 밑바닥부터 시작되는 딥러닝

이 그래프를 보면 기울기는 함수의 가장 최솟값을 가리키는 것처럼 보이지만, 반드시 그렇다고는 할 수 없다. 정확하게 말하자면 기울기가 가리키는 쪽은 각 장소에서 함수의 출력 값을 가장 크게 줄이는 방향이다.

경사법

신경망은 최적의 매개변수를 학습 시에 찾아야 한다. 하지만 일반적인 문제의 손실 함수는 매우 복잡하여 최솟값을 알아내기가 매우 어렵다. 이런 상황에서 기울기를 이용해 함수의 최솟값을 찾는 방법이 경사법이라고 할 수 있다.

하지만 기울기가 가리키는 곳에 함수의 최솟값이 있는지, 맞는 방향인지는 보장할 수가 없다. 하지만 기울기 방향으로 가야 함수의 값을 줄일 수 있기 때문에 이 기울기를 바탕으로 최솟값으로 나아가야 한다.

여기서 경사법이 등장한다. 경사법은 현 위치에서 기울어진 방향으로 일정 거리만큼 이동한다. 다음 이동한 곳에서 기울기를 구하고, 또 기울어진 방향으로 나아가기를 반복한다. 이렇게 해서 함수의 값을 점차 줄이는 것이 경사법이다.

최솟값을 찾는 경사법 : 경사 하강법
최댓값을 찾는 경사법 : 경사 상승법

경사법을 수식으로 나타내면 다음과 같다.

출처 : 밑바닥부터 시작하는 딥러닝

여기서 n(에타) 기호는 갱신하는 양을 나타낸다. 이를 신경망 학습에서는 학습률이라고 한다. 학습률이란 매개변수 값을 얼마나 갱신하느냐를 정하는 것이다.

이 학습률 값은 미리 특정 값으로 정해두어야 하는데, 이 값의 크기가 적절해야 올바르게 학습이 가능하다. 학습률이 너무 크면 큰 값으로 발산하고, 너무 작으면 값이 더이상 갱신되지 않은 채 끝나버린다. 그러므로 신경망 학습에서는 이 학습률 값을 변경하면서 올바르게 학습하고 있는지를 확인해야 한다.

경사 하강법의 Python 코드는 다음과 같다.

def gradient_descent(f, init_x, lr=0.01, step_num=100):
	x = init_x
    for i in range(step_num):
    	grad = numerical_gradient(f, x)
        x -= lr * grad
    return x

f는 최적화 하려는 함수, init_x는 초깃값, lr은 학습률, step_num은 경사법에 따른 반복 횟수를 뜻한다. 기울기를 구하고 이 기울기에 학습률을 곱한 값을 반복하여 진행한다. 이 함수를 이용해 함수의 극솟값 혹은 최솟값을 구할 수 있다.

신경망에서의 기울기

신경망에서의 기울기는 가중치 매개변수에 대한 손실 함수의 기울기 이다.

형상이 2X3, 가중치 W, 손실 함수가 L인 신경망을 생각했을 때, 수식으로 다음과 같이 나타내보자

아래의 각 원소는 각각의 원소에 관한 편미분이다. 각 원소의 가중치를 조금 변경했을 때 손실 함수 L이 얼마나 변화하는지를 나타낸다.

마무리

오늘은 신경망 학습에서의 데이터, 손실함수, 수치 미분, 기울기에 대해서 알아보았다. 다음에는 밑바닥부터 시작하는 딥러닝의 chapter 5, 오차역전파법에 대해서 알아보도록 하자.

0개의 댓글