Updated:

일반화: 머신 러닝의 목표

 머신 러닝의 근본적인 이슈는 최적화와 일반화 사이의 줄다리기이다.

  • 최적화(optimization): 가능한 훈련 데이터에서 최고의 성능을 얻으려고 모델을 조정하는 과정(머신 러닝에서 학습에 해당)
  • 일반화(generalization): 훈련된 모델이 이전에 본 적 없는 데이터에서 얼마나 잘 수행되는지 의미

 목표는 좋은 일반화 성능을 얻는 것이다. 하지만 일반화 성능을 제어할 벙법이 없다. 단지 모델을 훈련 데이터에 맞출 수만 있다. 만약 너무 잘 맞는다면 과대적합이 시작되고 일반화 성능은 나빠진다.

과소적합과 과대적합

 전형적인 과대접합 진행과정은 다음과 같다.

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

잡음 섞인 훈련 데이터
 실제 데이터셋에는 잘못된 입력이 있는 경우가 흔하다. 예를 들어 MNIST 숫자의 경우 이상한 이미지나 전부 검은식인 이미지가 있을 수 있다. 또한 더 안 좋은 완전히 정상적인 이미지인데 레이블이 잘못된 이미지가 있을 수 있다.
 모델을 이런 이상치에 맞추려고 하면 다음과 같이 일반화 성능이 감소된다.

불확실한 특성
 모든 데이터 잡음이 부정확성 때문에 발생하는 것은 아니다. 문제에 불확실성과 모호성이 있다면 왁변하고 깔끔하게 레이블이 부여된데이터라도 잡음이 있을 수 있다. 분류 작업에서 입력 특성 공간의 일부 영역이 동시에 여러 클래스에 연관된 경우가 종종있다.
 모델이 다음과 같이 특성 공간의 모호한 영역에 너무 확신을 가지면 이런 확률적인 데이터에 과대적합될 수 있다. 최적집합은 개별 데이터 포인트를 무시하고 더 큰 그림을 바라보아야 한다.

드문 특성과 가짜 상관관계
 평생 두 마리의 주황색 얼룩무늬 고양이만 보았고 둘 다 사교성이 매우 없다면, 주황색 얼룩무늬 고양이는 일반적으로 사요적이지 않다고 추측할 수 있다. 이것이 과대적합이다. 더 많은 주황색 고양이와 다양한 다른 종류의 고양이를 보았다면 고양이 색이 성격과 관련이 없다믄 것을 배웠을 것이다. 비슷하게 드문 특성 값을 포함한 데이터셋에서 훈련한 머신 러닝 모델은 과대적합될 가능성이 매우 높다.
 훈련 데이터에서 100개의 샘플에 등장하는 단어가 있고, 그 샘플 중 54%는 긍정이고 46%는 부정이라고 가정하면, 이 차이는 통계적으로 완전히 우연일 수 있지만 모델은 분류 작업에 이 특성을 활용할 가능성이 높다. 이것이 과대적합의 가장 보편적인 원인 중 하나이다.
 MNIST의 기존 데이터의 784차원에 백색 잡음인 784개의 차원을 연결하여 새로운 훈련 세트 예를 하나 만들어본다. 따라서 데이터 셋의 절반은 잡음이다. 비교를 위해 모두 0인 784개의 차원을 연결하여 동일한 데이터셋을 만든다. 의미 없는 특성의 연결은 데이터의 기존 정보에 전혀 영향을 미치지 않는다. 즉, 무언가 추가만 한 것이다.

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장의 모델을 훈련해 본다.

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)을 수행하는 것이 일반적이다. 특성 선택을 하는 일반적인 방법은 가용한 각 특성에 대해 어떤 유용성 점수를 계산하는 것이다. 즉, 특성과 레이블 사이의 상호 의존 정보(mutual information)처럼 작업에 대해 특성이 얼마나 유익한지 측정한다. 그다음 일정 임계 값을 넘긴 특성만 사용한다. 이렇게 하면 앞선 예에서 백색 잡음이 걸러질 수 있다.

