RNN

RECURRENT NEURAL NETWORK TUTORIAL, PART 4 – IMPLEMENTING A GRU/LSTM RNN WITH PYTHON AND THEANO

本文中,我们将学习关于LSTM (Long Short Term Memory)网络和 GRUs (Gated Recurrent Units)的知识。LSTM最初由Sepp Hochreiter and Jürgen Schmidhuber于1997年提出,如今是深度学习自然语言处理领域最流行的模型。GRU,出现于2014年,是LSTM的简化版,与LSTM有许多相似的特性。 ##LSTM 网络 在第三部分我们提到了梯度消失问题妨碍了标准的RNN学习长期依赖问题。LSTM被设计于用gate结构解决梯度消失问题。为了理解这个机制,我们来看看LSTM如何计算隐藏状态$$s_t$$(其中小圆圈代表Hadamard product,即同型矩阵各元素对应相乘,不同于矩阵点乘)。 式子看起来复杂,一步一步来理解实则简单。首先,LSTM的一层代表的只是另一种计算隐藏层的方法。之前我们计算了隐藏状态$$s_t = tanh(Ux_t + WS_{s-1})$$。对于当前的单元,输入是$$t$$时刻的$$x_t$$,而$$s_{t-1}$$是之前的隐藏状态,输出是新的隐藏状态$$s_t$$。其实,LSTM做的事情是完全一样的,只不过换了种方式,这也是理解LSTM的核心。我们可以把LSTM单元看作是黑盒子,给予其当前输入和之前的隐藏状态,它可以输出下一个隐藏状态。 把这个牢记于心,我们开始来阐述LSTM如何计算隐藏状态。关于这一点,详细可看这篇文章,这里我们只作简短描述: $$i,f,o$$分别被称为输入门、遗忘门和输出门。注意到,它们具有相同的等式,只是参数矩阵不同。它们之所以被称为“门”,是因为sigmoid函数将向量值压缩到0和1之间,再与另一个向量相乘,我们因此决定向量的多少“通过”。输入门决定当前输入计算出来的状态的多少成分被通过,遗忘门决定之前的状态有多少可以被保留到之后,输出门决定当前的状态有多少被传送到外层的网络(高层网络和下一时刻)。这些门的维度都是$$d_s$$,即隐藏层的大小。 $$g$$是一个候选的隐藏状态,基于当前的输入和之前的隐藏状态计算而出。其与vanilla RNN的计算等式相同,只是把参数$$U,W$$改名为$$U^g,W^g$$。和RNN的直接将$$g$$作为心的隐藏状态不同,我们将其通过一个输入门来决定保留它的多少成分。 $$c_t$$是单元的内部的记忆,它由之前的记忆$$c_{t-1}$$通过遗忘门再加上新计算出来的隐藏状态$$g$$通过输入门计算得出。因此,它代表了旧的记忆与新的输入的结合。我们可以选择全部忽略旧的记忆(遗忘门全部置零),或者忽略全部的计算出的新状态(输入门全部置零),但是通常来说,我们可能更希望介于两者之间。 给定记忆$$c_t$$,我们最终通过让记忆和输出门相乘计算出输出隐藏层状态$$s_t$$。在网络内,不是所有的内部记忆都与其他单元使用的隐藏状态有关。 换一种说法,我们可以将标准的RNN看作是特殊的LSTM,如果我们将遗忘门全部置零,输入门全部置一,输出门全部置一,我们就几乎得到一个标准的RNN。通过门机制,LSTM可以操作记忆从而解决长期依赖问题。 注意到,还有许多的LSTM变种,一种添加上“猫眼”结构,它的门同时取决于之前的隐藏状态$$s_{t-1}$$和之前的内部状态$$c_{t-1}$$。 LSTM: A Search Space Odyssey 实验观察了许多不同的LSTM机制。 ##GRU网络 GRU的理念类似于LSTM,其等式如下: GRU拥有两个门,称为重置门$$r$$和更新门$$z$$。直观来说,重置门决定如何联合新的输入和之前的记忆,而更新门决定留下多少之前的记忆。如果将重置门全部置一并且更新门全部置零,那么我们又得到了我们原始的RNN了。GRU的解决长期依赖的理念和LSTM基本类似,以下是一些不同之处: 两个门VS三个门 GRU不处理内层记忆$$c_t$$ 输入门和遗忘门被组合成更新门$$z$$,重置门$$r$$直接连接之前的隐藏状态。因此, 计算输出是不加上第二个非线性变换 ##GRU VS LSTM 如今你认识了两个对抗梯度消失的模型  

