4-1) 영화 리뷰 분류: 이진 분류 문제
4-2) 뉴스 기사 분류: 다중 분류 문제
4-3) 주택 가격 예측: 회귀 문제
4-1) 영화 리뷰 분류: 이진 분류 문제
이진 분류: 각 입력 샘플이 2개의 배타적 범주로 구분되는 분류 작업 (YES/NO, 정상/비정상 등)
영화 리뷰 분류
from tensorflow.keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(
num_words=10000)
데이터셋 구성: 숫자로 인코딩된 리뷰, 리뷰의 긍정/ 부정 레이블
word_index = imdb.get_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]])
리뷰를 인코딩할 떄에는 가장 자주 쓰이는 단어 10000개만 선별하여 그에 대한 숫자값을 할당해줌.
(숫자 키, 단어 값)의 형태인 딕셔너리의 키와 값 위치를 바꿔서 (단어 키, 숫자 값) 꼴로 만듬.
데이터 준비
import numpy as np
def vectorize_sequences(sequences, dimension=10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
for j in sequence:
results[i, j] = 1.
return results
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
리스트를 멀티-핫 인코딩을 하여 리뷰에 대한 각 시퀀스가 해당되는 숫자의 위치는 1, 나머지는 모두 0으로 변환해주는 과정을 거침.
해당 과정에서 단어가 존재하는 위치만 1이고 나머지는 0으로 채워져 있는 10000차원의 벡터 생성됨 (리뷰 길이* 단어 수 총 10000개)
신경망 모델 만들기 : 아래 두가지 고려 필요
- 총 층의 수
- 각 층에 둘 유닛의 수
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개 > 층: 유닛 16개 > 출력: 유닛 1개 (이진 분류) 구조로 모델 생성
유닛이 16개라는 뜻은 가중치 행렬 W의 크기가 (입력 차원, 16) 이라는 뜻
즉, 입력 데이터와 가중치 W를 점곱하면 입력 데이터가 16차원으로 표현된 공간으로 투영됨
유닛 수 늘렸을 때
장: 복잡한 표현 학습 가능
단: 계산 비용 증가, 오버피팅 가능성 올라감
유닛 수를 줄였을 때는 계산 비용이 줄어들지만 정보가 손실될 확률은 올라감.
이떄, 각 층마다는 유닛 수 외에도 활성화 함수가 존재함.
활성화 함수는 다음과 같은 Dense 층의 연산이 가지는 제약으로 인해 필요함.
- 선형 변환에 대해서만 학습할 수 있음
- 가설 공간에 제약이 많음
- 층을 여러개 쌓아도 하나의 선형 연산임
이러한 단점을 극복하고 가설 공간을 풍부하게 해 주기 위해 존재하는 것이 활성화 함수임.
이는 모델에서 2개의 중간층에 사용된 활성화 함수 relu().
해당 함수는 비선형성을 띄고 있다.
model.compile(optimizer="rmsprop",
loss="binary_crossentropy",
metrics=["accuracy"])
컴파일 단계에서는 손실함수, 옵티마이저, 측정 지표를 추가해 줌.
훈련 검증
history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val))
history_dict = history.history
history_dict.keys()
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])
model.fit( ) 메소드는 훈련 중 발생한 모든 정보를 담은 딕셔너리 history 객체를 반환함.
딕셔너리에 각 에포크에 대한 훈련 지표 4가지를 담고 있음.
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")
plt.plot(epochs, val_loss_values, "b", label="Validation loss")
plt.title("Training and validation loss")
plt.xlabel("Epochs")
plt.ylabel("Loss")
plt.legend()
plt.show()
각 에포크에 대한 훈련 데이터셋의 손실과 검증 데이터셋의 손실을 시각화함.
4 에포크 이후 검증 데이터셋에 대한 손실이 증가하는 것을 볼 수 있음
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()
각 에포크에 대한 훈련 데이터셋의 정확도와 검증 데이터셋의 정확도를 시각화함.
4 에포크 이후 검증 데이터셋에 대한 정확도가 감소하는 것을 볼 수 있음
>> 즉, 4~5 에포크를 기점으로 오버피팅이 일어난다는 것을 알 수 있음
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)
이를 바탕으로 훈련 에포크 수를 4로 수정하여 다시 모델을 훈련시킨 다음, 테스트 데이터에 대한 예측 결과를 results에 저장함.
4-2) 뉴스 기사 분류: 다중 분류 문제
다중 분류: 각 입력 샘플이 2개 이상의 범주로 구분되는 분류 작업 (숫자 구별, 영화 카테고리 등)
from tensorflow.keras.datasets import reuters
(train_data, train_labels), (test_data, test_labels) = reuters.load_data(
num_words=10000)
사용할 데이터셋은 뉴스를 46개의 서로 다른 토픽으로 구분짓고 있는 로이터 데이터셋임.
훈련 샘플에 대해 각 토픽은 최소 10개 이상의 샘플을 가지고 있고, 각 뉴스는 단 하나의 토픽으로만 분류될 수 있음.
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)
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)
데이터를 벡터로 변환하는 방법에는 원-핫 인코딩과 범주형 인코딩이 있음.
이 경우에는 레이블의 인덱스 자리를 1로 표기하고 나머지를 0으로 채우는 원-핫 인코딩을 진행함.
from tensorflow.keras.utils import to_categorical
y_train = to_categorical(train_labels)
y_test = to_categorical(test_labels)
이를 이와 같이 to_categorical() 내장 함수를 사용하여서 똑같이 구현 가능.
모델 구성
model = keras.Sequential([
layers.Dense(64, activation="relu"),
layers.Dense(64, activation="relu"),
layers.Dense(46, activation="softmax")
])
입력 > 층: 유닛 64개 > 층: 유닛 64개 > 출력: 유닛 46개 (다중 분류) 구조로 모델 생성
model.compile(optimizer="rmsprop",
loss="categorical_crossentropy",
metrics=["accuracy"])
컴파일 과정에서 옵티마이저, 손실 함수, 측정 지표 결정
이진 분류 문제와는 다르게 범주형 크로스 엔트로피를 손실함수로 지정함
*정수 텐서로 변환하기 위해서는 손실함수를 "sparse_categorical_crossentropy" 로 지정하면 됨.
훈련 검증
x_val = x_train[:1000]
partial_x_train = x_train[1000:]
y_val = y_train[:1000]
partial_y_train = y_train[1000:]
history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val))
history에 모델을 20번의 에포크에 대하여 훈련하는 도중 발생한 모든 정보 저장
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()
20번의 에포크에 대하여 훈련 데이터와 검증 데이터의 손실을 시각화함.
7.5~ 10 사이에서 겅증 데이터셋의 손실이 점점 줄어들다가 증가하는 것을 확인할 수 있음.
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()
20번의 에포크에 대하여 훈련 데이터와 검증 데이터의 정확도를 시각화함.
7.5~ 10 사이에서 검증 데이터셋의 정확도가 증가하다가 감소하는 것을 확인할 수 있음.
모델 다시 훈련하기
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)
이를 바탕으로 훈련 에포크 수를 9로 수정하여 다시 모델을 훈련시킨 다음, 테스트 데이터에 대한 예측 결과를 results에 저장함.
* 충분히 큰 중간층을 두어야 하는 이유
마지막 출력보다 중간층이 과하게 작아지는 경우 차원 압축 과정에서 발생하는 정보 손실이 최종 예측 정확도에 영향을 미치기 떄문임.
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))
예시로 64개의 유닛이 존재했던 중간층의 유닛 수를 4개로 줄인 경우 정확도가 80 > 71%로 약 9% 정도 감소하였음.
4-3) 주택 가격 예측: 회귀 문제
회귀: 어떤 범주가 아닌 값을 예측하는 분류 작업
- 타깃이 스칼라인지, 벡터인지에 따라 스칼라 회귀와 벡터 회귀로 구분
from tensorflow.keras.datasets import boston_housing
(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()
총 13개의 특성값을 가지는 보스턴 주택 가격 데이터를 예제로 사용할 것.
데이터 준비
mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std
test_data -= mean
test_data /= std
데이터의 값들을 정규화하여 줌으로써 각 특성들이 최종 결과값에 영향을 미치는 정도가 특성의 값 범주에 영향받지 않도록 함.
예시로 범죄율은 100까지의 값을 지니는 반면 찰스강에 대한 더미변수는 0 혹은 1의 값만 지님.
모델 구성
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
마지막 출력층의 유닛을 1로 지정, 값을 예측하는 문제이기에 활성화함수 지정 x
여기에서 손실 함수는 mse, 측정 지표는 mae로 지정하였음.
MSE는 (예측 값 - 실제 값)^2 을 다 더한 값의 평균이고,
MAE는 |예측 값 - 실제 값| 을 다 더한 것의 평균임.
회귀 문제이기에 이러한 실제 값으로부터 예측 값이 떨어진 정도를 손실로 두는 방법을 택함.
훈련 검증
- 해당 데이터셋의 경우, 샘플 수가 많지 않음
- 따라서, 어떤 데이터 포인트가 선택되었느냐에 따라 검증 점수가 크게 영향
>> 이러한 문제 해결 위해 'K- 폴드 교차검증' 사용!
K- 폴드 교차검증은 위 그림과 같은 방법으로 진행됨.
데이터셋을 여러 폴드로 분할하여 각 폴드를 검증 셋으로 지정하고, 나머지 폴드를 훈련 셋으로 지정하여 훈련을 진행함.
그렇게 구한 각 폴드에 대한 검증 검수를 평균내 최종 검증 점수를 구함.
* 에포크 수를 100으로 한 결과
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]
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()
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)
이를 코드로 구현하여 모델에 적용하는 과정이다.
[1.9948174953460693, 2.536346435546875, 2.4903550148010254, 2.3979060649871826]
모든 검증 점수를 출력해 보면 결과는 이렇게 나온다.
*에포크 수를 500으로 한 결과
코드는 동일, num_epochs = 500 로만 수정한 상태.
average_mae_history = [
np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]
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()
측정 지표인 MAE에 대해 시각화한 결과 130에포크 전후쯤에서 손실이 증가하는 것으로 보았을 떄, 해당 지점이 오버피팅이 발생하는 지점이란 걸 알 수 있음.
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)
4/4 [==============================] - 0s 3ms/step - loss: 17.5936 - mae: 2.7210
이를 바탕으로 모델을 다시 훈련시킨 후, 테스트 데이터셋에 대하여 예측