走进深度学习的世界|LSTM业务 Keras 中使用 TimeDistributed 层 ,迷你教程


文章目录

  • 内容介绍
  • 时间分布式层
  • 序列学习问题
  • LSTM 用于序列预测的一对一
  • LSTM用于序列预测的多对一(无 TimeDistributed)
  • LSTM用于序列预测的多对多 (使用 TimeDistributed)

内容介绍 长短期网络或 LSTM 是一种流行且功能强大的循环神经网络 (RNN)。
即使使用定义明确且“易于使用”的接口(如 Python 中的 Keras 深度学习库中提供的接口),它们也很难配置和应用于任意序列预测问题。
Keras 中出现这种困难的一个原因是使用了 TimeDistributed 包装层,并且需要一些 LSTM 层返回序列而不是单个值。
在本教程中,您将发现配置 LSTM 网络以进行序列预测的不同方法、TimeDistributed 层所扮演的角色以及如何使用它。
走进深度学习的世界|LSTM业务 Keras 中使用 TimeDistributed 层 ,迷你教程
文章图片

时间分布式层 LSTM 功能强大,但难以使用且难以配置,尤其是对于初学者而言。
增加的复杂性是TimeDistributed层(以及之前的TimeDistributedDense层),它被神秘地描述为层包装器:
这个包装器允许我们将一个层应用于输入的每个时间切片。
您应该如何以及何时将这个包装器与 LSTM 一起使用?
当您在 Keras GitHub 问题和 StackOverflow 上搜索有关包装层的讨论时,混淆会更加复杂。
例如,在“何时以及如何使用 TimeDistributedDense ”一期中,fchollet(Keras 的作者)解释说:
TimeDistributedDense 将相同的 Dense(全连接)操作应用于 3D 张量的每个时间步长。
如果您已经了解 TimeDistributed 层的用途以及何时使用它,这非常有意义,但对初学者来说根本没有帮助。
本教程旨在消除在 LSTM 中使用 TimeDistributed 包装器以及您可以检查、运行和使用的工作示例来帮助您具体理解的混淆。
序列学习问题 我们将使用一个简单的序列学习问题来演示 TimeDistributed 层。
在这个问题中,序列 [0.0, 0.2, 0.4, 0.6, 0.8] 将作为输入一次给出一项,并且必须依次作为输出返回,一次一项。
把它想象成学习一个简单的回声程序。我们给 0.0 作为输入,我们希望看到 0.0 作为输出,对序列中的每个项目重复。
我们可以直接生成这个序列如下:
from numpy import array length = 5 seq = array([i/float(length) for i in range(length)]) print(seq)

运行这个例子打印生成的序列:
[ 0.0.20.40.60.8]

该示例是可配置的,如果您愿意,您可以稍后自己玩更长/更短的序列。在评论中让我知道你的结果。
LSTM 用于序列预测的一对一 在我们深入研究之前,重要的是要证明这个序列学习问题可以分段学习。
也就是说,我们可以将问题重构为序列中每个项目的输入-输出对的数据集。给定 0,网络应该输出 0,给定 0.2,网络必须输出 0.2,依此类推。
这是问题的最简单表述,需要将序列拆分为输入-输出对,并且一次一步地预测序列并收集到网络外部。
输入输出对如下:
X,y 0.0, 0.0 0.2, 0.2 0.4, 0.4 0.6, 0.6 0.8, 0.8

LSTM 的输入必须是三维的。我们可以将 2D 序列重塑为具有 5 个样本、1 个时间步和 1 个特征的 3D 序列。我们将输出定义为具有 1 个特征的 5 个样本。
X = seq.reshape(5, 1, 1) y = seq.reshape(5, 1)

我们将网络模型定义为具有 1 个输入和 1 个时间步长。第一个隐藏层将是一个具有 5 个单元的 LSTM。输出层是具有 1 个输出的全连接层。
该模型将采用高效的 ADAM 优化算法和均方误差损失函数进行拟合。
批量大小设置为 epoch 中的样本数,以避免必须使 LSTM 有状态并手动管理状态重置,尽管这可以很容易地完成,以便在每个样本显示给网络后更新权重。
完整的代码清单如下:
from numpy import array from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM # 准备序列数据 length = 5 seq = array([i/float(length) for i in range(length)]) X = seq.reshape(len(seq), 1, 1) y = seq.reshape(len(seq), 1) # 定义LSTM配置 n_neurons = length n_batch = length n_epoch = 1000 # 创建LSTM model = Sequential() model.add(LSTM(n_neurons, input_shape=(1, 1))) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') print(model.summary()) # 训练LSTM model.fit(X, y, epochs=n_epoch, batch_size=n_batch, verbose=2) # 评估 result = model.predict(X, batch_size=n_batch, verbose=0) for value in result: print('%.1f' % value)

