ML문제는 데이터를 훈련데이터(training data)와 시험데이터(test data)로 나눠 학습과 실험 수행
훈련데이터만 사용하여 최적의 매개변수 찾음
시험데이터를 이용하여 훈련한 모델의 실력 평가
훈련-시험 데이터를 나누는 이유 : 범용능력을 평가하기 위해
범용능력 : 아직 보지 못한 데이터로도 문제를 올바르게 풀어내는 능력
데이터셋 하나로만 매개변수의 학습 및 평가를 수행하면 올바른 평가 될 수 없음
오버피팅(overfitting) : 한 데이터셋에만 지나치게 과적화된 상태
4.2 손실함수
손실함수 : 신경망이 최적의 매개변수 값을 탐색하는 기준 (지표)
임의의 함수 사용가능하지만 일반적으로 오차제곱합과 교차 엔트로피 오차 사용
손실함수는 신경망 성능의 '나쁨'을 나타내는 지표
4.2.1 오차제곱합 (sum of squares for error)
: 신경망 모델이 추정한 값
: 정답 레이블 또는 데이터
k는 차원의 수
1/2 곱해주는 이유는 미분식 때문 (1/2 * 2 = 1)
import numpy as np
# MSE 함수 구현
def mean_squared_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일 확률이 가장 높다고 했을 때
y = [0.1,0.2,0.6,0.0,0.05,0.02,0.0,0.0,0.0,0.03]
sum_squares_error(np.array(y), np.array(t))
>>> 0.10690000000000004
------------------------------------------------------
# 예 2 : 5일 확률이 가장 높다고 했을 때
y = [0.1,0.2,0.05,0.0,0.5,0.1,0.05,0.0,0.0,0.0]
sum_squares_error(np.array(y), np.array(t))
>>> 0.6075
손실 함수의 출력이 작을수록 정답에 가까울 것으로 판단할 수 있음
4.2.2 교차 엔트로피 오차 (cross entropy error)
: 밑이 e인 자연로그(loge)
: 신경망 모델의 출력
: 정답 레이블
실질적으로 정답일 때의 추정의 자연로그 계산 (정답이 아닌 것은 tk가 0 이므로 결과영향X)
교차 엔트로피 오차는 정답일 때의 출력이 전체 값을 정하게 됨
자연로그 y=logx의 그래프 : 정답출력이 커질수록 0에 다가가다가, 그 출력이 1일 때 0이 됨
Cross entropy 구현
def cross_entropy_error(y, t):
delta = 1e-7 # log함수에 0을 입력하면 -inf 출력되므로 이를 막기위해 아주 작은 값 더해줌
return -np.sum(t * np.log(y + delta))
수식 : 마지막에 N(데이터의 갯수)으로 나누어 '평균 손실 함수' 구함 --> 훈련 데이터 개수와 관계없이 언제든 통일된 지표 얻을 수 있음
미니 배치 : 데이터 일부를 추려 전체의 '근사치'로 이용하는 것
빅데이터를 대상으로 일일이 손실함수 계산하는 것은 현실적이지 않음
예) 60000장 중 100장 랜덤으로 뽑아 학습
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)
# 무작위로 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]
np.random.choice() --> 지정한 범위의 수 중에서 무작위로 원하는 개수만 꺼낼 수 있음
무작위로 선택한 인덱스를 사용 / 손실함수도 미니배치로 계산
4.2.4 (배치용) 교차 엔트로피 오차 구현하기
# 데이터가 하나인 경우와 데이터가 배치로 묶여 입력될 경우 모두 처리가능하게 구현
def cross_entropy_error(y, t) :
if y.ndim == 1: #y가 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
# 정답레이블이 원-핫 인코딩이 아닌 경우 (숫자 레이블로 주어진 경우)
def cross_entropy_error(y, t) :
if y.ndim == 1: #y가 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[np.arange(batch_size), t] + 1e-7)) / batch_size
원-핫 인코딩일 때 t가 0인 원소는 교차 엔트로피 오차도 0이므로, 그 계산은 무시해도 좋다는 것이 핵심
정답에 해당하는 신경망의 출력만으로 교차 엔트로피 오차 계산가능
t * np.log(y) 부분을 np.log(y[np.arange(batch_size), t])로 구현
np.arange(batch_size)는 0부터 batch_size -1까지 배열 생성
각 데이터의 정답레이블에 해당하는 신경망의 출력 추출
예) t = [2,7,0,9,4] 일 때, [y[0,2], y[1,7], y[2,0], y[3,9], y[4,4]]인 넘파이 배열 생성
4.2.5 왜 손실함수를 설정하는가?
'미분'의 역할에 주목하면 정확도 대신 손실함수 쓰는 이유 파악가능
신경망 학습에서는 최적의 매개변수 탐색 시 손실 함수 값 최소화하는 매개변수 값 찾음
이때 매개변수의 미분(기울기)을 계산하고, 그 값을 단서로 매개변수의 값을 서서히 갱신하는 과정 반복
미분 값의 양,음에 따라 반대방향으로 매개변수 변화시켜 손실함수 값 줄일 수 있음
미분 값이 0이 되면 가중치 매개변수의 갱신이 멈춤
정확도를 지표로 삼으면 매개변수의 미분이 대부분의 장소에서 0이 됨
정확도는 매개변수의 미소한 변화에는 거의 반응을 보이지 않고, 반응이 있더라도 그 값이 불연속적으로 갑자기 변화함
계단함수를 활성화함수로 사용하지 않는 것과 같은 이유
매개변수의 작은 변화가 주는 파장을 계단 함수가 말살하여 손실함수의 값에는 아무런 변화가 나타나지 않기 때문
어느 장소라도 함수의 미분(기울기)은 0이 되지 않는게 신경망의 중요한 성질
4.3 수치미분
경사법에서는 기울기값을 기준으로 나아갈 방향 정함
4.3.1 미분
예 ) 10분에 2km를 달렸다고 했을 때
10분 동안의 '평균속도'를 구한 것
시간을 가능한 한 줄여 '한 순간'의 변화량을 얻는 것이 미분(어느 순간의 속도)
시간을 뜻하는 h를 한없이 0에 가깝게 한다는 의미로 lim로 나타냄
# 미분 구현 (식을 곧이 곧대로 구현할 때)
# 미분 구현의 나쁜 예
def numerical_diff(f, x):
h = 10e-50
retrun (f(x+h) - f(x)) / h
함수 f와 함수 f에 넘길 인수 x - 두 인수를 받음, 그러나 개선해야 할 점 있음
h에는 가급적 작은 값 대입하고 싶어 (0에 가까이 하고싶어) 너무 작은값 사용 : 반올림 오차문제(작은값 생략) --> 10e-4사용
f의 차분(임의 두 점에서의 함수 값들의 차이) 문제
x + h와 x 사이의 함수 f의 차분을 계산하지만, 이 계산에는 애당초 오차가 있음
'진정한 미분'은 x위치의 함수의 기울기(=접선)에 해당
그러나 구현에서는 (x+h)와 x사이의 기울기이므로 엄밀히 일치하지는 않음
h를 무한히 0으로 좁히는게 불가능하여 생기는 한계
수치 미분에는 오차가 포함됨
이 오차를 줄이기 위해 (x+h)와 (x-h)일 때의 함수f의 차분을 계산하는 방법을 쓰기도 함 = 중심차분 or 중앙차분
# 두 개선점 적용해 수치미분 다시 구현
def numerical_diff(f,x):
h = 1e-4
return (f(x+h) - f(x-h)) / (2*h)
4.3.2 수치 미분의 예
2차함수 미분해보기 --> y = 0.01x^2 + 0.1x
# 2차 함수 구현하기
def function_1(x):
return 0.01*x**2 + 0.1*x
# 함수 그려보기
import matplotlib.pyplot as plt
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()
복잡한 함수는 평평한 곳으로 파고들면서 '고원'상태 (학습이 진행되지 않는상태)가 될 수 있음
경사법 : 현 위치에서 기울어진 방향으로 일정 거리만큼 이동하여 함수의 값을 점차 줄이는 방법
이동한 곳에서 기울기 구한 다음 기울어진 방향으로 이동 반복
[경사법 수식] n기호(에타)는 갱신하는 양 (학습률) : 한번의 학습으로 얼마나 갱신할지
학습률과 같은 매개변수를 하이퍼파라미터라고 함 (사람이 직접 설정해야 하는 매개변수)
# 경사법 구현
# f는 구현함수 / init_x는 초깃값 / lr은 학습률 / step_num은 반복 횟수
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