RNN循环神经网络原理与示例

一.原理解释

循环神经网络(Recurrent Neural Network,RNN)很多实时情况都能通过时间序列模型来描述。基于序列的模型可以用在很多领域中。在音乐中,一首曲子的下一个音符肯定取决于前面的音符,而在视频领域,电影中的下一帧肯定与先前的帧有关。此外,在某些情况下,视频的当前帧、单词、字符或音符不仅仅取决于过去的信号,而且还取决于未来的信号。

基于时间序列的模型可以用RNN来描述,其中,时刻 i 输入为 Xi,输出为 Yi,时刻 [0,i-1] 区间的状态信息被反馈至网络。这种反馈过去状态的思想被循环描述出来。由于反馈环路的缘故,梯度可以很快地发散到无穷大,或者迅速变为 0。梯度爆炸的问题可以通过一个简单的策略来解决,就是梯度裁剪。梯度消失的问题则难以解决,它涉及更复杂的 RNN 基本单元(例如长短时记忆(LSTM)网络或门控循环单元(GRU))的定义。先来讨论梯度爆炸和梯度裁剪:

梯度裁剪示例

解决梯度消失需要一个更复杂的记忆模型,它可以有选择地忘记以前的状态,只记住真正重要的状态。

长短时记忆网络(LSTM)

长短时记忆网络可以控制何时让输入进入神经元,何时记住之前时序中学到的东西,以及何时让输出传递到下一个时间戳。所有这些决策仅仅基于输入就能自我调整。

一个 LSTM 单元的示例

首先,需要一个逻辑函数 σ 计算出介于 0 和 1 之间的值,并且控制哪个信息片段流经 LSTM 门。请记住,logisitic 函数是可微的,所以它允许反向传播。

然后需要一个运算符 ⊗ 对两个相同维数的矩阵进行点乘产生一个新矩阵,其中新矩阵的第 ij 个元素是两个原始矩阵第 ij 个元素的乘积。同样,需要一个运算符 ⊕ 将两个相同维数的矩阵相加,其中新矩阵的第 ij 个元素是两个原始矩阵第 ij 个元素的和。在这些基本模块中,将 i 时刻的输入 xi 与前一步的输出 yi 放在一起。

方程 fi=σ(Wf·[yi-1,xi]+bf) 是逻辑回归函数,通过控制激活门 ⊗ 决定前一个单元状态 Ci-1 中有多少信息应该传输给下一个单元状态 Ci(Wf 是权重矩阵,bf 是偏置)。逻辑输出 1 意味着完全保留先前单元状态 Ct-1,输出 0 代表完全忘记 Ci-1 ,输出(0,1)中的数值则代表要传递的信息量。

接着,方程根据当前输入产生新信息,方程 si=σ(Wc·[Yi-1,Xi]+bc) 则能控制有多少新信息通过运算符 ⊕ 被加入到单元状态 Ci 中。利用运算符 ⊗ 和 ⊕,给出公式对单元状态进行更新。

最后,需要确定当前单元状态的哪些信息输出到 Yi。很简单,再次采用逻辑回归方程,通过 ⊗ 运算符控制候选值的哪一部分应该输出。在这里有一点需要注意,单元状态是通过 tanh 函数压缩到 [-1,1]。这部分对应的方程是 Yi=ti*tanh(Ci)。

因此可以使用 LSTM 单元作为标准 RNN 元的黑盒替换,并立即解决梯度消失问题。因此你真的不需要知道所有的数学理论,你只需从库中取出 TensorFlow LSTM 并使用它。

处理向量序列

真正使 RNN 强大的是它能够处理向量序列,其中 RNN 的输入和输出可以是序列RNN序列示例

二.示例

神经机器翻译(seq2seq RNN)实现详解

输入一个完整的文本序列,理解整个语义,然后输出翻译结果作为另一个序列。阅读整个序列的想法与以前的架构截然不同,在该架构中,一组固定词汇从一种源语言翻译成目标语言。第一个关键概念是编码器–解码器架构,其中编码器将源语句转换为表示语义的向量,然后这个向量通过解码器产生翻译结果。编码器和解码器都是 RNN,它们可以捕捉语言中的长距离依赖关系,例如性别一致性和语法结构,而不必事先知道它们,也不需要跨语言进行 1:1 映射。它能够流利地翻译并且具有强大的功能。