RECURRENT NEURAL NETWORKS TUTORIAL, PART 3 – BACKPROPAGATION THROUGH TIME AND VANISHING GRADIENTS

在之前的部分我们实现了RNN,但是并未深入探究时间反向传播算法,本文将对这一点作详细说明。我们将了解关于梯度消失问题的知识,它促使了LSTM和GRU的出现,而这两者都是NLP领域非常常见的模型。 ##BACKPROPAGATION THROUGH TIME (BPTT) 首先我们回顾一下RNN的基本等式: 我们也定义了损失函数(交叉熵): 在这里,$$y_t$$是 $$t$$时刻的正确的词语, $$tilde{y_t}$$ 是我们的预测。因为我们把一整个序列(句子)当做是输入,那么错误等同于每个时间step(词语)的错误的和。 ![](/images/2016/06/rnn-bptt1.png) 需要注意,我们的目标是计算基于参数$$U, V, W$$错误的梯度,并且通过SGD来学习到好的参数。类似于我们将错误相加的做法,对于一个训练样本,我们将各个时间点的梯度相加。 $$frac{partial{E}}{partial{W}} = sum_{t} frac{partial{E_t}}{partial{W}}$$ 我们使用链式求导法则来计算这些梯度,这就是反向传播算法:从错误处反向计算。以下我们使用$$E_3$$作为例子。 其中,$$z_3 = V s_3$$,并且$$otimes$$指的是向量外积。在这里我们需要注意到,$$frac{partial{E_3}}{partial{V}}$$只取决于当前时刻的值$$tilde{y_3}, y_3, s_3$$。如果你明确了这一点,那么计算$$V$$的梯度只是矩阵计算罢了。 但是,对于$$frac{partial{E_3}}{partial{W}}$$和$$V$$就不一样了。我们写出链式法则: 可以看到,$$s_3 = tanh(U x_t + W s_2)$$取决于$$s_2$$,而$$s_2$$又取决于$$W$$和$$s_1$$,以此类推。所以我们计算$$W$$的偏导,我们不能把$$s_2$$当做一个常量,相反我们需要一遍遍的应用链式法则: 我们把每个时间点对于梯度贡献综合起来。换句话说,因为$$W$$在直到我们需要输出的时刻都被用到,所以我们需要计算$$t=3$$时刻直到$$t=0$$时刻: 这其实和深度前馈神经网络里的标准的反向传播算法是类似的。主要的不同点在于我们把每个时间点的$$W$$的梯度综合起来。传统的神经网络的不同层之间不共享参数,于是我们也不需要综合什么。但是在我看来,BPTT只不过是在没有展开的RNN上的标准BP算法的别名罢了。类似于标准的BP算法,你也可以定义一个徳塔项来表示反向传播的内容。例如:$$delta_{2}^{(3)} = frac{partial{E_3}}{partial{z_2}} = frac{partial{E_3}}{partial{s_3}} frac{partial{s_3}}{partial{s_2}} frac{partial{s_2}}{partial{z_2}}$$,其中$$z_2 = Ux_2 + Ws_1$$。以此类推。 代码实现BPTT如下: 123456789101112131415161718192021222324 def bptt(self, x, y): T = len(y) …

RECURRENT NEURAL NETWORKS TUTORIAL, PART 3 – BACKPROPAGATION THROUGH TIME AND VANISHING GRADIENTS Read More »

RECURRENT NEURAL NETWORKS TUTORIAL, PART 2 – IMPLEMENTING A RNN WITH PYTHON, NUMPY AND THEANO

