본문 바로가기
DNN with Keras/TensorFlow

[TensorFlow] 과대적합 / 과소적합 (1)

by goatlab 2022. 6. 15.
728x90
반응형
SMALL

과대적합 / 과소적합

 

tf.keras API를 사용한다. 텐서플로 케라스 가이드에서 tf.keras API에 대해 더 많은 정보를 얻을 수 있다.

 

일정 에포크 동안 훈련하면 검증 세트에서 모델 성능이 최고점에 도달한 다음 감소하기 시작한 것을 알 수 있다.

 

다른 말로 하면, 모델이 훈련 세트에 과대적합 (overfitting)된 것이다. 과대적합을 다루는 방법이 필요하다. 훈련 세트에서 높은 성능을 얻을 수 있지만 진짜 원하는 것은 테스트 세트 (또는 이전에 본 적 없는 데이터)에 잘 일반화되는 모델이다.

 

과대적합의 반대는 과소적합 (underfitting)이다. 과소적합은 테스트 세트의 성능이 향상될 여지가 아직 있을 때 일어난다. 발생하는 원인은 여러가지이다. 모델이 너무 단순하거나, 규제가 너무 많거나, 그냥 단순히 충분히 오래 훈련하지 않는 경우이다. 즉, 네트워크가 훈련 세트에서 적절한 패턴을 학습하지 못했다는 뜻이다.

 

모델을 너무 오래 훈련하면 과대적합되기 시작하고 테스트 세트에서 일반화되지 못하는 패턴을 훈련 세트에서 학습한다. 과대적합과 과소적합 사이에서 균형을 잡아야 한다. 이를 위해 적절한 에포크 횟수동안 모델을 훈련하는 방법이 필요다.

 

과대적합을 막는 가장 좋은 방법은 더 많은 훈련 데이터를 사용하는 것이다. 많은 데이터에서 훈련한 모델은 자연적으로 일반화 성능이 더 좋다. 데이터를 더 준비할 수 없을 때 그 다음으로 가장 좋은 방법은 규제 (regularization)와 같은 기법을 사용하는 것이다. 모델이 저장할 수 있는 정보의 양과 종류에 제약을 부과하는 방법이다. 네트워크가 소수의 패턴만 기억할 수 있다면 최적화 과정동안 일반화 가능성이 높은 가장 중요한 패턴에 촛점을 맞출 것이다.

 

여기에서 널리 사용되는 두 가지 규제 기법인 가중치 규제와 드롭아웃 (dropout)이 있다.

 
import tensorflow as tf
from tensorflow import keras

import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)

 

IMDB 데이터셋 다운로드

 

여기서는 임베딩을 사용하지 않고 여기에서는 문장을 멀티-핫 인코딩 (multi-hot encoding)으로 변환한다. 이 모델은 훈련 세트에 빠르게 과대적합될 것이다. 과대적합을 발생시키기고 어떻게 해결하는지 보이기 위해 선택했다.

 

멀티-핫 인코딩은 정수 시퀀스를 0과 1로 이루어진 벡터로 변환한다. 정확하게 말하면 시퀀스 [3, 5]를 인덱스 3과 5만 1이고 나머지는 모두 0인 10,000 차원 벡터로 변환한다는 의미이다.

 
NUM_WORDS = 1000

(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)

def multi_hot_sequences(sequences, dimension):
    # 0으로 채워진 (len(sequences), dimension) 크기의 행렬을 만듭니다
    results = np.zeros((len(sequences), dimension))
    for i, word_indices in enumerate(sequences):
        results[i, word_indices] = 1.0  # results[i]의 특정 인덱스만 1로 설정합니다
    return results


train_data = multi_hot_sequences(train_data, dimension=NUM_WORDS)
test_data = multi_hot_sequences(test_data, dimension=NUM_WORDS)

 

만들어진 멀티-핫 벡터 중 하나를 살펴 본다. 단어 인덱스는 빈도 순으로 정렬되어 있다. 그래프에서 볼 수 있듯이 인덱스 0에 가까울수록 1이 많이 등장한다.

 
plt.plot(train_data[0])
[<matplotlib.lines.Line2D at 0x7f59993eb438>]

 

과대적합 예제

 

과대적합을 막는 가장 간단한 방법은 모델의 규모를 축소하는 것이다. 즉, 모델에 있는 학습 가능한 파라미터의 수를 줄인다 (모델 파라미터는 층 (layer)의 개수와 층의 유닛 (unit) 개수에 의해 결정된다). 딥러닝에서는 모델의 학습 가능한 파라미터의 수를 종종 모델의 "용량"이라고 말한다. 직관적으로 생각해 보면 많은 파라미터를 가진 모델이 더 많은 "기억 용량"을 가진다. 이런 모델은 훈련 샘플과 타깃 사이를 일반화 능력이 없는 딕셔너리와 같은 매핑으로 완벽하게 학습할 수 있다. 하지만, 이전에 본 적 없는 데이터에서 예측을 할 땐 쓸모가 없을 것이다.

 

