这一节我们主要学习
- RNN 网络的介绍
- 长短期记忆网络(LSTM)的介绍与详解
1.应用实例

我们假设订票系统听到用户说:“ i would like to arrive Taipei on November 2nd”,
你的系统有一些slot(有一个slot叫做Destination,一个slot叫做time of arrival),
系统要自动知道这边的每一个词汇是属于哪一个slot,
比如Taipei属于Destination这个slot,November 2nd属于time of arrival这个slot。
我们将每一个单词用一个向量来表示。
向量的维度刚好是字典的大小,每一个维度与字典中的一个单词对应。
我们首先要对输入序列向量化,将每一个输入的单词用向量表示,
可以使用 One-of-N Encoding 或者是 Word hashing 等编码方法,输出预测槽位的概率分布。
当遇到不在字典中的单词时,需要为向量增加一个新维度,表示不在字典中的单词。
我们也可以使用单词哈希的方法来表示一个单词。
输入一个单词,输出该单词属于各个 slot 的可能性。

当碰到”离开台北”或者”前往台北”的情况时,我们就无法判断”台北”是目的地还是出发地;
所以我们需要网络拥有记忆功能,去解决input不同的词汇,output不同的问题。

如果今天我们的neural network是有记忆力的,
它记得它看过红色的Taipei之前它就已经看过arrive这个词汇;
它记得它看过绿色之前,它就已经看过leave这个词汇,它就可以根据上下文产生不同的output。
如果让我们的neural network是有记忆力的话,它就可以解决input不同的词汇,output不同的问题。
2.RNN
这种有记忆的neural network就叫做Recurrent Neural network(RNN)。
在RNN里面,每一次hidden layer的neuron产生output的时候,
这个output会被存到memory里去(用蓝色方块表示memory)。
那下一次当有input时,这些neuron不只是考虑input $x_1,x_2$,还会考虑存到memory里的值。
对它来说除了$x_1,x_2$以外,这些存在memory里的值$a_1,a_2$也会影响它的output。
2.1 实例
memory 中的初始值可以设为 0,所有权重都为 1,没有 bias,激活函数为线性激活函数。
得到下面的结果:
当我们改变输入序列的顺序时,会得到不同的结果。

2.2 RNN架构
将 RNN 运用到插槽填充的例子中,前一次输入会对本次的输出结果产生影响。
相同的网络结构会被使用多次,但每次 memory 中的内容不会完全相同。

例如:当”arrive” 变成 “leave” 时,会使得 memory 中的内容改变,从而影响下一次的的输出

那所以我们有了memory以后,刚才我们讲了输入同一个词汇,我们希望output不同的问题就有可能被解决。
比如说,同样是输入“Taipei”这个词汇,但是因为红色“Taipei”前接了“leave”,
绿色“Taipei”前接了“arrive”(因为“leave”和“arrive”的vector不一样,所以hidden layer的output会不同),
所以存在memory里面的值会不同。现在虽然x2的值是一样的,
因为存在memory里面的值不同,所以hidden layer的output会不一样,
所以最后的output也就会不一样。这是Recurrent Neural Network的基本概念。
2.3 其他RNN

Recurrent Neural Networ的架构是可以任意设计的,比如说:
它当然是deep(刚才我们看到的Recurrent Neural Networ它只有一个hidden layer),
当然它也可以是deep Recurrent Neural Networ。
比如说,我们把$x^t$丢进去之后,它可以通过一个hidden layer,
再通过第二个hidden layer,以此类推(通过很多的hidden layer)才得到最后的output。
每一个hidden layer的output都会被存在memory里面,在下一个时间点的时候,
每一个hidden layer会把前一个时间点存的值再读出来,以此类推最后得到output,这个process会一直持续下去。
2.4 Elman Network 和 Jordan Network
Jordan Network 的表现通常比 Elman Network 好:
Jordan Network 将上一次的输出结果作用于本次的输入,有明确的实际意义;
而 Elman Network 将隐藏层的结果作用下一次的输入,无法判断该输出会产生怎样的影响。
2.5 Bidirectional neural network(双向RNN)
Recurrent Neural Networ还可以是双向,什么意思呢?
我们刚才Recurrent Neural Networ你input一个句子的话,它就是从句首一直读到句尾。
假设句子里的每一个词汇我们都有$x^t$表示它。他就是先读$x^t$在读$x^{t+1}$在读$x^{t+2}$ 。
但是它的读取方向也可以是反过来的,它可以先读$x^{t+2}$,再读$x^{t+1}$,再读$x^{t}$。
你可以同时train一个正向的Recurrent Neural Network,又可以train一个逆向的Recurrent Neural Network,
然后把这两个Recurrent Neural Network的hidden layer拿出来,都接给一个output layer得到最后的$y^t$ 。
所以你把正向的network在input $x^t$ 的时候跟逆向的network在input $x^t$ 时,都丢到 output layer产生$y^t$,
然后产生$y^{t+1}$,$y^{t+2}$,以此类推。
用Bidirectional neural network的好处是,neural在产生output的时候,它看的范围是比较广的。
如果你只有正向的network,再产生$y^t$,$y^{t+1}$的时候,你的neural只看过$x^1$到$x^{t+1}$的input。
但是我们今天是Bidirectional neural network,在产生$y^{t+1}$的时候,
你的network不只是看过$x^1$,到$x^{t+1}$所有的input,它也看了从句尾到$x^{t+1}$的input。
那network就等于整个input的sequence。
假设你今天考虑的是slot filling的话,
你的network就等于看了整个sentence后,才决定每一个词汇的slot应该是什么。
这样会比看sentence的一半还要得到更好的performance。
3.长短时记忆网络(Long Short-term Memory,LSTM)
最大的特点是有三个门控制输入,输出和记忆的保存:输入门(input gate),遗忘门(forget gate),输出门(output gate)。
激活函数 $f$ 通常是sigmoid函数,
$c’=g(z)f(z_i)+cf(z_f)$;
其中$z_i$为输入门的信号,$z$为输入信号,$c$为memory中的值,$z_f$为忘记门的信号
$a=h(c’)f(z_0)$;$c’$为memory中此次的输出信号,$z_0$为忘记门的输入信号,$a$ 为该block最终的输出结果
3.1 LSTM 实例化
我们的network里面只有一个LSTM的cell,那我们的input都是三维的vector,output都是一维的output。
那这三维的vector跟output还有memory的关系是这样的。
假设第二个dimension $x_2$ 的值是1时,$x_1$的值就会被写到memory里,
假设$x_2$ 的值是-1时,就会reset the memory,假设$x_3$的值为1时,你才会把output打开才能看到输出。
$x_1,x_2,x_3$ 分别对应该 block 的输入值,输入门和忘记门的控制信号以及输出门的控制信号。
该序列的输出结果如图所示,

