普通视图

发现新文章,点击刷新页面。
昨天以前首页

论文笔记:Few-Shot Learning

2019年12月8日 08:00

相关概念

Few-Shot Learning 泛指从少量标注数据中学习的方法和场景,理想情况下,一个能进行 Few-Shot Learning 的模型,也能快速地应用到新领域上。Few-Shot Learning 是一种思想,并不指代某个具体的算法、模型,所以也并没有一个通用的、万能的模型,能仅仅使用少量的数据,就把一切的机器学习问题都解决掉,讨论 Few-Shot Learning 时,一般会聚焦到某些特定的问题上,比如 Few-Shot Learning 思想应用到分类问题上时就称之为 Few-Shot Classification。

本文涉及的论文都是 Few-Shot Classification 相关的。

在 Few-Shot Classification 里,问题是这样的:

  • 已有一定量的标注数据,数据中包含较多个类别,但每个类别的数据量不多,将这个数据称之为 train set
  • 用 train set 通过某种方法得到一个模型 \(M\)
  • 给定一个新的标注数据,有 \(N\) 个类,每类中有 \(k\) 个样本,称之为 support set,注意 support set 中的类别和 train set 的不存在交叉
  • 要求借助 support set 但不修改模型 M 的参数,使之在给定一个新的输入时,能将其识别为 \(N\) 个类中的一个

上述问题称之为 \(N\)-way \(k\)-shot 问题,\(k\) 一般较小(10 以下)。特别的,当 \(k=1\) 时称之为 one-shot,当 \(k=0\) 时称之为 zero-shot。

本文涉及的论文,都是在相同的框架下来解决这个问题的,具体来说,模型 \(M\) 具有两大块能力

  1. 将一个类别的数据表示为一个向量,作为这个类的 representation
  2. 将一个输入和一个类的 representation 进行比较,判断两者的匹配程度

第 1 点的存在,让其产生的模型能处理任意新的类别,不管你给什么样的 support set,它总能产生 \(N\) 个向量表示这 \(N\) 个类别。

最简单的办法是用 train set 进行表示学习,然后对给定的 suppor set,将每个类中的 k 个样本的向量加和或平均作为类的 representation,待预测的数据也编码成向量和,和这些类的 representation 计算相似或距离就好了。之后可以看到本文中涉及的论文,其方法就是这个简单想法的扩充和改进。

为了尽量和实际使用时接近,Few-Shot Classification 在训练时引入一个叫做 episode 的概念,每个 episode 包含从 train set 中采样出来的一部分数据,以及用这部分数据进行训练的过程:

  • 首先从 train set 的类别集合中随机选取 \(N\) 个类别
  • 然后,对每个类别,从 train set 中采样 \(k\) 个样本,这 \(N\times k\) 个样本,同样称之为 support set,用来模拟实际的 support set
  • 然后,对每个类别,在上一步采样后剩余的样本里,采样 \(N_{q}\) 个样本,这 \(N\times N_{q}\) 个样本,称之为 query set,用来模拟实际使用时的待预测输入
  • 模型 \(M\) 作用于 support set 上,得到 \(N\) 个向量 \(C = \{c_{1}, c_{2}, \ldots, c_{N}\}\)
  • 模型 \(M\) 作用于 query set 上,得到每个样本的向量,并和 \(c_{i}\) 计算距离,选择距离最小的作为预测类别
  • 根据 query set 上样本的预测结果与期望结果,得到损失,然后使用优化算法去调整模型 \(M\)

模型测试时通常会在一个和 train set 类别不交叉的标注数据集上进行,称这个数据集为 test set。测试过程同样以 episode 为基础,一般是采样若干个 episode 计算 query set 的预测精度,然后地多个 episode 的结果平均作为整体结果。

相关数据集

  • Omniglot: 一个手写字符数据集,包含 50 个类共 1623 个样本

    20191208_15571575791863screenshot.png

论文笔记

一、Matching Networks for One Shot Learning1

观点

  • 数据增强和正则化技术能减轻数据很少时的过拟合状况,但是不能算是解决了它。

    所谓的 Few-Shot Learning 也不能算解决啊,也没有什么方法敢说是解决了。

  • 非参数化模型(如最近邻方法)能直接用已有的数据作为先验来对新的数据进行分类,也能快速地适应新的类别,这种优点值得借鉴

    考虑分类这个问题,非参数化模型的问题在于它需要将先验数据存储下来进行比对,当数据量很大时会有效率问题;另外就是现有的一些非参数化模型,往往只能使用一些通用的度量方法,往往会有泛化性问题。Few-Shot Learning 某种程度上来说确实算是借鉴了非参数化模型的思路,用 support set 作为先验知识,然后在度量计算上使用可训练的模块。

  • 论文作者认为,如果实际处理的数据和训练用数据的分布相差较大时,论文中提出的模型可能会没啥效果

模型和方法

模型的大致结构是这样的

screenshot_20191103_22171572790637.png

Given Support Set \(S={(x_{i}, y_{i})}_{i=1}^{k}\) ——由于本文讨论的 One-shot 的,所以 support 里的每个样本的类别也就是 \(y_{i}\) 都是不同的。

任给 \(\hat{x}\),将它预测为 \(y_{i}\) 的概率为 \(P(y_{i}|\hat{x}, S)\),最大概率的预测也就是 \(\mathop{\arg\max}\limits_{y}P(y|\hat{x}, S)\)

Few-Shot Learning 要学习的,就是这个 \(P\)。

如果用一个向量来表示预测输出的话(这也是通常做法),用 one hot 向量来表示每一个 \(y_{i}\) 的话,那么模型就可以写成

\[\hat{y} = \sum_{i=1}^{k}a(\hat{x}, x_{i})y_{i}\qquad (x_{i}, y_{i})\in S\]

这里的 \(a\) 可以是一个 attention 模块,其定义为

\[a(\hat{x}, x_{i})=\frac{e^{c(f(\hat{x}), g(x_{i}))}}{\sum_{j=1}^{k}e^{c(f(\hat{x}), g(x_{j}))}}\]

其中 \(f\) 和 \(g\) 都是一个 embedding 函数,在不同的任务上会不一样,比如在 NLP 任务上可以是一个 word embedding,在图像任务上则可以是一个 CNN 网络层;匹配函数 \(c(\cdot, \cdot)\) 则用来计算两个 embedding 的相关度,它可以是任意一个距离度量方法或相似度量方法,也可以是一个神经网络模块。

在模型上,这篇论文针对 embedding 函数 \(f\) 和 \(g\) 做了一些特殊设计,文中称为 Full Context Embedding,简称 FCE ;而匹配函数 \(c(\cdot, \cdot)\) 则统一使用 cosine 方法。下面则来看看这个 Full Context Embedding。

所谓 Context 指的是 support set \(S\);进一步的,所谓的 Full Context Embedding,意思就是说,在计算 embedding 的时候,也要用到 \(S\),而不是简单将数据输入到一个网络模块中然后得到一个表示向量。这个思想和观点,怎么说呢,熟悉 attention 机制的话应该不会觉得陌生。

\(f\) 的 FCE 版本定义如下:

\[f(\hat{x}, S) = attLSTM(f^{\prime}(\hat{x}), g(S), K)\]

其中 \(f^{\prime}\) 是一个单纯的编码器,可以是任意一种合适的神经网络,其输入只有 \(\hat{x}\),\(g(S)\) 是所有 \(g(x_{i})\) 的简化表示。\(K\) 表示处理步骤数 —— 打个比方,就是hi把 \(f^{\prime}(\hat{x})\) 重复 \(K\) 次作为 LSTM 的 \(K\) 个 timestep 的输入,然后用这 \(K\) 个 timestep 的 hidden state 输出和 \(g(S)\) 做 attention 计算,具体来说,在第 \(k\) 步,有:

\[\hat{h_{k}},c_{k} = LSTM(f^{\prime}(\hat{x}), [h_{k-1},r_{k-1}], c_{k-1})\]

\[h_{k}=\hat{h_{k}} + f^{\prime}(\hat{x})\]

\[r_{k-1}=\sum_{i=1}^{|S|}a(h_{k-1}, g(x_{i}))g(x_{i})\]

\[a(h_{k-1}, g(x_{i}))=softmax(h_{k-1}^{T}g(x_{i}))\]

注意:上面的 \(k\) 与 support set 的大小 \(k\) 无关,仅仅是一个符号表示上的巧合。

除了对 \(f\) 做改进外,\(g\) 也有 FCE 版本,如下所示:

\[g(x_{i}) = \overrightarrow{h}_{i} + \overleftarrow{h}_{i} + g^{\prime}(x_{i})\]

\[\overrightarrow{h}_{i}, \overrightarrow{c}_{i} = LSTM(g^{\prime}(x_{i}), \overrightarrow{h}_{i-1}, \overrightarrow{c}_{i-1})\]

\[\overleftarrow{h}_{i}, \overleftarrow{c}_{i} = LSTM(g^{\prime}(x_{i}), \overleftarrow{h}_{i+1}, \overleftarrow{c}_{i+1})\]

其中 \(g^{\prime}\) 和 \(f^{\prime}\) 类似,剩下的部分也很好理解,相当于把 \(g^{\prime}(x_{0})\ldots g^{\prime}(x_{k})\) 当作序列作为一个 BiLSTM 的输入了。

实验和结论

使用了 Omniglot 数据集、ImageNet 数据集和 Penn Treebank 三个数据集分别去和其他模型做对比实验,大致上,在三个数据集上,都是先划分出类别完全不交叉的训练集和测试集然后进行训练和测试,对测试的细节没有说清楚,我所谓的没说清楚包括:

  • 前面的模型部分都是以 one-shot 为背景来说的,但实验中却有 5-shot 的情况,模型在 5-shot 的时候是怎样的?直接将每个类别的 embedding 平均?
  • Few-Shot Learning 的测试有别于普通的分类问题,一般来说是多次采样出 support set 和 query set 进行测试,然后对多次采样结果进行平均,那么,采样多少次,是否覆盖了测试集中的大部分数据?

在 Omniglot 数据集上实验时,对比用的模型有:

  • PIXELS: 直接用图像的像素值做匹配
  • BASELINE CLASSIFIER: 先在训练集上做训练,由于普通的分类器并不能识别测试集中未见过的类别,所以训练完后测试时,用模型最后一层的输出作为图像的特征向量,然后去进行匹配
  • MANN: 一篇关于 Meta Learning 论文中2提出的模型
  • CONVOLUTIONAL SIAMESE NET3
  • MATCHING NETS: 这篇论文提出的模型

下面是在 Omniglot 数据集上的对比结果:

screenshot_20191118_22501574088651.png

我的疑惑:

  • BASELINE CLASSIFIER 的第三个模式用的 Matching Fn 是 Softmax,是什么意思? softmax 函数不是用来做匹配或者相似计算的呀?
  • MATCHING NETS 的 embedding 部分具体用的什么结构,单层 CNN、多层 CNN 或者别的什么?

其中第三列的 Fine Tune 如果为 Y 则说明在测试时,会在测试集的 support set 上进行微调,然后再在 query set 上预测,下同。

由于 Omniglot 数据集比较简单,所以这里的 MATCHING NETS 没有使用 FCE。

在 ImageNet 上做实验时,由于 ImageNet 太大,不方便实验,所以实际上作者是从 ImageNet 中按每类 600 个样本采样了 100 个类得到了 60000 个样本组成的更小的数据集,作者称之为 miniImageNet。其中 80 个类用来做训练,剩下 20 个类用来做测试。

下面是在 miniImageNet 上的对比结果:

screenshot_20191118_22521574088764.png

我的疑惑:为什么这里就不用 MANN、CONVOLUTIONAL SIAMESE NETS 两个模型来对比了,是不是因为这两个模型的效果都比 MATCHING NETS 好,被打脸了?

除了在 miniImageNet 上做实验外,作者也尝试在完整的 ImageNet 上做实验,但做了一些特殊的设置

  • 从 ImageNet 中随机去除了 118 个类别作为训练集,称之为 randImageNet,测试时在那 118 个类别的数据上进行
  • 从 ImageNet 中去除狗狗相关的所有类别共 118 个,用剩下的作为训练集,称之为 dogsImageNet,测试时在 118 个狗狗类别的数据上进行

对比的模型除了 PIXELS,还有就是两个 INCEPTION 的分类器

  • INCEPTION CLASSIFIER: 在去除 118 个类别后的数据上训练,然后同 BASELINE CLASSIFIER 一样,在测试时只用模型来输出图像的向量表示,得到向量表示后用进行匹配
  • INCEPTION ORACLE: 用完整的 ImageNet 进行训练,测试时同 INCEPTION CLASSIFIER

同时,由于从头训练会比较慢,所以 MATCHING NETS 直接使用训练好的 INCEPTION CLASSIFIER 来计算图像特征,在这个基础上再去训练 MATCHING NETS。

结果如下:

screenshot_20191118_23541574092492.png

注:表中的 \(\neq L_{rand}\) 和 \(\neq L_{dogs}\)表示训练集,,\(L_{rand}\) 和 \(L_{dogs}\) 表示测试集。

从表中可以看到,MATCHING NETS 在 dogsImageNet 上表现是比较差的,对此作者的猜测是训练时只去除狗狗的类别,对数据的分布产生了较大的影响,导致训练集上的数据分布和测试集上的数据分布差异很大,从而表现出了较差的结果。说白了,领域迁移性、泛化性差呗……

最后用 Penn Treebank 做了一个类似语言模型的实验,大致是这样的:

  • 给定 support set,里面有 5 条句子,每个句子都被挖了一个空,并给出了这个空要填的词是什么
  • 给定一个被挖掉一个词的句子,预测要填入的词,是 support set 中哪个被挖掉的词

如下所示:

screenshot_20191119_00051574093146.png

有点像语言模型,有点像阅读理解,但又都不是,奇奇怪怪的。

和 LSTM 语言模型做的对比:LSTM 语言模型能达到 72.8% 的效果;本文的模型,在 one-shot 的时候效果为 32.4%,2-shot 的时候是 36.1%,3-shot 的时候是 38.2% —— 就比瞎猜的 20% 高一点点吧。

个人总结

首先,放到现在来讲,这个 MATCHING NETS 的结构在 NLP 领域是很常见的,就是一个常规的匹配模型啊,不同之处在于使用 FCE 的时候它会用整个 support set 去做 attention,但这也不是什么新奇的东西。当然了这篇论文是 2016 年的,那个时候无论是 attention 机制还是我刚说的匹配模型,可能都还是挺创新的吧。

然后就是论文在一些细节上有没说清楚的,这个我已经在前面用「我的疑惑」这样的字眼指出来了。

二、Prototypical Networks for Few-shot Learning4

观点

  • 虽然 Few-Shot Classification 很难,但人类具备很强的这种能力,甚至在某类事物只见过一次后就能在之后非常精准地辨认出来

    嗯,这些论文都这么说,但也没见有引用哪怕一篇认知科学方面的论文来说明这点 —— 成年人见过一次新事物就能辨认出来我是相信的,但婴儿从出生后到什么阶段才具备这种能力呢?我对这个还是比较好奇的,总之对这种抛出一个看起来大家都认可的但没什么实质内容的观点感到无趣。

  • Vinyals 提出的 Matching Networks 可以视作一个在 embedding 空间中的加权最近邻分类器

    FCE 是 Matching Networks 里比较精髓的一个设计,虽然整个模型要说是一个最近邻分类器也没错,但这么说未免有点简化过头,而且当前这篇论文提出的 Prototypical Networks,也可以说是一个 embedding 空间中的加权最近邻分类器啊……没有营养的话……

  • Vinyals 提出的 episode 的概念让训练结果更加可信、更具有泛化性了

    我不知道在 Vinyals 提出基于 episode 的训练流程之前,做 Few-Shot Learning 的学者们都是怎么做的,但毫无疑问的是这套流程已经在之后成为了一个标准了。

  • 在 Prototypical Networks 中,距离度量的选择至关重要,实验表明选择欧式距离比余弦距离好得多

    后面有一堆推论,不是很能看懂。

模型和方法

一个假设: 每个类别都能在空间中对应到一个类比为「原型」的点上,该类的其他数据的表示以这个点为中心分布。

20191125_22491574693389screenshot.png

如上图所示,模型要做的事情是将 support set 中的数据映射到一个 embedding 空间中,然后对同类数据的 embedding 平均作为原型的 embedding;同时在预测的时候将输入数据也映射到这个 embedding 空间中,计算与各个原型的距离后,选择距离最小的类别作为预测结果。也就是说,这个模型的重点是:

  • 训练一个 encoder
  • 选择合适的距离度量方法

下面是模型的具体说明。

给定一个 support set \(S=\{(x_{1}, y_{1}),\ldots, (x_{N},y_{N})\}\),其中 \(x_{i} \in \mathbb{R}^{D}\) 是一个 \(D\) 维的向量,而 \(y_{i}\in \{1,\ldots, K\}\) 表示 \(K\) 个类别中的一个。特别的,\(S\) 中所有第 \(k\) 类组成的子集记为 \(S_{k}\)。

