딥러닝 Optimizer 종류 정리

2025. 4. 9. 11:35·Study/Data Science

- 본 포스팅에서는 딥러닝의 신경망 학습시 파라미터를 어떤 방식으로 갱신시키는지에 대한 역할을 담당하는 옵티마이저에 대해 정리하겠습니다.

 

저 스스로도 공부하고 글을 정리하며, 최대한 이해가 쉬우면서 객관적인 정보를 전하는 것을 목표로 했습니다.

이외에도 유튜브 동영상 https://www.youtube.com/watch?v=4qJaSmvhxi8&t=320s 를 추천하며, C2W2L01부터 옵티마이저에 대한 자세한 설명이 되어있습니다.

[옵티마이저란]
- 옵티마이저란, 머신러닝 학습 프로세스에서 실제로 파라미터를 갱신시키는 모듈을 의미합니다.

오차역전파법과 같은 방식으로, 각 파라미터의 기울기를 그라디언트로 구하여 재료를 구해왔으면, 이를 이용하여 실제 가중치 변화를 주는 부분이라 생각하시면 됩니다.
(어찌보면 딥러닝의 가장 핵심적인 두 부분중 하나라 할수 있겠네요. 나머지 하나는 그라디언트를 찾는 부분인데, 이것은, 변화를 줄 것도 없이, 방식(대부분 오차역전파)이 거의 비슷하니까 패스.)

[목차]
1. Stochastic Gradient Descent : SGD
2. momentum
3. NAG (Nesterov Accelerated Gradient)
4. AdaGrad
5. RMSProp
6. AdaDelta
7. Adam

[설명]
1. 확률적 경사 하강법 (Stochastic Gradient Descent : 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]

 

위 코드는, 멤버변수로 학습률(Learning rate)를 저장하고, 갱신할 params와, 그것을 grads를 받아와서, 내부 원소를 차례로 돌며, 학습률과 기울기를 곱한 값으로 파라미터를 갱신시키는 것입니다.

기본 경사하강법(GD)와 다른점은, GD의 경우, 그냥 개념이라고 생각하셔도 됩니다.

SGD나 다른 옵티마이저들 전부 손실함수를 기준으로 경사를 하강하며 오차를 수정해나가니, 엄밀히 말하자면, Grad를 사용하는 머신러닝 학습 옵티마이저는 GD라는 기본 개념을 근간으로 삼고 있습니다.

위의 코드도 그냥 GD의 작용 그대로입니다.
GD 옵티마이저를 이용하는 여러 방식에 대해 알아봅시다.

1.1 Batch GD
Batch GD는, 기존 GD를 그대로 사용하는 것인데,
다만, Grads를 구하는 시점에서, 데이터의 묶음으로 Grads를 구하는 것입니다.

데이터셋을, 하나의 무더기인 batch 단위로 묶어서,
하나하나 순전파를 시켜서, 그 모든 것들의 손실함수를 구합니다.
손실함수의 값은 모두 다를 것입니다.
왜냐면 아직 파라미터가 제대로 학습되지 않았고, 데이터 자체의 노이즈도 있을 것이기에...

일단 손실함수는 데이터셋의 rows 개수 대로 존재할텐데,
예를들어 10행 3열의, 3특징, 10개 데이터라면, 값이 손실함수 코스트 하나인 10행 1열의 데이터가 나올 것입니다.
존재하는 파라미터값이 이에 미치는 영향력을 미분하기 위해서, 이 모든 손실함수에 작용하는 방향으로 갱신을 해야하는데, 그러기 위해서 손실함수값을 모두 더하거나, 혹은 평균을 내어, 그 계산에 대한 오차역전파를 실행하여 각 파라미터의 편미분 기울기, 즉 grads를 구하면 됩니다.

일단 grads를 구했다면, 이후로는 그냥 GD와 다를것이 전혀 없습니다.
즉, 옵티마이저 작용은 전혀 달라질 것이 없습니다.

요약하자면, BGD = GD + batch
로, 과정을 다시 요약하자면,

입력 -> 순전파 -> 손실함수 계산 -> 입력 -> 순전파 -> 손실함수 계산 .... -> 배치 내 데이터에 대한 손실함수 값 모두 더하기 -> 역전파 -> grads 값 구하기 -> 값 갱신

이라는 과정을 거칩니다.


Grads를 구하기 전에, 모든 데이터에 대한 손실함수 값부터 구해서 저장해두고,
그렇게 구해진 손실함수 값을 모두 더하거나 평균을 냅니다.(두 방식 모두, 극단적으로 보았을 때, 손실함수값이 0이 되는 시점에서 0이 나오고, 또한 애초에 0이 나올수 없더라도, 최소값을 향해 탐색이 가능한 지표가 됩니다.)

그리고 그 합쳐진 값에 대해, 파라미터 w들의 각각의 편미분을 구한 후,
이를 GD를 사용하여 갱신하는 것입니다.

