6 분 소요

본 글은 [핸즈온 머신러닝 2판 (Hands-On Machine Learning with Scikit-Learn, keras & TensorFlow)] - (박해선 옮김) 책을 개인공부하기 위해 요약, 정리한 내용입니다.
전체 코드는 https://github.com/ingu627/handson-ml2에 기재했습니다.(원본 코드 fork)
뿐만 아니라 책에서 설명이 부족하거나 이해가 안 되는 것들은 외부 자료들을 토대로 메꿔 놓았습니다.
오타나 오류는 알려주시길 바라며, 도움이 되길 바랍니다.





11.3 고속 옵티마이저

  • 지금까지는 훈련 속도를 높이는 네 가지 방법을 봄 (연결 가중치에 좋은 초기화 전략 적용하기, 좋은 활성화 함수 사용하기, 배치 정규화 사용하기, 사전훈련된 네트워크의 일부 재사용하기)
  • 훈련 속도를 크게 높일 수 있는 또 다른 방법으로 표준적인 경사 하강법 옵티마이저 대신 더 빠른 옵티마이저를 사용할 수 있다.
    • 모멘텀 최적화
    • 네스테로프(Nesterov) 가속 경사(NAG)
    • AdaGrad
    • RMSProp
    • Adam 최적화
    • Nadam 최적화



11.3.1 모멘텀 최적화

  • 표준적인 경사 하강법은 경사면을 따라 일정한 크기의 스텝으로 조금씩 내려 간다.
    • 경사 하강법의 공식 : $\theta \leftarrow \theta-\eta\nabla_{\theta}J(\theta)$
      • $\theta$ : 가중치
      • $\eta$ : 학습률
      • $J(\theta)$ : 가중치에 대한 비용 함수
    • 이 식은 이전 그레이디언트가 얼마였는지 고려하지 않는다.
  • 모멘텀은 가중치 조정 과정을 추적하면서 변화 가속도를 조절한다.
    • 일반 경사하강법보다 빠르게 전역 최소점에 도달
  • 모멘텀의 이해는 볼링공이 매끈한 표면의 완만한 경사를 따라 굴러간다면 처음에는 느리게 출발하지만 종단속도 에 도달할 때까지는 빠르게 가속되는 것을 생각해본다.
    • 모멘텀 최적화는 이전 그레이디언트가 얼마였는지를 상당히 중요하게 생각한다.
    • 그레이디언트를 가속도로 사용한다.
    • 일종의 마찰저항을 표현하고 모멘텀이 너무 커지는 것을 막기 위해 이 알고리즘에는 모멘텀이라는 새로운 하이퍼파라미터 $\beta$가 등장한다.
      • 이 값은 0 (높은 마찰저항)과 1 (마찰저항 없음) 사이로 설정되어야 한다. (default : 0.9)
  • 모멘텀 알고리즘
    1. $ m \leftarrow \beta m - \eta \nabla_{\theta} J(\theta)$
    2. $ \theta \leftarrow \theta + m $

image 이미지출처: 1

  • 모멘텀 최적화는 골짜기를 따라 바닥(최적점)에 도달할 때까지 점점 더 빠르게 내려간다.
  • 모멘텀 때문에 옵티마이저가 최적값에 안정되기 전까지 건너뛰었다가 다시 돌아오고, 다시 또 건너뛰는 식으로 여러 번 왔다 갔다 할 수 있다.
from tensorflow.keras.optimizers import SGD

optimizer = SGD(lr=0.001, momentum=0.9)



11.3.2 네스테로프 가속 경사

  • 네스테로프 가속 경사 (NAG; Nesterov accelerated gradient)는 현재 위치가 $\theta$가 아니라 모멘텀의 방향으로 조금 앞선 $\theta + \beta m$에서 비용 함수의 그레이디언트를 계산한 것이다.
    • 기본 모멘텀 최적화보다 일반적으로 훈련 속도 빠름
  • 네스테로프 가속 경사 알고리즘
    1. $ m \leftarrow \beta m - \eta \nabla_{\theta}J(\theta + \beta m) $
    2. $ \theta \leftarrow \theta + m $