来看一个 RNN 例子:将 She loves cute cats 翻译成 Elle aime les chats mignons。有两个 RNN:一个充当编码器,一个充当解码器。源语句 She loves cute cats 后面跟着一个分隔符“-”和目标语句 Elle aime les chats mignon。这两个关联语句被输入给编码器用于训练,并且解码器将产生目标语句 Elle aime les chats mignon。当然,需要大量类似例子来获得良好的训练。

  • RNN 可以是单向的或双向的,后者将捕捉双向的长时间依赖关系。
  • RNN 可以有多个隐藏层,层数的选择对于优化来说至关重要...更深的网络可以学到更多知识,另一方面,训练需要花费很长时间而且可能会过度拟合。
  • RNN 可以具有嵌入层,其将单词映射到嵌入空间中,在嵌入空间中相似单词的映射恰好也非常接近。
  • RNN 可以使用简单的重复性单元、LSTM、窥孔 LSTM 或者 GRU。

可以使用嵌入层将输入的句子映射到一个嵌入空间。然后,存在两个连接在一起的 RNN——源语言的编码器和目标语言的解码器。如下图所示,有多个隐藏层和两个流动方向:前馈垂直方向连接隐藏层,水平方向是将知识从上一步转移到下一步的循环部分。

解码器的输出有多种策略:

  1. 贪婪:输出对应最大logit值的单词。
  2. 采样:通过对众多logit值采样输出单词。
  3. 集束搜索:有多个预测,因此创建一个可能结果的扩展树

注意学习能够进行是因为损失函数基于交叉熵,且labels=decoder_outputs。

具体实现过程

从 GitHub 克隆 NMT:

git clone https://github.com/tensorflow/nmt/

下载一个训练数据集。在这个例子中,使用训练集将越南语翻译成英语,其他数据集可以在https://nlp.stanford.edu/projects/nmt/上获得,如德语和捷克语:

nmt/scripts/download_iwslt15.sh/tmp/nmt_data

参考https://github.com/tensorflow/nmt/,这里将定义第一个嵌入层,嵌入层将输入、词汇量尺寸 V 和期望的输出尺寸嵌入到空间中。词汇量尺寸 V 中只有最频繁的单词才考虑被嵌入,所有其他单词则被打上 unknown 标签。在本例中,输入是 time-major,这意味着 max time 是第一个输入参数

embedding_encoder=variable_scope.get_variable("embedding_encoder",[src_vocab_size,embedding_size],...)
encoder_emb_inp=embedding_ops.embedding_lookup(embedding_encoder,encoder_inputs)

仍然参考 https://github.com/tensorflow/nmt/,这里定义一个简单的编码器,它使用 tf.nn.rnn_cell.BasicLSTMCell(num_units) 作为基本的 RNN 单元。虽然很简单,但要注意给定基本 RNN 单元,我们利用 tf.nn.dynamic_rnn 构建了 RNN 的(见https://www.tensorflow.org/api_docs/python/tf/nn/dynamic_rnn):

encoder_cell=tf.nn.rnn_cell.BasicLSTMCell(num_units)
encoder_outputs,encoder_sate=tf.nn.dynamic_rnn(encoder_cell,encoder_emb_inp,sequence_length=source_sequence_length,time_major=True)

定义解码器。首先要有一个基本的 RNN 单元:tf.nn.rnn_cell.BasicLSTMCell,以此来创建一个基本的采样解码器 tf.contrib.seq2seq.BasicDecoder,将结果输入到解码器 tf.contrib.seq2seq.dynamic_decode 中进行动态解码。

decoder_cell=tf.nn.rnn_cell.BasicLSTMCell(num_units)
helper=tf.contrib.seq2seq.TrainingHelper(decoder_emb_inp,decoder_lengths,time_major=True)
decoder=tf.contrib.seq2seq.BasicDecoder(decoder_cell,helper,encoder_state,output_layer=projection_layer)
outputs,_=tf.contrib.seq2seq.dynamic_decode(decoder,...)
logits=outputs.rnn_output

网络的最后一个阶段是 softmax dense 阶段,将最高隐藏状态转换为 logit 向量:

projection_layer=layers_core.Dense(tgt_vocab_size,use_bias=False)

定义在训练阶段使用的交叉熵函数和损失:

crossent=tf.nn.spase_softmax_cross_entropy_with_logits(labels=decoder_outputs,logits=logits)
train_loss=(tf.reduce_sum(crossent*target_weights)/batch_size)

定义反向传播所需的步骤,并使用适当的优化器(本例中使用 Adam)。请注意,梯度已被剪裁,Adam 使用预定义的学习率:

