[6] 옵티마이저
6. Adam
class Adam: # 전개를 하고 묶는 작업을 거쳐야 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 # B1 가중치, 관성과 미분치 미분할 때 필요 mn = B1mn-1 + (1 - B1) * df(xn)
self.beta2 = beta2 # B2 가중치, 미분치 좌표별 제곱할 때 필요 vn = B2vn-1 + (1-B2)*df(xn)*(df(xn)
self.iter = 0 # 보정작업에서 베타의 n 승해준 값 빼줘서 0에 편향되는 것을 막아줬었다. 그 n
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)
# 학습률 * 1/루트 (new)vn * (new)mn
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])
# m의 점화식 B1mn-1 + (1 - B1) * df(xn)
self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])
# v의 점화식 vn = B2vn-1 + (1-B2)*df(xn)*(df(xn)
params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
# Xn - 학습률 * 1/ 루트 (new)vn * (new)mn 로 업데이트 해라.
#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)
6. Adam
Momentum + RMSProp
= 기존의 '관성'을 반영 + 내분점을 반영 ( 현재에 치중할 것인가, 과거에 치중할 것인가)
관성 반대방향, 중력 반대방향을 반영하려고 각각 - 를 붙였다.
m = 관성 역할, 초기값 0
b = 감마 역할 (내분점), 초기값 0, 보통 1에 가까운 아주 큰 수로 감마를 둔다. 그래서 시작점에서 아주 가깝게, 점이 이동한다.(과거에 치중하고 있으니까 초기값 0에 편향되는 경향) -> 보정작업이 필요함.
mn = B1mn-1 + (1 - B1) * df(xn)
가중치 B2 등장
vn = B2vn-1 + (1-B2)*df(xn)*(df(xn)
보정작업
(new)mn = mn/1-b1^n+1 (n이 진행될수록 점점 더 작아지는. 원점에 가까울 수록 더 멀리 보내는 역할)
(new)vn = vn/1-b2^n+1
6-1 점화식
Xn+1 = Xn - 학습률 * 1/ 루트 (new)vn * (new)mn
매우 어렵다. 연습문제 풀면서 익혀볼래.
연습문제 1 :
f(x, y) = xy
x0 = (1, 2)
학습률 = 1
B1= 1/2 , B2= 1/2 (과거, 현재 똑같이 치중하겠다는 말)
df(x, y) = (y, x)
df(1, 2) = (2, 1)
m0 = 1/2 * m-1 + 1/2 * (2, 1) -> m-1 초기값 (0,0)
m0 = (1, 1/2) -> 보정작업 거쳐야 함
(new)m0 = m0/1/2 = 2 * (1, 1/2) = (2, 1)
v0 = 1/2 * v-1 + 1/2 * (4, 1) -> v-1 초기값 (0,0)
v0 = (2, 1/2) -> 보정작업 거쳐야 함
(new)v0 = (2, 1/2)/1/2 = (4, 1)
X1 = (1, 2) - 1/ 루트 (4, 1) * (2, 1)
X1 = (1, 2) - (1/2, 1) * (2, 1) = (0, 1)
df(X1) = (1, 0)
m1 = 1/2 * (1, 1/2) + 1/2 * (1, 0) = (1/2, 1/4) + (1/2, 0) = (1, 1/4)
(new)m1 = (1, 1/4)/1- 1/4 = (1, 1/4)/3/4 = (4, 1)/3 = (4/3, 1/3)
v1 = 1/2 * (2, 1/2) + 1/2*(1, 0) =(1, 1/4)+ (1/2 ,0) = (3/2 , 1/4)
(new)v1 = v1/1-1/4 = (3/2 , 1/4)/3/4 = (2, 1/3)
X2 = (0, 1) - 1/ 루트(2, 1/3) *(4/3, 1/3) =(0, 1) - (1/루트2, 루트3) * (4/3 , 1/3 ) = (-2루트2/3, 1-루트3/3)
코드로 풀어보았다.
#Adam 1번문제
BTS = Adam(1., 1/2, 1/2)
params= {'x':1.,'y':2.}
grads = {'x':2.,'y':1.}
BTS.update(params,grads)
print(BTS.m)
print(BTS.v)
print(params)
grads = {'x':1.,'y':0}
BTS.update(params,grads)
print(BTS.m)
print(BTS.v)
print(params)
연습문제2:
f(x, y) = x2+xy
x0 = (1, 1)
학습률 = 1
B1 = 1/2 B2 =1/2
df(x, y) = (2x + y, x)
df(1, 1) = (3, 1)
m0 = 1/2 * (0, 0) + 1/2 * (3, 1) = (3/2, 1/2)
v0 = 1/2 * (0, 0) + 1/2 *(9, 1) = (9/2, 1/2)
(new)m0 = (3/2, 1/2)/1/2 = (3, 1)
(new)v0 = (9/2, 1/2)/1/2 = (9, 1)
X1 = (1, 1) - 1/ 루트 (9, 1) * (3, 1) = (1, 1) - (1/3, 1) * (3, 1) = (0, 0)
df(X1) = (0, 0)
m1 = 1/2 * (3/2, 1/2) = (3/4, 1/4)
v1 = 1/2 * (9/2, 1/2) = (9/4, 1/4)
(new)m1 = (3/4, 1/4)/3/4 = (1, 1/3)
(new)v1 = (9/4, 1/4)/3/4 = (3, 1/3)
6-1 점화식
X2 = (0, 0) - 1/ 루트 (3, 1/3) * (1, 1/3) = (-루트3/3, -루트3/3)
#Adam 2번문제
BTOB = Adam(1., 1/2, 1/2)
params= {'x':1.,'y':1.}
grads = {'x':3.,'y':1.}
BTOB.update(params,grads)
print(BTOB.m) # (3/2, 1/2)
print(BTOB.v) # (9/2, 1/2)
print(params)
grads = {'x':0,'y':0}
BTOB.update(params,grads)
print(BTOB.m) # (3/4, 1/4)
print(BTOB.v) # (9/4, 1/4)
print(params)
print(-np.sqrt(3)/3)
7.옵티마이저 벤치마크
경사하강법 - 가장 가파른 방향으로 눈 감고 이동
Momentum - 관성 + 중력
NAG - 모멘텀 스텝만큼 먼저 이동
AdaGrad - 스텝, 변수마다 학습률 조정
RMSProp - 정체되지 않게끔 가중치 평균(내분) 해 학습률 보정
Adam - Momentum + RMSProp
7-1 반복문 사용해서 각각 그래프 그려보기
optimizer_compare_naive.py
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict
from optimizer import *
def f(x, y):
return x**2 / 20.0 + y**2 # f(x, y) = 1/20 * x^2 + y^2
def df(x, y):
return x / 10.0, 2.0*y # df(x, y) = (x/10 , 2y)
init_pos = (-7.0, 2.0) # 시작점
params = {}
params['x'], params['y'] = init_pos[0], init_pos[1]
grads = {}
grads['x'], grads['y'] = 0, 0
optimizers = OrderedDict()
optimizers["SGD"] = SGD(lr=0.95) # 이 것들로 이동해서 그려보라는 뜻
optimizers["Momentum"] = Momentum(lr=0.1) # 누락된 정보들은 디폴트값으로 하겠다는 말.
optimizers["AdaGrad"] = AdaGrad(lr=1.5) # 유독 학습률이 큰 이유 : 처음은 보폭을 크게 점점 잰걸음으로...
optimizers["Adam"] = Adam(lr=0.3) # 각각 클래스로 만든 인스턴스를 밸류값으로 취함
optimizers["RMSprop"] = RMSprop(lr=0.3)
optimizers["Nesterov"] = Nesterov(lr=0.3)
idx = 1 # 1씩 늘어날 것이다
plt.figure(figsize = (10, 15))
for key in optimizers:
optimizer = optimizers[key] # SGD, Momentum 등 차례로 키값에 들어갈거임
x_history = []
y_history = []
params['x'], params['y'] = init_pos[0], init_pos[1] # init_pos = (-7.0, 2.0) 의 [0], [1]
for i in range(30): # 30 걸음 걷겠다
x_history.append(params['x']) # 점의 x좌표 기록하겠다
y_history.append(params['y']) # 점의 y좌표 기록하겠다
grads['x'], grads['y'] = df(params['x'], params['y']) #미분값 딕셔너리 밸류값으로 정의해주기
optimizer.update(params, grads)
x = np.arange(-10, 10, 0.01) # 범위 -10 ~ 10이고 공차가 0.01인 등차수열
y = np.arange(-5, 5, 0.01)
X, Y = np.meshgrid(x, y) # x는 행에 붙이고, y는 열에 붙이겠다는 말.
Z = f(X, Y)
# 외곽선 단순화
mask = Z > 7 # 함수값이 7보다 크면 True, 아니면 False.
Z[mask] = 0 # True 부분은 날려버리고 등위선 그리지 않는다.
# 그래프 그리기
plt.subplot(3, 2, idx)
idx += 1 # 인덱스 늘어나는 순서대로(key 순서대로) 그림 그린다.
plt.plot(x_history, y_history, 'o-', color="red") # 점좌표 모아놓은 리스트를 이용해 그림을 그린다.
plt.contour(X, Y, Z) #등위선 그려라
plt.ylim(-10, 10)
plt.xlim(-10, 10)
plt.plot(0, 0, '+')
#colorbar()
#spring()
plt.title(key)
plt.xlabel("x")
plt.ylabel("y")
plt.show()
7-2 np.meshgrid(x, y) 보충설명
import numpy as np
x= np.array([1, 2, 3])
y= np.array([4, 5, 6])
X, Y = np.meshgrid(x, y)
print(X)
# [[1 2 3]
# [1 2 3]
# [1 2 3]]
print(Y)
# [[4 4 4]
# [5 5 5]
# [6 6 6]]
Z = X**2 + Y**2
print(Z) # 행렬 제곱이 아닌, 원소별 제곱
# [[17 20 25]
# [26 29 34]
# [37 40 45]]
7-3 6가지 그래프 mnist 같이 경주시켜보기.
optimizer_compare_mnist.py
# coding: utf-8
import os
import sys
sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import matplotlib.pyplot as plt
from mnist import load_mnist
from util import smooth_curve
from multi_layer_net import MultiLayerNet
from optimizer import *
# 0. MNIST 데이터 읽기==========
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
train_size = x_train.shape[0] # 60000개 데이타 * 784
batch_size = 128
max_iterations = 2000 # 2000 걸음으로 내려가겠다.
# 1. 실험용 설정==========
optimizers = {} #각각 변수별 인스턴스 밸류로 취함
optimizers['SGD'] = SGD() # 784- 100 - 100 - 100- 100 -10
optimizers['Momentum'] = Momentum() # 784- 100 - 100 - 100- 100 -10
optimizers['AdaGrad'] = AdaGrad() # 784- 100 - 100 - 100- 100 -10
optimizers['Adam'] = Adam() # 784- 100 - 100 - 100- 100 -10
optimizers['RMSprop'] = RMSprop() # 784- 100 - 100 - 100- 100 -10
optimizers['Nesterov'] = Nesterov()
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: # 100의 배수가 될 때마다 출력해주세요. ( 잘 되고 있는지 확인을 위해서 )
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", "RMSprop": "v", "Nesterov": "^"}
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()
8. Xavier/ He 초기값
옵티마이저 6가지의 방법은 산을 어떤 효율적인 방법으로 내려갈지 고르는 것.
가중치의 초기값 설정은 초기값 설정은 어느지점에서 출발을 해야 산을 잘 내려갈 수 있을지, 출발지점을 선택하는 것.
표준편차 1로 설정하고 히스토그램 그렸더니, 아파인 시그모이드 거쳐서 양쪽으로 찰딱 붙은 형태의 그래프.
표준편차 0.1로 설정하고 그렸더니, 아파인 시그모이드 거쳐서 중간으로 완전 쏠린 형태의 그래프.
적절한 값 설정이 아주 중요하겠네.
weight_init_activation_histogram.py
'2022.01. > 코딩 수업' 카테고리의 다른 글
02.11 (0) | 2022.02.11 |
---|---|
02.04 옵티마이저 (0) | 2022.02.07 |
01.28 (0) | 2022.01.28 |
01.26 softmax(확률), 추론(배치처리), 손실함수 (0) | 2022.01.26 |
01.25 퍼셉트론, 신경망 (0) | 2022.01.25 |