循环神经网络变种
标准循环神经网络虽然有记忆,但很健忘,再深层结构中循环神经网络实际上再时间序列的各个时刻重复应用相同的操作来构建深的计算图,而且模型的参数共享。比如 W 需要再时间步中反复用于相乘,如果W 可以特征值分解:
当特征值 λ 不在 1 附件,如果大于1 产生梯度爆炸,小于 1 就会产生梯度消失。梯度消失会使我们难以知道参数朝哪个方向移动能改进代价函数,而梯度爆炸会让学习过程变得不稳定。
再任何深度网络中都会存在这样的问题,就是RNN 更加明显,再RNN 中,相邻的时间步是链接再一起的,所有权重偏导数要么都小于1 ,要么都大于1 ,这样与前馈神经网络相比,RNN 的梯度消失和爆炸的问题更加明显。简单的RNN 随层数增加网络就会无法训练,无法实现长时间的记忆,这就导致RNN 存在短时记忆的问题。解决方法:
- 选择更好的激活函数,比如 ReLU,右导数恒为1 ,可以避免梯度消失
- 加入BN 层,有点可以加速收敛,控制过拟合
- 修改网络结构,比如LSTM 结构就可以有效的解决问题。
LSTM
长短时记忆网络(LSTM),还有基于LSTM 的集中变种算法,GRU算法等。可以解决信息的长期依赖,避免梯度消失和爆炸。与RNN 相比结构上设计了 循环体结构,用两个门来控制单元状态的内容,一个是遗忘门(Forget Gate) ,决定了上一时刻的单元状态有多少要保留到当前时刻,另一个输入门,决定当前时刻网络的输入 xt 有多少保存到单元状态中。用输出门 控制单元状态 ct 有多少输出到当前输出值 ht。
GRU
LSTM 结构比较复杂,计数复杂度较高。又推出其他变种 GRU ,对LSTM 进行了很多简化,计数效率更高,占用内存更少。但效果差异不大。
- 将输入门,遗忘门,输出门 简化成了, 更新门,重置门
- 将单元状态和输出合并为一个状态 ht.
Bi-RNN
双向循环神经网络,增加了RNN 可利用的信息,同时使用时序数据输入历史和未来数据,时序相反的两个循环神经网络链接同一个输出,输出层可以同时获取 t 历史和未来的信息。
百度语音识别就是通过 Bi-RNN 综合上下文语境,提升模型准确率。基本思想就是每一个训练序列向前向后分别是两个RNN ,而且这两个都链接一个 输出层。这个结构给输出每一个点完整的过去和未来的上下文信息。上图就是一个沿时间展开的双向循环神经网络,6个独特的权值参数共享。输入到向前和向后隐含层 (w1,w3)、隐含层到隐含层自己(w2,w5)、向前和向后隐含层到输出层 (w4,w6)。值得注意的是,向前和向后隐含层之间没有信息流,这保证了 展开图是非循环的。
循环神经网络pytorch实现
pytroch 提供了响应的API, 单元版的 nn.RNNCell, nn.LSTMCell, nn.GRUCell. 封装版的 nn.RNN, nn.LSTM, nn.GRU 。两者的区别就是输入,前者是时间步或序列的一个元素,后者是一个时间步序列。。。
RNN 实现
torch.nn.RNN( args, * kwargs)
# at=tanh(w^ih*x^t+b^ih+w^hh*a^t-1 +b^hh) 状态输出at 的计算公式
参数:
- input_size ,输入x 的特征数量
- hidden_size: 隐藏层的特征数量
- num_layers RNN 的层数
- nonlinearity : 激活函数默认 tanh ,可以选择relu
- bias :默认True,是否使用偏置权重 bi, bh 。
- batch_first,如果为True,输入Tensor的 shape应该是(batch, seq, feature),输出也是一样。默认输入
(seq, batch, feature) 序列长度,批次大小,特征维度。
- dropout,0-1 默认0,除了最后一层,其他层的输出都会加上一个 dropout层
- bidirectional,True 就变成一个双向rnn ,默认 False
输入:
- 特征 x (seq_len, batch, input_size)
- 隐含状态 h0 形状 (num_layers*num_directions,batch,hidden_size),
其中num_layers为层数,
num_directions方向数,如果取2则表示双向(bidirectional,),取1则表示单向。
输出:
- output^t (seq_len,batch,num_directions*hidden_size)
- hn (num_layers*num_directions,batch,hidden_size)
属性:
~RNN.weight_ih_l[k] – 第k层可学习的输入层-隐藏层的权重 of shape (hidden_size, input_size) for k = 0. Otherwise, the shape is (hidden_size, num_directions * hidden_size)
~RNN.weight_hh_l[k] – the learnable hidden-hidden weights of the k-th layer, of shape (hidden_size, hidden_size)
~RNN.bias_ih_l[k] – the learnable input-hidden bias of the k-th layer, of shape (hidden_size)
~RNN.bias_hh_l[k] – the learnable hidden-hidden bias of the k-th layer, of shape (hidden_size)
# 简单测试一下
rnn = nn.RNN(input_size=10, hidden_size=20,num_layers= 2)
# 推测:输入维度为10,隐含状态维度20,单向两层网络,输入节点和隐藏层节点之间是全连接
# 所有 w^ih 应该是 20*10,w^hh是20×20,b^ih和b^hh都 是hidden_size。
print("第一层:wih形状{},whh形状{},bih形状{}"
.format(rnn.weight_ih_l0.shape,rnn.weight_hh_l0.shape,rnn.bias_hh_l0.shape))
print(&