06.1 매개변수 갱신
신경망 학습의 목적은 손실 함수의 값을 가능한 한 낮추는 매개변수를 찾는 것이었죠. 이는 곧 최적 매개변수를 찾는 문제이며, 이러한 문제를 푸는 것을 최적화${}^{\text{optimization}}$라 합니다. 안타깝게도 신경망 최적화는 굉장히 어려운 문제입니다. 매개변수 공간은 매우 넓고 복잡해서 최적의 솔루션은 쉽게 못 찾으니까요. 수식을 풀어 순식간에 최솟값을 구하는 방법 같은 것은 없습니다. 게다가 깊은 네트워크에서는 매개변수의 수가 엄청나게 많아져서 사태는 더욱 심각해집니다.
우리는 지금까지 최적의 매개변수 값을 찾는 단서로 매개변수의 기울기(미분)를 이용했습니다. 매개변수의 기울기를 구해, 기울어진 방향으로 매개변수 값을 갱신하는 일을 몇 번이고 반복해서 점점 최적의 매개변수로 다가갔습니다. 이것이 확률적 경사 하강법${}^{\text{(SGD)}}$이란 단순한 방법인데(비록 이름은 어렵지만), 매개변수 공간을 무작정 찾는 것보다 ‘똑똑한’ 방법입니다. SGD는 단순하지만, (문제에 따라서는) SGD보다 똑똑한 방법도 있답니다. 지금부터 SGD의 단점을 알아보고 SGD와는 다른 최적화 기법을 소개하려 합니다.
6.1.1 모험가 이야기
본론으로 들어가기 전에, 최적화를 해야 하는 우리의 상황을 모험가 이야기에 비유해보겠습니다.
색다른 모험가가 있습니다. 광활한 메마른 산맥을 여행하면서 날마다 깊은 골짜기를 찾아 발걸음을 옮깁니다. 그는 전설에 나오는 세상에서 가장 깊고 낮은 골짜기, ‘깊은 곳’을 찾아가려 합니다. 그것이 그의 여행 목적이죠. 게다가 그는 엄격한 ‘제약’ 2개로 자신을 옭아맸습니다. 하나는 지도를 보지 않을 것, 또 하나는 눈가리개를 쓰는 것입니다. 지도도 없고 보이지도 않으니 가장 낮은 골짜기가 광대한 땅 어디에 있는지 알 도리가 없죠. 그런 혹독한 조건에서 이 모험가는 어떻게 ‘깊은 곳’을 찾을 수 있을까요? 어떻게 걸음을 옮겨야 효율적으로 ‘깊은 곳’을 찾아낼 수 있을까요?
최적 매개변수를 탐색하는 우리도 이 모험가와 같은 어둠의 세계를 탐험하게 됩니다. 광대하고 복잡한 지형을 지도도 없이 눈을 가린 채로 ‘깊은 곳’을 찾지 않으면 안 됩니다. 척 봐도 어려운 문제임이 느껴질 거에요.
이 어려운 상황에서 중요한 단서가 되는 것이 땅의 ‘기울기’입니다. 모험가는 주위 경치는 볼 수 없지만 지금 서 있는 땅의 기울기는 알 수 있습니다. 발바닥으로 전해지죠. 그래서 지금 서 있는 장소에서 가장 크게 기울어진 방향으로 가자는 것이 SGD의 전략입니다. 이 일을 반복하면 언젠가 ‘깊은 곳’에 찾아갈 수 있을지도 모르죠. 적어도 용감한 모험가는 그렇게 생각할지도 모릅니다.
6.1.2 확률적 경사 하강법(SGD)
최적화 문제의 어려움을 되새기고자 먼저 SGD를 복습해보겠습니다. SGD는 수식으로는 다음과 같이 쓸 수 있습니다.
\[\mathbf{W} \leftarrow \mathbf{W} - \eta \frac{\partial L}{\partial \mathbf{W}} \tag{식 6.1}\]여기에서 $\mathbf{W}$는 갱신할 가중치 매개변수고 $\frac{\partial L}{\partial \mathbf{W}}$은 $\mathbf{W}$에 대한 손실 함수의 기울기입니다. $\eta$는 학습률을 의미하는데, 실제로는 0.01이나 0.001과 같은 값을 미리 정해서 사용합니다. 또, $\leftarrow$는 우변의 값으로 좌변의 값을 갱신한다는 뜻입니다. [식 6.1]에서 보듯 SGD는 기울어진 방향으로 일정 거리만 가겠다는 단순한 방법입니다. 그러면 이 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]
초기화 때 받는 인수인 lr은 learning rate(학습률)를 뜻합니다. 이 학습률을 인스턴스 변수로 유지합니다. update(params, grads) 메서드는 SGD 과정에서 반복해서 불리게 됩니다. 인수인 params와 grads는 (지금까지의 신경망 구현과 마찬가지로) 딕셔너리 변수입니다. params['W1'], grads['W1'] 등과 같이 각각 가중치 매개변수와 기울기를 저장하고 있습니다.
SGD 클래스를 사용하면 신경망 매개변수의 진행을 다음과 같이 수행할 수 있습니다(다음 코드는 실제로는 동작하지 않는 의사 코드입니다).
network = TwoLayerNet(...)
optimizer = SGD()
for i in range(10000):
...
x_batch, t_batch = get_mini_batch(...) # 미니배치
grads = network.gradient(x_batch, t_batch)
params = network.params
optimizer.update(params, grads)
...
optimizer 변수는 ‘최적화를 행하는 자’라는 뜻의 단어입니다. 이 코드에서는 SGD가 그 역할을 합니다. 매개변수 갱신은 optimizer가 책임지고 수행하니 우리는 optimizer에 매개변수와 기울기 정보만 넘겨주면 되는 것이죠.
이처럼 최적화를 담당하는 클래스를 분리해 구현하면 기능을 모듈화하기 좋습니다. 예를 들어 곧이어 소개할 모멘텀이라는 최적화 기법 역시 update(params, grads)라는 공통의 메서드를 갖도록 구현합니다. 그때 optimizer = SGD() 문장을 optimizer = Momentum()으로만 변경하면 SGD를 모멘텀으로 바꿀 수 있는 것이죠.
NOTE_ 대부분의 딥러닝 프레임워크는 다양한 최적화 기법을 구현해 제공하며, 원하는 기법으로 쉽게 바꿀 수 있는 구조로 되어 있습니다. 예를 들어 Lasagne라는 딥러닝 프레임워크는 다양한 최적화 기법 구현해 updates.py* 파일에 함수로 정리해두었습니다. 사용자는 그중 쓰고 싶은 기법을 선택할 수 있습니다.
* http://github.com/Lasagne/Lasagne/blob/master/lasagne/updates.py
6.1.3 SGD의 단점
SGD는 단순하고 구현도 쉽지만, 문제에 따라서는 비효율적일 때가 있습니다. 이번 절에서는 SGD의 단점을 알아보고자 다음 함수의 최솟값을 구하는 문제를 생각해보겠습니다.
\[f(x, y) = \frac{1}{20} x^2 + y^2 \tag{식 6.2}\]이 함수는 [그림 6-1]의 왼쪽과 같이 ‘밥그릇’을 $x$축 방향으로 늘인 듯한 모습이고, 실제로 그 등고선은 오른쪽과 같이 $x$축 방향으로 늘인 타원으로 되어 있습니다.
그림 6-1 $f(x,y) = \frac{1}{20}x^2 + y^2$의 그래프(왼쪽)와 그 등고선(오른쪽)

