cs231n学习笔记——Lecture10 Recurrent Neural Network

管理员

该博客主要用于个人学习记录,部分内容参考自:2017CS231n笔记_S10循环神经网络CS231n笔记十:循环神经网络

一、循环神经网络(Recurrent Neural Networks,RNN)

1、RNN的基本结构

(1)RNN工作流程

每个RNN网络都有一个小的循环核心单元,它将传入输入值x,把x传到RNN中,RNN中有一个内部隐藏态(internal hidden state),这个内部隐藏态在每次RNN有新的输入时都会被更新。这个更新后的状态值会在下次读取输入时被反馈给模型。通常我们还希望RNN在每一次读取输入后都能产生一个输出。先读取输入,然后更新隐藏态,并生成输出

(2)循环函数:(h_t=f_W(h_{t-1},x_t))

注意,我们每一步用的都是同一个函数(f_w),同样的权重(w),会随隐藏态的变化而改变。
在每一个时间步,将当时的状态h输入到全连接层中,进行决策。

2、RNN的5种类型

依据输入或者输出的尺寸是否可变,RNN可分为5种情况,架构如下图所示:

Vanilla RNN
读取前一个隐藏态(h_{t-1})和当前的输入(x_t),生成下一个隐藏态。Vanilla的循环函数有两个权重,两个权重分别与前一个时间步状态h和此时间步的输入x进行相乘求和,然后使用tanh函数,将结果缩放至[-1,1]范围。Vanilla简单,但效果不怎么好。

One to Many:接收固定长的输入项,输出不定长的输出项

Many to One

Many to Many

Sequence to Sequence
Sequence to Sequence模型,会用来解决机器翻译之类的问题。输入一个不定长序列后输出一个不定长序列,可以看作是Many-to-one + One-to-many。存在编码和解码两个过程,编码阶段是Many-to-one,会收到一个不定长序列,可能是一个英语句子,整个句子会被编码器网络最终的隐层状态编码,把不定长输入编码成了一个单独的向量。解码网络是One-to-many,输入是前面编码好的向量,生成的是一个不定长输出序列,可能是用另一种语言表述的同意句子。

二、语言建模Language Modeling

1、字符级语言模型例子

通过读取一些语言从而让神经网络学会生成自然语言,我们让模型逐个生成字符/单词

假设有一个字符型的语言模型,网络读取一串字符序列,然后去预测这个文本流的下一个字符是什么


在本例中,词典为[h,e,l,o],该模型对输入序列进行训练,最后进行预测。
在训练阶段,设训练序列为"hello",将这个字符序列作为输入项(x_t)。 因为单词是由h,e,l,o这4个字母组成的,所以在输入序列中用四维向量表示这4个字母。
第一个time step,神经网络会收到输入'h',这个输入会进入到第一个RNN单元中,并输出(y_t),即网络对组成单词的每个字母做出的预测,也就是接下来它认为最有可能出现的字母。由于我们在训练的字母序列是hello,那么下一个正确的字母应该是e,但这里模型预测下一个最有可能的字母应该是o,在这种预测错误的情况下,我们可以使用Softmax损失函数来度量我们对这些预测结果的不满意程度。
下一个time step,我们将给模型输入在训练序列中的第二个字母e,重复执行上述过程,现在我们将e表述为一个向量,利用这个新的输入向量以及之前计算出的隐层状态,来生成输出一个新的隐层状态,并利用这个新的隐层状态再一次对组成单词的其他字母做出预测,我们下一个正确字母应该是l,但是这里模型预测认为不太可能是l,这时就会产生high loss。
不断重复上述过程并用不同的字母序列训练这个模型,最终它将会学习如何基于之前出现过的字符来预测接下来应出现的字符。

在测试阶段,我们想用该模型测试样本并使用这个训练好的神经网络模型生成新的文本,那种看起来类似于我们训练所使用的文本,通过一些输入前缀来测试这个模型。
在这个实例中,输入前缀h,会产生基于词库的所有字母的得分分布,在训练阶段会使用这些得分去输出一个结果,所以用softmax 函数把这些得分转换成一个概率分布,然后从概率分布中得到样本输出,去生成序列中的第二个字母,在这种情况下,即使得分情况非常不好,在这种情况下有可能从概率分布中得到e,现在把e以one-hot编码的向量形式在下一个time step中输入,重复上述过程,生成一个新的序列。

Q:为什么示例中不是输出一个得分最高的字母?
A:由于得到的是基于字母的概率分布图,不是直接取概率最高的字符输出,而是基于softmax输出的概率分布进行采样,高概率的字符更容易被采样并输出,低概率的字符也有几率被输出,从而使模型输出结果多样。比如对于相同输入的模型或者相同的前缀或在图像标注时使用相同的图像,当选择输出softmax采样值时会产生多组不同类型的输出序列。

Q:在测试阶段是否能输入一个softmax向量而不是一个one-hot向量?
A:首先这会与训练阶段所使用的数据不同,让训练好的模型去做与训练阶段不同的任务模型会产生偏差,通常会输出一些无用的信息。其次,当词典中含有大量信息时,想输出多个单词时,softmax向量在计算时间上会比较长,所以一般用稀疏向量处理one-hot向量。

