关键词

1.3 循环神经网络模型-深度学习第五课《序列模型》-Stanford吴恩达教授

循环神经网络模型 (Recurrent Neural Network Model)

上节视频中,你了解了我们用来定义序列学习问题的符号。现在我们讨论一下怎样才能建立一个模型,建立一个神经网络来学习 XXXYYY 的映射。

可以尝试的方法之一是使用标准神经网络,在我们之前的例子中,我们有9个输入单词。想象一下,把这9个输入单词,可能是9个one-hot向量,然后将它们输入到一个标准神经网络中,经过一些隐藏层,最终会输出9个值为0或1的项,它表明每个输入单词是否是人名的一部分。

但结果表明这个方法并不好,主要有两个问题,

一、是输入和输出数据在不同例子中可以有不同的长度,不是所有的例子都有着同样输入长度 TxT_xTx 或是同样输出长度的 TyT_yTy 。即使每个句子都有最大长度,也许你能够填充(pad)或零填充(zero pad)使每个输入语句都达到最大长度,但仍然看起来不是一个好的表达方式。

二、一个像这样单纯的神经网络结构,它并不共享从文本的不同位置上学到的特征。具体来说,如果神经网络已经学习到了在位置1出现的Harry可能是人名的一部分,那么如果Harry出现在其他位置,比如 x<t>x^{<t>}x<t> 时,它也能够自动识别其为人名的一部分的话,这就很棒了。这可能类似于你在卷积神经网络中看到的,你希望将部分图片里学到的内容快速推广到图片的其他部分,而我们希望对序列数据也有相似的效果。和你在卷积网络中学到的类似,用一个更好的表达方式也能够让你减少模型中参数的数量。

之前我们提到过这些(上图编号1所示的 x<1>x<t>x<Tx>x^{<1>}……x^{<t>}……x^{<T_x>}x<1>x<t>x<Tx> )都是10,000维的one-hot向量,因此这会是十分庞大的输入层。如果总的输入大小是最大单词数乘以10,000,那么第一层的权重矩阵就会有着巨量的参数。但循环神经网络就没有上述的两个问题。

那么什么是循环神经网络呢?我们先建立一个(下图编号1所示)。如果你以从左到右的顺序读这个句子,第一个单词就是,假如说是 x<1>x^{<1>}x<1> ,我们要做的就是将第一个词输入一个神经网络层,我打算这样画,第一个神经网络的隐藏层,我们可以让神经网络尝试预测输出,判断这是否是人名的一部分。循环神经网络做的是,当它读到句中的第二个单词时,假设是 x<2>x^{<2>}x<2> ,它不是仅用 x<2>x^{<2>}x<2> 就预测出 y^<2>hat{y}^{<2>}y^<2> ,他也会输入一些来自时间步1的信息。具体而言,时间步1的**值就会传递到时间步2。然后,在下一个时间步,循环神经网络输入了单词 x<3>x^{<3>}x<3> ,然后它尝试预测输出了预测结果 y^<3>hat{y}^{<3>}y^<3> ,等等,一直到最后一个时间步,输入了 x<Tx>x^{<T_x>}x<Tx> ,然后输出了 y^<Ty>hat{y}^{<T_y>}y^<Ty> 。至少在这个例子中 Tx=TyT_x=T_yTx=Ty ,同时如果 TxT_xTxTyT_yTy 不相同,这个结构会需要作出一些改变。所以在每一个时间步中,循环神经网络传递一个**值到下一个时间步中用于计算。

要开始整个流程,在零时刻需要构造一个**值 a<0>a^{<0>}a<0> ,这通常是零向量。有些研究人员会随机用其他方法初始化 a<0>a^{<0>}a<0> ,不过使用零向量作为零时刻的伪**值是最常见的选择,因此我们把它输入神经网络。

在一些研究论文中或是一些书中你会看到这类神经网络,用这样的图形来表示(上图编号2所示),在每一个时间步中,你输入 x<t>x^{<t>}x<t> 然后输出 y<t>y^{<t>}y<t> 。然后为了表示循环连接有时人们会像这样画个圈,表示输回网络层,有时他们会画一个黑色方块,来表示在这个黑色方块处会延迟一个时间步。我个人认为这些循环图很难理解,所以在本次课程中,我画图更倾向于使用左边这种分布画法(上图编号1所示)。不过如果你在教材中或是研究论文中看到了右边这种图表的画法(上图编号2所示),它可以在心中将这图展开成左图那样。