然后,第 \(k\) 个类别的原型,就可以表示为:

\[c_{k} = \frac{1}{|S_{k}|}\sum\limits_{(x_{i},y_{i})\in S_{k}}f_{\phi}(x_{i})\]

其中 \(f_{\phi}: \mathbb{R}^{D}\rightarrow \mathbb{R}^{M}\) 就是要学习的 encoder。

预测的时候,选定一个距离函数 \(d: \mathbb{R}^{M}\times\mathbb{R}^{M} \rightarrow \left[0,+\infty\right)\),然后对给定的输入 \(x\) 按如下方式进行预测:

\[p_{\phi}(y=k|x)= \frac{exp(-d(f_{\phi}(x), c_{k}))}{\sum_{k^{\prime}}(-d(f_{\phi}(x), c_{k^{\prime}}))}\]

对应的,训练的目标就是最小化损失函数 \(J(\phi) = -\mathop{log}p_{\phi}(y=k|x)\)。

下面是训练过程的伪代码描述:

20191126_21421574775748screenshot.png

实验和结论

注:这篇论文还做了一部分 Zero-Shot Learning 的实验,这个和 Few-Shot Learning 的细节还是有一点区别,我这篇文章专注于 Few-Shot Learning,所以 zero-shot learning 部分的内容暂且忽略,有需要的可以自行阅读论文。

在 Omniglot、miniImageNet 两个数据集上进行了 Few-Shot Learning 的实验。

在 Omniglot 数据集上做实验时,对模型做如下设置:

  1. 使用一个四层的 CNN 作为 \(f_{\phi}\)
  2. 每层由 64 个 3x3 的卷积核组成,并使用 Batch Normalization,激活函数用 ReLU,最后来一个 2x2 的 max-pooling
  3. 使用 Adam 优化算法
  4. 学习率初始化为 \(10^{-3}\),每 2000 个 episodes 裁剪一半

实验结果如下表所示:

20191126_22041574777087screenshot.png

这个实验没有和其他模型做对比 —— 可能是各个算法在这个数据集上的效果都能比较轻松地达到 90% 多吧。

作者从这个实验得到这么两个结论:

  1. 训练和测试时 support set 中的类别数比测试时的多,会带来更好的效果,这个从上面的表可以很清晰的看出来:1-shot 时 60-way 的效果好于 20-way 好于 5-way,5-shot 时也是同样的。
  2. 训练和测试时每类中样本的数量一样会比较好 —— 这我倒看不出来,不知道是不是我理解的不对,原文是这样的

    We found that it is advantageous to match the training-shot with the test-shot.

在 miniImageNet 数据集上做实验时,因为要和 Matching Networks 做对比,所以采用了同样的数据集划分方式。模型设置仍然和前一个实验一样使用四层 CNN。

实验结果如下:

20191126_22171574777857screenshot.png

作者从实验中得到的结论是:

  • 使用欧式距离比余弦距离要好,这个结论不光是对 Prototypical Networks 而言,甚至在 Matching Networks 上也成立;
  • 进行 N-way k-shot 训练时,N 的值越大,总体上看来模型效果会越好,可能是因为训练时类别数多会导致模型能更好地学习到不同类别之间的细微差别,从而提高了泛化性

个人总结

作者在论文中不厌其烦地说自己的方法是 simple method,我觉得确实如此,其模型结构一目了然,非常直白。作者在文中有说到这么一句话:

We hypothesize this is because all of the required non-linearity can be learned within the embedding function.

也就是说,为什么简单的方法也会有效,很大的成分还是因为模型学到了一个很好的表示。那么,如果将 encoder 的部分替换成一个表示学习能力更强的模块,是不是就能做到更好的效果?我觉得是的。

三、Learning to Compare: Relation Network for Few-Shot Learning5

观点

  • 之前的一些工作将工作重心放在学习一个好的数据表示上,然后使用一个已有的度量方法来进行分类,如果距离度量(或相似度量)也通过训练得到,可以得到泛化性更好的模型
  • 一些 meta learning 的方法在用于 Few-Shot Learning 问题时,都需要在新的数据上进行 fine tuning 才能得到较好效果,而本文提出的模型可以无需更新模型参数就能用于新类别数据的预测

    这个观点可以分几方面来看

    1. 我这个模型哪怕不 fine tuning 也能表现不错
    2. 提到的那几个 meta learning 的模型不 fine tuning 效果就不行

    对于第一点,看后面的实验结论即可,不过第二点在这篇论文中就看不到什么有力的证据了。

  • 与本文工作最接近的是 Prototypical Networks 和 siamese networks

    其实我觉得和 Matching Networks 也很像啦。

模型和方法

Relation Network(RN) 模型由 embedding 模块 \(f_{\varphi}\) 和 relation 模块 \(g_{\phi}\) 组成,如下图所示:

20191127_22311574865070screenshot_20191101_11331572579231.png

对比一下这张图和 Matching Networks 的结构图,实在是太像了。

具体来说

  • embedding 模块使用 4 层 CNN,每层 64 个 3x3 的卷积核,使用 Batch Normalization,激活函数用 ReLU —— 基本上和 Matching Networks、Prototypical Networks 是一样的,不过稍有区别,就是这里的 embedding 模块的后两层 CNN 没有加 max pooling,这个是为了适配 relation 模块做的修改
  • relation 模块用来将 support sample 和 query sample 的 feature map 融合起来并计算匹配分数

    因此首先有一个拼接函数 \(C(\cdot, \cdot)\),这个函数将两个 feature map 在 depth 通道上拼接起来。

    拼接起来后再通过一个 \(g(\phi)\) 进行融合并计算分数。

    对一个 N-way 1-shot 的任务,在给一个 query sample \(x_{j}\) 的时候,会得到 N 个分数:

    \[r_{i,j} = g_{\phi}(C(f_{\varphi}(x_{i}), f_{\varphi}(x_{j})))\qquad i=1,2,\ldots, N\]

    如果是 N-way K-shot 任务且 \(k > 1\),则把 support set 中同一类的 embedding 简单加起来再输入到 relation 模块中 —— 这里我有一个疑问,同一类的多个样本的 embedding 加起来后不平均一下吗?

    此处的 \(g_{\phi}\) 是两层 CNN 再加上两层全连接层。

模型的总体结构如下图所示:

20191127_22521574866354screenshot.png

训练时使用 MSE 作为 loss。

完了,模型上可以说也是比较简单直白的。

实验和结论

和前两篇论文一样在 Omniglot 和 miniImageNet 两个数据集上做的实验。

统一的训练设置

  • 使用 Adam 优化算法
  • 初始学习率置为 0.001
  • 每 10 万个 episodes 后对学习率进行裁剪

在 Omniglot 数据集上实验时,有如下额外设置:

  • 训练时的数据采样设置
    • 5-way 1-shot 时,query set 中每个类别有 19 张图片
    • 5-way 5-shot 时,query set 中每个类别有 15 张图片
    • 20-way 1-shot 时,query set 中每个类别有 10 张图片
    • 20-way 5-shot 时,query set 中每个类别有 5 张图片
  • 测试时,从 test set 中采样了 1000 个 episodes,将这 1000 个 episodes 的测试结果平均作为整体测试结果

实验结果如下:

20191128_22571574953069screenshot.png

在 miniImageNet 数据集上实验时,有如下额外设置:

  • 训练时的数据采样设置
    • 5-way 1-shot 时,query set 中每个类别有 15 张图片
    • 5-way 5-shot 时,query set 中每个类别有 10 张图片
  • 测试时,对 600 个 episodes 的结果平均作为整体结果

实验结果如下:

20191128_23021574953320screenshot.png

此外还有 zero shot learning 的实验,略。

个人总结

从结果上来看,Relation Networks 的效果并没有显著好于 Matching Networks 和 Prototypical Networks,在 miniImageNet 上的 5-way 5-shot 设置时甚至还比 Prototypical Networks 差一些。不过我觉得这个思路是没有问题的。

以及,可以看到各个方法都能轻松在 Omniglot 数据集上达到 99% 的准确率,所以这个数据集其实已经没有什么参考意义了……倒是各个模型在 miniImageNet 上都还有很大的提升空间。

四、Induction Networks for Few-Shot Text Classification6

观点

  • 之前的一些工作在计算一个类别的 representation 的时候,只是简单的将这个类的 support sample 的 representation 加起来或平均一下,这样做可能会丢失一些重要的信息

    不对哦,Matching Networks 那里用的 FCE 已经是很接近 attention 的思路了,可不是将 support sample 的 representation 简单加起来或平均的。

  • 一个 Few-Shot Classification 的任务,如果有足够多的类别供训练,那么过拟合的风险会比较低。

    文中说这个的时候举了一个例子:假设一个数据集中有 159 个训练用类别,按 5-way 的设置来训练的话,也就是从 159 个类里选 5 个类用于训练,这样可以有 794747031 种不同的子任务。

    我在想啊,这个想法能不能借鉴到普通的匹配任务上。在如检索式问答一类的匹配任务上,通常是做二分类,也就是选两个样本,判断他们是否属于同一类。而当 \(N > 3\) 且 \(k>2\) 的时候,组合数 \(C^k_{N}\) 总是比 \(C^{2}_{N}\) 大,所以如果借鉴 Few-Shot Learning 里的 episode 的思想的话,就能得到更多的训练机会;其次,原来在匹配的时候,对于负样本之间(可能属于不同的问题簇)是不做区分的,区分一下的话感觉也会有好处。

模型和方法

20191207_16081575706118screenshot.png

上图是一个 3-way 2-shot 的模型结构示例图。

模型由 Encoder 模块、Induction 模块和 Relation 模块组成。 Encoder 模块是双向 LSTM,加上 self attention —— 给定输入文本 \(x=\left(w_{1}, w_{2}, \ldots, w_{T}\right)\),最终得到一个向量 \[e = \sum_{t=1}^{T}a_{t}\cdot h_{t}\]

其中 \(h_{t}\) 是一个 biLSTM 的 hidden state:

\[\overrightarrow{h_{t}} = \overrightarrow{LSTM}(w_{t}, h_{t-1})\]

\[\overleftarrow{h_{t}} = \overleftarrow{LSTM}(w_{t}, h_{t+1})\]

\[h_{t} = concatenate(\overrightarrow{h_{t}}, \overleftarrow{h_{t}})\]

而 \(a_{t}\) 是 attention 权重,所有 \(a_{t}\) 组成的向量 \(a\) 是这样计算出来的:

\[a = softmax(W_{a2} tanh(W_{a1} H))\]

其中 \(W_{a1} \in R^{d_{a}\times 2u}\),\(W_{a2}\in R^{d_{a}}\),\(H = \left[h_{1} \ldots h_{T}\right]\) 表示由所有 \(h_{t}\) 组成的矩阵, \(2u\) 是 \(h_{t}\) 的向量长度,\(d_{a}\) 是超参数。

Induction 模块要做的是将 \(C\times K\) 个 support sample 的向量表示 \(e\) 转换成 \(C\) 个类的向量表示 —— 之前的一些工作在这里大都是通过将一个类中的 support sample 的向量表示平均或相加得到。这篇论文创新的地方是,在 Induction 模块这里,用了胶囊网络里的动态路由机制7

胶囊网络我不是太懂,暂时也没太多兴趣和精力去看 Hinton 那篇论文。论文中列出了 Induction 模块的计算过程,还是比较清楚的,如下图所示:

20191208_16101575792611screenshot.png

可以理解为在计算类的向量表示的时候,对这个类中的 support sample 做了加权,然后不同的 support sample 的权重是靠这个动态路由机制来计算的,其中所需的一些参数(如 \(W_{s}\) 和 \(b_{s}\))也是在训练中得到的。

最后的 Relation 模块,用来比对 query sample 也就是待预测的样本和每个类的向量表示 \(c_{i}\),以判断这个 query sample 应该被分到哪个类上。

Relation 模块首先用一个张量网络8来建模两个向量(也就是 query sample 的向量和类向量 \(c_{i}\))的关系,然后再经过一个全连接层输出匹配分数:

\[v(c_{i}, e^{q}) = f(c_{i}^{T}M^{\left[1:h\right]}e^{q})\]

\[r_{iq} = sigmoid(W_{r}v(c_{i}, e^{q}) + b_{r})\]

训练时的目标函数则为:

\[L(S, Q) = \sum\limits_{i=1}^{C}\sum\limits_{q=1}^{n}(r_{iq} - \mathbf{1}(y_{q} == i))^{2}\]

训练时候使用的优化算法是 Adagrad9

实验和结论

在两个数据集上做了实验,一个是 Amazon Review Sentiment Classification 数据集(简称为 ARSC 数据集),另外一个是阿里巴巴整理出来的 Open Domain Intent Classification for Dialog System 数据集(简称为 ODIC 数据集)。

两个实验都和这些模型进行对比:Matching Networks, Prototypical Networks, Relation Networks, Graph Network10, SNAIL11, ROUBUSTTC-FSL12

ARSC 数据集是在不同的电商产品类型上的评论数据,每个数据会被标记为正向、负向和不确定三个类别,总共有 23 个产品类型,所以加起来有 69 个类别。在该数据上实验时,设置如下:

  • 将四个产品的 12 个类别作为 test set,剩下产品的 57 个类别作为 train set
  • 使用 300 维的 Glove embedding
  • LSTM 的 hidden state size 设置为 128
  • 计算 self attention 时的超参 \(d_{a}\) 设置为 64
  • 训练时 support set 为 2-way 5-shot

实验结果如下:

20191207_20121575720750screenshot.png

ODIC 数据集有 216 个类别,将其中 159 个类别共 195775 个样本作为 train set,剩下 57 个类别共 2279 个样本作为 test set。在该数据集上实验时,设置如下:

  • 使用 300 维的中文 word embedding
  • LSTM 的 hidden state size 和 self attention 的超参同 ARSC 数据集
  • 分别进行了 5-way 5-shot、5-way 10-shot、10-way 5-shot 和 10-way 10-shot 四种实验
  • 测试时,在 test set 上采样 600 个 episode 进行测试,然后将平均测试结果作为整体结果

在 ODIC 数据集上的实验结果如下所示:

20191207_20121575720767screenshot.png

除了两个对比实验外,论文还做了一些模型的分析。

第一个分析是尝试将 Induction Networks 中的部分模块替换为更简单的模块,看模型的最终效果变化,来衡量这个模块是否有效。结果如下:

20191207_20131575720787screenshot.png

上图中前三行是 Induction Networks 自己的结果,后面三行就是替换其中部分模块或的结果,其中:

  • 第四行表示将 Relation 模块替换为简单的余弦相似方法
  • 第五行表示将 Induction 模块替换为将类中向量加和作为 \(c_{i}\) 的方法
  • 最后一行表示用 Attention 替代 Induction 模块 —— 文中说是 self attention,应该是指在计算一个类内 support sample 的权重时,对该类内所有的 support sample 进行 attention 计算来得到权重

可以看到,直接使用 Attention 替代 Induction 模块,比 Induction 模块不做迭代要好一点点,但三步迭代后本文模型还是会好一些。

然后还有一些可视化的分析,都是在论证 Induction Networks 比其他模型要好,这里不一一列举了。

个人总结

论文声称的「其他方法都是将 support sample 的向量表示简单加起来或平均作为」其实并不是事实,早在 2016 年的时候提出 Matching Networks 的论文中所使用的 Full Context Embedding 即 FCE 的目标和这篇论文里的 Induction 模块就是一样的,无非是方法不同而已。

借鉴胶囊网络思路的那个 Induction 模块还有点意思。

ODIC 数据集好像没有开放出来,遗憾。

五、A Closer Look at Few-Shot Classification13

这篇 ICLR 2019 的论文旨在分析现有 Few-Shot Classification 方法的一些问题。

观点

  • Few-Shot Classification 虽然在最近取得了不少进展,但是各个方法由于复杂的模型设计和不同的实现细节,导致他们很难互相比较
  • Few-Shot Classification 所使用的 baseline 方法效果被显著地低估了
  • 目前的 Few-Shot Classification 模型评估,都是从同样的数据集中采样出来进行的,导致模型的领域迁移性较低

    我理解之所以这么说,是因为 Few-Shot Learning 本来宣称的一个目标就是能应付新的类别的数据,而如果这个所谓的「新的类别」必须是在同一个领域内的,那么其实会很受限制。

    这点在 Matching Networks 那篇论文里也有提到。

模型和方法

这篇论文的目标不是提出一个新的模型来在 Few-Shot Classification 问题上得到更好的效果,所以在模型部分,只是对一个简单的 baseline 模型做了增强得到了 baseline++ 模型,用来在之后和其他模型对比,以论证前面的「baseline 方法效果被显著低估了」这个观点。

20191208_10521575773575screenshot.png

如上图所示,baseline 模型由一个 feature extractor 或者说 encoder 和一个分类模块组成,作者之所以认为之前的一些工作里 baseline 模型效果被低估了,是因为之前的一些论文,在分类模块这里,用的是一个固定的、简单的距离函数(如余弦距离、欧式距离)。

