본문 바로가기

인공지능[AI]

케라스 창시자에게 배우는 딥러닝[5장: 머신 러닝의 기본 요소]

728x90
반응형
SMALL

머신 러닝의 기본 요소

5.1 일반화: 머신 러닝의 목표

  • 머신 러닝의 근본적인 이슈는 최적화와 일반화 사이의 줄다리기
  • 최적화(optimization)는 가능한 훈련 데이터에서 최고의 성능을 얻으려고 모델을 조정하는 과정(머신 러닝에서 학습에 해당)
  • 반면 일반화(organization)는 훈련된 모델이 이전에 본 적 없는 데이터에서 얼마나 잘 수행되는지 의미
  • 당연히 목표는 좋은 일반화 성능을 얻는 것
  • 일반화 성능을 제어할 방법이 없음
  • 단지 모델을 훈련 데이터에 맞출 수만 있음
  • 만약 너무 잘 맞는다면 과대적합이 시작되고 일반화 성능은 나빠짐

과소적합과 과대적합

  • 이전 장에서 본 모델의 경우 홀드아웃(holdout) 검증 데이터에서 손실이 훈련이 진행됨에 따라 낮아지지만 잠시 후에 필연적으로 다시 높아짐

※ 4장 참고

2023.07.18 - [인공지능[AI]] - 케라스 창시자에게 배우는 딥러닝[4장: 신경망 시작하기: 분류와 회귀]

  • 이런 패턴이 일반적
  • 어떤 모델이나 데이터셋에서도 이를 볼 수 있음

전형적인 과대적합 진행 과정

  • 훈련 초기에 최적화와 일반화는 상호 연관되어 있음
  • 훈련 데이터의 손실이 낮아질수록 테스트 데이터의 손실도 낮아짐
  • 이런 상황이 발생할 때 모델이 과소적합(underfitting)되었다고 말함
  • 모델의 성능이 계속 발전될 여지가 있음
  • 즉, 네트워크가 훈련 데이터에 있는 모든 관련 패턴을 학습하지 못했음
  • 훈련 데이터에서 훈련을 특정 횟수만큼 반복하고 난 후에는 일반화 성능이 더 이상 높아지지 않으며 검증 세트의 성능이 멈추고 감소되기 시작
  • 즉, 모델이 과대적합(overfitting)되기 시작
  • 이는 훈련 데이터에 특화된 패턴을 학습하기 시작했다는 의미
  • 이 패턴은 새로운 데이터와 관련성이 적고 잘못된 판단을 하게 만듦
  • 과대적합은 데이터에 잡음이 있거나, 불확실성이 존재하거나, 드문 특성이 포함되어 있을 때 특히 발생할 가능성이 높음

잡음 섞인 훈련 데이터

  • 실제 데이터셋에는 잘못된 입력이 있는 경우가 흔함
  • 예를 들어 MNIST 숫자의 경우 아래 그림과 같은 이미지나 전부 검은색인 이미지가 있을 수 있음

이상한 MNIST훈련 샘플

  • 모두 MNIST훈련 세트에 잇는 이미지
  • 더 안 좋은 것은 아래 그림과 같이 완전히 정상적인 이미지인데 레이블이 잘못된 경우

레이블이 잘못된 MNIST 훈련 샘플

  • 모델을 이런 이상치에 맞추려고 하면 아래 그림과 같이 일반화 성능이 감소
  • 예를 들어 위 그림의 첫 번째 이미지와 배우 비슷한 숫자 4는 9로 분류될 수 있음

이상치 다루기: 최적적합 vs 과대적합

  • 모든 데이터 잡음이 부정확성 때문에 발생하는 것은 아님
  • 문제에 불확실성과 모호성이 있다면 완벽하고 깔끔하게 레이블이 부여된 데이터라도 잡음이 있을 수 있음
  • 분류 작업에서 입력 특성 공간의 일부 영역이 동시에 여러 클래스에 연관된 경우가 종종 있음
  • 예를 들어 바나나 이미지를 받아서 이 바나나가 덜 익었는지, 익었는지 또는 썩었는지 예측하는 모델을 개발 중이라고 가정해 보자
  • 이런 범주에는 객관적인 경계가 없기 때문에 레이블을 할당하는 사람마다 동일한 사진을 덜 익은 바나나 또는 익은 바나나로 분류할 수 있음
  • 비슷하게 많은 문제에는 무작위성이 포함되어 있음
  • 기압 데이터를 사용해서 내일 비가 올지 여부를 예측할 수 있음
  • 정확히 동일한 측정값을 얻은 후에도 일정 확률로 이따금 비가 오거나 맑은 하늘이 됨

 

  • 모델이 아래 그림과 같이 특성 공간의 모호한 영역에 너무 확신을 가지면 이런 확률적인 데이터에 과대적합될 수 있음
  • 최적적합은 개별 데이터 포인트를 무시하고 더 큰 그림을 바라보아야 함

특성 공간에 모호한 영역이 있을 때 최적적합 vs 과대적합

드문 특성과 가짜 상관관계

  • 평생 두 마리의 주황색 얼룩무늬 고양이만 보았고 둘 다 사교성이 매우 없다면, 주황색 얼룩무늬 고양이는 일반적으로 사교적이지 않다고 추측할 수 있음
  • 이것이 과대적합
  • 더 많은 주황색 고양이와 다양한 다른 종류의 고양이를 보았다면 고양이 색이 성격과 관련이 없다는 것을 배웠을 것
  • 비슷하게 드문 특성 값을 포함한 데이터셋에서 훈련한 머신 러닝 모델은 과대적합될 가능성이 매우 높음
  • 감성 분류 작업에서 'cherimoya'란 단어가 훈련 데이터의 한 텍스트에서만 나타나고 이 텍스트가 부정 레이블을 가지고 있다면, 규제가 잘 되지 않은 모델은 이 단어에 매우 높은 가중치를 부여해서 'cherimoya'를 언급한 텍스트를 항상 부정으로 분류할지 모름
  • 객관적으로 체리모야에 부정적인 것은 없음
  • 중요한 점은 가짜 상관관계를 만들어 내는 데 특성 값이 몇 번만 등장할 필요가 없다는 것
  • 훈련 데이터에서 100개의 샘플에 등장하는 단어가 있고, 그 샘플 중 54%는 긍정이고 46%는 부정이라고 가정해 보자
  • 이 차이는 통계적으로 완전히 우연일 수 있지만 모델은 분류 작업에 이 특성을 활용할 가능성이 높음
  • 이것이 과대적합의 가장 보편적인 원인 중 하나

 

  • MNIST를 사용해서 확실한 예를 하나 보자
  • 기존 데이터의 784차원에 백색 잡음인 784개의 차원을 연결하여 새로운 훈련 세트를 만듦
  • 데이터의 절반은 잡음
  • 비교를 위해 모두 0인 784개의 차원을 연결하여 동일한 데이터셋을 만듦
  • 의미 없는 특성의 연결은 데이터의 기존 정보에 전혀 영향을 미치지 않음
  • 즉, 무언가 추가만 한 것
  • 사람의 분류 정확도는 이런 변화에 전혀 영향을 받지 않음
  • MNIST에 백색 잡음 픽셀과 0픽셀 추가하기
from tensorflow.keras.datasets import mnist
import numpy as np

(train_images, train_labels), _ = mnist.load_data()
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype("float32") / 255

train_images_with_noise_channels = np.concatenate(
    [train_images, np.random.random((len(train_images), 784))], axis=1)

train_images_with_zeros_channels = np.concatenate(
    [train_images, np.zeros((len(train_images), 784))], axis=1)
  • 이 두 훈련 세트에서 2장의 모델을 훈련해 보자
  • 백색 잡음과 0을 추가한 MNIST 데이터에서 모델 훈련하기
from tensorflow import keras
from tensorflow.keras import layers

def get_model():
    model = keras.Sequential([
        layers.Dense(512, activation="relu"),
        layers.Dense(10, activation="softmax")
    ])
    model.compile(optimizer="rmsprop",
                  loss="sparse_categorical_crossentropy",
                  metrics=["accuracy"])
    return model

model = get_model()
history_noise = model.fit(
    train_images_with_noise_channels, train_labels,
    epochs=10,
    batch_size=128,
    validation_split=0.2)