params=tf.trainable_variable()
gradients=tf.gradients(train_loss,params)
clipped_gradients,_=tf,clip_by_global_norm(gradients,max_gradient_norm)
optimizer=tf.train.AdamOptimizer(learning_rate)
uodate_step=optimizer.apply_gradients(zip(clipped_gradients,params))

运行代码并理解不同的执行步骤。首先,创建训练图,然后开始迭代训练。评价指标是 BLEU(bilingual evaluation understudy),这个指标是评估将一种自然语言机器翻译为另一种自然语言的文本质量的标准,质量被认为是算法的结果和人工操作结果的一致性。

 

注意力(Attention)机制

它需要在编码器和解码器 RNN 之间增加额外的连接。事实上,仅将解码器与编码器的最新层连接会存在信息瓶颈,而且不一定能够传递先前编码器层的信息。

需要考虑以下三个方面:

  1. 将当前目标隐藏状态与所有先前的源状态一起使用,以导出注意力权重,用于给先前序列中的信息分配不同的注意力大小。
  2. 总结注意力权重的结果创建上下文向量。
  3. 将上下文向量与当前目标隐藏状态相结合以获得注意力向量。

注意力机制是使用编码器 RNN 内部状态获得的信息,并将该信息与解码器的最终状态进行组合的机制,关键思想是可以对源序列中的信息分配不同的注意力。

引入注意力模型的NMT示例

RNN训练模型并生成文本过程

将学习如何生成类似于莎士比亚风格的文本。核心思想非常简单:以莎士比亚写的真实文本作为输入,并输入到即将要训练的 RNN 中;然后,用训练好的模型来生成新文本,这些文本看起来像是英国最伟大的作家所写的。所开发的模型是字符级 RNN 语言模型,考虑的序列则是字符序列而不是单词序列。(参考https://github.com/tflearn/tflearn/blob/master/examples/nlp/lstm_generator_shakespeare.py

导入一些有用的模块,并下载莎士比亚写的文本。

import os
import pickle
from six.moves import urllib
import tflearn
from tflearn.data_utils import *
path="Shakespeare_input.txt"
char_idx_file='char_idx.pickle'
if not os.path.isfile(path):
urllib.request.urlretrive("https://raw.githubusercontent.com/tflearn/tflearn.github.io/master/resources/shakespeare_input.txt",path)

将输入文本转换为向量,并通过 string_to_semi_redundant_sequences() 返回解析的序列和目标以及关联的字典(函数输出一个元组:包括输入、目标和字典):

maxlen=25
char_idx=None
if os.path.isfile(char_idx_file):
    char_idx=pockle.load(open(char_idx_file,'rb'))
    X,Y,char_idx=\

string_to_semi_redundant_sequences(path,seq_maxlen=maxlen,redun_step=3,pre_defined_char_idx=char_idx)
pickle.dump(char_idx,open(char_idx_file,'wb'))

定义由三个 LSTM 组成的 RNN,每个 LTSM 有 512 个节点,并返回完整序列而不是仅返回最后一个序列。请注意,使用概率为 50% 的 drop-out 模块来连接 LSTM 模块。最后一层是全连接层,softmax 长度等于字典尺寸。损失函数采用 categorical_crossentropy,优化器采用 Adam:

g=tflearn.input_data([None,maxlen,len(char_idx)])
g=tflearn.lstm(g,512,return_seq=True)
g=tflearn.gropout(g,0.5)
g=tflearn.lstm(g,512,return_seq=True)
g=tflearn.gropout(g,0.5)
g=tflearn.lstm(g,512,return_seq=True)
g=tflearn.gropout(g,0.5)
g=tflearn.fully_connected(g,len(char_idx),activation='softmax')
g=tflearn.regression(g,optimizer='adam',loss='categorical_crossentropy',learning_rate=0.001)

现在可以用库函数 flearn.models.generator.SequenceGenerator(network,dictionary=char_idx,seq_maxlen=maxle,clip_gradients=5.0,checkpoint_path='model_shakespeare') 生成序列:

m=tflearn.models.generator.SequenceGenerator(g,dictionary=char_idx,seq_maxlen=maxlen,clip_gradients=5.0,checkpoint_path='model_shakespeare')

当一件新的未知或遗忘的艺术作品需要被鉴定归于某位作者时,就会有著名学者将这件作品与作者的其他作品进行比较。学者们所做的是在作者已知作品的文本序列中寻找共同特征,并希望在鉴定作品中找到相似的特征。

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值