image

from tensorflow.keras.optimizers import SGD

optimizer = SGD(lr=0.001, momentum=0.9, nesterov=True)



11.3.3 AdaGrad

  • 경사 하강법은 전역 최적점 방향으로 곧장 향하지 않고 가장 가파른 경사를 따라 빠르게 내려가기 시작해서 골짜기 아래로 느리게 이동한다.
  • AdaGrad 알고리즘은 가장 가파른 차원을 따라 그레이디언트 벡터의 스케일을 감소시켜 이 문제를 해결한다.
  • AdaGrad 알고리즘
    1. $ s \leftarrow s + \nabla_{\theta}J(\theta)\otimes \nabla_{\theta}J(\theta) $
    2. $ \theta \leftarrow \theta - \eta \nabla_{\theta}J(\theta)\oslash \sqrt{s + \varepsilon} $
  • 이 알고리즘은 학습률을 감소시키지만 경사가 완만한 차원보다 가파른 차원에 대해 더 빠르게 감소된다. (= 적응적 학습률 (adaptive learning rate))
    • 2차방정식에 대해 잘 작동함
    • 전역 최적점 방향으로 더 곧장 가도록 갱신되는 데 도움이 된다.
  • 하지만 단점으로 학습률이 너무 감소되어 전역 최적점에 도착하기 전에 알고리즘이 완전히 멈추므로 심층 신경망에는 사용하지 말아야 한다.

image

from tensorflow.keras.optimizers import Adagrad

optimizer = Adagrad(lr=0.001)



11.3.4 RMSProp

  • AdaGrad 때 너무 빨리 느려져서 전역 최적점에 수렴하지 못하는 위험을 RMSProp 알고리즘으로 가장 최근 반복에서 비롯된 그레이디언트만 누적함으로써 이 문제를 해결했다.
    • 알고리즘의 첫 번째 단계에서 지수 감소를 사용한다.
  • RMSProp 알고리즘
    1. $ s \leftarrow \beta s + (1 - \beta) \nabla_{\theta}J(\theta)\otimes \nabla_{\theta}J(\theta) $
    2. $ \theta \leftarrow \theta - \eta \nabla_{\theta}J(\theta)\oslash \sqrt{s + \varepsilon} $
from tensorflow.keras.optimizers import RMSProp

optimizer = RMSProp(lr=0.001, rho=0.9) # rho는 베타에 해당 (default = 0.9)
  • 이 알고리즘은 Adam 최적화가 나오기 전까지 연구자들이 가장 선호하는 최적화 알고리즘이었다.


11.3.5 Adam과 Nadam 최적화

  • Adam은 적응적 모멘트 추정 (adaptive moment estimation)을 의미
    • 모멘텀 최적화 (지난 그레이디언트의 지수 감소 평균을 따름) 와 RMSProp (지난 그레이디언트 제곱의 지수 감소된 평균을 따름) 를 합침
  • Adam 알고리즘
    1. $ m \leftarrow \beta_1 m - (1 - \beta_1) \nabla_{\theta} J(\theta)$
    2. $ s \leftarrow \beta_2 s + (1 - \beta_2) \nabla_{\theta}J(\theta)\otimes \nabla_{\theta}J(\theta) $
    3. $ \hat m \leftarrow \frac {m}{1-\beta_1^t} $
    4. $ \hat s \leftarrow \frac {s}{1-\beta_2^t} $
    5. $ \theta \leftarrow \theta + \eta\hat m \oslash \sqrt{\hat s + \varepsilon} $
    • $ \beta_1$ : 모멘텀 감쇠 하이퍼파라미터
    • $ \beta_2$ : 스케일 감쇠 하이퍼파라미터
from tensorflow.keras.optimizers import Adam

optimizer = Adam(lr=0.001, beta_1=0.9, beta_2=0.999)


  • 그 외 AdaMax, Nadam이 있다.
  • AdaMax 최적화
    • Adam 알고리즘 개선. 하지만 경우에 따라 Adam 성능이 더 좋다.