[식 6.2] 함수의 기울기를 그려보면 [그림 6-2]처럼 됩니다. 이 기울기는 $y$축 방향은 크고 $x$축 방향은 작다는 것이 특징입니다. 말하자면 $y$축 방향은 가파른데 $x$축 방향은 완만한 것이죠. 또, 여기에서 주의할 점으로는 [식 6.2]가 최솟값이 되는 장소는 $(x, y) = (0, 0)$이지만, [그림 6-2]가 보여주는 기울기 대부분은 $(0, 0)$ 방향을 가리키지 않는다는 것입니다.
그림 6-2 $f(x, y) = \frac{1}{20}x^2 + y^2$의 기울기

이제 [그림 6-1]의 함수에 SGD를 적용해볼까요? 탐색을 시작하는 장소(초깃값)는 $(x, y) = (-7.0, 2.0)$으로 하겠습니다. 결과는 [그림 6-3]처럼 됩니다.
그림 6-3 SGD에 의한 최적화 갱신 경로: 최솟값인 $(0, 0)$까지 지그재그로 이동하니 비효율적이다.

SGD는 [그림 6-3]과 같은 심하게 굽이진 움직임을 보여주죠. 상당히 비효율적인 움직임입니다. 즉, SGD의 단점은 비등방성${}^{\text{anisotropy}}$ 함수(방향에 따라 성질, 즉 여기에서는 기울기가 달라지는 함수)에서는 탐색 경로가 비효율적이라는 것입니다. 이럴 때는 SGD 같이 무작정 기울어진 방향으로 진행하는 단순한 방식보다 더 영리한 묘안이 간절해집니다. 또한, SGD가 지그재그로 탐색하는 근본 원인은 기울어진 방향이 본래의 최솟값과 다른 방향을 가리켜서라는 점도 생각해볼 필요가 있습니다.
도로시가 눈을 가리고 지그재그 모양의 가파른 절벽 사이 골짜기를 굽이굽이 지쳐서 헤매며 내려가고 있어요.
도로시의 깊은 한숨 (SGD)
- 도로시: “휴… 매 걸음마다 가장 가파른 경사 방향으로만 걸었더니, 목표 지점인 $(0,0)$으로 바로 가지 못하고 지그재그로 벽에 계속 부딪히면서 가게 돼! 정말 다리도 아프고 너무 비효율적이야.”
- 지니: “그게 바로 확률적 경사 하강법(SGD)의 한계란다. 골짜기의 기울어진 방향이 본래의 최솟값 방향과 어긋나 있으면 이렇게 심하게 요동치며 전진하게 되지.”
이제부터 SGD의 이러한 단점을 개선해주는 모멘텀, AdaGrad, Adam이라는 세 방법을 소개할 겁니다. 이들은 모두 SGD를 대체하는 기법으로, 각각을 간단히 설명하면서 수식과 파이썬 구현을 살펴보겠습니다.
6.1.4 모멘텀
모멘텀${}^{\text{Momentum}}$은 ‘운동량’을 뜻하는 단어로, 물리와 관계가 있습니다. 모멘텀 기법은 수식으로는 다음과 같이 쓸 수 있습니다.
\(\mathbf{v} \leftarrow \alpha \mathbf{v} - \eta \frac{\partial L}{\partial \mathbf{W}} \tag{식 6.3}\) \(\mathbf{W} \leftarrow \mathbf{W} + \mathbf{v} \tag{식 6.4}\)
[식 6.1]의 SGD처럼 여기에서도 $\mathbf{W}$는 갱신할 가중치 매개변수, $\frac{\partial L}{\partial \mathbf{W}}$은 $\mathbf{W}$에 대한 손실 함수의 기울기, $\eta$는 학습률입니다. $\mathbf{v}$라는 변수가 새로 나오는데, 이는 물리에서 말하는 속도${}^{\text{velocity}}$에 해당합니다. [식 6.3]은 기울기 방향으로 힘을 받아 물체가 가속된다는 물리 법칙을 나타냅니다. 모멘텀은 [그림 6-4]와 같이 공이 그릇의 바닥을 구르는 듯한 움직임을 보여줍니다.
그림 6-4 모멘텀의 이미지: 공이 그릇의 곡면(기울기)을 따라 구르듯 움직인다.