장점을 요약하자면,
일단 GD중에서는 안정적이게 학습을 수행해내며, 데이터 배치에 대해서는, 순전파만 여러번 실행하고, 역전파는 전체 손실함수 값에 대해 단 한번만 수행하므로, 연산횟수가 적다는 장점이 있습니다.
게다가 순전파의 경우에는, 각각의 연산이 서로에게 영향을 미치지 않으니, 이는 병렬처리가 가능합니다. (100명이, 한 데이터씩, 해당 모델의 파라미터를 사용해서 순전파에서부터 손실함수 값까지의 연산을 나눠서 처리하면 빠르겠죠?)

단점이란,
개별적인 학습 시간이 너무 오래걸리며, 배치단위로 손실함수의 값을 저장해야하기에 메모리가 많이 필요하며, 무엇보다, 꽤나 정적인 탐색, 즉 전체 데이터에 비해 실제 학습 갱신 횟수는 적기 때문에 기울기 소실에 빠질 우려가 큽니다.

1.2 SGD
BGD의 경우에는, 배치 단위가 큽니다.
즉, 배치 내 데이터들을 모두 사용해야만 학습이 이뤄지는 것이죠.
이미지 데이터 학습같은, 순전파에 시간이 걸리는 데이터의 경우에는, 한번 학습에 드는 시간이 오래 걸린다는 단점이 있습니다.

SGD는, 위에서 설명한 그냥 GD의 개념이라 생각하시면 됩니다.

데이터를 묶음으로 사용하지 않고,
한 데이터가 들어오면, 그에대한 순전파와 역전파를 한번에 하기를 반복하는 것입니다.

과정을 말하자면,


입력데이터 -> 순전파 -> 손실함수 계산 -> Grad 계산 -> 갱신


의 과정을, 데이터당 한번씩 수행하는 것입니다.

이 경우에는, BGD보다 난폭하게 가중치가 갱신되며, 언뜻 불안정해보이기는 하겠지만,
결국 GD 알고리즘에 따라서 최적화를 향해 나아가고,

그 과정에서 기울기가 크게 변화하며, 지역 소실점을 탈출하기 쉬워집니다.(골짜기를 타고 흐르는 물이, 조용히 흐르면, 중간에 고일수 있지만, 이리저리 방향을 바꾸며 내려오면, 중간 지점을 지나쳐서 아래로 튀어 내려갈수 있다고 생각하세요.)

이렇게, BGD에 비해 학습이 상당히 다이나믹하게 가중치를 갱신하므로, 이를 stochastic하다, 확률적이다. 라는 이름이 붙은 것입니다.
이에 대비하여 BGD는 Deterministic하다, 결정론적이다 라고 합니다.

SGD의 장점은,
일단 개별 데이터를 사용하므로, 실시간 학습이 가능하다는 것이고,
다이나믹한 갱신으로 인하여 기울기 소실에 BGD보다 강하며,
BGD보다 빠르게 최적점에 도달할수 있습니다.
왜냐면, BGD가 필요 이상으로 순전파를 통해 Grads의 재료들을 모아야 하는 것과는 반대로,
이리저리 튀며, 연산량도 많아지기는 하겠지만, 일단 최적점에 도달하는 시기만큼은 BGD보다 빨리 도달할 가능성이 크기 때문입니다.(BGD는, 해당 데이터셋의 최적의 위치에 도달하기 까지, 정확한 방향을 알고 한칸한칸 이동할테지만, BGD가 1걸음을 갈때, SGD는 100걸음을, 조금 틀린 방향일지라도 나아갑니다.)

반면 단점은,
일단 데이터 하나하나에 대해 연산을 실행하고, 그렇게 갱신된 데이터를 활용해야 하기에, 병렬처리에도 강점이 사라지며, 전체 데이터에서 얻어야 하는 정보에 대해 모르니, 오히려 이상한 방향으로 학습을 진행할 가능성이 있습니다.(지도를 보고 길을 찾아야 하는데, 지도 없이 일단 달려나가다가 길을 잃는 경우와 비슷한데, 대부분은 많은 시도로 이를 해결합니다.)

1.3 MiniBatch Stochastic GD (MSGD)
위와 같은 두 종류의 장점을 가져오고, 단점을 보완하는 방향으로,
미니배치라는 개념이 생겨납니다.

이름 그대로, 배치를 작게 두는 것입니다.
예를들면, 마트에서 100개 들이 라면박스를 사가는 사람은 아마 그다지 없을 것입니다.
하지만, 간단하게 5개 단위로 부담없는 단위로 나누어서 팔면, 개별 판매나 대량 판매보다 큰 장점이 있겠죠.

미니배치는, 배치 단위를 작게 만들어서, BGD의 장점인 병렬처리나 안정성 등의 장점과, SGD의 다이나믹함과 빠른 적용과 시도라는 장점을 가져오며, 나머지 단점들을 잘 무마시키는 방법이라 할수있네요.

과정은, 그냥 BGD를 생각하시면 될텐데,
배치단위로 순전파를 시키고, 그 손실함수들을 이용하여 한번씩 갱신을 해주는 것인데,
이 Batch 단위가 제법 부담없이 줄어들게 만들었다라고 생각하면 됩니다.

