학습률을 설정하는 방법

  • 신경망 모델을 학습시킬 때 일반적으로 경사하강법(GDA)을 사용하는데, 이 때 학습률은 \(0.1\)이나 \(0.01\)과 같은 값을 사용하였다.
  • 학습률의 값을 어떻게 설정하는지에 따라 최적해가 구해지는지 또는 구해지지 않는지가 정해진다고 하면 이 학습률도 적절히 설정하는 방법들이 개발되어 있다.


모멘텀(Momentum)

  • 국소최적해에 빠지지 않고 효율적으로 해를 구하려면 학습률을 '처음에는 크게, 그리고 점점 작게'하는 것이 바람직하다고 알려져 있다.
  • 모멘텀은 학습률의 값 자체는 같지만 매개변수를 변경해갈 때 모멘텀 항이라는 조정항을 사용해 유사적으로 '처음에는 크게 그리고 점점 작게'라는 개념을 표현한다.
  • 오차함수 \(E\)에 대한 신경망 모델의 매개변수를 \(\theta\)라 하고 \(\theta\)에 대한 \(E\)의 경사를 \(\nabla_\theta E\), 매개변수의 차 \(\Delta\theta^{(t)}\)를 식 \((13.1)\)라고 하면 스텝 \(t\)에서 모멘텀을 사용해  매개변수를 변경해 가는 식은 식 \((13.2)\)와 같다.
    • \(\gamma\Delta\theta^{(t-1)}\) : 모멘텀항
    • 계수 \(\gamma(<1)\)는 일반적으로 \(0.5\)나 \(0.9\)와 같은 값을 설정

\begin{align}\Delta\theta^{(t)} &= \theta^{(t-1)} - \theta^{(t)} \tag{13.1}\\&=-\eta\nabla_\theta E(\theta) + \gamma \Delta\theta^{(t-1)} \tag{13.2}\\\\ \Delta\theta^{(t)} - \gamma \Delta\theta^{(t-1)}&= -\eta\nabla_\theta E(\theta)\tag{13.3}\\\\\frac{1}{\eta}\Delta\theta^{(t)} - \frac{\gamma}{\eta} \Delta\theta^{(t-1)}&= -\nabla_\theta E(\theta)\tag{13.4}\end{align}

  • 식 \((13.2)\)는 물리학에 비유하면 식 \((13.5)\)와 같은 소위 '공기 저항식'과 같은 모양으로 단계가 높아질 수록 경사가 점점 낮아진다는 것을 알 수 있다.

\begin{align} m\frac{d^2\theta}{dt^2} + \mu\frac{d\theta}{dt} = -\Delta_\theta E(\theta)\tag{13.5}\end{align}


  • TensorFlow에서는 tf.train.momentumOptimizer()를 사용하면 모멘텀을 구현할 수 있다.
def train(loss):
    optimizer = tf.train.MomentumOptimizer(learning_rate=0.01, momentum=0.9)
    train = optimizer.minimize(loss)
    return train

그림 13.1 TensorFlow에서 Momentum Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 SGD()의  매개변수로 momentum=을 지정하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=SGD(lr=0.01, momentum=0.9), 
              metrics=['accuracy'])

그림 13.2 Keras에서 Momentum Optimizer를 사용했을 때의 정확도와 오차 변화


Nesterov Momentum

  • Yurii Nesterov, A method for unconstrained convex minimization problem with the rate of convergence \(O(1/k^2)\) . Doklady AN SSSR , 269(3):543–547, 1983
    • 해당 논문의 원문을 찾을 수가 없어 링크를 걸지 못함
  • Nesterov는 식 \((13.2)\)로 표현되는 순수한 모멘텀을 약간 변형해서 매개변수가 '어느 방향을 향해야 하는지'를 식으로 만든 것으로 식 \((13.2)\)에서 \(\Delta\theta^{(t)}=v^{(t)}\)라 놓으면 식 \((13.2)\)는 식 \((13.5)\)와 식 \((13.6)\)으로 표현할 수 있다.
\begin{align} v^{(t)} &= -\eta \nabla _\theta E(\theta) + \gamma \Delta \theta ^{(t-1)} \tag{13.5}\\\\ \theta^{(t)} &= \theta^{(t-1)} - v^{(t)}\tag{13.6}\end{align}
  • \((13.5)\)에 식 \((13.6)\)을 적용하여 모멘텀을 다시 표현하면 식 \((13.7)\)과 식 \((13.8)\)로 나타낼 수 있다.

