딥러닝 옵티마이저: Adam(Adaptive Momentum)

DonghyunAnn·2022년 5월 21일
0
post-thumbnail
  • 윤덕호 저서 파이썬 날코딩으로 알고 짜는 딥러닝을 공부하고 개인적으로 정리한 글입니다.
  • 사진, 내용, 코드는 책을 참고한 것입니다. 코드는 오픈소스로 저저의 깃허브에 모두에게 공개되어있습니다.
  • 오픈된 코드중 5 chapter를 재활용했습니다
  • 기존에 다뤘더 MLP를 이미지 처리에 적용시킨 것 외에는 다른 내용이 없기때문에 블로그에 chapter5를 생략했습니다.
  • 본 페이지는 chapter6 입니다.

딥러닝에서의 복합 출력의 학습법

  • 복합 출력이란 두 가지 차원에서의 분류를 한꺼번에 수행하는것
    - 예: 이미지의 도메인과 품목이 무엇인지 동시 판별
    • 차원 축소로 순서쌍으로 만드는 방법도 있으나 출력이 과도하게 커지고, 각각의 특성 또한 포착하기 어려워짐
  • 딥러닝에서는 같은 퍼셉트론이라도 학습과정에 따라 역할이 달라짐
  • 동일한 구조의 신경망을 다른용도로 이용하기 위해서는 후처리 과정에서의 처리 방법만 변경하면 됨
    • 후처리 과정의 순전파: 손실함수 계산 | 역전파: 손실 기울기 계산
  • 신경망은 기본적으로 알맞은 크기의 출력 벡터를 생성하면 됨(예: 도메인 3 + 품목판별 31 = 34)
  • 하나의 신경망 출력을 복합 출력으로 처리하는 대신 필요한 출력별로 별도의 신경망을 구성하는 것은 출력 계층만 따졌을때는 별 차이가 없지만, 은닉 계층을 따졌을 때는 커다란 차이가 생김
    • 하나의 은닉계층은 모든 출력 계층에 영향을 주고 역전파 피드백을 받음
    • 출력상의 내용은 서로 연관되어 있는 경우가 많고 공통특성이 있을 수 있음
    • 각각의 출력별로 은닉계층을 하나 하나 학습하는 것보다 한번에 공통특성을 포착할 수 있게 학습 시키는게 계산량 절감 측면에서 효율적임
  • 정답:y1,y2>output1,output2>Loss=Loss1+Loss2y_1, y_2 -> output_1, output_2 -> Loss = Loss_1 + Loss_2
    • L을 줄이는 것은 L_1, L_2를 함께 줄이는 것과 같다고 볼 수 있음
    • 여기서 각자 다른 영역은 편미분과 같은 연산에서 서로 상수로 취급됨 : output_1의 손실기울기는 y_1의 후처리를 통해 진행하는데 이 때 y_2는 상수로 취급

Adam 알고리즘 (Adaptive momenteum)

  • 파라미터에 적용되는 실질적인 학습률을 개별 파라미터 별로 동적으로 조절해 경사하강법의 동작을 보완하고 학습 품질을 높여주는 방법
  • 경사하강법은 손실기울기와 학습률을 곱한 값을 파라미터에서 뺴주는 방식으로 학습
  • 아담 알고리즘은 모멘텀 개념이 추가되어 처리과정을 보완함
  • 모멘텀 정보와 2차 모멘텀 정보는 신경망 차원에서 관리되는 것이 아닌 개별 파라미터 수준에서 따로 계산되고 관리됨, 즉 파라미터 하나 당 모멘텀 정보와 2차 모멘텀 정보가 따라붙어 메모리 소비가 세배가 늘어남

딥러닝 옵티마이저 정리

Gradient Descent

Batch Gradient Descent