배치 단위야, 위에서 나열한 BGD와 SGD의 내용을 숙지한 상태로, 이리저리 시도하며 사용자가 정하면 되는 것입니다.

장점과 단점 모두,
BGD와 SGD를 적절히 혼용한 것인데, 그냥 GD는 미니배치를 사용하는 것이 좋다고 생각합니다.
단점을 굳이 뽑자면, 기본이 그냥 GD라는 것에서, 바닐라 GD의 근본적인 문제점을 그대로 가지고 있다는 것이고,
미니배치를 얼마만큼 떼어내어 사용할것인지, 그 단위를 정하는 미니배치 사이즈를,
하이퍼 파라미터로써 설정해줘야 한다는 것입니다.

- 적절한 학습률을 찾는 것이 옵티마이저에서는 가장 중요한 일인데,
실습시에는 직접 각각 다른 lr을 적용해보며,
최적이 되는 수치를 찾아낸다면 될 것입니다.

- 이로써 GD를 모두 정리했는데,
이것이 딥러닝 학습의 가장 근간이 되고 기본이 되는 옵티마이저로,
다른 옵티마이저들 역시 이를 기본으로 하여, 하나하나 성능을 높이는 방향으로 진화했다고 생각하시면 됩니다.
GD 자체에 대한 장단점에 대해서는, GD를 개선하기 위해 발전한, 아래의 옵티마이저들을 살펴보며 알아봅시다.

2. 모멘텀 (momentum)
모멘텀의 뜻은 운동량.
즉, 위에서 설명했듯, 경사 위에서 아래로 흘러가는 학습을, 일종의 물리적인 운동으로 보고 만들어진 옵티마이저입니다.
(최하단 시각 데이터를 참고하신다면, 가중치 갱신의 움직임이 마치 물리 운동과 같다는 것을 알수있습니다.)

정확한 명칭으로는, GD with momentum이라고 하고,
말 그대로 모멘텀 개념이 포함된 GD라고 이해하시면 됩니다.

기본적으로 GD와 동일하게 경사를 하강하는 알고리즘.
다른점은, 기본적인 lr * grads라는 GD의 개념에 더불어서, 운동량이라는 개념을 추가했다고 생각하세요.
즉, 경사를 하강할 때, 바로 이전의 갱신량을 참고해서 새로운 갱신량에 추가해줌으로, 일종의 가속도를 구현한 것입니다.

기본적인 GD는 모든 파라미터에 공통으로 작용하는 global learning rate가 존재하고, lr의 값이 고정적입니다.
즉, lr이 너무 크면 갱신값이 이리저리 튀어서 최소점을 찾기 어렵고, lr이 너무 작으면, 지역 최소점에 갇히기 쉽죠.
이것을 해결하기 위해서는, 최적점에서 멀리 있을 때는 lr을 크게 하고, 점차 최소점에 나아갈수록 lr을 작게 만드는 것이 좋을 것입니다.

momentum이 바로 이러한 문제 해결을 위해 만들어진 옵티마이저이고,
그냥 편하게 GD의 가중치 갱신량에 운동량 계수 momentum에 따라 정해진 가속도를 더해준다고 생각하시면 됩니다.

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.lr * grads[key] + (self.momentum * self.v[key])
            params[key] -= self.v[key]

 

1. 운동량을 뜻하는 모멘텀 계수 설정
2. 운동량인 v가 없는 상태에선, v의 값을 0으로 생성
3. v는 멤버변수로, 계속 저장되어 사용되므로, 이 값을 먼저 갱신합니다.


위에서는 보기 쉽게 모멘텀 부분과 그라드 갱신 부분을 나눴는데, GD에서 갱신부분은 이해했을 것이고, 뒤의 모멘텀      부분을 더해주는 것이 포인트입니다. 바로, 이전 갱신량에 대해, 모멘텀 계수를 곱하여, 이번의 가속도를 구하고, 그것을 더해주는 것입니다.

이렇게 되면, v는 이전 갱신에 영향을 받게 되며, 이전 갱신값이 크다면, 이번에 그만큼 가속도를 받게 됩니다.

일단 가속도라는 개념을 사용했기에, 지역 최소점을 탈출하기 쉽고, 학습 자체도 빠릅니다.
하지만, 일단 완벽하게 구해진 grads 값에, 모멘텀을 적용해주기에,
적절하게 최소값을 찾아갔음에도 모멘텀이 작용하여 최소값을 벗어나는 일이 생겨납니다.

참고로, 기존 운동량 부분과, 이번 grads의 사이에 이동평균이라는 개념을 사용하여, 이를 제어할수도 있습니다.
이경우는,

v = momentum * v + (1-momentum) * (lr * grads)

입니다.


지수평균의 의미는, 뒤의 RMS에서 보도록 하죠.
가장 처음 설명한 모멘텀은, 말하자면 마찰력이 없어서, 무척 빠르게 이리저리 튀는 것이라고 하면,
지수평균을 이용하여, 모멘텀과 이번 grads의 비율의 상관관계를 준 것은, 가속도를 주는 동시에, 보다 안정적인 움직임을 하도록 만든 것이라고 일단 이해합시다.

