728x90
반응형
https://www.tensorflow.org/tutorials/keras/text_classification?hl=ko
- 영화 리뷰 텍스트를 긍정 또는 부정으로 분류
- 이진(binary) 분류 문제
- 인터넷 영화 데이터베이스의 IMDB 데이터셋 사용 : 5만개의 영화리뷰 텍스트
- 25000개의 훈련데이터셋, 25000개의 테스트 데이터셋
- 긍정적 리뷰와 부정적 리뷰 개수 동일
IMDB 데이터셋 다운로드
- 리뷰들은 미리 전처리 된 상태 : 정수 시퀸스로 변환
- 각 정수는 어휘사전에 있는 특정 단어 의미
- 파라미터 num_words는 훈련 데이터에서 가장 많이 등장하는 상위 단어 개수
imdb = keras.datasets.imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
데이터 탐색
- 데이터셋은 정수배열
- 레이블은 0 또는 1 (0: 부정적, 1: 긍정적)
print("훈련 샘플: {}, 레이블: {}".format(len(train_data), len(train_labels)))
print(train_data[0])
>>>
훈련 샘플: 25000, 레이블: 25000
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]
- 영화 리뷰들은 길이가 다름
- 신경망의 입력은 길이가 같아야 하기 때문에 추후에 문제 해결
len(train_data[0]), len(train_data[1])
>>>
(218, 189)
정수를 단어로 다시 변환하기
# 단어와 정수 인덱스를 매핑한 딕셔너리
word_index = imdb.get_word_index()
# 처음 몇 개 인덱스는 사전에 정의됨 (공백, 시작표시, Unknown, Unused)
word_index = {k:(v+3) for k,v in word_index.items()}
word_index["<PAD>"] = 0
word_index["<START>"] = 1
word_index["<UNK>"] = 2 # unknown
word_index["<UNUSED>"] = 3
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
def decode_review(text):
return ' '.join([reverse_word_index.get(i, '?') for i in text])
# decode_review 함수 이용해 첫 번째 리뷰 텍스트 출력
decode_review(train_data[0])
>>>
<START> this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert <UNK> is an amazing actor and now the same being director <UNK> father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for <UNK> and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also <UNK> to the two little boy's that played the <UNK> of norman and paul they were just brilliant children are often left out of the <UNK> list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for wha
데이터 준비
- 리뷰(정수 배열)는 신경망에 주입하기 전 텐서로 변환되어야 함
- 변환 방법
- One-Hot Encoding : 정수배열을 0과 1로 이루어진 벡터로 변환
- 실수 벡터 데이터를 다룰 수 있는 Dense층을 신경망의 첫 번째 층으로 사용
- num_words * num_reviews 크기의 행렬이 필요하기 때문에 메모리 많이 사용
- 정수 배열의 길이가 모두 같도록 패딩 추가
- max_length * num_reviews 크기의 정수 텐서 만듬
- 이런 형태의 텐서 다룰 수 있는 임베딩(embedding)층을 첫 번째 층으로 사용
- One-Hot Encoding : 정수배열을 0과 1로 이루어진 벡터로 변환
- 두 번째 방식 사용 : pad_sequences 함수 사용해 길이 맞춰줌
# 두 번째 방식 사용
train_data = keras.preprocessing.sequence.pad_sequences(train_data,
value=word_index["<PAD>"],
padding='post',
maxlen=256)
test_data = keras.preprocessing.sequence.pad_sequences(test_data,
value=word_index["<PAD>"],
padding='post',
maxlen=256)
# 샘플 길이 확인
len(train_data[0]), len(train_data[1])
>>>
(256, 256)
# 패딩된 첫 번째 리뷰 내용 확인
print(train_data[0])
>>>
[ 1 14 22 16 43 530 973 1622 1385 65 458 4468 66 3941
4 173 36 256 5 25 100 43 838 112 50 670 2 9
35 480 284 5 150 4 172 112 167 2 336 385 39 4
172 4536 1111 17 546 38 13 447 4 192 50 16 6 147
2025 19 14 22 4 1920 4613 469 4 22 71 87 12 16
43 530 38 76 15 13 1247 4 22 17 515 17 12 16
626 18 2 5 62 386 12 8 316 8 106 5 4 2223
5244 16 480 66 3785 33 4 130 12 16 38 619 5 25
124 51 36 135 48 25 1415 33 6 22 12 215 28 77
52 5 14 407 16 82 2 8 4 107 117 5952 15 256
4 2 7 3766 5 723 36 71 43 530 476 26 400 317
46 7 4 2 1029 13 104 88 4 381 15 297 98 32
2071 56 26 141 6 194 7486 18 4 226 22 21 134 476
26 480 5 144 30 5535 18 51 36 28 224 92 25 104
4 226 65 16 38 1334 88 12 16 283 5 16 4472 113
103 32 15 16 5345 19 178 32 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0]
모델 구성
- 모델 구조 결정
- 모델에서 얼마나 많은 층을 사용할 것인가?
- 각 층에서 얼마나 많은 은닉 유닛(층)을 사용할 것인가?
- 입력크기는 영화 리뷰 데이터셋에 적용된 어휘 사전의 크기
# 입력 크기는 영화 리뷰 데이터셋에 적용된 어휘 사전의 크기입니다(10,000개의 단어)
vocab_size = 10000
model = keras.Sequential()
model.add(keras.layers.Embedding(vocab_size, 16, input_shape=(None,)))
model.add(keras.layers.GlobalAveragePooling1D())
model.add(keras.layers.Dense(16, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid'))
model.summary() # 모델 구조 확인
>>>
Model: "sequential_4"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_2 (Embedding) (None, None, 16) 160000
_________________________________________________________________
global_average_pooling1d_2 ( (None, 16) 0
_________________________________________________________________
dense_8 (Dense) (None, 16) 272
_________________________________________________________________
dense_9 (Dense) (None, 1) 17
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________
- Embedding 층
- 정수로 인코딩된 단어를 입력받고 각 단어 인덱스에 해당하는 임베딩 벡터를 찾음
- 벡터는 모델이 훈련되면서 학습됨
- 벡터는 출력 배열에 새로운 차원으로 추가 (최종차원 (batch, sequence, embedding))
- GlobalAveragePooling1D 층
- sequence 차원에 대해 평균 계산하여 각 샘플에 대해 고정된 길이 출력벡터 반환
- 길이가 다른 입력을 다루는 가장 간단한 방법
- 16개의 은닉층을 가진 완전연결층(Dense) 통과
- 출력노드를 가진 완전연결층
- sigmoid 활성화 함수 사용하여 0과 1 사이의 실수 출력 (확률)
손실함수와 옵티마이저
model.compile(optimizer='adam',
loss='binary_crossentropy', # 이진분류에 적합한 손실 함수 (정답타깃분포 - 예측 분포 사이의 거리 측정)
metrics=['accuracy'])
검증세트 만들기
# 검증세트로 앞 부분 10000개 분리 (보통은 랜덤으로 분리함)
x_val = train_data[:10000]
partial_x_train = train_data[10000:]
y_val = train_labels[:10000]
partial_y_train = train_labels[10000:]
모델 훈련
- 512개의 샘플로 이루어진 미니배치에서 40번의 epoch동안 훈련
- x_train, y_train 텐서에 있는 모든 샘플에 대해 40번 반복
- 검증세트에서 모델의 손실과 정확도 모니터링
history = model.fit(partial_x_train,
partial_y_train,
epochs=40,
batch_size=512,
validation_data=(x_val, y_val),
verbose=1)
>>>
Epoch 39/40
30/30 [==============================] - 0s 13ms/step - loss: 0.0941 - accuracy: 0.9752 - val_loss: 0.3085 - val_accuracy: 0.8831
Epoch 40/40
30/30 [==============================] - 0s 15ms/step - loss: 0.0902 - accuracy: 0.9775 - val_loss: 0.3116 - val_accuracy: 0.8828
모델 평가
- 손실과 정확도
results = model.evaluate(test_data, test_labels, verbose=2)
print(results)
>>>
782/782 - 1s - loss: 0.3321 - accuracy: 0.8728
[0.3320859670639038, 0.8727999925613403]
정확도와 손실 그래프 그리기
- History 객체에 훈련동안 일어난 정보가 담긴 dictionary 들어 있음
history_dict = history.history
history_dict.keys()
>>>
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])
acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']
epochs = range(1, len(acc) + 1)
# "bo"는 "파란색 점"입니다
plt.plot(epochs, loss, 'bo', label='Training loss')
# b는 "파란 실선"입니다
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() # 그림을 초기화합니다
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()
- 검증 손실과 정확도에서 약 15~20번째 epoch부터 과대적합 현상 보임
- 과대적합을 막기 위해 20번째 epoch 근처에서 훈련 멈출 수 있음
- callback을 사용하여 자동으로 훈련 멈출 수 있음
케라스와 텐서플로 허브를 사용한 영화 리뷰 텍스트 분류하기
- TF Hub와 Keras 사용한 기초적인 전이학습(transfer learning)
728x90
'Minding's Programming > Tensorflow tutorial' 카테고리의 다른 글
[Tensorflow Tutorial] 자동차 연비 예측하기 : 회귀 (0) | 2021.06.04 |
---|---|
[Tensorflow Tutorial] 케라스와 텐서플로 허브를 사용한 영화 리뷰 텍스트 분류하기 (0) | 2021.05.28 |
[Tensorflow Tutorial] 첫 번째 신경망 훈련하기: 기초적인 분류 문제 (0) | 2021.05.28 |