model = get_model()
history_zeros = model.fit(
    train_images_with_zeros_channels, train_labels,
    epochs=10,
    batch_size=128,
    validation_split=0.2)
  • 시간에 따라 각 모델의 검증 정확도가 어떻게 변화하는지 비교해 보자
  • 검증 정확도 비교 그래프 그리기
import matplotlib.pyplot as plt
val_acc_noise = history_noise.history["val_accuracy"]
val_acc_zeros = history_zeros.history["val_accuracy"]
epochs = range(1, 11)
plt.plot(epochs, val_acc_noise, "b-",
         label="Validation accuracy with noise channels")
plt.plot(epochs, val_acc_zeros, "b--",
         label="Validation accuracy with zeros channels")
plt.title("Effect of noise channels on validation accuracy")
plt.xlabel("Epochs")
plt.ylabel("Accuracy")
plt.legend()
plt.show()

검증 정확도 비교 그래프 그리기

  • 두 경우 모두 동일한 정보를 가진 데이터이지만 잡음이 섞인 데이터에서 훈련된 모델의 검증 정확도가 1퍼센트 포인트 정도 낮음
  • 이는 순전히 가짜 상관관계의 영향 때문임
  • 잡음을 더 많이 섞을수록 정확도는 더 감소될 것

 

  • 잡음 특성은 필연적으로 과대적합을 유발시킴
  • 특성이 모델에 유익한지 또는 모델을 혼란스럽게 만드는지 확실하지 않다면 훈련 전에 특성 선택(feature selection)을 수행하는 것이 일반적
  • 예를 들어 IMDB데이터를 가장 자주 등장하는 최상위 1만 개 단어로 제한하는 것은 세련되지 않은 특성 선택 방법
  • 특성 선택을 하는 일반적인 방법은 가용한 각 특성에 대해 어떤 유용성 점수를 계산하는 것
  • 즉, 특성과 레이블 사이의 상호 의존 정보(matual information)처럼 작업에 대해 특성이 얼마나 유익한지 측정
  • 그다음 일정 임계 값을 넘긴 특성만 사용
  • 이렇게 하면 앞서 예제에서 백색 잡음이 걸러질 수 있음

딥러닝에서 일반화의 본질

  • 딥러닝 모델에 관한 놀라운 사실은 표현 능력이 충분하다면 어떤 것에도 맞추도록 훈련할 수 있다는 것
  • MNIST 레이블을 섞은 후 모델을 훈련해 보자
  • 입려과 뒤섞은 레이블 사이에 아무런 관계가 없지만 비교적 작은 모델에서도 훈련 손실이 잘 감소
  • 당연히 이런 상황에서 가능한 일반화가 없기 때문에 검증 손실은 시간이 지남에 따라 향상되지 않음
  • 랜덤하게 섞은 레이블로 MNIST 모델 훈련하기
(train_images, train_labels), _ = mnist.load_data()
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype("float32") / 255

random_train_labels = train_labels[:]
np.random.shuffle(random_train_labels)

model = keras.Sequential([
    layers.Dense(512, activation="relu"),
    layers.Dense(10, activation="softmax")
])
model.compile(optimizer="rmsprop",
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])
model.fit(train_images, random_train_labels,
          epochs=100,
          batch_size=128,
          validation_split=0.2)
  • 이렇게 MNIST 데이터를 사용할 필요도 없음
  • 백색 잡음으로 입력을 만들고 랜덤하게 레이블을 생성할 수도 있음
  • 모델 파라미터가 충분하다면 여기에서도 모델을 훈련할 수 있음
  • 파이썬 딕셔너리처럼 특정 입력을 외워 버리게 될 것
  • 결국 딥러닝에서 일반화의 본질은 딥러닝 모델 자체와 거의 관련이 없고 실제 세상의 정보 구조와 많은 관련이 있음

매니폴드 가설

  • (전처리하기 전의) MNIST 분류기 입력은 28 * 28 크기의 정수 배열이며 0~255 사이의 값을 가짐
  • 가능한 전체 입력 값의 가짓수는 784256
  • 우주에 있는 원자 개수보다 훨씬 많음
  • 이런 입력 중 매우 적은 수만 유효한 MNIST 샘플
  • 실제 손글씨 숫자는 가능한 모든 28 * 28 unit8 배열로 이루어진 공간에서 아주 작은 부분 공간만 차지
  • 더군다나 이 부분 공간은 부모 공간에 랜덤하게 뿌려진 포인트의 집합이 아니라 매우 구조적
  • 우선 유효한 손글씨 숫자의 부분 공간은 연속적
  • 하나의 샘플을 조금 수정해도 여전히 같은 손글씨 숫자로 인식할 수 있음
  • 게다가 유효한 부분 공간 안에 있는 모든 샘플이 이 부분 공간을 가로지르는 매끈한 경로로 연결되어 있음
  • 2개의 MNIST 숫자 A와 B를 무작위로 선택하여 A를 B로 변형시키는 연속적인 중간 이미지가 있다는 의미
  • 2개의 연속적인 중간 이미지는 서로 매우 비슷함
  • 두 클래스의 경계 부근에서는 모호한 모양이 조금 있겠지만 이런 모양도 여전히 숫자처럼 보일 것

힌 숫자에서 다른 숫자로 점차 변형되는 여러 가지 MNIST샘플, 손길씨 숫자의 공간이 '매니폴드'를 가지고 있음을 보여주준다.

  • 기술적으로 손글씨 숫자가 가능한 모든 28 * 28 unit8 배열로 이루어진 공간 안에서 매니폴드(manifold)를 형성한다고 말함
  • '매니폴드'는 국부적으로는 선형(유클리드) 공간과 비슷하게 보이는 부모 공간의 저차원 부분 공간
  • 예를 들어 평면상의 매끄러운 한 곡선은 2D 공간 안에 있는 1D 매니폴드
  • 이 곡선의 모든 포인트에서 접선을 그릴 수 있기 때문임(곡선은 모든 포인트에서 직선으로 근사할 수 있음)
  • 3D 공간상의 매끄러운 표면은 2D 매니폴드가 되는 식
  • 더 일반적으로 매니폴드 가설(manifold hypothesis)은 실제 세상의 모든 데이터가 (이 데이터가 인고팅된) 고차원 공간 안에 있는 저차원 매니폴드에 놓여 있다고 가정
  • 이는 우주에 있는 정보 구조에 관한 매우 강력한 가정
  • 우리가 아는 한 이것이 맞으면 딥러닝이 작동하는 이유
  • MNIST 숫자뿐만 아니라 사람 얼굴, 나무 형태, 음성, 심지어 자연어(natural language)도 해당
  • 매니폴드 가설은 다음을 의미
    • 머신 러닝 모델은 가능한 입력 공간 안에서 비교적 간단하고, 저차원이며, 매우 구조직인 부분 공간(잠재 매니폴드(latent manifold))만 학습
    • 이런 매니폴드 중 하나 안에서 두 입력 사이를 보간(interpolation)하는 것이 항상 가능. 즉, 연속적인 경로를 따라 한 입력에서 다른 입력으로 변형할 때 모든 포인트가 이 매니폴드에 속함
  • 샘플 사이를 보간하는 능력은 딥러닝에서 일반화를 이해하는 열쇠

일반화의 원천인 보간

  • 다루는 데이터 포인트가 보간할 수 있다면 이전에 본 적 없는 포인트를 해당 매니폴드에서 가까이 놓인 다른 포인트와 연결하여 이해할 수 있음
  • 다른 말로 하면 공간 안의 샘플만 사용해서 공간 전체를 이해할 수 있음
  • 보간을 사용해서 빈 곳을 채울 수 있기 때문임
  • 아래 그림에 나타난 것처럼 잠재 매니폴드에서 보간은 부모 공간에서의 선형 보간과 다름
  • 예를 들어 2개의 MNIST 숫자 사이의 픽셀을 평균하면 일반적으로 유효한 숫자가 만들어지지 않음