마지막으로, 이 경우에, momentum의 비율을 정하는 것이 중요한데,
지수평균을 사용하는 다른 것들도 그렇고, 0.9를 입력하는 것이 잘 동작한다고 합니다.
즉, 이전 이동량의 영향을 90퍼센트로 두고, 이번 grads로 갱신되는 영향을 10퍼센트로 한다는 뜻이죠.

3. Nesterov Accelerated Gradient (NAG)
Momentum 방식을 개선한 것입니다.
momentum의 경우에는, 일단 grads를 구하고,
가속도 벡터인 v를 모멘텀 계수와 곱하여 계산한 후, 이 값을 서로 더해주는 방식으로 갱신이 이뤄집니다.


(grad 계산 이후 운동량을 더해주기)
NAG는, 일단 방향성이 전해진 grad에 대해 운동량을 더해주기보다는,
운동량에 따른 변동사항을 먼저 적용시키고, 이후에 그것에 대한 grad를 구하여 갱신을 시킨다는 개념입니다.

grad를 구하는 시점에서 차이가 나므로,
기존 GD와 갱신법은 같지만, 운동량에 대해 저장해두고, 이를 이용해 grad를 구하기 전에 적용을 시킵니다.

vt=모멘텀계수*이전vt+lr*∇θJ(θ−모멘텀계수*이전vt)
θ=θ−vt

위의 수식에서 보았을 때, grad를 구하는 부분에서부터, 파라미터에 이전 운동량을 적용시켜서 구하는 것입니다.
운동량의 적용시키는 시점이, 기울기를 정하는 시점이므로, 기존 모멘텀이, 가속도로 인해서 최적점에 갔음에도 불구하고 지나쳐버리는 단점을 해결할수 있습니다.

4. AdaGrad
AdaGrad는 Adaptive Gradient Optimizer의 약자입니다.
적응형 Gradient 옵티마이저라니, 도대체 뭐에 어떻게 적응한다는 것일까요..
바로, 각 변수들에 대해 lr이 따로따로 적응하여 작용하는 것을 의미합니다.

여기서 Global lr을 그대로 사용하는 GD의 문제점을 봅시다.
각 파라미터 별 사이즈의 차이도 존재하고, 변화폭도 작기 때문에, 모두 같은 Global lr을 사용하여 갱신을 시행할 시에는, 큰 갱신이 필요한 파라미터는 상대적으로 작게 변화하고,
반면, 어느정도 최적점에 가까워, 작은 갱신이 필요한 파라미터는 크게 변화하겠죠?


이로인해 복잡한 모델에는, 각각의 파라미터별 필요한만큼의 lr을 설정해줘야합니다.

이를 사람이 하는게 아니라, 어떠한 기준에 따라 실행하는 것이 바로 AdaGrad의 기능입니다.

AdaGrad에서 변화하는 곳은,
기본적인 GD 공식인,
W = W - lr * grads
에서, 바로 저 lr부분입니다.

여기서는 간단히 aglr이라고 합시다.
W = W - aglr * grads

자, 이제 aglr을 뭘로 사용할까요?
이를 알아봅시다.

aglr = lr / (sqrt(g) + e)

입니다.

여기서 e값은 신경쓰지 않아도 됩니다.
g값이 만일 0일때, 이를 가지고 나누기를 해버리면, 에러가 발생하기에, 아주아주 의미없는 0 이상의 미세값을 나타내는 엡실론일 뿐이니까요.

그러면 g값은... g값의 제곱근의 값은 어떤 의미를 지닐까요?

g = g + grads * grads
를 의미합니다.

g값 자체는, lr을 점차 줄이는 기능을 하는군요.

이 g는, 처음에는 0으로 시작했다가,
점차 grads의 제곱을 더하고 더하며...
커져갑니다.

바로 grads의 값을 통해서, 가중치의 변화폭, 즉 lr이 줄어들죠...

중요한 것은, b값이건 w값이건, 학습이 필요한 파라미터별로,
그 grad의 제곱을 더해준 g값으로, lr을 나누고,
그 lr을 통해 각각의 파라미터를 학습시킨다는 것입니다.

예를들면, 특징이 w1 하나고, 편향으로 b가 존재하는 아주 간단한 모델을 학습시킬 때,

w1 = w1 - lr / (sqrt(gw1(w1기울기^2)) + e) * w1기울기
b = b - lr / (sqrt(gb(b기울기^2)) + e) * b기울기

이렇듯, 각각의 기울기에 따라 차별적으로 학습률이 정해지는군요.

이로써, 각 파라미터 별로 차별적인 학습률 설정이 가능하다는 것을 알수 있습니다.
가정하여, 만일 저번에 w1의 기울기 값이 작았다면,
이번에는 lr을 규제하는 gw1값이 작기에, 거의 방해 없이 lr을 통해 학습을 하는 것이고,
만일 기울기 값이 저번에 컸다면, gw1에 반영되었던 값이 컸기에, 이번도, 그리고 다음에도 점차 lr은 줄어드는 것입니다.

