RNN系列教程之二 | 在Python和Theano框架下实现RNN
本篇文章5179字,读完约13分钟
不久前,我们介绍了深度学习中常用的十种框架和选择策略(详见“深度学习介绍:你需要了解的十种框架和选择策略”)。
在本周的rnn教程中,我们将以python和an ano为例介绍rnn的实现方法。
让我们回顾一下本系列教程的四个部分:
1.循环神经网络基本介绍
2.在python和ant ano框架下实现rnn(本部分)
3.理解bptt算法和梯度消失问题
4.基于gru或lstm建立rnn模型
注:本节涉及的相关代码可从github平台获得
-1-语言建模在这一步中,我们的目标是使用rnn网络来构建和训练语言模型。语言模型的功能是当我们给出一个由m个词组成的句子时,它可以帮助预测下一个词是什么,包括这个词出现的概率,即条件概率。
例如,“他去买一些巧克力”这句话的概率实际上是在他被给定之后去的概率,乘以他去之后去的概率,乘以他去之后买的概率,等等(嗯,这有点迂回,你可以读几次)。
那么,为什么我们要用概率来预测句子呢?
首先,语言模型可以用作评分模型。例如,在机器翻译系统中输入句子通常会产生多个候选解决方案。在这种情况下,语言模型可以用来选择最可能的句子作为输出。
此外,语言模型是一个生成模型。我们也可以通过给出前一个单词来预测下一个单词的概率,并重复这个过程来生成新的文本。
应该注意的是,上述概率公式中每个单词的概率由它前面的所有给定单词决定。然而,由于计算或内存的限制,很难长时间存储大量的模型。因此,许多模型只能与前几个词相关联。
下面是对基于rnn的语言建模的代码实现的具体介绍。
-2-数据训练和预处理在训练语言模型之前,有必要训练数据(即特定文本)。就像孩子们学习说话一样,他们通过大量的词汇练习养成了说话的习惯。
方便的是,这种语言模型的训练可以通过原始文本来实现,而不需要对数据进行任何手工标记。在这里,我们从谷歌的大查询数据集下载了15000条reddit长评论作为文本数据。在此之前,像其他机器学习项目一样,数据必须通过预处理转换成正确的格式。
2.1切分如果你想预测每个单词的出现概率,你需要将原文的每个段落拆分成句子,然后将句子拆分成单词。虽然也可以用空分隔每个段落,但这种方法不能正确处理标点符号。例如,他离开了!应该分成三个字:他,左,!。ntlk(nltk/)分词系统中的Word_tokenize和sent _ tokenize可以分别实现英文分词和子句。也支持中文界面
2.2过滤低频词需要注意,在语料库中,很多词只出现一两次,庞大的词汇量会降低模型的训练速度。另外,我们没有足够的上下文信息来支持低频词的训练,所以我们可能希望在训练前删除这些低频词。
在代码中,词汇表大小代表词汇表大小(这里我们将大小设置为8000,代表8000个最常见的单词,并且这个值可以修改)。对于不在词汇表中的单词,我们将其设置为未知标记。例如,如果词汇中没有非线性,那么句子的非线性在神经网络中就变得很重要。这里,未知标记也是词汇的一部分,我们将预测它的概率。生成新文本时,如果预测的单词是未知标记,您可以选择用词汇表之外的任何单词替换它。例如,从词汇表中随机选择单词,或者直接生成句子,直到生成的句子不包含未知单词。
2.3添加特殊的开始和结束标记我们在每个句子的开头添加标记句子开始和句子结束作为标记来标识模型文本的开始和结束。
2.4建立培训数据矩阵。在rnn网络中,我们的输入都是向量,而不是数据集中的字符串。因此,在代码中,有必要通过索引到单词和单词到索引来创建单词和索引之间的映射,以便将字符串映射成向量。例如,友好这个词的索引可能是2001。训练示例x可以用向量[0,179,341,416]表示,其中0相当于句子_开始,对应的开始符y是[179,341,416,1]。
我们的目标是预测下一个单词,所以y是向量x的左边一个,最后一个单词是句尾。也就是说,单词179的正确预测是341,这是下一个单词。
以下是本文中的一个实际培训示例:
关于神经网络的基本介绍,请参阅本教程的第一篇文章,循环神经网络的基本介绍。
▲rnn结构图
rnn中输入的x是一个单词序列,每个x_t是一个单独的单词。但是,根据矩阵乘法的原理,我们不能直接使用上述的词索引表达方法,我们需要将词表示为动词大小的实数向量。例如,索引为36的所有字都是0,只有第36位是1。因此,x_t是一个向量,x是一个矩阵,一条线是一个单词。我们将在神经网络代码中进行转换,而不是在预处理阶段。类似地,网络的输出o具有类似的格式,o_t是词法大小元素的向量,该元素指示单词作为句子中的下一个单词出现的概率。
复习教程第一部分中的rnn公式:
此外,我们还发现通过记录每一层的矩阵和向量的维数来理解神经网络的结构是非常有帮助的。这里,我们假设词汇c=8000,存储单元(也称为隐藏层)之间的神经元数量h=100,并且其大小等于网络的存储容量,这意味着该容量越大,学习模式将越复杂,当然,额外的计算将会增加。
以下是整个结构的尺寸:
这是非常有价值的信息。u、v和w代表神经网络的权重参数。因此,我们需要掌握的参数数量是2hc+h^2.在特定网络中,当c = 8000且h = 100时,参数值为1,610,000。应该注意的是,实数向量x_t和u的乘法仅仅是选择u列的过程,这在计算上基本上是不必要的,因此没有必要执行完整的运算。这个神经网络中的最大矩阵积是在vs _ t的步骤。这就是为什么我们希望词汇量尽可能小。
掌握这些之后,我们可以开始实现以下操作:
3.1初始化开始于选择一个初始化所有权重参数的rnn,并将其命名为rnnnumpy,然后是一个版本的an rnn。初始化u、v和w很复杂,所以不容易将它们初始化为0,这将导致所有级别的对称操作。初始值会影响最终的训练结果,这一点非常重要,因此必须随机初始化。
该领域的大量研究表明,初始化的最佳效果取决于激活函数。通过等距随机抽样来初始化权重是一种好方法,在该范围内的较高层的传入连接数n作为距离。虽然看起来很复杂,但不要太担心,只要参数被初始化为一个小的随机值,它通常就能正常工作。
以下是代码:
在上面的代码中,word_dim表示词汇的大小,hidden_dim表示隐藏层的大小(可以自己选择)。还有一个bptt_truncate参数。
3.2前向传播过程接下来,介绍如何实现前向传播算法(用于预测单词的概率):
值得注意的是,为了避免二次运算,在这个函数的末尾,不仅输出层而且隐藏层都被返回,以便计算梯度。这里,每个o_t代表一个8000维的单词概率向量,它代表每个单词的输出概率。在模型评估的过程中,我们通常只想要概率最高的词,所以在下面,我们将使用预测函数来实现它:
让我们试试新的实现方法,看看它的输出示例:
对于句子中的每个单词(图片中的句子有45个单词),我们的模型输出8000个数值,对应于字典中的每个单词作为句子中的下一个单词出现的概率。因为u、v、w、v和w已经被初始化为随机值,所以这些预测是完全随机的。每个单词的最高预测概率指数如下:
3.3计算误差为了训练神经网络,需要一种方法来量化误差,以便找到能够最小化训练数据误差的最佳u、V、W、V和W参数。我们称这种方法为损失函数。通常我们选择一种损失函数,称为交叉熵损失。假设有n个训练样本(文本中的单词)和c个类别(词汇量,8000),预测输出o和实际注释y的损失函数如下:
这个公式看起来很复杂,但实际上它是对训练样本求和,然后将它们加到预测的损失上。y(实际标记值)和o(预测输出值)之间的距离越远,误差越大。因此,我们使用计算损耗函数来实现:
退一步想想随机预测的错误。我们将得到一个底线,并确认实施是正确的。词汇表中有c个单词,每个单词的(平均)预测概率应为1/c,因此误差值由以下公式获得:
结果非常接近!然而,应该清楚的是,计算整个数据集的损失将需要很多时间。如果数据量太大,将需要几个小时。
3.4在SGD和bptt训练下的rnn模型不要忘记,我们正在寻找u、V、W、V和W参数,它们可以使训练数据的总损失最小化。最常用的方法是sgd(随机梯度下降),这是一种随机梯度下降算法。sgd的原理非常简单。通过迭代所有训练样本并在每次循环操作中不断调整参数,错误率得以降低。这些方向由损耗梯度给出。
在这个过程中,我们需要一个学习速率来确定sgd在每次迭代中有多少进步(学习速率越大,学习速度越快,但是很容易交叉,导致没有最优解;学习速率越低,学习速度越慢)。该算法不仅可用于神经网络,还可用于许多其他机器学习算法。
那么,如何计算上面提到的梯度?在传统的神经网络中,我们采用反向传播算法进行计算。在rnn中,在此基础上,我们将做一个简单的修改,这是由BPTT(通过时间的反向传播)实现的,它在中文中被解释为跨越时间的反向传播算法。你为什么要用这种方法?在该模型中,所有参数都是实时共享的,这使得每个输出的梯度不仅取决于当前值,而且始终取决于以前的值。因此,这里我们将应用链式法则。
现在,我们可以首先将bptt算法(我们将在下一个教程中详细介绍)看作一个黑盒。让我们看看代码实现。当我们输入训练示例(x,y)时,我们将输出三个梯度来更新权重:
3.5梯度测试通常,我们建议同时应用反向传播算法和梯度测试,因为这是一种测试正确性的方法。梯度测试背后隐含的是参数的导数,其斜率与公式在某一时刻的斜率相同,但参数可以稍微改变,或者可以通过除以余数得到。
当您实现反向传播算法时,您可以同时进行梯度检查,以检查您的算法是否正确。检验背后的原理也很简单,即从导数的定义出发,公式如下:
下面是实现代码:
3.6通过以上准备工作,我们可以使用sgd算法来更新权重。它可以通过两个步骤来实现:第一,sdg_step方法,即更新一个批次的权重,它可以计算参数并在每个批次完成后完成更新;其次,使用外环遍历所有训练样本,并动态更新学习速率。下面是实现代码:
完成!尝试像这样训练网络所花费的时间如下:
可以看出,在我的电脑上,完成sgd算法大约需要350毫秒。大约有8万个训练样本。如果完成每个周期(整个数据集的迭代)需要几个小时,那么完成几个周期可能需要几天甚至几周。
幸运的是,有很多方法可以加速你的代码。例如,您可以始终使用相同的模型来提高代码运行速度,或者您可以更改模型来减少计算时间,或者您可以两者兼而有之。研究人员发现了许多减少模型计算时间的方法。例如,他们将使用分层的softmax函数或添加项目层来避免大规模的矩阵乘法。
另外,使用图形处理器可以提高计算速度。但在此之前,我们仍然需要尝试使用小数据集运行sgd算法,以便检查错误是否真的减少了。
-4-用模拟和图形处理器训练神经网络。我们已经编写了一个rnn实现程序代码来代替numpy。像本文中的其他代码一样,该代码可以在github上获得。
这一次,使用mac,我们只花了70毫秒就完成了一个sgd,比初始速度快15倍,这意味着只需要几个小时或几天就可以完成模型的训练。当然,虽然我们的模型现在已经足够好了,但是还有很大的改进空间。
以下是我自己预先训练的奥体模型的使用方法:
-5-生成文本有了模型后,您可以使用它来生成新文本:
下面我们选择了几个生成的句子:
不管怎样,在城市里你是个白痴少年。
什么?!!!!忽略!
去他妈的健身,你在说:https
谢谢你给我的建议,让我不去想女孩。
是的,请和可怕的一代一起消失。
值得注意的是,尽管在上面的句子中,该模型已经成功地应用了语法学习,并且适当地使用了逗号(通常在and和or之间),以句号结尾。有时,它模仿许多网络术语,如使用感叹号和笑脸符号。然而,许多生成的句子要么没有意义,要么有一些语法错误(我确实选择了其中最好的句子)。部分原因可能是我们的神经网络训练时间不够长,或者训练数据不够,但这不是主要原因。
主要原因是传统的rnn模型无法学习相距较远的两个词之间的相关性和相关性,因此无法生成有意义的文本。这也是rnn模型在最初开发时无法推广的原因。它们在理论上是完美的,但在实践中却不能很好地应用,其原因在短时间内无法找到。
当然,不要太担心。现在我们已经找到了许多更好的方法来训练rnn模型。
预览:下周,我们将详细介绍bptt算法,并解释梯度消失问题。Bptt算法使我们能够开发更复杂的rnn模型,例如lstm,它是目前nlp任务中非常好的模型。
此外,应该注意的是,本文涉及的算法也可以应用于lstm和其他rnn模型。
|来源:wildml;作者:denny britz;汇编人:科技沃克
标题:RNN系列教程之二 | 在Python和Theano框架下实现RNN
地址:http://www.6st8.com/zbxw/9117.html
免责声明:联合早报中文网从世界各个维度报道世界经济新闻,时政新闻,突发新闻等,本篇的部分内容来自于网络,不为其真实性负责,只为传播网络信息为目的,非商业用途,如有异议请及时联系btr2018@163.com,联合早报中文网的小编将予以删除。
下一篇:没有了