文章目录
1 LSTM模型
1.1 LSTM模型介绍
LSTM(Long Short-Term Memory)也称长短时记忆结构
- LSTM是传统RNN的变体
- LSTM与经典RNN相比,能够有效捕捉长序列之间的语义关联,缓解梯度消失或梯度爆炸现象。
LSTM结构说明
- 由输入层、隐藏层、输出层组成
- 每个时间步有三个输入:数据端输入,上一个时间步细胞状态Ct-1,上一个时间步的ht-1
- 每个时间步有三个输出:数据端输出,本时间步细胞状态Ct,本时间步的ht
- LSTM结构更加复杂,内部有3个门+1个细胞状态:遗忘门、输入门、细胞状态、输出门
1.2 LSTM模型内部结构
- 遗忘门
释义:上一个时间步隐藏输出和数据端输入拼接在一起,与权重参W, 进行矩阵运算,再把数据经过sigmoid变换到[0,1]区间,形成输入门
作用:遗忘门 作用在上一个时间步细胞状态之上,表示对上一个时间步信息遗忘多少
- 输入门
释义:输入门作用内部细胞状态之上,表
示对内部细胞状态要使用多少
- 细胞状态
释义:遗忘门作用在上一个时间步细胞状态之上 +输入门作用内部细胞状态之上,更新细胞状态
经典RNN结构作为内部细胞状态
- 输出门
释义:输出门作用内部细胞状态之上,表示对内部细胞状态要使用多少
1.3 LSTM如何实现记忆信息
- 首先,LSTM引进门控机制和细胞状态,也就是内部记忆单元。
- 在⼀个训练好的⽹络中,当输⼊的序列中没有重要的信息时,LSTM的遗忘门的数值接近于1,输入门的数据接近于0,此时过去的记忆会被保存,从⽽实现了长期的记忆功能;
- 当输⼊的序列中出现了重要的信息时,LSTM应该把其存⼊记忆时,此时输出门的数值将接近于1;
- 所以引入各种门机制(遗忘门 输入门 输出门,细胞状态)的确可以控制当前时间步、以前时间步的信息谁重要谁不重要。从而实现LSTM(长短时记忆网络)
1.4 LSTM模型API
- 传参的时候,多传一个参数Ct-1
- c0初始化的时候与h0一致
- 传参的时候,c与h组成元组传入
rnn = nn.LSTM(10, 20, 2) # (input_size,hidden_size,num_layers)
input = torch.randn(5, 3, 10) # (seq_len,batch_size,input_size)
h0 = torch.randn(2, 3, 20) # 初始化h0 (num_layers,batch_size,hidden_size)
c0 = torch.randn(2, 3, 20)
output, (hn, cn) = rnn(input, (h0, c0))
`
import torch
import torch.nn as nn
# 模型的隐藏层个数
def dm01_LSTM():
# 第1个参数:输入数据尺寸-特征维度
# 第2个参数:输出数据尺寸 6个特征(可认为是6个神经元)
# 第3个参数:隐藏层个数(隐藏层个数*方向数-(单向或者双向))
lstm = nn.LSTM(5, 6, 2) # A
# 数据input
# 第1个参数:单词个数 1
# 第2个参数:批次数
# 第3个参数:数据的尺寸-数据的特征维度
input = torch.randn(1, 3, 5) # B
# 数据hidden
# 第1个参数:模型的隐藏层个数 1
# 第2个参数:数据的批次数 3
# 第3个参数:模型的输出神经元的个数-6
h0 = torch.randn(2, 3, 6) # C
c0 = torch.randn(2, 3, 6)
# 给模型送数据
# 数据形状 input[1,3,5] h0[2,3,6] --->output[1,3,6],hn[2,3,6] cn[2,3,6]
output, (hn, cn) = lstm(input, (h0, c0))
# 打印输出
print('output-->', output.shape, output)
print('hn--->', hn.shape, hn)
print('cn--->', cn.shape, cn)
# 注1 : 当隐藏层个数配置成1时 output 和 hn 输出是一样的
# 注2 :如果隐藏层配置n个,则output的结果和最后一个隐藏层输出是一样
if __name__ == '__main__':
dm01_LSTM()
print('LSTM End')
1.5 LSTM优缺点
- LSTM优势
LSTM的门结构能够有效减缓长序列问题中可能出现的梯度消失或爆炸,虽然并不能杜绝这种现象,但在更长的序列问题上表现优于传统RNN
- LSTM缺点
由于内部结构相对较复杂,因此训练效率在同等算力下较传统RNN低很多
1.6 梯度消失和梯度爆炸优化
根据反向传播算法和链式法则,梯度的计算可以简化为以下公式:
其中sigmoid导数的值域在[0,0.25]之间,而一旦公式中的权重参数w的值小于1,那么通过这样的公式连乘之后,最终的梯度就会变得非常小,这种现象称为梯度消失。
反之,如果人为的增大w的值,使其大于1,那么连乘就可能会导致梯度过大,称为梯度爆炸。
- 梯度消失,权重无法更新,最终会导致训练失败
- 梯度爆炸,大幅度更新网络参数,在极情况下,结果会溢出(NAN值)
经典RNN缺点
- 在解决长序列之间的关联时,通过时间,证明经典RNN表现很差,原因在于进行反向传播的时候,过长的梯度序列导致梯度的计算异常,发生梯度消失或者爆炸
LSTM模型
1.7 Bi-LSTM模型
Bi-LSTM即双向LSTM,它没有改变LSTM本身任何的内部结构,只是将LSTM应用两次且方向不同,再将两次得到的LSTM结果进行拼接,作为最终输出
- 没有改变LSTM本身任何的内部结构
- 正向和反向序列都考虑,能够捕捉语法中的一些特定的前置或者后置特征,增强语义关联,但是模型参数和计算复杂度也增加一倍,一般需要对语料和计算资源进行评估后决定是否使用该结构
- “我爱中国"这句话从左到右和从右到左两次LSTM处理, 将得到的结果张量进行了拼接作为最终输出
2 GRU模型
2.1 GRU模型介绍
GRU(Gated Recurrent Unit)也称门控循环单元结构
- GRU是传统RNN的变体
- 同LSTM一样,能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆炸现象
- 同时它的结构比LSTM要简单,比RNN要复杂
GRU结构说明
- 由输入层、隐藏层、输出层组成
- 同RNN,每个时间步有2个输入:数据端输入,上一个时间步的ht-1
- 同RNN,每个时间步有2个输出:数据端输出,本时间步的ht
- GRU结构有2个门:更新门、重置门
2.2 GRU模型内部结构
- 重置门
释义:上一个时间步隐藏输出和数据端输入拼接在一起,与权重参W, 进行矩阵运算,再把数据经过sigmoid变换到[0,1]区间,形成重置门
2.3 GRU模型API
- GRU模型API与经典RNN模型的参数类似
import torch
import torch.nn as nn
# 模型的隐藏层个数
def dm01_GRU():
# 第1个参数:输入数据尺寸-特征维度
# 第2个参数:输出数据尺寸 6个特征(可认为是6个神经元)
# 第3个参数:隐藏层个数(隐藏层个数*方向数-(单向或者双向))
mygru = nn.GRU(5, 6, 2) # A
# 数据input
# 第1个参数:单词个数 1
# 第2个参数:批次数
# 第3个参数:数据的尺寸-数据的特征维度
input = torch.randn(1, 3, 5) # B
# 数据hidden
# 第1个参数:模型的隐藏层个数 1
# 第2个参数:数据的批次数 3
# 第3个参数:模型的输出神经元的个数-6
h0 = torch.randn(2, 3, 6) # C
# 给模型送数据 input[1,3,5] h0[2,3,6] -> output[1,3,6],hn[2,3,6] cn[2,3,6]
output, hn = mygru(input, h0)
# 打印输出
print('output-->', output.shape, output)
print('hn--->', hn.shape, hn)
# 注1 : 当隐藏层个数配置成1时 output 和 hn 输出是一样的
# 注2 :如果隐藏层配置n个,则output的结果和最后一个隐藏层输出是一样
if __name__ == '__main__':
dm01_GRU()
print('LSTM End')
2.4 GRU模型的优缺点
-
GRU模型优点
- GRU和LSTM作用相同, 在捕捉长序列语义关联时, 能有效抑制梯度消失或爆炸, 效果都优于传统RNN
- 且计算复杂度相比LSTM要小
-
GRU模型缺点
- GRU仍然不能完全解决梯度消失问题, 同时其作用RNN的变体, 有着RNN结构本身的一大弊端, 即不可并行计算, 这在数据量和模型体量逐步增大的未来, 是RNN发展的关键瓶颈
2.5 Bi-GRU模型
- 与Bi-LSTM模型类,都是不改变其内部结构,而是将模型应用两次且方向不同,再将两次得到的LSTM结果进行拼接作为最终输出