딥러닝 계열 모델에서 최적화를 위해 optimizer(최적화 함수) 를 사용한다. tensorflow나 pytorch를 사용해본 사람들은 SGD, Adam와 같은 optimize가 익숙할 것이다. 오늘은 이 optimizer에 대해 알아보자.
- gradient descent
- stocastic gradient descent
- momentum
- adam
- 요약
1. Gradient Descent (GD)
2022.03.09 - [머신러닝 with 파이썬] - 파이썬으로 기초 MLP 구현하기 와
2022.04.03 - [머신러닝 with 파이썬] - 파이썬과 기초 딥러닝 개념- 이론편 1
에서 구현했던 backpropagation이 이에 해당한다. 한국어로는 '경사하강법'으로, 학습이 진행될 때마다 경사가 하강하는 방향으로 점차 이동하는 방법이다. 어떤 친구의 경사를 본다는 걸까? 누구를 이동시킨다는 걸까?
딥러닝에서 optimizer를 사용하는 이유는 가중치를 업데이트하기 위함이다. 손실함수로 loss를 구하고, 이를 바탕으로 어느 방향으로 가중치 w를 업데이트하면 좋을지를 보는 것이다. 이때 '어느 방향'을 미분 값으로 구하게 되는데, 그림으로 보면 다음과 같다.
그림의 y축은 loss, x축은 가중치 w이다. loss가 작아지는 '방향'은 dLoss/dw, 즉 미분값을 통해 구할 수 있다. 여기에 그 방향만큼 얼마나 이동할 건지를 정하기 위해 learning_rate를 곱한다. 그래서 GD의 식은 다음과 같다. 미분 값이 0이라면 안타깝게도 더 이상의 가중치 업데이트는 없게 된다.
w = w - learning_rate * dloss/dw
2. Stocastic Gradient Descent(SGD)
GD를 확률론적으로 접근하는 방법론으로, 수식 자체는 GD와 다르지 않다. 이름에 stocastic(확률론적)이라는 단어가 붙는데, random하게 데이터를 뽑겠다는 말이다. 내가 학습 때 쓰는 데이터가 커질수록 이 데이터를 전부 보고 가중치를 업데이트한다면 학습 속도는 상대적으로 느려지게 될 것이다. 그렇기 때문에 랜덤 하게 1개의 데이터를 뽑아서 GD로 가중치를 업데이트하는 방법론이 SGD이다.
여러 개의 데이터로 가중치를 업데이트 하는 것은 Mini-Batch GD라고도 불리는데, SGD는 랜덤으로 데이터를, Mini-Batch는 batch크기만큼의 데이터를 한 번의 학습에 사용한다고 볼 수 있다.
다만 SGD는 다른 방향은 탐색하지 않기 때문에 까딱하면 local minima에 빠지기 좀 더 쉬울 수 있다.
(cf.local minima : 위 그림에서는 2차 함수의 형태를 띠고 있지만, 만약 3, 4차 함수라면 볼록해지는 구간이 1개가 아닐 수도 있다. loss가 내려가는 구간이어서 그쪽 방향으로 가중치를 갱신했는데, 실은 loss가 더 작아지는 볼록 구간이 있다면? 우리는 이때 local minima에 빠졌다고 얘기할 수 있다. 이는 비단 SGD의 문제만도 아니다. )
장점 | 단점 |
랜덤으로 일부 데이터로 학습을 하기 때문에 학습이 빠르다 | local minima에 빠질 수 있다. |
3. Momentum
pytorch에서 optimizer SGD를 불러오면 안의 인자에 momentum이 있을 것이다. 가중치 w에 대한 운동량, 관성량으로 해석할 수 있는 momentum을 계산해서 더해주는 형태이다. momentum 계수를 r로, v는 가중치 w가 움직이는 방향으로 생각하면 아래와 같이 갱신할 수 있다.
v = r * v - learning_rate * dloss/dw
w = w + v
수식을 봤을때는 SGD의 식에 r * v 만 추가로 더해줬을 뿐이다. r이 관성이라고 했는데 잘 와닿지 않을 수 있다. 아래 그림을 보자. w는 1번 위치에서 2번 위치로 움직이고, 2번에서 3번으로 움직인다. 이때 2번에서 3번으로 움직일 때의 보라색 화살표가 이전(1번->2번)에 움직였던 dloss/dw이다. 이전과 같이 움직이려 하는 성질, 즉 관성인 것이다. 그림의 현 미분 값(분홍색) 벡터와 이전 속도(보라색) 벡터의 덧셈 연산을 통해 새로운 v가 탄생했다. 보통 이전 v(보라색 벡터)에 곱하는 계수 r은 0.9를 사용한다.
이를 사용하면 SGD에 비해 oscillation은 다음과 같이 줄어드는 장점이 있다.
4. Adam
Machine Learning(ML)에서 optimizer로 자주 쓰이는 Adam이다. 이친구가 나오기 전까지 Adagrad, Adadelta, RMSProp이 있었는데 각각 어떤 걸 특화하기 위한 것이었는지 간단하게만 정리하면 다음과 같다.
- Adagrad : Adaptive Gradient. dloss/dw의 제곱을 구해서 learning rate의 값을 조정했다. 학습이 최적의 w를 찾기 전에 끝나버리면 어떡해?를 방지하기 위해 알아서 learning rate를 작게 만든다. (== learning rate decay) 이를 통해 sparse한 gradient에서도 잘 작동한다고 한다.
Adagrad
h = h + dloss/dw **2
w = w - learning_rate * 1/√h * dloss_dw
- RMSProp : Adagrad에서 파생한 친구로, 위 수식에서 ρ라는 하이퍼파라미터를 추가했다. 이전과 현재의 값을 ρ : 1- ρ로 섞었다고 보면 되겠다. nonstationary(비정상적) 목적함수(여기서는 loss함수를 의미한다)에서도 잘 작동한다고 한다.
RMSProp
h = ρ * h + (1- ρ) * (dloss/dw * *2)
w = w - learning_rate * 1/√h * dloss_dw
- Adadelta : An Adaptive Learning Rate Method. Adagrad에서 파생해 learning rate에 좌지우지당하지 않겠다는 포부를 지닌 optimizer로, 하이퍼 파라미터나 noisy gradient에 강건한 성능을 지닌다. 무슨 말인가 하니, dloss_dw의 제곱을 계속해서 축적해 더해가는 형식을 취해주어 learning rate 튜닝을 굳이 안 해도 학습이 진행될수록 알아서 잘 업데이트한다는 것이다. 수식에서 입실론ε 을 더하는 이유는 E(g**2)가 0이 될 경우를 대비한 것이다.
Adadelta
g = dloss/dw
E[g **2]_t = ρ E[g**2 ] + (1 − ρ) g**2
RMS[g]t = p E[g**2]t + ε
w = w - learning_rate / RMS[g] * g
이제 Adam을 설명하자면 위의 momentum 개념을 이해하면 쉽다. 모멘텀을 구하던 수식에 RMSProp의 ρ 아이디어를 가져왔다.
Momentum에서의 수식 | Adam에서의 1차 모멘텀을 구하는 수식 |
v = r * v - learning_rate * dloss/dw | m1= β* m + (1-β) * dloss/dw |
1차 모멘텀이라 했으니 2차도 있겠지? 이번엔 dloss/dw를 제곱한 값에 대해서 momentum을 구하는데, 1차 모멘텀 수식의 dloss/dw 자리에 dloss/dw**2가 들어간다. 1차 모멘텀의 β와는 다르기 때문에 β2라고 표기하겠다.
Adam 2차 momentum
v = β2* m + (1-β2) * (dloss/dw)**2
다 왔다! 잊지 말자, 기본 수식은 w = w - learning_rate * dloss/dw라는 걸!!
Adagrad, RMSProp, Adadelta를 보면 전부 learning_rate에 무언가를 나눠줬다. Adam도 똑같이, 2차 모멘텀에서 구한 걸 이용한다. 1차 모멘텀은 dloss/dw 자리에 들어가게 된다.
v_hat = v / (1 - β2)
m_hat = m / (1 - β1)
w = w - learning_rate * 1 / √(v_hat+ε) * m_hat
이를 통해 Adam은 Adagrad의 장점인 sparse한 gradient에서 잘 작동하는 것과, RMSprop의 nonstationary objective에서 잘 작동하는 이점을 취했다.
5. 요약
오늘 소개한 optimizer의 수식을 정리하자면 다음과 같다. 편의를 위해 learning_rate는 α 로, dloss/dw는 g로 대체하겠다. 아 결국 optimizer는 learning rate자리와 g 자리에 뭔가를 더 계산한 거로구나, 로 이해할 수 있겠다.
optimizer | 수식 |
GD / SGD | w = w - α * g |
GD + Momentum | v = r * v - α * g w = w + v |
Adagrad | h = h + g**2 w = w - α /√h * g |
RMSProp | h = ρ * h + (1- ρ) * g**2 w = w - α /√h * g |
Adadelta | E[g **2]_t = ρ E[g**2 ] + (1 − ρ) g**2 RMS[g]t = p E[g**2]t + ε w = w - α / RMS[g] * g |
Adam | m1 = β* m + (1-β) * g v = β2* v + (1-β2) * g**2 v_hat = v / (1 - β2) m_hat = m / (1 - β1) w = w - α / √(v_hat+ε) * m_hat |
오늘은 전반적인 optimizer 내용을 요약해 보았다. 본 내용의 GD, SGD, 논문 등 참고문헌은 다음과 같다.
https://ml-cheatsheet.readthedocs.io/en/latest/gradient_descent.html / https://towardsdatascience.com/stochastic-gradient-descent-clearly-explained-53d239905d31 / https://arxiv.org/pdf/1609.04747.pdf /
Stochastic Gradient Descent — Clearly Explained !!
Stochastic gradient descent is a very popular and common algorithm used in various Machine Learning algorithms, most importantly forms the…
towardsdatascience.com
다음 페이지에서는 Towards Theoretically Understanding Why SGD Generalizes Better Than ADAM in Deep Learning이라는 논문에 대해 잠깐 다뤄볼까 한다.
'머신러닝 > 파이썬 구현 머신러닝' 카테고리의 다른 글
pytorch 공식 구현체로 보는 transformer MultiheadAttention과 numpy로 구현하기 (0) | 2023.07.01 |
---|---|
파이썬으로 기초 CNN 구현하기 1 - conv, pooling layer (2) | 2022.04.17 |
파이썬과 기초 딥러닝 개념- 이론편 1 (0) | 2022.04.03 |
파이썬으로 기초 RNN 구현하기 (1) | 2022.03.27 |
파이썬으로 기초 MLP 구현하기 (2) | 2022.03.09 |