딥러닝에서 일반화의 본질

 딥러닝 모델에 관한 놀라운 사실은 표현 능력이 충분하다면 어떤 것에도 맞추도록 훈련할 수 있다는 것이다. 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 X 28 크기의 정수 배열이며 0 ~ 255 사이의 값을 가진다. 가능한 전체 입력 갔의 가짓수는 $784^{256}$이다. 하지만 이런 입력 중 매우 적은 수만 유효한 MNIST 샘플이다. 실제 손글씨 숫자는 가능한 모든 28 X 28 unit8 배열로 이루어진 공간에서 아주 작은 부분 공간만 차지한다. 더군다나 이 부분 공간은 부모 공간에 랜덤하게 뿌려진 포인트의 집합이 아니라 매우 구조적이다.
 우선 유요한 손글씨 숫자의 부분 공간은 연속적이다. 하나의 샘플을 조금 수정해도 여전히 같은 손글씨 숫자로 인식할 수 있다. 게다가 유효한 부분 공간 안에 있는 모든 샘플은 이 부분 공간을 가로지르를 매끈한 경로로 연결되어 있다. 2개의 MNIST 숫자 A와 B를 무작위로 선택하면 A를 B로 변형시키는 연속적인 중간 이미지가 있다는 의미이다. 2개의 연속적인 중간 이미지는 다음과 같이 서로 매우 비슷하다. 두 클래스의 경계 부근에서는 모호한 모양이 조금 있겠지만 이런 모양도 여전히 숫자처럼 보일 것이다.

 기술적으로 손글씨 숫자가 가능한 모든 28 X 28 unit8 배열로 이루어진 공간 안에서 매니폴드(manifold)를 형성한다고 말한다. 매니폴드는 국부적으로는 선형 공간과 비슷하게 보이는 부모 공간의 저차원 부분 공간이다. 예를 들어 평면상의 매끄러운 한 곡선은 2D 공간 안에 있는 1D 매니폴드이다.
 더 일반적으로 매니폴드 가설(manifold hypothesis)은 실제 세상의 모든 데이터가 고차원 공간 안에 있는 저차원 매니폴드에 놓여 있다고 가정한다. 이는 딥러닝이 작동하는 이유이다. 매니폴드 가설은 다음을 의미한다.

  • 머신 러닝 모델은 가능한 입력 공간 안에서 비교적 간단하고 저차원이며, 매우 구조적인 부분 공간(잠재 매니폴드(latent manifold))만 학습한다.
  • 이런 매니폴드 중 하나 안에서 두 입력 사이를 보간(interpolation)하는 것이 항상 가능한다.
    • 연속적인 경로를 따라 한 입력에서 다른 입력으로 변형할 때 모든 포인트가 이 매니폴드에 속함

 샘플 사이를 보간하는 능력은 딥러닝에서 일반화를 이해하는 열쇠이다.

일반화의 원천인 보간
 다루는 데이터 포인트가 보간할 수 있다면 이전에 본 적 없는 포인트를 해당 매니폴드에서 가까이 놓인 다른 포인트와 연결하여 이해할 수 있다. 즉, 공간 안의 샌플만 사용해서 공간 전체를 이해할 수 있다. 이는 보간을 사용해서 빈 곳을 채울 수 있기 때문이다.다음과 같이 잠재 매니폴드에서 보간은 부모 공간에서의 선형 보간과 다르다. 예를 들어 2개의 MNIST 숫자 사이의 픽세를 평균하면 일반저긍로 유효한 숫자가 만들어지지 않는다.

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

