728x90
◎ 밑바닥부터 시작하는 딥러닝(1)¶
● chapter 6. 학습 관련 기술들¶
6.1 매개변수 갱신
- 최적화(Optimization) : 매개변수의 최적값을 찾는 것
- 확률적 경사 하강법(SGD)이란 매개변수의 기울기를 구해, 기울어진 방향으로 매개변수 값을 갱신하는 일을 몇 번이고 반복해서 점점 최적의 값을 도출해내는 방법
In [1]:
# SGD 구현하기
class SGD:
def __init__(self, lr=0.01):
self.lr = lr # learning rage(학습률)
def update(self, parms, grads):
for key in params.keys():
params[key] -= self.lr*grads[key]
SGD의 단점
- SGD는 단순하고 구현도 쉽지만, 문제에 따라서는 비효율적일 때가 있다.
- SGD는 비등방성(anisotropy) 함수(방향에 따라 성질, 즉 여기에서는 기울기가 달라지는 함수)에서는 탐색 경로가 비효율적이다.
- 이러한 SGD의 단점을 개선해주는 모멘텀, AdaGrad, Adam 세 방법이 있다.
모멘텀
- 모멘텀(Momentum)은 운동량을 뜻하는 단어로 물리와 관계가 있다.
- 기존의 SGD 함수에 v(속도)라는 변수가 추가
In [2]:
# 모멘텀 구현 코드
class Momentum:
"""모멘텀 SGD"""
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.v = None # 초기화 때는 아무 값도 지정하지않는다.
def update(self, params, grads):
if self.v is None:
self.v = {}
for key, val in params.items():
self.v[key] = np.zeros_like(val)
for key in params.keys():
self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
params[key] += self.v[key]
☆ 모멘텀은 힘은 작지만 방향이 변하지 않으면 한 방향으로 일정하게 가속하고, 힘은 크지만 위아래로 번갈아 받으면 가속이 붙지 않는 특징이 있다.
AdaGrad
- 학습률을 정하는 효과적 기술로 학습률 감소(learning rate decay)가 있다. 이는 처음에는 크게 학습하다가 조금씩 작게 학습한다.
- AdaGrad는 각각의 매개변수에 맞춤형 값을 만들어준다.
- SGD 식에서 h를 추가, h는 기존 기울기 값을 제곱하여 계속 더해주는 변수 그리고 매개변수를 갱신할 때 1/sqrt(h)을 곱해 학습률 조정
- 매개변수의 원소 중에서 많이 움직인 원소는 학습률이 낮아진다는 뜨으로 학습률 감소가 매개변수의 원소마다 다르게 적용
- AdaGrad는 과거의 기울기를 제곱하여 계속 더해가 무한히 학습하면 어느 순간 갱신량이 0이 되어 전혀 갱신되지 않는다.
- 이 문제를 개선한 기법으로 RMSProp를 사용하며 RMSProp는 먼 과거의 기울기는 서서히 잊고 새로운 기울기 정보를 크게 반영.
In [3]:
# AdaGrad 구현하기
class AdaGrad:
"""AdaGrad"""
def __init__(self, lr=0.01):
self.lr = lr
self.h = None
def update(self, params, grads):
if self.h is None:
self.h = {}
for key, val in params.items():
self.h[key] = np.zeros_like(val)
for key in params.keys():
self.h[key] += grads[key] * grads[key]
params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
Adam
- 모멘텀과 AdaGrad를 융합한 Adam
- Adam은 하이퍼파라미터를 3개 설정한다.
- Adam은 학습의 갱신 강도를 적응적으로 조정한다.
In [4]:
# Adam 구현하기
class Adam:
"""Adam (http://arxiv.org/abs/1412.6980v8)"""
def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
self.lr = lr
self.beta1 = beta1
self.beta2 = beta2
self.iter = 0
self.m = None
self.v = None
def update(self, params, grads):
if self.m is None:
self.m, self.v = {}, {}
for key, val in params.items():
self.m[key] = np.zeros_like(val)
self.v[key] = np.zeros_like(val)
self.iter += 1
lr_t = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)
for key in params.keys():
#self.m[key] = self.beta1*self.m[key] + (1-self.beta1)*grads[key]
#self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2)
self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])
params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
#unbias_m += (1 - self.beta1) * (grads[key] - self.m[key]) # correct bias
#unbisa_b += (1 - self.beta2) * (grads[key]*grads[key] - self.v[key]) # correct bias
#params[key] += self.lr * unbias_m / (np.sqrt(unbisa_b) + 1e-7)
어느 갱신 방법을 이용할 것인가?
- SGD, 모멘텀, AdaGrad, Adam의 네 후보 중 풀어야 할 문제가 무엇이냐에 따라 최적의 모델이 달라진다.
In [5]:
import os
import sys
sys.path.append('C:/Users/설위준/Desktop/deep-learning-from-scratch-master') # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.util import smooth_curve
from common.multi_layer_net import MultiLayerNet
from common.optimizer import *
# 0. MNIST 데이터 읽기==========
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
train_size = x_train.shape[0]
batch_size = 128
max_iterations = 2000
# 1. 실험용 설정==========
optimizers = {}
optimizers['SGD'] = SGD()
optimizers['Momentum'] = Momentum()
optimizers['AdaGrad'] = AdaGrad()
optimizers['Adam'] = Adam()
#optimizers['RMSprop'] = RMSprop()
networks = {}
train_loss = {}
for key in optimizers.keys():
networks[key] = MultiLayerNet(
input_size=784, hidden_size_list=[100, 100, 100, 100],
output_size=10)
train_loss[key] = []
# 2. 훈련 시작==========
for i in range(max_iterations):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
for key in optimizers.keys():
grads = networks[key].gradient(x_batch, t_batch)
optimizers[key].update(networks[key].params, grads)
loss = networks[key].loss(x_batch, t_batch)
train_loss[key].append(loss)
if i % 100 == 0:
print( "===========" + "iteration:" + str(i) + "===========")
for key in optimizers.keys():
loss = networks[key].loss(x_batch, t_batch)
print(key + ":" + str(loss))
# 3. 그래프 그리기==========
markers = {"SGD": "o", "Momentum": "x", "AdaGrad": "s", "Adam": "D"}
x = np.arange(max_iterations)
for key in optimizers.keys():
plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery=100, label=key)
plt.xlabel("iterations")
plt.ylabel("loss")
plt.ylim(0, 1)
plt.legend()
plt.show()
===========iteration:0=========== SGD:2.364859910033151 Momentum:2.3788675543474618 AdaGrad:2.528272448845539 Adam:2.235997876357242 ===========iteration:100=========== SGD:1.5992606902740416 Momentum:0.2504306169106948 AdaGrad:0.08445802605572705 Adam:0.1634918124692979 ===========iteration:200=========== SGD:0.873243247626103 Momentum:0.36540554079047216 AdaGrad:0.17211302014861643 Adam:0.21041735663182815 ===========iteration:300=========== SGD:0.56419833017716 Momentum:0.14344889494763188 AdaGrad:0.06242140977498997 Adam:0.08893120294795881 ===========iteration:400=========== SGD:0.5286284753998669 Momentum:0.19578640991773893 AdaGrad:0.06099899326743148 Adam:0.10032119413049695 ===========iteration:500=========== SGD:0.42221056405104307 Momentum:0.15892078370215243 AdaGrad:0.07780167001895416 Adam:0.1447008805958458 ===========iteration:600=========== SGD:0.2665431792645768 Momentum:0.09697438898334072 AdaGrad:0.035364926162955085 Adam:0.05387317718053186 ===========iteration:700=========== SGD:0.22832682521731576 Momentum:0.10067052491328905 AdaGrad:0.035269901181942044 Adam:0.03501383896574582 ===========iteration:800=========== SGD:0.23969210543240513 Momentum:0.12149292942522986 AdaGrad:0.03416161431963891 Adam:0.05061805798571246 ===========iteration:900=========== SGD:0.19904484918644907 Momentum:0.11591596044525465 AdaGrad:0.06394948004871717 Adam:0.09337481240764646 ===========iteration:1000=========== SGD:0.20408405266209362 Momentum:0.08684704591693773 AdaGrad:0.027723048393423107 Adam:0.039168868724916296 ===========iteration:1100=========== SGD:0.2636500459043063 Momentum:0.07488656268603994 AdaGrad:0.025373036657606013 Adam:0.03597054313967997 ===========iteration:1200=========== SGD:0.38050312342934944 Momentum:0.1899120834931111 AdaGrad:0.08711859581702971 Adam:0.06358431452457379 ===========iteration:1300=========== SGD:0.17756894659262595 Momentum:0.10088642782063847 AdaGrad:0.04474453164019665 Adam:0.07672827729615211 ===========iteration:1400=========== SGD:0.2378664721622722 Momentum:0.06856824816467152 AdaGrad:0.019661023841531355 Adam:0.031038486247975068 ===========iteration:1500=========== SGD:0.1137087547281366 Momentum:0.025819885312704616 AdaGrad:0.01070283035264566 Adam:0.010063114633249099 ===========iteration:1600=========== SGD:0.09935952046364221 Momentum:0.020035939682084485 AdaGrad:0.013271164609785287 Adam:0.010973295781379067 ===========iteration:1700=========== SGD:0.3107802019135021 Momentum:0.07714253679955252 AdaGrad:0.03317289748725159 Adam:0.05860908673637018 ===========iteration:1800=========== SGD:0.191868543184755 Momentum:0.037077845865387316 AdaGrad:0.01647534367622892 Adam:0.031209817361101077 ===========iteration:1900=========== SGD:0.14915125971382256 Momentum:0.09744402928140178 AdaGrad:0.020000963267673106 Adam:0.042161584726553125
- 위 결과를 보면 SGD의 학습 진도가 가장 느리다.
- 일반적으로 SGD보다 다른 세 기법이 빠르게 학습하고, 때로는 최종 정확도도 높게 나타난다.
6.2 가중치의 초깃값
- 가중치 감소 기법 : 오버피팅을 억제해 범용 성능을 높이는 테크닉
- 가중치를 균일한 값으로 설정하면 오차역전파법에서 모든 가중치의 값이 똑같이 갱신되기 때문에 않좋은 방법이다.
- 가중치의 대칭적인 구조를 무너뜨리기 위해 초깃값을 무작위로 설정해야 한다.
In [6]:
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
input_data = np.random.randn(1000, 100) # 1000개의 데이터
node_num = 100 # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5 # 은닉층이 5개
activations = {} # 이곳에 활성화 결과를 저장
x = input_data
for i in range(hidden_layer_size):
if i != 0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) * 1
a = np.dot(x, w)
z = sigmoid(a)
activations[i] = z
for i, a in activations.items():
plt.subplot(1, len(activations), i+1)
plt.title(str(i+1) + "-layer")
if i != 0: plt.yticks([], [])
# plt.xlim(0.1, 1)
# plt.ylim(0, 7000)
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
- 각 층의 활성화값들이 0과 1에 치우쳐 분포되어 있다.
- 시그모이드 함수는 출력이 0 또는 1에 가까워지면 그 미분은 0에 가까워진다.
- 따라서 역전파의 기울기 값이 점점 작아지다가 사라지는 기울기 소실 문제가 발생할 수 있다.
In [7]:
for i in range(hidden_layer_size):
if i != 0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) * 0.01 # 초깃값 변경
a = np.dot(x, w)
z = sigmoid(a)
activations[i] = z
for i, a in activations.items():
plt.subplot(1, len(activations), i+1)
plt.title(str(i+1) + "-layer")
if i != 0: plt.yticks([], [])
# plt.xlim(0.1, 1)
# plt.ylim(0, 7000)
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
- 기울기 소실 문제는 일어나지 않겠지만, 활성화값들이 치우쳤다는 것은 표현력 관점에서는 큰 문제가 있다.
- 다수의 뉴런이 같은 값을 출력하고 있으니 뉴런을 여러 개 둔 의미가 없어진다는 뜻이다.
In [8]:
# Xavier 초깃값 사용
for i in range(hidden_layer_size):
if i != 0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num)
a = np.dot(x, w)
z = sigmoid(a)
activations[i] = z
for i, a in activations.items():
plt.subplot(1, len(activations), i+1)
plt.title(str(i+1) + "-layer")
if i != 0: plt.yticks([], [])
# plt.xlim(0.1, 1)
# plt.ylim(0, 7000)
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
ReLU를 사용할 때의 가중치 초깃값
- Xavier 초깃값은 활성화 함수가 선형인 것을 전제로 이끈 결과이다. Sigmoid 함수와 tanh 함수는 좌우 대칭이라 중앙 부근이 선형인 함수로 볼 수 있다.
- 반면 ReLU를 이용할 때는 ReLU에 특화된 He 초깃값을 이용하라고 권장한다.
In [9]:
def ReLU(x):
return np.maximum(0, x)
input_data = np.random.randn(1000, 100) # 1000개의 데이터
node_num = 100 # 각 은닉층의 노드(뉴런) 수
hidden_layer_size = 5 # 은닉층이 5개
activations = {} # 이곳에 활성화 결과를 저장
x = input_data
for i in range(hidden_layer_size):
if i != 0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num) # Xavier 초깃값
a = np.dot(x, w)
z = ReLU(a)
activations[i] = z
for i, a in activations.items():
plt.subplot(1, len(activations), i+1)
plt.title(str(i+1) + "-layer")
if i != 0: plt.yticks([], [])
# plt.xlim(0.1, 1)
# plt.ylim(0, 7000)
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
☆ 보통 ReLU를 사용할 때는 He 초깃값을, sigmoid나 tanh 등의 S자 모양 곡선일 때는 Xavier 초깃값을 사용한다.
☆ 가중치의 초깃값은 신경망 학습에 아주 중요한 포인트이다.
6.3 배치 정규화
배치 정규화의 장점
1. 학습을 빨리 진행할 수 있다.(학습 속도 개선)
2. 초깃값에 크게 의존하지 않는다.
3. 오버피팅을 억제한다.(드롭아웃 등의 필요성 감소)
In [10]:
import sys, os
sys.path.append('C:/Users/설위준/Desktop/deep-learning-from-scratch-master') # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net_extend import MultiLayerNetExtend
from common.optimizer import SGD, Adam
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
# 학습 데이터를 줄임
x_train = x_train[:1000]
t_train = t_train[:1000]
max_epochs = 20
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.01
def __train(weight_init_std):
bn_network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100], output_size=10,
weight_init_std=weight_init_std, use_batchnorm=True)
network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100], output_size=10,
weight_init_std=weight_init_std)
optimizer = SGD(lr=learning_rate)
train_acc_list = []
bn_train_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]
for _network in (bn_network, network):
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)
bn_train_acc = bn_network.accuracy(x_train, t_train)
train_acc_list.append(train_acc)
bn_train_acc_list.append(bn_train_acc)
#print("epoch:" + str(epoch_cnt) + " | " + str(train_acc) + " - " + str(bn_train_acc))
epoch_cnt += 1
if epoch_cnt >= max_epochs:
break
return train_acc_list, bn_train_acc_list
# 그래프 그리기==========
weight_scale_list = np.logspace(0, -4, num=16)
x = np.arange(max_epochs)
for i, w in enumerate(weight_scale_list):
#print( "============== " + str(i+1) + "/16" + " ==============")
train_acc_list, bn_train_acc_list = __train(w)
plt.subplot(4,4,i+1)
plt.title("W:" + str(w))
if i == 15:
plt.plot(x, bn_train_acc_list, label='Batch Normalization', markevery=2)
plt.plot(x, train_acc_list, linestyle = "--", label='Normal(without BatchNorm)', markevery=2)
else:
plt.plot(x, bn_train_acc_list, markevery=2)
plt.plot(x, train_acc_list, linestyle="--", markevery=2)
plt.ylim(0, 1.0)
if i % 4:
plt.yticks([])
else:
plt.ylabel("accuracy")
if i < 12:
plt.xticks([])
else:
plt.xlabel("epochs")
plt.legend()
plt.show()
C:/Users/설위준/Desktop/deep-learning-from-scratch-master\common\multi_layer_net_extend.py:104: RuntimeWarning: overflow encountered in square weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W**2) C:/Users/설위준/Desktop/deep-learning-from-scratch-master\common\multi_layer_net_extend.py:104: RuntimeWarning: invalid value encountered in double_scalars weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W**2) No handles with labels found to put in legend. C:\work\envs\datascience\lib\site-packages\numpy\core\fromnumeric.py:87: RuntimeWarning: overflow encountered in reduce return ufunc.reduce(obj, axis, dtype, out, **passkwargs) C:/Users/설위준/Desktop/deep-learning-from-scratch-master\common\multi_layer_net_extend.py:104: RuntimeWarning: invalid value encountered in double_scalars weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W**2) No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend. No handles with labels found to put in legend.
6.4 바른 학습을 위해
- 오버피팅이란 신경망이 훈련 데이터에만 지나치게 적응되어 그 외의 데이터에는 제대로 대응하지 못하는 상태
- 매개변수가 많고 표현력이 높은 모델, 훈련 데이터가 적음 이 두 경우에 주로 오버피팅 발생
In [11]:
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를 사용하지 않을 경우
# ====================================================
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()
가중치 감소
- 가중치 감소 : 큰 가중치에 대해서는 그에 상응하는 페널티를 부과하여 오버피팅을 억제하는 방법
In [12]:
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()
- 앞의 결과와 달리 정확도가 100%에 도달하지 못했다.
- 훈련 데이터와 테스트 데이터의 차이도 많이 줄어 들었다.
드롭아웃
- 가중치 감소는 간단하게 구현할 수 있고 어느 정도 지나친 학습을 억제할 수 있다. 그러나 신경망 모델이 복잡해지면 가중치 감소만으로는 대응하기 어려워진다. 이럴 때는 흔히 드롭아웃이라는 기법을 이용한다.
- 드롭아웃은 뉴런을 임의로 삭제하면서 학습하는 방법, 훈련 때는 데이터를 흘릴 때마다 삭제할 뉴런을 무작위로 선택하고, 시험 때는 모든 뉴런에 신호를 전달
앙상블 학습
- 앙상블 학습은 개별적으로 학습시킨 여러 모델의 출력을 평균 내어 추론하는 방식
- 드롭아웃은 앙상블 학습과 같은 효과를 하나의 네트워크로 구현
6.5 적절한 하이퍼파라미터 값 찾기
- 하이퍼파라미터의 성능을 평가할 때는 시험 데이터를 사용하면 안된다. 그 이유는 하이퍼파라미터 값이 시험 데이터에 오버피팅되기 때문입니다.
- 하이퍼파라미터 조정용 데이터를 일반적으로 검증 데이터(validation data)라고 부른다.
- 신경망의 하이퍼파라미터 최적화에서는 그리드 서치같은 규칙적인 탐색보다는 무작위로 샘플링해 탐색하는 편이 좋은 결과를 낸다. 이는 최종 정확도에 미치는 영향력이 하이퍼파라미터마다 다르기 때문이다.
- 하이퍼파라미터의 범위는 대략적으로 지정하는 것이 효과적이며 학습을 위한 에폭을 작게 하여, 1회 평가에 걸리는 시간을 단축하는 것이 효과적이다.
In [13]:
import sys, os
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.util import shuffle_dataset
from common.trainer import Trainer
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
# 결과를 빠르게 얻기 위해 훈련 데이터를 줄임
x_train = x_train[:500]
t_train = t_train[:500]
# 20%를 검증 데이터로 분할
validation_rate = 0.20
validation_num = int(x_train.shape[0] * validation_rate)
x_train, t_train = shuffle_dataset(x_train, t_train)
x_val = x_train[:validation_num]
t_val = t_train[:validation_num]
x_train = x_train[validation_num:]
t_train = t_train[validation_num:]
def __train(lr, weight_decay, epocs=50):
network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100],
output_size=10, weight_decay_lambda=weight_decay)
trainer = Trainer(network, x_train, t_train, x_val, t_val,
epochs=epocs, mini_batch_size=100,
optimizer='sgd', optimizer_param={'lr': lr}, verbose=False)
trainer.train()
return trainer.test_acc_list, trainer.train_acc_list
# 하이퍼파라미터 무작위 탐색======================================
optimization_trial = 100
results_val = {}
results_train = {}
for _ in range(optimization_trial):
# 탐색한 하이퍼파라미터의 범위 지정===============
weight_decay = 10 ** np.random.uniform(-8, -4)
lr = 10 ** np.random.uniform(-6, -2)
# ================================================
val_acc_list, train_acc_list = __train(lr, weight_decay)
#print("val acc:" + str(val_acc_list[-1]) + " | lr:" + str(lr) + ", weight decay:" + str(weight_decay))
key = "lr:" + str(lr) + ", weight decay:" + str(weight_decay)
results_val[key] = val_acc_list
results_train[key] = train_acc_list
# 그래프 그리기========================================================
print("=========== Hyper-Parameter Optimization Result ===========")
graph_draw_num = 20
col_num = 5
row_num = int(np.ceil(graph_draw_num / col_num))
i = 0
for key, val_acc_list in sorted(results_val.items(), key=lambda x:x[1][-1], reverse=True):
print("Best-" + str(i+1) + "(val acc:" + str(val_acc_list[-1]) + ") | " + key)
plt.subplot(row_num, col_num, i+1)
plt.title("Best-" + str(i+1))
plt.ylim(0.0, 1.0)
if i % 5: plt.yticks([])
plt.xticks([])
x = np.arange(len(val_acc_list))
plt.plot(x, val_acc_list)
plt.plot(x, results_train[key], "--")
i += 1
if i >= graph_draw_num:
break
plt.show()
=========== Hyper-Parameter Optimization Result =========== Best-1(val acc:0.86) | lr:0.00973910134615246, weight decay:1.9133285311075412e-06 Best-2(val acc:0.76) | lr:0.004918689185181991, weight decay:7.463532537095444e-05 Best-3(val acc:0.75) | lr:0.00670443929415795, weight decay:1.888078937716087e-06 Best-4(val acc:0.7) | lr:0.005138903888504449, weight decay:3.970219847905427e-06 Best-5(val acc:0.63) | lr:0.0033407275024504197, weight decay:2.3342109248992865e-07 Best-6(val acc:0.56) | lr:0.004593771226143216, weight decay:1.9235947438237254e-07 Best-7(val acc:0.54) | lr:0.003216805951950675, weight decay:2.8360071953636206e-08 Best-8(val acc:0.52) | lr:0.0035894326515725954, weight decay:4.959795017341513e-05 Best-9(val acc:0.47) | lr:0.003527225141063716, weight decay:5.9759668764489e-08 Best-10(val acc:0.45) | lr:0.0021604240316742355, weight decay:6.666610467090277e-05 Best-11(val acc:0.43) | lr:0.0031598459831883707, weight decay:2.4255506222427435e-06 Best-12(val acc:0.41) | lr:0.0023382296116417167, weight decay:9.741751600941648e-05 Best-13(val acc:0.4) | lr:0.0027220560455877983, weight decay:4.4506124086014257e-07 Best-14(val acc:0.37) | lr:0.0038414240221060466, weight decay:1.862336791659217e-08 Best-15(val acc:0.35) | lr:0.0032193045375685366, weight decay:1.078105609360396e-08 Best-16(val acc:0.34) | lr:0.001029921839026385, weight decay:6.716240046832123e-05 Best-17(val acc:0.32) | lr:0.0013673346166278937, weight decay:3.681300136171091e-05 Best-18(val acc:0.3) | lr:0.002646824918153067, weight decay:2.491192850859331e-05 Best-19(val acc:0.3) | lr:0.0018377793526536748, weight decay:2.539369989019738e-07 Best-20(val acc:0.27) | lr:0.0016958255128385691, weight decay:1.4719390569264908e-08
- Best-5 까지는 학습이 잘 되고 있어 Best-5까지의 값의 범위를 활용하는 식으로 범위를 좁혀간다.
In [14]:
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:80% !important;}</style>"))
728x90
'Book report > Deep Learning from Scratch' 카테고리의 다른 글
Chapter 5. 오차역전파법 (0) | 2021.10.09 |
---|---|
Chapter 4. 신경망 학습 (0) | 2021.10.06 |
Chapter 3. 신경망 (0) | 2021.08.16 |
Chapter 2. 퍼셉트론 (0) | 2021.08.15 |