循环神经网络是从左向右扫描数据,同时每个时间步的参数也是共享的,所以下页幻灯片中我们会详细讲述它的一套参数,我们用 WaxW_{ax}Wax 来表示管理着从 x<1>x^{<1>}x<1> 到隐藏层的连接的一系列参数,每个时间步使用的都是相同的参数 WaxW_{ax}Wax 。而**值也就是水平联系是由参数 WaaW_{aa}Waa 决定的,同时每一个时间步都使用相同的参数 WaaW_{aa}Waa ,同样的输出结果由 WyaW_{ya}Wya 决定。下图详细讲述这些参数是如何起作用。

在这个循环神经网络中,它的意思是在预测 y^<3>hat{y}^{<3>}y^<3> 时,不仅要使用 x<3>x^{<3>}x<3> 的信息,还要使用来自 x<1>x^{<1>}x<1>x<2>x^{<2>}x<2> 的信息,因为来自 x<1>x^{<1>}x<1> 的信息可以通过这样的路径(上图编号1所示的路径)来帮助预测 y^<3>hat{y}^{<3>}y^<3> 。这个循环神经网络的一个缺点就是它只使用了这个序列中之前的信息来做出预测,尤其当预测 y^<3>hat{y}^{<3>}y^<3> 时,它没有用到 x<4>x<5>x<6>x^{<4>},x^{<5>},x^{<6>}x<4>x<5>x<6> 等等的信息。所以这就有一个问题,因为如果给定了这个句子,“Teddy Roosevelt was a great President.”,为了判断Teddy是否是人名的一部分,仅仅知道句中前两个词是完全不够的,还需要知道句中后部分的信息,这也是十分有用的,因为句子也可能是这样的,“Teddy bears are on sale!”。因此如果只给定前三个单词,是不可能确切地知道Teddy是否是人名的一部分,第一个例子是人名,第二个例子就不是,所以你不可能只看前三个单词就能分辨出其中的区别。

所以这样特定的神经网络结构的一个限制是它在某一时刻的预测仅使用了从序列之前的输入信息并没有使用序列中后部分的信息,我们会在之后的双向循环神经网络(BRNN)的视频中处理这个问题。但对于现在,这个更简单的单向神经网络结构就够我们来解释关键概念了,之后只要在此基础上作出修改就能同时使用序列中前面和后面的信息来预测 y^<3>hat{y}^{<3>}y^<3> ,不过我们会在之后的视频讲述这些内容,接下来我们具体地写出这个神经网络计算了些什么。

这里是一张清理后的神经网络示意图,和我之前提及的一样,一般开始先输入 a<0>a^{<0>}a<0> ,它是一个零向量。接着就是前向传播过程,先计算**值 a<1>a^{<1>}a<1> ,然后再计算 y<1>y^{<1>}y<1>

a<1>=g1(Waaa<0>+Waxx<1>+ba)a^{<1>}=g_1(W_{aa}a^{<0>}+W_{ax}x^{<1>}+b_a)a<1>=g1(Waaa<0>+Waxx<1>+ba)
y^<1>=g2(Wyaa<1>+by)hat{y}^{<1>}=g_2(W_{ya}a^{<1>}+b_y)y^<1>=g2(Wyaa<1>+by)

我将用这样的符号约定来表示这些矩阵下标,举个例子 WaxW_{ax}Wax ,第二个下标意味着 WaxW_{ax}Wax 要乘以某个 xxx 类型的量,然后第一个下标 aaa 表示它是用来计算某个 aaa 类型的变量。同样的,可以看出这里的 WyaW_{ya}Wya 乘上了某个 aaa 类型的量,用来计算出某个 y^hat{y}y^ 类型的量。

循环神经网络用的**函数经常是tanh,不过有时候也会用ReLU,但是tanh是更通常的选择,我们有其他方法来避免梯度消失问题,我们将在之后进行讲述。选用哪个**函数是取决于你的输出 yyy ,如果它是一个二分问题,那么我猜你会用sigmoid函数作为**函数,如果是 kkk 类别分类问题的话,那么可以选用softmax作为**函数。不过这里**函数的类型取决于你有什么样类型的输出 yyy ,对于命名实体识别来说 yyy 只可能是0或者1,那我猜这里第二个**函数 ggg 可以是sigmoid**函数。

更一般的情况下,在 ttt 时刻,