作者在这里做了一些改动,包括:

  • baseline 模型的分类模块使用一个全连接层,训练好后,用于新类别数据时,会用新的 support set 进行 fine tuning (之前一些工作里是不做的)
  • baseline++ 在 baseline 模型的基础上对全连接层的计算做了简单的调整

    记权重矩阵 \(\mathbf{W} = [\mathbf{w}_{1}, \mathbf{w}_{2}, \ldots, \mathbf{w}_{c}]\),最终分类模块会输出一个长度为 \(c\) 的向量,表示预测为各个类别的概率,记为 \(\mathbf{s} = [s_{1}, s_{2}, \ldots, s_{c}]\)。

    在 baseline 模型里,是这么计算的:\(\mathbf{s} = softmax(\mathbf{W}^{T}f_{\theta}(x))\)

    而在 baseline 模型里,则是这么计算的:\(s_{i} = \frac{f_{\theta}(x)^{T}\mathbf{w}_{i}}{\parallel f_{\theta}(x) \parallel\parallel \mathbf{w}_{i} \parallel}\)

当然,作者在这里强调,这个 baseline++ 模型并不是他们的贡献,而是来自于 2018 年的一篇论文14

实验和结论

作者对比了 Matching Networks、Prototypical Networks、Relation Networks 和一个 meta learning 的方法 MAML 模型。

20191208_11271575775643screenshot.png

这个对比图还是挺直观的,对于理解不同模型之间的差异挺有帮助。

图上的 meta-training 和 meta-testing,其实就是指训练和测试。但是 Few-Shot Classification 的训练用数据和实际使用是要预测的类别原则上是不一样的,学到的更多的是区分不同类别的能力而不是区分某个指定类别的能力,这个还有监督分类问题是不太一样的,这里加个 meta 是和普通的分类模型的训练、测试进行区分,不用太在意。b

进行了三个实验:

  • 使用 miniImageNet 数据集进行一般的图像分类实验
  • 使用 CUB-200-2011 数据集(后面简称为 CUB 数据集)进行细粒度图像分类实验
  • 使用 miniImageNet 数据集训练,在 CUB 数据集上进行验证和测试,对比各个模型的跨领域适应能力

实验的通用设置为:

  • 分别进行 5-way 1-shot 和 5-way 5-shot 实验,训练时的 query set 每类 16 个样本
  • 对 1-shot 实验,训练 60000 个 episodes;对 5-shot 实验,训练 40000 个 episodes
  • 测试时,将 600 个 episodes 上测平均测试结果作为整体结果
  • baseline 模型和 baseline++ 模型训练时设置 batch size 为 16,训练 400 个 epochs
  • baseline 模型和 baseline++ 模型,在测试时,让 encoder 参数不变,用 support set 对分类模块进行 fine tuning,batch size 设置为 4 训练 100 次
  • 所有模型训练时都使用 Adam 优化算法,初始学习率设置为 0.001

由于作者自己重新实现了用于对比的几个模型,所以先和原作者论文中报告的结果做了对比,如下图所示:

20191208_11571575777421screenshot.png

可以看到作者实现的这几个模型和它们的原实现的效果相比,大部分是稍差一些,也有些表现更好的。

在 CUB 和 miniImageNet 两个数据集上做实验时,使用四层 CNN 作为 encoder,实验结果如下图所示:

20191208_11591575777591screenshot.png

可以看到,baseline++ 模型和这些 SOTA 的模型相比并没有很大的差距,甚至在一些情况下比 SOTA 的模型还要好。baseline++ 模型仅仅是对 baseline 模型的一个简单修改,所以作者说 baseline 模型的能力被显著地低估了。

接着,作者认为 encoder 是很重要的,所以尝试了将 encoder 替换成更深的网络,结果显示使用更深的网络后大部分模型的效果都有了显著的提升。

20191208_12071575778024screenshot.png

第三个实验 —— 也就是那个先在 miniImageNet 数据集上训练然后在 CUB 数据集上测试的实验,统一使用更深的 ResNet-18 作为 encoder,结果显示 baseline 模型的效果最好。

20191208_14091575785358screenshot.png

由于 CUB 数据集都是鸟类的数据,所以训练与测试时的领域差异性很小;miniImageNet 数据集包含了很多不同类别的物体,所以领域差异性比 CUB 要大;当训练使用 miniImageNet 数据集而测试时使用 CUB 时,领域差异性是最大的。同样使用 ResNet-18 作为 encoder 时,三个不同的 5-way 5-shot 实验对比结果也说明了目前这些 Few-Shot Classification 模型在领域迁移上的问题,如下图所示:

20191208_14131575785609screenshot.png

而之所以 baseline 模型会最好,有可能是因为 baseline 模型进行了 fine tuning,所以作者再进一步实验,在其他模型上也加上 fine tuning 操作,结果显示领域差异性大时,fine tuning 是很有必要的,如下图所示:

20191208_14151575785741screenshot.png

总结

Few-Shot Learning 这个概念最早是李飞飞提出来的15,不过早先的一些工作方法都比较复杂,除了上述我看的一些论文外,还有一些从 meta learning 的方向来做的。目前看来,Few-Shot Learning 特别是 Few-Shot Classification 的方法,主要都是在 2016 年 Matching Networks 提出的框架下使用越来越复杂的模型,比如还有一篇我没有通读的微软的论文16的做法就是使用复杂的 attention 模型,我相信 ELMo、BERT 等更强大的预训练模型也会逐步用到这个领域里。

回到这几篇论文,可以看到 Few-Shot Learning 应用到分类问题上时,能取得一定的成果,但也还是有一些问题或者限制的

  1. train set 中需要有足够多的类别,虽然每类的数据可以不多 —— 一定要认识清楚这点,不要以为 Few-Shot Learning 就真的只需要很少很少的数据就够了
  2. 领域迁移能力不够好 —— 当然这是目前几乎所有模型的问题,但 Few-Shot Learning 本来就想要去解决新类别的学习问题,希望未来能看到在这方面更多的一些讨论吧

我个人对于数据稀缺时该如何训练模型这个话题是很感兴趣的,除了 Few-Shot Learning,目前了解到的一些方法还有:数据增强、远程监督、多任务学习。从这几篇论文来看,表示学习也是很重要的一环,一个表示能力很强的预训练模型,也会很有帮助。

脚注:

1

Vinyals, Oriol, Charles Blundell, Timothy Lillicrap, Koray Kavukcuoglu, and Daan Wierstra. “Matching Networks for One Shot Learning.” ArXiv:1606.04080 {Cs, Stat}, June 13, 2016. http://arxiv.org/abs/1606.04080.

2

Santoro, Adam, et al. "Meta-learning with memory-augmented neural networks." International conference on machine learning. 2016.

3

Koch, Gregory, Richard Zemel, and Ruslan Salakhutdinov. "Siamese Neural Networks for One-Shot Image Recognition." In ICML Deep Learning Workshop, Vol. 2, 2015.

4

Snell, Jake, Kevin Swersky, and Richard S. Zemel. "Prototypical Networks for Few-shot Learning." ArXiv:1703.05175 {Cs, Stat}, March 15, 2017. http://arxiv.org/abs/1703.05175.

5

Sung, Flood, Yongxin Yang, Li Zhang, Tao Xiang, Philip H. S. Torr, and Timothy M. Hospedales. "Learning to Compare: Relation Network for Few-Shot Learning." ArXiv:1711.06025 {Cs}, March 27, 2018. http://arxiv.org/abs/1711.06025.

6

Geng, Ruiying, Binhua Li, Yongbin Li, Xiaodan Zhu, Ping Jian, and Jian Sun. "Induction Networks for Few-Shot Text Classification." ArXiv:1902.10482 {Cs}, September 29, 2019. http://arxiv.org/abs/1902.10482.

7

Sabour, Sara, Nicholas Frosst, and Geoffrey E. Hinton. "Dynamic Routing between Capsules." In Advances in Neural Information Processing Systems, 3856–3866, 2017.

8

Socher, Richard, Danqi Chen, Christopher D. Manning, and Andrew Ng. "Reasoning with Neural Tensor Networks for Knowledge Base Completion." In Advances in Neural Information Processing Systems, 926–934, 2013.

9

Duchi, John, Elad Hazan, and Yoram Singer. "Adaptive Subgradient Methods for Online Learning and Stochastic Optimization." Journal of Machine Learning Research 12, no. Jul (2011): 2121–2159.

10

Garcia, Victor, and Joan Bruna. "Few-Shot Learning with Graph Neural Networks." ArXiv:1711.04043 {Cs, Stat}, February 20, 2018. http://arxiv.org/abs/1711.04043.

11

Mishra, Nikhil, Mostafa Rohaninejad, Xi Chen, and Pieter Abbeel. "A Simple Neural Attentive Meta-Learner." ArXiv Preprint ArXiv:1707.03141, 2017.

12

Yu, Mo, Xiaoxiao Guo, Jinfeng Yi, Shiyu Chang, Saloni Potdar, Yu Cheng, Gerald Tesauro, Haoyu Wang, and Bowen Zhou. "Diverse Few-Shot Text Classification with Multiple Metrics." ArXiv:1805.07513 {Cs}, May 19, 2018. http://arxiv.org/abs/1805.07513.

13

Chen, Wei-Yu, Yen-Cheng Liu, Zsolt Kira, Yu-Chiang Frank Wang, and Jia-Bin Huang. "A Closer Look at Few-Shot Classification." ArXiv:1904.04232 {Cs}, April 8, 2019. http://arxiv.org/abs/1904.04232.

14

Gidaris, Spyros, and Nikos Komodakis. "Dynamic Few-Shot Visual Learning without Forgetting." In Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, 4367–4375, 2018.

15

Fei-Fei, Li, Rob Fergus, and Peitro Perona. "One-Shot Learning of Object Categories," 2006.

16

Sun, Shengli, Qingfeng Sun, Kevin Zhou, and Tengchao Lv. “Hierarchical Attention Prototypical Networks for Few-Shot Text Classification.” In Proceedings of the 2019 Conference on Empirical Methods in Natural Language Processing and the 9th International Joint Conference on Natural Language Processing (EMNLP-IJCNLP), 476–485, 2019.

论文笔记:Reading Wikipedia to Answer Open-Domain Questions

2019年8月7日 08:00

drqa.png

这篇论文实现了一个基于维基百科的开放域问答系统,其实可以归类到机器阅读理解里面。相应的,这篇论文有配套的开源代码(见 facebookresearch/DrQA),这个系统的工程部分还是蛮实在的。

论文地址:https://arxiv.org/abs/1704.00051

作者

一作是毕业于 Stanford 的陈丹琦,目前在 Facebook 人工智能研究院(Facebook AI Research, FAIR) 。陈丹琦在机器阅读理解方面做了不少工作,博士论文《Neural Reading Comprehension and Beyond》我计划之后也读一下。

其他参与者是 FAIR 的几个人:Adam Fisch, Jason Weston, Antoine Bordes。

相关工作

  • Ryu et al. 2014: They combine article content with multiple other answe matching modules based on different types of semi-structured knowledge such as infoboxes, arfticle structures, category structure and definitions
  • Ahn et al. 2004: combine Wikipedia as a text resource with other resources, in this case with information retrieval over other documents
  • Sun et al. 2015, QuASE: full pipeline QA approche
  • Microsoft's AskMSR, 2002
  • IBM's DeepQA, 2010: DeepQA is a very sophisticated system that relies on both unstructured information including text documents as well as structured data such as KBs, databases and ontologies to generate candidate answers or vote over evidence.
  • YodaQA, 2015

术语

  • Open-Domain Question Answering

    Open-domain QA was originally defined as finding answers in collections of unstructured documents, following the setting of the annual TREC competitions

  • Machine Reading at Scale, MRS

    In order to answer any question, one must first retrieve the few relevant articles among more than 5 million items, and then scan them carefully to identify the answer. We term this setting, machine reading at scale.

  • MurMurHash 算法

    我去了解了下,MurMurHash 算法是一种被广泛应用的速度快且碰撞少的哈希算法,sklearn 里的 HashingVectorizer、Redis 中都用到了这个算法。

观点

  • Freebase/DBPedia 等知识库对计算机友好,但是过于稀疏,直接用于 open-domain question answering 并不好

    标了一个来源: Miller et al., 2016

  • 如 IBM DeepQA(Ferrucci et al., 2010) 的大规模 QA 系统,依赖多个不同的数据源之间的信息冗余来提高回答效果,比如说 Wikipedia、知识库、词典、大量的新闻文章、书籍……

数据集

论文中提到的数据集

论文中使用的数据集

  • 2016 年 12 月 21 日导出的英文维基百科数据,包含 5075182 篇文章
  • SQuAD 数据集,这个是机器阅读理解领域最有名的数据集了
  • CuratedTREC 数据集,包含 2180 个问题
  • WebQuestions 数据集
  • WikiMovies 数据集,包含了 96000 对电影领域的问答数据

SQuAD 数据集是标准的阅读理解数据集,每一个样本都包含一个 (Q, A) 对,以及关联的维基百科文章中的段落,但 CuratedTREC、WebQuestions 和 WikiMovies 三个数据集只有单纯的 (Q, A) 对,所以论文中需要为后面这三个数据集补充上关联的文章段落数据才能用于训练。

具体来说,是通过远程监督的手段来为 CuratedTREC/WebQuestions/WikiMovies 三个数据集补充相关的文章段落数据的,过程如下:

  • 从数据集中选择一个 (Q, A) 对
  • 用问题文本也就是 Q 作为 query,检索 5 篇维基百科的文章
  • 将这 5 篇百科文章切分成段落,并按下面的步骤去除无关的内容
    • 若段落中不能找到 A,则去除
    • 去除字数在 25 以下或 1000 以上的段落
    • 如果 Q 中有命名实体(如人名、地名等),而某个段落中没有这个命名实体,则去除这个段落
  • 如果没有剩余可用段落,那么丢弃这个样本,如果有则进行排序:取段落中匹配到 A 的位置,以其为中心取一个 20 个词的窗口,计算其与 Q 的重叠程度(我理解是 LCS 之类的),选择最匹配的 5 个段落

模型和方法

模型分为两大块,首先有一个 Document Retriever,对给定的问题 question,从所有维基百科文章中检索;检索到文章后切分成段落,然后用一个称之为 Document Reader 的模块在段落中预测答案位置并给出分数。后者其实就是标准的阅读理解模型了,完全可以替换成其他的机器阅读理解模型。

总体上来说这个模型不复杂,或者说非常的简单直观……所以我就不在这里写那些模型的计算式了,需要的话可以自行查阅论文。

Document Retriever 部分,我去阅读了对应开源项目的代码,有两种方法

  • TfidfDocRanker: 将 question 用 TFIDF+BOW 编码成向量,然后和文档的向量表示做內积进行排序

    看代码的话是直接和所有文档的向量组成的矩阵相乘的,维基百科的所有文章数量是很多的,我只能说像我这种穷惯了的人没法想象吧……

  • ElasticDocRanker: 这个直接就是把所有维基百科的文章存到 ES 上,然后用 ES 的 search 接口进行检索,用检索结果里 _score 来排序

可以看到,所谓的 DocumentRetriever 的部分真的是超简单的……

DocumentReader 是一个标准的机器阅读理解模型了,输入一个段落和一个 Q,从段落中预测一个 start 位置和一个 end 位置,取两个位置中间的文本作为答案输出。其中 start 和 end 的计算都是一样的,用 question 的向量表示和段落的向量表示做 attention 得到每个位置的 score。对任意一个 (start, end) 区间,取两者 score 的乘积作为这个区间的 score 并用之来排序,要求区间长度不超过 15。

由于使用的数据有一部分是靠远程监督产生出来的,必然会存在噪音,所以这篇论文在训练阶段做了一些特殊处理,具体来说做了两个尝试:

  • 用 SQuAD 数据先训练出一个模型,然后在远程监督的数据上进行 finetuning
  • 或者,SQuAD 数据和远程监督数据一起作为训练数据,进行多任务学习,也就是分开训练,但是共享部分参数

实验和结论

  • 单独在 SQuAD 上做训练,本文的 Document Reader 模型,在 dev 集上效果要好于 R-net 等一干模型,在 test 集上基本与 R-net 持平而好于剩下的模型,如下表所示:

    drqa_eval.png

    注: 这个表的结果是指不做文档检索、已经给定文章段落时预测答案的模型效果,而下文那个表则是包含文档检索、答案预测的整体效果。

  • 加远程监督数据,无论是 finetunning 还是多任务学习,效果都有显著提升,如下表所示

    drqa_eval2.png

    虽然说在 WikiMovies 数据集上有多达 12 个百分点的提升,不过 top1 的精度最好也就 30% 多……

个人总结

  • 这是一篇工程向的论文,在模型结构上并没有什么亮点
  • 远程监督制造大量带噪声的数据,然后以多任务学习或者 finetuning 的形式参与模型训练,这个其实是蛮有用的一个经验,我个人也在多个领域看到不少这种做法,对于深度学习这种对数据太过贪婪的方法,这也是被逼出来的方法吧
  • DocumentRetriever 部分做得太简单了,我认为这是最终系统整体效果才 30% 多的一个较大原因,顺便吐槽一下,加个 ES 检索就说自己是「Machine Reading at Scale」了……

