RNN,LSTM中如何使用TimeDistributed包装层,代码示例
本文介绍了LSTM网络中的TimeDistributed包装层,代码演示了具有TimeDistributed层的LSTM网络配置方法。
演示了一对一,多对一,多对多,三种不同的预测方法如何配置。
在对多对一预测中用了不配置TimeDistributed的方法,在多对多预测中使用了TimeDistributed层。
对代码的解析在代码注释中
源码地址:
https://github.com/yangwohenmai/LSTM/tree/master/%E9%95%BF%E7%9F%AD%E6%9C%9F%E8%AE%B0%E5%BF%86(LSTM)/LSTM%E7%9A%84%E7%89%B9%E6%80%A7/%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E5%AF%B9%E5%92%8CTimeDistributed
正文:
长期短期网络或LSTM是一种流行且功能强大的递归神经网络或RNN。
它们可能很难配置并应用于任意序列预测问题,即使使用定义良好且“易于使用”的接口,如Python中的Keras深度学习库中提供的那些接口也是如此。
在Keras中遇到这种困难的一个原因是使用了TimeDistributed包装层,并且需要一些LSTM层来返回序列而不是单个值。
在本教程中,您将发现为序列预测配置LSTM网络的不同方法,TimeDistributed层所扮演的角色以及如何使用它。
TimeDistributed 包装层
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]
该示例是可配置的,如果您愿意,您可以稍后自己玩更长/更短的序列。请在评论中告诉我您的结果。
用于序列预测的一对一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优化算法和均方误差损失函数。
批量大小设置为时期中的样本数量,以避免必须使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)])
# 定义输入为:5个样本,一个步长,一个特征值
"""
[[[0. ]]
[[0.2]]
[[0.4]]
[[0.6]]
[[0.8]]]
"""
X = seq.reshape(5, 1, 1)
print(X)
# 定义输出为:5个样本,每个样本1个特征值
"""
[[0. ]
[0.2]
[0.4]
[0.6]
[0.8]]
"""
y = seq.reshape(5, 1)
print(y)
# define LSTM configuration
n_neurons = 5
n_batch = 5
# create LSTM
model = Sequential()
# 输入类型为1个步长和1个特征值
model.add(LSTM(5, 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=1000, 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(没有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个神经元。
# 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
# 输入为1个样本,5个步长,1个特征值
length = 5
seq = array([i/float(length) for i in range(length)])
"""
[[[0. ]
[0.2]
[0.4]
[0.6]
[0.8]]]
"""
X = seq.reshape(1, 5, 1)
print(X)
# 输出为五个特征值的一个样本
"""
[[0. 0.2 0.4 0.6 0.8]]
"""
y = seq.reshape(1, 5)
print(y)
# define LSTM configuration
n_neurons = length
n_batch = 1
# create LSTM
model = Sequential()
# 输入类型为5个步长,1个特征值
model.add(LSTM(5, input_shape=(5, 1)))
model.add(Dense(5))
model.compile(loss='mean_squared_error', optimizer='adam')
print(model.summary())
# train LSTM
model.fit(X, y, epochs=500, 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
_________________________________________________________________
该模型适合,在最终确定和打印预测序列之前打印损失信息。
序列被正确再现,但是作为单个部分而不是逐步通过输入数据。我们可能使用Dense层作为第一个隐藏层而不是LSTM,因为LSTM的这种使用并没有充分利用它们完整的序列学习和处理能力。
0.0
0.2
0.4
0.6
0.8
用于序列预测的多对多LSTM(具有TimeDistributed)
在本节中,我们将使用TimeDistributed层来处理LSTM隐藏层的输出。
使用TimeDistributed包装层时需要记住两个关键点:
- 输入必须(至少)为3D。这通常意味着您需要在TimeDistributed wrapped Dense层之前配置最后一个LSTM层以返回序列(例如,将“return_sequences”参数设置为“True”)。
- 输出将是3D。这意味着如果TimeDistributed wrapped 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
# prepare sequence
length = 5
seq = array([i/float(length) for i in range(length)])
# 输入为1个样本,5个步长,1个特征值
X = seq.reshape(1, 5, 1)
# 输出为1个样本,5个步长,1个特征值
y = seq.reshape(1, 5, 1)
# define LSTM configuration
n_batch = 1
# create LSTM
model = Sequential()
# 输入类型为5个步长和1个特征值,return_sequences=True返回整个序列
model.add(LSTM(5, input_shape=(5, 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=1000, 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层视为在第一个示例中实现一对一网络的更紧凑方式。它甚至可能在更大规模上更有效(空间或时间)。
参考:
https://blog.csdn.net/yangwohenmai1/article/details/84981645
https://machinelearningmastery.com/timedistributed-layer-for-long-short-term-memory-networks-in-python/
RNN,LSTM中如何使用TimeDistributed包装层,代码示例的更多相关文章
- 简:Spring中Bean的生命周期及代码示例
(重要:spring bean的生命周期. spring的bean周期,装配.看过spring 源码吗?(把容器启动过程说了一遍,xml解析,bean装载,bean缓存等)) 完整的生命周期概述(牢记 ...
- 浅谈 PHP 中的多种加密技术及代码示例
信息加密技术的分类 单项散列加密技术(不可逆的加密) 属于摘要算法,不是一种加密算法,作用是把任意长的输入字符串变化成固定长的输出串的一种函数 MD5 string md5 ( string $str ...
- c/c++中define用法详解及代码示例
https://blog.csdn.net/u012611878/article/details/52534622 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog. ...
- python---Numpy模块中创建数组的常用方式代码示例
要机器学习,这方面内容不可少. import numpy as np import time # 对比标准python实现和numpy实现的性能差异 def sum_trad(): start = t ...
- C#中构建多线程应用程序[转] ----代码示例
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- ECMAScript 6 中的快捷语法汇总及代码示例
对于每个 JavaScript 开发人员,快捷语法都是必备技能之一,下面就集中介绍这些快捷语法. 三元运算符 传统写法 const x = 20; let answer; if (x > 10) ...
- go中的方法以及自定义类型代码示例
package main import "fmt" type user struct { name string age int sex string } type admin s ...
- 深度学习中的序列模型演变及学习笔记(含RNN/LSTM/GRU/Seq2Seq/Attention机制)
[说在前面]本人博客新手一枚,象牙塔的老白,职业场的小白.以下内容仅为个人见解,欢迎批评指正,不喜勿喷![认真看图][认真看图] [补充说明]深度学习中的序列模型已经广泛应用于自然语言处理(例如机器翻 ...
- tensorflow学习之(十一)RNN+LSTM神经网络的构造
#RNN 循环神经网络 import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data tf.se ...
随机推荐
- Java匹马行天下——开篇
个人感言: 匹马行天下是我高中时候看过一部叫<九鼎记>的小说中的其中一个大章节标题,在整个这一章中,讲的是是主人公滕青山历经艰险,又心如磐石,一心修行,最后巅峰归来的故事.现在回想,依旧心 ...
- LeetCode: 106_Construct Binary Tree from Inorder and Postorder Traversal | 根据中序和后序遍历构建二叉树 | Medium
要求:根据中序和后序遍历序列构建一棵二叉树 代码如下: struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int ...
- Spring Boot功能实战
添加web功能启动器 添加了Spring Boot基础依赖后,如要使用web mvc功能,只需要添加如下启动器即可,Spring Boot会自动装配web功能. <dependencies> ...
- 14-使用glusterfs做持久化存储
使用glusterfs做持久化存储 我们复用kubernetes的三台主机做glusterfs存储. 以下步骤参考自:https://www.xf80.com/2017/04/21/kubernete ...
- 【2019北京集训测试赛(七)】 操作 分治+FFT+生成函数
题目大意:你有$n$个操作和一个初始为$0$的变量$x$. 第$i$个操作为:以$P_i$的概率给$x$加上$A_i$,剩下$1-P_i$的概率给$x$乘上$B_i$. 你袭击生成了一个长度为$n$的 ...
- PHP使用Zend Opcache之优化加速和缓存清理总结
简介 字节码缓存不是php的新特性,有很多独立性的扩展可以实现缓存,比如PHP Cache(APC),eAccelerator,ionCube和XCache等等.但是到目前为止,这些独立的扩展并没有集 ...
- SqlServer 更改数据库名称
1.首先选中需要更改数据库右击属性找到文件, 此处可直接修改数据库逻辑名称 2.选中数据库右击选择重命名修改数据库名称. 3.将数据库进行分离,找到数据库文件mdf与ldf文件,直接更改文件名称 4. ...
- Fiddler怎样抓取手机的包
Fiddler作为代理服务器,可以拦截到手机发出的请求,再经过Fiddler发送到服务器,获取到服务器响应的数据,这个过程,只要设置配置好了,使用过程不受影响. 要想抓取到手机的包,先要给手机设置一个 ...
- 链表的创建(C语言实现)
学习链表之前,我们要知道为什么要引入链表. C语言中的数组使用之前,我们必须要定义数组的大小.但是当我们不知道数据个数(或者很大)时,定义数组大小就成了一个困扰,而且对于这么多数据的处理也会很麻烦.所 ...
- java面试题:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
答:是值传递.Java编程语言只有值传递参数. 当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本.指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用 ...