从零实现深度学习框架——RNN从理论到实战理论

Posted 愤怒的可乐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零实现深度学习框架——RNN从理论到实战理论相关的知识,希望对你有一定的参考价值。

引言

本着“凡我不能创造的,我就不能理解”的思想,本系列文章会基于纯Python以及NumPy从零创建自己的深度学习框架,该框架类似PyTorch能实现自动求导。

要深入理解深度学习,从零开始创建的经验非常重要,从自己可以理解的角度出发,尽量不使用外部完备的框架前提下,实现我们想要的模型。本系列文章的宗旨就是通过这样的过程,让大家切实掌握深度学习底层实现,而不是仅做一个调包侠。

本文介绍RNN,一种用于处理序列数据的神经网络。

循环神经网络

循环神经网络(Recurrent Neural Network,RNN)是包含循环连接的网络,即有些单元是直接或间接地依赖于它之前的。

本文我们学习一种叫做Elman网络的循环网络,或称为简单循环网络(本文中的RNN都代表该网络)。隐藏层包含一个循环连接作为其输入。即,基于当前输入和前一时刻隐藏状态计算当前隐藏状态。

上图展示了RNN的结构,与普通前馈网络一样,表示当前输入 x t x_t xt的向量乘以权重矩阵,然后经过非线性激活函数来计算隐藏单元的值 h t h_t ht。然后用于计算相应的输出 y t y_t yt​。

该网络在处理序列时,一次(一个时间步)顺序地处理序列中的一个元素,与我们之前看到的基于窗口的方法不同。我们使用下表来表示时间,这样, x t x_t xt表示时刻(时间步) t t t的输入向量 x x x。与前馈网络的关键区别在于上图虚线显示的循环连接。此连接使用上一个时刻隐藏层的值来增强对于当前时刻隐藏层计算的输入。

前一时刻的隐藏层提供了一种记忆(或上下文)的功能,可以提供之前的信息为未来做决定提供帮助。重要的是,这种方法理论上不需要对前文的长度进行限制,不过实际上过远的信息很难有效的保留。

前向传播

RNN中的前向传播(推理)过程和前馈网络差不多。但在使用RNN处理一个序列输入时,需要将RNN按输入时刻展开,然后将序列中的每个输入依次对应到网络不同时刻的输入上,并将当前时刻网络隐藏层的输出也作为下一时刻的输入。

循环网络处理序列输入的示意图,图片来自https://medium.com/deeplearningbrasilia/deep-learning-recurrent-neural-networks-f9482a24d010

为了计算时刻 t t t的输入 x t x_t xt对应的输出 y t y_t yt(图中是 o t o_t ot),我们需要先计算隐藏状态 h t h_t ht。为了计算它,让输入 x t x_t xt乘以权重矩阵 W W W以及前一时刻的隐藏状态 h t − 1 h_t-1 ht1乘以权重矩阵 U U U。然后把它们的结果加起来,并经过一个激活函数 g g g,通常为 tanh ⁡ \\tanh tanh函数,计算当前的隐藏状态 h t h_t ht。此时,我们可以通过 h t h_t ht来生成输出向量 y t y_t yt
h t = g ( U h t − 1 + W x t ) y t = f ( V h t ) (1) \\beginaligned h_t &= g(Uh_t-1 + Wx_t) \\\\ y_t &= f(Vh_t) \\endaligned \\tag 1 htyt=g(Uht1+Wxt)=f(Vht)(1)
(为了理解方便,没有写出偏置项,实际上每个矩阵乘法都可以带一个偏置项)

这里要注意维度。我们用 d i n , d h , d o u t d_in,d_h,d_out din,dh,dout分别代表输入、隐藏和输出层的大小。那么这三个权重矩阵的维度是: W ∈ R d h × d i n , U ∈ R d h × d h , V ∈ R d o u t × d h W \\in \\BbbR^d_h \\times d_in, U \\in \\BbbR^d_h \\times d_h,V \\in \\BbbR^d_out \\times d_h WRdh×din,URdh×dh,VRdout×dh​。

如果是多分类问题, y t y_t yt softmax \\textsoftmax softmax函数计算而成:
y t = softmax ( V h t ) (2) y_t = \\textsoftmax(Vh_t) \\tag 2 yt=softmax(Vht)(2)
可以看到,时刻 t t t的计算需要啊前一个时刻 t − 1 t-1 t1的隐藏层激活值(隐藏状态)。显然,这是一种递归形式的定义,从序列开始到序列结束。每个时刻的输入经过层层递归,对最终的输出产生一定影响,每个时刻的隐藏状态 h t h_t ht承载了 1 ∼ t 1\\sim t 1t时刻的全部输入信息,因此循环神经网络中的隐藏单元也被称为记忆单元。

上图简单神经网络的前向推理。注意,矩阵 U , W , V U,W,V U,W,V在每个时刻都是共享的,每个时刻都会计算一个 h i h_i hi y i y_i yi

这里初始时隐藏状态 h 0 = 0 h^0=0 h0=0

学习

我们有三个权重要更新:输入层到隐藏层的权重 W W W;前一时刻隐藏层到当前时刻隐藏层的权重 U U U;隐藏层到输出层的权重 V V V

但更新时与前馈网络不同,主要有两点。一,为了计算时刻 t t t的损失,我们需要时刻 t − 1 t-1 t1的隐藏状态;二,时刻 t t t的隐藏状态同时影响了时刻 t t t的输出和时刻 t + 1 t+1 t+1的隐藏状态。所以,也影响了时刻 t + 1 t+1 t+1的输出和损失。因此,要评估 h t h_t ht​累积的损失,我们需要知道它对当前输出以及后续输出的影响。

RNN的沿着时间反向传播,图片来自https://mmuratarat.github.io/2019-02-07/bptt-of-rnn

此时,需要修改反向传播算法,形成两阶段的算法来训练RNN中的权重。第一阶段,在第一次传播中,我们执行正向推理,如上图右边黑色箭头所代表的方向(从左到右),计算 h t , y t h_t,y_t ht,yt​,在每个时刻累积损失,同时保存隐藏状态的值,以便在第二阶段使用。

在第二阶段,我们反向处理序列,从最后的输出往前计算梯度,即从右到左,如上图红色箭头所示。比如计算了 x t − 1 x_t-1 xt1处的梯度后,得到的损失还需要在前一步 x t x_t xt处使用。这种方法被称为沿着时间反向传播(Backpropagation Through Time,BPTT)。

我们说这里介绍的是Elman网络,那还有其他什么网络吗?

另一种称为Jordan网络。可以用以下公式来说明它们的区别:

Elman网络:
h t = g ( W h x t + U h h t − 1 + b h ) y t = f ( W y h t + b y ) \\beginaligned h_t &= g(W_hx_t + U_hh_t-1 + b_h) \\\\ y_t &= f(W_yh_t + b_y) \\endaligned htyt=g(Wh以上是关于从零实现深度学习框架——RNN从理论到实战理论的主要内容,如果未能解决你的问题,请参考以下文章

深度学习理论 循环神经网络 RNN

Keras深度学习实战(27)——循环神经详解与实现

Keras深度学习实战(27)——循环神经详解与实现

入门实战《深度学习技术图像处理入门》+《视觉SLAM十四讲从理论到实践》

对比学习资料《深度学习入门:基于Python的理论与实现》+《深度学习原理与实践》+《深度学习理论与实战基础篇》电子资料

深度学习理论与实战PyTorch实现