선형 보관과 잠재 매니폴드 보간 사이의 차이. 숫자의 잠재 매니폴드에서 모든 포인트는 유효한 숫자가 되지만 일반적으로 두 숫자를 평균하면 유효한 숫자가 되지 않는다

  • 근사적으로 학습된 데이터 매니폴드에서 보간을 통해 딥러닝의 일반화가 달성되지만 보간이 일반화의 전부라고 가정하는 것은 실수
  • 보간은 이전에 본 것과 매우 가까운 것을 이해하는 데 도움을 줄 수 있을 뿐
  • 이를 지역 일반화(local generalization)라고 함
  • 놀랍게도 사람은 항상 극도로 새로운 것을 다루면서도 잘 처리
  • 사람은 앞으로 마주할 모든 상황에 대해 무수히 많은 샘플에서 미리 훈련할 필요가 없음
  • 사람은 보간 이외의 인지 매커니즘으로 궁극 일반화(extreme generalization)를 할 수 있음
  • 인지 메커니즘은 추상화, 세상에 대한 상징적 모델, 추론, 논리, 상식, 일반적으로 이성이라고 부르는 세상에 대한 선천적 능력 등을 말하며 직관이나 패턴 인식과는 다름
  • 후자는 사실상 대체로 보간에 해당하지만 전자는 그렇지 않음
  • 둘 다 지능에 꼭 필요

딥러닝이 작동하는 이유

  • 한 장의 종이는 3D 공간 안의 2D 매니폴드를 나타냄
  • 딥러닝 모델은 종이 공을 펼치는 도구
  • 즉, 잠재 매니폴드를 풀기 위한 도구

복잡한 데이터 매니폴드 펼치기

  • 딥러닝 모델은 근본적으로 매우 고차원의 곡선
  • 미분할 수 있어야 하기 때문에 매끄럽고 연속적인 곡선(모델 구조에 대한 가정(prior)으로 인해 곡선의 구조에 추가적인 제약이 있음)
  • 경사 하강법을 통해 이 곡선을 부드럽고 점진적으로 데이터 포인트에 맞춤
  • 딥러닝은 본질적으로 크고 복잡한 곡선(매니폴드)을 선택하여 훈련 데이터 포인트에 맞을 때까지 파라미터를 점진적으로 조정하는 것
  • 이 곡선은 어떤 것에도 맞출 수 있는 충분한 파라미터가 있음
  • 실제로 모델을 충분히 오래 훈련한다면 결국 훈련 데이터를 완전히 외워 버리게 되고 일반화가 전혀 되지 않을 것
  • 학습하려는 데이터는 해당 공간에 희소하게 분산된 독립적인 포인트로 구성되지 않음
  • 이 데이터는 입력 공간 안에서 고도로 구조적인 저차원의 매니폴드를 형성
  • 경사 하강법으로 시간이 지남에 따라 부드럽고 점진적으로 모델 곡선을 이 데이터에 맞춤
  • 아래 그림에서 볼 수 있듯이 모델이 데이터의 매니폴드를 대략적으로 근사하는 중간 지점이 있을 것

랜덤한 모델에서 과대적합 모델로 이동하면서 중간 상태로 최적적합 모델을 얻는다.

  • 그 지점에서 모델이 학습한 곡선을 따라 이동하는 것은 데이터의 실제 잠재 매니폴드를 따라 이동하는 것과 비슷함
  • 모델이 훈련 입력 사이를 보간하여 이전에 본 적 없는 입력을 이해할 수 있을 것
  • 잠재 매니폴드를 학습하는 데 특히 잘 맞는 몇 가지 속성이 있음
    • 딥러닝 모델은 입력에서부터 출력으로 매끄럽고 연속적인 매핑을 구현. 필수적으로 미분 가능해야 하기 때문에 매끄럽고 연속적이어야 함(그렇지 않으면 경사 하강법을 사용할 수 없음)
    • 이런 매끄러움은 동일한 속성을 가진 잠재 매니폴드를 근사하는 데 도움이 됨. 딥러닝 모델은 (모델 구조에 대한 가정을 바탕으로) 훈련 데이터에 있는 정보의 형태를 반영하는 식으로 구조화되는 경향이 있음. 특히 이미지 처리 모델과 시퀀스 처리 모델에서 그러함. 더 일반적으로 심층 신경망은 학습한 표현을 계층적이고 모듈 방식으로 구조화되며 이는 자연적인 데이터가 구성되는 방식을 반영한 것

가장 중요한 훈련 데이터

  • 딥러닝이 실제로 매니폴드 학습에 잘 맞지만 일반화의 능력은 모델의 어떤 속성 때문이라기보다 데이터의 자연적인 구조로 인한 결과
  • 데이터가 보간할 수 있는 매니폴드를 형성하는 경우에만 일반화할 수 있음
  • 특성이 유익하고 잡음이 적을수록 입력 공간이 더 간단하고 구조적이기 때문에 더 잘 일반화할 수 있음
  • 또한, 딥러닝이 곡선을 맞추는 것이기 때문에 모델이 이를 잘 수행하려면 입력 공간을 조밀하게 샘플링하여 훈련해야 함
  • '조밀한 샘플링'은 입력 데이터 매니폴드 전체를 조밀하게 커버해야 한다는 의미
  • 결정 경계 근처에서는 특히 그러함
  • 충분히 조밀하게 샘플링하면 (머신 러닝 모델이 참고할 수 없는) 상식, 요약 추론 또는 세상에 대한 외부 지식을 사용하지 않아도 훈련 입력 사이를 보간하여 새로운 입력을 이해할 수 있음

정확한 일반화가 가능한 모델을 훈련하기 위해서는 입력 공간의 조밀한 샘플링이 필수적이다

  • 딥러닝 모델을 향상시키는 가장 좋은 방법은 더 좋고, 더 많은 데이터에서 훈련하는 것이라는 점을 항상 기억하자(당연히 잡음이 크거나 부정확한 데이터를 추가하면 일반화에 해가 됨)
  • 입력 데이터 매니폴드를 조밀하게 커버하면 일반화 성능이 더 좋은 모델을 만듦
  • 딥러닝 모델이 훈련 샘플 사이를 단순히 보간하는 것 이상을 수행하리라고 기대해서는 안 됨
  • 가능한 쉽게 보간하기 위해 할 수 있는 모든 일을 해야 함
  • 딥러닝 모델에서 찾게 될 것은 무엇을 모델에 넣었는지에 달려 있음
  • 바로 모델 구조에 인코딩된 가정과 훈련에 사용된 데이터
  • 데이터를 수집하는 것이 불가능하면 차선책은 모델이 저장할 수 있는 정보량을 조정하거나 모델 곡선의 매끄러운 정도에 제약을 추가하는 것
  • 네트워크가 적은 개수의 패턴만 기억하거나 매우 규칙적인 패턴만 기억할 수 있다면 최적화 과정은 일반화 가능성이 높은 가장 눈에 띄는 페턴에만 모델의 초점을 맞추도록 할 것
  • 이런 방식으로 과대적합과 싸우는 과정을 규제(regularization)라고 부름
  • 일반화가 더 잘되도록 모델을 조정하기 전에 현재 모델이 어떻게 동작하는지 평가할 방법이 필요함

5.2 머신 러닝 모델 평가

  • 관측할 수 있는 것만 제어할 수 있음
  • 새로운 데이터에 성공적으로 일반화할 수 있는 모델을 개발하는 것이 목표이므로 모델의 일반화 성능을 신뢰 있게 측정할 수 있어야 함