θt+1=θtηθJ(θ)J=i=1Nerrori\theta_{t+1} = \theta_t - \eta \nabla_{\theta} J(\theta) \\ J= \sum_{i=1}^{N} error_i

  • θ\theta를 인수로 갖는 목적함수 J(θ)J(\theta) 를 최소화 시키는 방법
    • 이렇다면 그냥 미분 계수가 0인 지점을 찾으면 된다고 볼 수 있지만
    • 실제 함수는 닫힌 형태가 아니고 복잡한 형태라서 그 근을 계산하기 어렵고
    • 미분계수를 컴퓨터로 구현하는 것보다 gradient descent를 컴퓨터로 구현하는게 편함
  • \nabla 는 나블라라고 읽으며 벡터의 미분을 의미
  • η\eta 에타는 learning rate로 gradient 반대 방향으로 얼마나 업데이트 할 것인지 결정함, 너무 크면 minimum을 지나쳐 발산할수도 있고, 너무 작으면 수렴 속도를 늦출 수 있음
  • 기본적인 Gradient Descent 는 Batch Gradient Descent인데 이는 한번의 업데이트를 위해 모든 데이터를 계산에 포함해야하기 때문에 속도가 느리다는 단점이 있음
Stochastic Gradient Descent (SGD)
  • 한 번의 파라미터 업데이트를 위해 하나의 훈련 데이터를 사용하는 것이 특징
    θt+1=θtηθJ(θ;xi,yi)\theta_{t+1} = \theta_t - \eta \nabla_{\theta} J(\theta;x_i,y_i)
  • 그만큼 빠르지만, 매 업데이트마다 들쭉날쭉한 gradient로 파라미터를 업데이트 함
Mini-batch Gradient Descent
  • 가장 흔하게 딥러닝에서 사용하는 방법
  • batch_size에 설정된 mini batch 데이터마다 손실함수를 만들어 gradient를 계산함
  • Batch Gradient보다 업데이트 속도가 빠르고 gradient 크기도 들쭉날쭉하지 않음
문제점
  • ml/dl 성능은 learning rate에 영향을 많이 받는데 SGD는 한번 정해진 learning rate를 학습상황에 따라 조정해주는 기능을 가지고 있지 않음
  • 코딩에서는 decay를 통해 줄여줄 수 있지만 특정 에포크마다 learning rate를 조정해줄만한 방법이 필요하기도 함
  • 모든 파라미터에 동일한 learning rate를 적용함
  • Saddle Point(안장점) 혹은 Local Minimum에서 빠져나오기 힘듬

Gradient Descent Optimization Algorithms

Momentum
  • 말그대로 관성을 의미함, 직관을 그대로 반영한 방법
  • 시기가 가까울수록 gradient의 영향력이 높으며 멀수록 γ\gamma배씩 감소시킴
    vt=γvt1+ηθtJ(θt)θt+1=θtvtv_t = \gamma v_{t-1} + \eta \nabla_{\theta_t} J(\theta_t) \\ \theta_{t+1} = \theta_t - v_t
  • 점화식의 원리로 시간이 작을수록 계속해서 γ\gamma가 곱해지는 것을 알 수 있음
  • 보통 γ\gamma는 0.9를 사용함
  • 이 개념을 통해 local minimum을 탈출할 수 있는 수단을 마련함
  • 모멘텀은 지수 가중평균을 베이스로 하고 있음
    • vt=βvt1+(1β)θtv_t=\beta v_{t-1} +(1-\beta)\theta_t
    • 여기서 1β1-\beta를 1에 근사하고 θ\theta에 그라디언트를 대입시키면 모멘텀임
    • vt(biascorrection)=vt(1βt)v_{t(biascorrection) = {v_t \over (1-\beta^t)}}: Adam 파트에서 bias-correct(바이어스 보정)을 사용하는데 이 또한 지수 가중평균에서 나온 개념임
