lstm结构图_【他山之石】LSTM模型结构的可视化

“他山之石,可以攻玉”,站在巨人的肩膀才能看得更高,走得更远。在科研的道路上,更需借助东风才能更快前行。为此,我们特别搜集整理了一些实用的代码链接,数据集,软件,编程技巧等,开辟“他山之石”专栏,助你乘风破浪,一路奋勇向前,敬请关注。

作者: 知乎—master苏

地址:https://www.zhihu.com/people/zhenyu22

最近在学习LSTM应用在时间序列的预测上,但是遇到一个很大的问题就是LSTM在传统BP网络上加上时间步后,其结构就很难理解了,同时其输入输出数据格式也很难理解,网络上有很多介绍LSTM结构的文章,但是都不直观,对初学者是非常不友好的。我也是苦苦冥思很久,看了很多资料和网友分享的LSTM结构图形才明白其中的玄机。

01

传统的BP网络和CNN网络

BP网络和CNN网络没有时间维,和传统的机器学习算法理解起来相差无几,CNN在处理彩色图像的3通道时,也可以理解为叠加多层,图形的三维矩阵当做空间的切片即可理解,写代码的时候照着图形一层层叠加即可。如下图是一个普通的BP网络和CNN网络。
fbb895468e36beef4b61fbe1de87ac1d.png BP网络

c7b3eb930fdce93fc26c4b2bcf3c4d60.png

CNN网络 图中的隐含层、卷积层、池化层、全连接层等,都是实际存在的,一层层前后叠加,在空间上很好理解,因此在写代码的时候,基本就是看图写代码,比如用keras就是:
# 示例代码,没有实际意义model = Sequential()model.add(Conv2D(32, (3, 3), activation='relu'))  # 添加卷积层model.add(MaxPooling2D(pool_size=(2, 2)))         # 添加池化层model.add(Dropout(0.25))                          # 添加dropout层model.add(Conv2D(32, (3, 3), activation='relu'))  # 添加卷积层model.add(MaxPooling2D(pool_size=(2, 2)))         # 添加池化层model.add(Dropout(0.25))                          # 添加dropout层....   # 添加其他卷积操作model.add(Flatten())                            # 拉平三维数组为2维数组model.add(Dense(256, activation='relu'))        添加普通的全连接层model.add(Dropout(0.5))model.add(Dense(10, activation='softmax'))....  # 训练网络

02

LSTM网络

当我们在网络上搜索看LSTM结构的时候,看最多的是下面这张图:

0c4df1da5ecb02d187207eef23360363.png

RNN网络 这是RNN循环神经网络经典的结构图,LSTM只是对隐含层节点A做了改进,整体结构不变,因此本文讨论的也是这个结构的可视化问题。 中间的A节点隐含层,左边是表示只有一层隐含层的LSTM网络,所谓LSTM循环神经网络就是在时间轴上的循环利用,在时间轴上展开后得到右图。 看左图,很多同学以为LSTM是单输入、单输出,只有一个隐含神经元的网络结构,看右图,以为LSTM是多输入、多输出,有多个隐含神经元的网络结构,A的数量就是隐含层节点数量。 WTH?思维转不过来啊。这就是传统网络和空间结构的思维。 实际上,右图中,我们看Xt表示序列,下标t是时间轴,所以,A的数量表示的是时间轴的长度,是同一个神经元在不同时刻的状态(Ht),不是隐含层神经元个数。 我们知道,LSTM网络在训练时会使用上一时刻的信息,加上本次时刻的输入信息来共同训练。 举个简单的例子:在第一天我生病了(初始状态H0),然后吃药(利用输入信息X1训练网络),第二天好转但是没有完全好(H1),再吃药(X2),病情得到好转(H2),如此循环往复知道病情好转。因此,输入Xt是吃药,时间轴T是吃多天的药,隐含层状态是病情状况。因此我还是我,只是不同状态的我。 实际上,LSTM的网络是这样的:
d0a2e0f624b869805af54d5e3776ed0f.png LSTM网络结构
上面的图表示包含2个隐含层的LSTM网络,在T=1时刻看,它是一个普通的BP网络,在T=2时刻看也是一个普通的BP网络,只是沿时间轴展开后,T=1训练的隐含层信息H,C会被传递到下一个时刻T=2,如下图所示。上图中向右的五个常常的箭头,所的也是隐含层状态在时间轴上的传递。