훈련, 검증, 테스트 세트

  • 모델 평가의 핵심은 가용한 데이터를 항상 훈련, 검증, 테스트 3개의 세트로 나누는 것
  • 훈련 세트에서 모델을 훈련하고 검증 세트에서 모델을 평가
  • 모델을 출시할 준비가 되면 테스트 세트에서 최종적으로 딱 한 번 모델을 테스트
  • 테스트 데이터는 가능한 제품 환경의 데이터와 비슷해야 함
  • 그 다음 모델을 제품 환경에 배포
  • 훈련 세트와 테스트 세트 2개만 사용하면 어떨까?
  • 훈련 세트에서 훈련하고 테스트 세트에서 평가하는 것
  • 훨씬 간단함!
  • 이렇게 하지 않는 이유는 모델을 개발할 때 항상 모델의 설정을 튜닝하기 때문임
  • 예를 들어 층이나 층의 유닛 개수를 선택(이런 파라미터를 네트워크의 가중치와 구분하기 위해 하이퍼파라미터(hyperparameter)라고 부름)
  • 검증 세트에서 모델의 성능을 평가하여 이런 튜닝을 수행
  • 본질적으로 이런 튜닝도 어떤 파라미터 공간에서 좋은 설정을 찾는 학습
  • 검증 세트의 성능을 기반으로 모델의 설정을 튜닝하면 검증 세트로 모델을 직접 훈련하지 않더라도 빠르게 검증 세트에 과대적합될 수 있음
  • 이 현상의 핵심은 정보 누설(information leak) 개념에 있음
  • 검증 세트의 모델 성능에 기반하여 모델의 하이퍼파라미터를 조정할 때마다 검증 데이터에 관한 정보가 모델로 새는 것
  • 하나의 파라미터에 대한 단 한 번만 튜닝한다면 아주 작은 정보가 누설
  • 이런 검증 세트로는 모델을 평가할 만함
  • 한 번 튜닝하고 나서 검증 세트에 평가한 결과를 가지고 다시 모델을 조정하는 과정을 여러 번 반복하면, 검증 세트에 관한 정보를 모델에 많이 노출시키게 됨
  • 결국 검증 데이터에 맞추어 최적화했기 때문에 검증 데이터에 의도적으로 잘 수행되는 모델이 만들어짐
  • 검증 데이터가 아니고 완전히 새로운 데이터에 대한 성능이 관심 대상이라면 모델을 평가하기 위해 이전에 본 적 없는 완전히 다른 데이터셋을 사용
  • 바로 테스트 세트
  • 모델은 간접적으로라도 테스트 세트에 대한 어떤 정보도 얻어서는 안 됨
  • 테스트 세트 성능에 기초하여 튜닝한 모델의 모든 설정은 일반화 성능을 왜곡시킬 것
  • 데이터를 훈련, 검증, 테스트 세트로 나누는 것은 간단해 보일 수 있지만 데이터가 적을 때는 몇 가지 고급 기법을 사용하면 도움이 됨
  • 대표적인 세 가지 평가 방법인 단순 홀드아웃 검증(hold-out validation), K-겹 교차 검증(K-fold cross-validation), 셔플링(shuffling)을 반복한 K겹 교차 검증(iterated K-fold cross-validation)을 살펴보자
  • 또한, 상식 수준의 기준 모델을 사용해서 훈련이 잘 진행되는지 확인하는 방법도 설명하겠다.

단순 홀드아웃 검증

  • 데이터의 일정량을 테스트 세트로 떼어 놓음
  • 남은 데이터에서 훈련하고 테스트 세트로 평가
  • 앞 절에서 설명했듯이 정보 누설을 막기 위해 테스트 세트를 사용하여 모델을 튜닝해서는 안 됨
  • 이런 이유로 검증 세트도 따로 떼어 놓아야 함

단순 홀드아웃 검증

  • 홀드아웃 검증 구현 예
num_validation_samples = 10000
np.random.shuffle(data) # 데이터를 섞는 것(셔플링)이 일반적으로 좋다
validation_data = data[:num_validation_samples] # 검증 세트를 만든다
training_data = data[num_validation_samples:] # 훈련 세트를 만든다.

# 훈련 세트에서 모델을 훈련하고 검증 세트로 평가
model = get_model()
model.fit(training_data, ...)
validation_score = model.evaluate(validation_data, ...)
""" 여기에서 모델을 튜닝, 훈련, 평가하는 과정을 반복 """

# 하이퍼파라미터 튜닝이 끝나면 테스트 데이터를 제외한 모든 데이터를 사용하여 모델을 훈련
model = get_model()
model.fit(np.concatenate([training_data,
		validation_data]), ...)
test_score = model.evaluate(test_data, ...)
  • 이 평가 방법은 단순해서 한 가지 단점이 있음
  • 데이터가 적을 때는 검증 세트와 테스트 테스의 샘플이 너무 적어서 전체 데이터를 통계적으로 대표하지 못할 수 있음
  • 쉽게 이를 확인할 수 있음
  • 다른 난수 초깃값으로 셔플링해서 데이터를 나누었을 때 모델의 성능이 매우 달라지면 바로 이 문제
  • 다음에 이야기할 K-겹 교차 검증과 반복 K-겹 교차 검증이 이 문제를 해결할 수 있음

K-겹 교차 검증

  • 이 방식에서는 데이터를 동일한 크기를 가진 K개의 분할로 나눔
  • 각 분할 i에 대해 남은 K-1개의 분할로 모델을 훈련하고 분할 i에서 모델을 평가
  • 최종 점수는 이렇게 얻은 K개의 점수를 평균
  • 이 방법은 모델의 성능이 데이터 분할에 따라 편차가 클 때 도움이 됨
  • 홀드아웃 검증처럼 이 방법은 모델의 튜닝에 별개의 검증 세트를 사용

3-겹 교차 검증

  • K-겹 교차 검증 구현 예
