TimeDistributed官方文档在这里。
长短期网络(LSTM)是一种流行且功能强大的递归神经网络(RNN)。
即使使用像Python的Keras深度学习库中提供的那样定义明确且“易于使用”的接口,它们也很难配置并应用于任意序列预测问题。
Keras中出现此困难的原因之一是使用了TimeDistributed包装器层,并且需要一些LSTM层返回序列而不是单个值。
在本教程中,您将发现配置LSTM网络进行序列预测的不同方法,TimeDistributed层扮演的角色以及确切的使用方法。
完成本教程后,您将知道:
- 如何设计一对一的LSTM进行序列预测
- 如何设计没有时间分布层的多对一LSTM序列预测。
- 如何设计多对多LSTM以便使用TimeDistributed Layer进行序列预测。
在我的新书中找到了如何开发LSTM(例如,堆叠式,双向,CNN-LSTM,Encoder-Decoder seq2seq等)以及更多内容,其中包括14个循序渐进的教程和完整代码。
教程概述
本教程分为5个部分。 他们是:
时间分布层
序列学习问题
一对一的LSTM用于序列预测
用于序列预测的多对一LSTM(无时间分布)
用于序列预测的多对多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.2 0.4 0.6 0.8]
该示例是可配置的,如果您愿意,以后可以自己播放更长/更短的序列。 在评论中让我知道您的结果。
输入输出对如下:
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优化算法和均方误差损失函数进行拟合。
批大小设置为时期中的样本数,以避免必须使LSTM有状态并手动管理状态重置,尽管这样做很容易,以便在将每个样本显示到网络后更新权重。
下面提供了完整的代码清单:
from numpy import array
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# prepare sequence
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)
# define LSTM configuration
n_neurons = length
n_batch = length
n_epoch = 1000
# create 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())
# train LSTM
model.fit(X, y, epochs=n_epoch, batch_size=n_batch, verbose=2)
# evaluate
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 Shape Param #
=================================================================
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(无时间分布)
在本节中,我们将开发LSTM以一次全部输出序列,尽管没有TimeDistributed包装器层。
LSTM的输入必须是三维的。 我们可以使用1个样本,5个时间步长和1个特征将2D序列重塑为3D序列。 我们将输出定义为具有5个功能的1个样本。
X = seq.reshape(1, 5, 1)
y = seq.reshape(1, 5)
立刻,您可以看到问题定义必须稍加调整,以支持没有TimeDistributed包装器的网络进行序列预测。 具体来说,输出一个向量,而不是一次建立一个输出序列。 两者之间的差异可能听起来有些微,但了解TimeDistributed包装器的作用很重要。
我们将模型定义为具有5个时间步长的一个输入。 第一个隐藏层将是具有5个单位的LSTM。 输出层是具有5个神经元的完全连接层。
# create 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。
# train 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
# prepare sequence
length = 5
seq = array([i/float(length) for i in range(length)])
X = seq.reshape(1, length, 1)
y = seq.reshape(1, length)
# define LSTM configuration
n_neurons = length
n_batch = 1
n_epoch = 500
# create 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())
# train LSTM
model.fit(X, y, epochs=n_epoch, batch_size=n_batch, verbose=2)
# evaluate
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 Shape Param #
=================================================================
lstm_1 (LSTM) (None, 5) 140
_________________________________________________________________
dense_1 (Dense) (None, 5) 30
=================================================================
Total params: 170.0
Trainable params: 170
Non-trainable params: 0.0
_________________________________________________________________
该模型是合适的,在最终确定并打印预测序列之前先打印损失信息。
序列可以正确地再现,但可以单段显示,而不是逐步输入数据。 我们可能已将密集层用作第一隐藏层而不是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个时间步。
通过将相同的密集层(相同的权重)应用于LSTM输出一次一次,TimeDistributed实现了这一技巧。 这样,输出层只需要与每个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
# prepare sequence
length = 5
seq = array([i/float(length) for i in range(length)])
X = seq.reshape(1, length, 1)
y = seq.reshape(1, length, 1)
# define LSTM configuration
n_neurons = length
n_batch = 1
n_epoch = 1000
# create 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())
# train LSTM
model.fit(X, y, epochs=n_epoch, batch_size=n_batch, verbose=2)
# evaluate
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 Shape Param #
=================================================================
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
在第一个示例中,我们可以认为使用时间步长和TimeDistributed层对问题进行框架化是实现一对一网络的更紧凑方式。 在更大范围内,它甚至可能更有效(时空或时空)。
很高兴有时间可以亲自翻译和实践一下大神Jason这篇基于LSTM的多种类型网络的数据建模分析教程,欢迎感兴趣的同学一起交流学习共同进步。