여기서 grads의 제곱 값을 사용하는 이유는,
이전 grads의 값을 반영하기 위한 것입니다.(방향이 아니라, 제곱을 줌으로써 얼마나 움직였는가에 대한 크기를 반영하기 위한 것.)
가중치 변화의 정도가 컸던 파라미터에 대해서는, 이미 그 학습으로 인하여 최적점에 도달했을 가능성이 높다고 보고, 점차 가중치를 줄이는 기준으로 삼는다는 것입니다.
그렇다면 그냥 절대값을 사용하면 되지, 굳이 제곱값을 사용하고 그것을 제곱근하는가에 대해서는, l2 norm을 사용한 것입니다.
norm도 정리해야겠네요.
간단하게 설명하자면, 벡터를 제곱하고, 그 값에 root를 씌워줌으로써 벡터의 크기를 나타내는 값이라고 아시면 됩니다.
여기서는 그라디언트 크기를 의미하며, 이것의 역수를 취해줌으로써, 이번 기울기의 크기를 이용해, 학습률에 제한을 주는 것입니다.

아래 코드를 보며 알아봅시다.

class AdaGrad:
    def __init__(self, lr=0.01):
        self.lr = lr
        self.g = None
    
    def update(self, params, grads):
        if self.g is None:
            self.g = {}
            for key, val in params.items():
                self.g[key] = np.zeros_like(val)
                
        for key in params.keys():
            self.g[key] += self.g[key] + (grads[key] * grads[key])
            params[key] -= self.lr * grads[key] / (np.sqrt(self.g[key]) + 1e-7)

 

살펴보면 위와 같은데, g값을 먼저 생성하고, grads 값을 제곱한 값을 더해줍니다.
이렇게, g값이 이전 값에 영향을 받으며 갱신되며,
기본적인 가중치 갱신값에, g값을 반영함으로써 가중치를 줄여나갑니다.(1e-7을 더해준 이유는, 단지 연산시 0을 나누어서 오류가 발생하지 않기 위한 매우 작은 값 엡실론)

위에서는 params[key] -= self.lr * grads[key] / (np.sqrt(self.g[key]) + 1e-7)로 표현을 했는데,
그냥 간단하게, 각 grads마다 차등한 lr을 적용하기 위한 작업으로,
params[key] -= (self.lr / (np.sqrt(self.g[key]) + 1e-7)) * grads[key] 
이렇게 이해하시는 편이 보다 좋을 것 같습니다.

학습을 반복한 파라미터의 g값의 원소가 커질수록, 반비례로, lr은 줄어드는 것이 보이네요.

이러한 특성으로, AdaGrad는 lr값에 제법 자유롭고, 복잡한 모델에 있어서 효율적인 학습을 해낸다고 합니다.
다만, 문제점이 보이는군요.
학습을 반복해나갈수록, g값은 커져만가고,
즉, g값이 너무나도 커져서, lr값이 거의 0에 가까워지기 때문에 의미를 상실하여 학습을 멈추는 경우도 존재합니다.

이는 기울기 소실과는 다릅니다.
기울기가 존재함에도 lr이 작아져서, 내려가기도 전에 학습이 멈춰버리는 문제인 것이죠.(어디까지나, 한번에 많은 변화를 겪은 파라미터는 최적점 근처에 있으리라는 확률적인 가정 위에서 동작하기에...)

5. RMSProp
AdaGrad를 개선한 방식으로, 
위에서 말한 학습률 소실 문제를 해결하기 위한 방안으로 나왔습니다.
이것의 이름은, Root Mean Square의 약자로, AdaGrad의 Root Square에, 가중평균 Mean이라는 개념을 추가했기에 이런 이름이 되었습니다.


일단 아래를 봅시다.

다른것은 동일하되,
가중치를 감소시키는 g를 구하는 방식이 달라졌습니다.
G=G + (grads)^2을, 
G=pG+(1−p)(grads)^2
으로 변화시킨 것입니다.

위의 모멘텀 마지막에 사용했었던 그 지수평균을 사용함으로써, 
무작정 g값이 커져서 기울기가 소실되지 않고, 변화량 억제의 비율은 유지시킵니다.
이렇게 되어 MS, 즉 Mean Square라고 부르고, 이에 제곱근을 붙이니 RMS, 즉 Root Mean Square라는 것을 알수 있습니다.

잠시 지수평균을 설명하자면, 계산에 두 파라미터가 존재할 때, 어떤 것에 보다 중점을 둘 것이냐에 대한 것입니다.
위에서 하이퍼 파라미터 p가 그 역할을 하는데,
G와 grad의 서로 견재하는 상관관계를 만들어줍니다.

이런 식을 이해하는데, 가장 좋은 것은, 극단값을 넣어보는 것인데,
p에 1을 대입하면, 기존 G값에만 영향을 받아서, 새로운 grad에 따라 이것이 갱신되지 않고,
p에 0을 대입하면, 기존 G값을 제거하여, 무조건 grads값만을 반영하게 되는 것입니다.