Nestrov Accelerated Gradient (NAG)
  • 앞을 미리 보고 현재의 관성을 조절하여 업데이트 크기를 바꾸는 방식
  • 기존의 모멘텀
    • ηθtJ(θt)\eta \nabla_{\theta_t} J(\theta_t): 현재 parameter에서 gradient를 계산하고
    • vt=γvt1+ηθtJ(θt)v_t = \gamma v_{t-1} + \eta \nabla_{\theta_t} J(\theta_t): 모멘텀을 구하고
    • θt+1=θtvt\theta_{t+1} = \theta_t - v_t: 모멘텀만큼 업데이트를 해줌
  • NAG
    • ηθtJ(θtvt)\eta \nabla_{\theta_t} J(\theta_t - v_t): 현재 parameter로부터 vtv_t 만큼 떨어진 위치의 gradient를 계산
    • ηθtJ(θtγvt1)\eta \nabla_{\theta_t} J(\theta_t - \gamma v_{t-1}): 현재의 gradient를 구하기 전까지는 현재의 모멘텀을 알 수 없기 때문에 vtv_t대신 γvt1\gamma v_{t-1} 사용
    • vt=γvt1+ηθtJ(θtγvt1)v_t = \gamma v_{t-1} + \eta \nabla_{\theta_t} J(\theta_t - \gamma v_{t-1}):다음으로 현재 모멘텀을 계산해줌
    • θt+1=θtvt\theta_{t+1} = \theta_t - v_t: 모멘텀만큼 업데이트를 해줌
  • 이는 SGD가 관성에 의해 수렴지점을 벗어나는걸 방지해줌
AdaGrad
  • SGD, Momentum, NAG는 모든 파라미터에 대해서 같은 learning rate를 적용함
  • 인풋 변수 중 희소한 변수는 업데이트 횟수가 적기 때문에 한번 업데이트 할 때 수렴 지점까지 조금은 더 빠르게 접근할 필요가 있음
  • θt,i\theta_{t,i}: t번째 step의 i번 파라미터
  • gt,i=θtJ(θt,i)g_{t,i} = \nabla_{\theta_t}J(\theta_{t,i}): θt,i\theta_{t,i}에 대한 gradient 벡터
  • θt+1,i=θt,iηGt,ii+ϵgt,i\theta_{t+1,i} = \theta_{t,i} - {\eta \over \sqrt{G_{t,ii} + \epsilon}}g_{t,i}
    • Gt,ii=g1,i2+g2,i2+...+gt,i2G_{t,ii} = g_{1,i}^2 + g_{2,i}^2 +...+ g_{t,i}^2: t 시점까지의 θi\theta_i에 대한 gradient들의 제곱 총합을 갖는 대각행렬
    • ϵ\epsilon은 분모를 0으로 만들지 않기 위해 충분히 작은 값
  • Vector Form: θt+1=θtηGt+ϵgt\theta_{t+1} = \theta_{t} - {\eta \over \sqrt{G_{t} + \epsilon}} \odot g_{t}
    • \odot 은 matrix vector 곱셈을 의미하는 연산자
    • G는 대각행렬이기 때문에 역수취해서 곱한다고 보면됨
    • 업데이트 빈도가 높을수록 learning rate는 점점 작아짐
  • 하지만 learning rate의 과도한 손실은 문제가 될 수 있음
