(十五)从零开始学人工智能-深度学习基础2

一、循环神经网络基础

​ 在前面的文章中,介绍了全连接神经网络(DNN)和卷积神经网络(CNN),以及它们的训练和使用。回忆一下,它们都是能单独的处理一个个的输入,前一个输入和后一个输入是完全没有关系的。然后,现实当中是,某些任务需要能够更好的处理序列性质的信息,即前面的输入和后面的输入是有关系的。例如,当我们理解一句话意思时,孤立的理解这句话的每个词都是不够的,我们需要处理这些词连接起来的整个序列;当我们处理视频时,也不能只单独的去分析每一帧,而要分析这些帧连接起来的整个序列。这时,使用我们前文介绍的DNN和CNN是不够的,而需要用到深度学习领域中另一类非常重要的神经网络:循环神经网络(Recurrent Neural Network, RNN)。RNN种类繁多,过程繁琐,本部分首先对其结构进行剥茧抽丝,以理解RNNs及其训练算法;进一步地,介绍两中常见的RNN类型:长短时记忆网络(Long Short-Term Memory Network,LSTM),门控循环单元(Gated Recurrent Unit, GRU)。

1.1 从语言模型开始

为什么要从语言模型开始呢?因为,RNN是在自然语言处理领域中最先被用起来的,例如,RNN可以构建语言模型。那什么是语言模型呢?

我们可以让电脑做这样一个练习:写出一个句子前面的一些词,然后,让电脑帮我们写出接下来的一个词。比如下面这句话:

我昨天上学迟到了,老师批评了____。

在这个句子中,接下来的词最有可能的是“我”,而不是“小明”,更不会是“吃饭”。

在这个例子中,语言模型是这样的一个东西:给定一个一句话前面的部分,预测接下来最有可能的一个词是什么。

语言模型是对一种语言的特征进行建模,它有很多用处。比如在语音转文本(STT)的应用中,声学模型输出的结果,往往是若干个可能的候选词,这时候就需要语言模型来从这些候选词中选择一个最有可能的。当然,它同样也可以用在图像到文本的识别中国(OCR)。

在使用RNN之前,语言模型主要采用N-Gram算法。N是一个自然数,比如2或者3.它的含义是:假设一个词出现的概率只与前面N个词相关。我们以2-Gram为例,对这句话给出部分进行分词:

我|昨天|上学|迟到|了|,|老师|批评|了|____。

如果用2-Gram进行建模,那么电脑在预测的时候,只会看到前面的“了”,然后,电脑会在语料库中,搜索“了”后面最有可能的一个词。不管最后电脑选择的是不是“我”,显然这个模型是不靠谱的,因为“了”前面说了那么一大推实际上是没有用到的。如果使用3-Gram模型呢,会搜索“批评了”后面最有可能的词,感觉比2-Gram靠谱了不少,但还是远远不够的。因为这句话最关键的信息“我”,远在9个词之前。

看到这儿,大家可能会想,可以继续提升N的值呀,比如4-Gram、5-Gram、 … \dots 。实际上,大家再深入想一下就会发现,这个想法是没有实用性的,因为当我们想处理任意长度的句子时,N设为多少都是不合适的;另外,模型的大小和N的关系是指数级的,4-Gram模型就会占用海量的存储空间。

所以,就该轮到RNN出场了,RNN理论上可以往前看(或往后看)任意多个词。

1.2 什么是循环神经网络?

循环神经网络种类繁多,我们先从最简单的基础循环神经网络开始吧~

1.2.1 基本循环神经网络

下图是一个简单的循环神经网络,它由输入层、一个隐藏层和一个输出层组成:

循环神经网络的实在是太难画出来了,网上所有大神们都不得不用了这种抽象的手法。不过,仔细看的话,如果把上面有 W W W的那个带箭头的圈去掉,它就变成了最普通的全连接网络了。 x x x是一个向量,它表示输入层的值(这里没有画出来表示输入层神经元节点的圆圈), s s s是一个向量,它表示隐藏层的值(这里隐藏层画了一个节点,你也可以想象这一层其实是多个节点,节点数目与向量 s s s的维度相同); U U U是输入层到隐藏层的权重矩阵; O O O也是一个向量,它表示输出层的值; V V V是隐藏层到输出层的权重矩阵。那么,现在我们来看看 W W W是什么,循环神经网络的隐藏层的值 s s s不仅仅取决于当前这次输入 x x x,还取决于上一次隐藏层的值 s s s。权重矩阵 W W W就是隐藏层上一次的值作为这一次的输入的权重。

如果我们把上面的图展开,循环神经网络的也可以画成下面的样子:

在这里插入图片描述