딥러닝이 작동하는 이유

 한 장의 종이는 3D 공간 안의 2D 매니폴드를 나타낸다. 딥러닝 모델은 종이 공을 펼치는 도구이다. 즉, 잠재 매니폴드를 풀기 위한 도구이다.
 딥러닝 모델은 근본적으로 미분할 수 있어야 하기 때문에 매끄럽고 연속적인 고차원의 곡선이다. 경사 하강법을 통해 이 곡선을 부드럽고 점진적으로 데이터 포인트에 맞춘다. 딥러닝은 본질적으로 곡선(매니폴드)을 선택하여 훈련 데이터 포인트에 맞을 때까지 파라미터를 점진적으로 조정하는 것이다.
 이 곡선은 어떤 것에도 맞출 수 있는 충분한 파라미터가 있다. 실제로 모델은 충분히 오래 훈련한다면 결국 훈련 데이터를 완전히 외워 버리게 되고 일반화가 전혀 되지 않을 것이다. 하지만 학습하려는 데이터는 해당 공간에 희소하게 분산된 독립적인 포인트로 구성되지 않는다. 이 데이터는 입력 공간 안에서 고도로 구조적인 저차원의 매니폴드를 형성한다. 이는 매니폴드 가설이다. 경사 하강법으로 시간이 지남에 따라 부드럽고 점진적으로 모델 곡선을 이 데이터에 맞춘다. 따라서 다음과 같이 모델이 데이터의 매니폴드를 대략적으로 근사하는 중간 지점이 있을 것이다.

 그 지점에서 모델이 학습한 곡선을 따라 이동하는 것은 데이터의 실제 잠재 매니폴드를 따라 이동하는 것과 비슷하다. 따라서 모델이 훈련 입력 사이를 보간하여 이전에 본 적 없는 입력을 이해할 수 있을 것이다.
 딥러닝 모델이 충분한 표현 능력을 가진다는 일반적인 사실 외에도 잠재 매니폴드를 학습하는데 특히 잘 맞는 몇 가지 속성이 있다.

  • 딥러닝 모델은 입력에서부터 출력으로 매끄럽고 연속적인 매핑을 구현하므로 필수적으로 미분가능해야 하기 때문에 매끄럽고 연속적이어야 한다.
    • 이런 매끄러움은 동일한 속성을 가진 잠재 매니폴드를 근사하는 데 도움이 된다.
  • 딥러닝 모델은 훈련 데이터에 있는 정보의 형태를 반영하는 식으로 구조화되는 경향이 있다. 더 일반적으로 심층 신경망은 학습한 표현을 계층적이고 모듈 방식으로 구조화된다.
    • 이는 자연적인 데이터가 구성되는 방식을 반영한 것이다.

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

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

머신 러닝 모델 평가

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

