我来钱庙复知世依,似我心苦难归久,相须莱共游来愁报远。近王只内蓉者征衣同处,规廷去岂无知草木飘。

你可能以为上面的诗句是某个大诗人所作,事实上上面所有的内容都是循环神经网络写的,是不是感觉很神奇呢?其实这里面的原理非常简单,只需要对循环神经网络有个清楚的理解,那么就能够实现上面的效果,在读完本篇文章之后,希望大家都能够学会如何使用循环神经网络来创作文本。

本次讲解RNN我会从以下四个方面来讲解:

  1. 语言模型
  2. 基本RNN
  3. 双向RNN
  4. RNN训练

    话不多说,开讲了。

1、语言模型

我们可以和电脑玩一个游戏,我们写出一个句子前面的一些词,然后,让电脑帮我们写下接下来的一个词。比如下面这句:

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

我们给电脑展示了这句话前面这些词,然后,让电脑写下接下来的一个词。在这个例子中,接下来的这个词最有可能是『我』,而不太可能是『小明』,甚至是『吃饭』。

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

正式定义:敲黑板!!!

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

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

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

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

现在我们可能会想,可以提升继续提升N的值呀,比如4-Gram、5-Gram…….。实际上,这个想法是没有实用性的。因为我们想处理任意长度的句子,N设为多少都不合适;另外,模型的大小和N的关系是指数级的,4-Gram模型就会占用海量的存储空间。所以,该轮到RNN出场了,RNN理论上可以往前看(往后看)任意多个词。

2、基本RNN

首先介绍一下什么是RNN,这里我们从一个最简单的RNN结构来进行深入理解。
灰灰深入浅出讲解循环神经网络(RNN)
不知道童鞋们能够理解这个图吗,反正我刚开始学习的时候是懵逼的,每个结点到底代表的是一个值的输入,还是说一层的向量结点集合,隐藏层又如何可以连接到自己等等这些疑惑~

我们现在这样来理解,先不看有W的那个带箭头的圈,它就变成了最普通的全连接神经网络。x是一个向量,它表示输入层的值这里面没有画出来表示神经元节点的圆圈);s是一个向量,它表示隐藏层的值(这里隐藏层面画了一个节点,你也可以想象这一层其实是多个节点,节点数与向量s的维度相同);

U是输入层到隐藏层的权重矩阵,o也是一个向量,它表示输出层的值;V是隐藏层到输出层的权重矩阵

那么,现在我们来看看W是什么。循环神经网络的隐藏层的值s不仅仅取决于当前这次的输入x,还取决于上一次隐藏层的值s。权重矩阵 W就是隐藏层上一次的值作为这一次的输入的权重。

这里有个对应该抽象图的具体图:

灰灰深入浅出讲解循环神经网络(RNN)

从上面这个图就能够很清楚的看到,上一时刻的隐藏层是如何影响当前时刻的隐藏层的

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

灰灰深入浅出讲解循环神经网络(RNN)

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

Ot=g(Vst) (式1)
St=f(Uxt+Wst1) (式2)

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

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

如果反复把式2带入到式1,我们将得到:
Ot=g(Vst)
=Vf(Uxt+Wst1)
=Vf(Uxt+Wf(Uxt1+Wst2))
=Vf(Uxt+Wf(Uxt1+Wf(Uxt2+Wst3)))
=Vf(Uxt+Wf(Uxt1+Wf(Uxt2+Wf(Uxt3+...))))

从上面可以看出,循环神经网络的输出值 ot,是受前面历次输入值xtxt1xt2xt3…影响的,这就是为什么循环神经网络可以往前看任意多个输入值的原因。

3、双向RNN

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

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

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

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

灰灰深入浅出讲解循环神经网络(RNN)
当遇到这种从未来穿越回来的场景时,难免处于懵逼的状态。不过我们还是可以用屡试不爽的老办法:先分析一个特殊场景,然后再总结一般规律。我们先考虑上图中y2的计算。

从上图可以看出,双向卷积神经网络的隐藏层要保存两个值,一个A参与正向计算,另一个值A’参与反向计算。最终的输出值y2取决于A2A2。其计算方法为:
y2=g(VA2+VA2)
A2A2则分别计算:
A2=f(WA1+Ux2)
A2=f(WA3+Ux2)
现在,我们已经可以看出一般的规律:正向计算时,隐藏层的值stst1有关;反向计算时,隐藏层的值stst+1有关;最终的输出取决于正向和反向计算的加和。现在,我们仿照式1和式2写出双向循环神经网络的计算方法:
ot=g(Vst+Vst)
st=f(Uxt+Wst1)
st=f(Uxt+Wst+1)
从上面三个公式我们可以看到,正向计算和反向计算不共享权重,也就是说UUWWVV都是不同的权重矩阵。

至此双向RNN就告一段落了。回过头来看前面讲的东西,可以发现,不管是基础RNN也好,双向RNN也好,都是只有一个隐藏层。那我们当然可以堆叠两个以上的隐藏层啊,这样就得到了深度循环神经网络。由于本文只是带领大家入门,更深层的东西先不说,后续会另开博客讲解。如果观众老爷有兴趣的,可以研究下这篇:零基础入门深度学习(5) - 循环神经网络

4、RNN的训练

一般而言,RNN的输入和输出存在着多种关系,比如1对多,多对多等等,不同的输入输出关系对应着不同的应用,网上也有很多这方面的文章可以去看看,这里我们要讲的RNN在训练网络的时候是相同长度的多对多的类型,也就是输入一个序列,输出一个相同的长度的序列。

具体的网络结构就是下面这个样子
灰灰深入浅出讲解循环神经网络(RNN)
输入一句话作为输入序列,这句话中的每个字符都按照顺序进入RNN,每个字符传入RNN之后都能够得到一个输出,而这个输出就是这个字符在这句话中紧跟其后的一个字符,可以通过上面的图示清晰地看到这一点。这里要注意的是,一个序列最后一个输入对应的输出可以有多种选择,上面的图示是将这个序列的最开始的字符作为其输出,当然也可以将最后一个输入作为输出,以上面的例子说明就是’光’的输出就是’光’本身。

生成文本过程

在预测的时候需要给网络一段初始的序列进行预热,预热的过程并不需要实际的输出结果,只是为了生成具有记忆的隐藏状态,然后将隐藏状态保留,传入之后的网络,不断地更新句子,直到达到要求的输出长度,具体可以看下面的图示
灰灰深入浅出讲解循环神经网络(RNN)
生成文本的过程就是每个字不断输入网络,然后将输出作为下一次的输出,不断循环递归,因为其会不限循环下去,所以可以设置一个长度让其停止。

训练过程就是这样,当然,具体的训练过程也是有很多数学推导,有兴趣的大佬请移步:数学 · RNN,感谢知乎答主:射命丸咲。

RNN的种类其实很多,原谅我不能一一列举,在此仅以此文作为抛砖引玉。更多详细的内容后续会一一更新,请大佬多多交流指点。最后感谢hanbingtao大神,其零基础入门深度学习系列文章给了编者很大的启发,在此致以深深的谢意!另系列文章链接附在下方。赠人玫瑰,手有余香。
零基础入门深度学习(1) - 感知器
零基础入门深度学习(2) - 线性单元和梯度下降
零基础入门深度学习(3) - 神经网络和反向传播算法
零基础入门深度学习(4) - 卷积神经网络
零基础入门深度学习(5) - 循环神经网络