k = 3
num_validation_samples = len(data) // k
np.random.shuffle(data)
validation_scores = []
for fold in range(k):
	# 검증 데이터 부분을 선택
	validation_data = data[num_validation_samples * fold:
    						num_validation_sample * (fold + 1)]
    training_data = np.concatenate(
    	data[: num_validation_samples * fold],
        data[num_validation_samples * (fold + 1):]
    model = get_model() # 훈련되지 않은 새로운 모델을 만든다.
    model.fit(training_data, ...)
    validation_score = model.evaluate(validation_data, ...)
    validation_scores.append(validation_score)
    validation_score = np.average(validation_scores) # 검증 점수: K개의 폴드 검증 점수 평균

# 테스트 데이터를 제외한 전체 데이터로 최종 모델을 훈련
model = get_model()
model.fit(data, ...)
test_score = model.evaluate(test_data, ...)

셔플링을 사용한 반복 K-겹 교차 검증

  • 이 방법은 비교적 가용 데이터가 적고 가능한 정확하게 모델을 평가하고자 할 때 사용
  • 캐글 경연에서는 이 방법이 아주 크게 도움이 됨
  • 이 방법은 K-겹 교차 검증을 여러 번 적용하되 K개의 분할로 나누기 전에 매번 데이터를 무작위로 섞음
  • 최종 점수는 모든 K-겹 교차 검증을 실행해서 얻은 점수의 평균이 됨
  • P * K개(P는 반복 횟수)의 모델을 훈련하고 평가하므로 비용이 매우 많이 듦

상식 수준의 기준점 넘기

  • 사용할 수 있는 여러 평가 방법 외에도 마지막으로 알아야 할 것은 상식 수준의 기준점
  • 딥러닝 모델 훈련은 평행 세계에 있는 로켓을 발사하는 버튼을 누르는 것과 비슷함
  • 볼 수도 들을 수도 없음
  • 즉, 매니폴드 학습 과정을 관찰할 수 없음
  • 이는 수천 개의 차원을 가진 공간에서 일어나며 3D로 투영한다고 해도 이를 해석할 수 없음
  • 유일한 피드백은 보이지 않는 로캣의 고도계와 같은 검증 지표뿐
  • 특히 로켓이 지상에서 벗어나고 있는지 확인하는 것이 중요
  • 데이터셋으로 작업을 시작하기 전에 항상 넘겨야 할 간단한 기준을 정해야 함
  • 이 임계 값을 넘으면 제대로 하고 있음을 알 수 있음
  • 모델이 실제 입력 데이터에 있는 정보를 사용하여 일반화되는 예측을 만들고 있으므로 계속 진행할 수 있음
  • 이 기준점은 랜덤한 분류기의 성능이거나 머신 러닝을 사용하지 않고 생각할 수 있는 가장 간단한 방법이 될 수 있음
  • 예를 들어 MNIST 숫자 분류 예제에서 간단한 기준점은 (랜덤한 분류기의 성능인) 0.1보다 높은 검증 정확도
  • IMDB 예제에서는 0.5보다 높은 검증 정확도
  • 로이터 예제에서는 클래스가 불균형하므로 0.18 ~ 0.19 근처가 될 것
  • 이진 분류 문제에서 90% 샘플이 클래스 A고 10%가 클래스 B에 속한다면 항상 클래스 A로 예측하는 분류기도 0.9의 검증 정확도를 달성
  • 이보다 더 높은 성능을 내야 함
  • 이전에 아무도 해결하지 못했던 문제를 다룰 때 참고할 수 있는 상식 수준의 기준점을 가지는 것이 필수적
  • 단순한 해결책보다 낫지 않다면 쓸모없는 모델
  • 아마도 잘못된 모델을 사용하거나 처음부터 머신 러닝으로 해결할 수 없는 문제일지 모름

모델 평가에 대해 유념해야 할 점

  • 평가 방식을 선택할 때 다음 사항을 유의해야 함
    • 대표성 있는 데이터: 훈련 세트와 테스트 세트가 주어진 데이터에 대한 대표성이 있어야 함. 예를 들어 숫자 이미지를 분류하는 문제에서 샘플 배열이 클래스 순서대로 나열되어 있다고 가정. 이 배열의 처음 80%를 훈련 세트로, 나머지 20%를 테스트 세트로 만들면 훈련 세트에는 0~7 숫자만 담겨 있고 테스트 세트에는 8~9 숫자만 담기게 됨. 어처구니없는 실수처럼 보이지만 놀랍게도 자주 일어나는 일. 이런 이유 때문에 훈련 세트와 테스트 세트로 나누기 전에 데이터를 무작위로 섞는 것이 일반적
    • 시간의 방향: 과거로부터 미래를 예측하려고 한다면(예를 들어 내일의 날씨, 주식 시세 등) 데이터를 분할하기 전에 무작위로 섞어서는 절대 안 됨. 이렇게 하면 미래의 정보가 누설되기 때문임. 즉, 모델이 사실상 미래 데이터에서 훈련될 것. 이런 문제에서는 훈련 세트에 있는 데이터보다 테스트 세트에 있는 모든 데이터가 미래의 것
    • 데이터 중복: 한 데이터셋에 어떤 데이터 포인트가 두 번 등장하면(실제 데이터셋에서 아주 흔한 일), 데이터를 섞고 훈련 세트와 검증 세트로 나누었을 때 훈련 세트와 검증 세트에 데이터 포인트가 중복될 수 있음. 이로 인해 훈련 데이터의 일부로 테스트하는 최악의 경우가 됨! 훈련 세트와 검증 세트가 중복되지 않는지 확인
  • 모델 성능을 신뢰 있게 평가할 수 있는 방법을 갖추면 머신 러닝의 핵심인 최적화와 일반화 사이의 긴장, 과소적합과 과대적합 사이의 균형을 모니터링할 수 있음

5.3 훈련 성능 향상하기

  • 최적적합 모델을 얻으려면 먼저 과대적합되어야 함
  • 이 경계가 어디인지 미리 알지 못하기 때문에 경계를 찾으려면 넘어가 보아야 함
  • 문제를 다루기 시작할 때 초기 목표는 약간의 일반화 능력을 보이고 과대적합할 수 있는 모델을 얻는 것
  • 이런 모델을 얻고 난 후 과대적합과 싸워 일반화 성능을 개선하는 데 초점을 맞춤
  • 이 단계에서 일반적으로 세 가지 문제가 발생
    • 훈련이 되지 않음: 시간이 지나도 훈련 손실이 줄어들지 않음
    • 훈련은 잘 시작되었지만 모델이 의미 있는 일반화를 달성하지 못함: 상식 수준의 기준점을 넘어설 수 없음
    • 시간이 지남에 따라 훈련과 검증 손실이 모두 줄어들고 기준점을 넘어설 수 있지만 과대적합되지 않을 것 같음. 여전히 과소적합 상태
  • 이런 이슈를 해결하여 머신 러닝 프로젝트의 첫 번째 큰 이정표(상식 수준의 기준점을 넘을 수 있어 약간의 일반화 능력이 있고 과대적합할 수 있는 모델을 얻는 것)를 달성하는 방법을 알아보자

경사 하강법의 핵심 파라미터 튜닝하기

  • 이따금 훈련이 시작되지 않거나 너무 일찍 중단
  • 이렇게 되면 손실은 멈추어 있음
  • 이런 문제는 항상 극복할 수 있음
  • 랜덤한 데이터에서도 모델을 훈련할 수 있다는 것을 기억하자
  • 문제에 대해 아무런 의미가 없더라도 훈련 데이터를 외우는 것만으로도 여전히 무언가를 훈련할 수 있음
  • 이런 상황이 발생하면 항상 경사 하강법 과정에 대한 설정에 문제가 있음
  • 옵티마이저 선택, 모델 가중치의 초깃값 분포, 학습률, 배치 크기
  • 이런 모든 파라미터는 상호 의존적
  • 일반적으로 나머지 파라미터는 고정하고 학습률과 배치 크기를 튜닝하는 것으로 충분함
  • 잘못된 높은 학습률로 MNIST모델 훈련하기
(train_images, train_labels), _ = mnist.load_data()
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype("float32") / 255

model = keras.Sequential([
    layers.Dense(512, activation="relu"),
    layers.Dense(10, activation="softmax")
])
model.compile(optimizer=keras.optimizers.RMSprop(1.),
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])
model.fit(train_images, train_labels,
          epochs=10,
          batch_size=128,
          validation_split=0.2)
  • 이 모델은 30% 정도의 훈련 정확도와 검증 정확도에 빠르게 도달하지만 이를 넘어서지 못함
  • 같은 모델을 적절한 학습률로 훈련하기
model = keras.Sequential([
    layers.Dense(512, activation="relu"),
    layers.Dense(10, activation="softmax")
])
model.compile(optimizer=keras.optimizers.RMSprop(1e-2), # 1e-2 = 1 * 10 ^ -2
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])
model.fit(train_images, train_labels,
          epochs=10,
          batch_size=128,
          validation_split=0.2)
  • 이제 모델 훈련이 가능
  • 비슷한 상황에 처했다면 다음을 시도해 보자
    • 학습률을 낮추거나 높임. 너무 높은 학습률은 이전 예제처럼 최적합을 크게 뛰어넘은 업데이트가 일어날 수 있음
    • 너무 낮은 학습률은 훈련을 너무 느리게 만들어 멈추어 있는 것처럼 보일 수 있음.
    • 배치 크기를 증가시킴. 배치 샘플을 더 늘리면 유익하고 잡음이 적은 (분산이 낮은) 그레이디언트가 만들어짐
  • 결국 훈련이 시작되는 설정을 찾을 것

구조에 대해 더 나은 가정하기

  • 모델이 훈련되지만 어떤 이유에서인지 검증 지표가 전혀 나아지지 않음
  • 랜덤 분류기가 달성할 수 있는 것보다 더 낫지 않은 상태
  • 즉, 모델이 훈련되지만 일반화되지 않음
  • 이는 아마도 맞닥뜨릴 수 있는 최악의 머신 러닝 상황일 것
  • 이는 접근 방식에 근본적으로 잘못된 무언가가 있다는 의미
  • 그것이 무엇인지 알기 쉽지 않을 수 있음
  •  몇 가지 팁은 다음과 같음
  • 먼저 단순하게 입력 데이터에 타깃 예측을 위한 정보가 충분하지 않을 수 있음
  • 즉, 현재 방식으로는 문제를 풀 수 없음
  • 이는 앞서 레이블을 뒤섞은 MNIST 모델을 훈련할 때 보았음
  • 모델이 잘 훈련될 수 있지만 검증 정확도는 10%에 멈춤
  • 이런 데이터셋으로는 당연히 일반화가 불가능하기 때문임
  • 현재 사용하는 모델의 종류가 문제에 적합하지 않을 수 있음
  • 예를 들어 밀집 연결 신경망을 사용하는 시계열(timeseries) 예측 문제를 볼 것
  • 이런 구조는 단순한 기준점을 넘어설 수 없음
  • 순환 신경망(recurrent neural network)이 더 적합하여 일반화가 잘됨
  • 일반화를 달성하려면 문제에 대한 올바른 가정을 하는 모델을 사용
  • 즉, 구조에 대한 올바른 가정을 내려야 함

모델 용량 늘리기

  • 다음은 모델을 과대적합시켜야 함
  • MNIST 데이터를 사용한 간단한 로지스틱 회귀 모델
model = keras.Sequential([layers.Dense(10, activation="softmax")])
model.compile(optimizer="rmsprop",
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])
history_small_model = model.fit(
    train_images, train_labels,
    epochs=20,
    batch_size=128,
    validation_split=0.2)
  • 이 모델을 훈련하면 아래 그림과 같은 손실 곡선을 얻을 수 있음
