Keras中的时间分布层TimeDistributed Layer使用教程

     

      TimeDistributed官方文档在这里。 

      长短期网络(LSTM)是一种流行且功能强大的递归神经网络(RNN)。

       即使使用像Python的Keras深度学习库中提供的那样定义明确且“易于使用”的接口,它们也很难配置并应用于任意序列预测问题。

       Keras中出现此困难的原因之一是使用了TimeDistributed包装器层,并且需要一些LSTM层返回序列而不是单个值。

        在本教程中,您将发现配置LSTM网络进行序列预测的不同方法,TimeDistributed层扮演的角色以及确切的使用方法。

       完成本教程后,您将知道:

  • 如何设计一对一的LSTM进行序列预测
  • 如何设计没有时间分布层的多对一LSTM序列预测。
  • 如何设计多对多LSTM以便使用TimeDistributed Layer进行序列预测。
    在我的新书中找到了如何开发LSTM(例如,堆叠式,双向,CNN-LSTM,Encoder-Decoder seq2seq等)以及更多内容,其中包括14个循序渐进的教程和完整代码。

How to Use the TimeDistributed Layer for Long Short-Term Memory Networks in Python

教程概述

      本教程分为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的多种类型网络的数据建模分析教程,欢迎感兴趣的同学一起交流学习共同进步。

发布了521 篇原创文章 · 获赞 490 · 访问量 323万+

猜你喜欢

转载自blog.csdn.net/Together_CZ/article/details/103596877
今日推荐