또, [식 6.3]의 $\alpha \mathbf{v}$ 항은 물체가 아무런 힘을 받지 않을 때 서서히 하강시키는 역할을 합니다($\alpha$는 0.9 등의 값으로 설정합니다). 물리에서는 지면 마찰이나 공기 저항에 해당하죠.
토토가 매끄러운 그릇 모양의 비탈길에서 무거운 쇠공을 아래로 힘차게 밀어 굴리고 있어요. 공은 물리적 속도의 관성을 얻어 좌우 요동치지 않고 곧장 밑바닥을 향해 부드럽게 굴러가고 있어요.
토토와 도로시의 대화 (Momentum)
- 토토: “왈왈! (가속이 붙은 쇠공을 가리키며) 멈추지 않고 슝 지나간다 멍!”
- 도로시: “와! 물리 법칙의 속도($v$)와 마찰 저항($\alpha$)을 공식에 넣었더니, 지그재그로 요동치던 힘이 관성에 의해 상쇄되어 바닥을 향해 썰매를 타듯이 매끄럽고 빠르게 수렴해 가네!”
다음은 모멘텀의 구현입니다(소스 코드는 common/optimizer.py에 있습니다).
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]
인스턴스 변수 v가 물체의 속도입니다. v는 초기화 때는 아무 값도 담지 않고, 대신 update()가 처음 호출될 때 매개변수와 같은 구조의 데이터를 딕셔너리 변수로 저장합니다. 나머지 부분은 [식 6.3]과 [식 6.4]를 간단히 코드로 옮긴 것입니다.
이제 모멘텀을 사용해서 [식 6.2]의 최적화 문제를 풀어봅시다. 결과는 [그림 6-5]처럼 됩니다.
그림 6-5 모멘텀에 의한 최적화 갱신 경로

