本文主要是结合torch的代码介绍RNN模型的过程及原理
为什么需要RNN
在最基本的全连接神经网络中,我们所建立的网络虽然可以对一个事物进行预测,比如根据输入的图片来进行分类图片里的究竟是猫还是狗还是一根小飞棍,但是全连接神经网络的前一条数据的输入和后一条数据的输入是完全独立的,这就使得它面对一些具有序列信息的数据无法处理。我们说一句话,每个字在这句话中都扮演着不同的角色,你不可能单个拉出来理解。所以一种根据时间来处理信息的神经网络便应运而生了。
RNN的基本结构
其实简单来说RNN的运行过程可以简单理解为同一个模块不断地和输入信息循环相乘得到输出的过程,这种模式可以有效地提取输入特征序列所隐藏的信息,但这也导致了RNN序列过长后容易出现梯度消失的问题(产生的具体原因后面有解释,在这不做过多的展开),下图是RNN的结构细节:
在我自己理解中
w
h
y
w_{hy}
why?:表示
y
t
y_t
yt?提取
h
t
h_{t}
ht?时刻信息的权重;
w
h
h
w_{hh}
whh?:表示
h
t
h_t
ht?提取之前
h
t
?
1
h_{t-1}
ht?1?时刻信息的权重;
w
x
h
w_{xh}
wxh?:表示
h
t
h_t
ht?提取
x
t
x_{t}
xt?时刻输入信息的权重;
其中
x
t
x_t
xt?是我们在t时刻的输入信息,
y
t
y_t
yt?是我们在t这个时间节点的输出信息而
h
t
h_t
ht?是我们
x
t
x_t
xt?与权重矩阵
w
x
h
w_{xh}
wxh?相乘的状态信息,而
w
h
y
w_{hy}
why?,
w
h
h
w_{hh}
whh?和
w
x
h
w_{xh}
wxh?是参数共享的(真正参与与输入信息和状态信息计算的只有这三个矩阵),反向传播时也只有这三个矩阵会被梯度下降算法更新。
torch中的RNN
pytorch中的RNN API文档
input_size:输入特征的维度, 一般rnn中输入的是词向量,那么 input_size 就等于一个词向量的维度,即feature_len; hidden_size::隐藏层神经元个数,或者也叫输出的维度(因为rnn输出为各个时间步上的隐藏状态); num_layers:网络的层数; nonlinearity:激活函数; bias:是否使用偏置,默认为True; batch_first:输入数据的形式,默认是 False,形状为:(seq_len, batch_size, input_dim),也就是将序列长度放在第一位,batch_size放在第二位; dropout:是否应用dropout, 默认不使用,如若使用将其设置成一个0-1的数字即可; birdirectional:是否使用双向的 rnn,默认是 False;
input:当batch_first为默认True时,input的三个维度分别是batch_size、sequence_length和input_size。 output:当batch_first为默认True时,output的三个维度分别是batch_size、sequence_length和output_size。
看个一层的RNN 的例子 example:
rnn = nn.RNN(10, 20, 2)
input = torch.randn(5, 3, 10)
h0 = torch.randn(2, 3, 20)
output, hn = rnn(input, h0)
RNN的优缺点
优点:DNN无法对时间序列上有变化的情况进行处理,而在RNN中,神经元的输出可以在下一个时间段直接作用到自身,即第i层神经元在t时刻的输入,除了(i-1)层神经元在该时刻的输出外,还包括其自身在(t-1)时刻的输出,这使得RNN具有了一定的记忆性,可以更加有效的提取出序列信息。 缺点:网络层次越深, 越容易引起梯度消失, 无论是纵向网络(bp), 还是横向网络(rnn), RNN随着序列的变长梯度消失是几乎无法避免的,虽然RNN使用了tanh而不是sigmoid函数来缓解这个问题,但也仅仅只是缓解而已, 而RNN随着序列的变长梯度消失于具体原因可以归结为激活函数的导数位于[0,1]区间引起的;用一个生动形象的毒鸡汤来举例子就是:
|