\begin{align} v^{(t)} &= -\eta \nabla _\theta E(\theta + \gamma v^{(t-1)}) + \gamma \Delta \theta ^{(t-1)} \tag{13.7}\\ &= -\eta \nabla_\theta E(\theta + \gamma \Delta\theta^{(t-1)})+ \gamma \Delta \theta ^{(t-1)}\tag{13.8}\\\\ \theta^{(t)} &= \theta^{(t-1)} - v^{(t)}\tag{13.9}\end{align}

  • \((13.5)\)와 식 \((13.7)\)의 차이는 \(E(\theta + \gamma v^{(t-1)})\)에 있는데, 이 차이로 인해 다음 단계에서 매개변수의 근사값이 구해지므로 학습률을 효율적으로 설정해 해를 구할 수 있다.


  • TensorFlow에서는 tf.train.momentumOptimizer()의 매개변수로  nesterov=True를 지정하면 된다.
def train(loss):
    optimizer = tf.train.MomentumOptimizer(learning_rate=0.01, momentum=0.9, use_nesterov=True)
    train = optimizer.minimize(loss)
    return train

그림 13.3 TensorFlow에서 Nestrov Momentum Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 SGD()의 매개변수로 nesterov=True를 지정하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=SGD(lr=0.01, momentum=0.9, nesterov=True), 
              metrics=['accuracy'])

그림 13.4 Keras에서 Nestrov Momentum Optimizer를 사용했을 때의 정확도와 오차 변화


Adaptive Gradient(Adagrad) 알고리즘

  • 모멘텀은 학습률 값을 고정하고 모멘텀 항으로 매개변수의 변경값을 조정했지만 Adagrad 방법은 학습률 값 자체를 변경한다.
  • 다음과 같이 정의하고 식을 전개해보자.

\begin{align} g_i = \nabla_\theta E(\theta_i)\tag{13.10}\end{align}

  • Adagrad 방법은 식 \((13.11)\)처럼 표현된다.

\begin{align} \theta_i^{(t+1)} = \theta_i^{(t)} - \frac{\eta}{\sqrt{G_{ii}^{(t)} +\epsilon}}\odot g^{(t)}\tag{13.11}\end{align}

  • 행렬 \(G^{(t)}\)는 대각행렬이며, \((i,i)\) 성분은 \(t\)번째 단계까지 \(\theta_i\)에 관한 경사를 제곱해서 총합을 낸 것으로 식 \((13.12)\)로 표현할 수 있다.

\begin{align} G_{ii}^{(t)} = \sum_{\tau=0}^t g_i^{(\tau)} \cdot g_i^{(\tau)}\tag{13.12}\end{align}

  • \(\epsilon\)은 분모가 \(0\)이 되지 않도록 하기 위한 작은 값으로 일반적으로 \(\epsilon=1.0\times 10^{-6}\sim1.0\times 10^{-8}\)정도로 설정한다.
  • 식 \((13.11)\)은 다음과 같이 정리할 수 있다.

\begin{align} \theta^{(t+1)} = \theta^{(t)} - \frac{\eta}{\sqrt{G^{(t)} + \epsilon}}\odot G^{(t)}\tag{13.13}\end{align}

  • Adagrad 알고리즘은 모멘텀과 비교하면 하이퍼  매개변수가 적고 이제까지의 경사를 바탕으로 해서 자동으로 학습률 \(\eta\)를 수정하므로 더욱 다루기 쉬운 기법이라고 할 수 있다.
  • 식 \((13.11)\)이나 식 \((13.13)\)의 유사 코드는 다음과 같지만 TensorFlow와 Keras에서 Adagrad 알고리즘 관련 API를 제공하므로 라이브러리를 사용하면 된다.
G[i][i] += g[i] * g[i]
theta[i] -= (learning_rate / sqrt(G[i][i] + epsilon)) * g[i]


  • TensorFlow에서는 tf.train.AdagradOptimizer()를 사용하면 된다.
def train(loss):
    optimizer = tf.train.AdagradOptimizer(learning_rate=0.01)
    train = optimizer.minimize(loss)
    return train

그림 13.5 TensorFlow에서 Adagrad Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 from keras.optimizers import Adagrad를 선언하고 optimizer=Adagrad()를 사용하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=SGD(lr=0.01, momentum=0.9, nesterov=True), 
              metrics=['accuracy'])

그림 13.6 Keras에서 Adagrad Optimizer를 사용했을 때의 정확도와 오차 변화