AdaDelta
  • 기존 문제
    • Adagrad의 iteration 마다 learning rate가 작아지는 문제
    • Hyperparameter인 learning rate의 필요
  • 첫번째 이슈를 해결하기위해서 분모를 대체함
    • 모든 gradient 정보를 저장하는 것이 아닌 지난 w개의 gradient 정보를 저장
    • E[g2]tE[g^2]_t: gradient 제곱의 합이 아닌 제곱에 대한 기댓값을 저장
    • E[g2]t=γE[g2]t1+(1γ)gt2E[g^2]_t = \gamma E[g^2]_{t-1} + (1-\gamma)g^2_t: Decaying Average of Squared Gradient
    • Δθt=ηE[g2]t+ϵgt=ηRMS[g]tgt\Delta \theta_t = -{\eta \over \sqrt{E[g^2]_t + \epsilon}}g_t= -{\eta \over RMS[g]_t}g_t: Root Mean Square로 식 간략화
  • 논문 저자는 first order method를 사용해 θ\theta를 업데이트 할 때 Δθ\Delta \theta와의 unit(단위)가 맞지 않음을 지적하며 second order method를 사용해서 unit을 통일 해야함을 강조함
    • θt\theta_t는 gradient θtJ(θt)\nabla_{\theta_t}J(\theta_t)를 포함하고 있음
    • 따라서 first order method를 적용했을 때 유닛이 θ\theta의 역수가 나옴 ΔθgJθ1unit(θ)\Delta\theta \propto g \propto {\partial J \over \partial\theta} \propto {1 \over unit(\theta)}
      • unit(x)unit(x)는 x의 단위를 의미 함
      • 가운데 손실함수J를 세타로 미분을 하는 식을 확장, 일반화 시켰을 때 고차 미분이 되고 그런 관점에서 비례식은 성립함
    • 여기에 second order method 행렬인 헤시안 행렬을 사용했을 때 unit을 맞춰줄 수 있음
      ΔθH1gJθ2Jθ2unit(θ)\Delta\theta \propto H^{-1}g \propto {{\partial J \over \partial\theta} \over {\partial^2 J \over \partial\theta^2}} \propto unit(\theta)
      - Newton's Method를 사용한 것임
      12Jθ2=ΔθJθ{1 \over {\partial^2 J \over \partial\theta^2}} = -{{\Delta \theta} \over {\partial J \over \partial\theta}}
      - Δθt=ηRMS[g]tg(t)\Delta \theta_t = -{\eta \over RMS[g]_t}g(t)로 앞에서 정의를 했는데 분모에는 이미 mean squared gradient가 들어갔고, unit(단위)로 계산되어야 하기 때문에 분자에 mean squared weight를 새롭게 구함, 여기서도 분모를 구한 과정과 비슷하게 전개하면
      E[Δθ2]t=γE[Δθ2]t1+(1γ)Δθt2RMS[Δθ]t=E[Δθ2]t+ϵE[\Delta \theta^2]_t = \gamma E[\Delta \theta^2]_{t-1} + (1-\gamma)\Delta \theta_t^2 \\ RMS[\Delta \theta]_t = \sqrt{E[\Delta \theta^2]_t + \epsilon}
      - 과 같이 나오고, 현재는 t를 업데이트 하기 전에 근사한 값으로 t-1시점의 값을 집어넣음
      Δθt=RMS[Δθ]t1RMS[g]tg(t)\Delta \theta_t = -{RMS[\Delta \theta]_{t-1} \over RMS[g]_t}g(t)
  • 최종적으로 업데이트 식은 다음과 같고 결국 learning rate를 필요로 하지 않게됨
    θt+1=θt+Δθt\theta_{t+1} = \theta_t +\Delta \theta_t
RMSProp
  • AdaGrad의 learning rate 소실 문제를 해결하기 위해 사용되며
  • AdaDelta에서 η\eta를 없애기 전 첫번째 식에서 γ\gamma를 0.9로 η\eta를 0.001로 제한
    E[g2]t=0.9E[g2]t1+0.1gt2θt+1=θtηE[g2]t+ϵgtE[g^2]_t = 0.9E[g^2]_{t-1} + 0.1g^2_t\\ \theta_{t+1} = \theta_t -{\eta \over \sqrt{E[g^2]_t + \epsilon}}g_t
Adam(Adaptive Moment Estimation)
  • AdaGrad, AdaDelta, RMSProp 처럼 각 파라미터마다 다른 크기의 업데이트를 적용하는 방법
  • 여기서 언급하는 모멘텀은 통계적 의미에서의 모멘텀임(E(X) = 1차 모멘텀, E(X^2) = 2차 모멘텀)
  • Exponentially Weighted Average(지수 가중 평균 or 지수 이동 평균) 사용
    -mtm_t는 1차 모멘텀에 대한 추정치 vtv_t는 2차 모멘텀에 대한 추정치에 가중평균 식 적용
    mt=β1mt1+(1β1)gtvt=β2vt1+(1β2)gt2m_t = \beta_1m_{t-1} +(1-\beta_1)g_t \\ v_t = \beta_2v_{t-1} +(1-\beta_2)g_t^2
  • mtm_tvtv_t의 초깃값을 0벡터로 줬을 때 학습 초기 구간에서 가중치가 0으로 수렴하는 경향이 있다. 이러한 편향을 잡아주기 위해 bias-correct 계산이 필요함
    mt^=mt1β1tvt^=vt1β2t\hat{m_t} = {m_t \over {1-\beta_1^t}}\\ \hat{v_t} = {v_t \over {1-\beta_2^t}}
  • 최종적으로 업데이트 식은 다음과같음
    θt+1=θtηvt^+ϵmt^\theta_{t+1} = \theta_t - {\eta \over {\sqrt{\hat{v_t}}+ \epsilon}}\hat{m_t}
