Skip to content

Latest commit

 

History

History
194 lines (140 loc) · 11.6 KB

220205.md

File metadata and controls

194 lines (140 loc) · 11.6 KB

Day 113

Deep Learning from Scratch

4. 신경망 학습

학습이란 훈련 데이터로부터 가중치 매개변수의 최적값을 자동으로 획득하는 것을 뜻한다.

데이터에서 학습

신경망의 특징은 데이터를 보고 학습한다는 것이다. 데이터에서 학습하면 가중치 매개변수의 값을 데이터를 보고 자동으로 결정한다는 것이다.

퍼셉트론도 선형 분리 가능 문제라면 데이터로부터 자동으로 학습할 수 있으나, 비선형 분리 문제는 자동으로 학습할 수 없다.

데이터 주도 학습

기계 학습의 접근법

  • 사람이 직접 알고리즘을 설계하는 것
  • 사람이 생각한 특징을 바탕으로 이미지 데이터를 벡터로 변환하고, 변환된 벡터를 가지고 학습시키는 것

fig 4-2

신경망은 이미지를 있는 그대로 학습하여 사람이 개입하지 않는다.

훈련 데이터와 시험 데이터

기계학습 문제는 데이터를 훈련 데이터(training data)와 시험 데이터(test data)로 나눠 우선 훈련 데이터만 사용하여 학습하면서 최적의 매개변수를 찾은 다음, 시험 데이터를 사용하여 앞서 훈련한 모델을 평가한다.

훈련 데이터와 시험 데이터를 나누는 이유는 범용적으로 사용할 수 있는 모델을 원하기 때문이다. 이 범용 능력을 제대로 평가하기 위해 데이터를 분리하는 것이다.
범용 능력이란 아직 보지 못한 데이터로도 문제를 올바르게 풀어내는 능력을 말한다.

데이터셋 하나로만 매개변수의 학습과 평가를 수행하면 올바른 평가가 될 수 없다. 학습할 때 사용한 데이터셋은 맞혀도 다른 데이터셋은 맞추지 못하는 일이 발생한다. 이렇게 한 데이터셋에만 지나치게 최적화된 상태를 오버피팅(overfitting)이라고 한다.


손실 함수(loss function)

손실 함수는 신경망 학습에서 사용하는 지표로, 임의의 함수를 사용할 수 있지만 일반적으로 오차제곱합과 교차 엔트로피 오차를 사용한다.

오차제곱합(sum of squares for error, SSE)

수식은 다음과 같다.

e 4 1

yk는 신경망의 출력(신경망이 추정한 값), tk는 정답 레이블, k는 데이터의 차원 수를 나타낸다.

예를 들어 전의 손글씨 숫자 인식에서 yk와 tk는 다음과 같은 원소 10개짜리 데이터이다.

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]

이 예에서 y는 이미지가 해당 숫자일 확률이고, 정답 레이블인 t는 정답을 가리키는 위치는 1로, 그 외는 0으로 표기한다. 즉, 여기서 숫자 2에 해당하는 원소의 값이 1이니까 정답이 2인 것을 알 수 있다.

오차제곱합은 각 원소의 출력(추정 값)과 정답 레이블(참 값)의 차(yk - tk)를 제곱한 후, 그 총합을 구한다.

파이썬으로 구현

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

정답이 '2'고 신경망의 출력도 '2'에서 가장 높은 경우

y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
sum_squares_error(np.array(y), np.array(t)) # 0.09750000000000003

정답은 똑같이 '2'지만, 신경망의 출력은 '7'에서 가장 높은 경우

y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
sum_squares_error(np.array(y), np.array(t)) # 0.5975

결과를 보면 첫 번째의 손실 함수 쪽 출력이 작으며 정답 레이블과의 오차도 작은 것을 알 수 있어 오차제곱합 기준으로는 첫 번쨰 추정 결과가 오차가 더 작으니 정답에 더 가까울 것으로 판단 할 수 있다.

교차 엔트로피 오차(cross entropy error, CEE)

수식은 다음과 같다.

e 4 2

yk는 신경망의 출력, tk는 정답레이블이다.
이 수식은 실질적으로 정답일 때의 추정(tk가 1일 때의 yk)의 자연로그를 계산하는 식이 된다.

예시

정답 레이블은 '2'가 정답이라고 하고 이때의 신경망 출력이 0.6이면 교차 엔트로피 오차는 -log0.6 = 0.51이 된다. 같은 조건에서 신경망 출력이 0.1이면 -log0.1 = 2.30이 된다.

교차 엔트로피 오차는 정답일 때의 출력이 전체 값을 정하게 된다.

자연로그는 x가 1일 때 y는 0이되고 x가 0에 가까워질수록 y의 값은 점점 작아진다.
위의 식도 마찬가지로 정답에 해당하는 출력이 커질수록 0에 가까워지고, 그 출력이 1일 때 0이 된다. 반대로 정답일 때의 출력이 작아질수록 오차는 커진다.

파이썬으로 구현

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

np.log를 계산할 때 아주 작은 값인 delta를 더하는데, 이는 np.log() 함수에 0을 입력하면 -inf가 되어 더 이상 계산을 진행할 수 없게 되기 때문이다. 아주 작은 값을 더해 절대 0이 되지 않도록 예방한 것이다.