现在看上去就比较清晰了,这个网络在 t t t时刻接收到输入 x t x_t xt之后,隐藏层的值是 s t s_t st,输出值是 o t o_t ot。关键一点是, s t s_t st的值不仅仅取决于 x t x_t xt,还取决于 s t − 1 s_{t-1} st1。我们可以用下面的公式来表示循环神经网络的计算方法:

o t = g ( V s t ) (1) o_t=g(Vs_t) \tag{1} ot=g(Vst)(1)

s t = f ( U x t + W s t − 1 ) (2) s_t = f(Ux_t+Ws_{t-1}) \tag{2} st=f(Uxt+Wst1)(2)

式1是输出层的计算公式,输出层是全连接层,也就是它的节点都是和隐藏层的每个节点相连。 V V V是一个输出层的权重矩阵, g g g是激活函数。式2是隐藏层的计算公式,它是循环层。 U U U是输入 x x x的权重矩阵, W W W是上一次输出值 s t − 1 s_{t-1} st1作为这一次输入的权重矩阵, f f f是激活函数。

从上面的公式我们可以看出,循环层和全连接层的区别就是循环层多了一个权重矩阵 W W W

如果反复把式2带入到式1,我们可以得到:

o t = g ( V s t ) = V f ( U x 1 + W s t − 1 ) = V f ( U x t + W f ( U x t − 1 + W s t − 2 ) ) = V f ( U x t + W f ( U x t − 1 + W f ( U x t − 2 + W s t − 3 ) ) ) = V f ( U x t + W f ( U x t − 1 + W f ( U x t − 2 + W f ( U x t − 3 + …   ) ) ) ) \begin{aligned}o_t &=g(Vs_t) \\ &=Vf(Ux_1+Ws_{t-1})\\ &=Vf(Ux_t+Wf(Ux_{t-1}+Ws_{t-2}))\\&=Vf(Ux_t+Wf(Ux_{t-1}+Wf(Ux_{t-2}+Ws_{t-3})))\\ &=Vf(Ux_t+Wf(Ux_{t-1}+Wf(Ux_{t-2}+Wf(Ux_{t-3}+\dots)))) \end{aligned} ot=g(Vst)=Vf(Ux1+Wst1)=Vf(Uxt+Wf(Uxt1+Wst2))=Vf(Uxt+Wf(Uxt1+Wf(Uxt2+Wst3)))=Vf(Uxt+Wf(Uxt1+Wf(Uxt2+Wf(Uxt3+))))

从上面可以看出,循环神经网络的输出值 o t o_t ot,是受前面历次输入值 X t X_t Xt X t − 1 X_{t-1} Xt1 X t − 2 X_{t-2} Xt2 … \dots 影响的,这也是为什么循环神经网络可以往前看任意多个输入值的原因。

1.2.2 双向循环神经网络

对于语言模型来说,很多时候光看前面的词是不够的,比如下面这句话:

我的手机坏了,我打算____一部新手机。

可以想象的是,如果我们只看到横线前面的词,手机坏了,那么我是打算修一修?换一部新的手机?还是哭哭?这些都是无法确定的。但是如果我们还看到横线后面的词是“一部新手机”,那么,横线上的词填“买”的概率就大得多了。

在上一节中的基本循环神经网络是无法对此进行建模的,因此,我们需要双向循环神经网络,如下图所示:

在这里插入图片描述

当遇到这种从未来穿越回来的场景时,难免处于懵逼的状态。不过我们还是可以用屡试不爽的老办法:先分析一个特殊场景,然后再总结一般的规律。我们先考虑上图中, y 2 y_2 y2的计算。

从上图可以看出,双向循环神经网络的隐藏层要保存两个值,一个 A A A参与正向计算,另一个值 A ′ A' A参与计算。最终的输出值 y 2 y_2 y2取决于 A 2 A_2 A2 A 2 ′ A_2' A2。其计算方法为:

y 2 = g ( V A 2 + V ′ A 2 ′ ) y_2=g(VA_2+V'A_2') y2=g(VA2+VA2)

其中 A 2 A_2 A2 A 2 ′ A_2' A2的计算为:

A 2 = f ( W A 1 + U X 2 ) A_2=f(WA_1+UX_2) A2=f(WA1+UX2)

A 2 ′ = f ( W ′ A 3 ′ + U ′ X 2 ) A_2'=f(W'A_3'+U'X_2) A2=f(WA3+UX2)

至此,我们已经可以看出一般的规律:正向计算时,隐藏层的值 S t S_t St S t − 1 S_{t-1} St1有关;反向计算时,隐藏层的值 S t ′ S_t' St S t + 1 ′ S_{t+1}' St+1有关;最终的输出取决于正向和反向计算的加和。现在,我们仿照式1和式2,写出双向循环神经网络的计算方法:

O t = g ( V S t + V ′ S t ′ ) O_t=g(VS_t+V'S_t') Ot=g(VSt+VSt)