3.2 LSTM 实现原理
将神经网络中的神经元用 LSTM block 替换即可。

你可能会想这个跟我们的neural network有什么样的关系呢。
你可以这样想,在我们原来的neural network里面,我们会有很多的neural,
我们会把input乘以不同的weight当做不同neural的输入,
每一个neural都是一个function,输入一个值然后输出一个值。
但是如果是LSTM的话,其实你只要把LSTM那么memory的cell想成是一个neuron就好了。
LSTM因为需要四个input,而且四个input都是不一样,原来的一个neuron就只有一个input和output,
所以LSTM需要的参数量(假设你现在用的neural的数目跟LSTM是一样的)是一般neural network的四倍。
这个跟Recurrent Neural Network 的关系是什么,这个看起来好像不一样,所以我们要画另外一张图来表示。
3.3 LSTM 详解
将输入向量 $x$ 分别乘以四个转换矩阵,得到 4 个输入向量,对应 LSTM 中的四个输入。
假设我们现在有一整排的neuron(LSTM),这些LSTM里面的memory都存了一个值,把所有的值接起来就变成了vector,
写为 $c^{t-1}$(一个值就代表一个dimension)。现在在时间点t,input一个vector $x^t$,
这个vector首先会乘上一matrix(一个linear transform变成一个vector z,z这个vector的dimension就代表了操控每一个LSTM的input(z这个dimension正好就是LSTM memory cell的数目)。
z的第一维就丢给第一个cell(以此类推)
这个$x^t$会乘上另外的一个transform得到$z^i$,然后这个$z^i$的dimension也跟cell的数目一样,
$z^i$ 的每一个dimension都会去操控input gate(forget gate 跟output gate也都是一样,这里就不在赘述)。
所以我们把$x^t$乘以四个不同的transform得到四个不同的vector,
四个vector的dimension跟cell的数目一样,这四个vector合起来就会去操控这些 memory cell运作。
对于输入向量,LSTM 仍然具有上述的性质。

一个memory cell就长这样,现在input分别就是$z,z^i,z^o,z^f$(都是vector),
丢到cell里面的值其实是vector的一个dimension,
因为每一个cell input的dimension都是不一样的,
所以每一个cell input的值都会是不一样。
所以cell是可以共同一起被运算的,怎么共同一起被运算呢?
我们说,$z^i$通过activation function跟z相乘,
$z^f$通过activation function跟之前存在cell里面的值相乘,
然后将z跟$z^i$相乘的值加上$z^f$跟$c^{t-1}$相乘的值,
$z^o$通过activation function的结果output,跟之前相加的结果再相乘,最后就得到了output$y^t$。
之前那个相加以后的结果就是memory里面存放的值$c^t$,这个process反复的进行,
在下一个时间点input $x^{t+1}$,把 z 跟input gate相乘,把forget gate跟存在memory里面的值相乘,
然后将前面两个值再相加起来,在乘上output gate的值,然后得到下一个时间点的输出$y^{t+1}$
你可能认为说这很复杂了,但是这不是LSTM的最终形态,真正的LSTM,会把上一个时间的输出接进来,
当做下一个时间的input,也就说下一个时间点操控这些gate的值不是只看那个时间点的input $x^t$,
还看前一个时间点的output $h^t$。其实还不止这样,还会加一个东西叫做“peephole”,
这个peephole就是把存在memory cell里面的值也拉过来。
那操控LSTM四个gate的时候,你是同时考虑了$x^{t+1}$,$h^t$,$c^t$,
你把这三个vector并在一起乘上不同的transform得到四个不同的vector再去操控LSTM。
3.4 多层 LSTM

LSTM通常不会只有一层,若有五六层的话。
大概是这个样子。每一个第一次看这个的人,反映都会很难受。
现在还是 quite standard now,
当有一个人说我用RNN做了什么,你不要去问他为什么不用LSTM,因为他其实就是用了LSTM。
现在当你说,你在做RNN的时候,其实你指的就用LSTM。Keras支持三种RNN:‘’LSTM‘’,“GRU”,”SimpleRNN”
3.5 GRU

GRU是LSTM稍微简化的版本,它只有两个gate,
虽然少了一个gate,但是performance跟LSTM差不多(少了1/3的参数,也是比较不容易overfitting)。
如果你要用这堂课最开始讲的那种RNN,你要说是simple RNN才行。