훈련, 검증, 테스트 세트

 모델 평가의 핵심은 가용한 데이터를 항상 훈련, 검증, 테스트 3개의 세트로 나누는 것이다. 훈련 세트에서 모델을 훈련하고 검증 세트에서 모델을 평가한다. 모델을 출시할 준비가 되면 테스트 세트에서 최종적으로 딱 한 번 모델을 테스트한다. 테스트 데이터는 가능한 제품 환경의 데이터와 비슷해야 한다. 그다음 모델을 제품 환경에 배포한다. 훈련 세트와 테스트 세트 2개만 사용하면 훈련 세트에서 훈련하고 테스트 세트에서 평가하므로 간단하다.
 하지만 이처럼 하지 않는 이유는 모델을 개발할 때 항상 모델의 설정을 튜닝하기 때문이다. 예를 들어 층이나 층의 유닛 개수를 선택한다(하이퍼파라미터(hyperparameter)). 검증 세트에서 모델의 성능을 평가하여 이런 튜닝을 수행한다. 본질적으로 이런 튜닝도 어떤 파라미터 공간에서 조흔 설정을 찾는 학습이다. 검증 세트의 성능을 기반으로 모델의 설정을 튜닝하면 검증 세트로 모델을 직접 훈련하지 않더라고 빠르게 검증 세트에 과대적합될 수 있다.
 이 현상의 핵심은 정보 누설(information leak)로, 검증 세트의 모델 성능에 기반하여 모델의 하이퍼파라미터를 조정할 때마다 검증 데이터에 관한 정보가 모델로 새는 것이다. 하나의 파라미터에 대해 단 한 번만 튜닝하면 아주 적은 정보가 누설된다. 이런 검증 세트로는 모델을 평가할 만하다. 하지만 한 번 튜닝하고 나서 검증 세트에 평가한 결과를 가지고 다시 모델을 조정하는 과정을 여러 번 반복하면, 검증 세트에 관한 정보를 모델에 많이 노출시키게 된다.
 결국 검증 데이터에 맞추어 최적화했기 때문에 검증 데이터에 의도적으로 잘 수행되는 모델이 만들어진다. 검증 데이터가 아니고 완전히 새로운 데이터에 대한 성능이 관심 대상이라면 모델을 평가하기 위해 이전에 본 적 없는 완전히 다른 데이터셋을 사용해야한다. 바로 테스트 세트이다. 모델은 간접적으로라도 테스트 세트에 대한 어떤 정보도 얻어서는 안된다. 테스트 세트 성능에 기초하여 튜닝한 모델의 모든 설정은 일반화 성능을 왜곡시킬 것이다.
 데이터를 훈련, 검증, 테스트 세트로 나누는 것은 간단해 보일 수 있지만 데이터가 적을 때는 몇가지 고급 기법을 사용하면 도움이 된다. 대표적인 세 가지 평가 방법이 있다.

  • 단순 홀드아웃 검증(hold-out validation)
    • 데이터의 일정량을 테스트 세트로 뗴어 놓고 남은 데이터에서 훈련하고 테스트 세트로 평가
    • 정보 누설을 막기 위해 테스트 세트를 사용하여 모델을 튜닝해서는 안되므로 검증 세트로 따로 떼어 놓음
    • 이 평가 벙법은 단순해서 데이터가 적을 때는 검증 세트와 테스트 세트의 샘플이 너무 적어 주어진 전체 데이터를 통계적으로 대표하지 못할 수 있음
    • K-겹 교차 검증과 반복 K-겹 교차 검증이 문제를 해결할 수 있음

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-fold cross-validation)
    • 데이터를 동일한 크기를 가진 K개의 분할로 나누고 각 분할 i에 대해 남은 K - 1개의 분할로 모델을 평가
    • 최종 점수는 이렇게 얻은 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_samples * (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_datam ...)
    validation_score = append(validation_score)
    validation_score = np.average(validation_scores) # 검증 점수: K개의 폴드 검증 점수 평균
### 테스트 데이터를 제외한 전체 데이터로 최종 모델을 훈련
model = get_model()
model.fit(data, ...)
test_score = model.evaluate(test_data, ...)
###
  • 셔플링(shuffling)을 사용한 반복 K겹 교차 검증(iterated K-fold cross-validation)
    • K-겹 교차 검층을 여러 번 적용하되 K개의 분할로 나누기 전에 매번 데이터를 무작위로 섞음
    • 최종 점수는 모든 K-겹 교차 검증을 실행해서 얻은 점수의 평균
    • 결국 P*K개(P는 반복 횟수)의 모델을 훈련하고 평가하므로 비용이 매우 많이 듬.
    • 이 방법은 비교적 가용 데이터가 적고 가능한 정확하게 모델을 평가하고자 할 때 사용

