딥러닝

Ch4

나몬 2024. 3. 31. 22:47

4.1 영화 리뷰 분류: 이진 분류 문제

IMDB 데이터셋

  • 인터넷 영화 데이터베이스로부터 가져온 양극단의 리뷰 5만 개로 이루어진 IMDB 데이터셋을 사용
  • 훈련 데이터 2만 5,000개와 테스트 데이터 2만 5,000개로 나뉘어 있고 각각 50%는 부정, 50%는 긍정 리뷰로 구성
  • 다음 코드는 데이터셋을 로드
  • IMDB 데이터셋 로드하기
from tensorflow.keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(
    num_words=10000)
  • num_words = 10000 매개변수는 훈련 데이터에서 가장 자주 나타나는 단어 1만개만 사용하겠다는 의미
  • train_labels와 test_labels는 부정을 나타내는 0과 긍정을 나타내는 1의 리스트
  • 단어 1만 개로 제한했기 때문에 단어 인덱스는 9,999를 넘지 않음
max([max(sequence) for sequence in train_data]) # 9999출력
  • 리뷰를 다시 텍스트로 디코딩하기
word_index = imdb.get_word_index() # word_index는 단어의 정수 인덱스를 매핑한 딕셔너리
reverse_word_index = dict(
    [(value, key) for (key, value) in word_index.items()]) # 정수 인덱스와 단어를 매핑하도록 뒤집는다.
decoded_review = " ".join(
    [reverse_word_index.get(i - 3, "?") for i in train_data[0]]) # 리뷰를 디코딩한다. 0,1,2는 "패팅", "문서 시작", "사전에 없음"을 위해 예약되어 있으므로 인덱스에서 3을 뺀다.

데이터 준비

  • 신경망에 숫자 리스트를 바로 주입할 수 없음. (현재 숫자 리스트는 모두 길이가 다르지만 신경망은 동일한 크기의 배치를 기대하기 때문)
  • 리스트를 텐서로 바꾸는 두 가지 방법
    • 같은 길이가 되도록 리스트에 패딩(padding)을 추가하고 (samples, max length)크기의 정수 텐서로 변환. 그 다음 이 정수 텐서를 다룰 수 있는 층으로 신경망을 시작(Embedding 층을 말함)
    • 리스트를 멀티-핫-인코딩(multi-hot encoding)하여 0과 1의 벡터로 변환
    • 예를 들어 시퀀스 [8,5]를 인덱스 8과 5의 위치는 1이고 그 외는 모두 0인 10,000차원의 벡터로 각각 변환. 그 다음 부동 소수점 벡터 데이터를 다룰 수 있는 Dense층을 신경망의 첫 번째 층으로 사용
  • 정수 시퀀스를 멀티-핫 인코딩으로 인코딩하기
import numpy as np
def vectorize_sequences(sequences, dimension=10000):
    results = np.zeros((len(sequences), dimension)) # 크기가 (len(sequences), dimension)이고 모든 원소가 0인 행렬을 만든다.
    for i, sequence in enumerate(sequences):
        for j in sequence:
            results[i, j] = 1. # result[i]에서 특정 인덱스의 위치를 1로 만든다.
    return results
x_train = vectorize_sequences(train_data) # 훈련 데이터를 벡터로 변환
x_test = vectorize_sequences(test_data) # 테스트 데이터를 벡터로 변환
  • 레이블은 쉽게 벡터로 바꿀 수 있음
y_train = np.asarray(train_labels).astype("float32")
y_test = np.asarray(test_labels).astype("float32")

신경망 모델 만들기

  • 입력 데이터: 벡터, 레이블: 스칼라(1 또는 0)
  • 이런 문제에 잘 작동하는 모델은 relu활성화 함수를 사용한 밀집 연결 층을 그냥 쌓은 것
  • Dense층을 쌓을 때 두 가지 중요한 구조상의 결정이 필요
    • 얼마나 많은 층을 사용할 것인가?
    • 각 층에 얼마나 많은 유닛을 둘 것인가?
  • 16개의 유닛을 가진 2개의 중간층
  • 현재 리뷰의 감정을 스칼라 값의 예측으로 출력하는 세 번째 층

3개의 층으로 된 모델

  • 모델 정의하기
