RNN

对于人类而言,以前见过的十五会在脑海里留下记忆,虽然随后记忆会慢慢消失,但是每当经过体系,人们对往事的记忆又会影响当下的判断。

而对于卷积神经网络,其相当于人类的视觉,对现在所看到的东西提取特征与分析,去没有记忆能力,所有它只能处理一种特定的视觉任务,没办法根据以前的记忆来处理新的问题。那有没有基于记忆的网络模型呢?RNN!!!

问题介绍

考虑两个句子:
$$
[我,喜欢,你]
$$

$$
[我,不,喜欢,你]
$$

对一句话表达的是喜欢你的意思,第二句话表达的是不喜欢你的意思。如果采用无记忆的网络模型,第一个句子作为训练集,在测试第二个句子时,模型会判断为喜欢你的意思。因为它无法将前文的 **“不”**和 喜欢一起分析,但是如果是模型能记忆 喜欢之前的词,就会预测出不同结果。

RNN基本结构

RNN的基本结构特别简单,就是将网络的输出保存在一个记忆单元中,这个记忆单元和下一次的输入一起进入神经网络中。

rFNlO.png

如图所示,A为神经网络,$[x_0,x_1,…,x_t]$为输入时间序列,RNN把t=0时刻的输出$h_0$又传入了t=1时刻作为输入,把t=1时刻的输出$h_0$又传入了t=2时刻作为输入即:
$$
h_t=tanh(x_t@w_{ih}+b_{ih}+h_{t-1}@w_{hh}+b_{hh})
$$
可以看到网络是单向的,即我们只能知道单侧的信息,比如输入为$x_i$时,除其本身之外,我们只输入$h_{i-1}$,其中只含有$[x_0,x_1,…,x_{i-1}]$的信息,而不知道$[x_{i+1},…,x_n]$的信息。对于文本等信号,常常是需要联系上下文做出解析,这时候就需要能记忆两侧信息的循环神经网络。

其基本结构如图所示:

rVUfR.png

RNN的问题

人的记忆最大的问题是它具有遗忘性,我们总是更加清楚的记得最近发生过的事而遗忘很久之前发生的事情,RNN有同样的问题。越早之前的信息经过网络的次数也就越多,在权值的作用下其在$h_{t-1}$中的占比也就越小。也就是所谓遗忘。这也是RNN没有广泛应用的原因:长时依赖问题

RNN的变式

LSTM(长短期记忆网络)

rVCsg.png

LSTM网络一个时刻的计算如图所示。

首先我们来看所谓记忆单元的运算,$C_{t-1}$为上一步所得到的记忆单元,$C_t$w为这一步所输出的记忆单元,$C_{t-1} \rightarrow C_t$的运算步骤如下:

第一步将上一层的输出$h_{t-1}$和本层输入$x_t$结合在一起做一个线性运算$W_f[h_{t-1},x_t]+b_f$,然后将其通过sigmoid函数作为长时记忆输入$C_{t-1}$的衰减系数:
$$
f_t=sigmoid(W_f[h_{t-1},x_t]+b_f)
$$
可以看到网络具体要保留多少记忆是由前一时刻输出和这一时刻输入共同决定的。

而对于该时刻学到的记忆,其衰减系数跟上述$C_{t-1}$的衰减系数计算方法一样,而当前学习到的记忆$\hat c_t$是通过线性变换$W_c[h_{t-1},x_t]+b_c$和tanh激活函数得到的。
$$
i_t=sigmoid(W_i[h_{t-1},x_t]+b_i)
$$

$$
\hat c_t=tanh(W_c[h_{t-1},x_t]+b_c)
$$

那么时刻t输出的长时记忆单元为:
$$
C_t=f_t \times C_{t-1}+i_t \times \hat c_t
$$

时刻t的输出为:
$$
o_t=sigmoid(W_o[h_{t-1},x_t]+b_o)
h_t=o_t \times tang(C_t)
$$

GRU(门控循环单元)

GRU将长时记忆单元和输入合成了一个更新单元,同时网络不在额外给出记忆状态$C_t$,而是将输出结果$h_t$作为记忆状态不断向后循环传递,网络的输入输出都变得特别简单。

rVKsR.png

其基本单元如图所示,计算如下:
$$
\begin{aligned}
z_t=sigmoid(W_z·[h_{t-1},x_t])\\
r_t=sigmoid(W_r·[h_{t-1},x_t])\\
h’t=tanh((W·[r_t*h{t-1},x_t])\\
h_t=(1-z_t)*h_{t-1}+z_th’_t
\end{aligned}
$$
GRU与LSTM最大的不同就在于记忆状态就是网络的输出结构$h_t$

收敛性问题

由于RNN权重在网络中循环的结构里会被不断地重复使用,那么梯度微小的变化在经过循环结构之后都被放大。所以设置一个固定的学习率并不能有效收敛,同时梯度变化没有规律,所以设置衰减的学习率也不能满足条件。解决办法之一是梯度裁剪(gradient clipping)

RNN的pytorch实现

最简单的标准RNN可以通过nn.RNN()直接调用,其输入,输出参数如下:

Args:
input_size: The number of expected features in the input `x`
hidden_size: The number of features in the hidden state `h`
num_layers: Number of recurrent layers. E.g., setting ``num_layers=2``
would mean stacking two RNNs together to form a `stacked RNN`,
with the second RNN taking in outputs of the first RNN and
computing the final results. Default: 1
nonlinearity: The non-linearity to use. Can be either ``'tanh'`` or ``'relu'``. Default: ``'tanh'``
bias: If ``False``, then the layer does not use bias weights `b_ih` and `b_hh`.
Default: ``True``
batch_first: If ``True``, then the input and output tensors are provided
as `(batch, seq, feature)` instead of `(seq, batch, feature)`.
Note that this does not apply to hidden or cell states. See the
Inputs/Outputs sections below for details. Default: ``False``
dropout: If non-zero, introduces a `Dropout` layer on the outputs of each
RNN layer except the last layer, with dropout probability equal to
:attr:`dropout`. Default: 0
bidirectional: If ``True``, becomes a bidirectional RNN. Default: ``False``

input_size:输入$x_t$的维度特征

hidden_size:输出$h_t$的维度特征