a<t>=g1(Waaa<t1>+Waxx<t>+ba)a^{<t>}=g_1(W_{aa}a^{<t-1>}+W_{ax}x^{<t>}+b_a)a<t>=g1(Waaa<t1>+Waxx<t>+ba)
y^<t>=g2(Wyaa<t>+by)hat{y}^{<t>}=g_2(W_{ya}a^{<t>}+b_y)y^<t>=g2(Wyaa<t>+by)

所以这些等式定义了神经网络的前向传播,你可以从零向量 a<0>a^{<0>}a<0> 开始,然后用 a<0>a^{<0>}a<0>x<1>x^{<1>}x<1> 来计算出 a<1>a^{<1>}a<1>y^<1>hat{y}^{<1>}y^<1> ,然后用 x<2>x^{<2>}x<2>a<1>a^{<1>}a<1> 一起算出 a<2>a^{<2>}a<2>y^<2>hat{y}^{<2>}y^<2> 等等,像图中这样,从左到右完成前向传播。

现在为了帮我们建立更复杂的神经网络,我实际要将这个符号简化一下,我在下一张幻灯片里复制了这两个等式(上图编号1所示的两个等式)。

接下来为了简化这些符号,我要将这部分( Waaa<t1>+Waxx<t>W_{aa}a^{<t-1>}+W_{ax}x^{<t>}Waaa<t1>+Waxx<t> )(上图编号1所示)以更简单的形式写出来,我把它写做 a<t>=g(Wa[a<t1>,x<t>]+ba)a^{<t>}=g(W_a[a^{<t-1>},x^{<t>}]+b_a)a<t>=g(Wa[a<t1>,x<t>]+ba) (上图编号2所示),那么左右两边划线部分应该是等价的。所以我们定义 WaW_aWa 的方式是将矩阵 WaaW_{aa}Waa 和矩阵 WaxW_{ax}Wax 水平并列放置, [WaaWax]=Wa[W_{aa}vdots W_{ax}]=W_a[WaaWax]=Wa (上图编号3所示)。举个例子,如果 aaa 是100维的,然后延续之前的例子, xxx 是10,000维的,那么 WaaW_{aa}Waa 就是个
100100100,100100100 )维的矩阵, WaxW_{ax}Wax 就是个( 10010,000100,10,00010010,000 )维的矩阵,因此如果将这两个矩阵堆起来, WaW_{a}Wa 就会是个( 10010,100100,10,10010010,100 )维的矩阵。

用这个符号( [a<t1>,x<t>][a^{<t-1>},x^{<t>}][a<t1>,x<t>] )的意思是将这两个向量堆在一起,我会用这个符号表示,即 [a<t1>x<t>]left[begin{matrix} a^{<t-1>}\x^{<t>} end{matrix}right][a<t1>x<t>] (上图编号4所示),最终这就是个10,100维的向量。你可以自己检查一下,用这个矩阵乘以这个向量,刚好能够得到原来的量,因为此时,矩阵 [WaaWax][W_{aa}vdots W_{ax}][WaaWax] 乘以 [a<t1>x<t>]left[begin{matrix} a^{<t-1>}\x^{<t>} end{matrix}right][a<t1>x<t>] ,刚好等于 Waaa<t1>+Waxx<t>W_{aa}a^{<t-1>}+W_{ax}x^{<t>}Waaa<t1>+Waxx<t> ,刚好等于之前的这个结论(上图编号5所示)。这种记法的好处是我们可以不使用两个参数矩阵 WaaW_{aa}WaaWaxW_{ax}Wax ,而是将其压缩成一个参数矩阵 WaW_aWa ,所以当我们建立更复杂模型时这就能够简化我们要用到的符号。

同样对于这个例子( y^<t>=g(Wyaa<t>+by)hat{y}^{<t>}=g(W_{ya}a^{<t>}+b_y)y^<t>=g(Wyaa<t>+by) ),我会用更简单的方式重写, y^<t>=g(Wya<t>+by)hat{y}^{<t>}=g(W_ya^{<t>}+b_y)y^<t>=g(Wya<t>+by) (上图编号6所示)。现在 WyW_yWybyb_yby 符号仅有一个下标,它表示在计算时会输出什么类型的量,所以 WyW_yWy 就表明它是计算 yyy 类型的量的权重矩阵,而上面的 WaW_aWabab_aba 则表示这些参数是用来计算 aaa 类型或者说是**值的。

RNN前向传播示意图:

好就这么多,你现在知道了基本的循环神经网络,下节课我们会一起来讨论反向传播,以及你如何能够用RNN进行学习。

课程板书





本文链接:http://task.lmcjl.com/news/1548.html

展开阅读全文