论文笔记: ReDecode Framework for Iterative Improvement in Paraphrase Generation

2018年11月24日 08:00

作者

  • Milan Aggarwal
  • Nupur Kumari
  • Ayush Bansal
  • Balaji Krishnamurthyk

观点与事实

观点

  • 当前的一些序列生成方法都是一锤子买卖,在生成的时候改正错误的能力太差,生成之后也不能去改进生成质量
  • 人类对话中用复杂的复述来表达相同意图的行为很普遍,但对机器来说要辨别或生成复述句是很困难的
  • VAE 学习的是一个概率分布,所以用于生成式的任务很合适

相关工作

  • 1983. Paraphrasing questions using givenand new information
  • 2003. Learning to paraphrase: anunsupervised approach using multiple-sequence alignment
  • 2004. Monolingualmachine translation for paraphrase generation. Associationfor Computational Linguistics.
  • 2004. Synonymous para-phrasing using wordnet and internet
  • 2016. Neural paraphrase genera-tion with stacked residual lstm networks
  • 2017. Learn-ing paraphrastic sentence embeddings from back-translatedbitext
  • 2017. Joint copying andrestricted generation for paraphrase
  • 2017. Learning to paraphrase for question answering
  • 2017. Adeep generative framework for paraphrase generation
  • 2018. Adversarial example generation with syn-tactically controlled paraphrase networks

数据集

论文里用到了两个数据集。

第一个是 Quora 的相似问题数据集,这个没啥好说的。

第二个是微软的 MSCOCO 数据集,这个其实是一个 Image Caption 的数据集,但是因为这个数据集里每张图片都标注了五个标题,可以认为这个五个标题互为复述句。

模型/方法/结论

模型结构如下图所示

redecode.png

模型又三部分组成,分别是

  • Sampling Encoder

    这个 Encoder 用来根据输入句子 \(x_{o}\) 产生一个概率分布,然后从 x 中进行采样得到一个 latent vector \(z\)

    在这篇论文里,Sampling Encoder 由一个单层的 LSTM 和两个全连接层构成。

    首先输入句子 \(x_{o}\) 经过 LSTM 后得到整个句子的向量表示 \(v_{o}\),然后分别输入一个全连接层,得到期望概率分布的均值 \(\mu\) 和方差 \(\sum\),用这个均值和方差就得到了一个正态分布 \(N(\mu, \sum)\),从这个概率分布中采样得到最终需要的 \(z\)。

    为了让最终模型的输出能更有多样性,在训练的时候,作者对 \(\mu\) 和 \(\sum\) 施加高斯噪声扰动来得到不同的 \(z\)。

  • Sentence Encoder

    这个 Encoder 用来对得到输入句子 x 的语义表示,用来作为 decoder 的语义输入。

    这个 Encoder 就比较简单了,一个两层的 LSTM,输出 hidden state 序列 \(H={h_{1}^{o}, h_{2}^{o}...h_{n}^{o}}\),并在 Decoder 中对其进行 attention 计算。

  • Sequencd Decoders

    这个是最终用来生成的复述句的,也是整篇论文中的要点。

    和常规的一个 decoder 做法不一样的是,作者认为生成的部分,应该用多个 decoder 来迭代地生成结果。具体来说,这篇论文的做法是这样的

    • 第一个 decoder 以 Sentence Encoder 输出的 hidden state 向量 \(H\) 和 Sampling Encoder 输出的 latent vector \(z\) 作为输入

      \(p_{1}=Dec_{\phi_{1}}(z, Attn(H))\)

    • 随后的 decoder 则以前一个 decoder 的输出和 Sampling Encoder 输出的 \(z\) 作为输入

      \(p_{i}=Dec_{\phi_{i}}(z, Attn({s_{Dec_{\phi_{i-1}}}}))\)

下图是该模型在两个数据集上的效果。 redecode_eval.png

以及一些输出示例

redecode_examples.png

总结

总的来说,模型并不算很复杂,也很简单易懂。

VAE 的部分我其实并不太熟悉,需要去补课。至于 Decoder 的部分,这种迭代生成的路子,其实早就见过了,比如说 HMN 模型,以及一些在 attention 机制上的改进工作之类的。

论文笔记:Word Embedding based Edit Distance

2018年11月17日 08:00

作者

Yilin Niu, Chao Qiao, Hang Li, and Minlie Huang

看这篇论文才知道李航去今日头条 AI 实验室了……

观点

  • 近年来深度学习被广泛应用到文本相似计算中,但这些方法一般都需要在标注数据上做有监督训练,而标注数据的收集、构建的代码是很高的
  • 在特定条件下,WED 可以退化为编辑距离或者 jaccard 系数的变种

    One can easily verify that WED degenerates to ED and a variant of Jaccard under certain condition.
    

    不过论文并没有细说这些方法之间是怎么转换的。

数据集

  • Quora: The dataset released from Quora contains 400k question pairs in community questionanswering labeled as matched or not-matched, 40w 样本
  • MSRP: The dataset for paraphrase detection released from Microsoft Research, 5.8k 句子对
  • CPC: The dataset referred to as Crowdsourced Paraphrase Collection (CPC), 2.6k 对复述句子

模型/方法/结论

记 \(S_{A}=(w_{A}^{1}, w_{A}^{2}, ..., w_{A}^{l_{A}})\) 和 \(S_{B}=(w_{B}^{1}, w_{B}^{2}, ..., w_{B}^{l_{B}})\) 为两个句子,其中 \(w_{A}^{i}\) 表示句子 \(S_{A}\) 中第 \(i\) 个词,\(w_{B}^{j}\) 表示句子 \(S_{B}\) 中第 \(j\) 个词。

在计算编辑距离时,以 \(S_{A}\) 为基准句子,计算 \(S_{B}\) 相对 \(S_{A}\) 的编辑距离,即认为 \(S_{B}\) 是 \(S_{A}\) 经过删除、插入、替换后得到的新的句子。

然后定义 \(c_{i,j}\) 为 \((w_{A}^{1}, w_{A}^{2}, ..., w_{A}^{i})\) 经过编辑变换成 \((w_{B}^{1}, w_{B}^{2}, ..., w_{B}^{j})\) 所需的最小操作代价。

那么就有

\[\begin{eqnarray}c_{i,j} = \begin{cases} c_{i,j-1}+i(w_{B}^{j}) \\ c_{i-1,j}+d(w_{A}^{i})\\ c_{i-1,j-1}+s(w_{A}^{i}, w_{B}^{j}) \end{cases}\end{eqnarray}\]

其中 \(i(w_{B}^{j})\) 表示插入 \(w_{B}^{j}\) 的代价;\(d(w_{A}^{i})\) 表示删除 \(w_{A}^{i}\) 的代码。这两者在传统的编辑距离里往往值是 1,在生物信息学的序列对比上,删除的代码可能会再适当地高一点。

\(s(w_{A}^{i}, w_{B}^{j})\) 则表示将 \(w_{A}^{i}\) 替换为 \(w_{B}^{j}\) 的代价,当两者想等时代码就是 0 ,当两者不相等时这个代价就要大于 0。替换操作在不同的地方使用的代价会不一样,有些地方当作和插入、删除一样,即 1,本文的方法则是将替换视作先删除后插入两次操作,所以代价是 2。

所谓的结合 embedding,就是对这三个代价函数做了修改,使之利用词的 embedding 信息。

对插入操作,定义代价为:

\[i(w_{B}^{j})=1 - \lambda \cdot max_{w_{A}^{k}\in S_{A}, w_{A}^{k} \neq w_{A}^{i}} (sim(w_{A}^{k}, w_{B}^{j}) + \mu)\]

对删除操作,定义代价为:

\[d(w_{A}^{i})=1 - \lambda \cdot max_{w_{B}^{k}\in S_{B}, w_{B}^{k} \neq w_{B}^{j}} (sim(w_{A}^{i}, w_{B}^{k}) + \mu)\]

对替换操作,定义代价为:

\[s(w_{A}^{i}, w_{B}^{j})=2 - 2 sim(w_{A}^{i}, w_{B}^{j})\]

第三个替换操作的定义很好理解,但是前面两个感觉有点疑惑呀……

能想到的解释是这样的

  • 当插入新的词时,如果词和句子 A 中的词有相似的,那么对语义的影响就不是那么大
  • 当删除句子 A 中的词时,如果句子 B 中还有词和这个删除的词相似,那么对语义的影响也不会那么大

但是 LCS 的好处在于每个词是一一对应的,而这里在计算插入和删除的时候并没有保证有序的一一对应关系呀。

和其他方法的对比

  • ED: 经典编辑距离
  • TF-IDF: 基于 tfidf 的 cosine 相似
  • Embedding: 基于 embedding 的 cosine 相似
  • Jaccard: jaccard 系数
  • Autoencoder: 这个应该是指语言模型一类的,但是为什么引用的是 Hinton 2006 年的论文……
  • BOW+MLP: 有监督方法,给定两个句子,先将句子中的 embedding 加和,然后拼接起来输入到 MLP

    分两种情况:一层 MLP 和三层 MLP

  • LSTM+MLP: 用 LSTM 编码句子,然后拼接起来输入到 MLP

    分两种情况:一层 MLP 和三层 MLP

对比结果如下图所示:

wed_results.png

结论:

  • 本文的方法要优于这几种无监督方法:编辑距离、基于TFIDF的 cosine、基于 word embedding 的 cosine 方法、jaccard 系数
  • 对比两个有监督方法(BOW/LSTM+3层MLP),发现大概在 30k 这个数据量以上,有监督的方法才会显著好于 WED 方法
  • 在 Quora 数据集上做了一些采样来分析错误,发现超过 50% 的错误在于没有区分开关键词和非关键词,所以如果有机制能给关键词更高的权重,那么 WED 应该还会有更高的效果 —— 比如说用 TF/IDF 来给每个词加权?

    嗯,没有给一些例子呀……

相关工作

  • Learning semantic representations using convolutional neural networks for web search, 2014
  • Convolutional neural network architectures for matching natural language sentences, 2014
  • Text matching as image recognition, 2016
  • A deep architecture for semantic matching with multiple positional sentence representations, 2016
  • A decomposable attention model for natural language inference, 2016
  • 编辑距离:Navarro 2001, Jurafsky and Martin 2009
  • jaccard 系数:Tan et al 2005
  • 基于 word embedding 的 cosine 相似:Mitchell and Lapata 2008, Milajevs et al 2014
  • 基于 TFIDF 的 cosine 相似:Buckley, 1988

概念和术语

  • WED: Word Embedding based Edit Distance 缩写(不应该是 WEED 吗)

论文笔记:Visualizing and understanding recurrent networks

2018年11月10日 08:00

作者

  • Andrej Karpathy
  • Justin Johnson
  • Li Fei-Fei

观点

  • LSTM 在实践中表现出了非常好的结果,但我们对其性能的来源和限制理解地都还很不够
  • 过去的一些分析都是靠最终测试集上的全局困惑度来评价 LSTM 的效果,并没有在「真实数据」上进行分析,也不够直观

数据集

  • 托尔斯泰的《战争与和平》文本,共 3,258,246 字
  • Linux 内核代码,共 6,206,996 字

模型/实验/结论

模型:

  • RNN,分别尝试层数为 1 层、2 层和 3 层,隐层大小分别尝试 64, 128, 256 和 512,共 12 个模型
  • LSTM,同 RNN
  • GRU,同 RNN

实验和结论

  • 用上述模型在两个数据集上训练语言模型,最后在测试集上计算交叉熵误差,对比三类共 36 个模型之间的结果
  • 对 LSTM/GRU 的 gate 输出分布做可视化分析。如下图所示,图中的小圆圈代表一个神经元,横轴表示该神经元 gate 值超过 0.9 的比例,纵轴是 gate 值小于 0.1 的比例

    lstm_gru_saturation.png

    其中

    • forget gate 值超过 0.9 的比例很大的神经元,说明它能一直记住比较早之前的信息
    • input gate 值超过 0.9 的比例很大的神经元,说明它对当前输入比较敏感
    • output gate 的值超过 0.9 的比例很大的神经元,没什么意义,单纯的控制大小

    对 LSTM 而言

    • 第一层只有很少一部分 gate 值超过 0.9 或小于 0.1,其比例比较密集地分布在 0 附近,说明大部分都是在 0.1 到 0.9 之间
    • 有一些神经元 forget gate 值超过 0.9 的比例超级大,也就是说它一直都超过 0.9,一直记着很早以前的东西
    • 有一些神经元 forget gate 的值小于 0.1 的比例很大,但没有一直都小于 0.1 的,比例最大在 0.8 左右
    • input gate 倾向于超过 0.9(相对比例大的神经元更多), output gate 的值分布比较均匀

    对 GRU 来说

    • 第一层的 update gate 普遍比较大而 reset gate 普遍比较小,注意本文中的 update gate 相当于 LSTM 中的 input gate
    • 高层不像第一层那么极端,但总体形式差不多,就是 update gate 大而 reset gate 小这样
  • 分析了 LSTM 在《战争与和平》文本上的错误类型

    • ngram 错误,1-9 阶 ngram 模型能预测正确但 LSTM 预测失败的部分
    • 罕见词错误: 既由词频不大于 5 的词导致的错误。这部分错误通过扩充数据集和 pretraining 是可以得到缓解的
    • 词建模错误: 在遇到空格、换行、引号等词分隔符后,预测错了下一个词的第一个字符,和前面个动态长期记忆错误不一样的是,这个相当于在前面的词的基础上要选择一个词,而前面那个相当于是已经知道是什么词了,但是要补全它,这两者的信息量是完全不一样的
    • 标点符号预测错
    • 最后剩余的错误称为 boost error,

结论

  • 多个隐藏层模型比单个隐藏层模型的效果要好
  • LSTM 和 GRU 之间难分伯仲,但都显著好于 RNN
  • LSTM 表现出了对长程结构的记忆能力,如在处理被引号括起来的长文本时,对开头和结尾的引号有特殊的响应
  • 在多层的 LSTM/GRU 中,高层的神经元都开始分化,会有一部分倾向于接收新信息,有一部分则倾向于记住旧的信息
  • GRU 的第一层几乎不怎么使用旧的信息,即使到高层后也更倾向于使用当前输入
  • LSTM 建模长程依赖的能力大大超过 ngram 模型,一个 11MB 的 LSTM 模型效果能略微超过一个 3GB 的 20-gram 模型
  • 对本身明显包含结构的文本(如内核代码)进行建模,当序列长度在 10 以下时,LSTM 和 20-gram 模型的差异不大,但随着序列变长,两者之间的差距逐渐变大,在 Linux 内核代码上,LSTM 能记忆的最大约 70 左右的距离
  • LSTM 在迭代训练的过程中,首先建模了短程依赖,然后在此基础上逐渐地学习到对长程依赖的建模能力,这也是 seq2seq 论文中提到的逆序源语言序列有效的原因,因为这让模型先开始建模短程依赖再去建模长程依赖
  • LSTM 并不能完全利用好最近的一些输入,LSTM 的错误中,有 42% 是 0-9 阶的 ngram 模型能够正确预测的
  • 相信像 Memory Networks 那样,如果能直接对序列中最近的历史进行 attention 操作,那么能够提高 RNNLM 的效果
  • 增大数据集、无监督预训练能提高 LSTM LM 对罕见词的效果
  • 增大模型大小,显著减小了 ngram 错误,但对其他类型的错误却没有明显的改善,这说明光是增大模型大小是不够的,可能需要设计更好、更新的结构

论文笔记:Visualization Analysis for Recurrent Networks

2018年11月10日 08:00

简介

这篇论文是清华大学语音和语言技术中心(Center for Speech and Language Technologies, CSLT)2016 年的一篇论文,不过奇怪的是在 Google Scholar 上找了一圈,给出的引用链接竟然是 CSLT 的 wiki 链接,难道是没有正式发表的一篇论文吗?

这篇论文主要从从近年来 LSTM 在 ASR 中的优异表现出发,用一些可视化方法,希望能深入理解这种有效性是如何产生的,非常有意思。这方面相关的工作有

  1. 2013 年的《Training and analysing deep recurrent neural networks》
  2. 2014 年的《Empirical evaluation of gated recurrent neural networks on sequence modeling》
  3. 2015 年的《LSTM: A search space odyssey》
  4. 2015 年 Andrej Karpathy 的《Visualizing and understanding recurrent networks》
  5. 2015 年的《Visualizing and understanding neural models in nlp》
  6. 2016 年的《Representation of linguistic form and function in recurrent neural networks》
  7. 2016 年的《Simplifying long short-term memory acoustic models for fast training and decoding》

后续这些论文的笔记我也会补充上来。

实验和结论