그림에서 보듯 모멘텀의 갱신 경로는 공이 그릇 바닥을 구르듯 움직입니다. SGD와 비교하면 ‘지그재그 정도’가 덜한 것을 알 수 있죠. 이는 $x$축의 힘은 아주 작지만 방향은 변하지 않아서 한 방향으로 일정하게 가속하기 때문입니다. 거꾸로 $y$축의 힘은 크지만 위아래로 번갈아 받아서 상쇄하여 $y$축 방향의 속도는 안정적이지 않습니다. 전체적으로는 SGD보다 $x$축 방향으로 빠르게 다가갈 수 있어서 지그재그 움직임을 줄일 수 있습니다.
6.1.5 AdaGrad
신경망 학습에서는 학습률(수식에서는 $\eta$로 표기) 값이 중요합니다. 이 값이 너무 작으면 학습 시간이 너무 길어지고, 반대로 너무 크면 발산하여 올바른 학습을 할 수 없습니다.
이 학습률을 정하는 효과적 기술로 학습률 감소${}^{\text{learning rate decay}}$가 있습니다. 이는 학습을 진행하면서 학습률을 점차 줄여가는 방법입니다. 처음에는 크게 학습하다가 조금씩 작게 학습한다는 얘기로, 실제 신경망 학습에 자주 쓰입니다.
학습률을 서서히 낮추는 가장 간단한 방법은 매개변수 ‘전체’의 학습률 값을 일괄적으로 낮추는 것이겠죠. 이를 더욱 발전시킨 것이 AdaGrad[6]입니다. AdaGrad는 ‘각각의’ 매개변수에 ‘맞춤형’ 값을 만들어줍니다.
AdaGrad는 개별 매개변수에 적응적으로${}^{\text{adaptive}}$ 학습률을 조정하면서 학습을 진행합니다. AdaGrad의 갱신 방법은 수식으로는 다음과 같습니다.
\(\mathbf{h} \leftarrow \mathbf{h} + \frac{\partial L}{\partial \mathbf{W}} \odot \frac{\partial L}{\partial \mathbf{W}} \tag{식 6.5}\) \(\mathbf{W} \leftarrow \mathbf{W} - \eta \frac{1}{\sqrt{\mathbf{h}}} \odot \frac{\partial L}{\partial \mathbf{W}} \tag{식 6.6}\)
마찬가지로 $\mathbf{W}$는 갱신할 가중치 매개변수, $\frac{\partial L}{\partial \mathbf{W}}$은 $\mathbf{W}$에 대한 손실 함수의 기울기, $\eta$는 학습률을 뜻합니다. 여기에서는 새로 $\mathbf{h}$라는 변수가 등장합니다. $\mathbf{h}$는 [식 6.5]에서 보듯 기존 기울기 값을 제곱하여 계속 더해줍니다([식 6.5]의 $\odot$ 기호는 행렬의 원소별 곱셈을 의미합니다). 그리고 매개변수를 갱신할 때 $\frac{1}{\sqrt{\mathbf{h}}}$을 곱해 학습률을 조정합니다. 매개변수의 원소 중에서 많이 움직인(크게 갱신된) 원소는 학습률이 낮아진다는 뜻인데, 다시 말해 학습률 감소가 매개변수의 원소마다 다르게 적용됨을 뜻하죠.
NOTE_ AdaGrad는 과거의 기울기를 제곱하여 계속 더해갑니다. 그래서 학습을 진행할수록 갱신 강도가 약해집니다. 실제로 무한히 계속 학습한다면 어느 순간 갱신량이 0이 되어 전혀 갱신되지 않게 되죠. 이 문제를 개선한 기법으로서 RMSProp[7]이라는 방법이 있습니다. RMSProp은 과거의 모든 기울기를 균일하게 더해가는 것이 아니라, 먼 과거의 기울기는 서서히 잊고 새로운 기울기 정보를 크게 반영합니다. 이를 지수이동평균${}^{\text{Exponential Moving Average, EMA}}$이라 하여, 과거 기울기의 반영 규모를 기하급수적으로 감소시킵니다.
도로시가 자주 다닌 경로에서는 보폭을 좁히고, 거의 다니지 않아 정보가 없는 경로에서는 크게 걸어 나가는 맞춤형 학습 부츠를 신어보고 있어요.
도로시의 맞춤형 보폭 체험 (AdaGrad)
- 도로시: “와! 이 신발은 내가 많이 밟은 땅(기울기가 커서 이미 많이 갱신된 매개변수) 위에서는 보폭을 좁혀서 조심조심 걷고, 덜 밟은 곳에서는 보폭을 넓혀 성큼성큼 나아가게 조절해 줘!”
- 지니: “맞아! 그게 바로 오차의 역사적 크기 누적값 $h$의 제곱근에 반비례하여 매개변수마다 맞춤형 학습률을 계산해 주는 AdaGrad의 마법이야. 처음엔 빠르게, 안정된 후에는 정밀하게 수렴하는 원동력이 되지.”
그럼 AdaGrad의 구현을 살펴보겠습니다(소스 코드는 common/optimizer.py에 있습니다).
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를 사용해서 [식 6.2]의 최적화 문제를 풀어보겠습니다. 결과는 [그림 6-6]처럼 됩니다.
그림 6-6 AdaGrad에 의한 최적화 갱신 경로