29f3b3c0bf33d0641749e4f0aae15769.png

注意,图中H表示隐藏层状态,C是遗忘门,后面会讲解它们的维度。

03

LSTM的输入结构

为了更好理解LSTM结构,还必须理解LSTM的数据输入情况。仿照3通道图像的样子,在加上时间轴后的多样本的多特征的不同时刻的数据立方体如下图所示:
aefb107da86050a38f6ebe0b9548e86c.png 三维数据立方体
右边的图是我们常见模型的输入,比如XGBOOST,lightGBM,决策树等模型,输入的数据格式都是这种(N*F)的矩阵,而左边是加上时间轴后的数据立方体,也就是时间轴上的切片,它的维度是(N*T*F),第一维度是样本数,第二维度是时间,第三维度是特征数,如下图所示:
e49ba3c43edb560a501c7a54f73ca3e5.png
这样的数据立方体很多,比如天气预报数据,把样本理解成城市,时间轴是日期,特征是天气相关的降雨风速PM2.5等,这个数据立方体就很好理解了。在NLP里面,一句话会被embedding成一个矩阵,词与词的顺序是时间轴T,索引多个句子的embedding三维矩阵如下图所示:
3c23d3cadd471e804d5afc5d04b82afc.png

04

pytorch中的LSTM

4.1 pytorch中定义的LSTM模型

pytorch中定义的LSTM模型的参数如下
class torch.nn.LSTM(*args, **kwargs)参数有:    input_size:x的特征维度    hidden_size:隐藏层的特征维度    num_layers:lstm隐层的层数,默认为1    bias:False则bihbih=0和bhhbhh=0. 默认为True    batch_first:True则输入输出的数据格式为 (batch, seq, feature)    dropout:除最后一层,每一层的输出都进行dropout,默认为: 0    bidirectional:True则为双向lstm默认为False
结合前面的图形,我们一个个看。 (1)input_size:x的特征维度,就是数据立方体中的F,在NLP中就是一个词被embedding后的向量长度,如下图所示:

7d1453e70ad767b4ae3aaa4c295c48ca.png

(2)hidden_size:隐藏层的特征维度(隐藏层神经元个数),如下图所示,我们有两个隐含层,每个隐藏层的特征维度都是5。注意,非双向LSTM的输出维度等于隐藏层的特征维度。

f585f7a2f2bb653d678854dda37a3317.png

(3)num_layers:lstm隐层的层数,上面的图我们定义了2个隐藏层。 (4)batch_first:用于定义输入输出维度,后面再讲。 (5)bidirectional:是否是双向循环神经网络,如下图是一个双向循环神经网络,因此在使用双向LSTM的时候我需要特别注意,正向传播的时候有(Ht, Ct),反向传播也有(Ht', Ct'),前面我们说了非双向LSTM的输出维度等于隐藏层的特征维度,而双向LSTM的输出维度是隐含层特征数*2,而且H,C的维度是时间轴长度*2。

34de970d532b36c2ad7a2b2770bd3521.png

4.2 喂给LSTM的数据格式

pytorch中LSTM的输入数据格式默认如下:
input(seq_len, batch, input_size)参数有:    seq_len:序列长度,在NLP中就是句子长度,一般都会用pad_sequence补齐长度    batch:每次喂给网络的数据条数,在NLP中就是一次喂给网络多少个句子    input_size:特征维度,和前面定义网络结构的input_size一致。
前面也说到,如果LSTM的参数 batch_first=True,则要求输入的格式是:
input(batch, seq_len, input_size)
刚好调换前面两个参数的位置。其实这是比较好理解的数据形式,下面以NLP中的embedding向量说明如何构造LSTM的输入。 之前我们的embedding矩阵如下图:
96b2daa7abcd3e596017933d6ac251a7.png
如果把batch放在第一位,则三维矩阵的形式如下:
5fe09bedb1e6fa26db4201fa31d7282e.png
其转换过程如下图所示:
cd45acc5c0cbe4396458af1afbfcede3.png
看懂了吗,这就是输入数据的格式,是不是很简单。 LSTM的另外两个输入是 h0 和 c0,可以理解成网络的初始化参数,用随机数生成即可。
h0(num_layers * num_directions, batch, hidden_size)c0(num_layers * num_directions, batch, hidden_size)参数:    num_layers:隐藏层数    num_directions:如果是单向循环网络,则num_directions=1,双向则num_directions=2    batch:输入数据的batch    hidden_size:隐藏层神经元个数
注意,如果我们定义的input格式是:
input(batch, seq_len, input_size)
则H和C的格式也是要变的:
h0(batc,num_layers * num_directions, h, hidden_size)c0(batc,num_layers * num_directions, h, hidden_size)