from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
    layers.Dense(16, activation="relu"),
    layers.Dense(16, activation="relu"),
    layers.Dense(1, activation="sigmoid")
])
  • 16개의 유닛이 있다는 것은 가중치 행렬 W의 크기가 (input dimension, 16)이라는 뜻
  • 입력 데이터와 W를 점곱하면 입력 데이터가 16차원으로 표현된 공간으로 투영(그리고 편향 벡터 b를 더하고 relu연산을 적용)
  • 표현 공간의 차원을 '모델이 내재된 표현을 학습할 때 가질 수 있는 자유도'로 이해할 수 있음
  • 유닛을 늘리면 (표현 공간을 더 고차원으로 만들면) 모델이 더욱 복잡한 표현을 학습할 수 있지만 계산 비용이 커지고 원하지 않는 패턴을 학습할 수도 있음(훈련 데이터에서는 성능을 향상시키지만 테스트 데이터에서는 그렇지 않은 패턴)
  • 중간층은 활성화 함수로 relu를 사용하고 마지막 층은 확률(0과 1 사이의 점수로, 어떤 샘플이 타깃 '1'일 가능성이 높다는 것은 그 리뷰가 긍정일 가능성이 높다는 것을 의미)을 출력하기 위해 시그모이드 활성화 함수를 사용
  • relu는 음수를 0으로 만드는 함수
  • 시그모이드는 임의의 값을 [0,1] 사이로 압축하므로 출력 값을 확률처럼 해석할 수 있음

relu함수

시그모이드(sigmoid) 함수

활성화 함수는 무엇인가요? 왜 필요한가요?

  • relu와 같은 활성화 함수(또는 비선형성(non-linearity)이라고도 부름)가 없다면 Dense층은 선형적인 연산인 점곱과 덧셈 2개로 구성

output = dot(W, input) + b

  • 이 층은 입력에 대한 선형 변환(아핀 변환)만 학습할 수 있음
  • 이 층의 가설 공간은 입력 데이터를 16차원의 공간으로 바꾸는 가능한 모든 선형 변환의 집합

마지막으로 손실 함수와 옵티마이저를 선택

  • 이진 분류 문제이고 모델의 출력이 확률이기 때문에(모델의 끝에 시그모이드 활성화 함수를 사용한 하나의 유닛으로 된 층을 놓았음), binary_crossentropy손실이 적합
  • 확률을 출력하는 모델을 사용할 때는 크로스엔트로피가 최선의 선택
  • 크로스엔트로피(crossentropy)는 정보 이론(information theory)분야에서 온 개념으로 확률 분포 간의 차이를 측정
  • 옵티마이저는 rmsprop을 사용. 이 옵티마이저는 일반적으로 거의 모든 문제에 기본 선택으로 좋음
  • 훈련하는 동안 정확도를 사용하여 모니터링
  • 모델 컴파일하기
model.compile(optimizer="rmsprop",
              loss="binary_crossentropy",
              metrics=["accuracy"])

훈련 검증

  • 검증 세트 준비하기(원본 훈련 데이터에서 1만 개의 샘플을 떼어 검증 세트 만듦)
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]
  • 훈련과 검증 손실 그리기
import matplotlib.pyplot as plt
history_dict = history.history
loss_values = history_dict["loss"]
val_loss_values = history_dict["val_loss"]
epochs = range(1, len(loss_values) + 1)
plt.plot(epochs, loss_values, "bo", label="Training loss") # "bo"는 파란색 점을 의미
plt.plot(epochs, val_loss_values, "b", label="Validation loss") # "b"는 파란색 실선을 의미
plt.title("Training and validation loss")
plt.xlabel("Epochs")
plt.ylabel("Loss")
plt.legend()
plt.show()

훈련과 검증 손실

  • 훈련과 검증 정확도 그리기
plt.clf() # 그래프를 초기화
acc = history_dict["accuracy"]
val_acc = history_dict["val_accuracy"]
plt.plot(epochs, acc, "bo", label="Training acc")
plt.plot(epochs, val_acc, "b", label="Validation acc")
plt.title("Training and validation accuracy")
plt.xlabel("Epochs")
plt.ylabel("Accuracy")
plt.legend()
plt.show()

훈련과 검증 정확도

  • 훈련 손실이 에포크마다 감소하고 훈련 정확도는 에포크마다 증가
  • 검증 손실과 정확도는 이와 같지 않음
  • 훈련 세트에서 잘 작동하는 모델이 처음 보는 데이터에서는 잘 작동하지 않는 과대적합(overfitting) 사례
  • 일반적으로 과대적합을 완화할 수 있는 여러 종류의 기술이 있다.
  • 처음부터 다시 새로운 신경망을 네 번의 에포크 동안만 훈련하고 테스트 데이터에서 평가
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"])
model.fit(x_train, y_train, epochs=4, batch_size=512)
results = model.evaluate(x_test, y_test)

