使用 Keras 在 Python 中创建和评估深度学习神经网络非常容易,但您必须遵循严格的模型生命周期。
在这篇文章中,您将了解在 Keras 中创建、训练和评估长短期记忆 (LSTM) 循环神经网络的分步生命周期,以及如何使用训练好的模型进行预测。
阅读本文后,你将了解:
- 如何在 Keras 中定义、编译、拟合和评估 LSTM。
- 如何为回归和分类序列预测问题选择标准默认值。
- 如何将所有这些结合起来,在 Keras 中开发和运行您的第一个 LSTM 循环神经网络。
购买我的新书 《使用 Python 的长短期记忆网络》,即可启动您的项目,其中包括分步教程和所有示例的 Python 源代码文件。
让我们开始吧。
- 2017 年 6 月更新:修复了输入大小调整示例中的错别字。

Keras 中长短期记忆模型的 5 步生命周期
图片作者:docmonstereyes,保留部分权利。
概述
下面是 Keras 中 LSTM 模型生命周期的 5 个步骤概述,我们将逐一介绍。
- 定义网络
- 编译网络
- 拟合网络
- 评估网络
- 进行预测
环境
本教程假定您已安装 Python SciPy 环境。您可以使用 Python 2 或 3。
本教程假定您已安装 Keras v2.0 或更高版本,并使用 TensorFlow 或 Theano 后端。
本教程还假定您已安装 scikit-learn、Pandas、NumPy 和 Matplotlib。
接下来,让我们看看一个标准的时间序列预测问题,我们可以将其作为本次实验的背景。
如果您需要帮助设置 Python 环境,请参阅此帖子
需要 LSTM 帮助进行序列预测吗?
参加我的免费7天电子邮件课程,了解6种不同的LSTM架构(附代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
步骤 1. 定义网络
第一步是定义您的网络。
神经网络在 Keras 中被定义为一系列层。这些层的容器是 Sequential 类。
第一步是创建 Sequential 类的实例。然后您可以创建层并按照它们应该连接的顺序添加它们。由记忆单元组成的 LSTM 循环层称为 LSTM()。通常跟在 LSTM 层后面并用于输出预测的全连接层称为 Dense()。
例如,我们可以分两步完成
1 2 3 |
model = Sequential() 模型。添加(LSTM(2)) 模型。添加(Dense(1)) |
但我们也可以通过创建层数组并将其传递给 Sequential 的构造函数来一步完成。
1 2 |
层 = [LSTM(2), Dense(1)] 模型 = Sequential(层) |
网络中的第一层必须定义预期的输入数量。输入必须是三维的,由样本、时间步长和特征组成。
- 样本。这是您数据中的行。
- 时间步长。这是特征的过去观测值,例如滞后变量。
- 特征。这是您数据中的列。
假设您的数据已作为 NumPy 数组加载,您可以使用 NumPy 中的 reshape() 函数将 2D 数据集转换为 3D 数据集。如果您希望列成为一个特征的时间步长,您可以使用
1 |
数据 = 数据。重塑((数据。形状[0], 数据。形状[1], 1)) |
如果您希望 2D 数据中的列成为具有一个时间步长的特征,您可以使用
1 |
数据 = 数据。重塑((数据。形状[0], 1, 数据。形状[1])) |
您可以指定 input_shape 参数,它需要一个包含时间步长数和特征数的元组。例如,如果我们有一个单变量时间序列,每行有两个滞后观测值,有两个时间步长和一个特征,它将按如下方式指定
1 2 3 |
model = Sequential() 模型。添加(LSTM(5, 输入形状=(2,1))) 模型。添加(Dense(1)) |
LSTM 层可以通过将它们添加到 Sequential 模型中来堆叠。重要的是,当堆叠 LSTM 层时,我们必须为每个输入输出一个序列而不是单个值,以便后续的 LSTM 层可以具有所需的 3D 输入。我们可以通过将 return_sequences 参数设置为 True 来实现这一点。例如
1 2 3 4 |
model = Sequential() 模型。添加(LSTM(5, 输入形状=(2,1), 返回序列=真)) 模型。添加(LSTM(5)) 模型。添加(Dense(1)) |
将 Sequential 模型视为一个管道,您的原始数据从一端输入,预测结果从另一端输出。
这在 Keras 中是一个有用的容器,因为传统上与层相关的关注点也可以分离出来并作为单独的层添加,清楚地显示它们在从输入到预测的数据转换中的作用。
例如,转换层中每个神经元的求和信号的激活函数可以提取出来并作为名为 Activation 的类似层对象添加到 Sequential 中。
1 2 3 4 |
model = Sequential() 模型。添加(LSTM(5, 输入形状=(2,1))) 模型。添加(Dense(1)) 模型。添加(激活('sigmoid')) |
激活函数的选择对输出层最为重要,因为它将定义预测的格式。
例如,下面是一些常见的预测建模问题类型以及您可以在输出层中使用的结构和标准激活函数
- 回归:线性激活函数,或“linear”,神经元数量与输出数量匹配。
- 二元分类(2 类):逻辑激活函数,或“sigmoid”,输出层有一个神经元。
- 多类别分类(>2 类):Softmax 激活函数,或“softmax”,每个类别值一个输出神经元,假设采用独热编码输出模式。
步骤 2. 编译网络
一旦我们定义了网络,我们必须对其进行编译。
编译是一个提高效率的步骤。它将我们定义的简单层序列转换为一系列高效的矩阵变换,其格式旨在根据 Keras 的配置在您的 GPU 或 CPU 上执行。
将编译视为网络的预计算步骤。定义模型后始终需要它。
编译需要指定许多参数,这些参数专门用于训练您的网络。具体来说,用于训练网络的优化算法和用于评估网络的损失函数由优化算法最小化。
例如,下面是一个编译定义模型并指定随机梯度下降 (sgd) 优化算法和均方误差 (mean_squared_error) 损失函数的情况,用于回归类型问题。
1 |
模型。编译(优化器='sgd', 损失='mean_squared_error') |
或者,优化器可以在提供给编译步骤作为参数之前创建和配置。
1 2 |
算法 = SGD(学习率=0.1, 动量=0.3) 模型。编译(优化器=算法, 损失='mean_squared_error') |
预测建模问题的类型对可以使用的损失函数类型施加了限制。
例如,下面是一些针对不同预测模型类型的标准损失函数
- 回归:均方误差或“mean_squared_error”。
- 二元分类(2 类):对数损失,也称为交叉熵或“binary_crossentropy”。
- 多类别分类(>2 类):多类别对数损失或“categorical_crossentropy”。
最常见的优化算法是随机梯度下降,但 Keras 还支持一系列其他最先进的优化算法,这些算法在很少或没有配置的情况下也能很好地工作。
由于它们的整体性能更好,最常用的优化算法可能是
- 随机梯度下降,或“sgd”,需要调整学习率和动量。
- ADAM,或“adam”,需要调整学习率。
- RMSprop,或“rmsprop”,需要调整学习率。
最后,除了损失函数之外,您还可以指定在拟合模型时要收集的指标。通常,最有用的额外指标是分类问题的准确性。要收集的指标以数组形式按名称指定。
例如:
1 |
模型。编译(优化器='sgd', 损失='mean_squared_error', 指标=['accuracy']) |
步骤 3. 拟合网络
网络编译完成后,就可以进行拟合,这意味着在训练数据集上调整权重。
拟合网络需要指定训练数据,包括输入模式矩阵 X 和匹配的输出模式数组 y。
网络使用反向传播算法进行训练,并根据编译模型时指定的优化算法和损失函数进行优化。
反向传播算法要求网络训练指定数量的 epoch 或训练数据集的曝光次数。
每个 epoch 可以划分为输入-输出模式对组,称为批次。这定义了网络在每个 epoch 内更新权重之前接触到的模式数量。这也是一种效率优化,确保一次不会将太多输入模式加载到内存中。
拟合网络的最小示例如下
1 |
历史 = 模型。拟合(X, y, 批次大小=10, epoch数=100) |
拟合完成后,将返回一个历史对象,它提供了模型在训练期间性能的摘要。这包括损失以及在编译模型时指定的任何其他指标,每个 epoch 都会记录。
训练可能需要很长时间,从几秒到几小时到几天,具体取决于网络的大小和训练数据的大小。
默认情况下,每个 epoch 都会在命令行上显示一个进度条。这可能会给您带来太多噪音,或者可能导致您的环境出现问题,例如您在交互式笔记本或 IDE 中。
您可以通过将 verbose 参数设置为 2 来将显示的信息量减少到每个 epoch 仅显示损失。您可以通过将 verbose 设置为 1 来关闭所有输出。例如
1 |
历史 = 模型。拟合(X, y, 批次大小=10, epoch数=100, 详细程度=0) |
步骤 4. 评估网络
网络训练完成后,即可进行评估。
网络可以在训练数据上进行评估,但这不会提供网络作为预测模型性能的有用指示,因为它之前已经看到了所有这些数据。
我们可以评估网络在测试期间未见的单独数据集上的性能。这将提供网络在未来对未见数据进行预测的性能估计。
模型评估所有测试模式的损失,以及编译模型时指定的任何其他指标,例如分类准确率。返回评估指标列表。
例如,对于使用准确率指标编译的模型,我们可以在新数据集上按如下方式评估它
1 |
损失, 准确率 = 模型。评估(X, y) |
与拟合网络一样,提供了详细输出以了解评估模型的进度。我们可以通过将 verbose 参数设置为 0 来关闭它。
1 |
损失, 准确率 = 模型。评估(X, y, 详细程度=0) |
步骤 5. 进行预测
一旦我们对拟合模型的性能感到满意,我们就可以使用它对新数据进行预测。
这就像在模型上调用 predict() 函数并提供一组新的输入模式一样简单。
例如:
1 |
预测 = 模型。预测(X) |
预测将以网络输出层提供的格式返回。
在回归问题中,这些预测可以直接以问题格式提供,由线性激活函数提供。
对于二元分类问题,预测可能是第一个类别的概率数组,可以通过四舍五入转换为 1 或 0。
对于多类别分类问题,结果可能以概率数组的形式出现(假设是独热编码的输出变量),可能需要使用 argmax() NumPy 函数转换为单个类别输出预测。
或者,对于分类问题,我们可以使用 predict_classes() 函数,它会自动将模糊预测转换为清晰的整数类别值。
1 |
预测 = 模型。预测类别(X) |
与拟合和评估网络一样,提供了详细输出以了解模型进行预测的进度。我们可以通过将 verbose 参数设置为 0 来关闭它。
1 |
预测 = 模型。预测(X, 详细程度=0) |
端到端工作示例
让我们用一个小型工作示例将所有这些联系起来。
这个例子将使用一个学习 10 个数字序列的简单问题。我们将向网络显示一个数字,例如 0.0,并期望它预测 0.1。然后显示 0.1 并期望它预测 0.2,依此类推到 0.9。
- 定义网络:我们将构建一个 LSTM 神经网络,在可见层中具有 1 个输入时间步和 1 个输入特征,在 LSTM 隐藏层中具有 10 个记忆单元,在全连接输出层中具有 1 个神经元,并带有线性(默认)激活函数。
- 编译网络:我们将使用高效的 ADAM 优化算法,采用默认配置和均方误差损失函数,因为它是一个回归问题。
- 拟合网络:我们将对网络进行 1,000 个 epoch 的拟合,并使用等于训练集中模式数量的批处理大小。我们还将关闭所有详细输出。
- 评估网络。我们将在训练数据集上评估网络。通常,我们会在测试集或验证集上评估模型。
- 进行预测。我们将对训练输入数据进行预测。同样,通常我们会在不知道正确答案的数据上进行预测。
完整的代码列表如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# LSTM 学习序列的示例 from pandas import DataFrame 从 pandas 导入 concat from keras.models import Sequential from keras.layers import Dense 来自 keras.层 导入 LSTM # 创建序列 长度 = 10 序列 = [i/浮点数(长度) for i in 范围(长度)] 打印(序列) # 创建 X/y 对 df = 数据帧(序列) df = 连接([df。移位(1), df], 轴=1) df。删除空值(原地=真) # 转换为 LSTM 友好格式 值 = df。值 X, y = 值[:, 0], 值[:, 1] X = X。重塑(长度(X), 1, 1) # 1. 定义网络 model = Sequential() 模型。添加(LSTM(10, 输入形状=(1,1))) 模型。添加(Dense(1)) # 2. 编译网络 模型。编译(优化器='adam', 损失='mean_squared_error') # 3. 拟合网络 历史 = 模型。拟合(X, y, epoch数=1000, 批次大小=长度(X), 详细程度=0) # 4. 评估网络 损失 = 模型。评估(X, y, 详细程度=0) print(loss) # 5. 进行预测 预测 = 模型。预测(X, 详细程度=0) 打印(预测[:, 0]) |
运行此示例将产生以下输出,显示 10 个数字的原始输入序列、网络在对整个序列进行预测时的均方误差损失以及每个输入模式的预测。
注意:由于算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
输出为了可读性进行了间隔。
我们可以看到序列学习得很好,特别是如果我们将预测四舍五入到小数点后第一位。
1 2 3 4 5 6 |
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] 4.54527471447e-05 [ 0.11612834 0.20493418 0.29793766 0.39445466 0.49376178 0.59512401 0.69782174 0.80117452 0.90455914] |
进一步阅读
总结
在这篇文章中,您发现了使用 Keras 库的 LSTM 循环神经网络的 5 步生命周期。
具体来说,你学到了:
- 如何在 Keras 中定义、编译、拟合、评估 LSTM 网络并进行预测。
- 如何为分类和回归问题选择激活函数和输出层配置。
- 如何在 Keras 中开发和运行您的第一个 LSTM 模型。
您对 Keras 中的 LSTM 模型或这篇文章有任何疑问吗?
请在评论中提出您的问题,我将尽力回答。
您的 reshape 语句中缺少“.shape”。
嗨 Peter,当提供整数而不是元组时,它似乎仍然有效。
不错的总结!
我想补充一点
如果时间步长 = 2,并保持特征不变,那么
– reshape(int(data[0]/2), 2, data[1])
如果时间步长 = 2,并保持样本不变,那么
– reshape(data[0], 2, int(data[1]/2))
谢谢 Birkey,这意味着我可以同时拥有多个时间步长和特征,对吗?
没错!
太棒了!
嗨,Birkey!
如果时间步长 = 2,并保持样本不变,那么
– reshape(data[0], 2, int(data[1]/2))
特征的数量已经改变。这会影响结果吗?它仍然有意义吗?
嗨,Jason,
一如既往,您的文章写得非常棒。我真的很喜欢。谢谢。
您在《深度学习书》中介绍过 LSTM 吗?
谢谢
谢谢。是的,那本书里有几章关于 LSTM 的内容。我希望能很快发布一本专门讨论这个主题的新书。
你好,
我有 3 个类别,想设计一个用于 3 类别分类的 LSTM。我在这里做错了什么,有什么建议吗?
我得到以下错误
输入 0 与层 lstm_1 不兼容:预期 ndim=3,找到 ndim=2
#下面是我的代码片段
打印 train_data.shape, train_labels.shape
(1199, 11) (1199, 3)
打印 test_data.shape, test_labels.shape
(592, 11) (592, 3)
##
model = Sequential()
model.add(LSTM(11, input_shape=(11,), return_sequences=True))
model.add(LSTM(11))
model.add(Dense(3, activation=’softmax’))
model.compile(loss=’categorical_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
打印(model.summary())
LSTM 需要 3D 输入。请确保您的输入数据形状是 3D [样本,时间步,特征],并且您使用 (时间步,特征) 元组指定 input_shape 参数。
嗨 Jason,
不错的教程。请解释您如何决定 LSTM 记忆单元的数量?
我只是好奇它如何影响模型准确性。
反复试验。我们无法解析计算如何计算神经网络。
又一个很棒的教程!
不过有一个问题:考虑到 X 和 y 之间存在完美的线性关系,这里的 LSTM 性能不是非常差吗(经过 1000 个 epoch 之后)?
可能吧。这是一个糟糕的例子,因为模型在每个样本只有一个时间步长的情况下,没有机会通过适当的 BPTT 进行学习。
嗨 Jason,我很喜欢阅读您的博客,您这里的解释是最好的之一。
我正在努力理解 LSTM,但对输入/输出的维度仍然有点困惑。我在这篇文章中找到的一些细节没有在任何地方提及,例如
“如果您希望列成为一个特征的时间步长,您可以使用
data = data.reshape((data.shape[0], data.shape[1], 1))
如果您希望 2D 数据中的列成为具有一个时间步长的特征,您可以使用
data = data.reshape((data.shape[0], 1, data.shape[1]))
”
如果您能详细解释如何针对不同类型的 LSTM 网络(一对一、多对一、多对多)重塑数据,那将非常有帮助。
这篇文章会更清楚地说明这一点
https://machinelearning.org.cn/reshape-input-data-long-short-term-memory-networks-keras/
你的文章清晰易懂,期待你能分享pytorch版本的case1
谢谢。
你好,Birkey!
除了较小的损失,还有什么方法可以判断回归模型的好坏吗?
是的,预测误差。
我有一个问题,我正在尝试使用 LSTM RNN 或 MLFNN 预测欧元/美元汇率。我应该使用什么神经网络?请提供一些答案
我对金融一无所知。也许可以尝试一系列方法,找出哪种方法最适合您的特定数据集。
嗨,Jason,
非常感谢您的网站和书籍。我已经购买了时间序列和机器学习资料,我正在尽可能快地吸收它们。
我打算使用 LTSM 预测 EURUSD 收盘价,我知道您不懂金融。所以,我不是特指那个。但是,我想看看如何扩展上面的例子来预测未来的时间步。您所做的只是“预测”您已经训练过的内容。
换句话说,要获得下一个数字“10”,我不必提供“10”,但我有点期望“10”是由上面传递 0-9 序列产生的。我错过了什么?
对不起我的新手状态。我敢肯定这很基础。我想我理解了这一点,但我只是想弄清楚。。
再次感谢!
机器学习的理念是它会泛化。它学习如何将输入映射到输出的一般模式。
一个只能准确预测训练数据的模型是没有用的。
我有许多关于预测样本外的帖子,也许可以从这里开始
https://machinelearning.org.cn/how-to-make-classification-and-regression-predictions-for-deep-learning-models-in-keras/
还有这里
https://machinelearning.org.cn/make-predictions-long-short-term-memory-models-keras/
亲爱的Jason
我问这个问题超出这个帖子
LSTM 和 GRU 的主要区别是什么?
自从我用 LSTM 处理非英语文本以来,感觉很好
GRU 更简单,这是主要区别。
嗨,Jason,
我有一个关于多个 LSTM 堆栈的问题。
假设这个例子,我想预测序列中的下一个数字
1 2 3 4 ==> 5
65,66,67,68 ==> 69
对这个例子使用 LSTM 堆栈是多余的。但我们假设我们正在使用它。
假设我们的架构如下所示
LSTM-2 ==> LSTM-3 ==> DENSE(1) ==> 输出
对于第一层 LSTM-2(我们只考虑样本-1,即 1,2,3,4)
=> 第一个 LSTM 单元将按顺序逐一输入 1,2,3,4 并生成中间向量 v1
=> 第二个 LSTM 单元(来自同一第一层)将按顺序逐一输入相同的 1,2,3,4 并生成中间向量 v2
问题-1] 第一个和第二个 LSTM 单元具有相同的输入 1,2,3,4,但它们的输出 v1 和 v2 不同,因为每个 LSTM 单元具有不同的权重。我的理解正确吗?
对于第二层 LSTM-3,输入将是 v1 和 v2(来自早期阶段),它们将按顺序逐一输入到第二层。
我的理解正确吗?
是的,正确。
我尝试在稠密层中使用 Relu 而不是线性激活函数进行回归
问题是尝试根据给定天数(有 3 个特征)预测商店一周的收入。
当我使用 3 天(所以时间步长 = 3)时,Relu 的效果比线性激活函数好。然而,对于 2 天或 4 天
(时间步长分别为 2 和 4),训练时的误差似乎以巨大的损失跳跃,而使用线性激活函数,两种情况下的衰减似乎都正常。这看起来非常奇怪(因为我不太期望负值)
关于 Relu 的行为,我可能遗漏了什么?也许是梯度问题?
抱歉我无法在这里附上图表。
您不能在输出层中使用 relu,对于回归问题,它必须是线性的。
你好,
我有一个模型,它由一个包含 100 个单元的 LSTM 层和一个单神经元的输出层组成
输入 ==> LSTM (100) ==> 输出 (1)
输入是一个时间步长,它有六个特征,我只预测其中一个特征。
我的问题是
例如,我的第一个时间步长是 [],它是否作为所有 100 个 LSTM 单元的输入,然后生成的向量 (h) 用于通过方程找到最终输出
输出 = h * W + 稠密层的偏差?
还是每个时间步长只进入一个 LSTM 单元?
LSTM 将在序列结束时为层中的每个单元输出一个值,例如,一个长度为 100 的向量。
也许这会有帮助。
https://machinelearning.org.cn/faq/single-faq/how-is-data-processed-by-an-lstm
感谢您如此有用的文章!
您能分享一下这个示例是为哪个版本的 TensorFlow 编写的信息吗?
我运行它时遇到了大量错误。
我使用的是 Python 3.9
Keras 2.1.2
Keras-Applications 1.0.8
Keras-Preprocessing 1.1.2
tensorboard 2.4.1
tensorboard-plugin-wit 1.8.0
tensorflow 0.12.0
tensorflow-estimator 1.13.0
我的错误摘要是:TypeError: Expected int32, got list containing Tensors of type ‘_Message’ instead。
通过将 python 版本从 3.9 降到 3.8 解决了,tensorflow 仍然不支持 3.9。再次感谢您的工作!
同意!好建议!
我建议使用最新版本的 Keras 和 TensorFlow 库。