运行示例首先打印配置网络的结构。
我们可以看到 LSTM 层有 140 个参数。这是根据输入数量 (1) 和输出数量(隐藏层中的 5 个单元为 5)计算的,如下所示:
n = 4 * ((inputs + 1) * outputs + outputs^2) n = 4 * ((1 + 1) * 5 + 5^2) n = 4 * 35 n = 140

我们还可以看到,全连接层只有 6 个参数,分别是输入数量(5 个代表前一层的 5 个输入)、输出数量(1 个代表该层中的 1 个神经元)和偏置。
n = inputs * outputs + outputs n = 5 * 1 + 1 n = 6 _________________________________________________________________ Layer (type)Output ShapeParam # ================================================================= lstm_1 (LSTM)(None, 1, 5)140 _________________________________________________________________ dense_1 (Dense)(None, 1, 1)6 ================================================================= Total params: 146.0 Trainable params: 146 Non-trainable params: 0.0 _________________________________________________________________

网络正确地学习了预测问题。
0.0 0.2 0.4 0.6 0.8

LSTM用于序列预测的多对一(无 TimeDistributed) 在本节中,我们开发了一个 LSTM 来一次性输出所有序列,尽管没有 TimeDistributed 包装层。
LSTM 的输入必须是三维的。我们可以将 2D 序列重塑为具有 1 个样本、5 个时间步长和 1 个特征的 3D 序列。我们将输出定义为具有 5 个特征的 1 个样本。
X = seq.reshape(1, 5, 1) y = seq.reshape(1, 5)

立即,您可以看到必须稍微调整问题定义以支持没有 TimeDistributed 包装器的序列预测网络。具体来说,输出一个向量,而不是一步一步地构建一个输出序列。差异可能听起来很微妙,但了解 TimeDistributed 包装器的作用很重要。
我们将模型定义为具有 5 个时间步长的一个输入。第一个隐藏层将是一个具有 5 个单元的 LSTM。输出层是一个全连接层,有 5 个神经元。
# 创建LSTM model = Sequential() model.add(LSTM(5, input_shape=(5, 1))) model.add(Dense(length)) model.compile(loss='mean_squared_error', optimizer='adam') print(model.summary())

接下来,我们只为训练数据集中的单个样本拟合 500 个时期和批量大小为 1 的模型。
# 训练LSTM model.fit(X, y, epochs=500, batch_size=1, verbose=2)

将所有这些放在一起,下面提供了完整的代码清单。
from numpy import array from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM # 准备序列数据 length = 5 seq = array([i/float(length) for i in range(length)]) X = seq.reshape(1, length, 1) y = seq.reshape(1, length) # 定义LSTM配置 n_neurons = length n_batch = 1 n_epoch = 500 # 创建LSTM model = Sequential() model.add(LSTM(n_neurons, input_shape=(length, 1))) model.add(Dense(length)) model.compile(loss='mean_squared_error', optimizer='adam') print(model.summary()) # 训练LSTM model.fit(X, y, epochs=n_epoch, batch_size=n_batch, verbose=2) # 评估 result = model.predict(X, batch_size=n_batch, verbose=0) for value in result[0,:]: print('%.1f' % value)

运行该示例首先打印已配置网络的摘要。
我们可以看到 LSTM 层和上一节一样有 140 个参数。
LSTM 单元已被削弱,每个单元将输出一个值,提供一个包含 5 个值的向量作为全连接层的输入。时间维度或序列信息已被丢弃并折叠为 5 个值的向量。
我们可以看到全连接输出层有 5 个输入,预计输出 5 个值。我们可以将要学习的 30 个权重计算如下:
n = inputs * outputs + outputs n = 5 * 5 + 5 n = 30

网络总结报告如下:
_________________________________________________________________ Layer (type)Output ShapeParam # ================================================================= lstm_1 (LSTM)(None, 5)140 _________________________________________________________________ dense_1 (Dense)(None, 5)30 ================================================================= Total params: 170.0 Trainable params: 170 Non-trainable params: 0.0 _________________________________________________________________