항상 기억해야 할 점은 딥러닝 모델이 훈련 세트에는 학습이 잘 되는 경향이 있지만 진짜 해결할 문제는 학습이 아니라 일반화라는 것이다.

 

반면에 네트워크의 기억 용량이 부족하다면 이런 매핑을 쉽게 학습할 수 없을 것이다. 손실을 최소화하기 위해서는 예측 성능이 더 많은 압축된 표현을 학습해야 한다. 또한, 너무 작은 모델을 만들면 훈련 데이터를 학습하기 어려울 것이다. "너무 많은 용량"과 "충분하지 않은 용량" 사이의 균형을 잡아야 한다.

 

안타깝지만, 어떤 모델의 (층의 개수나 뉴런 개수에 해당하는) 적절한 크기나 구조를 결정하는 마법같은 공식은 없다. 여러 가지 다른 구조를 사용해 실험을 해봐야만 한다.

 

알맞은 모델의 크기를 찾으려면 비교적 적은 수의 층과 파라미터로 시작해서 검증 손실이 감소할 때까지 새로운 층을 추가하거나 층의 크기를 늘리는 것이 좋다.

 

Dense 층만 사용하는 간단한 기준 모델을 만들고 작은 규모의 버전와 큰 버전의 모델을 만들어 비교한다.

 

기준 모델 만들기

 

baseline_model = keras.Sequential([
    # `.summary` 메서드 때문에 `input_shape`가 필요합니다
    keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(16, activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

baseline_model.compile(optimizer='adam',
                       loss='binary_crossentropy',
                       metrics=['accuracy', 'binary_crossentropy'])

baseline_model.summary()
baseline_history = baseline_model.fit(train_data,
                                      train_labels,
                                      epochs=20,
                                      batch_size=512,
                                      validation_data=(test_data, test_labels),
                                      verbose=2)

 

작은 모델 만들기

 

앞서 만든 기준 모델과 비교하기 위해 적은 수의 은닉 유닛을 가진 모델을 만든다.

 
smaller_model = keras.Sequential([
    keras.layers.Dense(4, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(4, activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

smaller_model.compile(optimizer='adam',
                      loss='binary_crossentropy',
                      metrics=['accuracy', 'binary_crossentropy'])

smaller_model.summary()

 

같은 데이터를 사용해 이 모델을 훈련한다.

 
smaller_history = smaller_model.fit(train_data,
                                    train_labels,
                                    epochs=20,
                                    batch_size=512,
                                    validation_data=(test_data, test_labels),
                                    verbose=2)

 

큰 모델 만들기

 

아주 큰 모델을 만들어 얼마나 빠르게 과대적합이 시작되는지 알아 볼 수 있다. 이 문제에 필요한 것보다 훨씬 더 큰 용량을 가진 네트워크를 추가해서 비교한다.

 
bigger_model = keras.models.Sequential([
    keras.layers.Dense(512, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(512, activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

bigger_model.compile(optimizer='adam',
                     loss='binary_crossentropy',
                     metrics=['accuracy','binary_crossentropy'])

bigger_model.summary()

 

역시 같은 데이터를 사용해 모델을 훈련한다.

 
bigger_history = bigger_model.fit(train_data, train_labels,
                                  epochs=20,
                                  batch_size=512,
                                  validation_data=(test_data, test_labels),
                                  verbose=2)

 

훈련 손실과 검증 손실 그래프 그리기

 

실선은 훈련 손실이고 점선은 검증 손실이다 (낮은 검증 손실이 더 좋은 모델). 여기서는 작은 네트워크가 기준 모델보다 더 늦게 과대적합이 시작되었다 (즉, 에포크 4가 아니라 6에서 시작). 또한, 과대적합이 시작되고 훨씬 천천히 성능이 감소한다.

 
def plot_history(histories, key='binary_crossentropy'):
  plt.figure(figsize=(16,10))

  for name, history in histories:
    val = plt.plot(history.epoch, history.history['val_'+key],
                   '--', label=name.title()+' Val')
    plt.plot(history.epoch, history.history[key], color=val[0].get_color(),
             label=name.title()+' Train')

  plt.xlabel('Epochs')
  plt.ylabel(key.replace('_',' ').title())
  plt.legend()

  plt.xlim([0,max(history.epoch)])


plot_history([('baseline', baseline_history),
              ('smaller', smaller_history),
              ('bigger', bigger_history)])

 

큰 네트워크는 거의 바로 첫 번째 에포크 이후에 과대적합이 시작되고 훨씬 더 심각하게 과대적합된다. 네트워크의 용량이 많을수록 훈련 세트를 더 빠르게 모델링할 수 있다 (훈련 손실이 낮아짐). 하지만, 더 쉽게 과대적합된다 (훈련 손실과 검증 손실 사이에 큰 차이가 발생).

 

https://www.tensorflow.org/tutorials/keras/overfit_and_underfit?hl=ko 

 

과대적합과 과소적합  |  TensorFlow Core

Google I/O는 끝입니다! TensorFlow 세션 확인하기 세션 보기 과대적합과 과소적합 Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반

www.tensorflow.org

 

728x90
반응형
LIST