深度学习实战案例:基于LSTM的四种方法进行电影评论情感分类预测(附完整代码)

序列分类是一个预测建模问题,你有一些输入序列,任务是预测序列的类别。

这个问题很困难,因为序列的长度可能不同,包含非常大的输入符号词汇表,并且可能需要模型学习输入序列中符号之间的长期上下文或依赖关系。

在本文中,你将了解如何使用 Keras 深度学习库在 Python 中为序列分类问题开发 LSTM 递归神经网络模型。
看完这篇文章,你会知道:

  • 如何为序列分类问题开发 LSTM 模型
  • 如何通过使用 dropout 减少 LSTM 模型中的过度拟合
  • 如何将 LSTM 模型与擅长学习空间关系的卷积神经网络相结合

技术提升

技术要学会分享、交流,不建议闭门造车。一个人走的很快、一堆人可以走的更远。

完整代码、数据、技术交流提升, 均可加入知识星球交流群获取,群友已超过2000人,添加时切记的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

方式①、添加微信号:pythoner666,备注:来自 CSDN + 电影评论
方式②、微信搜索公众号:Python学习与数据挖掘,后台回复:资料

问题描述

Keras 提供对 IMDB 数据集的内置访问。imdb.load_data () 函数允许你以可用于神经网络和深度学习模型的格式加载数据集。

这些词已被表示数据集中每个词的有序频率的整数所取代。因此,每条评论中的句子都由一系列整数组成。

词嵌入

你会将每个电影评论映射到一个真实的向量域,这是处理文本时的一种流行技术,称为词嵌入。这是一种将单词编码为高维空间中的实值向量的技术,其中单词之间在含义方面的相似性转化为向量空间中的接近度。

Keras 提供了一种方便的方法,可以通过嵌入层将单词的正整数表示转换为单词嵌入。

你会将每个单词映射到一个 32 长度的实值向量上。你还将对建模感兴趣的单词总数限制为 5000 个最常见的单词,并将其余单词清零。最后,每条评论的序列长度(字数)各不相同,因此你将每条评论限制为 500 个字,截断长评论并用零值填充较短的评论。

现在你已经定义了你的问题以及如何准备和建模数据,你已准备好开发一个 LSTM 模型来对电影评论的情绪进行分类。

用于序列分类的简单 LSTM

你可以针对 IMDB 问题快速开发一个小型 LSTM 并获得良好的准确性。

让我们首先导入该模型所需的类和函数,并将随机数生成器初始化为常量值,以确保你可以轻松重现结果。

import tensorflow as tf
from tensorflow.keras.datasets import imdb
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import LSTM
from tensorflow.keras.layers import Embedding
from tensorflow.keras.preprocessing import sequence

你需要加载 IMDB 数据集。你将数据集限制为前 5,000 个单词。你还将把数据集拆分为训练集 (50%) 和测试集 (50%)。

# load the dataset but only keep the top n words, zero the rest
top_words = 5000
(X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=top_words)

接下来,你需要截断和填充输入序列,使它们的长度都相同以进行建模。该模型将了解到零值不携带任何信息。就内容而言,序列的长度不同,但在 Keras 中执行计算需要相同长度的向量。

# truncate and pad input sequences
max_review_length = 500
X_train = sequence.pad_sequences(X_train, maxlen=max_review_length)
X_test = sequence.pad_sequences(X_test, maxlen=max_review_length)

你现在可以定义、编译和拟合你的 LSTM 模型。

第一层是 Embedded 层,它使用 32 个长度的向量来表示每个单词。下一层是具有 100 个记忆单元(智能神经元)的 LSTM 层。最后,由于这是一个分类问题,你将使用具有单个神经元的密集输出层和一个 sigmoid 激活函数对问题中的两个类别(好和坏)进行 0 或 1 预测。

因为是二分类问题,所以使用log loss作为损失函数( Keras中的binary_crossentropy)。使用高效的ADAM优化算法。该模型仅适用于两个时期,因为它很快就会过度适应问题。64 条评论的大批量用于间隔权重更新。

# create the model
embedding_vecor_length = 32
model = Sequential()
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model.summary())
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=3, batch_size=64)

一旦适合,你就可以估计模型在看不见的评论中的表现。

# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Accuracy: %.2f%%" % (scores[1]*100))

运行此示例会产生以下输出。

Epoch 1/3
391/391 [==============================] - 124s 316ms/step - loss: 0.4525 - accuracy: 0.7794
Epoch 2/3
391/391 [==============================] - 124s 318ms/step - loss: 0.3117 - accuracy: 0.8706
Epoch 3/3
391/391 [==============================] - 126s 323ms/step - loss: 0.2526 - accuracy: 0.9003
Accuracy: 86.83%

你可以看到,这个简单的 LSTM 几乎没有调整,在 IMDB 问题上取得了接近最先进的结果。重要的是,这是一个模板,你可以使用它来将 LSTM 网络应用于你自己的序列分类问题。

现在,让我们看一下这个简单模型的一些扩展,你可能还想将这些扩展用于你自己的问题。

用于带 Dropout 的序列分类的 LSTM

像 LSTM 这样的递归神经网络一般都存在过拟合的问题。