이 p값은 하이퍼파라미터로, 어떻게 정하는가는 사용자가 정하면 됩니다.
예를들어 0.25라고 하면, 기존 G값에 25퍼센트 영향을 받고, 현 grads에는 75퍼센트 영향을 받는 것이며,
0.92라고 한다면, 기존 G값에 92퍼센트 영향을 받고, 현 grads에는 8퍼센트의 영향을 받는 것이죠.

모멘텀에서도 말했듯, 이 값은 0.9정도로 두면 좋습니다.

G값을 구하는 것 외에는, 기존의 AdaGrad와 같이 갱신을 해주면 됩니다.

class RMSProp:
    def __init__(self, lr=0.01, p = 0.9):
        self.lr = lr
        self.g = None
        self.p = p
    
    def update(self, params, grads):
        if self.g is None:
            self.g = {}
            for key, val in params.items():
                self.g[key] = np.zeros_like(val)
                
        for key in params.keys():
            self.g[key] += p * self.g[key] + (1-p)*(grads[key] * grads[key])
            params[key] -= self.lr * grads[key] / (np.sqrt(self.g[key]) + 1e-7)

 

첨언하자면, RMSProp알고리즘은, 정식 논문에서 처음 소개된 것이 아닙니다.

이것의 창시자인 제프리 힌튼이, 무크... 그러니까 우리나라로 치자면 온라인 대학...

과는 조금 다른데, 학위가 목표라기보다는, 보다 보편적으로 대학수준의 양질의 지식을 공유하는 것을 목표로 하는 온라인 강의라고 보면 됩니다. 물론 후원금을 지불하고, 일반 대학처럼 강의를 듣고 과제를 수행하여 학위도 딸수 있기는 한데, 그보다 매력적인 것은, 옥스퍼드 대학 등의 세계적인 명대학의 강의를 쉽게 접할수 있다는 것이죠.

어쨌든, 유명한 무크인, 코세라에서 강의를 하던 중에, 그 강의에서 처음 소개된 방식이라고 합니다.

어찌보면, EBS 방송에서 신곡 im yours를 발표한 제이슨 므라즈와 비슷한 사례라 재밌는 배경지식입니다.
지식이라는 것은 역시, 우리나라로 치자면, 한글 창제의 의미와 같이, 보다 쉽게 접하고 많은 이들과 공유할 때 발전하는 것이라고 느꼈습니다.

6. AdaDelta
이 역시 AdaGrad를 개선하기 위해 나온 방식입니다.
여기서부터는 실제 논문을 공부하며 정리합니다.


https://arxiv.org/pdf/1212.5701v1.pdf

AdaDelta는, RMSProp을 기반으로 하며,
이것의 목적은,
'전역 학습률(global learning rate)을 인간이 정하지 않고, 적절한 학습률을 스스로 설정할수 있지 않을까?'
입니다.(해당 논문의 introduction에 수록. 요는, 휴리스틱한 lr 설정을 보다 기술적으로 하는 방식)
즉, 학습률 설정부분을 지우고, 이를 자동화한다는 것에 의의를 가집니다.

AdaDelta라는 이름의 의미를 먼저 살펴보자면,
AdaGrad와 비슷합니다.
AdaGrad가 Adaptive Gradient로,Grads를 이용하여 각 파라미터별 변화하는 lr을 적용한다고 하면,
AdaDelta는, Delta값, 즉 이전 파라미터와 이번 파라미터의 차이값인 갱신값에 중점을 두어, 고정적인 lr이 아닌, 변화하는 갱신값 delta를 사용한다는 의미입니다.

전체적인 구조로는 AdaGrad와 동일합니다.
grads와 곱해줄 어떠한 학습률과 같은 개념의 비율을 규제하기 위한 어떤 값으로 나눠주는 것이죠.

이 나눠주는 분모가 바로 RMSProp에서 알아봤던 RMS(Root Mean Square)값,
G=pG+(1−p)(grads)^2
입니다.

분모가 똑같은 이유는, RMSProp에서 설명한 그대로의 개념을 사용하겠다는 것입니다.

AdaDelta는, 기존에 사용한 분자값인 lr이 달라지는 것인데, 이를 미지수 ad라고 가칭하면,

param = param - ((ad/sqrt(G)+e) * grad)
이네요.

과연 lr을 사용하지 않는다는 저 ad는 무엇일까요?

ad = RMS(델타값)

입니다.

RMS는, 분모에 쓰였었죠?
RMS(grads) = G=pG+(1−p)(grads)^2
이것이 분모에 쓰인 RMS입니다.

여기서는 grads에 대한 RMS를 사용했는데,

분자의... 즉, lr의 역할을 할 수치는,
RMS(갱신값) = S = pS + (1-p)(갱신값)^2
이 되는 것입니다.

갱신값 = 델타값 = S / G * grads
이고, 이는,
RMS(갱신값)/RMS(grads) * grads
로 나타낼수 있는 것입니다.

