AI/EECS 498-007 (598-005)

[EECS 498-007 / 598-005] Lecture 4. Optimization

성실한 당근농부 2024. 1. 16. 14:51
해당 포스트는 2019년 강의 영상과 자료를 보고 작성하였으며, 2022년 강의 자료를 통해 보강하였습니다.
해당 포스트에 포함된 이미지는 2022년 강의 자료에서도 확인 가능합니다.

 

 

 

Optimization

손실함수를 최소화 하는 가중치 행렬 w*를 찾는 문제는, 최적화 문제임

이때 우리가 다룰 손실함수 L(w)는 가중치 행렬을 입력으로 받아 스칼라 손실을 출력하는 함수

 

 

최적화 문제를 직관적으로 이해할 때는 눈을 감은 채 사진과 같은 고차원의 풍경을 탐색하는 것으로 이해해볼 수 있음

  • 땅의 각 지점(x, y 좌표)은 가중치 행렬 w의 다른 값
  • 해당 지점의 높이는 손실함수 L(w)의 값이 됨

 

최적화 과정에서 우리는 어디가 바닥인지 정확히 알 수 없지만 탐색을 통해 최저점을 찾아내야함 → How?

 

선형 회귀와 같은 특정 유형의 최적화 문제에서는 목표점(바닥, 최저점)에 대한 명시적인 방정식을 작성 가능

  • 선형 회귀에서는 선형 방정식을 작성할 수 있고, 간단한 미분을 통해 w*를 찾을 수 있음

 

하지만 일반적으로 이와 같은 방식은 동작하지 않음반복적인 개선방식을 통해 목표에 접근 가능

 

더보기

[WHY] 이와 같은 방식은 동작하지 않음
우리는 고차원의 데이터를 사용해 모델을 만들고자 함

  • 이 경우에는 목표점에 대한 명시적인 방정식 작성 불가능
  • 작성하더라도 미분 과정이 매우 복잡함

 


 

반복적인 개선 방식 Idea #1: Random Search (bad idea!)

접근법 첫번째, Random Search는 매우 끔찍한 방법이지만 생각해볼만한 방법임

  1. 가중치 w를 무작위로 여러 번 생성
  2. 학습 세트(X_train, Y_train)를 가지고 1번에서 생성한 w에 대한 Loss를 평가
  3. 2번 과정에서 찾아낸 가장 낮은 손실 값을 추적

 

사진 속 예시 코드에서는 CIFAR10 데이터셋/선형 모델을 통해 학습하고 있는데, 무작위 탐색을 통한 정확도는 15.5%

매우 비효율적인 알고리즘임에도 불구하고 15.5%의 정확도를 보임 (SOTA 모델의 성능은 95% accuracy) 

 

반복적인 개선 방식 Idea #2: Follow the slope

접근법 두번째, 풍경의 경사를 따라 내려가기 (우리가 실제로 사용해볼 아이디어)

현재 위치에서 어떤 방향으로 지면이 경사져있는지, 주변 풍경을 느낄 수 있다면, 해당 지역 정보를 가지고 가장 경사진 곳으로 (기울기가 가장 크게 감소하는 방향으로) 조금씩 걸어가는 전략을 취해볼 수 있음 → 이 과정을 반복함으로써 최저점에 도달가능!

매우 간단한 알고리즘이지만 실제로 잘 동작함

 

 

단일 스칼라 값을 입력하고 출력하는, 단일 스칼라 함수(1차원)에서는 도함수를 정의해 임의 지점에서의 기울기(이하 gradient)를 구할 수 있음

  • 어떤 지점에서의 x에 변화량에 따른 y의 변화량을 뜻함
  • 1차원 함수에서의 gradient,또는 도함수, 미분의 개념
  • 이는 다차원으로 확장 가능하며, 이때의 gradient는 각 차원에 따른 (편미분들의) 벡터!

 