模型是拟合的,在最终确定和打印预测序列之前打印损失信息。
该序列被正确再现,但作为单个片段而不是通过输入数据逐步再现。我们可能使用 Dense 层作为第一个隐藏层而不是 LSTM,因为 LSTM 的这种用法没有充分利用它们的全部序列学习和处理能力。
0.0 0.2 0.4 0.6 0.8

LSTM用于序列预测的多对多 (使用 TimeDistributed) 在本节中,我们将使用 TimeDistributed 层来处理来自 LSTM 隐藏层的输出。
使用 TimeDistributed 包装层时需要记住两个关键点:
输入必须(至少)是 3D。这通常意味着您需要在 TimeDistributed 包裹的密集层之前配置最后一个 LSTM 层以返回序列(例如将“return_sequences”参数设置为“True”)。
输出将是 3D。这意味着,如果您的 TimeDistributed 包裹的 Dense 层是您的输出层,并且您正在预测一个序列,则您需要将 y 数组的大小调整为 3D 向量。
我们可以将输出的形状定义为具有 1 个样本、5 个时间步和 1 个特征,就像输入序列一样,如下所示:
y = seq.reshape(1, length, 1)

我们可以通过将“ return_sequences ”参数设置为 true来定义 LSTM 隐藏层以返回序列而不是单个值。
model.add(LSTM(n_neurons, input_shape=(length, 1), return_sequences=True))

这具有每个 LSTM 单元返回 5 个输出的序列的效果,输入数据中的每个时间步一个,而不是前一个示例中的单个输出值。
我们还可以使用输出层上的 TimeDistributed 将一个完全连接的 Dense 层与单个输出包装在一起。
model.add(TimeDistributed(Dense(1)))

输出层中的单个输出值是关键。它强调了我们打算为输入中的每个时间步从序列中输出一个时间步。碰巧我们将一次处理输入序列的 5 个时间步。
TimeDistributed 通过将相同的 Dense 层(相同的权重)应用于 LSTM 输出一次一个时间步来实现这一技巧。这样,输出层只需要一个连接到每个 LSTM 单元(加上一个偏置)。
出于这个原因,需要增加训练时期的数量以解决较小的网络容量。我将它从 500 翻倍到 1000 以匹配第一个一对一示例。
将这些放在一起,下面提供了完整的代码清单。
from numpy import array from keras.models import Sequential from keras.layers import Dense from keras.layers import TimeDistributed from keras.layers import LSTM # 准备序列数据 length = 5 seq = array([i/float(length) for i in range(length)]) X = seq.reshape(1, length, 1) y = seq.reshape(1, length, 1) # 定义LSTM配置 n_neurons = length n_batch = 1 n_epoch = 1000 # 创建LSTM model = Sequential() model.add(LSTM(n_neurons, input_shape=(length, 1), return_sequences=True)) model.add(TimeDistributed(Dense(1))) model.compile(loss='mean_squared_error', optimizer='adam') print(model.summary()) # 训练LSTM model.fit(X, y, epochs=n_epoch, batch_size=n_batch, verbose=2) # 评估 result = model.predict(X, batch_size=n_batch, verbose=0) for value in result[0,:,0]: print('%.1f' % value)

运行示例,我们可以看到配置的网络结构。
我们可以看到,和前面的例子一样,我们在 LSTM 隐藏层中有 140 个参数。
全连接输出层是一个非常不同的故事。事实上,它与一对一的例子完全匹配。一个神经元对前一层中的每个 LSTM 单元有一个权重,加上一个用于偏置输入的权重。
这有两件重要的事情:
允许按照定义的方式构建和学习问题,即一个输入到一个输出,使每个时间步的内部过程保持独立。
通过需要更少的权重来简化网络,以便一次只处理一个时间步。
一个更简单的全连接层应用于前一层提供的序列中的每个时间步,以构建输出序列。
_________________________________________________________________ Layer (type)Output ShapeParam # ================================================================= lstm_1 (LSTM)(None, 5, 5)140 _________________________________________________________________ time_distributed_1 (TimeDist (None, 5, 1)6 ================================================================= Total params: 146.0 Trainable params: 146 Non-trainable params: 0.0 _________________________________________________________________

同样,网络学习序列。
0.0 0.2 0.4 0.6 0.8

【走进深度学习的世界|LSTM业务 Keras 中使用 TimeDistributed 层 ,迷你教程】在第一个示例中,我们可以将问题的框架与时间步长和 TimeDistributed 层视为实现一对一网络的更紧凑的方式。在更大的范围内,它甚至可能更有效(空间或时间方面)。

    推荐阅读