최종 결과

※ 첫 번째 숫자 0.28은 테스트 손실, 두 번째 숫자 0.89는 테스트 정확도

  • 아주 단순한 방식으로도 88%의 정확도를 달성
  • 최고 수준의 기법을 사용하면 95%에 가까운 성능을 얻을 수 있음

훈련된 모델로 새로운 데이터에 대해 예측하기

  • predict() 메서드를 사용해서 어떤 리뷰가 긍정일 확률을 예측할 수 있음
model.predict(x_test)

정리

  • 원본 데이터를 신경망에 텐서로 주입하기 위해서는 꽤 많은 전처리가 필요, 단어 시퀀스는 이진 벡터로 인코딩할 수 있지만 다른 인코딩 방식도 있음
  • (출력 클래스가 2개인)이진 분류 문제에서 모델은 하나의 유닛과 sigmoid 활성화 함수를 가진 Dense층으로 끝나야 함. 이 모델의 출력은 확률을 나타내는 0과 1 사이의 스칼라 값
  • 이진 분류 문제에서 이런 스칼라 시그모이드 출력에 대해 사용할 손실 함수는 binary_crossentropy
  • rmsprop 옵티마이저는 문제에 상관없이 일반적으로 충분히 좋은 선택
  • 훈련 데이터에 대해 성능이 향상됨에 따라 신경망은 과대적합되기 시작하고 이전에 본 적 없는 데이터에서는 결과가 점점 나빠지게 됨
  • 항상 훈련세트 이외의 데이터에서 성능을 모니터링해야 함

4.2 뉴스 기사 분류: 다중 분류 문제

  • 클래스가 많기 때문에 이 문제는 다중 분류(multiclass classification)의 예
  • 각 데이터 포인트가 정확히 하나의 범주로 분류되기 때문에 좀 더 정확히 말하면 단일 레이블 다중 분류(single-label, multiclass classification) 문제
  • 각 데이터 포인트가 여러 개의 범주(예를 들어 토픽)에 속할 수 있다면 이것은 다중 레이블 다중 분류(multi-label, multiclass classification)문제가 됨
  • 로이터 데이터셋
    • 짧은 뉴스 기사와 토픽의 집합인 로이터 데이터셋
  • 로이터 데이터셋 로드하기
from tensorflow.keras.datasets import reuters
(train_data, train_labels), (test_data, test_labels) = reuters.load_data(
    num_words=10000)
  • 8,982개의 훈련 샘플과 2,246개의 테스트 샘플로 이루어짐

로이터 데이터셋

  • 로이터 데이터셋을 텍스트로 디코딩하기
word_index = reuters.get_word_index()
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
decoded_newswire = " ".join([reverse_word_index.get(i - 3, "?") for i in
    train_data[0]])

데이터 준비

  • 데이터 인코딩하기
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
  • 레이블을 벡터로 바꾸는 방법은 두 가지
    • 레이블의 리스트를 정수 텐서로 변환하는 것과 원-핫 인코딩(one-hot encoding)을 사용하는 것
    • 원-핫 인코딩이 범주형 데이터에 널리 사용되기 때문에 범주형 인코딩(categorical encoding)이라고도 부름
  • 레이블 인코딩하기
def to_one_hot(labels, dimension=46):
    results = np.zeros((len(labels), dimension))
    for i, label in enumerate(labels):
        results[i, label] = 1.
    return results
y_train = to_one_hot(train_labels) # 훈련 레이블 벡터 변환
y_test = to_one_hot(test_labels) # 테스트 레이블 벡터 변환
  • 케라스 내장 함수 이용하기
from tensorflow.keras.utils import to_categorical
y_train = to_categorical(train_labels)
y_test = to_categorical(test_labels)

모델 구성

  • 이전에 사용했던 것처럼 Dense 층을 쌓으면 각 층은 이전 층의 출력에서 제공한 정보만 사용할 수 있음
  • 한 층이 분류 문제에 필요한 일부 정보를 누락하면 그 다음 층에서 이를 복원할 방법이 없음
  • 이런 이유로 좀 더 규모가 큰 층을 사용
  • 64개의 유닛을 사용
  • 모델 정의하기