어떤 특정 방향에서의 기울기는 해당 방향과 gradient의 내적이므로 양수 기울기는 증가 방향, 음수 기울기는 감소 방향을 나타냄

우리는 가장 급격하게 기울기가 감소하는 지점에 관심이 있으므로, negative gradient 방향을 택하면 최저점을 향해 갈 수 있게 됨

 

➡️ 그렇다면 우리는 어떻게 임의의 입력(벡터)에 대한 임의의 함수의 gradient를 계산할 수 있는가?

 


 

 

 

  • 왼쪽: 현재의 가중치 행렬 w
  • 오른쪽: 해당 가중치 행렬 w에 대한 Loss의 gradient를 계산
    • Loss는 단일 스칼라 값이므로 가중치 행렬 w에 대한 Loss의 gradient도 가중치 행렬과 동일한 형태가 됨

 

 

 

w에 대한 Loss의 gradient를 계산하기 위해서 우리는 아래와 같은 과정을 시도할 수 있음

 

  1. 현재 가중치 행렬 w의 Loss을 구함
  2. 가중치 행렬의 n번째 슬롯(여기서는 첫 번째)을 미세하게 증가시킨 후 새로운 Loss을 구함
  3. 도함수의 극한 개념을 활용해 gradient의 근사치를 구함

 