S t = f ( U X t + W S t − 1 ) S_t=f(UX_t+WS_{t-1}) St=f(UXt+WSt1)

S t ′ = f ( U ′ X t + W ′ S t + 1 ′ ) S_t'=f(U'X_t+W'S_{t+1}') St=f(UXt+WSt+1)

从上面的三个公式可以看到,正向计算和反向计算不共享权重,也就是说 U U U U ′ U' U W W W W ′ W' W V V V V ′ V' V都是不同的权重矩阵。

1.2.3 深度循环神经网络

前面我们介绍的循环神经网络只有一个隐藏层,当然了,也可以堆叠两个以上的隐藏层,这样就得到了深度循环神经网络。如下图所示:

在这里插入图片描述

我们把第 i i i个隐藏层的值表示为 S t ( i ) S_t^{(i)} St(i) S t ′ ( i ) S_t'^{(i)} St(i),则深度循环神经网络的计算方式可以表示为:

O t = g ( V ( i ) S t ( i ) + V ′ ( i ) S t ′ ( i ) ) O_t=g(V^{(i)}S_t^{(i)}+V'^{(i)}S_t'^{(i)}) Ot=g(V(i)St(i)+V(i)St(i))

S t ( i ) = f ( U ( i ) S t i − 1 + W ( i ) S t − 1 ) S_t^{(i)}=f(U^{(i)}S_t^{i-1}+W^{(i)}S_{t-1}) St(i)=f(U(i)Sti1+W(i)St1)

S t ′ ( i ) = f ( U ′ ( i ) S t ′ ( i − 1 ) + W ′ ( i ) S t + 1 ′ ) S_t'^{(i)}=f(U'^{(i)}S_t'^{(i-1)}+W'^{(i)}S_{t+1}') St(i)=f(U(i)St(i1)+W(i)St+1)

⋯ \cdots

S t ( 1 ) = f ( U ( 1 ) X t + W ( 1 ) S t − 1 ) S_t^{(1)}=f(U^{(1)}X_t+W^{(1)}S_{t-1}) St(1)=f(U(1)Xt+W(1)St1)

S t ′ ( 1 ) = f ( U ′ ( 1 ) X t + W ′ ( 1 ) S t + 1 ′ ) S_t'^{(1)}=f(U'^{(1)}X_t+W'^{(1)}S_{t+1}') St(1)=f(U(1)Xt+W(1)St+1)

1.3 循环神经网络的训练

1.3.1 循环神经网络的训练算法:BPTT

B P T T BPTT BPTT算法是针对循环层的训练算法,它的基本原理和 B P BP BP算法是一样的,也包含同样的三个步骤:

  1. 前向计算每个神经元的输出值;

  2. 反向计算每个神经元的**误差项 δ j \delta_j δj**值,它是误差函数 E E E对神经元 j j j的加权输入 n e t j net_j netj的偏导数;

  3. 计算每个权重的梯度。

    循环层如下图所示:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a8kYlBqb-1583227061275)(./img2/5.png)]

最后再用随机梯度下降算法更新权重。

前向计算

使用前面的式2对循环层进行前向计算:

S t = f ( U X t + W S t − 1 ) S_t=f(UX_t+WS_{t-1}) St=f(UXt+WSt1)

注意:上面的 S t S_t St X t X_t Xt S t − 1 S_{t-1} St1都是向量,用黑体字母表示(在这里都用大写替代表示了😂,将就看吧❤️);而 U U U V V V是矩阵,用大写字母表示。向量的下标表示时刻,例如, S t S_t St表示在 t t t时刻向量 S S S的值。

我们假设输入向量 X X X的维度是 m m m,输出向量 S S S的维度是 n n n,则矩阵 U U U的维度是 n n n x m m m,矩阵 W W W的维度是 n n n x n n n。下面是我们上式展开成矩阵的样子,看起来更直观一些:

[ s 1 t s 2 t ⋮ s n t ] = f ( [ u 11 u 12 ⋯ u 1 m u 21 u 22 ⋯ u 2 m ⋮ ⋮ ⋱ ⋮ u n 1 u n 2 ⋯ u n m ] [ x 1 x 2 ⋮ x m ] + [ w 11 w 12 ⋯ w 1 n w 21 w 22 ⋯ w 2 n ⋮ ⋮ ⋱ ⋮ w n 1 w n 2 ⋯ w n m ] [ s 1 t − 1 s 2 t − 1 ⋮ s n t − 1 ] ) \left[ \begin{matrix} s_1^t \\ s_2^t \\ \vdots \\ s_n^t \end{matrix} \right]=f(\left[ \begin{matrix} u_{11} & u_{12} & \cdots & u_{1m}\\ u_{21} & u_{22} & \cdots & u_{2m} \\ \vdots & \vdots & \ddots & \vdots \\ u_{n1} & u_{n2} & \cdots & u_{nm} \end{matrix} \right]\left[ \begin{matrix} x_1 \\ x_2 \\ \vdots \\ x_m \end{matrix} \right]+\left[ \begin{matrix} w_{11} & w_{12} & \cdots & w_{1n}\\ w_{21} & w_{22} & \cdots & w_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ w_{n1} & w_{n2} & \cdots & w_{nm} \end{matrix} \right]\left[ \begin{matrix} s_1^{t-1} \\ s_2^{t-1} \\ \vdots \\ s_n^{t-1} \end{matrix} \right]) s1ts2tsnt=f(u11u21un1u12u22un2u1mu2munmx1x2xm+w11w21wn1w12w22wn2w1nw2nwnms1t1s2t1snt1)

