1) 더 크거나 작은 층을 사용해 보세요. 32개의 유닛, 128개의 유닛 등
2) 여기에서 2개의 은닉 층을 사용했습니다. 1개나 3개의 은닉 층을 사용해 보세요.
1) 더 크거나 작은 층을 사용해 보세요. 32개의 유닛, 128개의 유닛 등
2) 여기에서 2개의 은닉 층을 사용했습니다. 1개나 3개의 은닉 층을 사용해 보세요.
앞서 언급한 것 처럼 마지막 출력이 46차원이기 때문에 중간층의 히든 유닛이 46개보다 많이 적어선느 안 됩니다. 46차원보다 훨씬 작은 중간층(예를 들어 4차원)을 두면 정보의 병목이 어떻게 나타나는지 확인해 보겠습니다.
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(4, activation='relu'))
model.add(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))
검증 정확도의 최고 값은 약 71%로 8% 정도 감소되었습니다. 이런 손실의 원인 대부분은 많은 정보(클래스 46개의 분할 초평면을 복원하기에 충분한 정보)를 중간층의 저 차원 표현 공간으로 압축하려고 했기 때문입니다. 이 네트워크는 필요한 정보 대부분을 4차원 표현 안에 구겨 넣었지만 전부는 넣지 못했습니다.
앞서 언급한 것처럼 레이블을 인코딩하는 다른 방법은 다음과 같이 정수 텐서로 변환하는 것입니다.
y_train = np.array(train_labels)
y_test = np.array(test_labels)
이 방식을 사용하려면 손실 함수 하나만 바꾸면 됩니다. 코드 3-21에 사용된 손실 함수 categorical_crossentropy는 레이블이 범주형 인코딩되어 있을 것이라고 기대합니다. 정수 레이블을 사용할때는 sparse_categorical_crossentropy 를 사용해야 합니다.
model.comile(optimizer='rmsprop',
loss='sparse_categorical_crossentropy',
metrics=['acc'])
이 손실 함수는 인터페이스만 다를 뿐이고 수학적으로 categorical_crossentropy와 동일합니다.
모델 객체의 predict 메서드는 46개의 토픽에 대한 확률 분포를 반환합니다. 테스트 데이터 전체에 대한 토픽을 예측해 보겠습니다.
predictions = model.predict(x_test)
predictions의 각 항목은 길이가 46인 벡터입니다.
>>> predictions[0].shape
(46,)
이 벡터의 원소 합은 1입니다.
>>> np.sum(predictions[0])
1.0
가장 큰 값이 예측 클래스가 됩니다. 즉 가장 확률이 높은 클래스입니다.
>>> np.argmax(predictions[0])
3
훈련 데이터에서 1,000개의 샘플을 따로 떼어서 검증 세트로 사용하겠습니다.
x_val = x_train[:1000]
partial_x_train = x_train[1000:]
y_val = one_hot_train_labels[:1000]
partial_y_train = one_hot_train_labels[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['acc']
val_acc = history.history['val_acc']
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()
이 모델은 아홉 번째 에포크 이후에 과대 적합이 시작됩니다. 아홉 번의 엨포크로 새로운 모델을 훈련하고 테스트 세트에서 평가하겠습니다.
model = modles.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(1000, )))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy']
model.fit(partial_x_train,
paartial_y_train,
epochs=9,
batch_size=512,
validation_data=(x_val, y_val))
results = model.evaluate(x_test, on_hot_test_labels)
최종 결과는 다음과 같습니다
>>> results
[1.022498257544459, 0.7756010686194165]
대략 78%의 정확도를 달성했씁니다. 균형 잡힌 이진 분류 문제에서 완전히 무작위로 분류하면 50%의 정확도를 달성합니다. 이 문제는 불균형한 데이터셋을 사용하므로 무작위로 분류하면 18%정도를 달성합니다. 여기에 비하면 이 결과는 꽤 좋은 편입니다.
>>> import copy
>>> test_labels_copy = copy.copy(test_labels)
>>> np.random.shuffle(test_labels_copy)
>>> hits_array = np.array(test_labels) == np.array(test.labels_copy)
>>> float(np.sum(hits_array)) / len(test_labels)
0.182546749777382
이 토픽 분류문제는 이전의 영화 리뷰 분류 문제와 비슷해 보입니다. 두 경우 모두 잛은 텍스트를 분류하는 것이죠. 여기에서는 새로운 제약 사항이 추가되었습니다. 출력 클래스의 개수가 2에서 46개로 늘어난 점입니다. 출력 공간의 차원이 훨씬 커졌습니다.
이전에 사용했던 것처럼 Dense층을 쌓으면 각 층은 이전 층의 출력에서 제공한 정보만 사용할 수 있습니다. 한 층이 분류 문제에 필요한 일부 정보를 누락하면 그 다음 층에서 이를 복원할 방법이 없습니다. 각 층은 잠재적으로 정보의 병목(information bottleneck)이 될 수 있습니다. 이전 예제에서 16차원을 가진 중간층을 사용했지만 16차원 공간은 46개의 클래슬르 구분하기에 너무 제약이 많을 것 같ㅅ브니다. 이렇게 규모가 작은 층은 유용한 정보를 완전히 잃게 되는 정보의 병목 지점처럼 동작할 수 있습니다.
이런 이유로 좀 더 규모가 큰 층을 사용하겠습니다. 64개의 유닛을 사용해 보죠.
from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
이 구조에서 주목해야 할 점이 두 가지 있습니다.
1) 마지막 Dense 층의 크기가 46입니다. 각 입력 샘플에 대해서 46차원의 벡터를 출력한다는 뜻입니다. 이 벡터의 각 원소(각 차원)는 각기 다른 출력 클래스가 인코딩된 것입니다.
2) 마지막 층에 softmax활성화 함수가 사용되었습니다. MNIST예제에서 이런 방식을 보았습니다. 각 입력 샘플마다 46개의 출력 클래스에 대한 확률 분포를 출력합니다. 즉 46차원의 출력 벡터를 만들며 output[i]는 어떤 샘플이 클래스 i에 속할 확률입니다. 46개의 값을 모두 더하면 1이 됩니다.
이런 문제에 사용할 최선의 손실 함수는 categorical_crossentropy입니다. 이 함수는 두 확률 분포 사이의 거리르 측정합니다. 여기에서는 네트워크가 출력한 확률 분포와 진짜 레이블의 분포 사이의 거리입니다. 두 분포 사이의 거리를 최소화하면 진짜 레이블에 가능한 가까운 출력을 내도록 모델을 훈련하게 됩니다.
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
이전에 예제와 동일한 코드를 사용해서 데이터를 벡터로 변환합니다.
import numpy as np
def vectorize_sequences(sequences, dimension=10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] =1.
return results
x_train = vectorize_sequences(train_data) .............. 훈련 데이터 벡터 변환
x_test = vectorize_sequences(test_data) ................ 텍스트 데이터 벡터 변환
레이블을 벡터로 바꾸는 방법은 두 가지입니다. 레이블의 리스트를 정수 텐서로 변환하는 것과 원-핫 인코딩을 사용하는 것입니다. 원-핫 인코딩이 범주형 데이터에 널리 사용되지 때문에 범주형 인코딩(catetorical encoding)이라고 부릅니다. 원-핫 인코딩에 대한 자세한 설명은 6.1절을 참고하세요. 이 경우 레이블의 원-핫 인코딩은 각 레이블의 인텍스 자리는 1이고 나머지는 모두 0인 벡터입니다. 다음과 같습니다.
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
one_hot_train_labels = to_one_hot(train_labels) ............. 훈련 레이블 벡터 변환
one_hot_test_labels = to_one_hot(test_labels)..............테스트 레이블 벡터 변환
MNIST예제에서 이미 보았듯이 케라스에는 이를 위한 내장 함수가 있습니다.
from keras.utils.np_utils import to_categorical
one_hot_train_labels = to_categorical(train_labels)
ont_hot_test_labels = to_categorical(test_labels)