import matplotlib.pyplot as plt
val_loss = history_small_model.history["val_loss"]
epochs = range(1, 21)
plt.plot(epochs, val_loss, "b--",
         label="Validation loss")
plt.title("Effect of insufficient model capacity on validation loss")
plt.xlabel("Epochs")
plt.ylabel("Loss")
plt.legend()
plt.show()

손실 곡선에 나타난 불충분한 모델 용량의 효과

  • 검증 손실이 정점에 도달해서 역전되지 않고 멈추어 있거나 매우 느리게 좋아지는 것 같음
  • 검증 손실이 0.26에 도달한 후 그 지점에서 정체되어 있음
  • 모델을 훈련했지만 훈련 데이터에서 여러 번 반복한 후에도 과대적합되지 못했음
  • 종종 우리의 커리어도 이와 비슷할 때가 있음
  • 항상 과대적합이 가능하다는 것을 기억하자
  • 훈련 손실이 줄어들지 않는 문제와 마찬가지로 이런 문제는 항상 해결할 수 있음
  • 과대적합할 수 없는 것처럼 보인다면 모델의 표현 능력(representational power)이 부족한 것
  • 용량이 더 큰 모델이 필요함
  • 즉, 더 많은 정보를 저장할 수 있는 모델
  • 층을 추가하거나, (더 많은 가중치를 가지도록) 층 크기를 늘리거나, 현재 문제에 더 적합한 종류의 층(구조에 대해 더 나은 가정)을 사용할 수 있음
  • 96개의 유닛을 가진 2개의 중간층으로 구성되어 용량이 더 큰 모델을 훈련
model = keras.Sequential([
    layers.Dense(96, activation="relu"),
    layers.Dense(96, activation="relu"),
    layers.Dense(10, activation="softmax"),
])
model.compile(optimizer="rmsprop",
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])
history_large_model = model.fit(
    train_images, train_labels,
    epochs=20,
    batch_size=128,
    validation_split=0.2)
  • 모델이 빠르게 훈련되고 5번째 에포크 이후에 과대적합되기 시작

적절한 용량을 가진 모델의 검증 손실

5.4 일반화 성능 향상하기

데이터셋 큐레이션

  • 딥러닝의 일반화가 데이터의 잠재 구조에서 비롯된다는 것을 배웠음
  • 데이터를 사용하여 샘플 사이를 부드럽게 보간할 수 있다면 일반화 성능을 가진 딥러닝 모델을 훈련할 수 있을 것
  • 주어진 문제에 지나치게 잡음이 많거나 리스트 정렬처럼 근본적으로 불연속적인 경우 딥러닝은 도움이 되지 않음
  • 딥러닝은 마법이 아니라 일종의 곡선을 맞추는 작업
  • 적절한 데이터셋으로 작업하고 있는지 확인하는 것이 중요
  • 데이터 수집에 노력과 비용을 투자하는 것이 동일한 노력과 비용을 모델 개발에 투자하는 것보다 거의 항상 더 나은 결과를 가져다 줌
    • 데이터가 충분한지 확인. 입력에서 출력을 매핑하는 공간을 조밀하게 샘플링해야 한다는 것을 기억하자. 데이터가 많을수록 좋은 모델이 만들어짐. 이따금 처음에는 불가능한 것처럼 보이는 문제가 대용량의 데이터셋으로 해결
    • 레이블 할당 에러를 최소화. 입력을 시각화하여 이상치를 확인하고, 레이블을 교정
    • 데이터를 정제하고 누락된 값을 처리
    • 많은 특성 중에서 어떤 것이 유용한지 확실하지 않다면 특성 선택을 수행
  • 데이터의 일반화 가능성을 향상시키는 매우 중요한 한 방법은 특성 공학(feature engineering)
  • 대부분의 머신 러닝 문제에서 특성공학은 성공을 위한 핵심 요소

특성 공학

  • 특성 공학은 데이터와 머신 러닝 알고리즘(여기에서는 신경망)에 관한 지식을 사용하는 단계
  • 모델에 데이터를 주입하기 전에 (학습이 아닌) 하드코딩된 변환을 적용하여 알고리즘이 더 잘 수행되도록 만들어 줌
  • 많은 경우에 머신 러닝 모델의 임의의 데이터로부터 완벽한 학습을 한다고 기대하기는 어려움
  • 모델이 수월하게 작업할 수 있는 어떤 방식으로 데이터가 표현될 필요가 있음
  • 시계 이미지를 입력으로 받고 하루의 시간을 출력하는 모델을 개발한다고 가정
  • 이미지의 원본 픽셀을 입력으로 사용한다면 어려운 머신 러닝 문제가 될 것
  • 이를 해결하려면 합성곱 신경망이 필요할 것이고 이 네트워크를 훈련하기 위해 꽤 많은 컴퓨팅 자원도 필요함

시계의 시간을 읽기 위한 특성 공학

  • 고수준에서 이 문제를 이해하고 있다면(우리는 시계에서 시간을 읽는 방법을 알고 있음) 머신 러닝 알고리즘을 위해 훨씬 더 좋은 입력 특성을 만들 수 있음
  • 예를 들어 시계 바늘의 검은색 픽셀을 따라 각 바늘 끝의 (x,y)좌표를 출력하는 간단한 파이썬 스크립트를 만듦
  • 그 다음 간단한 머신 러닝 알고리즘을 사용하여 이 좌표와 적절한 시각의 관계를 학습할 수 있음
  • 이보다 더 좋은 특성을 만들 수도 있음
  • 좌표를 바꾸어 (x,y)포인트를 이미지 중심에 대한 극좌표로 나타낼 수 있음
  • 이제 각 시계 바늘의 각도가 입력
  • 이렇게 특성을 준비하면 문제가 너무 쉬워져서 머신 러닝이 전혀 필요하지 않음
  • 간단한 반올림 연산과 딕셔너리 참조만으로 하루의 시간을 추정하기 충분함
  • 이것이 특성 공학의 핵심
  • 특성을 더 간단한 방식으로 표현하여 문제를 쉽게 만듦
  • 잠재 매니폴드를 더 매끄럽고, 간단하고, 구조적으로 만듦
  • 이렇게 하려면 일반적으로 해당 문제를 아주 잘 이해하고 있어야 함
  • 딥러닝 이전에는 특성 공학이 머신 러닝 워크플로에서 가장 중요한 부분
  • 전통적인 얕은 학습 방법의 알고리즘들은 스스로 유용한 특성을 학습할 만큼 충분히 넓은 가설 공간을 가지고 있지 않음
  • 알고리즘에 데이터를 표현하는 방식에 성공 여부가 달려 있음
  • 예를 들어 합성곱 신경망인 MNIST 숫자 이미지 분류 문제를 해결하기 전까지 전형적인 해결책은 하드코딩된 특성을 사용하는 것
  • 숫자 이미지에 있는 동심원의 수, 이미지에 있는 숫자의 높이, 픽셀 값의 히스토그램(histogram) 등
  • 다행히 최신 딥러닝은 대부분 특성 공학이 필요하지 않음
  • 신경망이 자동으로 원본 데이터에서 유용한 특성을 추출할 수 있기 때문임
  • 그렇다면 심층 신경망을 사용할 때 특성 공학에 대해 신경 쓰지 않아도 될까?
  • 두 가지 이유로 그렇지 않음
    • 좋은 특성은 적은 자원을 사용하여 문제를 더 멋지게 풀어낼 수 있음. 예를 들어 시계 바늘을 읽는 문제에 합성곱 신경망을 사용하는 것은 어울리지 않음. 좋은 특성은 더 적은 데이터로 문제를 풀 수 있음.
    • 딥러닝 모델이 스스로 특성을 학습하는 능력은 가용한 훈련 데이터가 많을 때 발휘. 샘플 개수가 적다면 특성에 있는 정보가 매우 중요해짐