파라미터 -= RMS(갱신값)+e/RMS(grads) * grads+e
으로, 학습률을 대신하는 분자의 RMS는, 0에 가깝게 시작하여 점차 증가하며 학습률을 높이게 되고,
분모의 RMS 역시 0에 가깝게 시작하여 점차 증가하며 학습률을 제한하는 것입니다.

모두 이전 학습량과 grad에 영향을 받으며 저절로 학습 step을 변화시킵니다.

분자부분의 RMS에 대해서는 논문에서 여러 설명이 있었는데,
현상태로 제가 이해할수 없는 다른 지식들을 나열하기 때문에 확실히 이해하지 못했기에,
일단 여기까지만 설명합니다.

확실한 것은, 저 델타값을 기준으로 학습 스텝을 머신이 스스로 조절해나가니, 기존의 Adagrad가 가진 가장 큰 단점이자 오류로, 학습을 반복할수록 학습스텝이 굉장히 낮아지는 단점을 해결할수 있습니다.

고정적인 lr보다는, 이전 델타값을 이용하여 스스로 변화하는 델타값을 사용하는 장점이죠.

자세한 의미를 아시고 싶으시면 링크를 확인해주세요.

- 논문에 나온 수식

논문에 나온 수

 

기존 GD대로 이번 파라미터를 구할 때,
이번 파라미터와 이전 파라미터의 차이, 즉 이동량인 delta 파라미터는, 결국 파라미터와 손실함수간의 미분값만을 사용해서는 매칭이 되지 않고,
이를 해결하기 위해서는, 기존의 미분값인 grads를 구하고, 더불어, 세번째 수식의 분모에 있는 수, H^-1수를 곱해준다는데, 일단 기존 GD가 어떻게 매칭이 되지 않는지에 대해 모르겠고, H값의 역수인, 파라미터 제곱의 미분값을 나눠주면 이것이 해결되는지는 모르겠습니다.

이러한 것들을 가정으로 하여, lr이 아닌, grads만으로 적절한 학습 스탭인 델타값을 찾아가는 과정이 바로 네번째 수식이며, 기존 grads값에 더불어 H까지 미분을 하면 드는 방대한 연산량으로 인해, 이를 전개해주는 과정인 것입니다.
보신다면 이항을 하고 상쇄를 시키는 방식으로 어찌어찌 정리를 하자, 위에서 설명했던 RMS값들의 조합이 나오네요.

저는 일단 제가 이해하지 못한 수학적 컨셉부분은 버린 상태로, 기존의 RMS에 빗대어 달라진 부분...
즉 lr 부분의 작용에 대해 이야기 했을 뿐입니다.(가장 중요한 것이, 바로 저 적절한 델타값, 적절한 변화값을 계산하는 과정에서 도출되는 것 같은데, 후우... 수학에 약해서 그런지 논문 내용이 너무 어렵습니다.)

이마저도 오류가 있을수도 있으니, 추후 개인적으로 개선을 할 예정이며, 일단 위의 수식만을 봐주시길 바랍니다.

7. Adam
현재 가장 자주 사용되는 옵티마이저입니다.
옵티마이저를 모른다면 일단 이걸 사용해도 될 정도입니다. (많은 사용을 통해 검증된 알고리즘이라고 하죠.)
여러모로 이전 옵티마이저들의 장점을 취해 만들어졌다고 하더군요.
(combination of RMSprop and Stochastic Gradient Descent with momentum)

이름의 의미는,
Adaptive momentum estimation입니다.

이름 그대로 Adagrad와 momentum의 결합으로,
이전까지는 momentum의 성능이 매우 뛰어났기에, 상황에 따라 다른 옵티마이저를 사용하면서도, momentum을 주로 사용하였는데,

Adam이 나온 이후로는, 보다 폭넒은 딥러닝 아키텍쳐에서 좋은 성능을 보이는 이 옵티마이저가 주목받고 검증되었다고 합니다.

기본이 부족하기도 하고, 적당한 자료가 없어서 계속 해맸었는데, 가장 위에 링크한 강의 영상을 보고 쉽게 이해했습니다.

그냥 말 그대로 RMSprop과 momentum을 섞어서 쓰면 됩니다.
모멘텀의 운동량을 v로 두고, RMSprop의 수치를 s라고 했을 때,
먼저 둘은 모두 0으로 초기화 됩니다.(위의 각각의 알고리즘을 확인하면 그것과 똑같이 그냥 준비단계입니다.)

v = p* v + (1-p)*grads
s = p*s + (1-p)*grads^2

위와 같습니다.
그냥 RMS를 해주면 됩니다.

이에대한 의미는 모두 위에서 설명한 모멘텀(지수평균 사용)과, RMSprop에서 설명했으니 패스합니다.

거기에 더불어, 편향보정을 해줘야 하는데,
vc = v / (1-p^t)
sc = s/(1-p*t)
입니다.

편향보정에 대해서는, 제가 Adam을 정리할때 적극 참고한 유튜브 동영상에서 쉽게 알수 있는데,
마침 해당 영상을 간단 정리한 곳이 있어서 링크를 올립니다.
https://www.edwith.org/deeplearningai2/lecture/34856/