from tensorflow.keras.optimizers import Adamax

optimizer = Adamax(lr=0.001, beta_1=0.9, beta_2=0.999)
  • Nadam 최적화
    • Adam + Nesterov
    • 일반적으로 Adam보다 성능 좋지만 경우에 따라 RMSProp이 더 좋기도함
from tensorflow.keras.optimizers import Adamax

optimizer = Adamax(lr=0.001, beta_1=0.9, beta_2=0.999)


  • 지금까지 논의한 모든 최적화 기법은 1차 편미분 (야코비얀)에만 의존한다.



모든 옵티마이저를 표로 정리

  • 선택한 옵티마이저의 성능이 만족스럽지 않을 경우 기본 Nesterov 가속 경사 사용 추천
  • *=나쁨, **=보통, ***=좋음
클래스 수렴 속도 수렴 품질
SGD * ***
SGD(momentum=…) ** ***
SGD(momentum=…, nesterov=True) ** ***
Adagrad *** *(너무 일찍 멈춤)
RMSprop *** ** 또는 ***
Adam *** ** 또는 ***
Nadam *** ** 또는 ***
AdaMax *** ** 또는 ***


11.3.6 학습률 스케줄링

  • 학습률 선택이 훈련의 성패를 가른다.

  • 아래는 모델 훈련과정동안 학습률을 조정하는 기법들
  • 보통 높은 학습률로 시작해서 학습속도가 느려질 경우 학습률을 작게 조정


거듭제곱 기반 스케줄링

  • 거듭제곱 기반 스케줄링 (power scheduling) : 학습률을 반복 횟수 t에 대한 함수 $\eta(t) = \eta_0 / (1 + t/s)^c $로 지정.
  • $t=k\cdot s$로 커지면 학습률이 $\frac {\eta_0}{k+1}$로 줄어듦
  • 하이퍼파라미터
    • $\eta_0$ : 초기 학습률
    • $c$ : 거듭제곱수, 일반적으로 1로 지정
    • $s$ : 스텝 횟수
from tensorflow.keras.optimizers import SGD

optimizer = SGD(lr=0.01, decay=1e-4)


지수 기반 스케줄링

  • 지수 기반 스케줄링 (exponential scheduling) : $ \eta(t) = \eta_0 0.1^{t/s}$
  • 학습률이 s 스텝마다 10배씩 점차 줄어든다.
def exponential_decay_fn(epoch): # 현재 에포크의 학습률을 받아 반환하는 함수 필요
    return 0.01 * 0.1**(epoch / 20)


구간별 고정 스케줄링

  • 구간별 고정 스케줄링 (piecewise constant scheduling) : 일정 횟수의 에포크 동안 일정한 학습률을 사용하고 그 다음 또 다른 횟수의 에포크 동안 작은 학습률을 사용하는 식이다.
  • 학습률
def piecewise_constant_fn(epoch):
    if epoch < 5:
        return 0.01
    elif epoch < 15:
        return 0.005
    else:
        return 0.001
  • 콜백함수 지정
from tensorflow.keras.callbacks import LearningRateScheduler

lr_scheduler = LearningRateScheduler(piecewise_constant_fn)

history = model.fit(X_train_scaled, y_train, epochs=n_epochs,
                    validation_data=(X_valid_scaled, y_valid),
                    callbakcs=[lr_scheduler])


성능 기반 스케줄링

  • 성능 기반 스케줄링 (performance scheduling) : 매 $N$스텝마다 검증 오차를 측정하고 오차가 줄어들지 않으면 $\lambda$배만큼 학습률을 감소시킨다.
  • ReduceLROnPlateau 콜백 클래스 활용
from tensorflow.keras.callbacks import ReduceLROnPlateau
from tensorflow.keras.schedules import ExponentialDecay
from tensorflow.keras.optimizers import SGD

# 다음 콜백을 fit() 메서드에 전달하면 최상의 검증 손실이 다섯 번의 연속적인 에포크 동안 향상되지 안흥ㄹ 때마다 학습률에 0.5를 곱한다.
lr_scheduler = ReduceLROnPlateau(factor=0.5, patience=5) 