可以使用 Dropout Keras 层在层之间应用 Dropout ,你可以通过在 Embedding 和 LSTM 层以及 LSTM 和 Dense 输出层之间添加新的 Dropout 层来轻松做到这一点。

例如:

model = Sequential()
model.add(Embedding(top_words, embedding_vecor_length, input_length=max_review_length))
model.add(Dropout(0.2))
model.add(LSTM(100))
model.add(Dropout(0.2))
model.add(Dense(1, activation='sigmoid'))

运行此示例会提供以下输出。

Epoch 1/3
391/391 [==============================] - 117s 297ms/step - loss: 0.4721 - accuracy: 0.7664
Epoch 2/3
391/391 [==============================] - 125s 319ms/step - loss: 0.2840 - accuracy: 0.8864
Epoch 3/3
391/391 [==============================] - 135s 346ms/step - loss: 0.3022 - accuracy: 0.8772
Accuracy: 85.66%

你可以看到 dropout 对训练产生了预期的影响,收敛趋势稍慢,在这种情况下,最终准确度较低。该模型可能会使用更多的训练周期,并可能获得更高的技能(试试看)。

或者,dropout 可以精确地单独应用于记忆单元与 LSTM 的输入和循环连接。

Keras 通过 LSTM 层上的参数提供此功能,dropout用于配置输入 dropout,recurrent_dropout用于配置循环 dropout。

例如,你可以修改第一个示例以将 dropout 添加到输入和循环连接中,如下所示:

model = Sequential()
model.add(Embedding(top_words, embedding_vecor_length)
model.add(LSTM(100, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(1, activation='sigmoid'))

运行此示例会提供以下输出。

Epoch 1/3
391/391 [==============================] - 220s 560ms/step - loss: 0.4605 - accuracy: 0.7784
Epoch 2/3
391/391 [==============================] - 219s 560ms/step - loss: 0.3158 - accuracy: 0.8773
Epoch 3/3
391/391 [==============================] - 219s 559ms/step - loss: 0.2734 - accuracy: 0.8930
Accuracy: 86.78%

你可以看到,LSTM 特定的 dropout 对网络收敛的影响比 layer-wise dropout 更明显。和上面一样,epochs 的数量保持不变,可以增加以查看模型的技能是否可以进一步提升。

Dropout 是一种在 LSTM 模型中对抗过度拟合的强大技术,尝试这两种方法是个好主意。不过,你可能会通过 Keras 中提供的特定于门的 dropout 获得更好的结果。

用于序列分类的双向 LSTM

有时,以相反的顺序使用序列会更好。x在这些情况下,你可以简单地使用 Python 语法反转向量,x[::-1]然后再使用它来训练 LSTM 网络。

有时,正向和反向顺序都不能完美工作,但将它们组合起来会得到更好的结果。在这种情况下,你将需要一个双向 LSTM 网络

双向 LSTM 网络就是两个独立的 LSTM 网络;一个以正向顺序进料,另一个以反向顺序进料。然后将两个 LSTM 网络的输出连接在一起,然后再馈送到网络的后续层。

在 Keras 中,你可以Bidirectional()为前向-后向输入克隆 LSTM 层并连接它们的输出。例如,

model = Sequential()
model.add(Embedding(top_words, embedding_vecor_length)
model.add(Bidirectional(LSTM(100, dropout=0.2, recurrent_dropout=0.2)))
model.add(Dense(1, activation='sigmoid'))

由于你创建的不是一个,而是两个 LSTM,每个 100 个单元,因此该网络将花费两倍的时间来训练。根据问题的不同,这种额外费用可能是合理的。

运行此示例会提供以下输出。

Epoch 1/3
391/391 [==============================] - 405s 1s/step - loss: 0.4960 - accuracy: 0.7532
Epoch 2/3
391/391 [==============================] - 439s 1s/step - loss: 0.3075 - accuracy: 0.8744
Epoch 3/3
391/391 [==============================] - 430s 1s/step - loss: 0.2551 - accuracy: 0.9014
Accuracy: 87.69%

看起来你只能得到轻微的改善,但训练时间要长得多。

用于序列分类的 LSTM 和卷积神经网络

例如,你可以按如下方式创建模型:

model = Sequential()
model.add(Embedding(top_words, embedding_vecor_length))
model.add(Conv1D(filters=32, kernel_size=3, padding='same', activation='relu'))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(100))
model.add(Dense(1, activation='sigmoid'))

为了完整起见,下面列出了包含 CNN 和 LSTM 层的完整代码清单。

运行此示例会提供以下输出。

Epoch 1/3
391/391 [==============================] - 65s 163ms/step - loss: 0.4213 - accuracy: 0.7950
Epoch 2/3
391/391 [==============================] - 66s 168ms/step - loss: 0.2490 - accuracy: 0.9026
Epoch 3/3
391/391 [==============================] - 73s 188ms/step - loss: 0.1979 - accuracy: 0.9261
Accuracy: 88.45%

你可以看到你获得的结果比第一个示例稍微好一些,尽管权重更少且训练时间更快。
如果进一步扩展此示例以使用 dropout,你可能会期望获得更好的结果。

猜你喜欢

转载自blog.csdn.net/weixin_38037405/article/details/130463966