<jupyter_start><jupyter_text>使用 Keras 和 Tensorflow Hub 对电影评论进行文本分类 此笔记本（notebook）使用评论文本将影评分为*积极（positive）*或*消极（nagetive）*两类。这是一个*二元（binary）*或者二分类问题，一种重要且应用广泛的机器学习问题。本教程演示了使用 Tensorflow Hub 和 Keras 进行迁移学习的基本应用。我们将使用来源于[网络电影数据库（Internet Movie Database）](https://www.imdb.com/)的 [IMDB 数据集（IMDB dataset）](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/imdb)，其包含 50,000 条影评文本。从该数据集切割出的 25,000 条评论用作训练，另外 25,000 条用作测试。训练集与测试集是*平衡的（balanced）*，意味着它们包含相等数量的积极和消极评论。此笔记本（notebook）使用了 [tf.keras](https://www.tensorflow.org/guide/keras)，它是一个 Tensorflow 中用于构建和训练模型的高级API，此外还使用了 [TensorFlow Hub](https://www.tensorflow.org/hub)，一个用于迁移学习的库和平台。有关使用 `tf.keras` 进行文本分类的更高级教程，请参阅 [MLCC文本分类指南（MLCC Text Classification Guide）](https://developers.google.com/machine-learning/guides/text-classification/)。<jupyter_code>from __future__ import absolute_import, division, print_function, unicode_literals

import numpy as np

try:
  # Colab only
  %tensorflow_version 2.x
except Exception:
    pass
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.experimental.list_physical_devices("GPU") else "NOT AVAILABLE")<jupyter_output>Version:  2.0.0
Eager mode:  True
Hub version:  0.6.0
GPU is NOT AVAILABLE<jupyter_text>下载 IMDB 数据集IMDB数据集可以在 [Tensorflow 数据集](https://github.com/tensorflow/datasets)处获取。以下代码将 IMDB 数据集下载至您的机器（或 colab 运行时环境）中：<jupyter_code># 将训练集按照 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)<jupyter_output><empty_output><jupyter_text>探索数据让我们花一点时间来了解数据的格式。每一个样本都是一个表示电影评论和相应标签的句子。该句子不以任何方式进行预处理。标签是一个值为 0 或 1 的整数，其中 0 代表消极评论，1 代表积极评论。我们来打印下前十个样本。<jupyter_code>train_examples_batch, train_labels_batch = next(iter(train_data.batch(10)))
train_examples_batch<jupyter_output><empty_output><jupyter_text>我们再打印下前十个标签。<jupyter_code>train_labels_batch<jupyter_output><empty_output><jupyter_text>构建模型神经网络由堆叠的层来构建，这需要从三个主要方面来进行体系结构决策：* 如何表示文本？* 模型里有多少层？* 每个层里有多少*隐层单元（hidden units）*？本示例中，输入数据由句子组成。预测的标签为 0 或 1。表示文本的一种方式是将句子转换为嵌入向量（embeddings vectors）。我们可以使用一个预先训练好的文本嵌入（text embedding）作为首层，这将具有三个优点：* 我们不必担心文本预处理* 我们可以从迁移学习中受益* 嵌入具有固定长度，更易于处理针对此示例我们将使用 [TensorFlow Hub](https://www.tensorflow.org/hub) 中名为 [google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1) 的一种**预训练文本嵌入（text embedding）模型** 。为了达到本教程的目的还有其他三种预训练模型可供测试：* [google/tf2-preview/gnews-swivel-20dim-with-oov/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim-with-oov/1) ——类似 [google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1)，但 2.5%的词汇转换为未登录词桶（OOV buckets）。如果任务的词汇与模型的词汇没有完全重叠，这将会有所帮助。* [google/tf2-preview/nnlm-en-dim50/1](https://tfhub.dev/google/tf2-preview/nnlm-en-dim50/1) ——一个拥有约 1M 词汇量且维度为 50 的更大的模型。* [google/tf2-preview/nnlm-en-dim128/1](https://tfhub.dev/google/tf2-preview/nnlm-en-dim128/1) ——拥有约 1M 词汇量且维度为128的更大的模型。 让我们首先创建一个使用 Tensorflow Hub 模型嵌入（embed）语句的Keras层，并在几个输入样本中进行尝试。请注意无论输入文本的长度如何，嵌入（embeddings）输出的形状都是：`(num_examples, embedding_dimension)`。<jupyter_code>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])<jupyter_output><empty_output><jupyter_text>现在让我们构建完整模型：<jupyter_code>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()<jupyter_output>Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
keras_layer (KerasLayer)     (None, 20)                400020    
_________________________________________________________________
dense (Dense)                (None, 16)                336       
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 17        
=================================================================
Total params: 400,373
Trainable params: 400,373
Non-trainable params: 0
_________________________________________________________________<jupyter_text>层按顺序堆叠以构建分类器：1. 第一层是 Tensorflow Hub 层。这一层使用一个预训练的保存好的模型来将句子映射为嵌入向量（embedding vector）。我们所使用的预训练文本嵌入（embedding）模型([google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1))将句子切割为符号，嵌入（embed）每个符号然后进行合并。最终得到的维度是：`(num_examples, embedding_dimension)`。2. 该定长输出向量通过一个有 16 个隐层单元的全连接层（`Dense`）进行管道传输。3. 最后一层与单个输出结点紧密相连。使用 `Sigmoid` 激活函数，其函数值为介于 0 与 1 之间的浮点数，表示概率或置信水平。让我们编译模型。  损失函数与优化器一个模型需要损失函数和优化器来进行训练。由于这是一个二分类问题且模型输出概率值（一个使用 sigmoid 激活函数的单一单元层），我们将使用 `binary_crossentropy` 损失函数。这不是损失函数的唯一选择，例如，您可以选择 `mean_squared_error` 。但是，一般来说 `binary_crossentropy` 更适合处理概率——它能够度量概率分布之间的“距离”，或者在我们的示例中，指的是度量 ground-truth 分布与预测值之间的“距离”。稍后，当我们研究回归问题（例如，预测房价）时，我们将介绍如何使用另一种叫做均方误差的损失函数。现在，配置模型来使用优化器和损失函数：<jupyter_code>model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])<jupyter_output><empty_output><jupyter_text>训练模型以 512 个样本的 mini-batch 大小迭代 20 个 epoch 来训练模型。 这是指对 `x_train` 和 `y_train` 张量中所有样本的的 20 次迭代。在训练过程中，监测来自验证集的 10,000 个样本上的损失值（loss）和准确率（accuracy）：<jupyter_code>history = model.fit(train_data.shuffle(10000).batch(512),
                    epochs=20,
                    validation_data=validation_data.batch(512),
                    verbose=1)<jupyter_output>Epoch 1/20
