본문 바로가기

TIL/밑바닥부터 시작하는 딥러닝

손실 함수

1. 손실함수

  • 신경망이 최적의 매개변수 값을 탐색하는데 사용하는 지표

1.1. 오차제곱합

  • 가장 많이 쓰이는 손실 함수는 오차제곱합(SSE, sum of squares for error)이다.

$$
\text{E} = \frac{1}{2}\sum_{k} (y_k - t_k)^2
$$

  • $y_k$는 신경망의 출력, $t_k$는 정답 레이블, k는 데이터의 차원 수를 나타낸다.
import numpy as np

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

# 정답은 2인 원-핫 인코딩
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

# 예1 : '2'일 확률이 가장 높다고 추정(0.6)
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)))

# 예2 : '7'일 확률이 가장 높다고 추정(0.6)
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
"""
  • 오차 제곱합을 구현한 sum_squares_error() 함수와 예제 출력 2개이다.
  • 정답인 출력은 오차가 0.097
  • 오답인 출력은 오차가 0.597
  • 오차제곱합 기준으로는 첫 번째 추정 결과가 정답에 더 가까울 것으로 판단할 수 있다.

1.2. 교차 엔트로피 오차

$$
\text{E} = -\sum_{k} t_k\log y_k
$$

  • log는 밑이 e인 자연로그($log_e$)이다. $y_k$는 신경망의 출력, $t_k$는 정답 레이블
def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))

print(cross_entropy_error(np.array(y), np.array(t)))
print(cross_entropy_error(np.array(y), np.array(t)))
"""
출력
0.510825457099338
2.302584092994546
"""
  • cross_entropy_error() 함수를 보면 np.log를 계산할 때 delta 라는 값을 더했는데
  • 이는 np.log() 함수에 0을 입력하면 마이너스 무한대인 -inf가 되어 더 이상 계산을 진행할 수 없게 되기 때문이다.
  • 정답인 출력은 오차가 0.510
  • 오답인 출력은 오차가 2.302

2. 미니배치 학습

  • 머신러닝은 훈련 데이터를 사용해 손실 함수의 값을 구하고, 그 값을 최대한 줄여주는 매개변수를 찾아낸다.
  • 이렇게 하려면 모든 훈련 데이터를 대상으로 손실 함수 값을 구해야한다.
  • 지금까지는 데이터 하나에 대한 손실 함수만을 구했으니, 이제 훈련 데이터 모두에 대한 손실 함수의 합을 구해보겠다.

2.1. 미니배치 학습(교차 엔트로피)

  • 정답 레이블이 원-핫 인코딩일 때
import sys, os
sys.path.append(os.pardir)
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)

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]
    delta = 1e-7
    return -np.sum(t * np.log(y + delta)) / batch_size
  • 정답 레이블이 원-핫 인코딩이 아니라 '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]
    delta = 1e-7
    return -np.sum(np.log(y[np.arange(batch_size), t] + delta)) / batch_size

2.2. 왜 손실함수를 설정하는가?

  • 머신러닝에서 정확도라는 지표를 놔두고 손실함수를 설정하는 이유는 무엇일까?
  • 이 의문은 신경망 학습에서의 미분의 역할에 주목한다면 해결된다.
  • 신경망 학습에서는 최적의 매개변수(가중치와 편향)를 탐색할 때 손실 함수의 값을 최대한 작게 하는 매개변수 값을 찾는다.
  • 이때 매개변수의 미분(정확히는 기울기)을 계산하고, 그 미분 값을 단서로 매개변수의 값을 서서히 갱신하는 과정을 반복한다.
  • 정확도를 지표로 삼아서는 안 되는 이유는 미분 값이 대부분의 장소에서 0이 되어 매개변수를 갱신할 수 없기 때문이다.
  • 신경망을 학습할 때 정확도를 지표로 삼아서는 안된다. 정확도를 지표로 하면 매개변수의 미분이 대부분의 장소에서 0이 되기 때문이다.

'TIL > 밑바닥부터 시작하는 딥러닝' 카테고리의 다른 글

학습 알고리즘  (1) 2024.06.18
경사법  (1) 2024.06.16
소프트맥스 함수  (0) 2024.06.15
Numpy 다차원 배열의 계산  (0) 2024.06.14
활성화 함수  (0) 2024.06.14