model = keras.Sequential([
    layers.Dense(64, activation="relu"),
    layers.Dense(64, activation="relu"),
    layers.Dense(46, activation="softmax")
])
  • 문제에 적용할 최선의 손실 함수는 categorical_crossentropy
  • 이 함수는 두 확률 분포 사이의 거리를 측정
  • 모델 컴파일하기
model.compile(optimizer="rmsprop",
              loss="categorical_crossentropy",
              metrics=["accuracy"])

훈련 검증

  • 훈련 데이터에서 1,000개의 샘플을 따로 떼어서 검증 세트로 사용
x_val = x_train[:1000]
partial_x_train = x_train[1000:]
y_val = y_train[:1000]
partial_y_train = y_train[1000:]
  • 20번의 에포크로 모델을 훈련
history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=20,
                    batch_size=512,
                    validation_data=(x_val, y_val))
  • 손실과 정확도 곡선
import matplotlib.pyplot as plt
loss = history.history["loss"]
val_loss = history.history["val_loss"]
epochs = range(1, len(loss) + 1)
plt.plot(epochs, loss, "bo", label="Training loss")
plt.plot(epochs, val_loss, "b", label="Validation loss")
plt.title("Training and validation loss")
plt.xlabel("Epochs")
plt.ylabel("Loss")
plt.legend()
plt.show()

훈련과 검증 손실

plt.clf()
acc = history.history["accuracy"]
val_acc = history.history["val_accuracy"]
plt.plot(epochs, acc, "bo", label="Training accuracy")
plt.plot(epochs, val_acc, "b", label="Validation accuracy")
plt.title("Training and validation accuracy")
plt.xlabel("Epochs")
plt.ylabel("Accuracy")
plt.legend()
plt.show()
  • 모델은 아홉 번째 에포크 이후에 과대적합이 시작
  • 아홉 번의 에포크로 새로운 모델을 훈련하고 테스트 세트에서 평가
model = keras.Sequential([
  layers.Dense(64, activation="relu"),
  layers.Dense(64, activation="relu"),
  layers.Dense(46, activation="softmax")
])
model.compile(optimizer="rmsprop",
              loss="categorical_crossentropy",
              metrics=["accuracy"])
model.fit(x_train,
          y_train,
          epochs=9,
          batch_size=512)
results = model.evaluate(x_test, y_test)

새로운 데이터에 대해 예측하기

  • 테스트 데이터 전체에 대한 토픽을 예측
predictions = model.predict(x_test)
  • predictions의 각 항목은 길이가 46인 벡터
  • 이 벡터는 확률 분포를 나타내기 때문에 원소를 모두 더하면 1이 됨
  • 가장 큰 값이 예측 클래스가 됨

가장 확률이 높은 클래스

레이블과 손실을 다루는 다른 방법

  • 레이블을 인코딩하는 다른 방법은 다음과 같이 정수 텐서로 변환하는 것
y_train = np.array(train_labels)
y_test = np.array(test_labels)
  • 정수 레이블을 사용할 때는 sparse_categorical_crossentropy를 사용
model.compile(optimizer="rmsprop",
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])

충분히 큰 중간층을 두어야 하는 이유

  • 마지막 출력이 46차원이기 때문에 중간층의 중간 유닛이 46개보다 많이 적어서는 안 됨
  • 정보 병목이 있는 모델
model = keras.Sequential([
    layers.Dense(64, activation="relu"),
    layers.Dense(4, activation="relu"),
    layers.Dense(46, activation="softmax")
])
model.compile(optimizer="rmsprop",
              loss="categorical_crossentropy",
              metrics=["accuracy"])
model.fit(partial_x_train,
          partial_y_train,
          epochs=20,
          batch_size=128,
          validation_data=(x_val, y_val))
  • 검증 정확도의 최고 값이 68%로 11% 정도 감소
  • 이런 손실의 원인 대부분은 많은 정보(클래스 46개의 분할 초평면을 복원하기에 충분한 정보)를 중간층의 저차원 표현 공간으로 압축하려고 했기 때문임
  • 이 모델은 필요한 정보 대부분을 4차원 표현 안에 구겨 넣었지만 전부는 넣지 못했음