[그림 6-6]을 보면 최솟값을 향해 효율적으로 움직이는 것을 알 수 있습니다. $y$축 방향은 기울기가 커서 처음에는 크게 움직이지만, 그 큰 움직임에 비례해 갱신 정도도 큰 폭으로 작아지도록 조정됩니다. 그래서 $y$축 방향으로 갱신 강도가 약해졌고, 지그재그 움직임이 줄어듭니다.
6.1.6 Adam
모멘텀은 공이 그릇을 구르는 듯한 물리 법칙에 따르는 움직임을 보였습니다. AdaGrad는 매개변수의 원소마다 적응적으로 갱신 정도를 조정했습니다. 그럼 혹시 이 두 기법을 융합하면 어떻게 될까요? 이런 생각을 기초로 만든 기법이 바로 Adam[8]입니다.*
Adam은 2015년에 제안된 새로운 방법입니다. 그 이론은 다소 복잡하지만 직관적으로는 모멘텀과 AdaGrad를 융합한 듯한 방법입니다. 이 두 방법의 이점을 조합했다면 매개변수 공간을 효율적으로 탐색해줄 것으로 기대해도 좋겠죠? 또, 하이퍼파라미터의 ‘편향 보정’이 진행된다는 점도 Adam의 특징입니다. 여기에서는 더 깊게 파고들진 않겠습니다. 궁금하신 분은 원논문[8]을 참고하시고, 또 common/optimizer.py에 Adam 클래스를 구현해두었으니 함께 보시면 도움이 될 겁니다.
그럼 Adam을 사용하여 [식 6.2]의 최적화 문제를 풀어보겠습니다. 결과는 [그림 6-7]과 같습니다.
그림 6-7 Adam에 의한 최적화 갱신 경로

