0%

吴恩达李宏毅综合学习笔记:RNN入门

大纲

序号 课程 内容
2~8 吴恩达深度学习 one hot编码、RNN包括双向和深层、GRU、LSTM
9~14 李宏毅机器学习 RNN包括双向和深层、LSTM、RNN反向传播、seq2seq
15~20 李宏毅深度学习 计算图、语言模型中的深度学习、几个有用的网络架构。到原视频的 p12 结束,由于后续部分涉及到了 GAN 等其他模型,所以不在此处做笔记,详见对神经网络整体的理解博文中靠后的几节
21 李宏毅机器学习/深度学习剩余的一些知识点 这些知识点记录在其他的博客中,此处只提供链接

字母表示

假设:

x: Harry Potter and Hermione Granger invented a new spell.

y: 1 1 0 1 1 0 0 0 0

其中1代表人名地名之类的单词。这句话一共有九个单词,则x可以表示为:\(x^{<1>} x^{<2>} \cdots x^{<t>} \cdots x^{<9>}\)

则y可以表示为:\(y^{<1>} y^{<2>} \cdots y^{<t>} \cdots y^{<9>}\)

输入的长度表示为\(T_x\),则\(T_x = 9\)

输出的长度表示为\(T_y\),则\(T_y = 9\)

之前在神经网络中\(X^i\)\(X^(i)\)代表第i个训练样本。现在在序列模型中,\(X^{(i)<t>}\)代表代表第i个训练样本的第t个元素。对应地,\(T^i_x\)就代表第i个样本的输入长度。

one hot编码

在我们做自然语言处理时,一件需要事先决定的是,怎么表示一个序列里的单词。

第一件事就是做一张词表(Vocabulary)有时也叫字典(Dictionary),然后将表示方法中要使用的单词列出一列。最后将一个单词用一个稀疏向量表示,如Harry表示为\(\begin{pmatrix}0&0&0&\cdots&1&0&\cdots&0\end{pmatrix}\)。1所在位置就是Harry这个单词在词表中的所在位置。 ont hot例子

循环神经网络——RNN

与Simple Neural Network不同的是,循环神经网络的每一层都要有输入x和输出y。