首先论文用 LSTM 和 GRU 分别在华尔街日报(Wall Street Journal, WSJ)数据集上进行对比,结果显示在 ASR 任务上,GRU 的效果要比 LSTM 稍好,至于为什么会更好,则在后面的实验中进行了可能的探讨。

Activation patterns

接着,在已经训练好的 ASR 模型上,输入 500 条音频数据,然后在网络的各层中随机挑选 50 个神经元,观察这些神经元的 cell value 的分布情况。

在基于 LSTM 的模型上,1-4 层的 cell value 分布情况如下图所示:

lstm_activation_distribution.png

可以看到,在 LSTM 上,cell value 基本上分布在 [-10, 10] 这个区间,且在 0 附近比较密集,而这种密集程度在更高的层级上慢慢降低。

而 GRU 的情况则很是不同,cell value 分布在 [-1,1] 这个区间中,然后在边界(即-1 和 1处)上比较密集,并且随层级变高而更加集中。如下图所示:

gru_activation_distribution.png

通过下面的图则能更直接地看到,LSTM 中的大部分 cell value 都在 (-2, 2) 区间上,而 GRU 中大部分 cell value 都在 [-1, -0.7] 和 [0.7, 1] 中。

activation_percentage.png

当然 GRU 的 cell value 分布在 [-1, 1] 这个区间是可以直接通过 GRU 的计算式推导出来的,只要 cell value 的初始值是 [-1, 1] 内的值,后面 cell value 的新值都会严格地被限制在 [-1, 1] 这个区间。

论文对 LSTM 和 GRU在这点上的差异,作出如下结论:

  1. LSTM relies on greatpositive or negative cell values of some units to represent information.
  2. GRU relies on the contrast among cell values of different units to encode information.
  3. This difference in activationpatterns suggests that information in GRU is more distributed than in LSTM.
  4. this may lead to a more compact model with a better parameter sharing(in GRU)

Neuronal Responsibility

第二个可视化实验稍微复杂一些。首先要为一个神经元定义一个叫做 responsibility 的东西,这个 responsibility 指一个神经元对一个音素的 cell value 是 irregular 的概率,而所谓 irregular ,在 LSTM 上是至 cell value 超出 (-10, 10) 这个区间,在 GRU 上则指 cell value 包含在区间 (-0.5, 0.5) 之中。刚才的实验也说明了,神经元的 cell value,大部分都是 regular(和刚才的 irregular 对应)的,那么如果某个神经元对某个音素经常产生 irregular 的响应,这就说明这个神经元对这个音素很敏感,也就是说,它能识别出这个音素来。这个实验就是以此为出发点来进行的。

在有上述定义后,统计 LSTM 各层对各个音素的 responsibility 超过 80% 的神经元数量,如下图所示:

lstm_responsibility.png

统计 GRU 各层对各个音素的 responsibility 超过 50% 的 unit 数量,如下图所示。

gru_responsibility.png

首先,两者都有一个共性,那就是同一层上,对某个音素的 responsibility 超过 80% 的神经元数量基本一样;区别是,在 LSTM 上,随着层数变高,对音素的 responsibility 超过 80% 的神经元总体呈现变多的趋势,而且会对某些个特别的音素的特别多;而 GRU 上层数变高,responsibility 超过 80% 的神经元在变少,也有对某些音素 responsibility 超过 80% 的神经元,但相对少一些。

LSTM 在高层倾向于识别更多的音素,而 GRU 则相反倾向于只对少数音素作出响应。这种差异作何解释有待进一步讨论。

就这个实验的出发点来说,在我看起来,应该是可以用统计物理学上的能量函数来作解释。

Temporal Trace

第三个可视化实验也挺有意思,它将基于 LSTM 或 GRU 的 ASR 模型识别一条音频数据时的 cell vector 的连续变化情况记录下来,并用 t-SNE 降维后做可视化。结果如下图所示:

temporal_trace.png

可以看到 LSTM 再次和 GRU 产生了区别。LSTM 的每层的轨迹大体上都是连续平滑的,论文里的解释是说 LSTM 能记得更多的东西,因此当前的输出会很大地受到过去记忆的影响,被平均后就变得平滑;而与此对比的 GRU,则倾向于给予新的数据更大的权重,换言之遗忘地会更快,因此在前几层的时候,轨迹变化地会更剧烈一些。

另外一个不同是,GRU 的轨迹在高层后慢慢地也变得连续了,这就说明 GRU 的底层是倾向于记忆新的输入,但在高层则倾向于把旧的记忆也融合进来。这就很有意思了。

Memory robustness

这个实验是为了比较 LSTM 和 GRU 的抗噪能力,方法也很简单,就是在输入数据的前部插入一些噪声数据,然后和正常的数据对比,观察网络中神经元的 cell value 的差异,结果如下图所示:

noise_influence.png

可以看到 LSTM 中噪声的影响会长时间地持续下去,而在 GRU 中这种噪声的影响会比较快地被消除掉。也就是说 GRU 更鲁棒,从另外一方面来说,也可以认为 GRU 能记忆的东西更少,倾向于记忆新的输入,因而遗忘地更快,于是噪声数据产生的干扰就在后面被抹除了。

Application to structure design

在论文的最后,作者根据前几个实验得到的结论,尝试对 LSTM 作出一些调整。第一个调整是把 cell state 的更新操作放到计算输出之后,像 GRU 一样,并称这个特性为「lazy cell update」。结果显示这样修改后,LSTM 在 WSJ 数据集上的效果稍有提升并和 GRU 接近了。

lstm_gru_improve.png

有意思的一个现象是在这样修改后,LSTM 高层的 temporal trace 变得更碎更不平滑了,接近 GRU 的情况。这就说明前面的一些差异,很可能是和 LSTM 与 GRU 更新 cell state 的顺序差异造成的,从计算式上来看似乎也说得通。

此外作者还尝试在 LSTM/GRU 上加上 residual learning,效果也稍有提升。但是这个我持保留态度。

此外比较遗憾的一点是,作者虽然尝试修改 LSTM 让 cell state 的更新延后,却没有尝试对 GRU 作出相反的修改也就是让 GRU 的 cell state 的更新提前。我的想法是这样修改后再去做对比,如果得到的结论和修改 LSTM 的结论一致,就更能说明问题了。

论文笔记:Sequence to Sequence Learning with Neural Networks

2018年11月10日 08:00

三位来自 Google 的作者在这篇论文中提出了一种以两个 RNN 组合方式构成的网络结构,用来处理英语到法语的翻译问题,并且认为对于传统的深度神经网络(Deep Neural Network, DNN)不能处理的输入和输出都是变长序列的问题,这种模型都能很好地进行处理。

自 2006 年 Hinton 掀起深度学习这股浪潮,联结主义强势回归到现在已经有 10 年了,这 10 年里,深度学习已经在图像和语音两大领域取得了卓越的成就。然而大部分的深度神经网络仍然需要输入和输出的大小固定不变,并将一层一层不同类型的网络层组合在一起,通过巨量的数据、更有效的优化方法和训练技巧来达到目的,而这里的 "输入和输出的大小固定不变" 是一个很大的限制,比如说我们的语言、文字,天然就是变长的序列,这些网络结构要处理的话就必须要在核心模型之外在加上一些额外的处理。举个例子,图像识别中的图像一般也不是固定大小的,输入前一般都需要进行预处理将图像规整到同一尺寸。

面对这一问题,一个很自然的方法就是使用循环神经网络(Recurrent Neural Network, RNN),这是一种在空间结构上非常简单的模型,在一些不太复杂的问题上,甚至只需要三层结构就足够了,如下图所示(配图来自 Elman 的 《Find Structure in Time》)

simple_rnn.png

相比传统的前馈神经网络(Feedforward Neural Network, FNN),RNN 的独特之处在于其隐藏层的一个环状结构,这个结构相当于能够缓存当前的输入,并用之参与下一次的计算,这样就隐式地将 时间信息 包含到模型中去了,在输入变长序列时,可以序列中的最小单元逐个输入。RNN 虽然在空间结构上可以很简单,但在进行训练时通常需要在 时间维度 上展开(unroll),所以可以认为它是一个在时间维度上的 DNN,于是 DNN 训练中会出现的 gradient vanish (梯度消失)也会出现,直观上可以将其理解为 "记忆的衰退",换句话说, RNN 只能 "记住" 短期的信息。1997 年 "长短期记忆单元(Long Short-Term Memory, LSTM)" 被提出来解决这个问题,而本文提出的模型就是利用了 LSTM 的优点。

虽说 RNN 能用于处理变长序列,但具体要怎么做呢?最早提出 RNN 之一的 Elman 做过的一些实验是这样的: 将序列中的元素逐个输入网络,并预测下一时刻的输入,比如一条句子逐个字符输入网络,并在输入第 n 个字符时预测第 n+1 个字符。这种方式虽然能处理变长的输入序列,但得到的输出序列却是和输入序列长度一样的,限制仍然还在。

一种办法是在输出时增加一个 "空白" 的输出候选,然后在每次输出时取每一种可能输出结果的概率,得到一张路径网络后用类似 beam search 的方法来组装起真正的输出,由于 "空白" 输出的存在,最后得到的非空白输出序列的长度就变成可变的了。语音识别和一些 OCR、手写识别是这么做的,效果也还不错。

而作者提出的方法是将两个 RNN 组合起来,以更加灵活地处理变长输入序列和变长输出序列。其模型结构如下(注意,这是一个已经 unroll 的网络结构)

seq2seq.png

模型的左侧(到输入为 <EOS> 为止)是一个 RNN 在输入序列 "ABC<EOS>" 上的展开,右侧是在输出序列 "wxyz<EOS>" 上的展开,其中 <EOS> 是一个表示序列结束的特殊符号。功能上,第一个 RNN 用来将输入序列映射成一个固定长度的向量,这个 "固定长度的向量" 即是 RNN 中间隐藏层所缓存的对整个输入序列的 "记忆",我们可以说它表示了输入序列的语义;然后用第二个 RNN ,来从这个向量中得到期望的输出序列。

除了这个特殊的模型结构之外,再就是用 LSTM 来保留 一定程度 的长期记忆信息,并且作者表示复杂的网络结构(更多的参数)具有更强的表达能力,因此每个 RNN 用的都是 4 层的 LSTM, 参数两多达 380M, 也就是 38 亿 —— Google 的朋友们你们真是站着说话不腰疼啊,38 亿参数的模型,一般人哪来这么多数据喂饱这个大胃王,哪来那么强劲的机器来训练……

再就是,作者说将输入序列倒序后,效果得到了显著地改善,BLEU 从使用该方法之前的 25.9 上升到 30.6,然而自己也对其原因表示不太清楚,只作出了一些猜想(也就是说,并无明确的理论依据)。

原文中,作者对这个技巧的解释如下

While we do not have a complete explanation to this phenomenon, we believe that it is caused by the introduction of many short term dependencies to the dataset

以及

By reversing the words in the source sentence, the average distance between corresponding words in the source and target language is unchanged. However, the first few words in the source language are now very close to the first few words in the target language, so the problem's minimal time lag is greatly reduced.

模型训练时,以最大化条件概率为目标,也就是说,其目标函数为

\[\frac{1}{|S|}\sum \log P(T|S)\]

而在模型训练好后,用于实际的预测时,则也采用了简单的 beam search 方法,即在模型参数确定的情况下,对输入序列 \(S\) ,按下面的式子求解输出序列

\[\hat{T} = \arg\max_{T}P(T|S)\]

下表是与其他模型在机器翻上的的效果对比,其中上面两行是其他模型的效果,下面六行是作者模型在不同参数设置时的结果。

seq2seq_model_comparision.png

此外作者还尝试将自己的模型与传统的 SMT 系统进行结合,效果显著, BLEU 最好的达到了 37,超过 baseline system 4 个点。

seq2seq_model_comparision2.png

sequence to sequence 模型被提出后,由于其灵活性,受到了广泛的关注,我个人是很喜欢这个模型中的想法的。然而现在流行的几个开源库对 sequence to sequence 模型的支持仍然不太理想,它们都要求在模型定义时就将输入序列的最大长度和输出序列的最大长度确定,对于长度不足的,则要用特殊符号进行填充,并在模型内部或外部做一些特殊处理。比如用 Python 的深度学习框架 Keras 来实现一个弱化版的 sequence to sequence 模型,可以这样:

# coding: utf-8
"""Sequence to Sequence with Keras 1.0"""

from keras.models import Sequential
from keras.layers.core import Dense, RepeatVector
from keras.layers.recurrent import LSTM
from keras.layers.wrappers import TimeDistributed

def build_model(input_size, max_output_seq_len, hidden_size):
    """建立一个 sequence to sequence 模型"""
    model = Sequential()
    model.add(LSTM(input_dim=input_size, output_dim=hidden_size, return_sequences=False))
    model.add(Dense(hidden_size, activation="relu"))
    # 下面这里将输入序列的向量表示复制 max_output_seq_len 份作为第二个 LSTM 的输入序列
    model.add(RepeatVector(max_output_seq_len))
    model.add(LSTM(hidden_size, return_sequences=True))
    model.add(TimeDistributed(Dense(output_dim=input_size, activation="linear")))
    model.compile(loss="mse", optimizer='adam')

    return model

上面这段代码,从模型定义上,只对输出序列做了最大限制,但训练数据集中 不允许出现不同长度的输入序列 ,实际上不同长度的目标输出序列也不被允许。希望这种情况在将来能够有所改善,当然啦,不行的话可以自己用 Theano 写嘛~

论文笔记:Learning Phrase Representation using RNN Encode-Decoder for Statistical Machine Translation

2018年11月10日 08:00

作者在这篇论文中提出了一种新的模型,并用来进行机器翻译和比较不同语言的短语/词组(phrase)之间的语义近似程度。这个模型由两个 RNN 组成,其中一个(Encoder)用来将输入的序列表示成一个固定长度的向量,另一个(Decoder)则使用这个向量重建出目标序列 —— 在机器翻译任务里,可以认为 Encoder 产生的向量是输入文本的 语义表达 ,而 Decoder 则根据这个语义表达产生目标语言的文本。

模型结构如下图所示

encoder_decoder.png

这个结构其实和 "Sequence to Sequence Learning with Neural Networks" (后面简称 "Sequence to Sequence")里提出的结构是非常相似的,见下图:

seq2seq.png

"Sequence to Sequence" 这篇论文的作者自己也表示 "Our approach is very similar to Cho et al" 。

两种结构的不同之处在于,Encoder 输出的向量,在后者这里只用来作 Decoder 的第一个 time step 的输入,而在 Cho 的这篇论文里,它是要在 Decoder 的每一个 time step 中都参与计算的。即在 "Sequence to Sequence" 中,Decoder 中第 t 个 time step 时计算输出的式子为

\[P(y_{t})=f(h_{t}, y_{t-1})\]

而在 Cho 的论文中,则是

\[P(y_{t})=f(h_{t}, y_{t-1}, c)\]

其中 \(h_{t}\) 表示第 t 个 time step 时 Decoder 的内部状态(RNN 单元的 "memory"),\(c\) 表示 Encoder 输出的向量。

再有一点不同是,Cho 在这篇论文中提出了 GRU 这种 LSTM 的简化版本单元结构,使用的 RNN 也是以 GRU 而非 LSTM 组成的。对 LSTM、GRU 的细节这里就不深入了,反正 Cho 自己还有一篇论文就是比较 LSTM 和 GRU 的各自优缺点的,到时候也读一读记录一下笔记什么的。

Cho 的 Encoder-Decoder 模型中,Encoder 的行为比较简单,重点在 Decoder 上。

Decoder 中 t 时刻的内部状态的 \(h_{t}\) 为:

\[h_{t}=g(h_{t-1}, y_{t-1}, c)\]

该时刻的输出概率则为:

\[P(y_{t}|y_{t-1}, y_{t-2},...,y_{1}, c)=f(h_{t}, y_{t-1}, c)\]

模型训练时则去最大化给定输入序列 x 时输出序列为 y 的条件概率:

\[\arg\max_{\theta}\frac{1}{N}\sum_{n=1}^{N}logP_{\theta}(Y_{n}|X_{n})\]

这个模型用来做机器翻译是很自然的,除了机器翻译外,作者还用这个模型来得到英语 phrase 和法语 phrase 之间的语义近似值(或说相似性),方法是用大量的英语和法语的 phrase pair 进行训练,每个 pair 在训练时最后都会有概率输出,就用这个概率值作为这个 pair 对的 score ,并在整个训练过程中更新,最后得到一张记录很多 phare pair 近似值的表,这个表可以在其他的统计机器翻译(Statistical Machine Translation, SMT)系统中使用 —— 比如要将某个英语 phrase 翻译成法语 phrase 时,可以从表中找到与该英语 phrase 最接近的 K 个法语 phrase 作为候选。

在机器翻译上,作者用 Moses (一个 SMT 系统) 建立了一个 phrase based 的翻译模型作为 baseline system ,然后对比了以下四个模型的 BLEU 值

  1. Baseline configuration
  2. Baseline + RNN
  3. Baseline + CSLM + RNN
  4. Baseline + CSLM + RNN + Word penalty

