本文正在参加「金石计划 . 瓜分6万现金大奖」
前言
本文主要是用 cpu 版本的 tensorflow 2.1 搭建深度学习模型,完成对电影评论的情感分类任务。 本次实践的数据来源于IMDB 数据集,里面的包含的是电影的影评,每条影评评论文本分为积极类型或消极类型。数据集总共包含 50000 条影评文本,取该数据集的 25000 条影评数据作为训练集,另外 25000 条作为测试集,训练集与测试集中包含相等数量的积极和消极影评,保证数据质量的平衡。
本文大纲
- 下载影评数据并进行 padding 处理
- 创建验证集数据
- 搭建简单的深度学习模型
- 配置并编译模型
- 训练模型
- 评估模型
实现过程和思路解析
1. 下载影评数据并进行 padding 处理
(1)这里主要是通过 tf 内置的接口从网络上下载 imdb 数据,该数据是已经经过处理的,每一条评论中的每个单词都是用对应的整数表示的,所以每一条评论都是一个整数列表。而对应的每条影评的标签是一个 0 或 1 的整数, 0 代表的是消极评论,1 代表的是积极评论。
(2)这里的 num_words=6000 表示我们只需要保留了训练数据中出现频率最高的 6000 个常用单词,低频词会被全部弃用,因为一方面这些低频词可能是些符号或者停用词,对影评情感分类没有意义,另一方面如果低频词太多,需要构造的词典太过庞大,严重影响计算效率,且消耗大量内存。
import tensorflow as tf
from tensorflow import keras
imdb = keras.datasets.imdb
num_words = 6000
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=num_words)
(3)这里主要是建立整数与单词之间的映射关系,并建立了一个函数 decode_review 可以将影评数据从数字序列转换回单词序列。
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
index_word = dict([(value, key) for (key, value) in word_index.items()])
def decode_review(text):
return ' '.join([index_word.get(i, '?') for i in text])
(4)这里主要是对影评数据进行填充,因为每条数据的长度(数字序列的长度)是不一致的,而要进行深度学习模型的输入必须要保证每条影评数据的长度相等,所以我们要进行填充操作,指定了最大的影评长度 maxlen 为 256 ,如果超过 256 则进行截断,如果不足 256 则用 0 (也就是<PAD>对应的整数)在末尾进行填充。这里的 maxlen 可以随意指定,但是其长度会影响模型的效果,需要在实践中不断调整折中。
train_data = keras.preprocessing.sequence.pad_sequences(train_data, value=word_index["<PAD>"], padding='post', maxlen=256)
val_data = keras.preprocessing.sequence.pad_sequences(val_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)
2. 创建验证集数据
这里主要是创建验证集,那么有了训练集为什么要创建验证集呢?首先我们要知道训练集数据是用来开发和调整模型的,而检查模型的准确率指标只能是用未见过的数据进行评测,所以我们从原始的训练数据中抽取出 10000 个样本来创建验证集,此时训练样本只有 15000 个,我们结合使用训练集和验证集共同调整和开发模型,当模型定型之后,最后需要使用一次测试集评估模型的实际使用效果即可。
val_data = train_data[:10000]
val_labels = train_labels[:10000]
train_data = train_data[10000:]
train_labels = train_labels[10000:]
3. 搭建简单的深度学习模型
(1)第一层是 Embedding 层,它将输入的每一个单词转换为一个 32 维的向量,所以每一个样本输入就从一维的 (sequence) 变成了二维的 (sequence, 32)
(2)第二层主要是使用了平均池化操作 GlobalAveragePooling1D , 此操作可以将每个样本的二维维度向量 (sequence, 32) 又压缩成一维向量 (32) ,上一步中的 32 维向量表示的是每个单词向量,但是这里的 32 维向量表示的是一个影评样本向量。
(3)第三层是一个通过 relu 激活函数将输入转换成输出为 16 维度向量的全连接层操作
(4)第四层是一个通过 sigmoid 激活函数将输入转换成输出为 1 维度向量的全连接层操作,也就是最后输出一个介于 0 与 1 之间的浮点数,它表示了概率,常规情况下如果概率大于等于 0.5 则说明该样本预测类别为 1 ,否则说明该样本预测类别为 0
model = keras.Sequential()
model.add(keras.layers.Embedding(num_words, 32))
model.add(keras.layers.GlobalAveragePooling1D())
model.add(keras.layers.Dense(16 , activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid'))
model.summary()
4. 配置并编译模型
(1)我们选择了常用的 Adam 优化器,你也可以自行选择其他优化器。
(2)因为这是一个二分类问题且模型会输出一个概率值,我们选择了常见的 binary_crossentropy 损失函数。
(3)评估指标我们选择了最容易理解的准确率 accuracy 。
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
5. 训练模型
这里主要是使用训练数据进行模型的训练,使用训练集反复进行 40 次模型训练,并且在训练过程中,使用验证集的 10000 个样本来评估模型上的损失值(loss)和准确率(accuracy)。
model.fit(train_data, train_labels, epochs=40, batch_size=512, validation_data=(val_data, val_labels), verbose=1)
输出如下所示:
Train on 15000 samples, validate on 10000 samples
Epoch 1/40
15000/15000 [==============================] - 3s 169us/sample - loss: 0.6913 - accuracy: 0.5465 - val_loss: 0.6881 - val_accuracy: 0.6872
Epoch 2/40
15000/15000 [==============================] - 1s 88us/sample - loss: 0.6815 - accuracy: 0.7043 - val_loss: 0.6732 - val_accuracy: 0.7427
Epoch 3/40
...
Epoch 38/40
15000/15000 [==============================] - 1s 79us/sample - loss: 0.1045 - accuracy: 0.9707 - val_loss: 0.3431 - val_accuracy: 0.8738
Epoch 39/40
15000/15000 [==============================] - 1s 76us/sample - loss: 0.1022 - accuracy: 0.9712 - val_loss: 0.3470 - val_accuracy: 0.8737
Epoch 40/40
15000/15000 [==============================] - 1s 77us/sample - loss: 0.0985 - accuracy: 0.9735 - val_loss: 0.3526 - val_accuracy: 0.8725
6. 评估模型
这里主要是使用测试数据对模型进行评估,由于当前模型较为简单,如果使用复杂的模型和更好的训练方法,准确率应该能达到 98% 以上.
model.evaluate(test_data, test_labels, verbose=2)
结果输出为,损失为 0.368 ,准确率为 0.86488 :
[0.368153291721344, 0.86488]