조기 종료 사용하기

  • 딥러닝에서는 항상 지나치게 파라미터가 많은 모델을 사용
  • 즉, 잠재 매니폴드를 학습하기 위해 필요한 최소한 것보다 훨씬 많은 자유도를 가짐
  • 딥러닝 모델을 끝까지 훈련하지 않기 때문에 이런 과도한 파라미터는 문제가 되지 않음
  • 모델을 끝까지 훈련하면 일반화가 전혀 되지 않을 것
  • 항상 훈련 손실이 최솟값에 도달하기 훨씬 전에 훈련을 중단하게 됨
  • 훈련 중 일반화 성능이 가장 높은 정확한 최적적합의 지점(과소적합과 과대적합 사이의 정확한 경계)을 찾는 것은 일반화 성능을 향상시킬 수 있는 가장 효과적인 방법 중 하나
  • 이전 장의 예제에서 최상의 검증 점수를 내는 에포크 횟수를 찾기 위해 필요보다 오랫동안 모델을 훈련
  • 그 다음 정확히 해당 에포크 횟수 동안 새로운 모델을 다시 훈련
  • 이것이 기본이지만 중복 작업이며 종종 많은 비용이 듦
  • 그 대신 에포크가 끝날 때마다 모델을 저장하고 최상의 에포크를 찾은 후 저장된 모델을 재사용할 수 있음
  • 케라스에서는 일반적으로 EarlyStopping 콜백(callback)을 사용하여 이를 처리
  • 검증 지표가 더 이상 향상되지 않으면 바로 훈련을 중지하고 그 전까지 최상의 검증 점수를 낸 모델을 남길 수 있음

모델 규제하기

  • 규제(regularization) 기법은 훈련 데이터에 완벽하게 맞추려는 모델의 능력을 적극적으로 방해하는 일련의 모범 사례
  • 이를 통해 모델의 검증 점수를 향상시키는 것이 목적
  • 모델을 더 간단하고 더 평범하게, 곡선을 부드럽고 더 일반적으로 만드는 경향을 가지기 때문에 모델을 '규제'한다고 말함
  • 모델이 훈련 세트에 덜 특화되고 데이터의 잠재 매니폴드를 조금 더 가깝게 근사함으로써 일반화 능력을 높일 수 있음
  • 모델 규제는 항상 정확한 평가 절차를 따라야 하는 과정임을 명시하자
  • 측정이 가능한 경우에만 일반화를 달성할 수 있음
  • 가장 널리 사용되는 규제 기법을 알아보고 영화 분류 모델에 실제 적용하여 성능을 향상시켜 보자
  • 과대적합을 완화시키는 가장 간단한 방법은 모델 크기(층의 수와 층에 있는 유닛 개수로 결정되는 학습 가능한 파라미터 개수)를 줄이는 것
  • 모델의 기억 용량에 제한이 있다면 훈련 데이터를 단순히 외워 버리지 못할 것
  • 손실을 최소화하기 위해 타깃에 대한 예측 성능을 가진 압축된 표현을 학습
  • 정확히 이런 표현이 우리 관심 대상
  • 동시에 기억해야 할 것은 과소적합되지 않도록 충분한 파라미터를 가진 모델을 사용해야 한다는 점
  • 모델의 기억 용량이 부족해서는 안 됨
  • 너무 많은 용량과 충분하지 않은 용량 사이의 절충점을 찾아야 함
  • 안타깝지만 알맞은 층의 수나 각 층의 유닛 개수를 결정할 수 있는 마법 공식은 없음
  • 데이터에 알맞은 모델 크기를 찾으려면 각기 다른 구조를 (당연히 테스트 세트가 아니고 검증 세트에서) 평가해 보아야 함
  • 적절한 모델 크기를 찾는 일반적인 작업 흐름은 다음과 같음
  • 먼저 비교적 적은 수의 층과 파라미터로 시작
  • 그 다음 검증 손실이 감소되기 시작할 때까지 층이나 유닛 개수를 늘리는 것
  • 원본 모델
from tensorflow.keras.datasets import imdb
(train_data, train_labels), _ = imdb.load_data(num_words=10000)

def vectorize_sequences(sequences, dimension=10000):
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.
    return results
train_data = vectorize_sequences(train_data)

model = keras.Sequential([
    layers.Dense(16, activation="relu"),
    layers.Dense(16, activation="relu"),
    layers.Dense(1, activation="sigmoid")
])
model.compile(optimizer="rmsprop",
              loss="binary_crossentropy",
              metrics=["accuracy"])
history_original = model.fit(train_data, train_labels,
                             epochs=20, batch_size=512, validation_split=0.4)
  • 작은 용량의 모델
model = keras.Sequential([
    layers.Dense(4, activation="relu"),
    layers.Dense(4, activation="relu"),
    layers.Dense(1, activation="sigmoid")
])
model.compile(optimizer="rmsprop",
              loss="binary_crossentropy",
              metrics=["accuracy"])
history_smaller_model = model.fit(
    train_data, train_labels,
    epochs=20, batch_size=512, validation_split=0.4)

IMDB 리뷰 분류에 대한 원본 모델 vs 작은 용량의 모델

  • 여기에서 볼 수 있듯이 작은 모델이 기본 모델보다 더 나중에 과대적합되기 시작(두 번째 에포크가 아니라 열 번째 이후 에포크에서)
  • 과대적합이 시작되었을 때 성능이 더 천천히 감소
  • 이번에는 문제에 필요한 것보다 훨씬 더 많은 용량을 가진 네트워크를 비교해 보자
  • 학습 대상에 대해 과도하게 많은 파라미터를 가진 모델을 만드는 것이 표준이지만 기억 용량이 너무 많을 수 있음
  • 모델이 바로 과대적합되고 검증 손실 곡선이 고르지 않고 분산이 크다면 모델이 너무 큰 것
  • (검증 지표가 고르지 않다는 것은 신뢰할 수 있는 검증 과정을 사용하지 않았다는 징후일 수 있고 예를 들어 검증 세트가 너무 작은 경우)
  • 큰 용량의 모델
model = keras.Sequential([
    layers.Dense(512, activation="relu"),
    layers.Dense(512, activation="relu"),
    layers.Dense(1, activation="sigmoid")
])
model.compile(optimizer="rmsprop",
              loss="binary_crossentropy",
              metrics=["accuracy"])
history_larger_model = model.fit(
    train_data, train_labels,
    epochs=20, batch_size=512, validation_split=0.4)

IMDB 리뷰 분류에 대한 원본 모델 vs 큰 용량의 모델

  • 용량이 큰 모델은 첫 번째 에포크 이후 거의 바로 과대적합이 시작되어 갈수록 더 심해짐
  • 검증 손실도 매우 불안정
  • 훈련 손실은 매우 빠르게 0에 가까워짐
  • 용량이 많은 모델일수록 더 빠르게 훈련 데이터를 모델링할 수 있음(결국 훈련 손실이 낮아짐)
  • 더욱 과대적합에 민감해짐(결국 훈련과 검증 손실 사이에 큰 차이가 발생)