상식 수준의 기준점 넘기

 사용할 수 있는 여러 평가 방법 외에도 마지막으로 알아야 할 것은 상식 수준의 기준점이다. 매니폴드 학습 과정은 괒찰할 수 없다. 이는 수천 개의 차원을 가진 공간에서 일어나며 3D로 투영한다고 해도 이를 해석할 수 없다. 유일한 피드백은 검증 지표뿐이다.
 데이터셋으로 작업을 시작하기 전에 항상 넘어야 할 간단한 기준점을 정해야 한다. 이 임계 값을 넘으면 제대로 하고 있음을 알 수 있다. 모델이 실제 입력 데이터에 있는 정보를 사용하여 일반화 되는 예측을 만들고 있으므로 계속 진행할 수 있다. 이 기준점은 랜덤한 분류기의 성능이거나 머신 러닝을 사용하지 않고 생각할 수 있는 가장 간단한 방법이 될 수 있다.
 이전에 아무도 해결하지 못해던 문제를 다룰 때 참고할 수 있는 상식 수준의 기준점을 가지는 것이 필수적이다. 단순한 해결책보다 낫지 않으면 쓸모없는 모델이다. 아마도 잘못된 모델을 사용하거나 처음부터 머신 러닝으로 해결할 수 없는 문제일지 모른다.

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

 평가 방식을 선택할 때 다음 사항을 유의해야 한다.

  • 대표성 있는 데이터: 훈련 세트와 테스트 세트가 주어진 데이터에 대한 대표성이 있어야 한다.
  • 시간의 방향: 과거로부터 미래를 예측하려고 한다면 데이터를 분할하기 전에 무작위로 섞어서는 절대 안된다.
  • 데이터 중복: 한 데이터셋에 어떤 데이터 포인트가 두 번 등장하면, 데이터를 섞고 훈련 세트와 검증 세트로 나누었을 때 훈련 세트와 검증 세트에 데이터 포인트가 중복될 수 있다.

 모델 성능을 신뢰 있게 평가할 수 있는 방법을 갖추면 머신 러닝의 핵심인 최적화와 일반화 사이의 긴장, 과소적합과 과대적합 사이의 균형을 모니터링할 수 있다.

훈련 성능 형성하기

 최적적합 모델을 얻으려면 먼저 과대적합되어야 한다. 이 경계가 어디인지 미리 알지 못하기 때문에 경계를 찾으려면 넘어가 보아야 한다. 따라서 문제를 다루기 시작할 때 초기 목표는 약간의 일반화 능력을 보이고 과대적합할 수 있는 모델을 얻는 것이다. 이런 모델을 얻고 난 후 과대적합과 싸워 일반화 성능을 개선하는 데 초점을 맞춘다.
 이 단계에서 일반적으로 세 가지 문제가 발생한다.

  • 훈련이 되지 않음: 시간이 지나도 훈련 손실이 줄어들지 않음
  • 훈련은 잘 시작되었지만 모델이 의미 있는 일반화를 달성하지 못함: 상식 수준의 기준점을 넘어설 수 없음
  • 시간이 지남에 따라 훈련과 검증 손실이 모두 줄어들고 기준점을 넘어설 수 있지만 과대적합되지 않을 것 같음, 여전히 과소적합상태

 이런 이슈를 해결하여 머신 러닝 프로젝트의 첫 번째 큰 이정표를 달성하는 방법을 알아본다.

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

 이따금 훈련이 시작되지 않거나 너무 일찍 중단된다. 이렇게 되면 손실은 멈추어 있다. 이런 문제는 항상 극복할 수 있다.
 이런 상황이 발생하면 항상 경사 하강법 과정에 대한 설정에 문제가 있다. 옵티마이저 선택, 모델 가중치의 초깃값 분포, 학습률, 배치 크기이다. 이런 모든 파라미터는 상호 의존적이다. 일반적으로 나머지 파라미터는 고정하고 학습률과 배치 크기를 튜닝하는 것으로 충분하다.

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

 모델이 훈련되지만 어떤 이유에서인지 검증 지표가 전혀 나이지지 않는다. 랜덤 분류기가 달성할 수 있는 것보다 더 낫지 않은 상태이다. 즉, 모델이 훈련되지만 일반화되지 않는 것이다. 이는 아마도 맞닥뜨릴 수 있는 최악의 머신 러닝 상황이다. 이는 접근 방식에 근본적으로 잘못 된 무언가가 있다는 뜻이다. 몇가지 팁은 다음과 같다.

  • 입력 데이터에 타깃 예측을 위한 정보가 충분하지 않을 수 있음
    • 현재 방식으로는 문제를 풀 수 없다.
    • 일반화가 불가능하다.
  • 현재 사용하는 모델의 종류가 문제에 적합하지 않을 수 있음
    • 일반화를 달성하려면 문제에 대한 올바른 가정을 하는 모델을 사용해야 한다.
    • 구조에 대한 올바른 가정을 내려야 한다.