30/30 [==============================].7508 - accuracy: 0.49 - 1s 658ms/step - loss: 2.7317 - accuracy: 0.492 - 1s 467ms/step - loss: 2.7024 - accuracy: 0.490 - 1s 372ms/step - loss: 2.5895 - accuracy: 0.500 - 2s 314ms/step - loss: 2.5624 - accuracy: 0.497 - 2s 274ms/step - loss: 2.5335 - accuracy: 0.494 - 2s 246ms/step - loss: 2.4715 - accuracy: 0.496 - 2s 224ms/step - loss: 2.4200 - accuracy: 0.495 - 2s 208ms/step - loss: 2.3599 - accuracy: 0.496 - 2s 193ms/step - loss: 2.3031 - accuracy: 0.498 - 2s 181ms/step - loss: 2.2501 - accuracy: 0.499 - 2s 171ms/step - loss: 2.1951 - accuracy: 0.500 - 2s 162ms/step - loss: 2.1548 - accuracy: 0.497 - 2s 155ms/step - loss: 2.1081 - accuracy: 0.495 - 2s 149ms/step - loss: 2.0619 - accuracy: 0.495 - 2s 143ms/step - loss: 2.0159 - accuracy: 0.494 - 2s 138ms/step - loss: 1.9673 - accuracy: 0.495 - 2s 134ms/step - loss: 1.9247 - accuracy: 0.494 - 2s 130ms/step - loss: 1.8792 - accuracy: 0.494 - 3s 126ms/step - loss: 1.8344 - accuracy: 0.4[...]<jupyter_text>评估模型我们来看下模型的表现如何。将返回两个值。损失值（loss）（一个表示误差的数字，值越低越好）与准确率（accuracy）。<jupyter_code>results = model.evaluate(test_data.batch(512), verbose=2)
for name, value in zip(model.metrics_names, results):
  print("%s: %.3f" % (name, value))<jupyter_output>49/49 - 3s - loss: 0.3112 - accuracy: 0.8682
loss: 0.311
accuracy: 0.868
{'fraction_non_alphanumeric': 0.15110803324099722, 'fraction_numerical': 0.06468144044321329, 'mean_word_length': 5.582497721057429, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 33, 'https://': 14, 'lorem ipsum': 0, 'www.': 5, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '512444', 'license': 'Apache-2.0', 'n_tokens_mistral': 3790, 'n_tokens_neox': 3780, 'n_words': 403, 'path': 'chapter2/2.3.1-text_classification_with_hub.ipynb', 'repo_name': 'wangxingda/Tensorflow-Handbook', 'size': '7220'}