2、反向传播

(1)Backpropagation through time

假设有一个序列,每个时间步生成y以及loss,这就是沿时间的反向传播方法。但该方法在序列很长的情况下,十分耗时。因为每次loss的计算,梯度计算和BP依赖于模型输入序列的长度。当输入序列很长时,BP就十分耗时。模型很难收敛,并且占用非常大的内存。

(2)Truncated Backpropagation through time

在实际中,采用一种近似方法,叫做沿时间的截断反向传播方法。将输入序列划分成若干个子序列(一般100个输入值划成一个子序列),然后对于每个子序列,做一次前向传播(会保留上一个子序列输出的隐藏态),算出子序列的损失,然后用这个损失做反向传播,并对W做一次更新。相当于mini batch的作用。

三、RNN应用

1、图像标注Image Captioning

图像标注是输入一幅图像,模型输出自然语言的图像语义信息。


下图是一些比较好的效果

下图是一些判断错误的结果

2、视觉问答Visual Wuestion Answering

模型的输入为一个图像和一个用自然语言描述的问题,模型根据图像内容输出针对这一问题的回答。

模型由CNN和RNN组成。CNN将图像概括为一个向量。输入的问题概括为一个向量。将CNN向量和输入向量结合,输入到RNN中,来对答案进行预测。

四、RNN变种

1、多层递归神经网络Multilayers RNN

之前的RNN都只有1个隐藏态,而多层的RNN中有多个隐藏态。下图是一个三层的RNN。首先我们会把输入值输入到第一层网络中,并产生第一层的隐藏态,即横着的第一行绿色块;之后我们要把这层隐藏态输入到第二层网络中,获得第二层隐藏态,至此类推。在很多模型中,两层/三层的RNN模型是很常见的。

在Vanilla的RNN模型中,将(x_t)(h_{t-1})堆叠起来,然后和W做点乘,然后经过一个tanh函数得到(h_t)的输出。在反向传播时,先得到上游传回来的(frac{dh_{t-1}}{dL}),经过tanh门然后传到乘法门,矩阵乘法门处实际上就是把梯度乘上一个矩阵W的转置,之后再作为(frac{dh_{t-1}}{dL})传给下游。所以在反向传播时,每经过一个RNN基本单元,我们都会把梯度和部分权重矩阵的转置相乘(之所以是部分,是因为有一个tanh激活函数)。计算(frac{dh_{t}}{dL})是为了方便计算local (frac{dW}{dL})

通常,在一个RNN模型中有多个这样的单元,每经过一个单元,就要把梯度乘以一个W的转置,在经过多个单元后,梯度会随W发生指数变化:
若W的最大奇异值>1:发生梯度爆炸Exploding gradients
若W的最大奇异值<1:发生梯度消失Vanishing gradients

针对梯度爆炸问题,一种解决方法是采用梯度截断Gradient clipping。在计算梯度后,如果梯度的L2范式大于某个阈值,就把梯度除以(threshold/grad_norm)。在训练RNN时,这个方法虽然粗暴但对于防止发生梯度爆炸问题还是相对有用的。
针对梯度消失问题,常见做法是换一个更复杂的RNN结构

2、长短期记忆网络Long Short Term Memory(LSTM)

LSTM是一种更高级的循环神经网络,是用来解决梯度消失和梯度爆炸问题的。

Vanilla RNN有隐藏态,在每一个time step 利用递归关系来更新隐藏态。
LSTM在每个time step中都维持2个隐藏态,一个是(h_t),与Vanilla RNN中的隐藏态类似,另一个是(c_t),叫做单元状态(cell state),相当于保留在LSTM内部的隐藏状态,并不会暴露到外部。通过公式可知,首先我们用当前输入(x_t)和上一个time step的隐藏态堆叠在一起,乘以一个非常大的权重矩阵W,来计算(i, f, o, g)四个门向量,使用这些门来更新(c_t),然后用(c_t)计算下一个time step中的隐藏态(h_t)

i输入门,表示LSTM要接受多少新的输入信息
f遗忘门,表示要遗忘多少上一步的记忆信息
o输出门,表示要输出多少信息
g无名门,表示要把什么信息写到输入单元里去。
这4个门都使用了不同的非线性函数。其中,i、f、o都用了sigmoid函数,输出值都在0和1之间,而g使用了tanh函数,输出都在-1和1之间。

在LSTM中,可以看到从(c_t)(c_{t-1})反向传播计算梯度时,只会把梯度乘上一个f向量,而且是点乘还不是矩阵乘,且由于每一个time step里的f向量都不一样,所以也不会出现之前那样重复乘上同一个W而导致的梯度爆炸或梯度消失的问题出现。同时由于f向量是从一个sigmoid函数里出来的,所以它的值始终在0到1之间,乘上它之后的梯度仍然会在一个很合理的范围内。

3、其他RNN变种