s = 20 * len(X_train) // 32 # 20번 에포크에 담긴 전체 스텝 수 (배치 크기 = 32)
learning_rate = ExponentialDecay(0.01, s, 0.1)
optimizer = SGD(learning_rate=learning_rate)


1 사이클 스케줄링

  • 1 사이클 스케줄링 (1cycle scheduling) : 학습률을 훈련 과정 중에 올리거나 내리도록 조정
    • 훈련 전반부: 낮은 학습률 $\eta_0$에서 높은 학습률 $\eta_1$까지 선형적으로 높힘
    • 훈련 후반부: 다시 선형적으로 $\eta_0$까지 낮춤
    • 훈련 마지막 몇 번의 에포크: 학습률을 소수점 몇 째 짜리까지 선형적으로 낮춤


  • 지수 기반 스케줄링, 성능 기반 스케줄링, 1사이클 스케줄링이 수렴 속도를 크게 높일 수 있다.
  • 성능 기반과 지수 기반 모두 좋지만 지수 기반 스케줄링 선호
    • 튜닝 쉽고, 높은 성능, 구현이 쉽기 때문이다.



11.4 규제를 사용해 과대적합 피하기

  • 심층 신경망의 높은 자유도는 네트워크를 훈련 세트에 과대적합되기 쉽게 만들기 때문에 규제가 필요하다.
  • 지금까지 살펴본 규제기법으로는
    • 조기종료 기법 : EarlyStopping 콜백을 사용하여 일정 에포크 동안 성능이 향상되지 않는 경우 자동 종료시키기
    • 배치정규화 : 불안정한 그레이디언트 문제해결을 위해 사용하지만 규제용으로도 활용 가능 (가중치의 변화를 조절하는 역할)



11.4.1 $l_1$과 $l_2$ 규제

  • 층을 선언할 때 규제 방식과 규제강도를 지정할 수 있다.
    • kernel_regularizer 옵션 사용
from tensorflow.keras.layers import Dense
from tensroflow.keras.regularizers import l1,l2, l1_l2

layer = Dense(100, activation='relu',
              kernel_initializer='he_normal',
              kernel_regularizer=l2(0.01))
  • 일반적으로 동일한 매개변수 값을 반복하는 경우가 많다. functools.partial() 함수를 사용하여 기본 매개변수 값을 사용하여 함수 호출을 감싼다.
from functools import partial
from tensroflow.keras.regularizers import l1,l2, l1_l2
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Flatten

RegularizedDense = partial(Dense,
                           activation='elu',
                           kernel_initializer='he_normal',
                           kernel_regularizer=l2(0.01))

model = Sequential([
    Flatten(input_shape=[28,28]),
    RegularizedDense(300),
    RegularizedDense(100),
    RegularizedDense(10, activation='softmax',
                     kernel_initializer='glorot_uniform',)
])


11.4.2 드롭아웃

이미지출처: 2

  • 매 훈련 스텝에서 각 뉴런은 임시적으로 드롭아웃될 확률 $p$를 가진다. 즉, 이번 훈련 스텝에는 완전히 무시되지만 다음 스텝에는 활성화될 수 있다.
    • 하이퍼파라미터 $p$를 드롭아웃 비율이라고 하고 보통 10%와 50% 사이를 지정한다.
      • 순환 신경망: 20% - 30%
      • 합성곱 신경망: 40% - 50%
      • 순환신경망(15장): 20%-30%
  • 에포크 또는 스텝마다 서로 다른 고유한 모델이 훈련되는 효과를 발생시킴
  • 훈련이 끝난 후에는 뉴런에 더는 드롭아웃을 적용하지 않는다.
  • 케라스에서는 keras.layers.Dropout 층을 사용하여 드롭아웃을 구현한다.


  • 모든 Dense 층 이전에 드롭아웃 비율 0.2 지원하기
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Flatten, Dropout, Dense

