使用Tensorflow和Keras构建LSTM模型

2024年10月12日 由 alex 发表 41 0

基于长短时记忆(LSTM)的神经网络在自然语言处理领域发挥了重要作用。此外,它们还被广泛用于序列建模。LSTM 之所以被广泛应用于此,是因为在对样本进行前向传递时,模型会连接回自身,因此在对任何新样本进行预测时,都会受益于之前预测所产生的上下文。


在本文中,我们将了解如何使用 TensorFlow 和 Keras 构建 LSTM 模型。为此,我们首先要简单了解一下什么是 LSTM 及其工作原理。


简要回顾 LSTM

在我们实际编写任何代码之前,了解 LSTM 内部发生了什么很重要。首先,我们必须指出,LSTM 是对普通或传统递归神经网络(RNN)的改进。这种网络的外观如下:


10


在vanilla RNN 中,输入值 (X) 通过模型,模型在该时刻具有隐藏状态或学习状态 h。模型产生目标表征的输出 O。举例来说,利用这种工作方式,我们可以将英语输入转换为德语输出。因此,Vanilla RNN 被广泛用作序列到序列模型。


不过,我们也可以用经典神经网络做同样的事情。与传统的 MLP 相比,RNN 的优势在于它们会将输出传回自身,以便在下一次传回时使用。这就为神经网络提供了与之前输入相关的上下文(在翻译等语义混乱的任务中,这一点有时非常重要)。因此,经典的 RNN 只不过是一个将神经输出传递回神经元的全连接网络。


到目前为止,效果还不错。RNN 在当时确实提升了技术水平。但是,问题来了。当你想训练经典的循环神经网络时,问题就出现了。如果在训练普通神经网络时应用反向传播法,误差会被反向计算,这样梯度更新就会成为优化器可以应用的已知梯度。然而,递归反向传播并不容易实现,因此必须采用另一种方法。实际上,这涉及到展开网络,有效地复制网络(初始化完全相同)并对其进行改进。这样,我们就能更轻松地计算梯度,并将它们串联起来。这样就可以训练 RNN。


但是,将梯度有效地链在一起意味着你必须应用乘法。这就是问题所在:经典的 RNN 与 Sigmoid 和 Tanh 等激活函数相结合,但主要是 Sigmoid。(我们将在以后的文章中讨论如何实现这些功能)。由于这些函数的导数输出几乎总是小于 1.0,因此会出现梯度消失的严重情况。因此,当序列变长时,传统的 RNN 无法使用;它们只会卡住或训练得很慢。


进入 LSTM。这些长短期记忆网络有效地分割了输出和记忆。在所谓的存储单元中,它们可以实现所有功能,生成预测并更新内存。从视觉上看,情况如下:


11


让我们更详细地了解一下所有组件:

  • 所有功能都嵌入到一个存储单元中,上图中的圆角边框就是存储单元。
  • h[t-1] 和 h[t] 变量分别代表存储单元在 t-1 和 t 时的输出。
  • c[t-1]和 c[t] 变量代表已知时间步长下的内存本身。正如你所看到的,内存已经从输出变量中分离出来,成为一个独立的实体。
  • 我们有三个所谓的门,由单元中的三个元素块表示:
  • 左边是遗忘门。它利用之前的输出和当前的输入,通过 Sigmoid 激活计算出与当前和之前的输入相关的可遗忘内容,从而将其从内存中删除。将其与内存相乘,就能完成删除。
  • 在中间,我们看到一个输入门。它利用之前的输出和当前的输入,同时应用 Sigmoid 和 Tanh 激活。Sigmoid 激活有效地学习了输入中必须保留的内容,而 Tanh 激活则将输入值归一化为 [-1, +1] 范围,从而稳定了训练过程。正如你所看到的,首先将结果相乘(以确保实现归一化),然后将其添加到内存中。
  • 右边是一个输出门。它通过 Tanh 获取内存中的归一化值,并通过 Sigmoid 激活值获取前一个输出和当前输入,从而有效地学习当前输入值必须预测的结果。然后输出该值,并将记忆值和输出值传递给下一个单元。


与简单的 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
)


这些都是可以配置的属性:

  • 通过单元,我们可以定义输出空间的维度,如我们常用的密集层。
  • 激活属性定义了将使用的激活函数。默认情况下,它是 Tanh 函数。
  • 通过 recurrent_activation,可以定义递归功能的激活函数。
  • use_bias 属性可用于配置是否必须使用偏置来引导模型。
  • 初始化器可用于初始化内核和递归段的权重以及偏置。
  • unit_forget_bias 表示遗忘门的偏置值 (+1)。这是原始 LSTM 论文后续研究的建议。
  • 正则和约束可以对训练过程进行限制,从而阻止梯度消失和爆炸,并使模型保持足够的复杂度。
  • 为了避免过度拟合,可以在单元本身和递归片段中添加 Dropout。
  • 通过 return_sequences,你可以指出是只想将当前输入的预测结果作为输出,还是将所有之前的预测结果作为输出。
  • 使用 return_state,可以说明除了输出外,是否还需要返回状态。
  • 通过 go_backwards,可以确定是否要以相反的顺序返回序列。
  • 如果将 stateful 设置为 True,循环段将在批处理级别而非模型级别上工作。
  • 输入的结构(时间步、批次、特征或批次、时间步、特征)可以通过 time_major 进行切换。
  • 如果设置为 unroll,则仍可在训练时展开网络。如果设置为 “假”,则将使用符号循环。
  • 可以使用 **kwargs 传递其他参数。


如何在 TensorFlow 和 Keras 中使用 LSTM 层创建神经网络

既然我们已经了解了 LSTM 的工作原理以及它们在 TensorFlow 中的表现形式,那么现在就该用 Python、TensorFlow 及其 Keras API 实际构建一个 LSTM 了。我们将通过实例一步步引导你完成整个过程。整个过程包括以下步骤:

  1. 将我们需要的 Keras 功能导入 Python 脚本。
  2. 列出 LSTM 模型的配置并准备训练。
  3. 加载并准备数据集;我们今天将使用 IMDB 数据集。
  4. 定义 Keras 模型
  5. 编译 Keras 模型
  6. 训练 Keras 模型
  7. 评估 Keras 模型。


打开代码编辑器,创建一个文件,如 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


  • 我们需要 TensorFlow,因此导入时使用 tf。
  • 从 TensorFlow Keras 数据集中,我们导入 imdb 数据集。
  • 我们需要单词嵌入(Embedding)、MLP 层(Dense)和 LSTM 层(LSTM),因此也要导入它们。
  • 我们的损失函数将是二元交叉熵。
  • 由于我们将使用 model.add 将所有层堆叠在一起,我们首先需要 Sequential(Keras Sequential API)来构建模型变量。
  • 在优化方面,我们使用经典梯度下降算法的扩展,称为 Adam。
  • 最后,我们需要导入 pad_sequences。我们将使用具有评论序列的 IMDB 数据集。虽然我们会指定一个最大长度,但这可能意味着还有更短的序列存在;这些序列没有被截断,因此大小与我们想要的(即最大长度)不同。我们必须用 0 填充它们,以使它们具有相同的长度。


列出模型配置

下一步是指定模型配置。虽然严格来说没有必要(我们也可以硬编码指定它们),但我始终认为将它们组合在一起是个好主意。这样,你就可以很容易地看到模型是如何配置的,而无需查看所有方面。


下面我们可以看到,我们的模型将使用二元交叉熵损失和亚当优化,以 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 中的应用。




文章来源:https://medium.com/ai-in-plain-english/building-an-lstm-model-with-tensorflow-and-keras-880ca1626917
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消