【NLP251】NLP RNN 系列网络
NLP251 系列主要记录从NLP基础网络结构到知识图谱的学习
1.原理及网络结构
1.1RNN
在Yoshua Bengio论文中( http://proceedings.mlr.press/v28/pascanu13.pdf )证明了梯度求导的一部分环节是一个指数模型,当n<1时,就会出现“梯度消失"问题,而当η> 1时,“梯度爆炸”也就产生了。
1.2 双向-RNN
两个方向RNN的区别在于输入数据的不同,反向RNN数据是对正向RNN数据的反转
1.3深度双向-RNN
1.4LSTM
LSTM(长短期记忆网络)相较于RNN(循环神经网络)的主要优势如下:
1. 解决长期依赖问题
-
RNN在处理长序列数据时,容易出现梯度消失或梯度爆炸的问题,导致难以捕捉到序列中相隔较远的依赖关系。
-
LSTM通过引入“记忆单元”(Cell State)和门控机制(遗忘门、输入门、输出门),能够有效地学习和保持长期依赖关系。遗忘门可以有选择性地丢弃不再重要的信息,输入门可以添加新的重要信息,输出门则控制信息的输出,从而确保信息在长序列中能够稳定传递。
2. 缓解梯度消失问题
-
RNN在反向传播时,梯度可能会随着序列长度增加而迅速衰减或增大,导致训练困难。
-
LSTM通过门控机制,使得梯度可以直接通过记忆单元流动,减少了梯度在传播过程中的衰减,从而缓解了梯度消失问题。
LSTM怎么控制“细胞状态”?
LSTM变种
变种1 增加“peephole connections”层 ,让门层也接受细胞状态的输入
1.5GRU
总结:RNN与GRU细胞状态信息与输出信息相同,而GRU细胞状态信息可能与输出信息不同 。
2.API接口实现
2.1RNN API调用讲解
RNN返回值为两部分,第一部分是蓝框所示所有时刻 最后一个隐藏层的输出特征向量;
第二分是红色圈所示最后时刻 所有一个隐藏层的输出特征向量;
我们可以通过rnn.named_parameters()来查看详细的中间过程状态shape
rnn = nn.RNN(4, 8, num_layers=2, batch_first=True, bidirectional=True)
for name, param in rnn.named_parameters():
print(name, param.shape)
RNN无法保持长时依赖(代码验证):
2.2 LSTM API调用讲解
从网络结构图和代码中不难发现LSTM中ht与output输出相同
中间过程中的32从何而来?
weight_ih_l0 torch.Size([32, 4]) weight_hh_l0 torch.Size([32, 8]) bias_ih_l0 torch.Size([32]) bias_hh_l0 torch.Size([32])
附LSTM代码:
lstm = nn.LSTM(
input_size=4, # 每个样本每个时刻输入的向量维度大小
hidden_size=16, # 每个样本每个时刻输出的向量维度大小
num_layers=1, # RNN的层数,默认为1
bias=True, # 内部的线性转换是否添加bias,True表示添加,默认为True
batch_first=True, # 输入&输出数据的第一维是不是批次样本,True表示是,也就是输出的格式为:[N,T,E]; 默认为False,表示shape格式为[T,N,E]
dropout=0, # 针对输出的特征向量中,部分值重新为0的概率/可能性
bidirectional=False, # 是否构建双向的RNN,True表示构建,False表示不构建反向RNN;默认为False
proj_size=0 # 是否针对每个时刻输出的hi进行一个线性转换,0表示不进行转换;>0的值表示会将hi映射(全连接)为proj_size大小的向量
)
x = torch.randn(2, 3, 4) # 2个样本,每个样本3个token,每个token对应的向量维度大小为4
# batch_first = True
# output: 所有样本、所有时刻对应的输出特征向量值,shape为: [N,T,?]
# ? = hidden_size * (2 if bidirectional else 1) if proj_size <=0 else proj_size
# ct: 最后一个时刻的状态信息/细胞信息, shape为: [1 * num_layers * (2 if bidirectional else 1), N, hidden_size]
# ht: 最后一个时刻的状态信息/细胞信息, shape为: [1 * num_layers * (2 if bidirectional else 1), N, hidden_size]
output, (ht, ct) = lstm(x)
print(type(output), output.shape)
print(type(ht), ht.shape)
print(type(ct), ct.shape)
print(output[:, -1, :])
print(ht)
print(ct)
rnn = nn.LSTM(4, 8, batch_first=True, bidirectional=False, num_layers=1)
for name, param in rnn.named_parameters():
print(name, param.shape)
2.3 GRU API调用讲解
lstm = nn.GRU(
input_size=4, # 每个样本每个时刻输入的向量维度大小
hidden_size=16, # 每个样本每个时刻输出的向量维度大小
num_layers=1, # RNN的层数,默认为1
bias=True, # 内部的线性转换是否添加bias,True表示添加,默认为True
batch_first=True, # 输入&输出数据的第一维是不是批次样本,True表示是,也就是输出的格式为:[N,T,E]; 默认为False,表示shape格式为[T,N,E]
dropout=0, # 针对输出的特征向量中,部分值重新为0的概率/可能性
bidirectional=False # 是否构建双向的RNN,True表示构建,False表示不构建反向RNN;默认为False
)
# 2个样本,每个样本3个token,每个token对应的向量维度大小为4
x = torch.randn(2, 3, 4)
# batch_first = True
# output: 所有样本、所有时刻对应的输出特征向量值,shape为: [N,T,?]
# **** ? = hidden_size * (2 if bidirectional else 1)
# : 最后一个时刻的状态信息/细胞信息, shape为: [1 * num_layers * (2 if bidirectional else 1), N, hidden_size]
# ct/ht: 最后一个时刻的状态信息/细胞信息, shape为: [1 * num_layers * (2 if bidirectional else 1), N, hidden_size]
output, ct = lstm(x)
print(type(output), output.shape)
print(type(ct), ct.shape)
rnn = nn.GRU(4, 8, batch_first=True, bidirectional=False, num_layers=1)
for name, param in rnn.named_parameters():
print(name, param.shape)