Java教程

NLP学习笔记<4> 循环神经网络RNN之()LSTM

本文主要是介绍NLP学习笔记<4> 循环神经网络RNN之()LSTM,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

4.1 长短期记忆网络与门控循环

 4.2 RNN的架构设计

4.3 pytorch实现

1.RNN

 2.LSTM

 3.Bi-LSTM


4.1 长短期记忆网络与门控循环

长短期神经网络(Long Short-term Memory, LSTM)神经网络能够进一步改善之前RNN的记忆能力并且减轻梯度爆炸和梯度消失的问题,它对RNN的主要修改在于将循环函数f_r从简单的全连接改进为使用三个控制门的记忆单元,函数可以表示为:

 其中\sigma(x)为sigmoid函数,其输出值在0~1之间,值越接近1保留的信息越多。o为控制门的输出,即输入向量y通过该控制门的输出信息,c_t为t时刻RNN的单元状态,带有序列的历史信息。i_t,o_t,f_t分别称为输入门、输出门、遗忘门,分别通过以下式子计算。

LSTM通过这种复杂的循环函数在每个时间步上对当前的输入和记忆的历史信息进行重新的组合,有效地减轻了梯度爆炸、梯度消失等问题,其关键点在于以下两点。

        1.更复杂的循环函数。复杂的循环函数使梯度在回传过程中经历了很多倒数较小的激活函数,从而降低了梯度爆炸发生的可能性。

        2.遗忘门的使用。遗忘门中的偏置项b在开始会设置为较大的值,从而尽量的保存t-1的状态。但在训练时f中的元素值也可能会逐渐变小,通过遗忘门的梯度也会逐渐消失,所以这种设计并没有完全解决梯度消失这一问题。

        针对梯度消失问题,有学者去掉了LSTM网络中的遗忘门进行试验,实验说明有遗忘门的神经网络空直网络效果更好,也就说明通过控制门的结构优化能使模型自由选择想记住什么信息,更加现实。

        在大规模中的网络架构中LSTM的时间性能较差,门控循环单元(GRU)通过简化LSTM的设计达到了更高的时间空间效率,循环函数如下:

 4.2 RNN的架构设计

1. 多层RNN

        多层RNN和标准RNN相同,第一个隐藏层的输入是上一时刻的隐状态和当前的词向量,而其他隐藏层的输入则是上一时刻的隐状态和上一隐藏层当前时刻的隐状态,最终,多层RNN使用最后一个隐藏层的隐状态预测输出。相比于简单的RNN,多层RNN具有更多的网络参数和更深的网络结构,能够有效提高对更长时间序列的记忆能力。

2.双向RNN

在某些问题例如中文分词、词性标注中,当前时刻之前和之后的信息均对当前时刻的输出有影响 ,所以双向RNN提供了这种可能,即每一时刻的状态有两个方向编码得到的隐状态组成。

4.3 pytorch实现

1.RNN

 

nn.RNN(input_size, hidden_size, num_layers=1, nonlinearity=tanh, bias=True, batch_first=False, dropout=0, bidirectional=False)
参数说明

input_size 输入特征的维度, 一般rnn中输入的是词向量,那么 input_size 就等于一个词向量的维度,即用多少维修饰一个词
hidden_size 隐藏层神经元个数,或者也叫输出的维度(因为rnn输出为各个时间步上的隐藏状态),即用多少维修饰记忆
num_layers 网络的层数
nonlinearity 激活函数
bias 是否使用偏置
batch_first 输入数据的形式,默认是 False,就是这样形式,(seq(num_step), batch, input_dim),也就是将序列长度放在第一位,batch 放在第二位,这样可以统一其与CNN的格式
dropout 是否应用dropout, 默认不使用,如若使用将其设置成一个0-1的数字即可
birdirectional 是否使用双向的 rnn,默认是 False
PS:注意某些参数的默认值在标题中已注明
输入输出shape
input_shape = [(n_step)时间步数即t的大小, (batch_size)批量大小即一次处理的句子数, (n_class)词向量特征维度] 
        在前向计算后会分别返回outputs和隐藏状态hidden

        前者指的是隐藏层在各个时间步上计算并输出的隐藏状态,它们通常作为后续输出层的输⼊。需要强调的是,该“outputs”本身并不涉及输出层计算,形状为[时间步数, 批量大小, 隐藏单元个数(hidden_size)];

        后者指的是隐藏层在最后时间步的隐藏状态:当隐藏层有多层时,每⼀层的隐藏状态都会记录在该变量中;对于像⻓短期记忆(LSTM),隐藏状态是⼀个元组(h, c),即hidden state和cell state(此处普通rnn只有一个值)隐藏状态h的形状为(层数, 批量大小,隐藏单元个数(hidden_size))