AdaMax
  • Adam의 v_t텀에 다른 norm을 사용한 방법
    • Lass, Ridge 회귀에서 사용하는 노름(norm)개념을 사용하는 것
    • Adam에서는 g_t제곱을 사용하여, 최종식에는 루트가 있음, 즉 l2 norm
  • Adam의 l2 norm을 lp norm으로 일반화 시키면
    vt=β2pvt1+(1β2p)gtpv_t = \beta_2^pv_{t-1} + (1-\beta_2^p)|g_t|^p
    - p제곱이 아니고 단순 계수 노테이션
    -lp norm 값은 커질수록 불안하지만, ll_\inftynorm은 안정적인 양상을 띈다고 함, 따라서 AdaMax에서는 ll_\inftynorm을 채택
    ut=β2vt1+(1β2)gt=max(β2vt1,gt)u_t = \beta_2^\infty v_{t-1} + (1-\beta_2^\infty)|g_t|^\infty = max(\beta_2v_{t-1},|g_t|)
  • 따라서 최종 업데이트 식은 다음과 같음
    θt+1=θtηutmt^\theta_{t+1} = \theta_t - {\eta \over u_t}\hat{m_t}
NAdam(Nesterov-accelerated Adaptive Memoment)
  • 기존의 NAV를 변형함
    • 모멘텀 mt1m_{t-1}을 gradient 업데이트와 θ\theta업데이트에 모두 사용
    • 업데이트할때 mt1m_{t-1} 대신 mtm_{t}를 사용함
      gt=θtJ(θ)mt=γmt1+ηgtg_t = \nabla_{\theta_t} J(\theta) \\ m_t = \gamma m_{t-1} + \eta g_t
    • 위 까지는 NAV와 일치하지만 γmt1\gamma m_{t-1} 대신 γmt\gamma m_{t}를 사용하여 mt+1m_{t+1}, 즉 미래 momentum을 사용한 효과를 보임
      θt+1=θtγmt+ηgtθt+1=θtmt+1\theta_{t+1} = \theta_t - \gamma m_{t} + \eta g_t \\ \theta_{t+1} = \theta_t - m_{t+1}
  • 먼저 Adam 업데이트식은 다음과 같다 (v_t는 생략)
    mt=β1mt1+(1β1)gtmt^=mt1β1tθt+1=θtηvt^+ϵmt^=θtηvt^+ϵ(β1mt11β1t+(1β1)gt1β1t)m_t = \beta_1m_{t-1} +(1-\beta_1)g_t \\ \hat{m_t} = {m_t \over {1-\beta_1^t}}\\ \theta_{t+1} = \theta_t - {\eta \over {\sqrt{\hat{v_t}}+ \epsilon}}\hat{m_t} = \theta_t - {\eta \over {\sqrt{\hat{v_t}}+ \epsilon}}({\beta_1m_{t-1} \over {1-\beta_1^t}}+{(1-\beta_1)g_t\over {1-\beta_1^t}})
  • 여기서 앞에서 변형된 NAV에서 언급한 것과 같이 mt1m_{t-1} 대신 mtm_{t} 사용
    θt+1=θtηvt^+ϵ(β1mt1β1t+(1β1)gt1β1t)\theta_{t+1} = \theta_t - {\eta \over {\sqrt{\hat{v_t}}+ \epsilon}}({\beta_1m_{t} \over {1-\beta_1^t}}+{(1-\beta_1)g_t\over {1-\beta_1^t}})

출처: https://hiddenbeginner.github.io/deeplearning/2019/09/22/optimization_algorithms_in_deep_learning.html

구현 코드 및 Annotation