所谓 BLEU 呢,是 BiLingual Evaluation Understudy 的缩写,是业界用来评估一个机器翻译系统水平的基准,如果遇到做机器翻译的上去问问 “你们的系统 BLEU 是多少” ,绝对会被认为你是专业人士,所以不妨记一下这个词 :) 。不过实际上 BLEU 和人工评价相差甚远,而且对于一些小的错误非常敏感,Yvette Graham 等人在 NAACL 2016 上专门有一篇长论文(Achieving Accurate conclusions in Evaluation of Automatic Machine Translation Metrics)讨论这个事情,这篇论文受到了很大的重视。

回归正题,四种不同的模型的 BLEU 值如下表所示:

bleu_of_models.png

其实也不见得比传统方法要好多少。当然据我所知, Encoder-Decoder 这个模型还是挺受重视的,如果我没记错的话,上次在 QCon 上看到阿里的跨境电商翻译系统应该是用上了 Encoder-Decoder 结构(不想误导,他们还有专业团队进行人工翻译)。

phrase pair 打分的结果如下:

pair_score.png

其中第一栏是输入的英语 phrase ,第二栏是用传统的模型得到的最近似的三个法语 phrase,第三栏是用 Encoder-Decoder 模型得到的最近似的三个 phrase。

就我对这篇论文的理解来说,应该是这样的。结尾再对计算两个序列之间的相似性这件事情说说自己的想法,在这件事情上,作者只是在训练过程中得到这样一个额外的、静态的结果,最后得到的那张表,其中包含的 phrase 是确定的、有限的,姑且认为其中的 phrase 都是粒度比 sentence 更细的合法单元,但实际上可以把 Decoder 的输出看成一个搜索空间,在给定 任意 输入序列的时候,用输出序列在这个空间里确定搜索路径,这条路径上每一条边都是有置信度(概率)的,这样就可以直接得到一个条件概率来表征连个序列之间的相似程度,好处是可以处理任意的 sequence pair,这对实际问题应该会有帮助。当然这个想法可行性有待验证就是了。

论文笔记:Memory Networks

2018年11月10日 08:00

这篇论文是第一篇提出 Memory Network 的论文,和神经元图灵机(Neural Turing Machine)的论文在同一年。不过说实话,我觉得这篇论文的质量很一般,是比不上 NTM 那篇论文的,只不过其中提出来的一些观点和思想在后面逐渐被补充和完善,才有了现在 Memory Network 的名声。

论文的出发点有这么几个:

  1. 大部分 ML 方法都缺乏建立长期记忆(long term memory)的能力以及基于长期记忆的推理和处理 —— RNN 虽然能一定程度上做到,但由于 gradient vanish 的问题,实际上长期记忆在 RNN 中会衰退为短期记忆,LSTM 的提出对此问题有所改善,但效果有限
  2. 作者主要是想进行基于知识库(Knowledge Base,或称知识图谱)的问答,但希望知识库可以作为模型的一部分并在此基础上进行 QA
  3. NTM 的结构很好,但是它能使用的 memory 还是太小了

首先明确一下这里的「Memory」也就是「记忆」的概念。在不同的领域,比如脑科学、认知科学、物理学,对记忆的定义和理解都不一样,我对这些学科不太懂,不知道该对应到其中的哪部分上去。在机器学习/深度学习里,所谓的记忆,只是指「处理过的数据中会对目前的处理过程造成影响的信息」,也就是说,是和时序存在关联的。图像识别里用大量的图片来训练模型,最后能识别出新的图像的类别,这算不算「记忆」呢?按照我们的观点,其实也算,但这种我们更多认为是从大量同类图片中提取出了「模式」,本文讨论的「记忆」并不是这样的,除了有时序关系,还要有一定的因果关系,比如说我前 1 分钟给我看一张搞笑图片,现在给我一张根据这个搞笑图片 PS 过的川普和希拉里的图片,我会 get 到笑点然后拍桌狂笑。

总结一下的话是两点:

  1. 时序性: 信息的输入是时序的,有先有后
  2. 因果性: 先输入的信息对后输入的信息会产生影响,如果将两者调换,那么结果会不一样

看到时序性,很自然会想到 RNN,而它也满足第二点,只不过在它里面,「记忆」是隐式存在的,也就是它里面的 hidden state。但众所周知,RNN 因为 gradient vanish 的问题没法对长期记忆进行建模,LSTM 的提出倒是神来之笔,大大提高了 vanilla RNN 这方面的能力,后来 residual network 里的 skip connection 和 LSTM 里的 constant error flow 是很相似的。

对概念的讨论就这样,先来看看论文的模型,大致如下:

memnn_model.png

整个模型分成五个模块,分别是:

  1. 输入模块 I: 用来对输入进行特征提取
  2. 泛化模块 G: 用来将输入根据一定的机制写入到 memory 中去
  3. 记忆模块 memory: 用来存储信息,供后面的处理使用
  4. 响应模块 R: 对特定的问题,用来从 memory 中提取信息并进行推理,得到输出信息
  5. 输出模块 O: 对模块 R 的输出进行处理,表达成自然语言

如果读过神经元图灵机那篇论文,应该能一眼看出来模块 G 和模块 R 其实就是 NTM 里面的 write heads 和 read heads,在读写的时候计算 memory 里所有成分和当前数据的相似度,其实就是类似现在的 attention 机制了。然而有一个问题是,如果 memory 超级大,比如像作者说的一样,要将整个 freebase 作为 memory,别说计算 softmax 了,就是遍历 memory 都会是难以承受的,这问题该怎么解决呢?

Consequently, for efficiency at scale, G(and O) need not operate on all memories: they can operate on only a retrieved subset of candidates(only operating on memories that are on the right topic). We explore a simple variant of this in our experiments.

如上,作者的办法是,建立 word 到 memory 的哈希,然后根据 question 来去检索出关联的 memory,只对这部分 memory 进行推理 —— 这其实就是一个倒排索引了……

在这个模型中,模块 G 和模块 R 是核心所在,真的如作者所说要存储大规模的 memory 并在其基础上进行推理时,模块 G 和模块 R 的具体设计是很重要的,然后作者在后面描述基于 freebase 的 QA 任务时,模块 G 只是填充「下一个空白的 memory cell」,模块 R 则是「根据倒排索引去检索相关的 memory cell」,完全就是一套检索系统,整个训练过程也完全不是端到端的,臭不要脸 :)。直到 15 年 Sukhbaatar 提出了端到端的模型后, Memory Network 才算是真正意义上的完成了,我是这样认为的。

总之我个人对这篇论文评价不高,虽然它提出的 Memory Network 这个模型的思想是不错的,某种意义上来说,它其实是一个框架而不是一个具体的模型,因为其中的 I/G/R/O 四个模块都是可以用不同 NN 来实现,扩展性很好,这也是后来持续地有 Memory Network 相关的工作的原因, NIPS 2015 还专门有一个 RAM workshop —— 所谓「RAM」是 "RNN-Attention-Memory" 的缩写。

以上。

论文笔记:LSTM: A Search Space Odyssey

2018年11月10日 08:00

作者

  • Klaus Greff
  • Rupesh Kumar Srivastava
  • Jan Koutnik
  • Bas R. Steunebrink
  • Jurgen Schmidhuber

观点

  • LSTM 结构的核心思想是其能维护历史状态的记忆单元,以及能调节信息出入的非线性门控单元(gating unit)
  • 自 LSTM 提出后,陆续有多种对 LSTM 结构的改进工作,并广泛应用到了许多规模、性质迥异的问题上,但却缺乏对 LSTM 及其变体中各个计算部件的系统性分析

模型/实验/结论

模型,LSTM 即八种待对比的 LSTM 变体

  • V: vanilla LSTM,即经典的 LSTM 模型
  • NIG: 在 LSTM 基础上去除 input gate 得到的结构
  • NFG: 在 LSTM 基础上去除 forget gate 得到的结构
  • NOG: 在 LSTM 基础上去除 output gate 得到的结构
  • NIAF: 在 LSTM 基础上去除 input activation function 得到的结构
  • NOAF: 在 LSTM 基础上去除 output activation function 得到的结构
  • NP: 在 LSTM 基础上去除 peephole 得到的结构
  • CIFG: 既 GRU
  • FGR: 在 LSTM 基础上让门控单元互相之间都有连接(full gate recurrence)

实验

  • 在 TIMIT 数据集和 IAM 在线手写数据库上使用双向 LSTM,在 JSB Chorales 数据集上使用 LSTM
  • 在 TIMIT 数据集和 JSB Chorales 上使用交叉熵作为损失函数,在 TIMIT 数据集上则使用 CTC
  • 对总共 27 个模型各进行 200 次对数尺度上的超参搜索,然后进行训练,共进行 5400 次实验
  • 每个数据集上的每个变体的 200 次实验中,最好的 20 个实验结果被拿来和 vanilla LSTM 模型的结果对比

结论

  • 在三个数据集上,移除 forget gate 或 output activation function 都严重损害了模型性能,forget gate 对 LSTM 来说至关重要
  • 对连续实数数据上的监督学习问题,input gate、output gate 和 input activation function 的存在非常重要
  • GRU 和 移除 peephole 的变体相比 vanilla LSTM 没有显著的性能差异,但它们都在一定程度上简化了 LSTM 结构
  • full gate recurrence 结构没有改善 LSTM 的性能,相反还在 JSB Chorales 数据集上让结果变差了不少,加上它让 LSTM 更复杂了,不建议使用
  • 动量项对性能和训练速度都没有提高作用
  • 梯度裁剪会损害整体性能
  • 在使用 SGD 进行训练时,动量项对训练没什么显著好处;但在用 BSGD 进行训练时可能会起到一定的作用
  • 学习率和网络大小是 LSTM 中非常重要的两个超参

论文笔记:Hierarchical Memory Networks for Answer Selection on Unknown Words

2018年11月10日 08:00

这篇论文是中科院自动化研究所(CASIA)在 9 月份发表的一篇论文,收录于 COLING2016。该论文基于 Memmory Network 做了一些改进,使得模型在特定的 QA 任务中能更好地从 memory 中选择答案,并且能一定程度上处理低频词甚至未登录词(unknown words)。论文的数据集以及模型实现已经在 Github 上开源,如果对论文细节没有太多兴趣,可以直接去 项目地址 了解项目详情。

论文使用的数据集如下表所示

hmn_datasets.png

数据集包含机票预订和酒店预订两个领域,而且包含中文的数据集哦,这点很赞,虽然数据量看起来并不是很多。截取中文的机票预订数据中的片段如下:

1 下午 好 , 我 是 机票 预订 服务 代理 , 需要 什么 服务 ?
2 我要 预订 一张 机票 。
3 请问 先生 您 从 哪里 起飞 ?
4 由 BGI 起飞 的 飞机 。
5 去 到 哪里 ?
6 到 印第安纳 去 。
7 电话 号 ?
8 13228762221 , 这 是 我 的 号码
9 时间 是 ?
10 2015年09月26日22点 之前 。
11 先生 , 乘客 的 身份证 是 ?
12 我 的 身份证号 是 110100195352319154 。
13 麻烦 说 下 您 的 名字 ? 谢谢 先生 。
14 好 的 , 名字 是 袁磊 。
15 先生 , 我们 已经 成功 为 您 预订 。
16 这么 快 , 非常 感谢您
17 订票 人 的 姓名 叫 什么 ? 袁磊 16
18 出发 城市 是 哪里 ?       BGI 16
19 到达 城市 是 哪里 ?       印第安纳 16
20 出发 时间 是 什么 时候 ?  2015年09月26日22点 16
21 证件号码 是 多少 ?        110100195352319154 16
22 联系电话 是 多少 ?        13228762221 16

数据集的情况似乎有点像多轮对话,但并不完全是,如上 22 轮对话,其中前 16 轮是客服和客户之间的对话,这段内容被作为 history 输入到模型中储存为 memory,而 17-22 则是问题和对应的答案,每个问题的答案都是一个单独的词,是从 memory 中挑选出来的。所以从数据集上来看,本文的方法适用于一些像机票预订、酒店预订这种流程比较明确的业务。

像这种数据集,如果要我做我会怎么做呢?粗暴点的思路是将 history 和 question 各自 encode,然后将两者一起用于计算来去预测输出,事实上之前不少 QA 方面的工作都是这种思路。论文开头就批评这种做法

the memory of these methods, such as Long Short-Term Memory(LSTM) (Hochreiter and Schmidhuber, 1997) and Gated Recurrent Unit (GRU) (Cho et al., 2014) compressing all the external sentences into a fixed-length vector, is typically too small to accurately rememberfacts from the past, and may lose important details for response generation

这个也是当前在 sentence representation 讨论得比较多的话题吧,将句子直接 encode 成一个固定长度的向量,是会丢失一些细节的。不过我觉得还是看应用场景,如果是那种一问一答且目的性不是非常强的 QA 场景,encoder-decoder 的框架问题不大,语义漂移(如万能回复)的问题可以在前期进行意图识别、情感分析来得到额外的特征输入到模型里。但像本文的这种数据集就不是简单的一问一答,而是先有了一部分历史信息,然后给定问题从历史信息里寻找答案,有点类似英语的阅读理解题目 —— 因此 PaperWeekly 在 教机器学习阅读 中介绍了《End-to-End Memory Networks》这一篇作为本篇论文基础的论文(好绕口呀)。

对于类似本文的 QA 任务,早先的一些相关工作也是可以用上的,比如 Sukhbaatar 提出的端到端的 Memory Networks,文中记为「MemNN」。但是 MemNN 的问题在于它单纯在句子级别进行 "推理",具体一点是只使用了 sentence level 的 attention 机制,见之前写的笔记: End-to-End Memory Networks。如果能进一步地在词级别进行分析,结果应该会更好一点。事实上也有人这么做了,2015 年俞扬等人的《Empirical study on deep learningmodels for question answering》论文中就提出了一种 "Search-Response" 结构的模型,先使用 MemNN 从 history 中挑选出相关的句子(supporting sentences),这一步称为 "Search";然后用 NTM 或者 NMT(Neural Machine Translation) 来从这些 supporting sentences 中生成答案,这一步称为 "Response"。在 "Search-Response" 结构中,Search 和 Response 两个步骤是分别独立训练的,也就是说这其实是一个 pipeline 的方法。

所以本文的基本思想是: 结合 MemNN 和 "Search-Response" 的思想,得到一个端到端的系统。本文的模型结构如下图所示:

hmn.png

图中左侧是模型的整体结构,右边的两个小图是左图中两个模块的细节图示。整个模型大体上可以划分为四个部分,分别是:

  1. sentence level memory and reasoning

    这部分将 history 信息转换成内部的 memory,并在 此基础上计算结果,同 MemNN,上图中右下块是这部分的图示,要注意的是这里为了简化画成了类似 RNN 的结构图,但并不是说 \(X\) 中的句子依次输入这个模型,然后更新自连接的 \(u_{r}{(S)}\),这里的自连接只是表示多层结构(文中使用的层数为 3),而这种多层结构和 RNN 的结构有共同之处。

    在这里,history 也就是 \(X\) 中的每个句子 \(x_{i}\) 和 question 也就是 \(q\) 都要表示成一个向量,使用的是《End-to-End Memory Networks》中的 position encoding 方法,即将句子中每个词的 embedding 加权求和,而这个权值和词在句子中的次序有关。

    总之这部分跟《End-to-End Memory Networks》中的内容基本一样,不赘述,详见 MemNN 的笔记

  2. k-max pooling

    这部分连接 1 和 3,利用 1 输出的 internel state 和 question 一起挑选出 history 中和 question 最相关的 k 个句子。

    所谓的 internel state,就是图中的 \(\alpha^{(S)}\)

    \[\alpha^{(S)}=softmax(M^{T}u_{1}^{(S)})\]

    上式中 \(M\) 为 sentence level memory,\(u_{1}\) 为问题 \(q\) 的 representation。

  3. word level memory and attention: 使用 2 得到的 k 个句子,进行 word level 的 attention 来得到结果

    这部分使用 BiGRU 来得到 attention

    \[M^{(W)} = \{m_{t}\}_{t=(1,2,...)}\]

    \[m_{t}=\overrightarrow{h_{t}}+\overleftarrow{h_{t}}\]

    \[\alpha^{(W)}=softmax(v^{T}\tanh(Wu_{R}^{(S)}+U\hat{m}_{t}))\]

    这里的 \(alpha^{(W)}\) 就是 word level 得到的结果,但是这个结果是在 k 个句子中的词上的概率分布,没法直接用于第 4 步的计算,因此要做一次转换,将 \(\alpha^{(W)}\) 扩充为长度为 \(V\) 的向量,\(V\) 是词典的大小,方法是用 0 填充。

  4. output: 综合 1 和 3 的输出来计算最后的结果

    第一步得到的输出记为 \(p^{(S)}\),第三步得到的输出记为 \(p^{(W)}\),将这两者直接相加作为最后的输出

    \[p = p^{(S)}+p^{(W)}\]

