이번 튜토리얼에서는 인공신경망 모델을 이용해서 영화 리뷰(review) 텍스트를 긍정(positive) 또는 부정(negative)으로 분류합니다. 이 예제는 머신러닝에서 널리 사용되는 이진(binary) 즉, 클래스(class)가 두 개인 분류 문제입니다.
여기에서는 인터넷 영화 데이터베이스(Internet Movie Database)에서 수집한 50,000개의 영화 리뷰 텍스트를 담은 IMDB 데이터셋을 사용하겠습니다. 총 50,000개의 리뷰 데이터 중 25,000개 리뷰는 훈련용으로, 25,000개는 테스트용으로 나누어져 있습니다. 나누어져 있는 훈련 세트와 테스트 세트의 클래스는 긍정적인 리뷰와 부정적인 리뷰의 개수가 동일하기 때문에 균형이 잡혀 있다고 할 수 있습니다.
이번 예제에서는 전이 학습 라이브러리이자 플랫폼인 텐서플로우 허브(Tensoorflow Hub)를 사용합니다.
# 필요한 라이브러리 임포트
import warnings
warnings.simplefilter('ignore')
import numpy as np
import tensorflow as tf
import tensorflow_hub as hub
# Tensorflow의 버전을 확인합니다
print("버전: ", tf.__version__)
# 즉시 실행 모드는 즉시 실행은 그래프를 생성하지 않고 함수를 바로 실행하는 명령형 프로그래밍 환경입니다.
print("즉시 실행 모드: ", tf.executing_eagerly())
# Tensorflow Hub는 머신러닝 모델의 재사용 가능한 부분을 게시, 검색, 소비하기 위한 라이브러리입니다.
print("허브 버전: ", hub.__version__)
IMDB 데이터셋은 텐서플로 데이터셋(TensorFlow datasets)에 포함되어 있기 때문에 다음 코드를 이용하여 IMDB 데이터셋을 다운로드 할 수 있습니다.:
import tensorflow_datasets as tfds
# 훈련 세트를 검증 데이터 사용을 위하여 6대 4로 나눕니다.
# 따라서 결국 훈련에 15,000개 샘플, 검증에 10,000개 샘플, 테스트에 25,000개 샘플을 사용하게 됩니다.
train_validation_split = tfds.Split.TRAIN.subsplit([6, 4])
(train_data, validation_data), test_data = tfds.load(
name="imdb_reviews",
split=(train_validation_split, tfds.Split.TEST),
as_supervised=True)
잠시 데이터 형태를 알아 봅시다. 이 데이터셋의 샘플은 전처리된 정수 배열입니다. 각 정수는 영화 리뷰에 나오는 단어를 나타냅니다. 레이블(label)은 정수 0 또는 1입니다. 0은 부정적인 리뷰 이고 1은 긍정적인 리뷰입니다.
처음 10개의 샘플을 출력해 보겠습니다.
train_examples_batch, train_labels_batch = next(iter(train_data.batch(10)))
train_examples_batch
처음 10개의 레이블도 출력해 보겠습니다.
train_labels_batch
신경망은 층을 쌓아서 만듭니다. 여기에는 세 개의 중요한 구조적 결정이 필요합니다:
이 예제의 입력 데이터는 문장으로 구성됩니다. 예측할 레이블은 0 또는 1입니다.
텍스트를 표현하는 한 가지 방법은 문장을 임베딩(embedding) 벡터로 바꾸는 것입니다. 임베딩은 텍스트를 숫자로 이루어진 벡터로 바꾼 결과 또는 그 과정 전체를 말합니다. 그러면 첫 번째 층으로 사전 훈련(pre-trained)된 텍스트 임베딩을 사용할 수 있습니다. 여기에는 다음과 같은 장점이 있습니다.
이번 예제는 텐서플로우 허브에 있는 사전 훈련된 텍스트 임베딩 모델인 google/tf2-preview/gnews-swivel-20dim/1을 사용합니다.
먼저 문장을 임베딩시키기 위해 텐서플로 허브 모델을 사용하는 케라스(keras) 층을 만들어 봅시다.
그 다음 몇 개의 샘플을 입력하여 테스트해 보겠습니다.
입력 텍스트의 길이에 상관없이 임베딩의 출력 크기는 (num_examples, embedding_dimension)가 됩니다.
embedding = "https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1"
hub_layer = hub.KerasLayer(embedding, input_shape=[],
dtype=tf.string, trainable=True)
hub_layer(train_examples_batch[:3])
이제 전체 모델을 만들어 보겠습니다:
model = tf.keras.Sequential()
model.add(hub_layer)
model.add(tf.keras.layers.Dense(16, activation='relu'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
model.summary()
순서대로 층을 쌓아 분류기(classifier)를 만듭니다:
첫 번째 층은 텐서플로 허브 층입니다. 이 층은 사전 훈련된 모델을 사용하여 하나의 문장을 임베딩 벡터로 매핑합니다. 여기서 사용하는 사전 훈련된 텍스트 임베딩 모델(google/tf2-preview/gnews-swivel-20dim/1)은 하나의 문장을 토큰(token)으로 나누고 각 토큰의 임베딩을 연결하여 반환합니다. 최종 차원은 (num_examples, embedding_dimension)입니다.
이 고정 크기의 출력 벡터는 16개의 은닉 유닛(hidden unit)을 가진 완전 연결 층(Dense)으로 주입됩니다.
마지막 층은 하나의 출력 노드를 가진 완전 연결 층입니다. sigmoid 활성화 함수를 사용하므로 확률 또는 신뢰도 수준을 표현하는 0~1 사이의 실수가 출력됩니다.
이제 모델을 컴파일합니다.
모델이 훈련하려면 손실 함수(loss function) 과 옵티마이저(optimizer) 가 필요합니다. 이 예제는 이진 분류 문제이고 모델이 확률을 출력하므로(출력층의 유닛이 하나이고 sigmoid 활성화 함수를 사용합니다), binary_crossentropy 손실 함수를 사용하겠습니다.
물론 다른 손실 함수를 선택할 수 없는 것은 아닙니다. 예를 들어 mean_squared_error를 선택할 수 있습니다.
하지만 일반적으로 binary_crossentropy가 확률을 다루는데 적합합니다. 이 함수는 확률 분포 간의 거리를 측정합니다. 여기에서는 정답인 타깃 분포와 예측 분포 사이의 거리입니다.
이제 모델이 사용할 옵티마이저와 손실 함수를 설정해 봅시다:
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
이 모델을 512개의 샘플로 이루어진 미니배치(mini-batch)에서 20번의 에포크(epoch) 동안 훈련합니다. x_train과 y_train 텐서에 있는 모든 샘플에 대해 20번 반복한다는 뜻입니다. 훈련하는 동안 10,000개의 검증 세트에서 모델의 손실과 정확도를 모니터링합니다:
history = model.fit(train_data.shuffle(10000).batch(512),
epochs=20,
validation_data=validation_data.batch(512),
verbose=1)
모델의 성능을 확인해 봅시다. 손실(loss, 오차를 나타내는 숫자이므로 낮을수록 좋습니다) 과 정확도(accuracy) 두 개의 값이 반환됩니다.
results = model.evaluate(test_data.batch(512), verbose=2)
for name, value in zip(model.metrics_names, results):
print("%s: %.3f" % (name, value))
학습 결과, 약 87% 정도의 정확도를 달성했습니다.
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#@title MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.