【Python】RNN实现文本生成

2018-08-06 18:47:18 浏览数 (7811)

本文转载至知乎ID:Charles(白露未晞)知乎个人专栏

导语

本文将在此基础上介绍LSTM网络。最后举一个类似“Python学写作”的例子来实现文本生成,如生成诗歌、小说等等。

让我们愉快地开始吧~~~


参考文献

Understanding LSTM Networks:

http://colah.github.io/posts/2015-08-Understanding-LSTMs/


相关文件

百度网盘下载链接: https://pan.baidu.com/s/16hjmw1NtU9Oa4iwyj4qJuQ

密码: gmpi


开发工具

Python版本:3.6.4

相关模块:

tensorflow-gpu模块;

numpy模块;

以及一些Python自带的模块。

其中TensorFlow-GPU版本为:

1.7.0


环境搭建

安装Python并添加到环境变量,pip安装需要的相关模块即可。

另外,TensorFlow-GPU的环境搭建请自行参考相关的网络教程,注意版本和驱动严格对应即可。


原理介绍

一. RNNs

人们的思维总是具有延续性的,比如当你阅读这篇文章时,你对每个词的理解都会依赖于你前面看到的一些词,而不是把你前面看的内容全部抛弃,再去理解每个词。而传统的神经网络(CNN)无法做到这一点,因此有了循环神经网络(RNNs)。

在RNNs中,存在循环操作,使得它们能够保留之前学习到的内容:

在上图网络结构中,对于矩形块A的那部分,通过输入Xt(t时刻的特征向量),它会输出一个结果ht(t时刻的状态或者输出),网络中的循环结构使得当前状态作为下一时刻输入的一部分。

将RNNs在时间步上进行展开,就可以得到下图:

也就是“Python实现简单的机器翻译模型”一文中所使用的RNNs链状的结构。显然,这样的结构是有利于处理序列相关问题的。近年来,其在语音识别、语言翻译等等领域都取得了巨大的成功。

而RNNs的成功,主要归功于LSTMs这种特殊RNNs结构的使用,而非普通的RNNs结构。

二. LSTMs

全称为Long Short Term Memory networks.

即长短期记忆网络。

普通RNNs的局限性在于当我们所要预测的内容和相关信息之间的间隔很大时,普通RNNs很难去把它们关联起来:

尽管从理论上来讲,只要参数合适,还是可以解决长时期依赖关系无法很好联系这一问题的,但具体实现起来似乎并不容易,至少目前为止是不容易的。

幸运的是,LSTMs能够很好地解决这一问题。它被设计的初衷就是为了能够记住长时期内的信息。

循环神经网络是由相同结构的神经网络模块进行复制而形成的。在标准的RNNs中,神经网络模块的结构非常简单,比如可以由单一的tanh层构成:

LSTMs也有类似的结构,不过神经网络模块的结构变得相对复杂了一些:

接下来,我们来详细介绍一下这个结构。首先,我们来定义一下用到的符号:

粉红色的圈:

代表向量加之类的逐点操作;

黄色矩形框:

代表神经网络层;

普通的线:

用于携带并传递向量;

合并的线:

代表对两条线上所携带的向量进行合并;

分开的线:

代表将线上所携带的向量复制后传给两个地方。

2.1 LSTMs的核心思想

假设一个绿色的框就是一个cell。

向量通过结构图最上面的那条贯穿cell的水平线穿过整个cell,而cell仅对其做了少量的线性操作:

显然,这样的结构能够很轻松地让信息从整个cell中穿过而不发生变化。

当然,只有一条水平线是无法实现添加或者删除信息的,也就是实现让信息有选择地通过cell,这需要通过一种叫做门(gates)的结构来实现。

门结构主要由一个sigmoid神经网络层和一个逐点相乘的操作来实现:

sigmoid层输出的向量每个元素都是介于0和1之间的实数,表示此时通过的信息的权重,当其为0时表示“此时不让任何信息通过”,为1时表示“此时让所有信息通过”。每个LSTM都有三个这样的门结构,来实现保护和控制信息。

2.2 逐步理解LSTM

遗忘门(forget gate layer):

首先,LSTM需要决定哪些信息需要丢弃,哪些信息需要保留。这是通过一个叫做遗忘门的sigmoid层来实现的。它的输入是ht-1xt,输出是一个数值都在0到1之间的向量,表示Ct-1中各部分信息的权重,0表示不让该部分信息通过,1表示让该部分信息全部通过。

具体而言,比如在语言模型中,我们要根据所有的上下文信息来预测下一个词。在这种情况下,每个cell的状态中都应该包含了当前主语的性别信息。这样,接下来我们才能够正确地使用代词。但是,当我们开始描述一个新的主语时,就应该把之前的主语性别给丢弃了才对。

传入门(input gate layer):

其次,LSTM将决定让哪些新的信息加入到cell的状态中来。该实现分两个步骤进行:

① 用一个tanh层生成一个备选向量,用于表示获得的所有可添加信息;

② 用一个叫做传入门的sigmoid层来决定步骤①中获得的可添加信息各自的权重。

具体而言,比如在语言模型中,我们需要把新主语的性别信息添加到cell状态中,来替换掉之前的主语性别信息。

有了遗忘门和传入门,我们就能够更新cell的状态了,即把Ct-1更新为Ct

还是以语言模型为例,假设我们的模型刚输出了一个代词,接下来可能要输出一个动词,那么这个动词应该采用单数形式还是复数形式呢?显然,我们需要把代词相关的信息和当前的预测信息都加入到cell的状态中来,才能够进行正确的预测。

具体计算方式如下图所示:

输出门(Output):

最后,我们需要决定输出值。输出值的计算方式为:

① 使用sigmoid层来决定/计算出Ct中的哪部分信息会被输出;

② 利用tanh层将Ct的取值压缩到-1到1之间;

③ 将tanh层的输出和sigmoid层的输出相乘即为最终的输出结果。

三. LSTMs的变种

① 将cell的状态作为门结构输入的一部分。

② 将遗忘门与传入门耦合,即不再分开决定要遗忘和添加的信息。

③ GRU

GRU模型“简化”了LSTM模型的设计,其中rt由LSTM中的遗忘门和传入门合并而得,称为重置门;zt为更新门,作用相当于LSTM中的输出门。

实际应用

为了贯彻理论与实践相结合的理念,本文将举一个简单的小例子,该例子使用的模型与“Python学写作”类似,本文不再作多余的介绍。

具体实现过程详见相关文件中的源代码。

使用演示

模型训练:

在cmd窗口运行'train.py'文件即可:

如有需要,可自行修改相关参数:

模型使用:

在cmd窗口运行“generate.py”文件即可。

注意模型参数需和train.py文件中的模型参数一致:

结果展示

生成英文文本:

以莎士比亚的作品为训练素材获得的结果:

生成中文文本:

以周杰伦的作品为训练素材获得的结果:


更多

代码截止2018-06-24测试无误。

模型比较简单,有兴趣的朋友可以在此基础上进行优化,当然RNN的作用可不仅仅是文本生成哦~

以后有机会再举其他例子吧~~~