Adadelta 알고리즘

  • Adagrad 알고리즘을 통해 학습률을 자동으로 조정할 수 있게 되었지만 대각행렬 \(G^{(t)}\)는 경사의 제곱의 누적합이기 때문에 단조증가한다.
  • 따라서 식 \((13.13)\)에서도 알 수 있듯이 학습의 단계가 진행될 수록 곱해지는 계수의 값이 급격하게 작아져버려 학습을 진행할 수 없게되는 문제가 발생하는데 이 문제를 해결한 것이 Adadelta 알고리즘이다.
  • Adadelta의 기본적인 개념은 \(0\)번째 단계부터 제곱합을 누적해가는 것이 아니라 누적해가는 단계 수를 정수 \(w\)로 제한하는 것이다.
  • 단순히 \(w\)만큼 제곱합을 동시에 유지하는 것은 구현의 관점에서 보면 비효율적인 방법이므로 Adadelta는 직전의 단계까지의 모든 경사의 제곱합을 감쇠평균시켜서 재귀식으로 계산한다.
  • 식을 간단하게 정리하기 위해 이제까지 \(g^{(t)}\)라고 한 것을 \(g_t\)라고 쓰면 \(t\)단계에서의 경사의 제곱(\(g_t\odot g_t\))의 이동평균 \(E[g^2]_t\)는 다음과 같이 쓸 수 있다.

\begin{align} E[g^2]_t = \rho E[g^2]_{t-1} + (1-\rho)g^2_t\tag{13.14}\end{align}

  • 식 \((13.14)\)를 보면 단계가 거듭될 수록 경사의 합이 지수함수적으로 작아져간다는 것을 알 수 있다.
  • 그런데 Adagrad를 나타내는 식은 식 \((13.15)\)인데

\begin{align} \theta_{t+1} = \theta_{t} - \frac{\eta}{\sqrt{G^{t} + \epsilon}}\odot G_t\tag{13.15}\end{align}

  • Adadelta는 \(G_t\)를 이제까지의 경사를 제곱한 것의 감쇠평균인 \(E[g^2]_t\)로 치환되므로 식 \((13.16)\)과 같이 나타낼 수 있다.

\begin{align} \theta^{(t+1)} = \theta^{(t)} - \frac{\eta}{\sqrt{E[g^2]_t} + \epsilon}g^t\tag{13.16}\end{align}

  • 이 때 \(\sqrt{E[g^2]_t + \epsilon}\)에 주목하면 (\(\epsilon\)을 무시하면) 제곱평균제곱근(root mean square) 형태이기 때문에 \(\text{RMS}[\cdot]\)로 나타내면 식 \((13.16)\)은 식 \((13.17)\)과 같이 정리된다.

\begin{align} \theta^{(t+1)} = \theta^{(t)} - \frac{\eta}{\textrm{RMS}[g]_t}g^t\tag{13.17}\end{align}

  • 해당 논문에서는 식 \((13.17)\)을 한 단계 더 변형해서 학습률 \(\eta\)를 설정하지 않아도 되게 한다. 다음과 같이 정의하면

\begin{align} \Delta\theta_t = \theta^{(t)} - \frac{\eta}{\textrm{RMS}[g]_t}g^t\tag{13.17}\end{align}

  • 식 \((13.14)\)에 대해 \(\nabla \theta^2_t\)의 감쇠평균은 식 \((13.18)\)과 같이 나타낼 수 있으며

\begin{align} E[\Delta \theta^2]_t = \rho E[\Delta \theta^2]_{t-1} + (1-\rho)\Delta \theta^2_t\tag{13.18}\end{align}

  • \(\Delta\theta_t\)는 알 수 없으므로

\begin{align} \text{RMS}[\Delta\theta]_t=\sqrt{E[\Delta\theta^2]_t+\epsilon}\tag{13.19}\end{align}

  • 식 \((13.19)\)를 \(t-1\)에서의 \(\text{RMS}\)로 근사하면 식 \((13.20)\)으로 표현되는 Adadelta 식이 구해진다.

\begin{align} \Delta\theta_t = - \frac{\text{RMS}[\Delta\theta]_{t-1}}{\text{RMS}[g]_t}g_t\tag{13.20}\end{align}

  • 결국 Adadelta에서는 \(\rho\)(또는 \(epsilon\))만 설정하면 된다는 것이며 이것은 일반적으로 \(\rho=0.95\)로 설정한다.


  • TensorFlow에서는 tf.train.AdadeltaOptimizer()를 사용하면 된다.
    • 수식에서는 식 \((13.18)\)처럼 학습률이 필요없지만 구현할 때에는 learning_rate=1.0이라고 해야 한다. 이는 \(\theta_{t+1}=\theta_t+\alpha\Delta\theta_t\)의 \(\alpha\)를 설정하기 위한 것이다.
    • TensorFlow에서는 기본적으로 이 값을 \(0.001\)로 정하는데 앞서 나온 식의 흐름을 따라가보면 이 값을 \(1.0\)으로 정해도 문제없으므로 learning_rate=1.0을 매개변수로 넘겨준 것이다.