如上所述,因为在 sentence level memory network 的基础上加上了 word level memory,作者将这个模型称为「Hierarchical Memmory Networks」,这是一个能够进行端到端训练、能在更细粒度的语言成分上进行"推理"的 QA 模型。

下图是 HMN 和其他模型在给定的数据集上的对比结果

hmn_compare_with_others.png

以及 HMN 模型使用不同的 word level encoder 时的效果对比

hmn_results.png

下面的图显示了模型回答一个问题时的过程

  1. 表格第一列是 history,这些内容将会被 encode 后储存到 sentence level memory 中
  2. 需要回答的问题是: When does the client depart,正确答案是 history 中第 14 条中的日期
  3. 表格第二列是 sentense level reasoning 的过程,其中的值是每一个 reasoning 层的 internel state 也就是 \(u_{r}^{(S)}\),可以看到进行三次 reasoning 后正确的 supporting sentence 也就是第 14 条的权重已经非常的高了
  4. 第三列是 k-max pooling,这里的 k 取值为 4,得到的就是 8、10、13、14 这四个候选的句子
  5. 第四列是 word level 部分,使用 attention 来得到每个候选句子中的词作为答案的概率,这里概率最大的就是第 14 条句子中的日期

hmn_example.png

以上就是本篇论文的主要内容,至于开头作者提到的未登录词问题的处理,作者就是指着上面的图说:你看, 10/13/2018 这样的词也被找出来了!总之,作者没有提出一种对未登录词的解决办法,我猜他的意思是,在构建 vocabulary 的时候,不要根据词频去除低词频的词,而是照单全收喂给模型,而这个模型是能够将这种词频非常低的词正确地找出来作为答案的。

我觉得这就有点标题党了哈……

论文笔记:Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling

2018年11月10日 08:00

作者

  • Junyoung Chung
  • Caglar Gulcehre
  • KyungHyun Cho
  • Yoshua Bengio

观点

  • RNN 在很多机器学习任务尤其是变长输入输出的任务上效果拔群
  • 经典 RNN 有两个主要的问题: 梯度消失, 长期记忆急速衰减。
  • 解决 RNN 难以训练的尝试有两种: 一种是设计更好的学习方法(Bengio 2013),另外一种是设计更复杂的激活函数
  • LSTM 不会每次都重写 memory,而是可以通过 input/forget gate 在需要的时候尽量地保留原来的 memory
  • LSTM/GRU 中额外增加的 cell state,让它们能记住较早之前的某些特定输入,同时让误差反向传播时不会衰减地太快

数据集

模型/实验/结论

实验: 在上述几个数据集上,分别使用经典 RNN、LSTM、GRU 进行训练,并记录 NLL 的变化情况。

结论: LSTM/GRU 在收敛速度和最后的结果上,都要比经典 RNN 要好,但 LSTM 和 GRU 在不同的数据集和任务上虽然互有优劣但差异不大,具体使用 LSTM 还是 GRU 还要视情况而定。

概念和术语

  • polyphonic music:

    (来自维基百科)

    复音音乐/复调音乐/和弦,一种“多声部音乐”。作品中含有两条以上(含)独立旋律,通过技术性处理,和谐地结合在一起,这样的音乐就叫做复音音乐。

    复音音乐第一个“音”字表示旋律,中国音乐界习惯将“复音音乐”称为“复调音乐”,主要是着眼于曲调一词,但“复调音乐”容易与二十世纪的“复调性音乐”一词混淆。

总结

实验很粗暴,结论很简单。

论文笔记:An Ensemble of Retrieval- and Generation-Based Dialog Systems

2018年11月10日 08:00

这篇论文在指出基于检索方法(retrieval-based)和基于生成方法(generation-based)的两种对话系统的问题后,提出了一种结合检索方法和生成方法的对话系统,并在 700w 的中文对话数据(来自微博、百度知道、贴吧等)上获得了比两种方法都更好的结果。

检索方法在一些垂直领域的对话系统中比较常见,这是因为特定领域的对话系统中用户的目的性比较明确,多为查询信息或者提供信息(以完成某种「任务」),通过检索和人工规则一般就能解决问题。而开放域的闲聊类场景下,如果有足够的数据,用生成方法会是一种比较好的选择。作者关注的开放域中的对话系统,这种场景下也可以应用一些检索方法和人工规则,但检索方法只能根据浅层语义来发现关联的回复,而尝试用人工规则覆盖开放域下的种种不同情况将会导致规则数量爆炸增长;生成方法固然可以生成知识库中没有的回答,但很容易抛出类似「呵呵」、「是吗」这种万能回复,这种语义漂移现象已经被很多将生成方法应用于对话系统的人发现并进行研究了。

文本的思路是将检索方法和生成方法结合起来,主要思想有两点:

  1. 使用生成方法时,使用检索到的回复作为额外的信息
  2. 对检索方法和生成方法给出的结果,在最后进行重新排序

ensemble-dialog-system.png

具体一点的话,过程是这样的:

  1. 对给定的 query \(q\),首先从知识库中进行检索,得到候选的回复 \(r^{\ast}\),这个 \(r^{\ast}\) 会有很多个
  2. 将每个候选回复 \(r^{\ast}\) 和 \(q\) 一起输入到生成模型也就是图中的「multiseq2seq generator」中,后文又称之为「biseq2seq」
  3. \(q\) 和 \(r^{\ast}\) 都是一个句子,它们在 biseq2seq 中经过 encoder 被各自编码成一个向量
  4. 将 \(q\) 和 \(r^{\ast}\) 的向量 拼接 起来,decoder 以其作为输入,生成一个回复 \(r^{​+}\)
  5. 将所有检索出的回复 \(r^{\ast}\) 和生成的回复 \(r^{​+}\) 混合起来,进行重排序,返回最优的一个

检索阶段没有太多好说的,就是搜索系统用的那一套东西,先根据 query 内容找到知识库中可能有关联的 "问-答" 对 \((q^{\ast}, r^{\ast})\),然后进行排序,选出 1000 条最好的结果。需要注意的是,在排序时,\(q\) 和 \(q^{\ast}\) 的关联性、\(q\) 和 \(r^{\ast}\) 的关联性会一起被考虑。

生成阶段,其所谓的「biseq2seq」的结构如下图所示

biseq2seq.png

这个模型里有两个 encoder 和一个 decoder,一个 encoder 用来对 \(q\) 进行编码,另一个 encoder 用来对 \(r^{\ast}\) 进行编码。两个 encoder 输出会拼接成一个向量输入到 decoder 中。不过论文没有就 biseq2seq 的具体数学表示和训练做详细说明,从引用文献里看,biseq2seq 的想法是来自 Barret Zoph 和 Kevin Knight 的一篇论文《Multi-source neural translation》,是 2016 年发表的一篇论文,回头再去了解一下。

下表是论文中方法和其他一些方法的实验对比结果。

ensemble-dialog-system-results.png

论文中还展示了 biseq2seq 和 seq2seq 在生成回复时的结果对比,从下表来看,biseq2sq 生成的结果确实更有意义一些。

biseq2seq_vs_seq2seq.png

论文笔记:End-to-End Memory Networks

2018年11月10日 08:00

这篇论文是在看 Hierarchical Memory Networks for Answer Selection on Unknown Words 时追过去的 —— 这篇论文里有些东西讲的不明不白,还好它里面说明一些思想来自哪里。

论文标题为 "End-to-End Memory Networks" ,很显然这篇论文是基于 Memory Networks 来做了一些工作,按照惯例,论文在开头说了一些原始的 Memory Networks 的缺点:

  1. Memory Networks 难以进行 BP 训练
  2. Memory Networks 需要更多的 supervision(这个没太懂,回头把 Memory Networks 再看一下)

模型的基本结构如下图所示。

memn2n_single_layer.png

模型的输入包含两部分,一个是图左端那个「Sentences」,即由多个句子组成的内容,这些句子会被输入到网络中并储存为「memory」,可以认为是在检索/问答时的历史信息或者说上下文;另一部分是 query 或说 question,就是图中下端那个 q,也是一个句子。模型最后的输出也是一个句子。

模型的输出,按照我对这张图的理解,应该是一个经过 softmax 的 d 维向量(d 是 vocabulary 的大小),但是这样的话就只能输出一个词了?从后面的实验部分来看确实是这样的,总感觉这样的话并不是很适用于常规的 QA 任务了。

输入的 Sentences,首先通过 embedding 矩阵 A 得到 input memory。其实就是将每一个句子 encode 成一个向量,也就是图上的 \(m_{i}\),这些 \(m_{i}\) 组合起来就得到 input memory,也就是涂上标为 Input 的那块矩形 M。问题 q 同样通过 embedding 矩阵 B 来 encode 成一个向量 u,然后用 u 和 M 內积得到一个权值向量:

\[p=softmax(u^{T}M)\]

接着,输入的 Sentences 通过另外一个 embedding 矩阵 C 得到另外一个 memory 表示,也就是图上标为 Output 的那块矩形 C,然后用上一步得到的权值向量来对 C 进行加权求和:

\[o=\sum_{i}p_{i}c_{i}\]

对于这部分,我的理解是,u 和 M 的內积相当于一个 attention,得到 attention 后,就用来在用于输出的 memory 也就是 C 中进行检索得到输出结果。不过还是有些地方不太明白,比如说反映在网络结构上,u 和 M 的內积是怎么体现的?输入连接两个 embedding 矩阵又是怎么体现的?

在得到输出向量 o 后,按理来说直接 softmax 就可以得到结果吧,但是这里却和 question 的向量 u 做了一个加法才进行 softmax,不是很懂,也许和 ResNet 的那个 residual connection 是同样的思想?但是 2015 年 3 月份(这篇论文的发表时间)的时候 ResNet 还没有正式发表吧。

以上就是单层时的情况,多层的时候就用前一层的(未经过 softmax 的)输出作为本层的输入向量 u,即

\[u^{k+1} = o^{k}+u^{k}\]

一图胜千言:

memn2n_multi_layer.png

在多层结构的时候,作者又对 "weight typing" 做了不同的尝试 —— 不知道「weight typing」该怎么翻译,且看下文吧。

可以看到,在上面的多层网络结构中,每一层都有一个 A 和 C,对 A 和 C 的处理,有两种方式:

  1. Adjacent: 将前一层的输出 embedding 矩阵 C 作为本层的输入 embedding 矩阵 A: \(A^{k+1}=C^{k}\)
  2. Layer-wise: 像 RNN 一样,所有输入 embedding 矩阵 A 共享参数: \(A^{1}=A^{2}=...=A^{K}\),输出矩阵 C 同理

在对句子的 encode 上,也有两种不同的处理方式:

  1. BoW: 简单的将句子中每个词的 embedding 累加起来: \(m_{i}=\sum_{j}Ax_{ij}\),u 和 \(c_{i}\) 的计算同理
  2. position encoding: \(m_{i}=\sum_{j}l_{j}\cdot Ax_{ij}\),这里的 \(l_{j}\) 就是所谓的反映了词序的 position encoding,它和后面的值进行 element-wise 的乘法而不是內积,而这个 position encoding 的计算方法是: \(l_{kj}=(1-j/J)-(k/d)(1-2j/J)\),其中 j 是词在句子中的位置,J 是句子的长度,k 是这个词的 vocabulary id,d 是 vocabulary 的大小。

此外就是 Sentences 中的句子之间也是有次序的,为了反映这个次序,又在计算 \(m_{i}\) 和 \(c_{i}\) 时加了一个额外的叫做 temporal encoding 的东西,分别记为 \(T_{A}(i)\) 和 \(T_{C}(i)\):

\[m_{i}=\sum_{j}Axij + T_{A}(i)\]

但是并没有说 \(T_{A}(i)\) 和 \(T_{C}(i)\) 是如何表现的,只是带了一句: Both \(T_{A}\) and \(T_{C}\) are learnd during training。另外,在模型中,句子的次序对真实的次序而言是倒序的。

作者提出这个模型除了用在 QA 上外,还在语言模型上做了实验。在做语言模型时,是没有 question/query 的,所以 u 被设置为一个常量的向量(值都为 0.1);然后每个 \(m_{i}\) 和 \(c_{i}\) 都是对应一个词而不是一个句子了,因为 LM 是 word-level 的问题嘛。

最后总结一下,本文的主要贡献是增强了原始的 Memory Networks,让其称为一个端到端的模型,能更方便地使用 BP 方法进行训练。

就酱!

论文笔记:Decoupled Neural Interfaces using Synthetic Gradients

2018年11月10日 08:00

这篇论文是 DeepMind 在 2016 年 8 月 18 日发布的,最初是在智能单元上看到这个消息的,觉得挺有趣的,就去读了一下。

这篇论文干了什么呢?它在现有的神经网络模型基础上,提出了一种称为 Decoupled Neural Interfaces(后面缩写为 DNI) 的网络层之间的交互方式,用来加速神经网络的训练速度。

作者在开篇指出,在神经网络模型训练中存在几个问题

  • Forward Locking: 前一层没有用输入完成计算,后一层无法进行根据输入进行计算
  • Update Locking: 如果一个网络层依赖的层没有完成前馈计算,该层无法进行更新
  • Backward Locking: 如果一个网络层依赖的层没有完成反向传播,该层无法进行更新

刚看到这说不定会想你这不是废话吗,前一层没计算好后一层怎么计算怎么更新?哎,作者表示我就是不信这个邪我就是要上天和太阳肩并肩啊!为什么呢,因为在一些情况下,神经网络的这种计算模式会带来一些问题,这些情况包括:

  • 由多个异步模块组成的复杂的系统(这个不懂……)
  • 分布式模型,模型的一部分共享给多个下游客户端使用,最慢的客户端将会成为模型更新速度的短板

所以呢,作者就想办法把这种训练时层与层互相依赖的限制干掉咯。

第一个想法,是干掉 Update Locking 和 Backward Locking,让网络的每一层在完成前馈计算后能马上计算出残差,然后立即进行该层的参数更新,而不用等待后一层的前馈计算和误差反向传播。这个是怎么做到的呢?办法就是, 增加一个额外的模型 ,去根据当前网络层的输出去预测其残差,并用预测到的残差去计算梯度,这个实际梯度的估计值,被称为「合成梯度(synthetic gradients)」。

先来看看传统的误差反向传播(Backpropagation, BP)算法,我们假设一个有 N 层的神经网络,其第 i 层的激活值即输出为 \(h_{i}\) ,那么对第 i 层,其参数更新式子为:

\[\begin{array}{rcl} \theta_{i} &\leftarrow& \theta_{i} - \alpha\delta_{i}\frac{\partial h_{i}}{\partial \theta_{i}} \\ &\leftarrow& \theta_{i} - \alpha\delta_{N}\frac{\partial h_{i}}{\partial \theta_{i}}\cdot \prod_{j=0}^{N-i-1}\frac{\partial h_{N-j}}{\partial h_{N-j-1}}\end{array}\]

可以看到,需要依赖后面所有层的激活值的计算。作者的思路就是只利用当前层的激活值 \(h_{i}\) 去得到当前层的残差 \(\delta_{i}\) 的估计值 \(\hat{\delta_{i}}\):

\[\begin{array}{rcl} \delta_{i} &\sim& \hat{\delta_{i}} \\ &=& M_{i+1}(h_{i})\end{array}\]

但是可想而知,在初始的时候,这样一个「额外的模型 M」所给出的残差估计值肯定是与实际的残差有很大的偏差的,那么怎么办呢?办法就是用每一层的实际的残差去训练这个额外的模型 M —— 不过这样不就将 BP 的过程转移到另外一个模型里去了么,这不是耍流氓嘛!不过作者在这里又使用了一个技巧,将 BP 过程彻底干掉了。

我们都知道,所谓误差反向传播,是先计算出最后一层的残差,然后用最后一层的残差去计算倒数第二层的残差,依次类推,故称「误差反向传播」。如果在训练模型 M 时依然遵照这个流程,毫无疑问 Update Locking 和 Backward Locking 依然存在,所以作者在计算每一层的“实际残差”时,用的是后一层的“合成残差”,而合成残差的计算是可以立即计算的。这里实际上又做了一次近似,也就是:

\[\delta_{i} = \hat{\delta}_{i+1}\frac{\partial h_{i+1}}{\partial h_{i}}\]

这样用这个近似的 \(\delta_{i}\) ,去评估模型 M 给出的残差估计值 \(\hat{\delta_{i}}\) ,并用两者之间的误差去更新模型 M。由于最后一层是能得到真正的残差的,所以最后一层的模型 M 能较快得到训练,随着最后一层的模型 M 被训练的越来越好,它所估计出的 \(\hat{\delta_{i}}\) 也能越接近真实的 \(\delta_{i}\) 。如下图所示:

dni_update.png

再进一步地,作者表示,利用 DNI 的思想,去预测每一层的输入也是可以的,这样就把 Forward Locking 也去掉了。基本思想和合成梯度是一样的,不同之处在于预测每一层的输入时只用到第一层也就是输入层的输入:

dni_unlock_all.png

