Press "Enter" to skip to content

优化RNN

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

3.4 优化RNN

 

文章目录

 

RNN擅长处理序列数据,尤其当后面数据与前面数据有依赖关系时。不过,如果这种依赖涉及长依赖的话,使用RNN的效果将受到较大影响,因长依赖就意味着时间步就要大,而根据梯度的关联规则,会发现累乘会导致激活函数导数的累乘,如果取tanh或sigmoid函数作为激活函数的话,那幺必然是一堆小数在做乘法,结果就是越乘越小。随着时间序列的不断深入,小数的累乘就会导致梯度越来越小直到接近于0,这就是“梯度消失“现象。实际使用中,会优先选择tanh函数,原因是tanh函数相对于sigmoid函数来说梯度较大,收敛速度更快且引起梯度消失更慢。通常减缓梯度消失的几种方法:

 

1、选取更好的激活函数,如Relu激活函数。ReLU函数的左侧导数为0,右侧导数恒为1,这就避免了“梯度消失“的发生。但大于1的导数容易导致“梯度爆炸“,但设定合适的阈值可以解决这个问题。

 

2、加入BN层,其优点包括可加速收敛、控制过拟合,可以少用或不用Dropout和正则、降低网络对初始化权重不敏感,且能允许使用较大的学习率等。

 

3、改变传播结构,LSTM结构可以有效解决这个问题。接下来我们将介绍LSTM相关内容。

 

这些方法中,LSTM比其他几种方法效果要好,LSTM的核心思想就是增加一条记忆以往信息的传送带,这条传送带通过各种更新信息相加,而从有效避免梯度消失问题。

 

LSTM成功用于许多应用(如语言建模,手势预测,用户建模)。 LSTM基于记忆的序列建模架构非常有影响力——它启发了许多最新的改进方法,例如Transformers。

 

3.4.1 LSTMCell

 

1.RNN与LSTM的关系图

 

 

图1-14 RNN与LSTM的关系

 

从图1-14 可知,RNN对应LSTM中方框部分,方框部分的信息就是当前状态信息,该信息将通过输入门之后加入到传送带C中。

 

2.LSTMCell整体架构图

LSTM 通过精心设计的称作为“门”的结构来去除或者增加信息到传送带C状态的能力。门是一种让信息选择式通过的方法。他们通过含激活 sigmoid 神经网络层形成门(如图1-15中

等信息进行过滤。

 

 

图1-15 LSTM架构图

 

LSTM三个门的主要功能:

 

 

输入门对当前信息C ̃_t进行过滤,其表示式为:

2.LSTMCell的详细结构

 

 

图1-16 LSTM的详细内部结构图

 

为简明起见,图1-16中假设输入为3个元素的向量,隐藏状态及传送信息都是2个元素的向量。隐藏状态与输入拼接成5个元素的向量。实际应用中输入、状态一般加上批量等维度,如[batch,input_size],如果在LSTM模块中还需加上序列长度,形如[batch,seg_len,input_size]。multiplication是指哈达玛积,S表示Softmoid,T表示Tanh。

 

3.4.2 LSTMCell

 

LSTMCell这是LSTM的一个单元,如图1-15所示。该单元可以用PyTorch的 nn.LSTMCell模块来实现,该模块构建LSTM中的一个Cell,同一层会共享这一个Cell,但要手动处理每个时刻的迭代计算过程。如果要建立多层的LSTM,就要建立多个nn.LSTMCell。

 

1 .构造方法

 

构造方法和nn.RNNcell类似,依次传入feature_len和hidden_len,因为这只是一个计算单元,所以不涉及层数。

 

2. forward方法

 

回顾一下nn.RNNCell的forward方法,它是:

 

ht=nn.rnncell(x,ht-1)

 

即上一时刻的输出ht−1经nn.RNNCell前向计算得到这一时刻的ht。

 

对于nn.LSTMCell也是类似,但因为LSTM计算单元中还涉及对上一时刻的记忆Ct−1的使用,所以是

 

ht,ct=lstmcell(xt,(ht-1,ct-1))

 

因为输入xt只是t时刻的输入,不涉及seq_len,所以其shape是[batch,feature_len]

 

而ht和Ct在这里只是t时刻本层的隐藏单元和记忆单元,不涉及num_layers,所以其shape是 [batch,hidden_len]

 

3.4.3 一层的例子

 

每个时刻传入新的输入xt和上一时刻的隐藏单元ht−1和记忆单元Ct−1,并把这两个单元更新。

 

import torch
from torch import nn
 
# 一层的LSTM计算单元,输入的feature_len=100,隐藏单元和记忆单元hidden_len=20
cell = nn.LSTMCell(input_size=100, hidden_size=20)
 
# 初始化隐藏单元h和记忆单元C,取batch=3
h = torch.zeros(3, 20)
C = torch.zeros(3, 20)
 
# 这里是seq_len=10个时刻的输入,每个时刻shape都是[batch,feature_len]
xs = [torch.randn(3, 100) for _ in range(10)]
 
# 对每个时刻,传入输入x_t和上个时刻的h_{t-1}和C_{t-1}
for xt in xs:
    h, C = cell(xt, (h, C))
 
print(h.shape)  # torch.Size([3, 20])
print(C.shape)  # torch.Size([3, 20])

 

3.4.4 两层的例子

 

在最底下一层l0层和上面的例子一样,上层还需要接受下层的输出ht作为当前输入,然后同样是依赖本层上一时刻的h和C更新本层的h和C。注意LSTM单元的输入输出结构,向上层传递的是h而不是C。

 

import torch
from torch import nn
 
# 输入的feature_len=100,变到该层隐藏单元和记忆单元hidden_len=30
cell_l0 = nn.LSTMCell(input_size=100, hidden_size=30)
# hidden_len从l0层的30变到这一层的20
cell_l1 = nn.LSTMCell(input_size=30, hidden_size=20)
 
# 分别初始化l0层和l1层的隐藏单元h和记忆单元C,取batch=3
# 注意l0层的hidden_len=30
h_l0 = torch.zeros(3, 30)
C_l0 = torch.zeros(3, 30)
# 而到l1层之后hidden_len=20
h_l1 = torch.zeros(3, 20)
C_l1 = torch.zeros(3, 20)
 
# 这里是seq_len=10个时刻的输入,每个时刻shape都是[batch,feature_len]
xs = [torch.randn(3, 100) for _ in range(10)]
 
# 对每个时刻,从下到上计算本时刻的所有层
for xt in xs:
    h_l0, C_l0 = cell_l0(xt, (h_l0, C_l0))  # l0层直接接受xt输入
    h_l1, C_l1 = cell_l1(h_l0, (h_l1, C_l1))  # l1层接受l0层的输出h为输入
 
# 最后shape是不变的
print(h_l0.shape)  # torch.Size([3, 30])
print(C_l0.shape)  # torch.Size([3, 30])
print(h_l1.shape)  # torch.Size([3, 20])
print(C_l1.shape)  # torch.Size([3, 20])

 

3.4.5 PyTorch的LSTM模块

 

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

 

1、多层LSTM的网络架构

 

 

图1-17 多层LSTM架构图

 

2、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),默认为:False
    dropout:除最后一层,每一层的输出都进行dropout,默认为: 0
    bidirectional:True则为双向lstm默认为False

 