모델 용량 늘리기

 항상 과대적합이 가능하다. 훈련 손실이 줄어들지 않는 문제와 마찬가지로 이런 문제는 항상 해결할 수 있다. 과대적합할 수 없는 것처럼 보인다면 모델의 표현 능력(representaional power)이 부족한 것이다. 용량이 더 큰 모델이 필요하다. 즉, 더 많은 정보를 저장할 수 있는 모델이다. 층을 추가하거나, 층 크기를 늘리거나, 현재 문제에 더 적합한 종류의 층을 사용할 수 있다.

일반화 성능 형성하기

 모델이 어느 정도 일반화 성능을 갖고 과대적합할 수 있다면 이제 일반화를 극대화하는 데 초점을 맞출 차례이다.

데이터셋 큐레이션

 딥러닝은 일종의 곡선을 맞추는 작업이다. 따라서 적절한 데이터셋으로 작업하고 있는지 확인하는 것이 중요하다. 데이터 수집에 노력과 비용을 투자하는 것이 동일하 노력과 비용을 모델 개발에 투자하는 것보다 거의 항상 더 나은 결과를 가져다준다.

  • 데이터가 충분한지 확인
  • 레이블 할당 에러를 최소화: 입력을 시각화하여 이상치를 확인하고, 레이블을 교정
  • 데이터를 정제하고 누락된 값을 처리
  • 많은 특성 중에서 어떤 것이 유용한지 확실하지 않다면 특성 선택을 수행

 데이터의 일반화성능을 향상시키는 매우 중요한 방법은 특성 공학(feature engineering)이다. 대부분의 머신 러닝 문제에서 특성 공학은 성공을 위한 핵심 요소이다.

특성 공학

 특성 공학은 데이터와 머신 러닝 알고리즘에 관한 지식을 사용하는 단계이다. 모델에 데이터를 주입하기 전에 학습이 아닌 하드코딩된 변환을 적용하여 알고리즘이 더 잘 수행되도록 만들어준다. 많은 경우에 머신 러닝 모델이 임의의 데이터로부터 완벽한 학습을 한다고 기대하기는 어렵다. 모델이 수월하기 작업할 수 있는 어떤 방식으로 데이터가 표현될 필요가 있다.
 특성을 더 간단한 방식으로 표현하여 문제를 쉽게 만드는 것이 특성 공학의 핵심이다. 잠재 매니폴드를 더 매끄럽고, 간단하고, 구조적으로 만든다. 이렇게 하려면 일반적으로 해당 문제를 아주 잘 이해하고 있어야 한다.
 딥러닝 이전에는 특성 공학이 머신 러닝 워크플로에서 가장 중요한 부분이였지만, 최신 딥러닝은 대부분 특성 공학이 필요하지 않는다. 신경망이 자동으로 원본 데이터에 서 유용한 특성을 추출할 수 있기 때문이다. 하지만 다음과 같이 심층 신경망을 사용할 때는 특성 공학에 대해 신경을 써야 한다.

  • 좋은 특성은 적은 자원을 사용하여 문제를 더 멋지게 풀어낼 수 있음
  • 좋은 특성은 더 작은 데이터로 문제를 풀 수 있음

조기 종료 사용하기

 딥러닝에서는 항상 지나치게 파라미터가 많은 모델을 사용한다. 즉, 잠재 매니폴드를 학습하기 위해 필요한 최소한 것보다 훨씬 많은 자유도를 가진다. 딥러닝 모델을 끝까지 훈련하지 않기 때문에 이런 과도한 파라미터는 문제가 되지 않는다. 모델을 끝까지 훈련하면 일반화가 전혀 되지 않을 것이다. 항상 훈련 손실이 최솟값에 도달하기 훨씬 전에 훈련을 중단해야 한다. 훈련 중 일반화 성능이 가장 높은 정확한 최적적합의 지점을 찾는 것은 일반화 성능을 향상시킬 수 있는 가장 효과적인 방법 중 하나이다.

모델 규제하기