4.3 LSTM的output格式

LSTM的输出是一个tuple,如下:
output,(ht, ct) = net(input)    output: 最后一个状态的隐藏层的神经元输出    ht:最后一个状态的隐含层的状态值    ct:最后一个状态的隐含层的遗忘门值
output的默认维度是:
output(seq_len, batch, hidden_size * num_directions)ht(num_layers * num_directions, batch, hidden_size)ct(num_layers * num_directions, batch, hidden_size)
和input的情况类似,如果我们前面定义的input格式是:
input(batch, seq_len, input_size)
则ht和ct的格式也是要变的:
ht(batc,num_layers * num_directions, h, hidden_size)ct(batc,num_layers * num_directions, h, hidden_size)
说了这么多,我们回过头来看看ht和ct在哪里,请看下图:

8961a113213621d2e11ec4b6ba4fdbc3.png

output在哪里?请看下图:

a7b64f4937bc972652778eaf5d87a10f.png

05

LSTM和其他网络组合

还记得吗,output的维度等于隐藏层神经元的个数,即hidden_size,在一些时间序列的预测中,会在output后,接上一个全连接层,全连接层的输入维度等于LSTM的hidden_size,之后的网络处理就和BP网络相同了,如下图:
4421943be9c59867f8e9168e8a435c44.png
用pytorch实现上面的结构:
import torchfrom torch import nnclass RegLSTM(nn.Module):    def __init__(self):        super(RegLSTM, self).__init__()        # 定义LSTM        self.rnn = nn.LSTM(input_size, hidden_size, hidden_num_layers)        # 定义回归层网络,输入的特征维度等于LSTM的输出,输出维度为1        self.reg = nn.Sequential(            nn.Linear(hidden_size, 1)        )    def forward(self, x):        x, (ht,ct) = self.rnn(x)        seq_len, batch_size, hidden_size= x.shape        x = y.view(-1, hidden_size)        x = self.reg(x)        x = x.view(seq_len, batch_size, -1)        return x
当然,有些模型则是将输出当做另一个LSTM的输入,或者使用隐藏层ht,ct的信息进行建模,不一而足。 好了,以上就是我对LSTM的一些学习心得,看完记得关注点赞。 参考: https://zhuanlan.zhihu.com/p/94757947 https://zhuanlan.zhihu.com/p/59862381 https://zhuanlan.zhihu.com/p/36455374 https://www.zhihu.com/question/41949741/answer/318771336 https://blog.csdn.net/android_ruben/article/details/80206792

本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。

96b2f3d8e027771de2520c7fe205aaa3.gif

直播预告

96b2f3d8e027771de2520c7fe205aaa3.gif 7483be98706695eac01bf5e226c0a008.png

他山之石”历史文章

  • PointNet论文复现及代码详解

  • SCI写作常用句型之研究结果&发现

  • 白话生成对抗网络GAN及代码实现

  • pytorch的余弦退火学习率

  • Pytorch转ONNX-实战篇(tracing机制)

  • 联邦学习:FedAvg 的 Pytorch 实现

  • PyTorch实现ShuffleNet-v2亲身实践

  • 训练时显存优化技术——OP合并与gradient checkpoint

  • 浅谈数据标准化与Pytorch中NLLLoss和CrossEntropyLoss损失函数的区别

  • 在C++平台上部署PyTorch模型流程+踩坑实录

  • libtorch使用经验

  • 深度学习模型转换与部署那些事(含ONNX格式详细分析)

  • 如何支撑上亿类别的人脸训练?显存均衡的模型并行(PyTorch实现)

  • PyTorch trick 集锦

d28285bad392010c6b22a045532bf127.png 6bc782e001ccfc061e148fd835420aa6.gif

分享、点赞、在看,给个三连击呗!

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值