TensorFlow 2之IMDB评论文本分类

  |   0 评论   |   0 浏览

背景

对 IMDB上的评论数据,进行积极和消极的情感二分类。

简介

数据集

IMDB情感分类的数据集,包含提供了25,000套电影评论供训练,25,000套则供测试。

https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz

初体验

import tensorflow as tf
from tensorflow import keras

import numpy as np

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

#   离线数据集
#     下载:https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
#     参数 num_words=10000 保留了训练数据中最常出现的 10,000 个单词。为了保持数据规模的可管理性,低频词将被丢弃。

imdb = keras.datasets.imdb
(train_data, train_labels), (test_data,
                             test_labels) = imdb.load_data(num_words=10000)

#  了解数据格式
#    1. 训练数据包括25000个样本
# print("Training entries: {}, labels: {}".format(
#     len(train_data), len(train_labels)))
# Training entries: 25000, labels: 25000
#
#    2. 每个标签都是一个值为 0 或 1 的整数值,其中 0 代表消极评论,1 代表积极评论。
# print(train_labels[:30])
# [1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 1 0 1 0 0 1 0 1 1 0 0 1 0]
#
#    3. 评论文本被转换为整数值,其中每个整数代表词典中的一个单词。第一条评论中前30个单词如下:
# print(train_data[0])
# [1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480]
#
#    4. 每条评论的长度也不一定是相同的,如前两条评论的长度为:218, 189
# print("{}, {}".format(len(train_data[0]), len(train_data[1])))
# 218, 189
#
#    5. 将文本中的数字转换为单词
# 一个映射单词到整数索引的词典
word_index = imdb.get_word_index()

# 保留第一个索引
word_index = {k: (v+3) for k, v in word_index.items()}
word_index["<PAD>"] = 0
word_index["<START>"] = 1
word_index["<UNK>"] = 2  # unknown
word_index["<UNUSED>"] = 3

reverse_word_index = dict([(value, key)
                           for (key, value) in word_index.items()])


def decode_review(text):
    return ' '.join([reverse_word_index.get(i, '?') for i in text])


# print(decode_review(train_data[0]))
# <START> this film was just brilliant casting location scenery story

# 第二步 预处理
#   了解完数据格式后,开始准备数据
#   由于电影评论长度必须相同,我们将使用 pad_sequences 函数来使长度标准化:
train_data = keras.preprocessing.sequence.pad_sequences(train_data,
                                                        value=word_index["<PAD>"],
                                                        padding='post',
                                                        maxlen=256)

test_data = keras.preprocessing.sequence.pad_sequences(test_data,
                                                       value=word_index["<PAD>"],
                                                       padding='post',
                                                       maxlen=256)

# print("train data len: {}, {}".format(len(train_data[0]), len(train_data[1])))
# print(train_data[0])


# 第三步 搭建模型
#   神经网络由堆叠的层来构建,这需要从两个主要方面来进行体系结构决策:
#   模型里有多少层?
#   每个层里有多少隐层单元(hidden units)?
#   输入形状是用于电影评论的词汇数目(10,000 词)
#   第一层是嵌入(Embedding)层。该层采用整数编码的词汇表,并查找每个词索引的嵌入向量(embedding vector)。
#     这些向量是通过模型训练学习到的。向量向输出数组增加了一个维度。得到的维度为:(batch, sequence, embedding)。
#   接下来,GlobalAveragePooling1D 将通过对序列维度求平均值来为每个样本返回一个定长输出向量。这允许模型以尽可能最简单的方式处理变长输入。
#   该定长输出向量通过一个有 16 个隐层单元的全连接(Dense)层传输。
#   最后一层与单个输出结点密集连接。使用 Sigmoid 激活函数,其函数值为介于 0 与 1 之间的浮点数,表示概率或置信度。
vocab_size = 10000

model = keras.Sequential()
model.add(keras.layers.Embedding(vocab_size, 16))
model.add(keras.layers.GlobalAveragePooling1D())
model.add(keras.layers.Dense(16, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid'))

# model.summary()
# Model: "sequential"
# _________________________________________________________________
# Layer (type)                 Output Shape              Param #
# =================================================================
# embedding (Embedding)        (None, None, 16)          160000
# _________________________________________________________________
# global_average_pooling1d (Gl (None, 16)                0
# _________________________________________________________________
# dense (Dense)                (None, 16)                272
# _________________________________________________________________
# dense_1 (Dense)              (None, 1)                 17
# =================================================================
# Total params: 160,289
# Trainable params: 160,289
# Non-trainable params: 0
# _________________________________________________________________

# 第四步 编译模型
#   模型准备训练前,在模型编译(Compile)时还需要设置一些参数
#   Loss function - 损失函数,训练时评估模型的正确率,希望最小化这个函数,往正确的方向训练模型。
#     由于这是一个二分类问题且模型输出概率值(一个使用 sigmoid 激活函数的单一单元层),我们将使用 binary_crossentropy 损失函数。
#   Optimizer - 优化器算法,更新模型参数的算法。
#   Metrics - 指标,用来监视训练和测试步数,下面的例子中使用accuracy,即图片被正确分类的比例。
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

# 第五步 训练模型
#   训练神经网络,通常有以下几个步骤。
#   创建一个验证集。通过从原始训练数据中分离 10,000 个样本来创建一个验证集。(
#   为什么现在不使用测试集?我们的目标是只使用训练数据来开发和调整模型,然后只使用一次测试数据来评估准确率(accuracy))。
x_val = train_data[:10000]
partial_x_train = train_data[10000:]

y_val = train_labels[:10000]
partial_y_train = train_labels[10000:]
#    训练模型
#    验证过程的损失值(loss)与准确率(accuracy)的情况却并非如此——它们似乎在 20 个 epoch 后达到峰值。
#    这是过拟合的一个实例:模型在训练数据上的表现比在以前从未见过的数据上的表现要更好。
#    在此之后,模型过度优化并学习特定于训练数据的表示,而不能够泛化到测试数据。
#    图形见 https://www.tensorflow.org/tutorials/keras/text_classification?hl=zh-cn
history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=40,
                    batch_size=512,
                    validation_data=(x_val, y_val),
                    verbose=1)

# 第六步 评估准确率
#   看看在测试集中表现如何?
test_loss, test_acc = model.evaluate(test_data,  test_labels, verbose=2)
print('\nTest accuracy:', test_acc)
# Test accuracy: 0.8706799745559692

参考

安装包

pip3 install tensorflow_datasets