Deep Learning

[Deep Learning]Optimizer 매개변수 갱신

heedy 2022. 11. 13. 15:09
728x90

매개변수 갱신

  • 신경망 학습의 목적은 손실 함수의 값을 가능한 한 낮추는 매개변수를 찾는 것
  • 이것은 매개변수의 최적값을 찾는 문제이며, 이러한 문제를 푸는 것을 최적화라 함.

확률적 경사 하강법(SGD)

SGD

class SGD:
  def __init__(self, lr = 0.01):
    self.lr = lr

  def update(self, params, grads):
    for key in params.keys():
      params[key] -= self.lr * grads[key]
  • optimizer는 '최적화를 행하는 자'라는 뜻
  • 매개변수 갱신은 optimizer가 책임지고 수행하니 optimizer에 매개변수와 기울기 정보만 넘겨주면 됨.

SGD의 단점

함수의 그래프와 등고선

 

[SGD에 의한 최적화 갱신 경로: 최솟값인 (0,0)까지 재그재그로 이동하니 비효율적이다.

  • SGD의 단점은 비등방성 함수(방향에 따라 성질, 기울기가 달라지는 함수)에서는 탐색 경로가 비효율적임.
  • SGD의 단점을 개선해주는 방법: 모멘텀, AdaGrad, Adam

모멘텀(Momentum)

  • 모멘텀은 운동량을 뜻하는 단어로, 물리와 관계가 있음.
  • 모멘텀 수식은 다음과 같이 쓸 수 있음.

  • W는 갱신할 가중치 매개변수, 손실함수에 대한 기울기, η는 학습률임. v는 물리에서 말하는 속도에 해당됨.
  • 모멘텀은 공이 그릇의 바닥을 구르는 듯한 움직임을 보여줌.
  • αv항은 물체가 아무런 힘을 받지 않을 때 서서히 하강시키는 역할. 물리에서의 지면 마찰이나 공기 저항에 해당
# 모멘텀 구현

import numpy as np

class Momentum:
  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]

[모멘텀에 의한 최적화 갱신 경로]

Momentum

  • 모멘텀의 갱신 경로는 공이 그릇 바닥을 구르듯 움직임.
  • SGD와 비교하면 '지그재그 정도'가 덜함. 이는 x축의 힘은 아주 작지만 방향은 변하지 않아서 한 방향으로 일정하게 가속하기 때문임.
  • 거꾸로 y축의 힘은 크지만 위아래로 번갈아 받아서 상충하여 y축 방향의 속도는 안정적이지 않음.
  • 전체적으로 SGD보다 x축 방향으로 빠르게 다가가 지그재그 움직임이 줄어듦.

AdaGrad

  • 신경망 학습에는 학습률 값이 중요함. 이 값이 너무 작으면 학습 시간이 너무 길어지고, 반대로 너무 크면 발산하여 학습이 제대로 이루어지지 않음.
  • 이 학습률을 정하는 효과적 기술로 학습을 진행하면서 학습률을 점차 줄여가는 학습률 감소가 있음.
  • 이를 더욱 발전시킨 것이 AdaGrad임. AdaGrad는 '각각의 매개변수에 맞는 맞춤형' 값을 만들어줌.
  • AdaGrad는 개별 매개변수에 적응적으로 학습률을 조정하면서 학습을 진행함.
  • AdaGrad의 갱신 방법 수식

  • AdaGrad는 과거의 기울기를 제곱하여 계속 더해감. 그래서 학습을 진행할수록 갱신 강도가 약해짐. 실제로 무한히 계속 학습한다면 어느 순간 갱신량이 0이 되어 전혀 갱신되지 않게 됨. 이 문제를 개선한 기법으로 RMSProp이라는 방법이 있음. RMSProp은 과거의 모든 기울기를 균일하게 더해가는 것이 아니라, 먼 과거의 기울기는 서서히 잊고 새로운 기울기 정보를 크게 반영함. 이를 지수이동평균이라 하여, 과거 기울기의 반영 규모를 기하급수적으로 감소시킴.
# AdaGrad 구현

class 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)
  • 여기에서 주의할 것은 마지막 줄에서 1e-7이라는 작은 값을 더하는 부분임. 이 작은 값은 self.h[key]에 0이 담겨있다 해도 0으로 나누는 사태를 막아줌.

    [AdaGrad]에 의한 최적화 갱신 경로

AdaGrad



그림을 보면 최솟값을 향해 효율적으로 움직임. y축 방향은 기울기가 커서 처음에는 크게 움직이지만, 큰 움직임에 비례해 갱신 정도도 큰 폭으로 작아지도록 조정됨. 그래서 y축 방향으로 갱신 강도가 빠르게 약해지고, 지그재그 움직임이 줄어듦.


Adam

  • 모멘텀은 공이 그릇 바닥을 구르는 듯한 움직임을 보이고, AdaGrad는 매개변수의 원소마다 적응적으로 갱신 정도를 조정함. 이 두 기법을 융합한 것이 Adam임.
  • 매개변수 공간을 효율적으로 탐색해주고, 하이퍼파리머트의 '편향 보정'이 진행됨.

[Adam에 의한 최적화 갱신 경로]

Adam


- Adam 갱신 과정도 그릇 바닥을 구르듯 움직임. 모멘텀과 비슷한 패턴인데, 모멘텀 때보다 공의 좌우 흔들림이 적음. 이는 학습의 갱신 강도를 적응적으로 조정해서 얻는 혜택임.


Optimizer 비교

# optimizer 비교
import sys, os
sys.path.append(os.pardir)
import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict
from common_optimizer import *


def f(x, y):
    return x**2 / 20.0 + y**2


def df(x, y):
    return x / 10.0, 2.0*y

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)

idx = 1

for key in optimizers:
    optimizer = optimizers[key]
    x_history = []
    y_history = []
    params['x'], params['y'] = init_pos[0], init_pos[1]
    
    for i in range(30):
        x_history.append(params['x'])
        y_history.append(params['y'])
        
        grads['x'], grads['y'] = df(params['x'], params['y'])
        optimizer.update(params, grads)
    

    x = np.arange(-10, 10, 0.01)
    y = np.arange(-5, 5, 0.01)
    
    X, Y = np.meshgrid(x, y) 
    Z = f(X, Y)
    
    # for simple contour line  
    mask = Z > 7
    Z[mask] = 0
    
    # plot 
    plt.subplot(2, 2, idx)
    idx += 1
    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()

optimizer 비교

# mnist 데이터셋으로 optimizer 비교

import os
import sys
sys.path.append(os.pardir)
import matplotlib.pyplot as plt
from mnist import load_mnist
from common_util import smooth_curve
from common_multi_layer_net import MultiLayerNet
from common_optimizer import *



(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

train_size = x_train.shape[0]
batch_size = 128
max_iterations = 2000


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] = []    


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))


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()

- 참고: 밑바닥부터 시작하는 딥러닝1
- notobook ipynb file: https://github.com/heejvely/Deep_learning/blob/main/%EB%B0%91%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0_%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94_%EB%94%A5%EB%9F%AC%EB%8B%9D_1/%ED%95%99%EC%8A%B5%20%EA%B4%80%EB%A0%A8%20%EA%B8%B0%EC%88%A0%EB%93%A4.ipynb

 

GitHub - heejvely/Deep_learning: deep learning 기초 공부

deep learning 기초 공부. Contribute to heejvely/Deep_learning development by creating an account on GitHub.

github.com

 

728x90