第一步与Simple Neural Network类似,\(a_1 = w_{ax} * x^{<1>} + b_a\),这样就获得了激活值a,但是这时需要使用sigmoid函数或者其他函数直接算出y,另外与Simple Neural Network不同的是,它在计算激活值时需要附带加上前一层的激活值乘上一个权重,此权重与其他的权重类似,也是NN自己训练的。所以第二个序列的计算公式是\(a_2 = w_{aa} * a_1 + w_{ax} * x^{<2>} + b_a\)。后面的序列就跟第二个序列一样。注意一点,RNN中平行方向是时间序列,并不是隐藏层,并且此例中为了方便起见,垂直方向只有一个隐藏层。那几个圆圈是神经元。看下图。 吴恩达深度学习中的RNN示意图 1. 由于为了一般化,第一层需要修改成跟后面的计算类似,所以引入一个零向量\(a_0\)来计算\(a_1\)。 所以RNN的计算公式为: \[ \left\{ \begin{array}{c} a^{<1>} = g_1(w_{aa} * a^{<0>} + w_{ax} * x^{<1>} + b_a)\\ \hat{y}^{<1>} = g_2(w_{ya} * a^{<1>} + b_y)\\ a^{<2>} = g_1(w_{aa} * a^{<1>} + w_{ax} * x^{<2>} + b_a)\\ \hat{y}^{<2>} = g_2(w_{ya} * a^{<2>} + b_y)\\ \vdots\\ a^{<t>} = g_1(w_{aa} * a^{<t-1>} + w_{ax} * x^{<t>} + b_a)\\ \hat{y}^{<t>} = g_2(w_{ya} * a^{<t>} + b_y)\\ \end{array} \right. \] 注意上式中的\(w_{aa}\)\(w_{ax}\)\(w_{ya}\)\(b_{a}\)\(b_{y}\)并没有上标或者下标,所以意味着每一层同一个符号的权重值和偏差值都是一样的。另外对于激活函数也是用户自行选择,在对神经网络整体的理解一文中已经解释的很清楚了,为了区分输入与输出的激活函数不同,我特意使用了不同的下标,这个下标仅代表这个意思。

  1. 为了进一步地一般化,我们将\(w_{aa}\)\(w_{ax}\)合并成为\(w_{a}\),如果表示为矩阵形式就是\(w_{a} = \begin{pmatrix}w_{aa} | w_{ax}\end{pmatrix}\),然后将1中的最后两行表达式一般化为: \[ a^{<t>} = g_1(w_{a} * [a^{<t-1>}, x^{<t>}] + b_a)\\ \hat{y}^{<t>} = g_2(w_{y} * a^{<t>} + b_y)\\ \] 表达式\([a^{<t-1>}, x^{<t>}]\)的意思是将两个向量堆起来,如果表示为矩阵形式就是\(\begin{pmatrix} a^{<t-1>}\\ x^{<t>}\\ \end{pmatrix}\),上式为了排版问题就不写成矩阵形式了。

RNN的反向传播

跟Simple Neural Network类似,也要先定义一个cost function,可以选择crossentropy。由于RNN每一层都有输出值y,所以需要对每一层都求出代价,最后将这些代价值加起来

<p>吴恩达老师在讲反向传播的实现时并没有讲计算过程,所以有点糊里糊涂的。从代价函数到激活值反向传播还可以理解,但是从后一层到前一层的反向传播理解不了。另外由于权重值一样,那么权重值到底该怎么更新?</p>

不同类型的RNN

上面讲到的都是\(T_x = T_y\),但是有时候输入和输出的长度并不相同。 不同类型的RNN实例

多对多(many to many)、多对一(many to one)、一对一(one to one)、一对多(one to many)架构 不同类型的RNN结构

语言模型和序列生成

对新序列采样

长期依赖,梯度消失

观察两个句子:

  • The cat, which already ate..., was full.
  • The cats, which already ate..., were full.

这两个句子只有复数形式上的不同,但是开头的名词影响到了最后面的be动词。但是我们目前见到的最基本的RNN不擅长捕获这种长期依赖效应。

用梯度消失解释一下为什么,其实原理相同的,这里引用之前的文章 梯度消失和梯度爆炸

GRU单元——Gate Recurrent Unit

中文名为门控循环单元。它也解决了梯度消失的问题。

符号表示

c = memonry cell,使用\(c^{<t>}\)符号表示输出,其中\(c^{<t>} = a^{<t>}\),由于后面的LSTM的c和a代表意思不同,所以这里直接使用c来表示输出值。所以本小章下的c你都看作是a即可。

GRU工作流程

由于通过\(c^{<t-1>}\)来更新\(c^{<t>}\)的值,但是现在我们使用GRU,GRU就是来控制是否更新\(c^{<t>}\)的值的,这里使用“更新”的名词可能有点怪,因为\(c^{<t>}\)实际上是通过\(c^{<t-1>}\)计算出来的。那么公式\(a^{<t>} = g_1(w_{a} * [a^{<t-1>}, x^{<t>}] + b_a)\)变为\(\tilde{c}^{<t>} = tanh(w_{c} * [c^{<t-1>}, x^{<t>}] + b_c)\),这里的\(\tilde{c}^{<t>}\)是一个候选值——candidate value,类似于中间变量,而激活函数我们选择tanh。

GRU的核心是有一个Gate,就是上面说的是否更新值的功能,它的公式为\(\Gamma_u = \sigma(w_{u} * [c^{<t-1>}, x^{<t>}] + b_u)\)\(\Gamma_u\)的u的意思是update,sigmoid函数的输出范围在0-1之间,所以就完成了类似更新的功能。如果是0就代表不让你更新,如果是1就代表让你更新,这里听起来还有点绕,没关系看下面的表达式。

这时开始执行更新步骤:\(c^{<t>} = \Gamma_u * \tilde{c}^{<t>} + (1 - \Gamma_u) * c^{<t-1>}\),这一步可以看出如果\(\Gamma_u\)等于1就将\(c^{<t>}\)更新为\(\tilde{c}^{<t>}\),如果等于0就相当于不让你更新,结果还是上一个的c,即\(c^{<t-1>}\)

将公式写在一起,GRU的工作流程就是: \[ \begin{cases} \tilde{c}^{<t>} = tanh(w_{c} * [c^{<t-1>}, x^{<t>}] + b_c)\\ \Gamma_u = \sigma(w_{u} * [c^{<t-1>}, x^{<t>}] + b_u)\\ c^{<t>} = \Gamma_u * \tilde{c}^{<t>} + (1 - \Gamma_u) * c^{<t-1>}\\ \end{cases} \]

GRU完整版

可以看到下式中就多了一个\(\Gamma_r\),但是为什么不用上面的简化版呢?那是因为经研究者多年的尝试,发现下面的版本是很实用的,也算是一个标准版,你可以自己开发不同的版本。 \[ \begin{cases} \tilde{c}^{<t>} = tanh(w_{c} * [\Gamma_r * c^{<t-1>}, x^{<t>}] + b_c)\\ \Gamma_u = \sigma(w_{u} * [c^{<t-1>}, x^{<t>}] + b_u)\\ \Gamma_r = \sigma(w_{r} * [c^{<t-1>}, x^{<t>}] + b_r)\\ c^{<t>} = \Gamma_u * \tilde{c}^{<t>} + (1 - \Gamma_u) * c^{<t-1>}\\ \end{cases} \]

LSTM

吴恩达老师讲得感觉理解起来有点费劲,因为他觉得图片比文字更难理解,所以写了一大堆公式,只是再后面补充了图片。所以我建议看李宏毅老师的深度学习视频来理解LSTM。李宏毅老师的视频用了一张图片很好的解释了LSTM,并且他还举了一个例子,更加生动形象。

可能是东西方的差异,我感觉是图片好理解点,所以我选择看李宏毅老师的视频。这里就不写了,因为我在下面写了李宏毅老师课程的笔记

双向神经网络

深层神经网络


李宏毅机器学习 **********************************

字母表示

跟吴恩达老师讲的类似,李宏毅老师也讲了文字如何表示,与吴恩达老师不同的是,李宏毅老师多讲了几个。

最简单的方法利用向量来表示文字,就是上面说过的one-hot:

1 of N encoding 因为会出现某些单词没见到过,所以需要使用other这一维来表示。并且在右边的图中还可以使用字母来表示。然后理想上只要将词向量放入神经网络就会出现结果。但是Feedforward Network其实没办法解决这问题。

beyond 1-of-N encoding
beyond 1-of-N encoding

可以看到下图,由于Feedforward Network没有记忆,所以两个句子对它来说是一个意思,但是对人来说可以很明显判断出第一句话台北是目的地,第二句话台北是出发地。Feedforward Network它只能训练当前的词,前一个词是什么它并不知道。 Feedforward Network无法解决的问题

RNN

上面讲到Feedforward Network由于没有记忆,无法记住前一个或者前几个词,所以就诞生了RNN。RNN其实也没那么神秘,就是每次输入并交给激活函数计算完毕后,将计算结果存入缓存中,并且在下一次计算时,将缓存取出来一起计算(这里一起计算的意思是将 memory 也当做 input,也就是说下图的 RNN 有 4 个输入)。就是下图的蓝色方框,由于是第一次计算,其中初始化为0。下图第一遍已经在计算了,实际上已经准备更新蓝色方框中的值了。RNN在上面的章节中其实已经写过了,都是类似的。

注意一点,下图代表一个 RNN,那几个圆圈是一个神经细胞,而不代表一个 RNN。一个神经细胞中有一个权重向量,对比 simple NN 就能理解了。 一个RNN的小型例子

经过上面的例子发现,当前的输入已经在依赖前一个的缓存了,所以当顺序有所变化,或者前一个数据有所变化时,RNN可以察觉到,输出的结果也自然不同。

deep RNN

我一共写了两个RNN的笔记,无论是吴恩达老师的还是李宏毅老师的到目前为止,RNN其实都不是deep的,之前也在疑惑,RNN横轴有很多层,但是实际上那些层只是不同时间的输入,根本不算deep。今天继续看下去,发现这个问题终于有解了,RNN也可以是deep的。 deep RNN

Elman Network & Jordan Network

上面讲的RNN都被称为Elman Network。还有另一种辩题叫做Jordan Network,它将输出值缓存起来。传说之中Jordan Network可以有更好的性能。

为什么有更好的性能

Elman Network和ordan Network
Elman Network和ordan Network

双向RNN——Bidirectional RNN

RNN——Bidirectional RNN
RNN——Bidirectional RNN

长短期记忆——Long Short-term Memory(LSTM)

LSTM的神经元个数不同有什么区别?其他的NN架构也有同样的疑问

上面讲的memory实际上是最简单的,LSTM才是现在最常用的Memory。Menory在RNN中实际只是一个神经元而已,它负责输入和输出。它们之间的关联是:RNN依旧是RNN,只不过把RNN中的神经元换成了LSTM。我们知道神经元的逻辑其实很简单,只有输入——计算——输入到激活函数——输出激活值,而LSTM只不过麻烦一点罢了。

下图就是一个LSTM。Input Gate中如果f(z)是1就代表Gate打开,也就是f(z)*g(z) = 1 * g(z) = g(z),就相当于可以让外界输入。如果f(z)=0,Gate被关闭,那么 f(z)*g(z)=0,是不是就像不允许外界输入一样?因为你输入多少都被置为0。而Forget Gate也类似,当f(z)=1时,即Forget Gate被打开,这里与直觉有点相反,因为Gate打开,有点感觉像遗忘。但是其实c*f(z) = 1,所以Forget Gate为1其实是记住原本的c的意思。

另外图中也写到了,Gate的激活函数一般选sigmoid,里面的值就代表Gate的打开程度。 LSTM示例

LSTM的例子

例子介绍:只有一个LSTM,输入有3维,输出有1维。\(x_2 = 1\)\(x_1\)的值就会被存到Memory中,\(x_2 = -1\)则重置Memory,\(x_3 = 1\)则输出。

LSTM例子介绍 注:下图中的蓝色数字和灰色数字是权重值。

Q:权重值是初始化的?还是固定的?还是初始化后自己可以训练的?其实就是LSTM的反向传播算法要弄懂。

A:是初始化后自己可以训练的。2019 年 11 月 11 日回答

LSTM例子计算
LSTM例子计算
  1. Input Gate: 将偏差设为-10是因为我们通过x2来对Input Gate控制。平常x2=0,计算x*w+b=-10,那么通过sigmoid function就会得到一个接近于0的值,所以就实现了将Input Gate关闭的功能。而如果x2=1,那么x2*100=100,通过sigmoid function就会得到一个接近于1的值,Input Gate就实现了打开的功能。
  2. Forget Gate: 这里的功能跟Input Gate类似。
  3. Output Gate: 如果Output Gate被关闭,那么输出0.

多个LSTM工作场景

里面的\(x^t\)就是对应于NN中的一个向量,它分别乘上4个参数矩阵得到4个不同的向量,以此操控LSTM,而LSTM实际上就等于神经元,说白了就是一个类似激活函数的功能。

LSTM实际工作场景
LSTM实际工作场景2

多个LSTM连起来工作就是像下面一样,红线和红线旁边的那个黑色曲线链接的值之前没有讲过,但是下图的这样才是LSTM实际的长相,所以之前讲的那么复杂实际上还是LSTM的简化版。 LSTM实际工作流程

RNN反向传播

此处的笔记来源于 26: Recurrent Neural Network (Part II) 从 0 分开始。

BPTT——backpropagation through time,与 NN 的 backpropagation 类似,李宏毅老师也没讲原理直接跳过了。

然而不幸的是,RNN 的 training 是很困难的。下面蓝色的线是希望的结果,但是实际上是绿色的线,会出现剧烈地抖动,最后在某个点出现NAN。这就是类似梯度消失问题。可以使用一些办法解决,但是现在用得最多的方法是LSTM。

视频中花了很长的时间去讲解梯度爆炸和梯度消失的问题,但是我没有将它记录在这,详情可访问下面两个链接。

Q:如何防止出现如下剧烈抖动的 loss 曲线?

A:处理梯度爆炸的问题。

RNN trWaining碰到的问题

其他解决梯度消失的办法

其他解决梯度消失的办法
其他解决梯度消失的办法

seq2seq

Many to one

输入一个向量sequence,只输出一个向量。

  1. 语义分析。比如分析电影评论是好是坏。
  2. key term extraction。对文档提取关键词。
Many to One
Many to One

Many to many(Output is shorter)

输入和输出都是向量sequence,但是输出要短。

  1. Speech Recognition 。语音辨识。

Many to many(No limitation)

输入和输出都是序列且长短不一。被称为 Sequence to sequence learning

  1. Machine Translation. 机器翻译。
Many to Many(No Limitation)
Many to Many(No Limitation)

Beyond Sequence

  1. Syntactic parsing

autoencoder

26: Recurrent Neural Network (Part II),45.43 开始。


李宏毅深度学习


注意事项

此系列视频还有两个Review视频,分别为第一个视频:Basic Structures for Deep Learning Models(Part 1), 第二个视频:Basic Structures for Deep Learning Models(Part 2)。

个人认为Review视频不需要看,而且这两个视频时间贼长,加起来得有两个多小时。没必要浪费时间,即使你根本没学过Review中的知识点也不用去看。他的Review里不会讲很深,基本上就过过场,就算有很深的东西也完全不影响继续往下学。1P时长80分钟,说实话如果自己属于小白阶段,去看那么长的视频是挺打击人的兴趣的,如果是大佬或者已经入门的人当然看得津津有味了。 此文记录了李宏毅机器学习视频中讲解的RNN的笔记。

Computational Graph & Backpropagation

2019年6月7号更新:关于计算图这章,现在才发现原来很重要,因为这是完成自动求导的关键。学了 pytorch 之后才发现的。

什么是Computational Graph

这实际上跟要学的深度学习没什么关系,只是名字好听点,无视就好,如下图就是一个Computational Graph。主要用来在计算神经网络一些输出时,便于理解。 Computational Graph例子

在看一个比较贴近实际的例子,顺便复习一下链式求导法则。 Computational Graph链式求导法则示例

通过链式求导的例子理解反向传播(Backpropagation)算法

首先进行正向链式求导,如下图: 正向链式求导

图中要求计算e对a求偏导,首先给出a=3, b=2。其中c=a+b, d=b+1。

按照李宏毅老师使用链式求导法则,先要计算c对a求导得到1。e再对c求导得到b+1,带入b=2,得到3。所以3对a求偏导等于1*3=3。

上面这种链式求导法则有点乱,如果没仔细学过微积分可能难以理解。其实对于方程e = (a+b) * (b+1),e对a求偏导,直接看出来都可以。利用考研时的口诀“左导右不导,左不导右导”(也就是莱布尼茨公式),直接得到结果\(\frac{\partial e}{\partial a} = b+1\)

然后将b=2带入b+1得到结果还是3。

接着进行反向模式,如下图: 反向链式求导

现在图中要求计算\(\frac{\partial e}{\partial a}\)以及\(\frac{\partial e}{\partial b}\),当然你可以分别进行两次链式求导,得到结果。但是如果从e出发,也就是反向,那么就可以同时得到\(\frac{\partial e}{\partial a}\)以及\(\frac{\partial e}{\partial b}\)的结果。

不要在意e为什么等于1,只不过一个输入而已。 此外,如果阅读过《deep learning and neural network》一书,看过吴恩达机器学习视频或者其它资料的应该已经能反应出来。连接线上的求偏导实际上就跟神经网络上的权重一个意思,然后也是一层一层地反向传播。

这个输入e实际上就是神经网络中的反向传播算法中的输入。就是最后一层神经元的误差\(\delta^l = h-y\)。这里吴恩达老师和《deep learning and neural network》作者的最后一层误差公式不一样,目前不明,暂时不做解释,这里的公式是吴恩达老师的。

然后就是误差*权重+偏差得到前一层的误差,具体不展开。

反向传播的好处

如果你的root只有一个,那么这个Computational Graph中的所有偏微分就都可以一次性算出。对应于神经网络,我们就是要这样的效果。

参数共享(Parameter sharing)

略,看了一眼貌似挺简单。16:20

Computational Graph for Feedforword Net

李宏毅深度学习p3从21:16到52:48讲解梯度下降算法、前馈神经网络以及反向传播算法的具体数学原理 一直没看懂原理,以后再看。

Computational Graph for Recurrent Network

※ Deep Learning for Language Modeling

语言模型就是预测一个word sequence出现的几率有多大。 Language Modeling

N-gram

N-gram是自然语言处理中的算法。2-gram读作bi-gram。

传统做法

  • 怎么预测一句话出现的几率
  • 收集大量文本作为训练数据
    • 然后计算\(w_1\cdots w_n\)这句话在训练数据中出现的概率
  • N-gram语言模型:
    • 如何计算一小部分的概率?例如下图的p(beach|nice)出现的概率。就是将nice beach出现的次数除以nice出现的次数。

前两条是理想的处理办法,但是麻烦的是要预测的句子在语料库——corpus中八成一次都没出现过。于是就需要使用N-gram模型。它的处理办法就是将句子拆成比较小的部分——component,再把每个小部分的概率乘起来就是句子出现的几率。像下图这种只考虑前一个单词的模型叫做2-gram model。 N-gram

NN-based LM

怎么做基于NN的N-gram?做法如下:

  1. 搜集training数据
  2. learn一个Neural Network,通过两个词predict下一个词,如下图: NN-based LM
  3. 使用cross entropy minimize
  4. 有了Neural Network后算一个句子的几率,如下图: 计算句子的几率 其中STRAT是一个token,代表句子的起始。

RNN-based LM

往上翻循环神经网络——RNN,原理就是这个。 RNN-based LM

Challenge of N-gram

NN-based model

为什么要使用NN-based model。相较于传统方法有什么好处。就是概率估不准,因为永远没有足够的数据。 Challenge of N-gram

视频13:20~27:01仔细讲解了为什么要使用NN,而且把我困惑了快一个月的问题解决了,就是将文字转为数字之后进行训练的意义。

RNN-based model

为什么要使用RNN-based model。相较于传统方法有什么好处。

几个有用的network架构

Spatial Transformer Layer

论文地址,中文可以叫空间变换层。 此神经网络架构的出现的原因:CNN 对图片的缩放以及旋转无所谓(CNN is invariant to scaling androtation)。比如说在图片的局部地区中,一个人移动一点点距离,对 CNN 来说其实没什么多大区别。不过距离有点远的话,还是有点影响的。

Highway Network

先对前馈神经网络和 RNN 进行一下对比。

  1. Feedforward NN 不是每一步都有输入。
  2. Feedforward NN 每一层都有不同的参数。 Feedforward NN和RNN的对比

Highway Network 论文地址Highway Network 实战论文地址 Highway Network 的想法就是把 RNN 起来,把它当做前馈神经网络来用。 Highway Network 的改进版论文地址,这个就是残差神经网络

Grid LSTM

论文地址 太复杂了,估计以后也很难用到。。。

Recusive Network

Recursive Network 是 Recurrent Network 更 Generalize 的版本。Recurrent Network 是 Recursive Network 的一个特殊的例子,如果翻译成中文的话,实际上名字都一样。所以可以称之为递归式网络。以下是 RNN 和 Recursive Network 的对比图: Recursive Network示意图

在做 Recursive Network 之前,需要考虑输入的序列的结构。图中将 \(x_1\)\(x_2\) 一同输入进一个 function,但是其实可以不这么做,具体要怎么输入,取决于输入数据的结构。而由于 f 与 f 前后相接,所以在写代码时需要预先做好设计

举个具体的例子,要判断“not very good”包含什么情绪,可以先使用语法解析,将句子结构化,然后根据句子的语法结构来使用 Recursive Network 进行训练,如下图:

“very”的词向量和“good”的词向量一同放入 f 中训练,我们可以将得到的向量看做是“very good”的意思。 根据句子语法结构训练1

根据句子语法结构训练2
根据句子语法结构训练2

当然两个词向量不能是简单的相加,具体做法可以自行选择。最简单的做法可以参考下图的上半部分,而下图的下半部分被称为 Recursive Neural Tensor Network,总而言之就是一个很复杂的做法来解决两个词向量不仅仅是进行简单的拼接或者相加。 Recursive Neural Tensor Network

对于 f 还有其他的做法,如 Matrix-Vector Recursive Network,Tree LSTM 2015 等。具体就不记了,以后可以查 Recursive Network 相关论文。

Conditional Generation by RNN & Attention

注意本文讲的是 RNN 入门,而下面的部分也只是讲普通的 RNN Generation,甚至连 decoder 部分都没用。下图是生成文字,其实也可以生成图片、音频等,我就不一一截图了,第二张图将这些想法及其论文都汇总了。 一个简单的Generation

Generation汇总
Generation汇总

Conditional Generation

但是在真实的场景中,我们不仅仅是希望只生成随机的句子,我们更偏向于生成一些基于某些条件的句子,比如:当看见一张一个人正在跳舞的图片,我们希望电脑生成“A young girl is dancing”;当给予一个条件“Hello”时,我们希望电脑生成“Hello, nice to see you.”。

一个实际的例子,我们可以将一张图片输入进 CNN,从而产生一个向量,再把该向量输入进 decoder 部分,最后生成句子。如下图所示,其他类型的条件生成也类似。 Image Caption Generation

Attention

\(z_0\)\(h_1 h_2 h_3 h_4\) 分别做一次 match,至于 match 怎么计算可以看下图右边。

计算步骤可以参考下列公式: \[ \begin{align} h & = [h^1, h^2, h^3, h^4] \\ s & = h^T z \\ c^0 & = h^t s \\ \end{align} \]

attention score 的计算公式可以由自己设计,下图使用的是 \(h^T W z\),有兴趣的话,这里介绍了三个。 Attention机制

然后获得 \(a^1_0 a^2_0 a^3_0 a^4_0\),之后将它们输入 softmax 层(有实验发现其实不经过 softmax 层也可以,甚至效果更好),最后将所有 a 分别乘上它们对应的向量并且相加,得到一个向量 \(c^0\)Attention机制计算score

使用 Attention 机制计算完毕后,将向量 \(c^0\) 输入进 decoder 即可,接下来的计算都是以此类推。 Attention机制计算完毕后输入到decoder

Attention应用到Speach Recognition

Attention for Speach Recognition
Attention for Speach Recognition

Memory Network

论文地址视频地址43:00开始。 Memory Network 最先被用在 Reading Comprehension,说白了就是一个 Attention 机制。下图就是一个简易的 Memory Network

  1. 首先将 document 由多个句子组成,句子由 vector x 表示。具体如何表示的问题,可以由自定义解决,如 bag of word 或者由词向量表示;
  2. query 就是问题,也由 vector q 表示;
  3. 使用 q 对每个句子做 attention 得到 match score \(\alpha\),然后使用 \(\alpha\) 和 x 做 weighted sum;
  4. 最后将 weighted sum 后的 vector 和 vector q 都丢到 DNN 中,得到答案。

注:这是在做阅读理解。document -> vector 等于 input(I) 和 generalization(G),attention 等于 output(O),生成答案等于 response(R)。 一个简易的Memory Network

Memory Network 还有更复杂的版本,即 attention 的 vector 和抽取信息的 vector 并不需要是同一个,如下图所示。

  1. 将 document 表示为句子时,使用两组向量。一组用于计算 match score,一组用于 weighted sum。
  2. 其他的步骤都差不多,但是有一个地方不一样。在 weighted sum 得到一个 vector 之后,可以和 q 加在一起,得到一个新的 q,再重复 步骤 1。而且这个步骤可以做很多次,做完之后再输入进 DNN 获取答案。这个步骤被称之为 Hopping,注意从 document 抽取的两组 vector 在 hopping 的时候,可以是不一样的。 更复杂的memory network

Neural Turing Machine

Tips for Generation

这里听不太懂,跳过了。有 Beam Search 之类的。 对Generation的建议1

Pointer Network

论文地址

举一个简单的例子助于理解 Pointer Network。在二维坐标系中任意给出 4 个点,我们的目标是找到几个点,将它们连起来形成一个封闭圈,剩下的那几个点要正好在这个封闭圈之中,如下图: 助于理解Pointer Network的一个例子

当然,这肯定已经有一些算法可以解了,比如在坐标系中计算距离。但是今天我们使用硬 train 一发的方法,即不管三七二十一将它输入到神经网络里面训练。首先制造一些训练数据,然后给 encoder-deocder 训练。具体的训练步骤为:输入点的坐标,输出 one-hot 表示 {1,2,3,4,END} 的五维向量,碰到 END 则代表解码完毕。

但结果是网络训练不起来,因为在上述的例子中我们只输入了 4 个点,我们的目的是得到 1-4 个点。但是如果我们的测试数据是输入 400 个点呢?那么我们也只会得到 1-4 个点,因为 {1,2,3,4,END} 是预先定义好的。你可能会想那就多定义一点啊,但是下次我要是输入 4000 个点呢?要是 40000 个点呢?总有你无法预先定义的时候。

所以我们需要 Pointer Network动态的改变类别(具体做法详见下一小节),注意我这里直接说成类别了,我们可以把 decoder 部分看作是多元分类的工作,如输出 4000 个点,就是 4000 元分类。

上面的例子其实是 Pointer Netwoek 论文中的一个例子,但是对于这个例子来说,使用 Pointer Network 其实没多大意义,因为问题本身有更简单的解法,下面说一下有意义的用途。

Pointer Network 应用于 Summarization论文地址。给定一篇文档,让机器做出总结。对于此类问题,我们会碰到很多生僻的地名、人名等等字词。我们可以使用 Pointer Network 来解决这个问题。

下图就是做法,整张图的意思就是在做文本摘要的工作,输入一个句子,输出摘要。先不看中间的黄色圆圈 \(p_{gen}\),看看其他部分(红黄两部分)就是很普通的 encoder-decoder。但是对于这个 encoder-decoder 来说,词表中并没有 Aregentina 这个单词。那么我们就可以使用 Pointer Network,这个 \(p_{gen}\) 就是概率(具体描述见下一节)。最后结果就是我们将注意力关注到 Aregentina 这个单词。当然对于 encoder-decoder 这部分的工作也是要做的,我们可以将两个结果加起来,从而判断出最终要产生哪个单词,做法详见原论文。 Pointer Network for Summarization

还可用于 machine learningchatbot 等。

具体做法

具体的实现就是像下图一样,首先在输入的序列之前加入一个 END 序列,然后将 decoder 删掉。我们还是使用 Attention 机制计算每个序列的 attention score,但是这次的 score 不再乘上它对应的向量,而是直接当做向量输出,意思就是把所以的 score 做一次 max,最大的就输出 1。而停止条件就是 END 这个序列的 score 是最大的,即为 1 就停止训练。

这样的做法乍一看好像无法理解,我解释一下。由于 encoder 是对序列的长度不敏感的,也就是说如果预先定义的类别是 40 维,而我输入 400 个点,那么对于 encoder 来说,它可以增加神经元的数量从而使得 400 个点正好全部输入进 encoder。但是对于 decoder 来说,它输出只能是 40 维。那么 Pointer Network 的做法是将 decoder 删除,把输出的工作也交给 encoder 去做。所以我输入 400 个点,自然也就可以输出 400 维的类别(这里应该是 401 维,因为还有一个 END 序列)。看下图的 encoder,\(h^4\) 的分数是 0.7,所以我们的输出就是 4,当然对于向量来说就是 \((0, 0, 0, 0, 1)\)Pointer Network的做法

剩余的一些与NLP关系不大的视频

  1. 调参的技巧
  2. 训练的时候的 loss 曲线一般如下所示,过去人们常常以为这是卡在了 local minima(右上图),但是后来发现可能是卡在了 saddle point(右下)。有一个对为什么卡在 saddle point 而不是 local minima 的直观解释:local minima 要求你的所有 dimension 都必须往下凹的,可以看右上的图,二维的图展示了要想处于 local minima,必须两个维度在一个区间都是向下的才行。假设你有 1000 个维度,那么其实出现这种几率是很低的。 假设多数点的微分值是零的情况,通常这些点都是有些维度趋于向上,有些维度趋于向下,类似右下图。现在多数人比较相信我们训练数据时,其实是卡在了鞍点上。 但是 Ian GoodFellow(花书作者)曾经说过,可能这种说法也是不准确的。因为即使是鞍点,该位置的梯度也应该很低才对,但是他给出了一张图,证明训练至 loss 卡住之后,该处的梯度很大。 训练卡住了是因为?
  3. Brute-force Memorization:神经网络其实可能只是在暴力记忆?
  4. 知识蒸馏