#@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.

"""
Explanation: Copyright 2019 The TensorFlow Authors.
End of explanation
"""


!pip install tensorflow-hub
!pip install tensorflow-datasets

import os
import numpy as np

import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_datasets as tfds

print("Version: ", tf.__version__)
print("Eager mode: ", tf.executing_eagerly())
print("Hub version: ", hub.__version__)
print("GPU is", "available" if tf.config.list_physical_devices("GPU") else "NOT AVAILABLE")

"""
Explanation: 케라스와 텐서플로 허브를 사용한 영화 리뷰 텍스트 분류하기
<table class="tfo-notebook-buttons" align="left">
  <td>     <a target="_blank" href="https://www.tensorflow.org/tutorials/keras/text_classification_with_hub"><img src="https://www.tensorflow.org/images/tf_logo_32px.png">TensorFlow.org에서 보기</a>   </td>
  <td><a target="_blank" href="https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/tutorials/keras/text_classification_with_hub.ipynb"><img src="https://www.tensorflow.org/images/colab_logo_32px.png">Google Colab에서 실행</a></td>
  <td><a target="_blank" href="https://github.com/tensorflow/docs-l10n/blob/master/site/ko/tutorials/keras/text_classification_with_hub.ipynb"><img src="https://www.tensorflow.org/images/GitHub-Mark-32px.png">GitHub에서 소스 보기</a></td>
  <td><a href="https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ko/tutorials/keras/text_classification_with_hub.ipynb"><img src="https://www.tensorflow.org/images/download_logo_32px.png">노트북 다운로드</a></td>
  <td><a href="https://tfhub.dev/s?module-type=text-embedding"><img src="https://www.tensorflow.org/images/hub_logo_32px.png">TF Hub 모델 보기</a></td>
</table>

이 노트북은 리뷰의 텍스트를 사용하여 영화 리뷰를 긍정적 또는 부정적으로 분류합니다. 이진( 또는 2-클래스 분류인 이 예는 광범위하게 적용할 수 있는 중요한 머신러닝 응용 사례입니다.
이 튜토리얼은 TensorFlow Hub 및 Keras를 사용한 전이 학습의 기본적인 응용을 보여줍니다.
여기서 사용하는 IMDB 데이터세트에는 인터넷 영화 데이터베이스에서 가져온 50,000개의 영화 리뷰 텍스트가 포함되어 있습니다. 훈련용 리뷰 25,000개와 테스트용 리뷰 25,000개로 나뉩니다. 훈련 및 테스트 세트는 균형을 이룹니다. 즉, 동일한 수의 긍정적인 리뷰와 부정적인 리뷰가 포함되어 있습니다.
이 노트북은 높은 수준의 API인 tf.keras를 사용하여 TensorFlow에서 모델을 빌드 및 훈련하고, 단일 코드 줄로 TFHub로부터 훈련된 모델을 로드하기 위한 라이브러리인 tensorflow_hub를 사용합니다. tf.keras를 사용한 고급 텍스트 분류 튜토리얼에 대해서는 MLCC 텍스트 분류 가이드를 참조하세요.
End of explanation
"""


# Split the training set into 60% and 40% to end up with 15,000 examples
# for training, 10,000 examples for validation and 25,000 examples for testing.
train_data, validation_data, test_data = tfds.load(
    name="imdb_reviews", 
    split=('train[:60%]', 'train[60%:]', 'test'),
    as_supervised=True)

"""
Explanation: IMDB 데이터셋 다운로드
IMDB 데이터셋은 imdb reviews 또는 텐서플로 데이터셋(TensorFlow datasets)에 포함되어 있습니다. 다음 코드는 IMDB 데이터셋을 컴퓨터(또는 코랩 런타임)에 다운로드합니다:
End of explanation
"""


train_examples_batch, train_labels_batch = next(iter(train_data.batch(10)))
train_examples_batch

"""
Explanation: 데이터 탐색
잠시 데이터 형태를 알아 보죠. 이 데이터셋의 샘플은 전처리된 정수 배열입니다. 이 정수는 영화 리뷰에 나오는 단어를 나타냅니다. 레이블(label)은 정수 0 또는 1입니다. 0은 부정적인 리뷰이고 1은 긍정적인 리뷰입니다.
처음 10개의 샘플을 출력해 보겠습니다.
End of explanation
"""


train_labels_batch

"""
Explanation: 처음 10개의 레이블도 출력해 보겠습니다.
End of explanation
"""


embedding = "https://tfhub.dev/google/nnlm-en-dim50/2"
hub_layer = hub.KerasLayer(embedding, input_shape=[], 
                           dtype=tf.string, trainable=True)
hub_layer(train_examples_batch[:3])

