#wannabeeeeeee the best DataScientist
Chapter 6. 학습 관련 기술들 본문
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 |