TensorFlow 2之迁移学习进行IMDB评论文本分类

  |   0 评论   |   0 浏览

背景

使用迁移学习,来进行IMDB评论文本的二元分类。

简介

数据集

http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz

初体验

import numpy as np

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

# 第一步 准备数据集
#   IMDB数据集样本,其包含 50,000 条影评文本。
#   从该数据集切割出的 25,000 条评论用作训练,另外 25,000 条用作测试。
#   训练集与测试集是平衡的(balanced),意味着它们包含相等数量的积极和消极评论。
#   将训练集分割成 60% 和 40%,最终有 15,000 个训练样本, 10,000 个验证样本和 25,000 个测试样本。

#   下载数据集时,可以自行配置使用代理:
# export TFDS_HTTP_PROXY="http://127.0.0.1:8118/"
# export TFDS_HTTPS_PROXY="http://127.0.0.1:8118/"
# export HTTP_PROXY="http://127.0.0.1:8118/"
# export HTTPS_PROXY="http://127.0.0.1:8118/"

train_data, validation_data, test_data = tfds.load(
    name="imdb_reviews",
    split=('train[:60%]', 'train[60%:]', 'test'),
    as_supervised=True,
    try_gcs=False)

#   预览数据集
#   打印前10个样本
train_examples_batch, train_labels_batch = next(iter(train_data.batch(10)))
# print(train_examples_batch)

#   打印前10个标签
# print(train_labels_batch)
# tf.Tensor([0 0 0 1 1 1 0 0 0 0], shape=(10,), dtype=int64)

# 第二步 搭建模型
#   表示文本的一种方式是将句子转换为嵌入向量(embeddings vectors)。我们可以使用一个预先训练好的文本嵌入(text embedding)作为首层,这将具有三个优点:
#   * 我们不必担心文本预处理
#   * 我们可以从迁移学习中受益
#   * 嵌入具有固定长度,更易于处理
#   针对此示例我们将使用 TensorFlow Hub 中名为 google/tf2-preview/gnews-swivel-20dim/1 的一种预训练文本嵌入(text embedding)模型 。

# embedding = "https://hub.tensorflow.google.cn/google/tf2-preview/gnews-swivel-20dim/1"
embedding = "datasets/gnews-swivel-20dim/"
hub_layer = hub.KerasLayer(embedding, input_shape=[],
                           dtype=tf.string, trainable=True)
# 预览hub层结构
# print(hub_layer(train_examples_batch[:3]))
# tf.Tensor(
# [[ 1.765786   -3.882232    3.9134233  -1.5557289  -3.3362343  -1.7357955
#   -1.9954445   1.2989551   5.081598   -1.1041286  -2.0503852  -0.72675157
#   -0.65675956  0.24436149 -3.7208383   2.0954835   2.2969332  -2.0689783
#   -2.9489717  -1.1315987 ]
#  [ 1.8804485  -2.5852382   3.4066997   1.0982676  -4.056685   -4.891284
#   -2.785554    1.3874227   3.8476458  -0.9256538  -1.896706    1.2113281
#    0.11474707  0.76209456 -4.8791065   2.906149    4.7087674  -2.3652055
#   -3.5015898  -1.6390051 ]
#  [ 0.71152234 -0.6353217   1.7385626  -1.1168286  -0.5451594  -1.1808156
#    0.09504455  1.4653089   0.66059524  0.79308075 -2.2268345   0.07446612
#   -1.4075904  -0.70645386 -1.907037    1.4419787   1.9551861  -0.42660055
#   -2.8022065   0.43727064]], shape=(3, 20), dtype=float32)

#   完整模型如下
#     1. 第一层是 Tensorflow Hub 层。这一层使用一个预训练的保存好的模型来将句子映射为嵌入向量(embedding vector)。
#        我们所使用的预训练文本嵌入(embedding)模型(google/tf2-preview/gnews-swivel-20dim/1)将句子切割为符号,嵌入(embed)每个符号然后进行合并。
#        最终得到的维度是:(num_examples, embedding_dimension)。
#     2. 该定长输出向量通过一个有 16 个隐层单元的全连接层(Dense)进行管道传输。
#     3. 最后一层与单个输出结点紧密相连。使用 Sigmoid 激活函数,其函数值为介于 0 与 1 之间的浮点数,表示概率或置信水平。
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()
# Model: "sequential"
# _________________________________________________________________
# 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
# _________________________________________________________________

# 第三步 编译模型
#   二分类问题且模型输出概率值(一个使用 sigmoid 激活函数的单一单元层),我们将使用 binary_crossentropy 损失函数。
#   这不是损失函数的唯一选择,例如,您可以选择 mean_squared_error 。
#   但是,一般来说 binary_crossentropy 更适合处理概率——它能够度量概率分布之间的“距离”,或者在我们的示例中,指的是度量 ground-truth 分布与预测值之间的“距离”。
model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

# 第四步 训练模型
history = model.fit(train_data.shuffle(10000).batch(512),
                    epochs=20,
                    validation_data=validation_data.batch(512),
                    verbose=1)

# 第五步 评估准确率
results = model.evaluate(test_data.batch(512), verbose=2)

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

# 49/49 - 1s - loss: 0.3182 - accuracy: 0.8580
# loss: 0.318
# accuracy: 0.858
# 这种十分朴素的方法得到了约 87% 的准确率(accuracy)。若采用更好的方法,模型的准确率应当接近 95%。

参考