def train(loss):
    optimizer = tf.train.AdadeltaOptimizer(learning_rate=1.0, rho=0.95)
    train = optimizer.minimize(loss)
    return train

그림 13.7 TensorFlow에서 Adadelta Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 from keras.optimizers import Adadelta를 선언하고 optimizer=Adadelta(rho=0.95)를 사용하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=Adadelta(rho=0.95), 
              metrics=['accuracy'])

그림 13.8 Keras에서 Adadelta Optimizer를 사용했을 때의 정확도와 오차 변화


RMSprop 알고리즘

  • RMSprop 알고리즘은 Adadelta와 마찬가지로 Adagrad에서 학습률이 급격하게 감소하는 문제를 해결하기 위한 기법이다.
  • RMSprop 알고리즘은 Adadelta와 비슷한 시기에 고안된 기법이지만 논문의 형태로 존재하지 않고 Coursera 온라인 강의에 사용되는 강의 자료로 정리되어 있는 기법이다.
  • RMSprop은 Adadelta의 간이 버전이라고 할 수 있으며 식 \((13.14)\)에서 \(\rho=0.9\)로 설정해서

\begin{align} E[g^2]_t &= \rho E[g^2]_{t-1} + (1-\rho)g^2_t\tag{13.21}\\ &=0.9 E[g^2]_{t-1} + (1-0.9)g^2_t\tag{13.22}\\ &=0.9 E[g^2]_{t-1} + 0.1g^2_t\tag{13.23}\end{align}

  • 식을 위와 같이 두고

\begin{align} \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{E[g^2]_t+\epsilon}}g^t\tag{13.24}\end{align}

  • 식 \((13.24)\)로 매개변수를 변경해가는 기법이다. 학습률 \(\eta\)는 일반적으로 \(0.001\)과 같은 작은 값으로 설정한다.


  • TensorFlow에서는 tf.train.RMSpropOptimizer()를 사용하면 된다.
def train(loss):
    optimizer = tf.train.RMSpopOptimizer(learning_rate=0.01)
    train = optimizer.minimize(loss)
    return train

그림 13.9 TensorFlow에서 RMSprop Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 from keras.optimizers import RMSprop을 선언하고 optimizer=RMSprop(lr=0.001)를 사용하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=RMSprop(lr=0.001), 
              metrics=['accuracy'])

그림 13.10 Keras에서 RMSprop Optimizer를 사용했을 때의 정확도와 오차 변화


Adam 알고리즘

  • Adadelta와 RMSprop이 직전의 단계인 \(t-1\)까지 경사의 제곱의 이동평균 \(v_t=E[g^2]_t\)를 지수함수적으로 감쇠평균한 항을 저장해가며 유지하고 매개변수의 변경식에 이 값을 사용했던 것과는 달리 Adam(Adaptive moment estimation)에서는 여기에 추가로 단순한 경사의 이동평균인 \(m_t=E[g]_t\)를 지수함수적으로 감쇠시킨 항도 사용한다.
  • \(m_t\)와 \(v_t\)는 각각 식 \((13.25)\)와 식 \((13.26)\)으로 나타낼 수 있다.
    • \(\beta_1,\beta_2\in [0,1]\)은 하이퍼 매개변수이며 이동평균의 (지수함수적인) 감쇠율을 조정한다.
    • \(m_t\)와 \(v_t\)는 각각 경사의 1차 모멘트 (평균), 2차 모멘트 (분산)의 추정값에 해당한다.

\begin{align} m_t &= \beta_1m_{t-1} + (1-\beta_1)g_t\tag{13.25}\\\\ v_t &= \beta_2v_{t-1} + (1-\beta_2)g^2_t\tag{13.26}\end{align}

  • 두 개의 이동평균 \(m_t\)와 \(v_t\)는 모두 편중된 모멘트이므로 이 편중은 보정한(편중을 \(0\)으로 만든) 추정값을 구하는 것을 생각해보자.
  • 이 때, \(v_0=\mathbf{0}\)으로 초기화했다고 가정하면 식 \((13.26)\)에 의해 식 \((13.27)\)을 얻을 수 있다.

