最近看了台湾大学李宏毅老师讲的RNN视频,觉得非常好,因此总结一下,希望对自己有提升,同时也分享给需要的朋友。
下面进入正题:
我们先来看一个例子,在订票系统或者说更通用的智能客服中都涉及到Slot Filling问题,具体如下图:
如上图所示,针对用户提出的I would like to arrive Taipei on November 2nd,ticket booking system系统需要有效识别出槽位信息即Destination-> Taipei,与 time of arrival-> November 2nd。好了,这就是我们要解决的问题,那么如何实现呢?
首先,我们很容易想到的是使用Feedforward network来解决slot filling,那我们来看一下具体如何解决以及解决过程中又会出现什么样的问题。
如上图所示在我们的Feedforward network里面,对于输入的word,通常都是表示成vector,对于如何使用vector来表示word,通常我们最容易想到的是1-of-N encoding,如下图所示
但是在1-of-N encoding会出现Beyond 1-of-N encoding的问题,即总会出现不在词表中的word,在这里把不在词表中的word用other表示,比如下图中的Gandalf与Sauron。
同样也可以使用Word hashing来表示,比如下图中的apple就使用Word hashing表示:
到目前为止我们已经知道如何把word表示成vector,对于我们的问题slot filling,在使用Feedforward network中我们的输入是vector,输出是计算输入的vector是否是slots的概率
但仅仅使用Feedforward network会出现一些问题,如下图所示:
上图中对于arrive Taipei on November 2nd和leave Taipei on November 2nd两句话,如果仅仅使用Feedforward network,对于Taipei这个word不可能出现两个slot,即分别对应dest与place of departure。因此我们需要使我们的Neural network具备memory的功能(通俗来讲,就是利用上下文信息)
如何使Neural network具备memory的功能呢?这就需要我们这次主要讨论的Recurrent Neural Network (RNN)
从上图中我们可以看到,在RNN输出的hidden layer中存储我们的memory,同时把hidden layer的输出又作为下一个输入,输入到最后的输出中。
上面的解释可能还不够清晰,我们来看一个具体的例子,假设RNN网络中的所有weight都是1,没有bias,同时所有的激活函数(activation function)都是线性(linear)的,我们的输入如下:
把我们的输入依次输入下面的网络中,
我们先从第一次输入开始,初始化hidden layer中的memory为(0,0),对于我们的第一个输入(1,1)经过线性变化(具体过程为1+1+0+0,1+1+0+0),我们的hidden layer中的值变为(2,2),利用hidden layer中的(2,2)更新图中蓝色的memoey中,同时hidden layer经过线性变换(具体过程为2+2,2+2)到达输出层,输出层值为(4,4),因此输出层的最后结果如下:
接下来我们看第二个输入,此时我们的memory更新后变成了(2,2),因此对于第二个输入(1,1)线性变化(具体过程为1+1+2+2,1+1+2+2),此时的hidden layer中的值变为(6,6),同时利用hidden layer中的(6,6)去更新图中蓝色的memoey中,同时hidden layer经过线性变换(具体过程为6+6,6+6)到达输出层,输出层值为(12,12),因此输出层的最后结果如下:
接下来我们看第三个输入,此时我们的memory更新后变成了(6,6),第三次输入(2,2)线性变化(具体过程为2+2+6+6,2+2+6+6),此时的hidden layer中的值变为(16,16),利用hidden layer中的(16,16)更新图中蓝色的memoey中,同时hidden layer经过线性变换(具体过程为16+16,16+16)到达输出层,输出层值为(32,32),因此输出层的最后结果如下:
通过上面的例子,我们清晰的看到在RNN中对于同样的输入(1,1),由于考虑前一时刻hidden layer的输出,最终的输出会表现出不同的结果,同时RNN对顺序是比较敏感的,改变顺序将会得到不同的最终输出。
通过上面的解释,相信大家对RNN已经有了清晰的了解,接下来再看下我们的例子,把arrive Taipei on November 2nd输入RNN看是如何运转的,执行流程如下图所示:
根据上图首先把arrive(即X1)输入RNN,经过hidden layer输出是a1,同时把a1放入memory中(图中浅蓝色表示),接下来输入Taipei(即X2),此时hidden layer的输出既依赖于X2又依赖于memeory中的a1(即上一个hidden layer的输出),上述过程依次传递。
通过前面的例子我们可以看到对于同样的输入,如果输入顺序不同会导致结果不同,因为对于arrive Taipei on November 2nd和leave Taipei on November 2nd两句话,第一句话中Taipei的前一个输入是arrive,后一句话中Taipei的前一个输入是leave。因为会导致最终的输出结果不同,也就能区分出Taipei具有不同的含义。
上面我们只简单介绍了单层的RNN,同样RNN也可以多层,如下图所示:
在我们熟知的RNN中一般我们都是指Elman Network,如下图所示:
当然还存在一个RNN是Jordan Network,如下图所示:
他们两者的区别在于Elman Network是利用hidden layer的输出作为下一个的输入,而 Jordan Network是直接把输出作为下一个hindden layer的另一个输入。
到这里我们可能已经发现上面RNN的问题,就是它只依赖于上一个时刻的输入,对下一刻的输入没有依赖,视野方面不够宽广,所以为了解决这个问题,出现了Bidirectional RNN,网络结构如下图:
希望的朋友可以关注下我们的微信公众号“AI壹号堂”,以及我们的QQ技术交流群220823345。