정답이 '2'고 신경망의 출력도 '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]
cross_entropy_error(np.array(y), np.array(t)) # 0.510825457099338

정답은 똑같이 '2'지만, 신경망의 출력은 '7'에서 가장 높은 경우

y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
cross_entropy_error(np.array(y), np.array(t)) # 2.302584092994546

결과인 오차 값이 더 작은 첫 번째 추정이 정답일 가능성이 높다고 판단한 것이다.

미니배치 학습

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

지금까지는 데이터 하나에 대한 손실 함수였고, 훈련 데이터 모두에 대한 손실 함수의 합을 구하는 것을 생각해야 한다.

엔트로피 오차의 수식

e 4 3

데이터가 N개라면 tnk는 n번째 데이터의 k번째 값을 의미한다.

위의 수식은 원래의 수식에서 N개의 데이터로 확장하고 마지막에 N으로 나누어 정규화한 것이다. 즉, N으로 나눠 '평균 손실 함수'를 구하는 것이다.

데이터셋의 훈련 데이터가 많으면 이 많은 대이터를 대상으로 일일이 손실 함수를 계산하는 것은 현실적이지 않다. 이 경우 데이터 일부를 추려 전체의 '근사치'로 이용할 수 있다.

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

import numpy as np
from dataset.mnist import load_mnist

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000, 10)

위를 실행시켜보면 훈련 데이터는 6만개고, 입력 데이터는 784열인 이미지 데이터이고, 정답 레이블은 10줄짜리 데이터임을 알 수 있다.

훈련 데이터에서 무작위로 빼내려면 np.random.choice() 함수를 사용하면 된다.

np.random.choice(600000, 10) 

출력 결과

array([ 46257, 524702, 478410, 380081, 362514, 514924, 480344, 576263, 529729, 130159])

위의 경우 0 이상 60000 미만의 수 중에서 무작위로 10개를 골라낸다.

train_size = x_train.shape[0]
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]

이렇게 무작위로 선택한 인덱스를 사용해 미니배치를 뽑아내면 된다. 손실 함수도 이 미니배치로 계산해야 한다.

(배치용) 교차 엔트로피 오차 구현

배치 데이터를 지원하는 교차 엔트로피 오차 구현하는데 데이터가 하나인 경우와 데이터가 배치로 묶여 입력될 경우 모두를 처리할 수 있도록 구현한다.

정답 레이블이 원-핫 인코딩인 경우

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

y가 1차원이라면, 즉 데이터 하나당 교차 엔트로피 오차를 구하는 경우는 reshape 함수로 데이터의 형상을 바꿔준다. 그리고 배치의 크기로 나눠 정규화하고 이미지 1장당 평균의 교차 엔트로피 오차를 계산한다.

정답 레이블이 숫자 레이블('2', '7'...)로 주어졌을 경우

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(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

이 경우는 원-핫 인코딩일 때 t가 0인 원소는 교차 엔트로피 오차도 0이므로, 그 계산은 무시해도 좋다는 것이 특징이다. 즉, 정답에 해당하는 신경망의 출력만으로 교차 엔트로피 오차를 계산할 수 있다.

np.log(y[np.arange(batch_size), t] + 1e-7)에서 np.arange(batch_size)는 0부터 batch_size - 1까지 배열을 생성한다.
t에는 레이블이 [2, 7, 0, 9, 4]와 같이 저장되어 있으므로 y[np.arange(batch_size), t]는 각 데이터의 정답 레이블에 해당하는 신경망의 출력을 추출한다. 위의 예시에서는 [y[0, 2], y[1, 7], y[3, 8], y[4, 4]]인 넘파이 배열을 생성(델타는 편의상 생략)

손실 함수를 설정하는 이유

신경망 학습에서 최적의 매개변수를 탐색할 때 손실 함수의 값을 가능한 한 작게 하는 매개변수 값을 찾는다. 이때 매개변수의 미분(정확히는 기울기)을 계산하고, 그 미분 값을 단서로 매개변수의 값을 서서히 갱신하는 과정을 반복한다.

가중치 매개변수의 손실 함수를 미분이란 '가중치 매개변수의 값을 아주 조금 변화시켰을 때, 손실 함수가 어떻게 변하나'라는 의미이다. 이 미분 값이 음수면 가중치 매개변수를 양의 방향으로 변화시켜 손실 함수의 값을 줄일 수 있고, 그 반대도 마찬가지지만 미분 값이 0이면 가중치 매개변수를 어느쪽으로 움직여도 손실 함수의 값은 줄어들지 않는다. 이때 가중치 매개변수의 갱신은 멈춘다.

정확도를 지표로 삼으면 매개변수의 미분 값이 대부분의 장소에서 0이되어 매개변수를 갱신할 수 없기 때문이다.

정확도는 매개변수의 미소한 변화에는 거의 반응을 보이지 않고, 반응이 있더라도 그 값이 불연속적으로 변화한다.

이는 계단 함수를 활성화 함수로 사용하지 않는 이유와 같다. 계단 함수는 대부분의 곳에서 미분 값이 0이다. 이에 반해 시그모이드 함수는 출력이 연속적으로 변하고, 곡선의 기울기도 연속적으로 변해 미분이 어느 장소라도 0이 되지 않는다. 이렇게 기울기가 0이 되지 않아야 신경망이 올바르게 학습할 수 있다.