【深度学习与自然语言处理 01】循环神经网络RNN

开这一个系列,一方面是为了作为自己的笔记,加深理解,希望自己能够坚持;另一方面是因为csdn复制粘贴内容太多,现在百度都难找到什么有用的信息了,开头几页全是一样的。由于是一边学习一边写,在文章中会加入很多自己的理解,并不一定正确,希望大家在多多包涵,在评论区指正。本篇学习过程中参考了很多邱锡鹏老师的《神经网络与深度学习》,这本书对神经网络的推导非常详细,推荐同为入门者的大家阅读。

前言

深度学习,准确的说是神经网络,实际上源于上世纪六十年代的感知机技术,当时由于技术不成熟和算力不发达,并没有在当时就取得特别好的成果。这门技术爆发是在计算机图形学(CV)领域,大家惊奇地发现虽然深度学习可解释性很差,但它作为一种仿照人类大脑的模型,在图像分类、目标检测等领域还真就碾压了传统方法,甚至取得了比人类还要好的成绩。之后,人们又发现,通过使用RNN,深度学习在自然语言处理这个领域也居然也取得了非常好的效果。

到目前为止,人工智能的两大明珠,CV和NLP的榜单的绝大部分都被深度学习方法占据。因此,这个方向现在已经被广泛接受作为人工智能这个概念的代名词。

在本篇博客中,为了节约篇幅将会把深度学习和机器学习基础的部分省略,默认大家已经有了这部分知识,包括最基本的全连接神经网络、反向传播、梯度下降、卷积层。

这部分推荐当时我入门的博客龙心尘:从初等数学的角度初探神经网络 以及龙心尘:深度学习与计算机视觉系列这两个系列,或者邱锡鹏老师的《神经网络与深度学习》进行学习。如果对自己理解能力有信心,直接往下看也成。

本系列将着重介绍深度学习在自然语言处理领域的应用。因此,传统的统计学方法在这里也不会被详细介绍。有兴趣的朋友可以读《统计自然语言处理》这本书去了解,但不是必须掌握。


RNN

循环神经网络(Recurrent Neural Network,RNN)是一类具有短期记忆能力的神经网络。

引入它的动机(Motivation),是前馈神经网络中信息的流通方向是单向的,如输入一张图片对其分类,对下一张的分类任务并没有影响。但是,在现实的任务中有很多是与上下文有关的,比如视频、语音(当然,还有语言)都是一种“序列”的形式存在,把他们单独拆分为帧、段、词再这么独立进行判别,理所当然会损失其上下文信息。就算把他们都看作是一个巨大的输入,也会有序列长度不固定的问题存在。因此当处理这一类问题时,需要一种更强的模型,即使这里提到的循环神经网络RNN。

它一般是以层的结构作为整个网络的组成部分。这个结构之所以被称为循环,是因为其会利用上一次输出的信息,也就是说,该层在进行计算时不仅利用 t t t 时刻输入 x ⃗ t \vec x_t x t,还会运用本层上一次的输出 h ⃗ t − 1 \vec h_{t-1} h t1,RNN模型如下图所示。
【深度学习与自然语言处理 01】循环神经网络RNN_第1张图片
在典型的RNN中,以上模型的数学形式为:
h t ⃗ = f ( U h t − 1 ⃗ + W x t ⃗ + b ⃗ ) (0) \vec{h_t}=f(U\vec{h_{t-1}}+W\vec{x_t}+\vec b) \tag{0} ht =f(Uht1 +Wxt +b )(0)
其中 f f f是非线性函数,可以为ReLU或sigmoid等函数。
这样,模型除了当前的输入,还可以利用之前的结果。RNN层可以看做是一层,但在时间维度上,这个模型可以展开为一个 t t t 层的网络,如下图中所示。


RNN的参数更新

RNN也可以通过梯度下降方法进行学习,参数的更新量也通过反向传播得到,但由于引入了时序的概念,这一过程的方式有一定的变化。

我们定义整个序列总体的损失函数为每个时刻的损失函数累加,即:
L = ∑ t = 1 T L t L = \sum^{T}_{t=1}L_t L=t=1TLt
与传统的梯度下降相同,参数 U U U的更新为
∂ L ∂ U = ∑ t = 1 T ∂ L t ∂ U (1) \frac{\partial L}{\partial U}=\sum^{T}_{t=1}\frac{\partial L_t}{\partial U} \tag{1} UL=t=1TULt(1)
但由于在公式(0)中,得出输出 h t h_t ht的过程中存在递归项 h t − 1 h_{t-1} ht1,对 h t − 1 h_{t-1} ht1的处理的不同,对应RNN中两种计算梯度的策略:

  • 随时间反向传播 (Backpropagation Through Time,BPTT)
  • 实时循环学习 (Real-Time Recurrent Learning,RTRL)

BPTT方法的思想比较简单,即在一次完整的向前传播,即整个长度为 T T T的序列得出结果后,将RNN看做是层数为 T T T的网络进行向后传播。

对每一个时刻 t ∈ [ 1 , T ] t\in [1,T] t[1,T],RNN都可以看做是一个沿时间维度展开的 t t t层的网络,例如当t=3时,RNN可以展开为一个3层网络,如图所示。
【深度学习与自然语言处理 01】循环神经网络RNN_第2张图片
这个网络中共享U这个权重,因此要求U的梯度,实际上是对这t层网络中,每一层网络中的U求导后累加,公式为:

∂ L t ∂ U = ∑ k = 1 t ∂ L t ∂ h k ⃗ ∂ h k ⃗ ∂ U (2) \frac{\partial L_t}{\partial U} = \sum^{t}_{k=1}\frac{\partial L_t}{\partial \vec{h_k}}\frac{\partial \vec{h_k}}{\partial U}\tag{2} ULt=k=1thk LtUhk (2)
将(2)带入(1)得:
∂ L ∂ U = ∑ t = 1 T ∑ k = 1 t ∂ L t ∂ z k ⃗ ∂ z k ⃗ ∂ U (3) \frac{\partial L}{\partial U} = \sum^{T}_{t=1}\sum^{t}_{k=1}\frac{\partial L_t}{\partial \vec{z_k}}\frac{\partial \vec{z_k}}{\partial U}\tag{3} UL=t=1Tk=1tzk LtUzk (3)
这样,在一次序列的完整的向前传播之后,通过该公式反向传播即可获得更新梯度。

而RTRL算法,则是基于对以下思想:
∂ h t ∂ U = ∂ t h ∂ z t ( ∂ + z t ∂ U + U ∂ h t − 1 ∂ U ) \frac{\partial h_t}{\partial U}=\frac{\partial ^ h_t}{\partial z_t}(\frac{∂^+z_{t}}{∂U}+U\frac{∂h_{t-1}}{∂U}) Uht=ztth(U+zt+UUht1)
其中 ∂ + ∂^+ +表示保持 h t − 1 h_{t-1} ht1不变求导。

这其中, ∂ h t − 1 ∂ U \frac{∂h_{t-1}}{∂U} Uht1一项实际上可以在每次向前计算后得出并保存,交给第t次计算时使用。这样就可以在每一个时刻t实时地更新参数。

RTRL算法和BPTT算法都是基于梯度下降的算法,分别通过前向模式和反向模式应用链式法则来计算梯度。在循环神经网络中,一般网络输出维度远低于输入维度,因此BPTT算法的计算量会更小,但是BPTT算法需要保存所有时刻的中间梯度,空间复杂度较高。RTRL算法不需要梯度回传,因此非常适合用于需要在线学习或无限序列的任务中。

你可能感兴趣的:(机器学习,NLP)