\begin{align} v_t=(1-\beta_2)\sum_{i=1}^t\beta_2^{t-i}\cdot g_i^2\tag{13.27}\end{align}

  • 이제 우리가 알고싶은 것은 2차 모멘트 \(v_t\)의 이동평균 \(E[v_t]\)와 2차 모멘트 \(E[g_t^2]\)의 관련성이므로 이것을 식 \((13.27)\)을 통해 구하면 다음과 같은 식을 얻을 수 있다.

\begin{align} E[v_t] &= E\Bigg[(1-\beta_2)\sum_{i=1}^t\beta_2^{t-i}\cdot g_i^2\Bigg]\tag{13.28}\\&= E[g_i^2]\cdot(1-\beta_2)\sum_{i=1}^t\beta_2^{t-i}+\zeta\tag{13.29}\\&= E[g_t^2]\cdot(1-\beta_2^t) + \zeta\tag{13.30}\end{align}

  • 이 때 \(\zeta=0\)으로 근사할 수 있도록 하이퍼 매개변수의 값을 설정하면 식 \((13.31)\)을 얻을 수 있다.
    • 2차 모멘트 \(E[g_t^2]\)가 불변이면 \(\zeta=0\)이고, 그렇지 않으면 각 감쇠율 \(1-\beta_1\)과 \(1-\beta_2\)를 축소시켜 \(zeta=0\)으로 근사시킬 수 있다.
    • 따라서 일반적으로 \(\beta_1=0.9\), \(\beta_2=0.999\)로 설정한다.

\begin{align} \hat{v}_t = \frac{v_t}{1-\beta_2^t}\tag{13.31}\end{align}

  • 식 \((13.31)\)과 같이 (편중되지 않은) 추정값을 얻을 수 있으며, \(m_t\)도 같은 방식으로 계산하면 식 \((13.32)\)을 얻을 수 있다.

\begin{align} \hat{m}_t = \frac{m_t}{1-\beta_1^t}\tag{13.32}\end{align}

  • 따라서 매개변수 변경식은 다음과 같이 식 \((13.33)\)으로 정리된다.

\begin{align}\theta_t = \theta_{t-1} - \frac{\alpha}{\sqrt{\hat{v}_t}+\epsilon}\hat{m}_t\tag{13.33}\end{align}

  • Adam도 식은 복잡하지만 최종적인 알고리즘은 단순하게 정리되는데 다음과 같은 유사코드를 보며 생각해보자.
# 초기화
m = 0
v = 0

# 반복
m = beta1 * m + (1 - beta1) * g
v = beta2 * v + (1 - beta2) * g * g
m_hat = m / (1 - beta1 ** t)
v_hat = v / (1 - beta2 ** t)
theta -= learning_rate * m_hat / (sqrt(v_hat) + epsilon)


  • 구체적은 계산은 생략하겠지만 학습률 \(\alpha\)는 식 \((13.34)\)와 같다.

\begin{align}\alpha_t = \alpha \cdot \frac{\sqrt{1-\beta_2^t}}{1-\beta_1^t} \tag{13.34}\end{align}

  • 식 \((13.34)\)를 만족시키는 \(\alpha\)를 사용하면 더욱 효율적으로 탐색할 수 있다. 이 경우의 유사코드는 다음과 같다.
# 초기화
m = 0
v = 0

# 반복
learning_rate_t = learning_rate * sqrt(1 - beta2 ** t) / (1 - beta1 ** t)
m = beta1 * m + (1 - beta1) * g
v = beta2 * v + (1 - beta2) * g * g
theta -= learning_rate * m / (sqrt(v) + epsilon)


  • TensorFlow에서는 tf.train.AdamOptimizer()를 사용하면 된다.
def train(loss):
    optimizer = tf.train.AdamOptimizer(learning_rate=0.001,
                                       beta1=0.9,
                                       beta2=0.999)
    train = optimizer.minimize(loss)
    return train

그림 13.11 TensorFlow에서 Adam Optimizer를 사용했을 때의 정확도와 오차 변화


  • Keras에서는 from keras.optimizers import Adam을 선언하고 optimizer=Adam(lr=0.001, beta_1=0.9, beta_2=0.999)를 사용하면 된다.
model.compile(loss='categorical_crossentropy', 
              optimizer=Adam(lr=0.001, beta_1=0.9, beta_2=0.999), 
              metrics=['accuracy'])

그림 13.12 Keras에서 Adam Optimizer를 사용했을 때의 정확도와 오차 변화



출처 : 정석으로 배우는 딥러닝

+ Recent posts