규제(regularization) 기법은 훈련 데이터에 완벽하게 맞추려는 모델의 능력을 적극적으로 방해하는 일련의 모법 사례이다. 이를 통해 모델의 검증 점수를 향상시키는 것이 목적이다. 모델을 더 간단하고 더 평범하게, 곡선을 부드럽고 더 일반적으로 만드는 경향을 가지기 때문에 모델을 ‘규제’한다고 말한다. 따라서 모델이 훈련 세트에 덜 특화되고 데이터의 잠재 매니폴드를 조금 더 가깝게 근사함으로써 일반화 성능을 높일 수 있다.
 모델 규제는 항상 정확한 평가 절차를 따라야 하는 과정이다. 측정이 가능한 경우에만 일반화를 달성할 수 있다.
 너무 작은 모델은 과대적합되지 않는다. 과대적합을 완화시키는 가장 간단한 방법은 모델 크기를 줄이는 것이다. 모델의 기억 용량에 제한이 있다면 훈련 데이터를 단순히 외워 버리지 못할 것이다. 따라서 손실을 최소화하기 위해 타깃에 대한 예측 성능을 가진 압축된 표현을 학습해야 한다. 동시에 기억해야 할 것은 과소적합되지 않도록 충분한 파라미터를 가진 모델을 사용해야 한다는 것이다.
 적절한 모델 크기를 찾는 일반적인 작업은 다음과 같다.

  • 먼저 비교적 적은 수의 층과 파라미터로 시작
  • 그다음 검증 손실이 감소되기 시작할 때까지 층이나 유닛 개수를 늘리는 것

가중치 규제 추가
오캄의 면도날(Occam`s razor) 이론은 어떤 것에 대한 두 가지의 설명이 있다면 더 적은 가정이 필요한 간단한 설명이 옳을 것이라는 이론이다. 이 개념은 신경망으로 학습되는 모델에도 적용된다. 어떤 훈련 데이터와 네트워크 구조가 주어졌을 때 데이터를 설명할 수 있는 가중치 값의 집합은 여러개이다. 간단한 모델이 복잡한 모델보다 덜 과대적합될 가능성이 높다.
 여기세서 간단한 모델은 파라미터 값 분포의 엔트로피가 작은 모델이다. 그러므로 과대적합을 완화하기 위한 일반적인 방법은 모델의 복잡도에 제한을 두어 가중치가 작은 값을 가지도록 강제하는 것이다. 이로 인해 가중치 값의 분포가 더 균일하게 된다. 이를 가중치 규제(weight regularization)라고 하며, 모델의 손실 함수에 큰 가중치에 연관된 비용을 추가한다. 이는 두 가지 형태의 비용이 있다.

  • L1 규제: 가중치의 절댓값에 비례하는 비용이 추가된다.
  • L2 규제: 가중치의 제곱에 비례하는 비용이 추가된다.
    • L2 규제는 신경망에서 가중치 감쇠(weight decay)라고도 부른다.

 케라스에서는 가중치 규제 객체를 층의 키워드 매개변수로 전달하여 가중치 규제를 추가할 수 있다.
 가중치 규제는 일반적으로 작은 딥러닝 모델에서 사용된다. 대규모 딥러닝 모델은 파라미터가 너무 많기 때문에 가중치 값을 제약하는 것이 모델 용량과 일반화에 큰 영향을 미치지 않는 경향이 있다. 이런 경우 드롭아웃이라는 다른 규제 방법이 선호된다.

드롭아웃 추가
드롭아웃(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 # 스케일을 낮추는 대신 높인다.

 드롭아웃이 과대적합을 줄이는 데 도움이 되는 이유는 노이즈가 없다면 모델이 이 패턴을 기억하기 시작하기 때문이다.
 정리하면 신경망에서 일반화 성능을 극대화하고 과대적합을 방지하기 위해 가장 널리 사용하는 방법은 다음과 같다.

  • 훈련 데이터를 더 모은다. 또 더 나은 데이터를 모은다.
  • 더 나은 특성을 개발한다.
  • 네트워크의 용량을 감소시키다.
  • 가중치 규제를 추가한다.
  • 드롭아웃을 추가한다.

댓글남기기