정리

  • N개의 클래스로 데이터 포인트를 분류하려면 모델의 마지막 Dense층의 크기는 N이어야 함
  • 단일 레이블, 다중 분류 문제에서는 N개의 클래스에 대한 확률 분포를 출력하기 위해 softmax 활성화 함수를 사용
  • 이런 문제에는 항상 범주형 크로스엔트로피를 사용
  • 이 함수는 모델이 출력한 확률 분포와 타깃 분포 사이의 거리를 최소화
  • 다중 분류에서 레이블을 다루는 두 가지 방법
    • 레이블을 범주형 인코딩(또는 원-핫 인코딩)으로 인코딩하고 categorical_crossentropy 손실 함수를 사용
    • 레이블을 정수로 인코딩하고 sparse_categorical_crossentropy 손실 함수를 사용
  • 많은 수의 범주를 분류할 때 중간층의 크기가 너무 작아 모델에 정보의 병목이 생기지 않도록 해야 함

4.3 주택 가격 예측: 회귀 문제

  • 앞의 두 예제는 분류 문제
  • 입력 데이터의 개별적인 레이블 하나를 예측하는 것이 목적이었다.
  • 또 다른 종류의 머신 러닝 문제는 개별적인 레이블 대신에 연속적인 값을 예측하는 회귀(regression)
  • 회귀와 로지스틱 회귀(logistic regression) 알고리즘을 혼동하지 말자
  • 로지스틱 회귀는 회귀 알고리즘이 아니라 분류 알고리즘

보스턴 주택 가격 데이터셋

  • 1970년 중반 보스턴 외곽 지역의 범죄율, 지방세율 등의 데이터가 주어졌을 때 주택 가격의 중간 값을 예측
  • 데이터 포인트가 506개로 비교적 개수가 적고 404개는 훈련 샘플로, 102개는 테스트 샘플로 나뉘어 있음
  • 입력 데이터에 있는 각 특성(features)(예를 들어 범죄율)의 스케일이 서로 다름
  • 어떤 값은 0과 1 사이의 비율을 나타내고, 어떤 것은 1과 12사이의 값을 가지거나 1과 100사이의 값을 가짐
  • 보스턴 주택 데이터셋 로드하기