가중치 규제 추가

  • 오캄의 면도날(Occam's razor): 어떤 것에 대한 두 가지의 설명이 있다면 더 적은 가정이 필요한 간단한 설명이 옳을 것이라는 이론
  • 이 개념은 신경망으로 학습되는 모델에도 적용
  • 어떤 훈련 데이터와 네트워크 구조가 주어졌을 때 데이터를 설명할 수 있는 가중치 값의 집합(모델)은 여러 개
  • 간단한 모델이 복잡한 모델보다 덜 과대적합될 가능성이 높음
  • 여기에서 간단한 모델은 파라미터 값 분포의 엔트로피가 작은 모델(또는 앞 절에서 본 것처럼 적은 수의 파라미터를 가진 모델)
  • 과대적합을 완화하기 위한 일반적인 방법은 모델의 복잡도에 제한을 두어 가중치가 작은 값을 가지도록 강제하는 것
  • 이로 인해 가중치 값의 분포가 더 균일하게 됨
  • 이를 가중치 규제(weight regularization)라고 하며, 모델의 손실 함수에 큰 가중치에 연관된 비용을 추가
  • 두 가지 형태의 비용이 있음
    • L1 규제: 가중치의 절댓값에 비례하는 비용이 추가(가중치의 L1 놈(norm))
    • L2 규제: 가중치의 제곱에 비례하는 비용이 추가(가중치의 L2 놈(norm)) L2 규제는 신경망에서 가중치 감쇠(weight decay)라고도 부름
  • 케라스에서는 가중치 규제 객체를 층의 키워드 매개변수로 전달하여 가중치 규제를 추가할 수 있음
  • 모델에 L2 가중치 추가하기
from tensorflow.keras import regularizers # L2 규제를 적용하기 위한 import
model = keras.Sequential([
    layers.Dense(16,
                 kernel_regularizer=regularizers.l2(0.002), # L2 규제 추가
                 activation="relu"),
    layers.Dense(16,
                 kernel_regularizer=regularizers.l2(0.002), # L2 규제 추가
                 activation="relu"),
    layers.Dense(1, activation="sigmoid")
])
model.compile(optimizer="rmsprop",
              loss="binary_crossentropy",
              metrics=["accuracy"])
history_l2_reg = model.fit(
    train_data, train_labels,
    epochs=20, batch_size=512, validation_split=0.4)
  • 위의 코드에서 l2(0.002)는 가중치 행렬의 모든 원소를 제곱하고 0.002를 곱해서 모델의 전체 손실에 더해진다는 의미
  • 이 패널티(penalty) 항은 훈련할 때만 추가
  • 이 모델의 손실은 테스트보다 훈련할 때 더 높을 것
  • 아래 그림은 L2 규제 패널티의 효과를 보여 줌
  • 여기에서 볼 수 있듯이 두 모델이 동일한 파라미터 개수를 가지고 있더라도 L2규제를 사용한 모델이 기본 모델보다 훨씬 더 과대적합에 잘 견디고 있음

검증 손실에 대한 L2 가중치 규제의 효과

  • 케라스에서는 L2 규제 대신에 다음 가중치 규제 중 하나를 사용할 수 있음
  • 케라스에서 사용할 수 있는 "가중치 규제"
from tensorflow.keras import regularizers
regularizers.l1(0.001) # L1 규제
regularizers.l1_l2(l1=0.001, l2=0.001) # L1 규제와 L2 규재 병행
  • 가중치 규제는 일반적으로 작은 딥러닝 모델에서 사용
  • 대규모 딥러닝 모델은 파라미터가 너무 많기 때문에 가중치 값을 제약하는 것이 모델 용량과 일반화에 큰 영향을 미치지 않는 경향이 있음
  • 이런 경우 드롭아웃이라는 다른 규제 방법이 선호

드롭아웃 추가

  • 드롭아웃(dropout)은 신경망을 위해 사용되는 규제 기법 중에서 가장 효과적이고 널리 사용되는 방법 중 하나
  • 모델 층에 드롭아웃을 적용하면 훈련하는 동안 무작위로 층의 출력 특성일 일부 제외시킴(0으로 만듦)
  • 한 층이 훈련하는 동안에는 어떤 입력 샘플에 대해 [0.2, 0.5, 1.3, 0.8, 1.1] 벡터를 출력한다고 가정
  • 드롭아웃을 적용하면 이 벡터의 일부가 무작위로 0으로 바뀜
  • 드롭아웃 비율은 0이 될 특성의 비율
  • 보통 0.2에서 0.5 사이로 지정
  • 테스트 단계에서는 어떤 유닛도 드롭아웃되지 않음
  • 그 대신에 층의 출력을 드롭아웃 비율에 비례하여 줄여 줌
  • 훈련할 때보다 더 많은 유닛이 활성화되기 때문임
  • 크기가 (batch_size, features)인 어떤 층의 출력을 담고 있는 넘파이 행렬 layer_output이 있다고 가정해 보자
  • 훈련할 때는 이 행렬 값의 일부가 랜덤하게 0이 됨
# 훈련할 때 유닛의 층의 출력 중 50%를 버림
layer_output *= np.random_randint(0, high=2, size=layer_output.shape)
  • 테스트할 때는 드롭아웃 비율로 출력을 낮추어 주어야 함
  • 여기에서는 0.5배만큼 스케일을 조정(앞에서 절만의 유닛을 드롭아웃했으므로)
# 테스트 단계
layer_output *= 0.5
  • 훈련 단계에 이 두 연산을 포함시켜 테스트 단계에는 출력을 그대로 두도록 구현할 수 있음
  • 실제로 종종 이런 방식으로 구현
layer_output *= np.random,randint(0, high=2, size=layer_output.shape) # 훈련 단계
layer_output /= 0.5 # 여기에서 스케일을 낮추는 대신 높인다.

훈련 단계에서 활성화 함수의 출력 행렬에 적용된 드롭아웃으로, 훈련할 때 스케일을 높여 주고 테스트 단계에서는 활성화 함수의 출력을 그대로 사용한다.

  • 이 기법이 이상하고 무계획적으로 보일 수 있음
  • 드롭아웃이 과대적합을 줄이는 데 도움이 될까?
  • 힌트는 은행에서 사용하는 부정 방지 메커니즘에서 착안
  • 노이즈가 없다면 모델이 이 패턴을 기억하기 시작할 것
  • 케라스에서는 층의 출력 바로 뒤에 Dropout 층을 추가하여 모델에 드롭아웃을 적용할 수 있음
  • IMDB 모델에 2개의 Dropout 층을 추가하고 과대적합을 얼마나 줄여 주는지 확인해 보자

검증 손실에 대한 드롭아웃의 효과

  • 기본 모델보다 확실히 향상
  • 더 낮은 검증 손실을 달성했기 때문에 L2규제보다 훨씬 잘 동작하는 것 같음
  • 정리하면 신경망에서 일반화 성능을 극대화하고 과대적합을 방지하기 위해 가장 널리 사용되는 방법은 다음과 같음
    • 훈련 데이터를 더 모음 또는 더 나은 데이터를 모음
    • 더 나는 특성을 개발
    • 네트워크의 용량을 감소
    • (작은 모델을 만들기 위해) 가중치 규제를 추가
    • 드롭아웃 추가

5.5 요약

  • 머신 러닝 모델의 목적은 이전에 본 적 없는 입력에서 정확하게 동작하는 일반화
  • 이는 보기보다 어려움
  • 심층 신경망은 훈련 샘플 사이를 성공적으로 보간할 수 있는 모수 모델(parametric model)을 훈련하여 일반화를 달성
  • 이런 모델은 훈련 데이터의 '잠재 매니폴드'를 학습했다고 말할 수 있음. 이것이 딥러닝 모델의 훈련 도중 본 샘플에 매우 가까운 입력만 이해할 수 있는 이유
  • 머신 러닝의 근본적인 문제는 최적화와 일반화 사이의 줄다리기. 일반화를 달성하기 위해 먼저 훈련 데이터에 잘 맞추어야 하지만 훈련 데이터에 대한 성능 향상은 잠시 후 불가피하게 일반화를 저해
  • 딥러닝의 모든 모범 사례는 이런 긴장 관계를 관리하는 것
  • 딥러닝 모델의 일반화 능력은 데이터의 잠재 매니폴드를 근사하는 방법을 학습하고 보간을 통해 새로운 입력을 이해할 수 있다는 사실에서 비롯됨
  • 모델을 개발하는 동안 모델의 일반화 능력을 정확하게 평가할 수 있어야 함. 
  • 간단한 홀드아웃 검증에서부터 K-겹 교차 검증과 셔플링을 사용한 반복 K-겹 교차 검증까지 다양한 방법을 사용할 수 있음
  • 검증 데이터에서 모델로 정보가 누출될 수 있기 때문에 최종 모델 평가를 위해 완전히 별개의 테스트 세트를 떼어 놓아야 한다는 것을 기억하자
  • 모델을 구축하기 시작할 때 먼저 약간의 일반화 능력을 가지고 과대적합할 수 있는 모델을 만드는 것이 목표
  • 이를 위한 모범 사례는 학습률과 배치 크기를 튜닝하고, 구조에 대해 더 나은 가정을 활용하고, 모델 용량을 늘리고 또는 단순히 더 오래 훈련하는 것
  • 모델이 과대적합되기 시작할 대 규제를 통해 일반화 성능을 향상시키도록 목표가 바뀜
  • 이를 위해 모델 용량을 줄이고, 드롭아웃이나 가중치 규제를 추가하고 또는 조기 종료를 사용할 수 있음
  • 당연히 더 크고 더 좋은 데이터셋이 모델의 일반화를 향상시키는 데 언저나 가장 좋은 방법
728x90
반응형
LIST