本文将用Python实现完整的RNN,并且用Theano来优化。 语言模型 我们的目标是使用RNN建立一个语言模型。以下我们举例说明什么是语言模型。例如,你说了一句包括$$m$$个词语的句子,语言模型可以为这句话出现的概率打分: $$P(w_1,cdots,w_m) = prod_{i=1}^m P(w_i mid w_1,cdots,w_{i-1})$$ 每一个词语的概率都取决于它之前的所有的词的概率。 这样的模型有什么用处呢? 可以用于机器翻译或者语音识别中的正确句子打分 以概率生成新的句子 注意到在上面的公式内,我们使用了所有的之前的词的概率,实际上这在计算和存储时的耗费都是巨大的,通常而言只会取2~4个词左右。 预处理并训练数据 1.标记化 原始的文本需要被标记化,例如需要把文本标记为句子,句子标记为词语,并且还需要处理标点符号。我们将使用NLTK的word_tokenizesent_tokenize方法。 2.移除低频词 移除低频词不管是对于训练和预测都是有帮助的。这里我们设置一个上限vocabulary_size为8000,出现次数少于它的词都会被替换为UNKNOWN_TOKEN输入,而当输出是UNKNOWN_TOKEN时,它将被随机替换为一个不在词表内的词,亦或者持续预测直到不出现UNKNOWN_TOKEN为止。 3.放置句子开始和结束标记 为了解句子的开始和结束,我们把SENTENCE_START放置在句子开头,并且把SENTENCE_END放置在句子结尾。 4.建立训练数据的矩阵 RNN的输入和输出都是向量而不是字符串,我们需要把词与向量一一对应,通过index_to_word和word_to_index。比如一个训练的例子$$x$$为[0, 179, 341, 416](注意到其中每个元素都是长度为vocabulary_size的one-hot向量,所以$$x$$实际上是一个矩阵),那么其label-$$y$$为[179, 341, 416, 1],注意到我们的目标是预测下一个词,所以$$y$$就是$$x$$移动一位,并添加上最后的一个元素(预测词)的结果,其中SENTENCE_START和SENTENCE_END分别为0和1. 123456789101112131415161718192021222324252627282930313233343536373839404142 vocabulary_size = 8000unknown_token = "UNKNOWN_TOKEN"sentence_start_token = "SENTENCE_START"sentence_end_token = "SENTENCE_END"# Read the data and append SENTENCE_START and SENTENCE_END tokensprint "Reading CSV file…"with open('data/reddit-comments-2015-08.csv', 'rb') as f: …

RECURRENT NEURAL NETWORKS TUTORIAL, PART 2 – IMPLEMENTING A RNN WITH PYTHON, NUMPY AND THEANO Read More »

RECURRENT NEURAL NETWORKS TUTORIAL, PART 1 – INTRODUCTION TO RNNS

RNN,也即是递归神经网络,是许多NLP任务的流行处理模型。本部分中将简介RNN。 本部分主要实现此模型– recurrent neural network based language model,模型有两个作用: 可以基于出现的概率对句子进行打分,可以对于语法和语义正确性进行评估,从而应用于机器翻译等领域。 可以依据概率生成新的语料。 ##什么是RNN RNN背后的核心理念是利用序列的信息。传统的神经网络常常假设输入(输出)是独立于彼此的,这对于某些应用来说是不可行的,例如NLP任务,如果你需要预测下一个单词是什么,你不可能不用到之前的单词的信息。RNN之中的recurrent指的就是循环往复的意思,网络对于序列数据的每个元素进行同样的操作,网络的输出取决于之前的计算。另一种理解RNN的方法则是把它们看成是有记忆的网络,记忆收集从开始到现在被考虑的信息。理论上RNN可以利用任意时间长度的信息,但是实际上这比较困难。以下是RNN的典型结构: 以上的结构将RNN展开成完整的网络。例如,我们需要处理一个5层的序列,那么就需要展开成5层:每层对应一个词。 $$x_t$$是$$t$$时刻的输入 $$s_t$$是$$t$$时刻的隐藏状态,代表网络的“记忆”,计算公式为$$s_t = f(Ux_t + Ws_{t-1})$$,其中$$f$$通常是tanh函数或者RELU函数。 $$o_t$$是$$t$$时刻的输出,如果我们想预测下一个词,那么计算公式为$$o_t = softmax(Vs_t)$$,指的是在整个词表内词的概率值。 对于以上有几点需要提示: 我们可以把隐藏状态$$s_t$$看成是网络的“记忆”,其捕捉到在之前所有时间的信息。输出$$o_t$$只取决于在$$t$$时刻的记忆。而在实际上,由于长期依赖问题,$$s_t$$很难捕捉到很长时间以前的信息。 RNN每个step中的参数($$U,V,W$$)是相同的,这使得学习的代价减小许多。 取决于实际任务,我们可能并不需要每个step都有输入和输出。 ##RNN的实际应用以下是RNN在NLP领域的一些实际应用。 ###语言模型与生成语句在给定之前词语的情况下我们希望产生出下一个词语是什么。语言模型的作用就是让我们可以衡量一个句子的可能性,这对于机器翻译是很重要的(可能性越高的更可能正确)。而语言模型的另一个作用则是预测下一个词语是什么,我们可以通过在输出的概率词汇中采样得到,。对于语言模型,输入是一序列的词语(每一个词语都是one-hot表示),输出则是一序列的预测的词语。在训练时我们设置$$o_t = x_{t + 1}$$,因为这一时刻的输出就是下一时刻的输入。 关于语言模型与生成语句的论文: Recurrent neural network based language model Extensions of Recurrent neural network based language model Generating Text with Recurrent Neural Networks ###机器翻译 机器翻译类似于语言模型,其输入为需要翻译的句子,输出则是翻译的目标语言的句子。与语言模型不同的是,我们在输入整个句子之后才输出。 …