위 값들을 이용해서 실제 갱신을 해준다면,

w = w - lr * vc / sqrt(sc)+e

으로 쉽게 이를 적용할수 있습니다.

말 그대로, 위에서 우리가 배운 momentum과 RMSprop을 한번에 사용한 것이네요.

마지막으로 각 파라미터에 대해 조금 설명하자면,
지수평균에 사용한 p값의 경우는, v를 구할때는 0.9
s를 구할때는 0.999를 사용하는게 좋으며, e값은 10^-8을 추천한다고 논문의 저자가 서술했다는군요.

[각 옵티마이저별 에폭별 학습 진행 시각화 자료]

옵티마이저별 경사 하강 그래픽

 

그래프는, z축, 그러니까 높낮이가 손실함수의 cost 값을 의미하며, 파라미터를 수정하여 가장 아랫부분으로 나아가야 하는 것을 나타냅니다.

기본적으로 2차원 평면상에 나타내는 경사하강법의 그래프를 생각해보면, y축이 손실함수 값이고, x축이 파라미터 값이었죠?
위 그래프 역시 마찬가지입니다.
다만, 파라미터가 한 종류가 아니기에 그래프는 2차원 이상의 다차원 그래프가 되며,
위에서는 w값을 두종류로 사용한 3차원 그래프가 되는 것입니다.

그 이상의 것은 상상하기 어렵겠죠?

그러니 개인적으로는, 위와 같이 3차원으로 표현하는 것보다는,
cost값과, w1
cost값과, b
cost값고, w2
...


이런식으로, 2차원 그래프를 나열하는 형태가 오히려 보기 편하지 않나 싶습니다.

시각자료에서 보신다면, 학습이라는 것이 위와 같이 골짜기를 타고 내려가는 것을 가시적으로 볼수 있는데,
파라미터가 보통은 2,3개를 넘기에, 이로인해 나타나는, 모든 파라미터에 부합하는 최적의 위치를 찾아내는 것이 힘든 것이라고 알아갑시다.

// 전체적인 구성과 수식의 경우는,
http://shuuki4.github.io/deep%20learning/2016/05/20/Gradient-Descent-Algorithm-Overview.html
를 참고했으며,

코드의 경우는,
https://sacko.tistory.com/42
를 참고하며, 제가 이해하는 한에서 새롭게 만들거나 수정했습니다.

 

현재는 기존 글을 복원하느라 최신 정보가 반영이 안 되어있을지도 모르는데, 추후 자세히 추가 수정을 진행하겠습니다.

저작자표시 비영리 변경금지 (새창열림)

'Study > Data Science' 카테고리의 다른 글

고차원 데이터에서 차원의 저주란? (딥러닝 학습시 데이터 특징을 제한하는 이유와 빅데이터 기술의 중요성) (구 블로그 글 복구)  (0) 2025.04.10
정보이론 기초 정리(정보량 + 정보 엔트로피) (구 블로그 글 복구)  (0) 2025.04.09
데이터 사이언스 실습을 위한 개발 환경 준비(Python, Anaconda, CUDA, Torch)  (0) 2025.04.02
마할라노비스 거리(Mahalanobis Distance) 개인정리 (구 블로그 글 복구)  (0) 2025.04.01
최대 우도 추정법(MLE : Maximum Likelihood Estimation)이란?  (0) 2024.10.14
'Study/Data Science' 카테고리의 다른 글
  • 고차원 데이터에서 차원의 저주란? (딥러닝 학습시 데이터 특징을 제한하는 이유와 빅데이터 기술의 중요성) (구 블로그 글 복구)
  • 정보이론 기초 정리(정보량 + 정보 엔트로피) (구 블로그 글 복구)
  • 데이터 사이언스 실습을 위한 개발 환경 준비(Python, Anaconda, CUDA, Torch)
  • 마할라노비스 거리(Mahalanobis Distance) 개인정리 (구 블로그 글 복구)
Railly Linker
Railly Linker
IT 지식 정리 및 공유 블로그
  • Railly Linker
    Railly`s IT 정리노트
    Railly Linker
  • 전체
    오늘
    어제
  • 공지사항

    • 분류 전체보기 (106)
      • Programming (33)
        • BackEnd (18)
        • FrontEnd (2)
        • DBMS (1)
        • ETC (12)
      • Study (72)
        • Computer Science (20)
        • Data Science (17)
        • Computer Vision (16)
        • NLP (15)
        • ETC (4)
      • Error Note (1)
      • ETC (0)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 태그

    unique
    kotlin linkedlist
    springboot 배포
    단축키
    논리적 삭제
    network_mode: "host"
    docker 배포
    지리 정보
    localhost
    데이터베이스 제약
    docker compose
    MacOS
    list
    Kotlin
    kotlin mutablelist
    jvm 메모리 누수
    kotlin arraylist
  • 링크

    • RaillyLinker Github
  • hELLO· Designed By정상우.v4.10.0
Railly Linker
딥러닝 Optimizer 종류 정리
상단으로

티스토리툴바