5. 신경망 학습 관련 기술(4)
※ 오버피팅 개선
오버피팅 : 신경망이 훈련 데이터에만 지나치게 적응되어 새로운 데이터에 제대로 대응하지 못하는 상태
-> 머신러닝, 딥러닝은 훈련 데이터에서 보지 못한 새로운 데이터에서도 범용적으로 바르게 식별해내는 모델이 필요함.
1) 매개변수가 많고 표현력이 높은 모델
2) 훈련 데이터가 데이터셋의 데이터에 비해 현저히 적은 경우
# coding: utf-8
import os
import sys
sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net import MultiLayerNet
from common.optimizer import SGD
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
# 오버피팅을 재현하기 위해 학습 데이터 수를 줄임
x_train = x_train[:300]
t_train = t_train[:300]
# weight decay(가중치 감쇠) 설정 =======================
#weight_decay_lambda = 0 # weight decay를 사용하지 않을 경우
weight_decay_lambda = 0.1
# ====================================================
network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10,
weight_decay_lambda=weight_decay_lambda)
optimizer = SGD(lr=0.01) # 학습률이 0.01인 SGD로 매개변수 갱신
max_epochs = 201
train_size = x_train.shape[0]
batch_size = 100
train_loss_list = []
train_acc_list = []
test_acc_list = []
iter_per_epoch = max(train_size / batch_size, 1)
epoch_cnt = 0
for i in range(1000000000):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
grads = network.gradient(x_batch, t_batch)
optimizer.update(network.params, grads)
if i % iter_per_epoch == 0:
train_acc = network.accuracy(x_train, t_train)
test_acc = network.accuracy(x_test, t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print("epoch:" + str(epoch_cnt) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc))
epoch_cnt += 1
if epoch_cnt >= max_epochs:
break
# 그래프 그리기==========
markers = {'train': 'o', 'test': 's'}
x = np.arange(max_epochs)
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
일부러 train데이터를 300개로 줄여 60,000개의 MNIST 데이터셋을 200개 가량의 epoch으로 조사해보았다. overfiitting을 강제로 일으킨 것인데 그래프를 보자.
그래프를 보면 거의 100epoch부터는 1.0에 가까운 accuracy가 train에서는 나타나고 있다. 하지만, test 데이터는 0.7 정도 밖에 되지 않는 것을 보아 test 데이터에서 제대로 적용되지 않는 것이라고 판단할 수 있다.
■ 가중치 감소법
- 일부러 큰 가중치에 대해서 그에 상응하는 페널티를 부과하여 오버피팅을 억제하는 방법
if 가중치를 W로 설정 -> L2 노름에 따른 가중치 감소 페널티 부과
L2 노름에 따른 가중치 감소 수치를 이렇게 지정하고 손실 함수에 더하여 람다를 크게 설정할수록 큰 가중치에 대한 페널티가 커지도록 조정한다. 모든 가중치 각각의 손실 함수에 위 수식값을 더하는 셈이다.
가중치의 기울기를 계산하기 위해서는 오차역전파법에 따른 결과에 정규화 항을 미분한 람다*W를 더한다.
def gradient(self, x, t):
"""기울기를 구한다(오차역전파법).
Parameters
----------
x : 입력 데이터
t : 정답 레이블
Returns
-------
각 층의 기울기를 담은 딕셔너리(dictionary) 변수
grads['W1']、grads['W2']、... 각 층의 가중치
grads['b1']、grads['b2']、... 각 층의 편향
"""
# forward
self.loss(x, t)
# backward
dout = 1
dout = self.last_layer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
# 결과 저장
grads = {}
for idx in range(1, self.hidden_layer_num+2):
grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dW + self.weight_decay_lambda * self.layers['Affine' + str(idx)].W
grads['b' + str(idx)] = self.layers['Affine' + str(idx)].db
return grads
common.multi_layer_net 코드의 gradient 함수의 grads 부분을 가중치 감소법을 사용하여 적용시켜 보았다.
위와 같이 overfitting을 일으키는 코드를 다시 running 시켜보면 이와 같이 train과 test accuracy 차이가 상당히 줄어들면서 overfitting 문제가 일정 부분 해결된 것을 볼 수 있다.