"""
Explanation: 모델 구성
신경망은 층을 쌓아서 만듭니다. 여기에는 세 개의 중요한 구조적 결정이 필요합니다:

어떻게 텍스트를 표현할 것인가?
모델에서 얼마나 많은 층을 사용할 것인가?
각 층에서 얼마나 많은 은닉 유닛(hidden unit)을 사용할 것인가?

이 예제의 입력 데이터는 문장으로 구성됩니다. 예측할 레이블은 0 또는 1입니다.
텍스트를 표현하는 한 가지 방법은 문장을 임베딩 벡터로 변환하는 것입니다. 사전 훈련 된 텍스트 임베딩을 첫 번째 레이어로 사용할 수 있으며, 두 가지 이점이 있습니다.

텍스트 전처리에 대해 걱정할 필요가 없습니다.
전이 학습에 따른 이점이 있습니다.
임베딩은 고정 크기이기 때문에 처리 과정이 단순해집니다.

이 예에서는 google/nnlm-en-dim50/2라고 하는 TensorFlow Hub에서 사전 훈련된 텍스트 임베딩 모델을 사용합니다.
이 튜토리얼에서 사용할 수 있는 TFHub의 다른 많은 사전 훈련된 텍스트 임베딩이 있습니다.

google/nnlm-en-dim128/2 - google/nnlm-en-dim50/2와 동일한 데이터에 동일한 NNLM 아키텍처로 훈련하지만 임베딩 차원이 더 큽니다. 보다 큰 차원의 임베딩은 작업을 개선할 수 있지만 모델을 훈련하는 데 더 오래 걸릴 수 있습니다.
google/nnlm-en-dim128-with-normalization/2 - google/nnlm-en-dim128/2와 동일하지만 구두점 제거와 같은 추가적인 텍스트 정규화가 있습니다. 이는 작업의 텍스트에 추가 문자나 구두점이 포함된 경우 도움이 될 수 있습니다.
google/universal-sentence-encoder/4 - DAN(deep averaging network) 인코더로 훈련된 512 차원 임베딩을 생성하는 훨씬 더 큰 모델입니다.

그 밖에도 많이 있습니다! TFHub에서 더 많은 텍스트 임베딩 모델을 찾아보세요.
먼저 문장을 임베딩시키기 위해 텐서플로 허브 모델을 사용하는 케라스 층을 만들어 보죠. 그다음 몇 개의 샘플을 입력하여 테스트해 보겠습니다. 입력 텍스트의 길이에 상관없이 임베딩의 출력 크기는 (num_examples, embedding_dimension)가 됩니다.
End of explanation
"""


model = tf.keras.Sequential()
model.add(hub_layer)
model.add(tf.keras.layers.Dense(16, activation='relu'))
model.add(tf.keras.layers.Dense(1))

model.summary()

"""
Explanation: 이제 전체 모델을 만들어 보겠습니다:
End of explanation
"""


model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

"""
Explanation: 순서대로 층을 쌓아 분류기를 만듭니다:

첫 번째 레이어는 TensorFlow Hub 레이어입니다. 이 레이어는 사전 훈련된 저장된 모델을 사용하여 문장을 임베딩 벡터에 매핑합니다. 사용 중인 사전 훈련된 텍스트 임베딩 모델(google/nnlm-en-dim50/2)은 문장을 토큰으로 분할하고 각 토큰을 임베딩한 다음 임베딩을 결합합니다. 결과적인 차원은 (num_examples, embedding_dimension)입니다. 이 NNLM 모델의 경우에는 embedding_dimension은 50입니다.
이 고정 크기의 출력 벡터는 16개의 은닉 유닛(hidden unit)을 가진 완전 연결 층(Dense)으로 주입됩니다.
마지막 층은 하나의 출력 노드를 가진 완전 연결 층입니다. sigmoid 활성화 함수를 사용하므로 확률 또는 신뢰도 수준을 표현하는 0~1 사이의 실수가 출력됩니다.

이제 모델을 컴파일합니다.
손실 함수와 옵티마이저
모델에는 훈련을 위한 손실 함수와 옵티마이저가 필요합니다. 이진 분류 문제이고 모델이 로짓(선형 활성화가 있는 단일 단위 레이어)을 출력하므로 binary_crossentropy 손실 함수를 사용합니다.
다른 손실 함수를 선택할 수 없는 것은 아닙니다. 예를 들어 mean_squared_error를 선택할 수 있습니다. 하지만 일반적으로 binary_crossentropy가 확률을 다루는데 적합합니다. 이 함수는 확률 분포 간의 거리를 측정합니다. 여기에서는 정답인 타깃 분포와 예측 분포 사이의 거리입니다.
나중에 회귀 문제(예: 주택 가격 예측)를 살펴볼 때 평균 제곱 오차라고 하는 또 다른 손실 함수를 사용하는 방법을 살펴볼 것입니다.
이제 모델이 사용할 옵티마이저와 손실 함수를 설정해 보죠:
End of explanation
"""


history = model.fit(train_data.shuffle(10000).batch(512),
                    epochs=10,
                    validation_data=validation_data.batch(512),
                    verbose=1)

"""
Explanation: 모델 훈련
512개 샘플의 미니 배치에서 10개 epoch 동안 모델을 훈련합니다. 이 동작은 x_train 및 y_train 텐서의 모든 샘플에 대한 10회 반복에 해당합니다. 훈련하는 동안 검증 세트의 10,000개 샘플에서 모델의 손실과 정확도를 모니터링합니다.
End of explanation
"""


results = model.evaluate(test_data.batch(512), verbose=2)

for name, value in zip(model.metrics_names, results):
  print("%s: %.3f" % (name, value))

"""
Explanation: 모델 평가
모델의 성능을 확인해 보죠. 두 개의 값이 반환됩니다. 손실(오차를 나타내는 숫자이므로 낮을수록 좋습니다)과 정확도입니다.
End of explanation
"""