RECURRENT NEURAL NETWORKS TUTORIAL, PART 1 – INTRODUCTION TO RNNS Read More »

理解LSTM网络

Recurrent Neural Networks(RNN) 人类是依靠自己过往的经验来学习,如同我们在读文章时,每一时刻,我们对于当前的概念的理解总要添加上对于之前获取的知识经验,总之,我们的思维是有持续性的。 传统的神经网络模型无法做到这一点,而这也是它的主要缺陷之一。 循环神经网络解决了这个难题,所谓循环,简单来说其结构允许信息在网络内留存,也即是,网络是有记忆的。 上图中,表示A表示神经网络的一块,在$$t$$时刻有输入$$X_t$$和输出$$h_t$$,这结构很简单,特别的是中间那一环,表示信息可以在网络内保存和传递。 以上的循环结构也可以拆解为下面的串列结构来理解: 我们可以把一个网络块的循环看成是多个相同的网络之间信息的传递,而这样的结构也就意味着,RNN天生就适合处理与序列相关的数据,如时间序列、NLP中的语言模型、音乐等。 近年来RNN的成功应用是与LSTM划不开联系的。接下来就给大家介绍LSTM网络。 ##长期依赖问题 RNN能够利用之前的信息来帮助当前的处理,这是我们对于RNN的期待。但是RNN能够做到这一点是有条件的:需要解决长期依赖问题。 举个例子,例如我们需要预测如下句子中的最后一个单词:“the clouds are in the sky”,因为有关的信息与其被需要的位置的距离不远,所以RNN可以轻易解决这个问题。 但是当预测需要更早的信息的时候呢?例如“I grew up in France… I speak fluent French.” 这时候有关的信息与其被需要的位置的距离可能很远。而此时,RNN变得很难处理这个问题。 理论上,RNN应该可以解决这类“长期依赖问题”,但是实际上却无法做到。幸运的是,LSTM可以解决这个问题! LSTM 网络 Long Short Term Memory networks,简称为LSTM,是一种可以学习长期依赖的RNN。那么,LSTM为何具有这种能力呢? 我们先来看看标准的RNN形式: 形式很简单,tanh层接受当前时刻的输入$$X_t$$以及上一时刻传来的记忆信息,输出$$h_t$$及记忆信息。 而LSTM在标准的RNN形式下,网络增加到了四层。 此图中,黄色的框格代表神经网络层,粉红色的圆圈代表逐点运算,例如向量相加或者相乘,单条黑色的线代表向量的传输,合并的黑色的线代表向量的连接,分叉的黑色线代表向量的复制(同一向量传送到不同的方向)。 ##LSTM网络核心理念 LSTM的核心就是网络内上面的那条水平线,称为cell state $$C_t$$,类似于传送带的功能,它在整个网络内直线传送,只做一些线性的变动,它代表着的是RNN中不变的信息。 LSTM具有对cell state进行增减信息的功能,是靠如下的部件–gate完成的。 gate由一个sigmoid层和逐点乘操作构成,我们都知道sigmoid函数输出的是一个介于0和1之间的数,那么gate的作用就显而易见了:通过sigmoid层的输入决定gate的输入有多少能被输入。而LSTM具有三个gate,都是为了保持和控制cell state。 分步理解LSTM LSTM结构的第一步是决定我们应该把多少信息从cell state里面丢弃。这是有一个叫做“forget gate”的部分完成的。其接收$$h_{t-1}$$和$$x_t$$,输出一个0和1之间的数,再与上一时刻的cell state$$C_{t-1}$$做逐点乘。 下一步则是决定把多少的新信息存储在cell state里面。首先,一个称为“input …

理解LSTM网络 Read More »