규제 방법
이 글은 <핸즈온 머신러닝:2판>을 참고하여 만들어졌습니다.
11.4 규제를 사용해 과대적합 피하기
심층 신경망은 전형적으로 수만 개, 때로는 수백만 개의 파라미터를 가지고 있습니다.
이 때문에 네트워크의 자유도가 매우 높습니다.
=> 즉, 대규모의 복잡한 데이터셋을 학습할 수 있다는 뜻입니다.
하지만 이런 높은 자유도는 네트워크를 훈련 세트에 과대적합되기 쉽게 만듭니다. (즉, 규제가 필요합니다)
10장에서 이미 최상의 규제 방법 중 하나인 조기 종료를 구현했습니다.
또한 배치 정규화는 불안정한 그레이디언트 문제를 해결하기 위해 고안되었지만 꽤 괜찮은 규제 방법으로도 사용할 수 있습니다.
=> l1과 l2 규제, 드롭아웃(dropout), 맥스-노름(max-norm) 규제
11.4.1 l1과 l2 규제
간단한 선형 모델에 했던 것처럼 신경망의 연결 가중치를 제한하기 위해
=> l2 규제를 사용하거나 (많은 가중치가 0인) 희소 모델을 만들기 위해 l1 규제를 사용
다음은 케라스 층의 연결 가중치에 규제 강도 0.01을 사용하여 l2 규제를 적용하는 방법을 보여줌
layer = keras.layers.Dense(100, activation="elu", kernel_initializer="he_normal", kernel_regularizer=keras.regularizers.l2(0.01))
- l2() 함수는 훈련하는 동안 규제 손실을 계산하기 위해 각 스텝에서 호출되는 규제 객체를 반환
- 이 손실은 최종 손실에 합산
- l1 규제가 필요하다면 keras.regularizers.l1()를 사용
- l1과 l2가 모두 필요하면 keras.regularizers.l1_l2()를 사용(두 개의 규제 강도를 모두 지정)
일반적으로 네트워크의 모든 은닉층에 동일한 활성화 전략, 동일한 초기화 전략을 사용하거나
모든 층에 동일한 규제를 적용하기 때문에 동일한 매개변수 값을 반복하는 경우가 많습니다.
=> 코드를 읽기 어렵게 만들고 버그를 만들기 쉽습니다. (이를 피하려면 반복문을 사용하도록 코드를 리팩터링(refactoring))
또 다른 방법은 파이썬의 functools.partial() 함수를 사용하여 기본 매개변수 값을 사용하여 함수 호출을 감싼다.
from functools import partial
RegularizedDense = partial(keras.layers.Dense,
activation="elu",
kernel_initializer="he_normal",
kernel_regularizer=keras.regularizers.l2(0.01))
model=keras.models.Sequential([
keras.layers.Flatten(input_shape[28,28]),
RegularizedDense(300),
RegularizedDense(100),
RegularizedDense(10, activation="softmax", kernel_initalizer="glorot_uniform")
])
11.4.2 드롭아웃
드롭아웃(dropout) : 심층 신경망에서 가장 인기 있는 규제 기법 중 하나 (제프리 힌턴이 제안)
* 최고 성능을 내는 신경망조차도 드롭아웃을 적용해서 정확도를 1~2% 높였습니다.
(모델의 정확도가 95%일때 2% 상승하는 것은 오차율이 거의 40% 정도 줄어드는 것을 의미한다)
- 이 알고리즘은 매우 간단합니다. 매 훈련 스텝에서 각 뉴런(입력 뉴런은 포함하고 출력 뉴런은 제외)은 임시적으로 드롭아웃될 확률 p를 가집니다.
- 즉, 이번 훈련 스텝에서는 완전히 무시되지만 다음 스텝에는 활성화될 수 있습니다.
- 하이퍼파라미터 p를 드롭아웃 비율(dropout rate)이라고 하고 보통 10%에서 50% 사이를 지정합니다.
- 순환 신경망에서는 20~30%에 가깝고 합성곱 신경망에서는 40~50%에 가깝습니다.
- 훈련이 끝난 후에는 뉴런에 더는 드롭아웃을 적용하지 않습니다.
=> 각 훈련 반복에서 (출력층을 제외하고) 하나 이상의 층에 있는 모든 뉴런의 일부를 제거합니다.
(이런 뉴런은 이 반복에서 0을 출력합니다)
- 드롭아웃으로 훈련된 뉴런은 이웃한 뉴런에 맞추어 적응될 수 밖에 없습니다. (자기자신이 유용)
- 또 이런 뉴런들은 몇 개의 입력 뉴런에만 지나치게 의존할 수 없습니다
- 더 안정적인 네트워크 형성
드롭아웃의 능력을 이해하는 또 다른 방법은 각 훈련 스텝에서 고유한 네트워크가 생성된다고 생각하는 것입니다.
- 개개의 뉴런이 있을 수도 없을 수도 있기 때문에 2^N개의 네트워크가 가능합니다.(N은 드롭아웃이 가능한 뉴런 수)
- 이는 너무 큰값이어서 같은 네트워크가 2번 선택 X
- 결과적으로 만들어진 신경망은 이 모든 신경망을 평균한 앙상블로 볼 수 있습니다.
TIP) 일반적으로 맨 위의 층부터 세 번째 층까지 있는 뉴런에만 드롭아웃을 적용합니다.
기술적인 세부 사항에 대해서 얘기를 해보자면,
p = 50%로 하면 테스트하는 동안에는 하나의 뉴런이 훈련 때보다 2배 많은 입력 뉴런과 연결됩니다.
=> 보상하기 위해 훈련하고 각 뉴런의 연결 가중치에 0.5를 곱할 필요가 있습니다. 그렇지 않으면 각 뉴런이 훈련한 것보다 거의 2배 많은 입력 신호를 받기 때문에 잘 동작하지 않을 것입니다.
조금 더 일반적으로 말하면 훈련이 끝난 뒤 각 입력의 연결 가중치에 보존 확률(1-p)을 곱해야 합니다.
또는 훈련하는 동안 각 뉴런의 출력을 보존 확률로 나눌 수도 있습니다.
케라스에서는 keras.layers.Dropout 층을 사용하여 드롭아웃을 구현합니다.
1. 이 층은 훈련하는 동안 일부입력을 랜덤하게 버립니다.
2. 그다음 남은 입력을 보존 확률로 나눕니다. (훈련이 끝난 후에도 어떤 작업도 하지 않습니다.)
3. 입력을 다음 층으로 그냥 전달합니다.
model = keras.models.Sequential([
keras.layers.Flatten(input_shape[28,28]),
keras.layers.Dropout(rate=0.2),
keras.layers.Dense(300, activation="elu", kernel_initializer="he_normal"),
keras.layers.Dropout(rate=0.2),
keras.layers.Dense(100, activation="elu", kernel_initializer="he_normal"),
keras.layers.Dropout(rate=0.2),
keras.layers.Dense(10, activation="softmax")
])
※ 드롭아웃은 훈련하는 동안에만 활성화되므로 훈련 손실과 검증 손실을 비교하면 오해를 일으키기 쉽습니다.
특히 비슷한 훈련 손실과 검증 손실을 얻더라도 모델이 훈련세트에 과대적합될 수 있습니다.
따라서 드롭아웃을 빼고 훈련 손실을 평가해야 합니다.
- 모델이 과대적합되면 드롭아웃 비율을 늘릴 수 있습니다.
- 반대로 모델이 훈련 세트에 과소적합되면 드롭아웃 비율을 낮추어야 합니다.
- 많은 최신의 신경망 구조는 마지막 은닉층 뒤에만 드롭아웃을 사용합니다.
TIP) 만약 SELU 활성화 함수를 기반으로 자기 정규화하는 네트워크를 규제하고 싶다면 알파(alpha) 드롭아웃을 사용해야 합니다.
이 방법은 입력의 평균과 표준편차를 유지하는 드롭아웃의 변종입니다.
11.4.3 몬테 카를로 드롭아웃
y_probas = np.stack([model(X_test_scaled, training=True) for sample in range(100)])
y_proba = y_probas.mean(axis=0)
=> 드롭아웃 모델을 재훈련하지 않고 성능을 향상시키는 MC 드롭아웃 구현
- model(X)는 넘파이 배열이 아니라 텐서를 반환한다는 것만 빼고 model.predict(X)와 비슷하고 training 매개변수를 지원
- 이 코드 예에서 training=True로 지정하여 Dropout 층이 활성화
- 첫 번째 차원(axis=0)을 기준으로 평균하면 한 번의 예측을 수행했을 때와 같은 [10000, 10] 크기의 배열 y_proba를 얻게 된다.
- 드롭아웃으로 만든 예측을 평균하면 일반적으로 드롭아웃이 없어 예측한 하나의 결과보다 더 안정적입니다.
np.round(model.predic(X_test_scaled[:1], 2)
=> 모델은 거의 확실하게 이 이미지가 클래스 9(앵클 부츠)에 속한다고 확신합니다.
np.round(y_probas[:, :1], 2)
=> 이 결과는 조금 다릅니다. 드롭아웃을 활성화하면 모델이 더 이상 확신하지 않습니다.
첫번째 차원으로 평균을 내면 MC 드롭아웃의 예측은 다음과 같습니다.
np.round(y_proba[:1], 2)
모델은 여전히 이 이미지가 클래스 9에 속한다고 생각(62% 확신)
y_std = y_probas.std(axis=0)
np.round(y_std[:1], 2)
=> 이 확률 추정에는 많은 분산이 있습니다. 만약 위험에 민감한 시스템을 만든다면 이런 불확실한 예측을 매우 주의 깊게 다루어야 합니다. (모델의 정확도 향상: 86.8% -> 86.9%)
accuracy = np.sum(y_pred == y_test) / len(y_test)
accuracy
* 몬테 카를라 샘플의 숫자는 튜닝할 수 있는 하이퍼파라미터입니다.
- 이 값이 높을수록 예측과 불확실성 추정이 더 정확해질 것입니다.
- 하지만 샘플 수를 2배로 늘리면 예측 시간도 2배가 됩니다.
- 또한 일정 샘플 수가 넘어서면 성능이 크게 향상되지 않습니다.
- 따라서 애플리케이션에 따라 성능과 정확도 사이에 균형점을 찾는 것이 중요합니다.
모델이 훈련하는 동안 다르게 작동하는 (BatchNormalization 층과 같은) 층을 가지고 있다면
훈련 모드를 강제로 설정해서는 안 됩니다.
Dropout 층 -> McDropout 클래스
class MCDropout(keras.layers.Dropout):
def call(self, inputs):
return super().call(inputs, training=True)
- Dropout 층을 상속하고 call() 메서드를 오버라이드(override)하여 training 매개변수를 True로 강제 설정
- 처음부터 모델을 만든다면 Dropout 대신 MCDropout을 사용
- 이미 Dropout을 사용하여 모델을 훈련했다면 Dropout 층을 MCDropout으로 바꾸고 기존 모델과 동일한 모델을 만듭니다.
- 그 다음 기존 모델의 가중치를 복사합니다.
MC 드롭아웃은 드롭아웃 모델의 성능을 높여주고 더 정확한 불확실성 추정을 제공하는 환상적인 기술
훈련하는 동안은 일반적인 드롭아웃처럼 수행하기 때문에 규제처럼 작동합니다.
11.4.4 맥스-노름 규제
신경망에서 널리 사용하는 또 다른 규제 기법: 맥스-노름 규제(max-norm regularization)
이 방식은 뉴런에 대해 입력의 연결 가중치 w가 ㅣwㅣ2 =< r이 되도록 제한
r은 맥스-노름 하이퍼파라미터, ㅣㅣ2는 l2 노름입니다.
- 맥스-노름 규제는 전체 손실 함수에 규제 손실 항을 추가하지 않습니다.
- 대신 일반적으로 매 훈련 스텝이 끝나고 |w|2를 계산하고 w의 스케일을 조정합니다.(w <- w*r/|w|2).
- r을 중리면 규제의 양이 증가하여 과대적합을 감소시키는데 도움을 줍니다.
- 맥스-노름 규제는 (배치 정규화를 사용하지 않았을 때) 불안정한 그레이디언트 문제를 완화하는데 도움을 줍니다.
케라스에서 맥스-노름 규제를 구현하려면 다음처럼 적절한 최댓값으로 지정한 max_norm()이 반환한 객체로 은닉층의 kernel_constraint 매개변수를 지정합니다.
keras.layers.Dense(100, activation="elu", kernel_initializer="he_normal",
kernel_constraint=keras.constraints.max_norm(1.))
- 매 훈련 반복이 끝난 후 모델의 fit() 메서드가 층의 가중치와 함께 max_norm()이 반환한 객체를 호출하고 스케일이 조정된 가중치를 반환받습니다.
- 이 값을 사용하여 층의 가중치를 바꿉니다.
- 필요하면 사용자 정의 규제 함수를 정의하고 kernel_constraint 매개변수에 이를 지정
- bias_constraint 매개변수에 지정하여 편향을 규제
- max_norm() 함수는 기본값이 0인 axis 매개변수가 있습니다.
- Dense 층은 일반적으로 [샘플 개수, 뉴런 개수] 크기의 가중치를 가집니다.
- axis=0을 사용하면 맥스-노름 규제는 각 뉴런의 가중치 벡터에 독립적으로 적용됩니다.
11.5 정리
기본 DNN 설정
하이퍼파라미터 | 기본값 |
커널 초기화 | He 초기화 |
정규화 | 얕은 신경일 경우 없음. 깊은 신경망이라면 배치 정규화 |
규제 | 조기 종료(필요하면 l2 규제 추가) |
활성화 함수 | ELU |
옵티마이저 | 모멘텀 최적화(RMSProp이나 Nadam) |
학습률 스케줄 | 1사이클 |
네트워크가 완전 연결 층을 쌓은 단순한 모델이라면 자기 정규화를 할 수 있습니다.
자기 정규화를 위한 DNN 설정
하이퍼파라미터 | 기본값 |
커널 초기화 | 르쿤 초기화 |
활성화 함수 | SELU |
정규화 | 없음(자기 정규화) |
규제 | 필요하다면 알파 드롭아웃 |
옵티마이저 | 모멘텀 최적화(RMSProp이나 Nadam) |
학습률 스케줄 | 1사이클 |
레이블이 없는 데이터가 많으면 비지도 사전훈련 사용하기
레이블된 데이터가 많으면 보조 작업에서 사전훈련을 수행
예외)
- 희소 모델이 필요하다면, l1 규제 사용(훈련 후 작은 가중치를 0으로 둠)
- 빠른 응답을 하는 모델이 필요하면 층 개수를 줄이고 배치 정규화 층을 이전 층에 합치자 (LeekyReLU나 ReLU)
- 위험에 민감하고 예측 속도가 매우 중요하지 않은 애플리케이션이라면 성능을 올리고 불확실성 추정과 신뢰할 수 있는 확률 추정을 얻기 위해 mc 드롭아웃을 사용