이 과정을 모든 슬롯에 대해 반복할 수 있으며, 이러한 방법을 Numeric Gradient라고 함

  • Numeric Gradient의 단점
    • 매우 느림: O(#-dimensions)
      • 지금의 예시에서는 계산 비용이 높지 않지만, 대규모 신경망으로 이동했을 경우에는?
      • 계산 과정에서 가중치 행렬의 각 슬롯에 대해 별도의 계산 과정이 필요함
    • Approximate(근사치)
      • 계산 과정에서 우리는 도함수의 극한 개념을 활용해 gradient의 근사치를 구함
      • 이때 사용하는 h는 사용자가 정한 임의의 값
  • 이러한 단점에도 분명한 장점은 존재
    • 대규모 신경망 시스템에서 gradient를 계산하려고 할 때, 실제로 무슨 일이 일어나고 있는 건지에 대한 이해를 도와줌

 

 

또 다른 방법은 Analytic Gradient

: Loss 함수와 가중치 행렬 w에 대한 Loss의 Gradient를 직접 계산하는 식을 적어낼 수 있음 (미분 공식 이용)

Newton과... Leibniz의 도움을 받아...

 

 

 

따라서 dL/dW를 표현할 수 있는 함수를 유도하고, 이를 통해 w에 대한 Loss의 gradient 값을 계산할 수 있다는 소리!

+ 실제로 우리는 이 과정에서 역전파를 이용해 계산하게 될 것 (6강에서 더 자세히 설명)

 

Computing Gradients

우리는 Loss 함수를 최소화 시키기 위해 gradient 정보를 사용할 것임 → 반복을 통한 개선

Numeric gradient Analytic gradient
approximate exact
slow fast
easy to write error-prone (오류가 발생하기 쉬움)

 

실제로는 Analytic gradient를 사용하지만 error-prone한 특성을 가지고 있으므로, Numerical gradient를 통해 gradient를 올바르게 구했는지 확인(use the numeric gradient as a debugging tool)

→ 대부분 두 가지 방법을 모두 사용, Gradient check

 

파이토치에서는 아래와 같은 함수를 사용해 Gradient check가 가능함

  • input: 함수식
  • output: bool

고차 미분에는 gradgradcheck() 사용 → 1차를 포함한 헤시안, 자코비언 행렬의 값까지 검증

 


 

Gradient Descent(경사 하강법)

경사 하강법은 임의의 지점에서 시작해 local gradient를 계산하고, 그 방향으로 업데이트 하는 과정을 반복하는 것으로 매우 간단하게 구현 가능

# Vanilla gradient descent
w = initalize_weights() ### 가중치 초기화
for t in range(num_steps): ### 일정 횟수만큼 반복
    dw = compute_gradient(loss_fn, data, w) ### gradient를 계산한 후
    w -= learning_rate * dw  ### negative gradient 방향으로 업데이트

 

하이퍼 파라미터

  • Weight initialization method: 가중치 초기화 방법
    • 직관적으로 우리는 가중치를 임의의 값으로 초기화하고 싶어하지만, 이 임의의 값을 정하는 것에 대한 정확한 매커니즘이나 값의 분포가 경사 하강법의 과정에서 매우 중요하게 동작한다는 것이 밝혀짐 → 가중치를 초기화하는 올바른 방법에 대해서는 추후 강의에서 더 설명 예정
  • Number of steps: 경사 하강법의 실행 횟수
    • 여러 가지의 중단 방법이 있지만 가장 일반적인 것은 단순히 일정 횟수만큼만 실행하는 것 
    • 일반적으로 더 많은 반복을 실행하는 것이 더 잘 작동하는 경향이 있지만, 일반적으로 계산 비용과 시간을 고려해 정해짐
  • Learning rate: 학습률
    • negative gradient 방향으로 업데이트 할 때, 얼만큼 업데이트 하고 싶은지(보폭)에 대한 것
    • 학습 속도에 영향을 줌
      • 높은 학습률은 더 큰 보폭을 의미, 알고리즘이 빠르게 수렴하기를 원할 때 사용 → 하지만 overshoot 일어날 수 있음
      • 낮은 학습률은 수렴/학습하는 데 더 오랜 시간이 걸림

 

 

경사 하강법의 특징

진행 방향이 최저점을 향해 이동할 때 곧바로 직진하지 않음

✦ 약간 타원형의 모양을 띄고 있어 gradient 방향이 최저점으로 직접 가는 방향과는 차이가 있음 → 곡선을 그리며 옆으로 돌아가 목표했던 최저점에 도달

 

시작할 때는 매우 빠르게 움직이지만 점차 느리게 움직임(보폭이 작아짐) → 최저점(바닥)에 가까워짐에 따라 주위가 점차 평평해져 gradient의 크기가 작아지기 때문

 

 

 

➡️ 지금까지 이야기한 Gradient Descent는 Batch Gradient Descent 또는 Full-batch Gradient Descent라고도 부름

: Loss 함수가 학습 데이터셋의 개별 손실 Loss 함수들에 대한 합이기 때문

 


 

Batch Gradient Descent

  • xi, yi는 학습 데이터셋 중 하나이며, L(w)는 학습 데이터셋의 개별 Loss의 합
  • Gradient가 선형 연산이기 때문에, Gradient 또한 학습 데이터셋의 개별 Gradient의 합으로 구할 수 있음

문제는 N이 커짐에 따라 이 합계들 또한 매우 커질 수 있다는 점인데, 그렇게 되면 계산 비용과 시간이 매우 많이 들어감

  • 비효율적이며 학습을 위해 실행하기엔 적합하지 않음 → Stochastic Gradient Descent (SGD)

 


 

Stochastic Gradient Descent (SGD)

 

전체 학습 데이터셋에 대한 합을 계산하는 대신 작은 하위 샘플(minibatch)을 이용해 Loss 함수를 근사하고, gradient를 근사

  • minibatch는 일반적으로 32 / 64 / 128 등의 크기를 가짐
    → 왜? CPU와 GPU의 메모리가 2배수이므로 batch size가 2의 제곱수일 때 데이터 송수신 및 연산의 효율성을 높일 수 있기 때문임

알고리즘을 수정: minibatch를 생성하는 부분을 추가 → minibatch 단위로 loss와 gradient를 계산

# Stochastic gradient descent
w = initialize_weights()
for t in range(num_steps):
    minibatch = sample_data(data, batch_size)
    dw = compute_gradient(loss_fn, minibatch, w)
    w -= learning_rate * dw

 

하이퍼 파라미터

  • Weight initialization method: 가중치 초기화 방법
  • Number of steps: 경사 하강법의 실행 횟수
  • Learning rate: 학습률
  • Batch size: 배치 크기
    • 경험적으로 해당 하이퍼파라미터는 그다지 민감하지 않은 경향이 있음을 알아냄
    • GPU 메모리가 부족할 때까지 미니 배치 크기를 최대한 크게 만들어 사용하는 것이 일반적임 
    • 여러 GPU나 여러 기기에 접근 가능하다면 분산을 통해 더 큰 배치를 사용할 수도 있음
  • Data sampling: 배치에 들어가는 학습 데이터를 샘플링
    • 데이터를 무작위로 선정하거나 반복의 시작마다 데이터를 섞어주는 등의 방법 존재 
    • 이미지 분류 결과에는 크게 영향을 미치지 못하는(그다지 중요하지 않은) 파라미터
    • 다른 유형의 문제(ranking problem 등)에 대해서는 중요한 매개 변수가 될 수 있음

 

 

 

왜 확률적(Stochastic) 경사하강법이라고 부르는가?

  • Loss 함수를 확률적으로 생각하기 때문
    : 학습 데이터(데이터 x와 라벨 y)를 샘플링 했을 때의 Loss 함수를, 전체 데이터 분포 Pdata Loss에 대한 기댓값(가능한 모든 샘플에 대한 기댓값)으로 볼 수 있음! (by. 큰 수의 법칙)
  • 모든 데이터셋 샘플에 대한 Loss 함수의 평균은 전체 확률 분포에 대한 몬테카를로 추정

➡️ 이 확률론적 개념이 바로 SGD 이름의 기원

 

Gradient도 마찬가지로 샘플에 의한 추정으로 근사할 수 있으며, 이러한 방식으로 손실함수에 대해 전체 기댓값을 근사화하기 위해서는 얼마나 많은 샘플을 취하고 싶은지 선택해야함

 

더보기

[WHAT] 큰 수의 법칙, Law of Large Numbers: 대수의 법칙, 라플라스의 정리라고도 부르는 이 법칙은, 큰 모집단에서 무작위로 뽑은 표본의 평균이 전체 모집단의 평균과 가까울 가능성이 높다는 통계와 확률 분야의 기본 개념

[WHAT] 몬테카를로 방법, Monte Carlo method: 반복된 무작위 추출(repeated random sampling)을 이용하여 함수의 값을 수리적으로 근사하는 알고리즘

  • 계산하려는 값이 닫힌 형식으로 표현되지 않거나 복잡한 경우, 근사적으로 계산할 때 사용
  • 주로 확률 분포에서 확률 변수 값을 생성하는 작업, 수학적 최적화, 수치적분 등에서 활용함

 

Interactive Web Demo

 

Multiclass SVM optimization demo

Parameters \(W,b\) are shown below. The value is in bold and its gradient (computed with backprop) is in red, italic below. Click the triangles to control the parameters. Multiclass SVM loss formulation:

vision.stanford.edu

여러가지로 선형 분류기를 만져볼 수 있는 웹 데모

 


 

Problems with SGD ⓐ

 

만약 한 방향으로는 매우 빠르게 변하고, 다른 방향에서는 매우 천천히 변하는 위와 같은 타원형의 풍경이 존재한다면?

그리고 이와 같은 공간에 Full batch Gradient Descent(GD) 혹은 Stochastic Gradient Descent(SGD)를 적용한다면?

 

 

보폭(Step size)이 너무 크면 진동할 수 있음 (zigzag pattern) + 목표했던 최저점을 지나칠 수 있음 (overshoot) → 방향 교정이 필요해지거나 수행해야 했던 단계보다 더 많은 단계를 거치게 됨

  • 가로 방향으로는 w에 따른 loss의 변화가 매우 느림 (weight is non sensitive)
  • 세로 방향으로는 w에 따른 loss의 변화가 매우 빠름 (weight is sensitive)

보폭을 작게 설정하면 overshooting 문제와 zigzag pattern 문제를 피할 수 있지만, 이 경우에는 알고리즘이 매우 느리게 수렴하며 목표했던 최저점에 도달하는 게 어려워짐

 

➡️ 보폭 크기에 대한 trade-off 존재

 

이 문제는 높은 조건수(high condition number)를 갖는 문제라고도 불림: Hessian matrix의 가장 큰 특이값과 가장 작은 특이값의 비율이 높음, 오차에 민감하고 불안정함

 

 

Problems with SGD

(오) 안장점 추가 설명 이미지

 

손실함수에 local minimum이나 saddle point가 존재한다면?

  • Local minimum(지역 최소점): 함수의 기울기가 0이지만 실제 함수의 최저점은 아닌 지점
    • Local minimum을 탈출하려면? 언덕이나 다른 것(some hill or something)을 넘어야함
    • 따라서 Local minimum이 존재하는 함수에서 SGD를 사용한다면 Local minimum에 갇히게 됨
  • Saddle point(안장점): 한 방향에서는 함수가 증가하고 다른 방향에서는 함수가 감소하는 지점, 이 지점에서도 기울기가 0임 
    • 고차원에서 훨씬 더 자주 발생되는 문제
    • Local minimum과 같이 Saddle point에 갇힐 수 있음

두 상황 모두 기울기가 0이 되어 업데이트를 중단하고 멈추는 상황이 발생함

Zero gradient, gradient descent gets stuck

 

Problems with SGD

SGD의 또다른 문제는 'Stochastic'에서 발생 → SGD의 gradient가 정확하지 않은 근사치라는 것이 잠재적 문제점이 됨

 

전체 데이터셋에 대한 작은 추정치(minibatches)만을 사용해 gradient를 구하기 때문에 noise가 많고, 실제로 우리가 가고자 하는 방향과 잘 일치하지 않을 수 있음

✦ 왼쪽 이미지: 알고리즘을 수행해 gradient를 구할 때 약간의 노이즈를 추가한 경우

     ✧ 이전 버전이 목표 지점으로 향하던 것과는 달리 목표 지점(최저점) 주위를 맴돌며 바닥으로 가는 데 오랜 시간이 걸림 → 기다리면 결국 최저점에 도착할 수는 있지만 trade-off가 명확하지 않음

 

 


 

각각의 코드는 가중치 초기화 단계를 제거한 코드임

 

위의 문제점들로 인해 실제로는 기본 SGD 모델을 잘 사용하지 않음 → 좀 더 개선된 버전의 SGD를 사용: SGD + Momentum(관성)

 

 

SGD + Momentum: 고차원 표면을 따라 굴러가는 공을 떠올려 직관적으로 이해할 수 있음

  • 각 지점에서 시간에 따른 경사도를 반영하여, 언덕을 따라 내려가는 공에 대한, 일종의 속도 벡터(velocity vector)를 계산
  • 현재 gradient 값과 과거 속도에 대한 가중 이동 평균 조합을 이용하여 속도 벡터를 업데이트
  • 따라서 우리는 기본 SGD 모델과 달리 속도 벡터를 고려한 방향으로 나아가게 됨

이 방법에는 'rho'라는 새로운 스칼라 하이퍼파라미터를 도입 → 마찰(friction) 혹은 감쇠율(decay rate)

 

이제 매 시점에서 우리는 위치 값인 xt와 속도 벡터인 vt 두 가지를 추적

  1. rho를 곱해 속도 벡터를 감쇠시키고, 현재 지점에서 계산된 gradient 값을 더함
  2. 1번에서 계산된 속도 벡터 값을 반영해 w를 업데이트

 

 

SGD +  Momentum 에 대한 두 가지 수식 → 실제로 뜯어보면 동일한 코드

하지만 어떤 paper나 자료를 읽느냐에 따라 다르게 나타날 수 있으므로 두 개 다 알아두어야 함

 

SGD + Momentum을 통해 SGD의 세 가지 문제점을 해결

 

Local minimum: 언덕에서 내려와 지역 최소값을 지날 때에도 어느정도 속도를 가지고 있기 때문에 멈추지 않고 다른 쪽으로 올라가 지역 최소점을 탈출할 수 있음

Saddle point: 안장점도 마찬가지로 속도가 존재하기 때문에 안장점을 지나 다른 최저점으로 이동할 수 있게 됨

 

Poor Conditioning: 속도 벡터를 gradient의 가중 이동 평균으로 보기 때문에, 진동을 평활화하는 데에 도움이 됨

y축의 빠른 진동은 상대적으로 낮은 속도로 평탄화되어 수평 방향으로 속도를 가속시킬 수 있음

 

Gradient Noise: SGD에 Momentum을 추가하면 노이즈를 완화시킬 수 있고, 좀 더 직접적인 목표로의 진행 경로를 택하는 것을 확인할 수 있음

 


 

Nesterov Momentum

 

  • Momentum: 속도와 Gradient를 고려하여 실제 진행 방향을 정함 → 최적화 절차를 부드럽게 하는 데 도움을 줌
  • Nesterov Momentum: 기존 모멘텀과 유사하지만 순서가 조금 다름
    • 시작점은 동일하게 빨간 점, 속도 벡터도 동일
    • 차이점: 속도 벡터에서의 새로운 gradient를 계산해 실제 진행 방향을 정함 → 속도 벡터의 방향을 미리 살펴보는 것! (Look ahead, Looking ahead into the future)

 

 

속도 벡터와 위치 값은 동일하게 존재하지만 속도 벡터를 업데이트 할 때 gradient 계산 지점이 다름 (xt + ⍴vt)

일반적인 최적화 알고리즘은 현재 지점과 현재 지점에서의 gradient에 의존해 계산하고, 이것이 더 편리한 방법

Nesterov Momentum은 우리가 기대하는 최적화 함수의 API와 잘 맞지 않음

→ 간단한 변수 변환을 통해 (접근 방식은 유지하되) 현재 지점과 현재 지점에서의 gradient에 의존해 계산하도록 바꿔줄 수 있음

 


 

Momentum을 사용하는 알고리즘의 속도가 기본 SGD 알고리즘보다 빨라진 것을 확인할 수 있음

  • 시간에 따른 속도 벡터를 이용하기 때문에 처음에는 빠르게 이동하다 목표 지점의 근처에서는 느려지며 수렴
  • 하지만 Momentum을 사용한 알고리즘은 최저점 근처에서 overshooting이 발생하는 경향을 띔
    • 시간에 따른 속도 벡터를 이용하므로 최저점 근처에서 비교적 큰 속도를 가지고 있기 때문

 

✦ SGD+Momentum 과 Nesterov 중에서는 어떤 게 더 빠른지

더보기

학습률이 충분히 클 경우 Nesterov는 SGD+Momentum보다 더 큰 decay rate를 허용함 → 진동을 방지하며, 더 빠르고 안정적으로 학습이 진행됨

학습률이 작은 경우에는 SGD+Momentum과 Nesterov가 동일한 효과를 보임

➡️ 학습률이 충분히 클 때만 차이를 보임

 


 

AdaGrad

적응형 학습률(Per-parameter learning rates, adaptive learning rates) 범주에 속하는 최적화 함수의 대표적인 예

# AdaGrad
grad_squared = 0
for t in range(num_stemps):
	dw = compute_gradient(w)
    grad_squared += dw * dw
    w -= learning_rate * dw / (grad_squared.sqrt() + 1e-7)
  • gradient 제곱의 평균을 추적

 

 

위와 같은 타원형 모양의 풍경에서 AdaGrad를 실행하면

  • 가파른 방향으로의 진행은, 큰 값으로 나누어져 진행이 느려짐
  • 상대적으로 가파르지 않은 방향에서의 진행은, 작은 값으로 나누어져 진행을 가속화

하지만 AdaGrad를 오래 실행할 경우 grad_squared 값은 계속 누적되어 점차 크기가 커져 학습 속도가 점차 감소하는 문제가 발생, 학습이 중단될 수 있음 → RMSProp: "Leaky AdaGrad"

 

RMSProp: "Leaky AdaGrad"

# RMSProp
grad_squared = 0
for t in range(num_stemps):
    dw = compute_gradient(w)
    grad_squared = decay_rate * grad_squared + (1 - decay_rate) * dw * dw
    w -= learning_rate * dw / (grad_squared.sqrt() + 1e-7)

 

RMSProp에는 SGD + Momentum의 rho와 같은 마찰(friction) 혹은 감쇠율(decay rate) 파라미터가 존재함: decay_rate

  • gradient의 제곱의 평균을 감소 시키는 역할
  • 약간의 마찰을 추가하여 학습이 멈추지 않고 계속 진행될 수 있도록 도와줌

 

  • SGD
  • SGD+Momentum: overshooting 발생
  • RMSProp: 가파른 방향에서는 진행 속도를 늦추고, 가파르지 않은 방향에서는 진행을 가속화 (적응형 학습률)

➡️ SGD 알고리즘을 개선할 수 있는 두 가지 방법을 확인했음

: 그렇다면 이 두 가지 방법을 합쳐볼 수는 없을까?

 

 


 

Adam: RMSProp + Momentum

# Adam
moment1 = 0
moment2 = 0
for t in range(num_steps):
    dw = compute_gradient(w)
    
    # ----- Momentum에서 사용된 개념과 유사
    moment1 = beta1 * moment1 + (1 - beta1) * dw
    # ----- RMSProp에서 사용된 개념과 유사
    moment2 = beta2 * moment2 + (1 - beta2) * dw * dw
    
    # ----- learning_rate * moment1: Momentum
    # ----- (moment2.sqrt() + 1e-7): RMSProp
    w -= learning_rate * moment1 / (moment2.sqrt() + 1e-7)

 

딥러닝에서 자주 쓰이는 최적화 알고리즘이며, RMSProp와 Momentum을 결합한 알고리즘임

이 방법은 최적화 초기 단계에서 문제가 발생할 수 있음

  • 만약 moment2의 beta2가 0.999와 같이 매우 큰 값이라고 가정해본다면?
    • t가 0일 때(~초기 단계에서는)  moment2의 값이 0에 매우 가까워짐 → 보폭이 커져 최적화 과정에서 문제가 발생할 수 있음

 

# Adam + Bias correction
moment1 = 0
moment2 = 0
for t in range(num_steps):
    dw = compute_gradient(w)
    
    # ----- Momentum
    moment1 = beta1 * moment1 + (1 - beta1) * dw
    # ----- RMSProp
    moment2 = beta2 * moment2 + (1 - beta2) * dw * dw
    # ----- Bias correction
    moment1_unbias = moment1 / (1 - beta1 ** t)
    moment2_unbias = moment2 / (1 - beta2 ** t)

    # ----- learning_rate * moment1: Momentum
    # ----- (moment2.sqrt() + 1e-7): RMSProp
    w -= learning_rate * moment1_unbias / (moment2_unbias.sqrt() + 1e-7)

 

이 문제점을 해결하기 위해 Adam 알고리즘에 편향 보정을 추가

  • 목적: moment1, 2의 추정치가 0에 가깝게 시작하여 보폭이 커지는 것을 보정해주기 위함

 

Adam을 사용한 다양한 논문들

Adam 알고리즘의 다양한 딥러닝 문제에 잘 적용되며,

beta1 = 0.9 / beta2 = 0.999 / learning rate = 1e-3, 5e-4, 1e-4 는 많은 모델의 좋은 시작점!

 

 

Adam 알고리즘은 Momentum 기반 방법과 RMSProp 기반 방법의 특징을 모두 보임

  • 모멘텀 기반 방법과 유사하게 overshooting이 일어나는 경향이 있음 → 하지만 SGD+momentum 알고리즘보다 덜 극단적
  • 적응형 학습률 방법과 유사하게 최소점을 향해 올바른 방향으로 업데이트 → 많은 딥러닝 모델에 잘 작동하는 이유 중 하나

 

 


 

  • Tracks first moments (Momentum): Momentum이 적용되었는가?
  • Tracks second moments (Adaptive learning rates): 적응형 학습률을 사용하는가?
  • Leaky second moments: decay rate(⍴)가 적용되는가?
  • Bias correction for moment estimates: Bias correction이 적용되었는가?

 


 

 

 

지금까지는 1차 최적화 알고리즘(First Order Optimization)을 살펴보았음

1차 미분 정보(gradient)만을 이용해 선형 근사 

 

 

 

Gradient(1차 미분 정보)와 Hessian 행렬(2차 미분 정보) 사용해 2차 최적화 알고리즘을 사용할 수도 있음

1차 미분시 알 수 있는 정보는 그 지점에서의 '기울기'지만 2차 미분시에는 곡률 정보를 알 수 있기 때문에 2차 최적화 알고리즘을 사용할 경우 보폭을 유연하게 선택할 수 있음 → 수렴률이 빠름

  • 곡률이 큰 지점에서는 step 크기를 작게, 곡률이 작은 지점에서는 step 크기를 크게함
  • 학습률 설정이나 step 크기에 있어 좀 더 강건한 모델
    : AdaGrad, SGD+Momentum과 같은 모멘텀 기반 방법, 적응형 학습률 방법이 좋은 아이디어일 수 있다는 점을 시사함

 

 

2차 최적화는 테일러 근사를 통해 표현 가능 하지만 딥러닝 시스템에서 자주 사용되지는 않음

우리는 고차원의 작업을 하고자 하는데, 이에 대한 헤시안 행렬(다차원 공간에서의 2차 미분 정보) 계산에 많은 계산 비용(메모리 소모 큼)이 들고 역행렬을 계산할 때도 마찬가지임 → 저차원 최적화문제에는 사용되지만 고차원 최적화 문제에서는 사용되지 않는 이유

  • 헤시안 행렬의 요소 수: O(N^2)
  • 역행렬의 요소 수: O(N^3)
  • N = (Tens of Hundreds of) Millions

 

Quasi-Newtom methods (BFGS, Broyden–Fletcher–Goldfarb–Shanno most popular)

: Hessian의 역행렬을 직접 계산하지 않고(O(N^3)) 근사치로 계산

 

L-BFGS(Limited-memory BFGS, BFGS를 기반으로 함)

: 전체 해시안 행렬을 저장하는 것이 아니라 일부만 저장, 나머지는 근사치로 추정함

  • 보통 Full batch, deterministic mode에서 매우 잘 작동함
  • 단일 결정론적 f(x)가 있다면 L-BFGS는 매우 잘 작동할 것
  • minibatch 설정으로 잘 전환되지 않음

 


 

실제로는 Adam Optimizer가 대부분의 케이스에서 좋은 선택이 될 수 있음

SGD + Momentum이 Adam보다 더 좋은 성능을 낼 수도 있지만 많은 튜닝이 필요

full batch를 업데이트 할 수 있는 여유가 있다면 L-BFGS를 시도해봐도 좋음 (don’t forget to disable all sources of noise)

 


 

Summary

  • 이미지 분류 문제에 대해 선형 모델을 사용
  • 다른 가중치 선택에 대한 선호를 표현하기 위해 손실 함수를 사용
  • 손실함수 최소화 및 모델 학습을 위해 SGD를 사용
728x90