from tensorflow.keras.datasets import boston_housing
(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()
  • 데이터 살펴보기

보스턴 주택 가격 데이터셋

※ 이 특성들은 1인당 범죄율, 주택당 평균 방의 개수, 고속도로 접근성 등

  • 타깃은 주택의 중간 가격으로 천 달러 단위

보스턴 주택 가격 타겟 데이터셋

데이터 준비

  • 상이한 스케일을 가진 값을 신경망에 주입하면 문제가 됨
  • 모델이 이런 다양한 데이터에 자동으로 맞추려고 할 수 있지만 이는 확실히 학습을 더 어렵게 만듦
  • 이런 데이터를 다룰 때 대표적인 방법은 특성별로 정규화를 하는 것
  • 입력 데이터에 있는 각 특성(입력 데이터 행렬의 열)에 대해 특성의 평균을 빼고 표준 편차로 나눔
  • 특성의 중앙이 0 근처에 맞추어지고 표준 편차가 1이 됨
  • 넘파이를 사용하면 간단하게 할 수 있음
  • 데이터 정규화하기
mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std
test_data -= mean
test_data /= std
  • 테스트 데이터를 정규화 할 때 사용한 값이 훈련 데이터에서 계산한 값임을 주목하자
  • 머신 러닝 작업 과정에서 절대로 테스트 데이터에서 계산한 어떤 값도 사용해서는 안됨
  • 데이터 정규화처럼 간단한 작업조차도 그러함

모델 구성

  • 샘플 개수가 적기 때문에 64개의 유닛을 가진 2개의 중간층으로 작은 모델을 구성하여 사용
  • 일반적으로 훈련 데이터의 개수가 적을수록 과대적합이 더 쉽게 일어나므로 작은 모델을 사용하는 것이 과대적합을 피하는 방법
  • 모델 정의하기
# 동일한 모델을 여러 번 생성할 것이므로 함수를 만들어 사용
def build_model():
    model = keras.Sequential([ 
        layers.Dense(64, activation="relu"),
        layers.Dense(64, activation="relu"),
        layers.Dense(1)
    ])
    model.compile(optimizer="rmsprop", loss="mse", metrics=["mae"])
    return model
  • 이 모델의 마지막 층은 하나의 유닛을 가지고 있고 활성화 함수가 없음(선형 층이라고 부름)
  • 이것이 전형적인 스칼라 회귀(하나의 연속적인 값을 예측하는 회귀)를 위한 구성
  • 활성화 함수를 적용하면 출력 값의 범위를 제한하게 됨
  • 예를 들어 마지막 층에 sigmoid 활성화 함수를 적용하면 모델이 0과 1 사이의 값을 예측하도록 학습될 것
  • 여기에서는 마지막 층이 순수한 선형이므로 모델이 어떤 범위의 값이라도 예측하도록 자유롭게 학습
  • 이 모델은 mse 손실 함수를 사용하여 컴파일
  • 이 함수는 평균 제곱 오차(mean squared error)의 약어로 예측과 타깃 사이 거리의 제곱
  • 회귀 문제에서 널리 사용되는 손실 함수
  • 훈련하는 동안 모니터링을 위해 새로운 지표인 평균 절대 오차(Mean Absolute Error, MAE)를 측정
  • 이는 예측과 타깃 사이 거리의 절댓값
  • 예를 들어 이 예제에서 MAE가 0.5면 예측이 평균적으로 500달러 정도 차이가 난다는 뜻

K-겹 검증을 사용한 훈련 검증

  • (훈련에 사용할 에포크의 수 같은) 매개변수들을 조정하면서 모델을 평가하기 위해 이전 예제에서 했던 것처럼 데이터를 훈련 세트와 검증 세트로 나눔
  • 데이터 포인트가 많지 않기 때문에 검증 세트도 매우 작아짐(약 100개의 샘플)
  • 검증 세트와 훈련 세트로 어던 데이터 포인트가 선택되었는지에 따라 검증 점수가 크게 달라짐
  • 즉, 검증 세트의 분할에 대한 검증 점수의 분산(variance)이 높음
  • 이렇게 되면 모델을 신뢰 있게 평가할 수 없음
  • 이런 상황에가 가장 좋은 방법은 K-겹 교차 검증(K-fold cross-validation)을 사용하는 것
  • 데이터를 K개의 분할(즉, 폴드(fold)로 나누고(일반적으로 K = 4 또는 5), K개의 모델을 각각 만들어 K-1개의 분할에서 훈련하고 나머지 분할에서 평가하는 방법)
  • 모델의 검증 점수는 K개의 검증 점수 평균이 됨

3-겹 교차 검증

  • K-겹 검증하기
k = 4
num_val_samples = len(train_data) // k
num_epochs = 100
all_scores = []
for i in range(k):
    print(f"#{i}번째 폴드 처리중")
    val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples] # 검증 데이터 준비: k번째 분할
    val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]

    # 훈련 데이터 준비: 다른 분할 전체
    partial_train_data = np.concatenate(
        [train_data[:i * num_val_samples],
         train_data[(i + 1) * num_val_samples:]],
        axis=0)
    partial_train_targets = np.concatenate(
        [train_targets[:i * num_val_samples],
         train_targets[(i + 1) * num_val_samples:]],
        axis=0)

    model = build_model() # 케라스 모델 구성(컴파일 포함)

    # 모델 훈련(verbose = 0이므로 훈련 과정이 출력되지 않는다.)
    model.fit(partial_train_data, partial_train_targets,
              epochs=num_epochs, batch_size=16, verbose=0)

    # 검증 세트로 모델 평가
    val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
    all_scores.append(val_mae)
  • num_epochs = 100으로 실행하면 다음 결과를 얻음
  • 검증 세트가 다르므로 확실히 검증 점수가 2.0에서 2.5까지 변화가 큼
  • 평균값 2.3이 각각의 점수보다 훨씬 신뢰할 만함
  • 이것이 K-겹 교차 검증의 핵심
  • 각 폴드의 검증 점수 저장하기
num_epochs = 500
all_mae_histories = []
for i in range(k):
    print(f"#{i}번째 폴드 처리중")

    # 검증 데이터 준비: K번째 분할
    val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]
    val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]

    # 훈련 데이터 준비: 다른 분할 전체
    partial_train_data = np.concatenate(
        [train_data[:i * num_val_samples],
         train_data[(i + 1) * num_val_samples:]],
        axis=0)
    partial_train_targets = np.concatenate(
        [train_targets[:i * num_val_samples],
         train_targets[(i + 1) * num_val_samples:]],
        axis=0)

    # 케라스 모델 구성(컴파일 포함)
    model = build_model()

    history = model.fit(partial_train_data, partial_train_targets,
                        validation_data=(val_data, val_targets),
                        epochs=num_epochs, batch_size=16, verbose=0)
    mae_history = history.history["val_mae"]
    all_mae_histories.append(mae_history)
  • K-겹 검증 점수 평균 기록하기