model = Sequential([
    Flatten(input_shape=[28, 28]),
    Dropout(rate=0.2),
    Dense(300, activation='elu', kernel_initializer='he_normal'),
    Dropout(rate=0.2),
    Dense(100, activation='elu', kernel_initializer='he_normal'),
    Dropout(rate=0.2),
    Dense(10, activation='softmax')
])


  • 훈련시에만 드롭아웃을 적용하기 때문에 훈련손실과 검증손실을 그대로 비교하면 안된다.
  • 훈련 후 드롭아웃을 끄고 훈련손실을 재평가해야 한다.


알파 드롭아웃

  • SELU 활성화함수를 사용하는 경우 알파(alpha) 드롭아웃을 사용 추천
    • 입력과 평균의 표준편차를 유지시켜준다.
    • 일반 드롭아웃은 자기 정규화 기능 방해
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Flatten, AlphaDropout, Dense

model = Sequential([
    Flatten(input_shape=[28, 28]),
    AlphaDropout(rate=0.2),
    Dense(300, activation='selu', kernel_initializer='lecun_normal'),
    AlphaDropout(rate=0.2),
    Dense(100, activation='selu', kernel_initializer='lecun_normal'),
    AlphaDropout(rate=0.2),
    Dense(10, activation='softmax')
])



11.4.3 몬테 카를로 드롭아웃

  • 훈련된 드롭아웃 모델을 재훈련하지 않으면서 성능을 향상시키는 기법
  • 훈련된 모델의 예측기능을 이용한 결과를 스택으로 쌓은 후 평균값을 계산
import numpy as np

y_probas = np.stack([model(X_test_scaled, training=True)
                    for sample in range(100)])
y_proba = y_probas.mean(axis=0)
y_std = y_probas.std(axis=0)

# 해석
# 테스트 세트 샘플 수는 10,000개. 클래스 수는 10개. 따라서 [10000, 10] 모양의 행렬 100개
# [100, 10000, 10] 모양의 행렬이 y_probas에 저장됨


  • 모델이 훈련하는 동안 다르게 작동하는 층을 가지고 있다면 앞에서와 같이 훈련 모드를 강제로 설정하면 안 된다.
    • Dropout 층을 MCDropout 클래스로 바꿔준다.
from tensorflow.keras.layers import Dropout

class MCDropout(Dropout):
    def call(self, inputs):
        return super().call(inputs, training=True)



11.4.4 맥스-노름 규제

  • 맥스-노름 규제는 각각의 뉴런에 대해 입력의 연결 가중치 $w$가 $|w |_2 \le r $이 되도록 제한한다.
    • $r$ : 맥스-노름 하이퍼파라미터
    • $| \cdot |_2$ : $l_2$ 노름
    • 훈련 스텝이 끝나고 $|w |_2$를 계산하고 필요하면 $w$의 스케일을 조정한다. ($w \leftarrow \frac {r}{|w |_2}$)
  • 케라스에서 맥스-노름 규제를 구현하려면 적절한 최댓값으로 지정한 max_norm()이 반환환 객체로 은닉층의 kernel_constraint 매개변수를 지정한다.
from tensorflow.keras.layers import Dense
from tensorflow.keras.constraints import max_norm

Dense(100, activation='elu', kernel_initializer='he_normal',
      kernel_constraint=max_norm(1.))



11.5 요약 및 실용적인 가이드라인

  • 기본 DNN 설정
하이퍼파라미터 기본값
커널 초기화 He 초기화
활성화 함수 ELU
정규화 얕은 신경일 경우 없음. 깊은 신경망이라면 배치 정규화
규제 조기 종료 (필요하면 $l_2$ 규제 추가)
옵티마이저 모멘텀 최적화 (또는 RMSProp이나 Nadam)
학습률 스케줄 1사이클
  • 네트워크가 완전 연결 층을 쌓은 단순한 모델일 때는 자기 정규화 사용
하이퍼파라미터 기본값
커널 초기화 르쿤 초기화
활성화 함수 SELU
정규화 없음 (자기 정규화)
규제 필요하다면 알파 드롭아웃
옵티마이저 모멘텀 최적화 (또는 RMSProp이나 Nadam)
학습률 스케줄 1 사이클





References

댓글남기기