class TextRNN(nn.Module):
    def __init__(self):
        super(TextRNN, self).__init__()
        self.rnn = nn.RNN(input_size=n_class, hidden_size=n_hidden)
        self.W = nn.Linear(n_hidden, n_class, bias=False)
        self.b = nn.Parameter(torch.ones([n_class]))

    def forward(self, hidden, X):
        X = X.transpose(0, 1) # X : [n_step, batch_size, n_class]
        outputs, hidden = self.rnn(X, hidden)
        # outputs : [n_step, batch_size, num_directions(=1) * n_hidden]
        # hidden : [num_layers(=1) * num_directions(=1), batch_size, n_hidden]
        outputs = outputs[-1] # [batch_size, num_directions(=1) * n_hidden]
        model = self.W(outputs) + self.b # model : [batch_size, n_class]
        return model

 

 2.LSTM

输入输出格式基本一致,各参数定义也一致

 

class TextLSTM(nn.Module):
    def __init__(self):
        super(TextLSTM, self).__init__()

        self.lstm = nn.LSTM(input_size=n_class, hidden_size=n_hidden)
        self.W = nn.Linear(n_hidden, n_class, bias=False)
        self.b = nn.Parameter(torch.ones([n_class]))

    def forward(self, X):
        input = X.transpose(0, 1)  # X : [n_step, batch_size, n_class]

        hidden_state = torch.zeros(1, len(X), n_hidden)  # [num_layers(=1) * num_directions(=1), batch_size, n_hidden]
        cell_state = torch.zeros(1, len(X), n_hidden)     # [num_layers(=1) * num_directions(=1), batch_size, n_hidden]

        outputs, (_, _) = self.lstm(input, (hidden_state, cell_state))
        outputs = outputs[-1]  # [batch_size, n_hidden]
        model = self.W(outputs) + self.b  # model : [batch_size, n_class]
        return model

 3.Bi-LSTM

输入输出格式如LSTM相同,只不过要将bidirections设为True并将后面的状态乘二。

具体原理此处在加以说明:如现有有由四个词构成的一句话"i like your friends"。常规单向LSTM的做法就是直接输入"i like your",然后预测出"friends",而双向LSTM会同时输入"i like your"和"your like i",然后将Forward Layer和Backward Layer的output进行concat(这样做可以理解为同时"汲取"正向和反向的信息),最后预测出"friends"

class BiLSTM(nn.Module):
    def __init__(self):
        super(BiLSTM, self).__init__()

        self.lstm = nn.LSTM(input_size=n_class, hidden_size=n_hidden, bidirectional=True)
        self.W = nn.Linear(n_hidden * 2, n_class, bias=False)
        self.b = nn.Parameter(torch.ones([n_class]))

    def forward(self, X):
        input = X.transpose(0, 1)  # input : [n_step, batch_size, n_class]

        hidden_state = torch.zeros(1*2, len(X), n_hidden)   # [num_layers(=1) * num_directions(=2), batch_size, n_hidden]
        cell_state = torch.zeros(1*2, len(X), n_hidden)     # [num_layers(=1) * num_directions(=2), batch_size, n_hidden]

        outputs, (_, _) = self.lstm(input, (hidden_state, cell_state))
        outputs = outputs[-1]  # [batch_size, n_hidden]
        model = self.W(outputs) + self.b  # model : [batch_size, n_class]
        return model

参考文档

BiLSTM的PyTorch应用_mathor的博客-CSDN博客

https://zhuanlan.zhihu.com/p/79064602

详解BiLSTM及代码实现 - 知乎

【自然语言处理】循环神经网络和LSTM-基础与实战 [NLP|RNN|LSTM|Pytorch|1080P]_哔哩哔哩_bilibili

这篇关于NLP学习笔记<4> 循环神经网络RNN之()LSTM的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!