上式中,手写体字母表示向量的一个元素,它的下标表示它是这个向量的第几个元素,它的上标表示第几个时刻。例如, s j t s_j^t sjt表示向量 S S S的第 j j j个元素在 t t t时刻的值; u j i u_{ji} uji表示输入层第 i i i个神经元到循环层第 j j j个神经元的权重; w j i w_{ji} wji表示循环层第 t − 1 t-1 t1时刻的第 i i i个神经元到循环层第 t t t个时刻的第 j j j个神经元的权重。

误差项的计算

B T P P BTPP BTPP算法将第 l l l t t t时刻的**误差项 δ t l \delta_t^l δtl**值沿两个方向传播,一个方向是其传递到上一层网络,得到 δ t l − 1 \delta_t^{l-1} δtl1,这一部分只和权重矩阵 U U U相关;另一个方向是将其沿时间线传递到初始时刻 t 1 t_1 t1,得到 δ 1 l \delta_1^l δ1l,这部分只和权重矩阵 W W W相关。

我们用向量 n e t t net_t nett表示神经元在 t t t时刻的加权输入,得到:

n e t t = U X t + W S t − 1 net_t=UX_t+WS_{t-1} nett=UXt+WSt1

S t − 1 = f ( n e t t − 1 ) S_{t-1}=f(net_{t-1}) St1=f(nett1)

因此:

∂ n e t t ∂ n e t t − 1 = ∂ n e t t ∂ S t − 1 ∂ S t − 1 ∂ n e t t − 1 \frac{\partial net_t}{\partial net_{t-1}}=\frac{\partial net_t}{\partial S_{t-1}}\frac{\partial S_{t-1}}{\partial net_{t-1}} nett1nett=St1nettnett1St1

约定:我们使用 a a a表示列向量,用 a T a^T aT表示行向量。

上式第一项是向量函数对向量求导,其结果为 J a c o b i a n Jacobian Jacobian矩阵:

∂ n e t t ∂ S t − 1 = [ ∂ n e t 1 t ∂ S 1 t − 1 ∂ n e t 1 t ∂ S 2 t − 1 ⋯ ∂ n e t 1 t ∂ S n t − 1 ∂ n e t 2 t ∂ S 1 t − 1 ∂ n e t 2 t ∂ S 2 t − 1 ⋯ ∂ n e t 2 t ∂ S n t − 1 ⋮ ⋮ ⋱ ⋮ ∂ n e t n t ∂ S 1 t − 1 ∂ n e t n t ∂ S 2 t − 1 ⋯ ∂ n e t n t ∂ S n t − 1 ] = [ w 11 w 12 ⋯ w 1 n w 21 w 22 ⋯ w 2 n ⋮ ⋮ ⋱ ⋮ w n 1 w n 2 ⋯ w n m ] = W \begin{aligned}\frac{\partial net_t}{\partial S_{t-1}} &=\left[ \begin{matrix} \frac{\partial net_1^t}{\partial S_1^{t-1}} & \frac{\partial net_1^t}{\partial S_2^{t-1}} & \cdots & \frac{\partial net_1^t}{\partial S_n^{t-1}} \\ \frac{\partial net_2^t}{\partial S_1^{t-1}} & \frac{\partial net_2^t}{\partial S_2^{t-1}} & \cdots & \frac{\partial net_2^t}{\partial S_n^{t-1}} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{\partial net_n^t}{\partial S_1^{t-1}} & \frac{\partial net_n^t}{\partial S_2^{t-1}} & \cdots & \frac{\partial net_n^t}{\partial S_n^{t-1}} \end{matrix} \right] \\ &=\left[ \begin{matrix} w_{11} & w_{12} & \cdots & w_{1n}\\ w_{21} & w_{22} & \cdots & w_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ w_{n1} & w_{n2} & \cdots & w_{nm} \end{matrix} \right]\\ &=W \end{aligned} St1nett=S1t1net1tS1t1net2tS1t1netntS2t1net1tS2t1net2tS2t1netntSnt1net1tSnt1net2t

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值