딥러닝

규제 방법

잔잔한 흐름 2025. 5. 18. 17:23

이 글은 <핸즈온 머신러닝: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 드롭아웃을 사용