TensorFlow实操之--电影评论文本分类

问题描述

从电影的影评中获取对电影的评价是一件有意义的事情。电影的影评一般分为正面(positive)或负面(nagetive)两类。这是一个二元(binary)或者二分类问题,一种重要且应用广泛的机器学习问题。在网络电影数据库(Internet Movie Database)的 IMDB 数据集(IMDB dataset)中包含了影评文本和标签,如何从这些数据中获取电影评价模型是问题的核心。IMDB 数据集包含 50,000 条影评文本。从该数据集切割出的25,000条评论用作训练,另外 25,000 条用作测试。训练集与测试集是平衡的(balanced),意味着它们包含相等数量的积极和消极评论。

解决思路

思路还是一般神经网络的步骤,那就是

  1. 获取数据,包括训练数据和测试数据。在有些情况下还需要对数据进行处理,即数据处理。
  2. 模型建立
  3. 训练
  4. 模型验证

代码实现

1. 数据获取与处理
在获取数据之前,首先要配置一下环境。

from __future__ import absolute_import, division, print_function, unicode_literals#该行要放在第一行位置
import warnings#忽略系统警告提示
warnings.filterwarnings('ignore')
import tensorflow as tf
from tensorflow import keras

基本配置设定完毕后,可以下载数据集。IMDB 数据集已经打包在 Tensorflow 中。该数据集已经经过预处理,评论(单词序列)已经被转换为整数序列,其中每个整数表示字典中的特定单词。具体代码如下:

imdb = keras.datasets.imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
#参数 num_words=10000 保留了训练数据中最常出现的 10,000 个单词。为了保持数据规模的可管理性,低频词将被丢弃。

该数据集是经过预处理的:每个样本都是一个表示影评中词汇的整数数组。每个标签都是一个值为 0 或 1 的整数值,其中 0 代表消极评论,1 代表积极评论。评论文本被转换为整数值,其中每个整数代表词典中的一个单词。
既然文本能转换为整数值,那么整数值一定可以转换回去文本,操作方法如下:

#一个映射单词到整数索引的词典
word_index = imdb.get_word_index()#建立词典索引
#保留第一个索引
word_index = {k:(v+3) for k,v in word_index.items()}
word_index["<PAD>"] = 0#这里0代表<PAD>
word_index["<START>"] = 1#这里1代表<START>
word_index["<UNK>"] = 2#这里2代表<UNK>(unknown)
word_index["<UNUSED>"] = 3#这里3代表<UNUSED>

print(word_index)
print(train_data[1])
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])

由于神经网络的输入必须是张量形式,因此影评需要首先转换为张量,然后才可以进行学习,转换的方式有两种:
1、 将数组转换为表示单词出现与否的由 0 和 1 组成的向量,类似于 one-hot 编码。例如,序列[3, 5]将转换为一个 10,000 维的向量,该向量除了索引为 3 和 5 的位置是 1 以外,其他都为 0。然后,将其作为网络的首层——一个可以处理浮点型向量数据的稠密层。不过,这种方法需要大量的内存,需要一个大小为 num_words * num_reviews 的矩阵。
2、 我们可以填充数组来保证输入数据具有相同的长度,然后创建一个大小为 max_length * num_reviews 的整型张量。我们可以使用能够处理此形状数据的嵌入层作为网络中的第一层。

在本示例中,我们将使用第二种方法。
由于电影评论长度必须相同,我们将使用 pad_sequences 函数来使长度标准化:

train_data = keras.preprocessing.sequence.pad_sequences(train_data,
                                                        value=word_index["<PAD>"],
                                                        padding='post',
                                                         maxlen=256)
#测试数据长度设置为256
test_data = keras.preprocessing.sequence.pad_sequences(test_data,
                                                       value=word_index["<PAD>"],
                                                       padding='post',
                                                        maxlen=256)

2. 模型建立
通过keras的Sequential创建模型,并添加神经网络的层次

vocab_size = 10000
model = keras.Sequential()#搭建层
model.add(keras.layers.Embedding(vocab_size, 16))#embedding 是一个将单词向量化的函数,嵌入(embeddings)输出的形状都是:(num_examples, embedding_dimension)
model.add(keras.layers.GlobalAveragePooling1D())#添加全局平均池化层
model.add(keras.layers.Dense(16, activation = 'relu'))
model.add(keras.layers.Dense(1, activation = 'sigmoid'))

model.summary()

可以看到模型的结构如下图:

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

第一层是嵌入(Embedding)层。该层采用整数编码的词汇表,并查找每个词索引的嵌入向量(embedding vector)。这些向量是通过模型训练学习到的。向量向输出数组增加了一个维度。得到的维度为:(batch, sequence, embedding)。
embedding 是一个将单词向量化的函数,嵌入(embeddings)输出的形状都是:(num_examples, embedding_dimension)

接下来,GlobalAveragePooling1D 将通过对序列维度求平均值来为每个样本返回一个定长输出向量。这允许模型以尽可能最简单的方式处理变长输入。

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

最后一层与单个输出结点密集连接。使用 Sigmoid 激活函数,其函数值为介于 0 与 1 之间的浮点数,表示概率或置信度。

3. 模型配置,训练和验证

model.compile(optimizer = 'adam',
              loss = 'binary_crossentropy',
              metrics = ['accuracy'])


x_val = train_data[:10000]#取训练数据集前10000个进行训练和验证
partial_x_train = train_data[10000:]
y_val = train_labels[:10000]#同理取前10000个标签
partial_y_train = train_labels[10000:]
#以 512 个样本的 mini-batch 大小迭代 40 个 epoch 来训练模型。这是指对 x_train 和 y_train 张量中所有样本的的 40 次迭代。在训练过程中,监测来自验证集的 10,000 个样本上的损失值(loss)和准确率(accuracy):
history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=15,
                    batch_size=512,
                    validation_data=(x_val, y_val),
                    verbose=1)


results = model.evaluate(test_data,  test_labels, verbose=2)
print(result)

我们来看一下模型的性能如何。将返回两个值。损失值(loss)(一个表示误差的数字,值越低越好)与准确率(accuracy)。可以看到准确率为86%左右

[0.3407367579460144, 0.86448]

总结

从影评分类的例子可以看出,我们花在文本处理上的笔墨非常多,在网络模型建立方面,尤其是在引进使用keras之后,花费的篇幅相对较少。由此可以看到,数据处理在整个项目中的分量。如果在有了网络模型(事实上,由于开源,网络模型已经是有了,而且并且不断更新创新),那么数据将是关键(当你的重点不是网络模型创新的学术研究,而是工程应用的话)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章