Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/zh-cn/hub/tutorials/tf2_text_classification.ipynb
25118 views
Kernel: Python 3

Licensed under the Apache License, Version 2.0 (the "License");

# Copyright 2019 The TensorFlow Hub Authors. All Rights Reserved. # # 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 # # http://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 # IGNORE_COPYRIGHT: cleared by OSS licensing # # 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.

电影评论文本分类

此笔记本利用评论文本将电影评论分类为正面负面评价。这是一个二元(或二类)分类示例,也是一个重要且应用广泛的机器学习问题。

我们将使用包含 Internet Movie Database 中的 50,000 条电影评论文本的 IMDB 数据集。先将这些评论分为两组,其中 25,000 条用于训练,另外 25,000 条用于测试。训练组和测试组是均衡的,也就是说其中包含相等数量的正面评价和负面评价。

此笔记本在 TensorFlow 和 TensorFlow Hub(一个用于迁移学习的库和平台)中使用高级 API tf.keras 来构建和训练模型。有关使用 tf.keras 的更高级文本分类教程,请参阅 MLCC 文本分类指南

更多模型

这里可以找到更具表现力或性能的模型,您可以使用这些模型来生成文本嵌入向量。

安装

import numpy as np import tensorflow as tf import tensorflow_hub as hub import tensorflow_datasets as tfds import matplotlib.pyplot as plt 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")

下载 IMDB 数据集

TensorFlow 数据集上提供了 IMDB 数据集。以下代码可将 IMDB 数据集下载到您的计算机(或 Colab 运行时)上:

train_data, test_data = tfds.load(name="imdb_reviews", split=["train", "test"], batch_size=-1, as_supervised=True) train_examples, train_labels = tfds.as_numpy(train_data) test_examples, test_labels = tfds.as_numpy(test_data)

探索数据

我们花一点时间来了解数据的格式。每个样本都是一个代表电影评论的句子和一个相应的标签。句子未经过任何预处理。标签是一个整数值(0 或 1),其中 0 表示负面评价,1 表示正面评价。

print("Training entries: {}, test entries: {}".format(len(train_examples), len(test_examples)))

我们打印前 10 个样本。

train_examples[:10]

我们再打印前 10 个标签。

train_labels[:10]

构建模型

神经网络是通过堆叠层创建的,这需要确定三个主要架构决策:

  • 如何表示文本?

  • 在模型中使用多少个层?

  • 为每个层使用多少个隐藏神经元

在本例中,输入数据由句子组成。要预测的标签要么是 0,要么是 1。

表示文本的一种方法是将句子转换为嵌入向量。我们可以将预训练的文本嵌入向量作为第一层,这有两个优势:

  • 不必担心文本预处理。

  • 可以从迁移学习获益。

对于本示例,我们将使用 TensorFlow Hub 中名为 google/nnlm-en-dim50/2 的模型。

为了学习本教程,还要测试另外两个模型:

我们先创建一个使用 TensorFlow Hub 模型嵌入语句的 Keras 层,并使用几个输入样本试试效果。请注意,产生的嵌入向量的输出形状是预期的:(num_examples, embedding_dimension)

model = "https://tfhub.dev/google/nnlm-en-dim50/2" hub_layer = hub.KerasLayer(model, input_shape=[], dtype=tf.string, trainable=True) hub_layer(train_examples[: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)) model.summary()

按顺序堆叠层以构建分类器:

  1. 第一层是 TensorFlow Hub 层。该层使用预训练的 SaveModel 将句子映射到其嵌入向量。我们使用的模型 (google/nnlm-en-dim50/2) 可将句子拆分为词例,嵌入每个词例,然后组合嵌入向量。生成的维度是:(num_examples, embedding_dimension)

  2. 此定长输出向量通过一个有 16 个隐藏单元的全连接 (Dense) 层传输。

  3. 最后一层与单个输出节点密集连接。这会输出 logits:真类的对数几率(根据模型而定)。

隐藏神经元

上述模型在输入和输出之间有两个中间(或称“隐藏”)层。输出(单元、节点或神经元)的数量是层的表示空间的维度。换言之,即网络学习内部表示时允许的自由度。

模型的隐藏单元越多(更高维度的表示空间)和/或层越多,则网络可以学习的表示越复杂。但是,这会导致网络的计算开销增加,并且可能导致学习不需要的模式——提高在训练数据(而不是测试数据)上的性能的模式。这就叫过拟合,我们稍后将进行探讨。

损失函数和优化器

模型训练需要一个损失函数和一个优化器。由于这是二元分类问题,并且模型输出概率(具有 Sigmoid 激活的单一单元层),我们将使用 binary_crossentropy 损失函数。

这并非损失函数的唯一选择,比如,您还可以选择 mean_squared_error。但是,一般来说,binary_crossentropy 更适合处理概率问题,它可以测量概率分布之间的“距离”,或者在我们的用例中,是指真实分布与预测值之间的差距。

稍后我们研究回归问题时(比如说,预测一套房子的价格),我们将看到如何使用另一个称为均方误差的损失函数。

现在,配置模型以使用优化器和损失函数:

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

创建验证集

训练时,我们希望检验该模型在未见过的数据上的准确率。为此,需要将原始训练数据中的 10,000 个样本分离出来,创建一个验证集。(为何现在不使用测试集?因为我们的目标是仅使用训练数据开发和调整模型,然后只使用一次测试数据来评估准确率)。

x_val = train_examples[:10000] partial_x_train = train_examples[10000:] y_val = train_labels[:10000] partial_y_train = train_labels[10000:]

训练模型

使用包含 512 个样本的 mini-batch 对模型进行 40 个周期的训练,也就是在 x_trainy_train 张量中对所有样本进行 40 次迭代。在训练时,监测模型在验证集的 10,000 个样本上的损失和准确率:

history = model.fit(partial_x_train, partial_y_train, epochs=40, batch_size=512, validation_data=(x_val, y_val), verbose=1)

评估模型

我们来看一下模型的性能如何。将返回两个值。损失值(一个表示误差的数字,值越低越好)与准确率。

results = model.evaluate(test_examples, test_labels) print(results)

这种相当简单的方法可以达到 87% 的准确率。对于更高级的方法,模型的准确率应该会接近 95%。

创建准确率和损失随时间变化的图表

model.fit() 会返回包含一个字典的 History 对象。该字典包含训练过程中产生的所有信息:

history_dict = history.history history_dict.keys()

其中有四个条目:每个条目代表训练和验证过程中的一项监测指标。我们可以使用这些指标来绘制用于比较的训练和验证图表,以及训练和验证准确率图表:

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" is for "blue dot" plt.plot(epochs, loss, 'bo', label='Training loss') # b is for "solid blue line" 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() # clear figure 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()

在该图表中,虚线代表训练损失和准确率,实线代表验证损失和准确率。

请注意,训练损失会逐周期下降,而训练准确率则逐周期上升。使用梯度下降优化时,这是预期结果,它应该在每次迭代中最大限度减少所需的数量。

但是,对验证损失和准确率来说则不然——它们似乎会在经过 20 个周期后达到顶点。这是过拟合的一个例子:模型在训练数据上的表现要好于在之前从未见过的数据上的表现。经过这一点之后,模型会过度优化和学习特定于训练数据的表示,但无法泛化到测试数据。

对于这种特殊情况,我们只需在经过 20 个左右的周期后停止训练即可防止过拟合。稍后您将看到如何使用回调自动执行该操作。