基于长短时记忆(LSTM)的神经网络在自然语言处理领域发挥了重要作用。此外,它们还被广泛用于序列建模。LSTM 之所以被广泛应用于此,是因为在对样本进行前向传递时,模型会连接回自身,因此在对任何新样本进行预测时,都会受益于之前预测所产生的上下文。
在本文中,我们将了解如何使用 TensorFlow 和 Keras 构建 LSTM 模型。为此,我们首先要简单了解一下什么是 LSTM 及其工作原理。
简要回顾 LSTM
在我们实际编写任何代码之前,了解 LSTM 内部发生了什么很重要。首先,我们必须指出,LSTM 是对普通或传统递归神经网络(RNN)的改进。这种网络的外观如下:
在vanilla RNN 中,输入值 (X) 通过模型,模型在该时刻具有隐藏状态或学习状态 h。模型产生目标表征的输出 O。举例来说,利用这种工作方式,我们可以将英语输入转换为德语输出。因此,Vanilla RNN 被广泛用作序列到序列模型。
不过,我们也可以用经典神经网络做同样的事情。与传统的 MLP 相比,RNN 的优势在于它们会将输出传回自身,以便在下一次传回时使用。这就为神经网络提供了与之前输入相关的上下文(在翻译等语义混乱的任务中,这一点有时非常重要)。因此,经典的 RNN 只不过是一个将神经输出传递回神经元的全连接网络。
到目前为止,效果还不错。RNN 在当时确实提升了技术水平。但是,问题来了。当你想训练经典的循环神经网络时,问题就出现了。如果在训练普通神经网络时应用反向传播法,误差会被反向计算,这样梯度更新就会成为优化器可以应用的已知梯度。然而,递归反向传播并不容易实现,因此必须采用另一种方法。实际上,这涉及到展开网络,有效地复制网络(初始化完全相同)并对其进行改进。这样,我们就能更轻松地计算梯度,并将它们串联起来。这样就可以训练 RNN。
但是,将梯度有效地链在一起意味着你必须应用乘法。这就是问题所在:经典的 RNN 与 Sigmoid 和 Tanh 等激活函数相结合,但主要是 Sigmoid。(我们将在以后的文章中讨论如何实现这些功能)。由于这些函数的导数输出几乎总是小于 1.0,因此会出现梯度消失的严重情况。因此,当序列变长时,传统的 RNN 无法使用;它们只会卡住或训练得很慢。
进入 LSTM。这些长短期记忆网络有效地分割了输出和记忆。在所谓的存储单元中,它们可以实现所有功能,生成预测并更新内存。从视觉上看,情况如下:
让我们更详细地了解一下所有组件:
与简单的 RNN 相比,LSTM 的优势在于记忆与实际输出机制的分离。如你所见,所有导致梯度消失的机制都在单元内。在单元间通信中,梯度计算过程中遇到的唯一元素是乘法(x)和加法(+)。这些都是线性运算,因此 LSTM 可以确保细胞间的梯度始终为 1.0。因此,LSTM 解决了梯度消失的问题。
这使得它们比普通的 RNN 快很多。
TensorFlow 和 Keras 中的 LSTM
既然我们已经了解了 LSTM 在理论上的工作原理,那就让我们看看如何在 TensorFlow 和 Keras 中构建 LSTM。当然,我们必须先看看它们是如何表示的。
事实上,这就是我们想要的 LSTM,尽管它可能还不具备所有的门--在另一篇跟进 Hochreiter 论文的论文中,门被修改了。不过,了解具有所有门电路的 LSTM 是个好主意,因为现在大多数 LSTM 都是这个样子的。
代码如下:
tf.keras.layers.LSTM(
units, activation='tanh', recurrent_activation='sigmoid',
use_bias=True, kernel_initializer='glorot_uniform',
recurrent_initializer='orthogonal',
bias_initializer='zeros', unit_forget_bias=True,
kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None,
activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None,
bias_constraint=None, dropout=0.0, recurrent_dropout=0.0,
return_sequences=False, return_state=False, go_backwards=False, stateful=False,
time_major=False, unroll=False, **kwargs
)
这些都是可以配置的属性:
如何在 TensorFlow 和 Keras 中使用 LSTM 层创建神经网络
既然我们已经了解了 LSTM 的工作原理以及它们在 TensorFlow 中的表现形式,那么现在就该用 Python、TensorFlow 及其 Keras API 实际构建一个 LSTM 了。我们将通过实例一步步引导你完成整个过程。整个过程包括以下步骤:
打开代码编辑器,创建一个文件,如 lstm.py,然后开始!
定义模型导入
让我们先指定模型导入:
import tensorflow as tf
from tensorflow.keras.datasets import imdb
from tensorflow.keras.layers import Embedding, Dense, LSTM
from tensorflow.keras.losses import BinaryCrossentropy
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.preprocessing.sequence import pad_sequences
列出模型配置
下一步是指定模型配置。虽然严格来说没有必要(我们也可以硬编码指定它们),但我始终认为将它们组合在一起是个好主意。这样,你就可以很容易地看到模型是如何配置的,而无需查看所有方面。
下面我们可以看到,我们的模型将使用二元交叉熵损失和亚当优化,以 128 的批次大小进行训练,并且只训练 5 个历元(我们只需要向你展示它的工作原理)。20% 的训练数据将用于验证目的,输出将是冗长的,verbosity 模式设置为 1(从 0、1 和 2 中选择)。我们学习的单词嵌入将有 15 个隐藏维度,通过模型的每个序列最多为 300 个字符。我们的词汇量最多将包含 5000 个单词。
# Model configuration
additional_metrics = ['accuracy']
batch_size = 128
embedding_output_dims = 15
loss_function = BinaryCrossentropy()
max_sequence_length = 300
num_distinct_words = 5000
number_of_epochs = 5
optimizer = Adam()
validation_split = 0.20
verbosity_mode = 1
现在,你可能还想在 TensorFlow 中禁用 “快速执行”(Eager Execution)功能。虽然它并非对所有人都有效,但有些人报告说,使用它后训练过程会加快。不过,没必要这么做,只需测试一下它在你的机器上的表现即可:
# Disable eager execution
tf.compat.v1.disable_eager_execution()
加载和准备数据
完成上述工作后,我们就可以加载和准备数据了。为了方便起见,Keras 提供了一套标准数据集,其中 IMDB 数据集可用于情感分析(本质上是两个类别的文本分类)。使用 imdb.load_data(...),我们可以加载数据。
加载数据后,我们将应用 pad_sequences。这可以确保短于最大句子长度的句子通过使用填充(在本例中为 0,因为这通常与填充字符相对应)达到等长。
# Load dataset
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=num_distinct_words)
print(x_train.shape)
print(x_test.shape)
# Pad all sequences
padded_inputs = pad_sequences(x_train, maxlen=max_sequence_length, value = 0.0) # 0.0 because it corresponds with <PAD>
padded_inputs_test = pad_sequences(x_test, maxlen=max_sequence_length, value = 0.0) # 0.0 because it corresponds with <PAD>
定义 Keras 模型
然后,我们可以定义 Keras 模型。由于我们使用的是 Sequential API,因此可以使用 Sequential() 对模型变量进行初始化。第一层是嵌入层,用于学习单词嵌入,在我们的案例中,嵌入的维度为 15。随后是一个 LSTM 层,提供递归段(默认启用 tanh 激活),以及一个 Dense 层,该层有一个输出--通过 Sigmoid 输出一个介于 0 和 1 之间的数字,代表对一个类别的定向。
# Define the Keras model
model = Sequential()
model.add(Embedding(num_distinct_words, embedding_output_dims, input_length=max_sequence_length))
model.add(LSTM(10))
model.add(Dense(1, activation='sigmoid'))
编译 Keras 模型
然后就可以编译模型了。这将初始化模型,到目前为止,模型只是一个骨架、一个基础,还没有实际的模型。为此,我们需要指定优化器、损失函数和之前指定的附加指标。
# Compile the model
model.compile(optimizer=optimizer, loss=loss_function, metrics=additional_metrics)
这也是生成模型外观摘要的好地方。
# Give a summary
model.summary()
训练 Keras 模型
然后,我们可以指示 TensorFlow 开始训练过程。
# Train the model
history = model.fit(padded_inputs, y_train, batch_size=batch_size, epochs=number_of_epochs, verbose=verbosity_mode, validation_split=validation_split)
传递给模型的(输入、输出)对是经过填充的输入及其相应的类标签。训练时使用的批量大小、历时次数、冗余模式和验证分割也在上文的配置部分中定义。
评估 Keras 模型
我们无法在用于训练模型的相同数据集上评估模型。幸运的是,我们可以通过 load_data(...) 部分中执行的 train/test 分割获得测试数据,并使用内置的评估工具对模型进行评估。然后我们在屏幕上打印测试结果。
# Test the model after training
test_results = model.evaluate(padded_inputs_test, y_test, verbose=False)
print(f'Test results - Loss: {test_results[0]} - Accuracy: {100*test_results[1]}%')
运行模型
是时候运行模型了!打开至少安装了 TensorFlow 和 Python 的终端,运行模型 - python lstm.py。
几秒钟后,模型就会开始训练。如果你的机器上还没有下载 IMDB 数据集,则会先下载。
最终,你将在评估集上看到大约 87.1% 的准确率:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (None, 300, 15) 75000
_________________________________________________________________
lstm (LSTM) (None, 10) 1040
_________________________________________________________________
dense (Dense) (None, 1) 11
=================================================================
Total params: 76,051
Trainable params: 76,051
Non-trainable params: 0
_________________________________________________________________
2021-01-08 14:53:19.988309: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:116] None of the MLIR optimization passes are enabled (registered 2)
Epoch 1/5
157/157 [==============================] - 19s 106ms/step - loss: 0.6730 - accuracy: 0.5799 - val_loss: 0.4866 - val_accuracy: 0.8174
Epoch 2/5
157/157 [==============================] - 13s 83ms/step - loss: 0.4312 - accuracy: 0.8445 - val_loss: 0.3694 - val_accuracy: 0.8540
Epoch 3/5
157/157 [==============================] - 14s 86ms/step - loss: 0.2997 - accuracy: 0.8955 - val_loss: 0.3333 - val_accuracy: 0.8680
Epoch 4/5
157/157 [==============================] - 15s 96ms/step - loss: 0.2499 - accuracy: 0.9133 - val_loss: 0.3078 - val_accuracy: 0.8782
Epoch 5/5
157/157 [==============================] - 14s 90ms/step - loss: 0.2032 - accuracy: 0.9316 - val_loss: 0.3152 - val_accuracy: 0.8806
Test results - Loss: 0.3316078186035156 - Accuracy: 87.09200024604797%
TensorFlow/Keras LSTM 在 GPU 上运行缓慢
如果在 GPU 上训练 TensorFlow LSTM 时遇到速度问题,可以在 model.fit 前添加以下内容,暂时禁止其访问 GPU:
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
结论
长短期记忆网络(LSTM)是一种递归神经网络,可用于自然语言处理、时间序列和其他序列建模任务。在本文中,我们逐步介绍了 LSTM 在 TensorFlow 和 Keras 中的应用。