복합 출력의 처리 방법: 오피스31 다차원 분류 신경망

  • 시각화와 데이터셋을 불러오는 부분은 생략
  • Data : 오피스31
    • 4652장의 사무용품 이미지
    • 사진 수집방법에 따른 도메인
    • 담긴 내용이 어디 품목에 속하는지에 대한 이중 레이블링
    • 컴퓨터 비전 분야에서의 전이학습 연구용
      • 전이학습이란
        • 한 도메인에서 학습시킨 과를 다른 도메인에서 활용하여 학습 효과를 높이는 학습 기법
        • 수집된 데이터에 정답을 표시하는 레이블링에 소비되는 작업량을 줄이는데 효과적임

  • Call Chapter5 mlp_model
%run ../chap05/mlp_model.ipynb

  • Adam Model 클래스 선언
class AdamModel(MlpModel):
    def __init__(self, name, dataset, hconfigs):
        # 모델 객체를 생성하고나서 exec_all() 메서드 호출 전에 use_adam을 True 로 변경
        self.use_adam = False
        super(AdamModel, self).__init__(name, dataset, hconfigs)

  • 신경망 역전파 메서드 정의
def adam_backprop_layer(self, G_y, hconfig, pm, aux):
    x, y = aux
    
    # hconfigs는 은닉 계층 구성 정보
    # hconfig 는 결국 가중치와 편차로이루어진 은닉층 그 자체임 = 행렬
    if hconfig is not None: G_y = relu_derv(y) * G_y
    
    g_y_weight = x.transpose()
    g_y_input = pm['w'].transpose()
    
    G_weight = np.matmul(g_y_weight, G_y)
    G_bias = np.sum(G_y, axis=0)
    G_input = np.matmul(G_y, g_y_input)
    
    self.update_param(pm, 'w',  G_weight)
    self.update_param(pm, 'b',  G_bias)

    return G_input

AdamModel.backprop_layer = adam_backprop_layer

  • 파라미터 수정 메서드 정의
def adam_update_param(self, pm, key, delta):
    # delta = 파라미터의 손실 기울기 
    if self.use_adam:
        # self.use_adam = True 일경우 메서드 활성화
        delta = self.eval_adam_delta(pm, key, delta)
    
    pm[key] -= self.learning_rate * delta
        
AdamModel.update_param = adam_update_param

  • 기울기값 보정 메서드 정의
def adam_eval_adam_delta(self, pm, key, delta):
    # 모멘텀과 2차 모멘텀 값을 이동평균 방식으로 유지함
    # 미니배치 하나를 처리하는 단계마다 기존 값과 새 값 사이의 가중 평균을 구함
    # 이 때 적용할 비율을 ro_1, ro_2
    ro_1 = 0.9
    ro_2 = 0.999
    epsilon = 1.0e-8
    
    # 모멘텀, 2차 모멘텀, 처리 횟수 : 아담 알고리즘에서 지속적으로 관리해야 하는 값
    skey, tkey, step = 's' + key, 't' + key, 'n' + key
    # 처음 호출되어 버퍼 공간이 존재하지 않을 때
    if skey not in pm:
        pm[skey] = np.zeros(pm[key].shape)
        pm[tkey] = np.zeros(pm[key].shape)
        pm[step] = 0
    
    # 이동평균, 기존 값과 새 값의 가중 평균 계산 -> 모멘텀 값
    s = pm[skey] = ro_1 * pm[skey] + (1 - ro_1) * delta
    t = pm[tkey] = ro_2 * pm[tkey] + (1 - ro_2) * (delta * delta)
    
    # step이 작을 때 모멘텀, 2차 모멘텀 증폭시키는 식
    pm[step] += 1
    s = s / (1 - np.power(ro_1, pm[step]))
    t = t / (1 - np.power(ro_2, pm[step]))
    
    return s / (np.sqrt(t)+epsilon)

AdamModel.eval_adam_delta = adam_eval_adam_delta

내용 및 코드 정리 ipynb 및 실행 ipynb 링크

profile
운동을 좋아하는 데이터 사이언티스트

0개의 댓글