* 이 책은 Adam을 이해하기 쉽도록 직관적으로 설명하여, 완전히 정확하지는 않습니다. 자세한 내용은 원논문을 참고하세요.
[그림 6-7]과 같이 Adam 갱신 과정도 그릇에서 공이 구르는 듯 움직임입니다. 모멘텀과 비슷한 패턴인데, 모멘텀 때보다 공의 좌우 흔들림이 적습니다. 이는 학습의 갱신 강도를 적응적으로 조정해서 얻는 혜택입니다.
지니가 관성의 모멘텀과 개별 맞춤형 학습률의 AdaGrad 엔진이 결합된 하이브리드 마법 자동차(Adam)를 운전해 울퉁불퉁하고 휘어진 곡면 경사를 매우 부드럽고 신속하게 돌파해 나가는 모습을 도로시가 환호하며 보고 있어요.
지니의 하이브리드 설명 (Adam)
- 지니: “도로시! 모멘텀의 ‘속도 관성 효과’와 AdaGrad의 ‘보폭 맞춤 조정 효과’를 영리하게 융합한 하이브리드 엔진이 바로 Adam이야! 2015년 발표된 이래 딥러닝 세계에서 가장 널리 사랑받는 만능 해결사지.”
- 도로시: “와! 두 가지의 최적 장점만 쏙 합쳐놓으니까, 흔들림도 적고 복잡한 협곡 코스에서도 막힘없이 부드러우면서 가장 빠른 속도로 골짜기 밑바닥까지 무사히 운전해 가는구나!”
- 토토: “왈왈! (지니 옆 보조석에서 신나하며) 속도와 조향 밸런스가 최고다 멍!”
NOTE_ Adam은 하이퍼파라미터를 3개 설정합니다. 하나는 지금까지의 학습률(논문에서는 $\alpha$로 등장), 나머지 두 개는 일차 모멘텀용 계수 $\beta_1$과 이차 모멘텀용 계수 $\beta_2$입니다. 논문에 따르면 기본 설정값은 $\beta_1$은 0.9, $\beta_2$는 0.999이며, 이 값이면 많은 경우에 좋은 결과를 얻을 수 있습니다.
6.1.7 어느 갱신 방법을 이용할 것인가?
지금까지 매개변수의 갱신 방법을 4개 살펴봤습니다. 이번 절에서는 이들 네 기법의 결과를 비교해보겠습니다(소스 코드는 ch06/optimizer_compare_naive.py에 있습니다).
그림 6-8 최적화 기법 비교: SGD, 모멘텀, AdaGrad, Adam

[그림 6-8]과 같이 사용한 기법에 따라 갱신 경로가 다릅니다. 이 그림만 보면 AdaGrad가 가장 나은 것 같은데, 사실 결과는 풀어야 할 문제가 무엇이냐에 따라 달라지므로 주의해야 합니다. 또한, 당연하지만 (학습률 등의) 하이퍼파라미터를 어떻게 설정하느냐에 따라서도 결과가 바뀝니다.
SGD, 모멘텀, AdaGrad, Adam의 네 후보 중 어느 것을 채택하면 될까요? 유감스럽게도 모든 문제에서 항상 뛰어난 기법이라는 것은 (아직까지는) 없습니다. 각자의 장단이 있어 잘 푸는 문제와 서툰 문제가 있죠.
지금도 많은 연구에서 SGD를 사용하고 있습니다. 모멘텀과 AdaGrad도 시도해볼 만한 가치 있는 방법입니다. 요즘에는 많은 분들이 Adam에 만족해하며 쓰는 것 같습니다. 이 책에서는 주로 SGD와 Adam을 사용합니다만, 여러분은 각자의 상황을 고려해 여러 가지로 시도해보세요.
6.1.8 MNIST 데이터셋으로 본 갱신 방법 비교
손글씨 숫자 인식을 대상으로 지금까지 설명한 네 기법을 비교해봅시다. 각 방법의 학습 진도가 얼마나 다른지를 [그림 6-9]에 그려보았습니다(소스 코드는 ch06/optimizer_compare_mnist.py에 있습니다).
그림 6-9 MNIST 데이터셋에 대한 학습 진도 비교

이 실험은 각 층이 100개의 뉴런으로 구성된 5층 신경망에서 ReLU를 활성화 함수로 사용해 측정했습니다.
[그림 6-9]의 결과를 보면 SGD의 학습 진도가 가장 느리군요. 나머지 세 기법의 진도는 비슷한데, 잘 보면 AdaGrad가 조금 더 빠른 것 같습니다. 이 실험에서 주의할 점은 하이퍼파라미터인 학습률과 신경망의 구조(층 깊이 등)에 따라 결과가 달라진다는 것입니다. 다만 일반적으로 SGD보다 다른 세 기법이 빠르게 학습하고, 때로는 최종 정확도도 높게 나타납니다.