我们先举个例子,Slot Filling(填槽)。 现在很流行做一些智慧客服,智慧的订票系统之类的。这种智慧客服或者智慧订票系统里面往往需要Slot Filling这个技术。 Slot Filling指的是什么呢? 比如,你的系统里面有一些slot,比如Destination,time of arrival等。当一个人说"I would like to arrive Taipei on November 2nd",你的系统要自动知道说这句话的每一个词汇属于哪一个slot。你的系统要知道Taipei属于Destination这个slot,November 2nd属于time of arrival这个slot,其他的词汇呢不属于任何slot。
那这个问题要怎么解呢?这个问题可以用 Feedforward Network来解。我叠一个全连接网络,它的输入呢,是Taipei,把它变成一个向量,丢到网络里面。
怎么把一个词汇用一个向量来表示呢?方法实在太多了。 最naive的方法呢,就是1-of-N encoding,这个就不要细讲。当然用word vector来表示一个词汇也是可以的。 或者是有一些beyond 1-of-N encoding的方法。 有时候你只用1-of-N encoding来描述一个词汇的话,你会遇到一些问题,因为有很多次词汇你可能从来没有见过。所以你需要在1-of-N encoding里面多加一个dimension,这个dimension代表"other"。然后所有的词汇,如果它不是在我们的词典里,就归类到"others"。比如说,"Gandalf"、"Sauron"(索伦)不在我们的词典里面,就归类到"other"。 也可以用某一个词汇的字母来表示它的向量,这样就不会有某个字母不在词典中的问题。比如说有个词汇"apple",它里面有出现"app","ppl","ple",在向量里面对应的三个dimension就是1,其他的为0。
好,无论如何,我们假设能把一个词汇表示成一个向量。就可以把这个向量丢到全连接网络里面去,在Slot Filling这个task里面呢,就会希望你的output是一个概率分布,这个概率分布代表表现在imput的这个词汇属于每一个slot的概率。举例还说,Taipei属于destination的概率。
但是,光有这个是不够的。全连接神经网络没有办法解决这个问题。为什么呢?假设现在有一个使用者说"arrive Taipei on November 2nd",另一个说"leave Taipei on November 2nd"。这两个"Taipei"一个是出发地,一个是目的地。但是对神经网络来说,一样的东西,输出是一样的。输入"Taipei",输出要么是出发地,要么是目的地。 那怎么办呢?这个时候,我们就希望我们的网络时有记忆力的。如果这个网络是有记忆力的,它在看过红色的Taipei之前就已经看过arrive这个词汇;在看过绿色的Taipei之前,就已经看过leave这个词汇。它就可以根据上下文产生不同的output。所以,让我们的网络有记忆力的话,就可以解决input相同的词汇但是output不同的问题。
------------------------------------------------------------------------------------------------------------------------------- 这种有记忆力的神经网络就叫做Recurrent Neural Network(RNN)。 在RNN里面,每一次隐藏层的神经元产生output的时候,这个output都会被存到memory(这里用蓝色的方块来表示)里面
下一次,当有input的时候,隐藏层的神经元不仅考虑输入的x1和x2还要考虑存在memory里面的值a1,a2。
我们举一个例子,可能会比较清楚。 假设我们图上的网络,所有的weight都是1,所有的神经元都没有bias,所有的激活函数都是线性的。我们的input呢,是 当这个序列输入到网络里面,会发生什么事呢?
首先在开始要使用RNN之前,必须要给memory初始值,假设我们给的是0。 输入第一个输入[1,1]T。对于隐藏层神经元来说,除了接到输入[1,1]T,还接到0和0。得到的output是1*1+1*1+0*1+0*1=2。 红色神经元的的output是,2*1+2*1=4。 接下来,RNN会把隐藏层的output存到memory里面去。所以memory里面的值会变成2. 接下来再输入第二个[1,1]T,隐层的神经元的输入有4个。1*1+1*1+2*1+2*1 = 6。红色神经元的输入是12。 所以,对于RNN来说,就算输入相同的input,它的output也是有可能不一样的。因为存在memory中的值是不一样的。 接下来,6和6会存到memory里面。 接下来,input是[2,2]T。输出是[32,32]T。
在做RNN的时候,有一件很重要的事情,就是输出是跟输入的顺序有关系的。比如例子中,如果把[2,2]T放到前面,那么输出就会完全不一样。
------------------------------------------------------------------------------------------------------------------------------- 所以,今天我们要用RNN来处理Slot Filling这个问题的话,看起来就像这样。 有一个使用者说,"arrive Taipei on Novement 2nd"。将arrive变成一个向量,丢到神经网络里面去。隐藏层的输出是a1,根据a1产生y1。y1就是"arrive"属于每个slot的概率。
接下来,a1会放到memory里面。 然后,Taipei会变成input。隐藏层会同时考虑input"Taipei"和"a1"。得到a2,然后得到y2,y2时Taipei属于每个slot的概率。 这个过程以此类推,再把a2存到memory里面,再把"on"丢进去得到a3,y3。y3代表"on"属于每个slot的概率。
有一个注意的事情是,有人看到这个图会说,这里有三个网络。而这不是三个网络,而是同一个网络在三个不同的时间点被使用了三次。 所以,我们有了memory以后,刚才的我们希望输入相同的词汇得到不同的输出的问题就有可能被解决。比如,同样输入"Taipei",但是他们前面接的词汇一个是"leave",一个是"arrive"。因为"leave"和"arrive"他们的向量不一样,所以隐层的输出也不一样,所以存在memory里面的值也会不同。所以,虽然都是输入"Taipei",但是存在memory里面的值不同,所以得到值就不一样。
------------------------------------------------------------------------------------------------------------------------------- 上面就是RNN的基本概念。当然RNN的架构呢,是可以任意设计的。比如说,当然可以是deep的。比如说,我们把x1丢进去以后,它可以通过一个隐藏层,再通过第二个隐藏层,...,通过很多个隐藏层以后才得到最后的output。
每一个隐藏层的output都会存在memory里面,在下一个时间点的时候,每一个隐藏层会把前一个时间点存放在memory里面的值读出来,再计算。最后,得到output。 这个过程一直持续下去。
deep要叠几层都是可以的。
------------------------------------------------------------------------------------------------------------------------------- RNN有不同的变形。我们刚才讲的叫做Elman Network。我们是把隐藏层的值存起来,下一个时间点再读出来,这个叫做Elman Network。 有另外一种,叫做Jordan Network,Jordan Network它存的是,整个网路的output值。它在下一个时间点再把output值读进来。传说呢,Jordan Network可以得到比较好的performance。因为,隐藏层是没有target的,有点难控制说它学到了什么隐藏层的information,要把什么放到memory里面。而y是有target的,所以我们可以比较清楚放到memory里面的是什么东西。 ------------------------------------------------------------------------------------------------------------------------------- RNN还可以是双向的。
什么意思呢?我们刚才看到RNN,输入一个句子的话,它就是从句首一直读到句尾。假设句子中的每一个词汇,我们用xt来表示的话,它就是先读xt,再读xt+1,再读xt+2。
但是呢,其实它的读取方向也可以是反过来的。先读xt+2,再读xt+1,再读xt。 你可以同时train一个正向的RNN和一个逆向的RNN,然后把这两个RNN的隐藏层拿出来,都接给一个输出层。得到最后的y。
用双向RNN的好处呢,是在产生output的时候,看的范围是比较广的。如果只有正向的网络,在产生yt+1的时候,网络只看过x1,x2,...,xt+1的input。但是如果是双向RNN的话,在产生yt+1的时候,网络不仅看了y1~yt+1的值,它也看了从句尾到xt+1的input。就相当于,你的网络看了整个句子以后,才决定每个单词是什么,会比只看一半得到更好的performance。
|