3、输入

 

input(seq_len, batch, input_size)

 

参数有:

 

seq_len:序列长度,在NLP中就是句子长度,一般都会用pad_sequence补齐长度

 

batch:每次喂给网络的数据条数,在NLP中就是一次喂给网络多少个句子

 

input_size:特征维度,和前面定义网络结构的input_size一致

 

 

图1-18 LSTM的输入格式

 

如果LSTM的参数 batch_first=True,则要求输入的格式是:input(batch, seq_len, input_size)

 

4、隐含层

 

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)

 

5、输出

 

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)

 

3.5 LSTM实现文本生实例

 

本实例使用2014人民日报上的一些新闻,使用PyTorch提供的nn.LSTM模型,根据提示字符串预测给定长度的语句。整个处理与3.3小节基本相同,构建模型时稍有不同,LSTM有两个变量h和c,RNN只要一个h。

 

3.6 GRU结构

 

https://blog.csdn.net/Jerr__y/article/details/58598296

 

LSTM门比较多,状态有C和H,其中C就相当于中间变量一样,输出只有H。有关LSTM的改进方案比较多,其中比较着名的变种 GRU(Gated Recurrent Unit ),这是由 Cho, et al. (2014) 提出。在 GRU 中,如 下图所示,只有两个门:重置门(reset gate)和更新门(update gate)。同时在这个结构中,把细胞状态和隐藏状态进行了合并。最后模型比标准的 LSTM 结构要简单,而且这个结构后来也非常流行。

 

 

图1-19 GRU模型结构图

 

其中,r_t 表示重置门,z_t表示更新门。重置门决定是否将之前的状态(h_(t-1))忘记。当r_t趋于 0 的时候,前一个时刻的状态信息 h_(t-1)将被忘掉,隐藏状态 h_t会被重置为当前输入的信息。更新门决定是否要将隐藏状态更新为新的状态 h ̃_t (更新门的作用相当于合并了 LSTM 中的遗忘门和输入门)。

 

和 LSTM 比较一下:

 

(1) GRU 少一个门,同时少了传送带状态 C_t。

 

(2) 在 LSTM 中,通过遗忘门和输入门控制信息的保留和传入;GRU 则通过重置门来控制是否要保留原来隐藏状态的信息,但是不再限制当前信息的传入。

 

(3) 在 LSTM 中,虽然得到了新的传送带状态 C_t,但是它仅仅作为一个中间变量,不直接输出,而是需要经过一个过滤的处理:

 

h_t=O_t 〖⊗tanh⁡(C〗_t)

 

同样,在 GRU 中, 虽然 (2) 中也得到了新的隐藏状态h_t, 但是还不能直接输出,而是通过更新门来控制最后的输出:

 

h_t=(1-z_t )⊗h_(t-1)+z_t⊗h ̃_t

 

保留以往隐藏状态(h_(t-1))和保留当前隐藏状态(h ̃_t)之间是一种互斥关系,这点从上面这个公式也可说明。如果z_t 越接近1,则(1-z_t )就越接近于0;反之,也成立。

 

3.7 biRNN结构

 

1.biRNN的网络架构

 

图1-20 为biRNN的网络架构图,从图中还可以看出,对于一个序列,一次向前遍历得到左LSTM,向后遍历得到右LSTM。隐层矢量直接通过拼接(concat)得到,最终损失函数直接相加,具体如下图。

 

 

图1-20 biRNN模型结构图

 

2、biRNN的不足

 

(1)biRNN模型的前向和后向LSTM模型是分开训练的,故它不是真正双向学习的模型,即biRNN无法同时向前和向后学习。

 

(2)biRNN如果层数多了,将出现自己看到自己的情况。下图中第2行第2列中的A|CD,此结果是第一层Bi LSTM的B位置输出内容,包括正向A和反向CD,然后直接拼接就得到A| CD。下图中第3行第2列中的ABCD, 此结果为正向BCD和反向AB | D拼接而成,当前位置需要预测的是B,但B已经在这里ABCD出现了。所以对于Bi LSTM,只要层数增加,会有一个“看到你自己”的问题。

 

 

图1-21 多层biRNN将自己看到自己示意图

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注