average_mae_history = [
    np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]
  • 검증 점수 그래프 그리기
plt.plot(range(1, len(average_mae_history) + 1), average_mae_history)
plt.xlabel("Epochs")
plt.ylabel("Validation MAE")
plt.show()

에포크별 검증 MAE

  • 처음 10개의 데이터 포인트를 제외한 검증 점수 그래프 그리기
truncated_mae_history = average_mae_history[10:]
plt.plot(range(1, len(truncated_mae_history) + 1), truncated_mae_history)
plt.xlabel("Epochs")
plt.ylabel("Validation MAE")
plt.show()

처음 10개의 데이터 포인트를 제외한 검증 점수 그래프 그리기

※ 검증 MAE가 (그래프에서 뺐던 열 번의 에포크를 포함하여)120~140번째 에포크 이후에 줄어드는 것이 멈추었음

  • 이 지점 이후로는 과대적합이 시작
  • 모델의 여러 매개변수에 대한 튜닝이 끝나면(에포크의 수뿐만 아니라 중간층의 크기도 조절할 수 있음) 모든 훈련 데이터를 사용하고 최상의 매개변수로 최종실전에 투입될 모델을 훈련시킴
  • 그 다음 테스트 데이터로 성능을 확인
  • 최종 모델 훈련하기
# 새롭게 컴파일된 모델을 얻는다.
model = build_model()

# 전체 데이터로 훈련시킨다.
model.fit(train_data, train_targets,
          epochs=130, batch_size=16, verbose=0)
test_mse_score, test_mae_score = model.evaluate(test_data, test_targets)
  • 최종 결과는 다음과 같음

최종 결과

새로운 데이터에 대해 예측하기

  • 이 이진 분류 모델에서 predict()메서드를 호출할 때 각 샘플에 대해 0과 1 사이의 스칼라 점수가 반환
  • 다중 분류 모델이므로 각 샘플마다 모든 클래스에 대한 확률 분포를 얻었음
  • 이제 이 스칼라 회귀 모델의 predict()메서드를 사용하여 새로운 샘플의 가격을 1,000달러 단위로 예측할 수 있음
  • 테스트 세트에 있는 첫 번째 주택의 가격은 약 9,000달러로 예상

정리

  • 회귀는 분류에서 사용했던 것과는 다른 손실 함수를 사용. 평균 제곱 오차(MSE)는 회귀에서 자주 사용되는 손실 함수
  • 비슷하게 회귀에서 사용되는 평가 지표는 분류와 다름. 당연히 정확도 개념은 회귀에 적용되지 않음. 일반적인 회귀 지표는 평균 절대 오차(MAE)
  • 입력 데이터의 특성이 서로 다른 범위를 가지면 전처리 단계에서 각 특성을 개별적으로 스케일 조정
  • 가용한 데이터가 적다면 K-겹 검증을 사용하는 것이 신뢰할 수 있는 모델 평가 방법. 가용한 훈련 데이터가 적다면 과대적합을 피하기 위해 중간층의 수를 작은 모델을 사용하는 것이 좋음(일반적으로 1개 또는 2개)

4.4 요약

  • 벡터 데이터를 사용하는 가장 일반적인 머신 러닝 작업은 이진 분류, 다중 분류, 스칼라 회귀
  • 회귀(regression)에서 사용하는 손실 함수와 평가 지표는 분류(classification)와 다름
  • 보통 원본 데이터를 신경망에 주입하기 전에 전처리해야 함
  • 데이터에 범위가 다른 특성이 있다면 전처리 단계에서 각 특성을 독립적으로 스케일 조정
  • 훈련이 진행됨에 따라 신경망의 과대적합이 시작되고 새로운 데이터에 대해 나쁜 결과를 얻게 됨
  • 훈련 데이터가 많지 않으면 과대적합을 피하기 위해 1개 또는 2개의 중간층을 가진 모델을 사용
  • 데이터가 많은 범주로 나뉘어 있을 때 중간층이 너무 작으면 정보의 병목이 생길 수 있음
  • 데이터양이 적을 때는 K-겹 검증이 신뢰할 수 있는 모델 평가를 도와줌