这样 Forward Locking、Update Locking 和 Backward Locking 都被去掉了,通过适当的设计,整个训练可以被很好地并行化、异步化了。

DNI 的思想除了用在前馈神经网络上,也可以用于循环神经网络(Recurrent Neural Network, RNN)的训练上面,因为 RNN 在时间维度上展开后,其实就相当于是一个前馈神经网络了。而且由于应用 DNI 的模型,最多只有两层的网络层依赖,那么在用于 RNN 训练时,可以不用将 RNN 完全展开,而是可以以两个 time step 为最小单元进行展开,即一次只展开两个 time step,这样在存储上的消耗也可以被降低。

rnn_dni.png

来看看作者的牛皮

Although we have explicitly described the application of DNI for communication between layers in feedforward networks, and between recurrent cores in recurrent networks, thereis nothing to restrict the use of DNI for arbitrary network graphs. The same procedure can be applied toany network or collection of networks, any number of times.

以上就是 DNI 的理论部分,实际上还有很多东西没有讲清楚,比如:

  • 那个额外的模型 M 的具体细节?
  • 增加了额外的模型 M ,相当于增加了参数数量,是不是更容易过拟合了?

作者首先在 MNIST 和 CIFAR-10 两个数据集上测试了 DNI 方法和 BP 方法之间的训练效果(只去除 Update Locking 和 Backward Locking)

dni_every_layer.png

需要说明的是,这里用的模型(FCN 和 CNN)层数都不多,实验分别使用了 3-6 的层数,见上图中右侧部分,从该图来看,使用 DNI 的训练方法在训练速度和训练效果上并没有什么优势。这个实验只是表明,使用 DNI ,模型 能够被训练

第二个实验是这样的:对一个四层的前馈网络,以随机的顺序来更新每一层,并且每一层在被选中都是有概率的。在这样的情况下,模型依然是可以被训练的。

dni_sparse_update.png

不过明显能看出来,概率值越大,收敛是越快的,最后在迭代次数达到 50w 次时,不同的概率都达到了接近的精度(2% 的误差),不过一个四层的网络,真的不是过拟合了么……

第三个实验在第二个实验的基础上,加上了 synthetic inputs ,也就是把 Forward Locking 去掉了,从结果上来看,和第二个实验差不多的样子。

dni_sparse_update_without_locking.png

最后在 RNN 上进行了三个实验,分别是:

  • Copy: 读入 N 个字符,然后将这 N 个字符原样输出,有点类似 char-level language model 和 autoencoder。
  • Repeat Copy: 读入 N 个字符,以及一个表示重复次数的数字 R,然后重复输出 R 次这 N 个字符构成的序列。
  • char-level language modeling: (持续地)读取一个字符,并预测下一个字符。

结果如下图所示:

dni_on_rnn.png

上图中 Copy 和 Repeat Copy 两栏中的值表示建模的最大序列长度,越大越好;Penn Treebank 一栏表示语言模型的困惑度(Perplexity,这里用 bits per word 进行度量),越小越好。从实验结果上来看,在 Copy 和 Repeat Copy 任务上,使用 DNI 的模型能建模更长的序列。

论文笔记:Ask Me Anyting: Dynamic Memory Networks for NLP

2018年11月10日 08:00

这篇论文的作者来自一家 AI 公司 MetaMind,因此虽然用了「Memory Network」这个概念,但在思路上和 FAIR 提出 Memory Network 的几个人都很不一样。按我的观点,这篇论文里的模型与其说是 Memory Network,不如说是 encoder-decoder + attention。

这篇论文同样是用来处理 QA 任务的,用的是 FAIR 公开的 bAbI 数据集。这个数据集所描述的场景,其实和 reading comprehension 更接近一点,先会有一段话进行描述,然后给出若干个问题,要求根据前面的描述来寻找答案,下面是一个例子:

I: Jane went to the hallway.
I: Mary walked to the bathroom.
I: Sandra went to the garden.
I: Daniel went back to the garden.
I: Sandra took the milk there.
Q: Where is the milk?
A: garden
I: It started boring, but then it got interesting.
Q: What’s the sentiment?
A: positive
Q: POS tags?
A: PRP VBD JJ , CC RB PRP VBD JJ .

其中以 "I" 开头的句子就是描述性句子,被称为上下文(Context);"Q" 开头的句子则是问题,"A" 开头的句子是回答。在这个场景下,context 有一定的长度,要比 RNN 能处理的信息长度更大,但又是有限的。

与 14 年那提出的 Memory Network 模型和 Neural Turing Maching 模型不同的是,DMN 里没有使用外部的存储空间,而是直接用一个 RNN 的 encoder 对 context 进行编码,将这个过程中输出的 hidden state 作为 memory —— 所以我会认为这个模型更接近 encoder-decoder + attention 而不是 Memory Network 了。

dmn_overview.png

如上图所示,整个模型分为四个模块,和 Memory Network 里的 I/G/R/O 四个模块一一对应,但刚才也说了,其实这个模型和 Memory Network 并不是很像,所以作者为了对应上 Memory Network 的四个模块而作出类比,搞得有点不太好理解。图中的 Episodic Memory 模块并不对应 Memory Network 里的 memory 存储区域,非要说的话,应该是对应 Memory Network 里的 Response 模块,用来做结果的推理也就是 attention。

接下来我还是按我的理解来梳理这篇论文吧。

模型的 Input Module,其实就是一个 encoder,用来将 context 表示成 hidden state 形式的 "memory"。与 Memory Network 不同的是,各个 context 并不是分开处理的,而是串联成一个长的 sequence,然后喂给 RNN,然后只取每个 context 结束时输出的 hidden state。当然也有一些极端情况,比如说只有一个 context 的句子,这种情况下就直接输出每个 time step 的 hidden state 作为 memory。如下图所示:

dmn_input_module.png

然后同样用一个 RNN encoder 对 quesiton 进行处理,不过这时只取这个 encoder 最后一个 time step 的 hidden state,作为这个 question 的表示。

Episodic Memory Module 其实就是一个 attention 模块,用 question 在 Input Module 产生的 memory 上进行 attention。不过与普通的 attention 不同的是,这里的 attention 不是单纯地用 question 和 memory 进行內积算 softmax,而是再用一个 RNN encoder,将 Input Module 产生的 memory 依次输入到这个 encoder 中,并且每次都将 Question Module 的输出作为额外的信息参与计算,然后取这个 encoder 的最后一个 time step 的 hidden state 作为最后 Answer Module 里的 decoder 的 context vector。除了这点不同外,再就是这个过程可能会进行多遍,用每次计算出来的 context vector 输入到一个 decoder 里,用产生的 hidden state 再参与到 attention 中,这个迭代次数是认为设定的,在论文里作者就迭代了三次。如下图所示:

dmn_memory_module.png

最后,将 Episodic Memory Module 的输出作为 context vector 输入到 Answer Module 的 decoder 里去,来生成最终的答案,这块比较简单,如下图所示。

dmn_answer_module.png

比较有意思的是它里面那个 Episodic Memory Module 多遍迭代的结果,作者可视化后发现,第一遍的时候可能找到的是字面上相关的 context 句子,然后后面在迭代会慢慢定位到真正语义相关的 context 句子上,相当于是在做了推理。

dmn_focus.png

此外,作者还把模型用在了情感分析上,对句子中词的 attention 结果,在迭代过程中的变化也展现出了类似的现象。

dmn_focus2.png

以上。

使用 Keras 实现简单的 Sequence to Sequence 模型

2016年5月29日 08:00

Sequence to Sequence Model

Sequence to Sequence 模型是近几年来比较热门的一个基于 RNN 的模型,现在被广泛运用于机器翻译、自动问答系统等领域,并且取得了不错的效果。该模型最早在 2014 年被 Cho 和 Sutskever 先后提出,前者将该模型命名为 "Encoder-Decoder Model",后者将其命名为 "Sequence to Sequence Model",两者有一些细节上的差异,但总体思想大致相同,所以后文不做区分,并简称为 "seq2seq 模型"。

seq2seq 模型利用了 RNN 对时序序列天然的处理能力,试图建立一个能 直接处理变长输入与变长输出 的模型——机器翻译是一个非常好的例子。传统的机器翻译系统当然也能根据变长的输入得到变长的输出,但这种处理能力是通过很多零碎的设置、规则和技巧来达成的,而 seq2seq 模型不一样,它结构简单而自然,对变长输入和变长输出的支持也是简单直接的。来看一下它的结构:

seq2seq.png

上图是一个已经在时间维度上展开(unroll)的 seq2seq 模型,其输入序列是 "ABC" ,其输出序列是 "WXYZ" 。模型由两个 RNN 组成:第一个 RNN 接受输入,并将其表示成一个语义向量,在上面这个例子中,A、B、C 和表示序列结束的特殊符号 <EOS> 依次输入,并在读取到 <EOS> 时终止接收输入,并输出一个向量作为 "ABC" 这个输入向量的语义表示,因此也被称作 "Encoder";第二个 RNN 接受第一个 RNN 产生的输入序列的语义向量,然后从中产生出输出序列,因此也被称作 "Decoder"。

更多的细节见我的两篇论文阅读笔记:

  1. "Sequence to Sequence Learning with Neural Networks" 阅读笔记
  2. "Learning Phrase Representation using RNN Encoder-Decoder" 笔记

Keras 简介

Keras 是一个 Python 的深度学习框架,它提供一些深度学习方法的高层抽象,后端则被设计成可切换式的(目前支持 Theano 和 TensorFlow)。4 月份 Keras 发布了 1.0 版本,意味着 Keras 的基础特性已经基本稳定下来,不用担心其中的方法会发生剧烈的变化了。

(注: 因为发布 1.0 版本时间还比较短,网上的一些例子有不少还使用的旧版本的方法,建议多多查阅文档)

在 Keras 中实现神经网络模型的方法很简单,首先实例化一个 "Sequential" 类型的模型,然后往其中添加不同类型的 layer 就可以了。比如要实现一个三层的 MLP,可以这样:

from keras.models import Sequential
from keras.layers import Dense

model = Sequential()
model.add(Dense(input_dim=4, output_dim=9, activation="relu"))
model.add(Dense(output_dim=3, activation="softmax"))

其中的 "Dense" 是经典的全连接层(在 Caffe 中被称为 InnerProduct)。每个网络层的输入、输出参数和激活函数都可以方便地直接设置。

另外,由于目前使用的后端 Theano 和 TensorFlow 都是基于符号计算的,上述模型定义后只是产生了一个符号计算的图,并不能直接用来训练和识别,所以需要进行一次转换,调用 "Sequential" 的 compile 方法即可:

model.compile(loss="categorical_crossentropy", optimizer="sgd")

compile 时需要设定训练时的目标函数和优化方法,而 Keras 中已经实现了目前常用的目标函数和优化方法,详情见:

  1. Keras Document: Objectives
  2. Keras Document: Optimizers

训练时则要求数据是 Numpy 的多维数组形式,保证这一点的情况下使用 "Sequential" 的 "fit" 方法即可,如:

model.fit(IRIS_X, IRIS_Y, nb_epoch=300)

如果需要进行交叉验证,不用自己手工去分割训练数据,只需要使用 "fit" 方法的 "validation_split" 参数即可,其值应为 0-1 的浮点数,表示将训练数据用于交叉验证的比例,如:

model.fit(IRIS_X, IRIS_Y, nb_epoch=300, validation_split=0.1)

训练过程中会将 loss 打印出来,方便了解模型训练情况:

Epoch 1/100
1024000/1024000 [==============================] - 725s - loss: 0.7274 - val_loss: 0.6810
Epoch 2/100
1024000/1024000 [==============================] - 725s - loss: 0.6770 - val_loss: 0.6711
Epoch 3/100
1024000/1024000 [==============================] - 723s - loss: 0.6723 - val_loss: 0.6680

在 Keras 中也提供模型的持久化方法,通过 "Sequential.to_json" 方法可以将模型结构保存为 json 然后写入到文件中,通过 "Sequential.save_weights" 方法可以直接将模型参数写入文件,结合这两者就可以将一个模型完整地保存下来:

def save_model_to_file(model, struct_file, weights_file):
    # save model structure
    model_struct = model.to_json()
    open(struct_file, 'w').write(model_struct)

    # save model weights
    model.save_weights(weights_file, overwrite=True)

对于保存下来的模型,当然也有对应的方法来进行读取:

from keras.models import model_from_json

def load_model(struct_file, weights_file):
    model = model_from_json(open(struct_file, 'r').read())
    model.compile(loss="categorical_crossentropy", optimizer='sgd')
    model.load_weights(weights_file)

    return model

Pig Latin: Sequence to Sequence 实践

Pig Latin 的一种翻译叫做 “儿童黑话”,罗嗦一点的意译是 “故意颠倒英语字母顺序拼凑而成的黑话”,它有很多种变种规则,这里只取其原始的、最简单的两条规则:

  1. 如果单词起始的字母是 元音 ,那么在单词后面添加一个 'yay'

    'other' -> 'otheryay'
    
  2. 如果单词起始的字母是 辅音 ,那么将起始到第一个元音之前所有的辅音字母从单词首部挪到尾部,然后添加一个 'ay'

    'book' -> 'ookbay'
    

Pig Latin 是一个典型的输入和输出都是变长序列的问题,我们通过它来了解一下基于 Keras 的简单 seq2seq 模型。

首先我们需要准备训练数据,在这个问题中,由于规则是确定的,我们可以来生成大量的数据,如下所示,简单几行代码就可以啦:

from itertools import dropwhile

def is_vowel(char):
    return char in ('a', 'e', 'i', 'o', 'u')

def is_consonant(char):
    return not is_vowel(char)

def pig_latin(word):
    if is_vowel(word[0]):
        return word + 'yay'
    else:
        remain = ''.join(dropwhile(is_consonant, word))
        removed = word[:len(word)-len(remain)]
        return remain + removed + 'ay'

然后需要实现一个简单的 seq2seq 模型

from keras.models import Sequential
from keras.layers.recurrent import LSTM
from keras.layers.wrappers import TimeDistributed
from keras.layers.core import Dense, RepeatVector

def build_model(input_size, max_out_seq_len, hidden_size):
    model = Sequential()
    model.add(LSTM(input_dim=input_size, output_dim=hidden_size, return_sequences=False))
    model.add(Dense(hidden_size, activation="relu"))
    model.add(RepeatVector(max_out_seq_len))
    model.add(LSTM(hidden_size, return_sequences=True))
    model.add(TimeDistributed(Dense(output_dim=input_size, activation="linear")))
    model.compile(loss="mse", optimizer='adam')

    return model

对上述代码作一点解释:

  1. Encoder(即第一个 LSTM) 只在序列结束时输出一个语义向量,所以其 "return_sequences" 参数设置为 "False"
  2. Decoder(即第二个 LSTM) 需要在每一个 time step 都输出,所以其 "return_sequences" 参数设置为 "True"
  3. 使用 "RepeatVector" 将 Encoder 的输出(最后一个 time step)复制 N 份作为 Decoder 的 N 次输入
  4. TimeDistributed 是为了保证 Dense 和 Decoder 之间的一致,可以不用太关心

之所以说是 "简单的 seq2seq 模型",就在于第 3 点其实并不符合两篇论文的模型要求,不过要将 Decoder 的每一个时刻的输出作为下一个时刻的输入,会麻烦很多,所以这里对其进行简化,但用来处理 Pig Latin 这样的简单问题,这种简化问题是不大的。

另外,虽然 seq2seq 模型在理论上是能学习 "变长输入序列-变长输出序列" 的映射关系,但在实际训练中,Keras 的模型要求数据以 Numpy 的多维数组形式传入,这就要求训练数据中每一条数据的大小都必须是一样的。针对这个问题,现在的常规做法是设定一个最大长度,对于长度不足的输入以及输出序列,用特殊的符号进行填充,使所有输入序列的长度保持一致(所有输出序列长度也一致)。

在 Pig Latin 这个问题上,用收集的 2043 个长度在 3-6 的常用英文单词作为原始数据,去除其中包含非 a-z 的字母的单词,最后得到了 2013 个有效单词。然后根据的规则将这些单词改写为 Pig Latin 形式的单词,作为对应的输出。然后在单词前都添加开始符 BEGIN_SYMBOL、在单词后都添加结束符 END_SYMBOL,并用 END_SYMBOL 填充 使其长度为 15 ,并将每一个字符用 one-hot 形式进行向量化(向量化时用于填充的空白符用全 0 向量表示),最后得到大小都为 2013x10x28 的输入和输出。

训练的话,batch_size 设置为 128, 在 100 个 epoch 后模型收敛到了一个令人满意的程度。

在一些训练集之外的数据上,能看到网络给出了正确的结果:

ok -> okyay
sin -> insay
cos -> oscay
master -> astermay
hanting -> antinghay

当然,也有一些反例:

dddodd -> oddddray
mxnet -> inetlay
zzzm -> omhmay

但是无论结果错误程度如何,能看到结果总是以 'ay' 结尾的!

完整代码见: soph/demos/pig_latin.py

❌
❌