NLP——RNN变体LSTM和GRU
LSTM
一、概念
LSTM(Long Short-Term Memory)也称长短时记忆结构, 它是传统RNN的变体, 与经典RNN相比能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆炸现象. 同时LSTM的结构更复杂, 它的核心结构可以分为四个部分去解析:
遗忘门:控制上一个时间步的细胞状态遗忘多少,通过门宽的保留下来
输入门:控制当前时间步的候选记忆( 传统RNN公式得到的结果 )保留多少
细胞状态:存储长期记忆,贯穿计算的全过程
输出门:从当前细胞状态中得到当前隐层输出
这种分工使LSTM既能捕捉长期依赖(如小说伏笔),又能灵活处理局部特征(如对话语气)。
二、LSTM内部结构
计算过程
简单来讲,LSTM一个时间步的计算通过以下几步进行运行:
-
遗忘门筛选历史记忆: 决定从长期记忆(细胞状态)选择遗忘丢弃哪些旧信息
-
输入:拼接后的参数矩阵[h(t-1), x(t)]
-
输出:遗忘门门值f(t)
-
-
输入门吸取新信息:选并整合当前输入中的关键信息生成候选记忆
-
输入:拼接后的参数矩阵[h(t-1), x(t)]
-
输出:输入门门值i(t),生成候选记忆C~(t) (RNN的计算方式)
-
-
细胞状态更新融合新旧记忆:通过线性叠加更新长期记忆C(t)。
-
输入:候选记忆C~(t),遗忘系数 f(t), 上一个时间步的细胞状态C(t-1) , 输入门值i(t)
-
输出:长期记忆 C(t)
-
-
输出门生成隐藏状态:控制细胞状态中哪些信息作为当前输出(短期记忆)
-
输入:当前时间步的细胞状态 C(t),拼接后的参数矩阵[h(t-1), x(t)]
-
输出:输出门门值o(t) (中间变量),短期记忆h(t)
-
三、代码实现
实例化LSTM是默认参数batch_first=False,
所以构建的输入参数先sequence_length后batch_size;
如果更改参数batch_first=True,则构建的输入参数先batch_size后sequence_length。
# -*- coding: utf-8 -*-
import torch.nn as nn
import torch# 第一个参数:input_size代表输入数据的词嵌入表示维度
# 第二个参数:hidden_size代表隐藏层输出维度
# 第三个参数:num_layers代表隐藏层的个数
lstm = nn.LSTM(input_size=5, hidden_size=6, num_layers=1)# 第一个参数:sequence_length代表输入数据的每个样本的句子长度
# 第二个参数:batch_size代表一个批次几个样本
# 第三个参数:input_size代表输入数据的词嵌入表示维度
input_x = torch.randn(4, 3, 5)# 第一个参数:num_layers代表隐藏层的个数
# 第二个参数:batch_size代表一个批次几个样本
# 第三个参数:hidden_size代表隐藏层输出维度
h0 = torch.randn(1, 3, 6)
c0 = torch.randn(1, 3, 6)output, (hn, cn) = lstm(input_x, (h0, c0))
print(f'output: {output}')
print(f'hn: {hn}')
print(f'cn: {cn}')
四、LSTM优缺点
优点:
LSTM的门结构能够有效减缓长序列问题中可能出现的梯度消失或爆炸, 虽然并不能杜绝这种现象, 但在更长的序列问题上表现优于传统RNN。
缺点:
由于内部结构相对较复杂, 因此训练效率在同等算力下较传统RNN低很多。
五、BI-LSTM
定义: 不改变原始的LSTM模型内部结构,只是将文本从左到右计算一遍,再从右到左计算一遍,把最终的输出结果拼接得到模型的完整输出。
GRU
一、概念
GRU(Gated Recurrent Unit)也称门控循环单元结构, 它也是传统RNN的变体, 同LSTM一样能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆炸现象. 同时它的结构和计算要比LSTM更简单, 它的核心结构可以分为两个部分去解析:
更新门:
作用:决定当前时刻的隐藏状态需要保留多少上一时刻的隐藏状态信息,以及吸收多少新的候选隐藏状态信息。(类似于LSTM中的“输入门”和“遗忘门”的结合,但更高效)
门值越趋近于1,保留上一时间步信息越多;越趋近于0,表示更新的越多,采用当前状态越多。
重置门:
作用:控制上一时刻的隐藏状态对当前候选隐藏状态的影响程度。(决定是否“忽略”过去的信息,以便更好地捕捉短期依赖)
门值越趋近于1,表示将上一时刻状态与当前输入结合生成候选状态;门值越趋近0,忽略上一时刻状态越多,候选状态更依赖当前输入。
二、GRU内部结构
1. 计算更新门和重置门的门值
更新门值
重置门值
2. 然后重置门值作用在h(t-1)上,控制上一时间步的输入保留下多少(作用类似遗忘门),再用留下的部分进行基本RNN运算(与x(t)拼接进行线性变换,经过tanh激活),得到新,再使用新值与
相乘。
3. 使用(1-z(t))作用再h(t-1)上再与第二步结果相加输出新的h(t)
三、代码实现
# -*-coding:utf-8-*-
import torch
import torch.nn as nn
# 实例化GRU模型
#todo: 1.GRU实例化主要参数说明:
# 第一个参数:input_size代表输入数据的词嵌入表示维度
# 第二个参数:hidden_size代表隐藏层输出维度
# 第三个参数:num_layers代表隐藏层的个数gru = nn.GRU(5, 6, 1)#todo: 2.输入数据参数说明:
# 第一个参数:sequence_length代表输入数据的每个样本的句子长度
# 第二个参数:batch_size代表一个批次几个样本
# 第三个参数:input_size代表输入数据的词嵌入表示维度
input_x = torch.randn(3, 4, 5)#todo: 3.隐藏状态参数说明:
# 第一个参数:num_layers代表隐藏层的个数
# 第二个参数:batch_size代表一个批次几个样本
# 第三个参数:hidden_size代表隐藏层输出维度
h0 = torch.randn(1, 4, 6)# todo:将数据送入模型
output, hn = gru(input_x, h0)
print(f'output-->{output}')
print(f'hn-->{hn}')
四、GRU优缺点
GRU的优势:
GRU和LSTM作用相同, 在捕捉长序列语义关联时, 能有效抑制梯度消失或爆炸, 效果都优于传统RNN且计算复杂度相比LSTM要小、重置门直接作用于上一隐藏状态,而LSTM遗忘门作用于细胞状态。更像一个滑动开关,决定保留与更新的比例。
GRU的缺点:
GRU仍然不能完全解决梯度消失问题, 同时其作用RNN的变体, 有着RNN结构本身的一大弊端, 即不可并行计算, 这在数据量和模型体量逐步增大的未来, 是RNN发展的关键瓶颈。
五、BI-GRU
Bi-GRU与Bi-LSTM的逻辑相同, 都是不改变其内部结构, 而是将模型应用两次且方向不同, 再将两次得到的LSTM结果进行拼接作为最终输出. 具体参见上小节中的Bi-LSTM。