记忆网络(译)

笔者:本文为英文论文的翻译文章,供机器学习、深度学习相关学者参考,如有错误理解之处请指出,不胜感激!(如需转载,请联系本人:jtianwen2014,并注明出处)。

记忆网络(译)

Jason Weston, Sumit Chopra & Antoine Bordes.

Facebook AI Research, 770 Broadway, New York, USA.

{jase,spchopra,abordes}@fb.com

摘要

本文将介绍一种新的学习模型,称为记忆网络。记忆网络利用结合了长期记忆模块的推断组件进行推理;并学习如何联合地使用它们。长期记忆模块被用作预测,可以读出也可以写入。我们将记忆网络用于问答任务,长期记忆模块扮演着知识库的角色,记忆网络的输出是文本回复。我们在一个大规模的问答任务上,以及一个从仿真的世界里生成的小规模但是足够复杂的玩具任务(toy task)上进行评测。在本文的末尾,我们通过链乘支持语句到需要理解动词意图的问答来展示这写模型的推理能力。

1 概述

大多数机器学习模型缺乏一种简单的方式从长期记忆模块中读取或写入,并无缝地结合到推理中。所以,这些方法并没有利用现代计算机的卓越的优势之一。举个例子,考虑一个任务,给定一些事实或者故事的集合,然后需要回答相关的问题。理论上这个任务可以通过语言模型来完成,比如循环神经网络(RNN)(Mikolov et al., 2010; Hochreiter & Schmidhuber, 1997) ,因为这些模型被训练得可以对于给定的单词流预测出下一个(或多个)可能的单词(或单词集合)。然而,它们的内存(编码在隐状态和权值)一般很小,并不能准确地记住过去的事实(致使被压缩到稠密的向量中)。众所周知,RNNs很难执行记忆操作,比如简单的拷贝任务即输出刚刚读到的输入(Zaremba & Sutskever, 2014)。同样的情形发生在其他的任务上,比如,在视频和音频领域,观看影片需要长期记忆以回答关于这个影片的问题。

在本文的工作中,我们介绍一种叫做记忆网络的模型,尝试修复以上提到的问题。核心思想就是将在机器学习中用于推理的有效学习策略和可以读写的内存模块相结合。然后训练模型,使之可以高效的操作内存内存模块。我们在第2部分介绍整体的框架,在第3部分相大家展示在文本问答领域的具体实现。我们在第4部分讨论相关工作,然后再第5部分介绍实验部分,最后在第6部分作出总结。

2 记忆网络

一个记忆网络由1个内存模块\(\mathbf{m}\)(用\(m_i\)索引的数组)和如下的4个(通过学习得到的)模块\(I,G,O,R\)

\(\mathbf{I}\):(输入特征映射)—— 将输入转换为内部特征表示。

\(\mathbf{G}\):(泛化)—— 对于给定新的输入更新旧的内存。我们称之为泛化是因为在这个阶段网络有机会压缩并泛化其内存以供未来某些需要。

\(\mathbf{O}\):(输出特征映射)—— 给定新的输入与当前的内存状态,产生新的输出(在特征空间中)。

\(\mathbf{R}\):(回复)—— 将输出转换为特定格式的回复。比如,文本回复或者一个动作。

给定输入\(x\)(比如,一个输入字符,单词或者句子,这依赖于粒度的选择,一幅图片或者一段音频信号),模型执行如下的流程:

  1. \(x\)转换为内部特征表示\(I(x)\)
  2. 对于给定的新的输入,更新内存\(\mathbf{m}_i\)\(\mathbf{m}_i=G(\mathbf{m}_i,I(x),\mathbf{m}),\forall i\)
  3. 对于给定的新的输入和内存,计算输出特征\(o\)\(o=O(I(x),\mathbf{m})\)
  4. 最后,将输出\(o\)解码为最后的回复:\(r=R(o)\)

训练和测试阶段都需要这个过程,这两个阶段的差别在于测试阶段不更新\(I,G,O,R\)的参数。记忆网络可以用于很多任务。其组件\(I,G,O,R\)可以使用很多目前存在的机器学习策略,如,SVMs、决策树、等等。

\(I\)组件:组件\(I\)可以利用一些通用的预处理过程,如,为文本输入进行句法分析、指代和实体消解。它也可以将输入编码到一个内部特征表示,比如,将文本转换到稀疏或稠密的特征向量。

\(G\)组件:\(G\)最简单的形式是将\(I(x)\)存储在内存的‘槽’中:

\[\mathbf{m}_H(x) = I(x) \tag{1} \]

其中\(H(.)\)是槽选择函数。也就是,\(G\)只更新内存\(\mathbf{m}\)中索引为\(H(x)\)的部分,而不去触碰其他部分。\(G\)的更复杂的变种可以基于当前输入\(x\)所获的的信息来回退并更新早期存储的内存(或者全部内存)。如果输入是字符级别或者可以词级别,那么可以将其进行分组(也就是,将其分块化)并将每块存储到一个内存槽中。

如果需要的内存巨大(比如,对于Freebase或者Wikidata),那么我们需要对其进行组织。这可以通过之前介绍的槽选择函数来实现:比如,可以对其设计或者训练使之可以按照实体或者主题存储记忆。因此,为了提高扩展的效率,\(G\)(以及\(O\))不需要在所有内存上进行操作:可以只对检索到的候选子集进行操作(只在相关主题的记忆上进行操作)。我们将在后续的实验中介绍一个简单的变种。

如果内存满了,\(H\)可以通过选择替换哪个内存来进行“忘记”处理,比如,\(H\)可以对每个记忆的价值进行打分,覆盖掉最没有价值的部分。我们还未开展这个实验。

\(O\)\(R\)组件:组件\(O\)通常负责从内存中读取并执行推理,比如,计算哪个是可以获得优质回复的相关内存。组件\(R\)对于给定的\(O\)产生最后的回复。举个例子,在问答任务上,阶段\(O\)找到相关的内存,然后由\(R\)产生实际的回答词语,比如\(R\)可能是个以\(O\)为输入的RNN。我们的假设是,如果没有这些记忆作为条件,这样的RNN无法很好执行。

3 为文本实现记忆网络

有一种特别的记忆网络,其组件都是神经网络。我们称其为记忆神经网络。在本节中,我们描述具有文本输入和输出的记忆神经网络的一个相对简单实现。

3.1 基础模型

在基础架构中,\(I\)模块以文本为输入。我们首先假设这是一个句子:也可以是一个事实的陈述,或者需要系统回答的问题(稍后我们基于单词的输入序列)。这个文本以原始的形式存储在下一个可用的内存槽中,也就是\(S(x)\)返回下一个空的内存槽\(N\)\(\mathbf{m}_N=x,N=N+1\)\(G\)模块只用来存储这个新的记忆,旧的内存不被更新。更加复杂的模型将在后续的章节中叙述。

推理的核心模块位于\(O\)\(R\)模块。对于给定\(x\),模块\(O\)通过查找\(k\)个支持记忆来产生输出特征。我们将\(k\)设为2,但是这个过程适用于更大的\(k\)。对于\(k=1\),通过如下方式检索出得分最高的支持记忆:

\[o_1 = O_1(x,\mathbf{m})=\mathop{\arg\max}_{i=1,\cdots,N}s_O(x,\mathbf{m}_i) \tag{2} \]

其中,\(s_O\)\(x\)\(\mathbf{m}_i\)的匹配程度进行打分的函数。对于\(k=2\),我们可以利用迭代的方式发现第二个支持记忆:

\[o_2 = O_2(x,\mathbf{m})=\mathop{\arg\max}_{i=1,\cdots,N}s_O([x,\mathbf{m}_{o_1}],\mathbf{m}_i) \tag{3} \]

其中候选支持记忆\(\mathbf{m}_i\)现在需要基于初始输入和第一个支持记忆通过打分,方括号代表列表。最终的输出\(o\)\([x,\mathbf{m}_{o_1},\mathbf{m}_{o_2}]\),作为模块\(R\)的输入。

最后,\(R\)需要产生一个文本回复\(r\)。最简单的回复就是返回\(\mathbf{m}_{o_k}\),也就是输出刚刚检索到的句子。为了进行真实语句的生成,可以利用RNN来代替。在我们的实验中,我们也考虑使用一种简单的方式来评估方法,即我们通过排序的方式将文本回复限定为一个单词:

\[r=\mathrm{argmax}_{w\in W}s_R([x,\mathbf{m}_{o_1},\mathbf{m}_{o_2}],w) \tag{4} \]

其中\(W\)是字典中所有单词的集合,\(s_R\)是对匹配进行打分的函数。

图1给出了一个任务的例子。未来回答问题\(x=\)“Where is the milk now?”,模块\(O\)需要首先对所有内存进行打分,也就是所有之前见过的语句,以\(x\)为背景进行检索最相关的事实,在这个例子中\(\mathbf{m}_{o_1}=\)“Joe left the milk”。然后在给定\([x,\mathbf{m}_{o_1}]\)的情况下,再一次搜索第二个相关事实,也就是\(\mathbf{m}_{o_2}=\)“Joe travelled to the office”(在Joe放下牛奶之前去过的最后一个地方)。最后,模块\(R\)对给定的\([x,\mathbf{m}_{o_1},\mathbf{m}_{o_2}]\)利用公式(4)对单词进行打分,输出\(r=\)“office”。

图 1:

在我们的实验中,打分函数\(s_O\)\(s_R\)具有相同的形式,一个嵌入表示模型:

\[s(x,y) = \Phi_x(x)^{\top}U^{\top}U\Phi_y(y) \tag{5} \]

其中,\(U\)是一个\(n\times D\)矩阵,\(D\)是特征的数量以及\(n\)是嵌入表示的维度。\(\Phi_x\)\(\Phi_y\)是用于将原始文本映射到\(D\)维的特征空间里。特征空间最简单的选择是词袋表示,对于\(s_O\)我们选择\(D=3|W|\),也就是词典中的每个单词有三个不同的表示:一个用于\(\Phi_y(.)\),另外两个用于\(\Phi_x(.)\),依赖于输入的单词来自于实际输入\(x\)还是支持记忆,以使得它们可以不同建模。类似地,对于\(s_R\)也使用\(D=3|W|\)。这两个打分函数使用不同的矩阵\(U_O\)\(U_R\)

训练 我们在完全有监督的设置下训练,在训练数据中给定应有的输入和回答,以及被标记的支持这个回答的句子(但在测试集中只有输入)。也就是在训练时我们知道eq.(2)和eq.(3)的最佳选择。训练使用一个边际排序损失和随机梯度下降(SGD)来进行。更确切点说,对于给定真实回复\(r\)的问题\(x\),以及支持的语句\(\mathbf{m}_{o_1},\mathbf{m}_{o_2}\)(当\(k=2\)时),我们在参数\(U_O\)\(U_R\)之上优化模型:

\[\sum_{\overline{f}\ne \mathbf{m}_{o_1}}\mathrm{max}(0, \gamma-s_O(x,\mathbf{m}_{o_1})+s_O(x,\overline{f}))+ \tag{6} \]

\[\sum_{\overline{f'}\ne \mathbf{m}_{o_2}}\mathrm{max}(0, \gamma-s_O([x,\mathbf{m}_{o_1}], \mathbf{m}_{o_2})+s_O([x,\mathbf{m}_{o_1}],\overline{f'}))+ \tag{7} \]

\[\sum_{\overline{r}\ne r}\mathrm{max}(0, \gamma-s_R([x,\mathbf{m}_{o_1}, \mathbf{m}_{o_2}], r)+s_O([x,\mathbf{m}_{o_1}, \mathbf{m}_{o_2}], \overline{r}))+ \tag{8} \]

其中\(\overline{f},\overline{f'}\)以及\(\overline{r}\)都是非正确的标签选择,是\(\gamma\)边际。在SGD的每一步,我们采样选取\(\overline{f},\overline{f'},\overline{r}\)而不是为每个训练样例计算所有的总和,这和Weston et al.(2011)的做法相同。

当我们使用RNN来实现记忆神经网络的\(R\)模块时(而不是像上面那样用单词回复),我们将最后一项替换为语言建模任务中使用的标准对数似然函数,其中RNN的输入是序列\([x,o_1,o_2,r]\)。在测试的时候,在给定\([x,o_1,o_2]\)的情况下输出\(r\)。对比最简单的模型,即使用\(k=1\)并且输出\(\mathbf{m}_{o_1}\)的记忆作为回复\(r\),将只使用第一项用来训练。

接下来的部分,我们将考察一些基于基础模型的扩展。

3.2 词序列作为输入

如果输入是词而非句子级别,即词以流的形式到达(就像RNN经常处理的那样)并且陈述和问题都没有进行词分割,我们需要修改下之前描述的方法。所以我们加入“分割”函数,将会通过学习得到,输入单词的最后没有分割的序列以寻找分割点。当分割器触发时(指示当前序列时分割段)我们将序列写入内存,然后就可以像以前一样处理。这个分割器的建模方式和我们其他的模块类似,以嵌入表示模型的形式:

\[seg(c) = W^{\top}_{seg}U_S\Phi_{seg}(c) \tag{9} \]

其中\(W_{seg}\)是一个向量(实际上是嵌入空间里的线性分类器的参数),\(c\)是用词袋表示的词序列。如果\(seg(c)>\gamma\),其中\(\gamma\)是边际,那么这个序列被视为一个分割。通过这种方式,我们的记忆神经网络在写操作时拥有一个学习模块。我们将这个分割器视为第一个概念证明:当然,可以设计得更加复杂。训练时进一步的细节将在附录B中展现。

3.3 利用散列表的高效记忆

如果存储的记忆集合十分庞大,那么eq.2和eq.3中对所有记忆进行打分的代价将十分巨大。我们使用散列的技巧来加速查找:将输入\(I(x)\)散列到一个或多个桶中,然后只对相同桶里的记忆\(\mathbf{m}_i\)进行打分。我们考察两种散列的方式:(i)散列词语;以及(ii)对词嵌入表示进行聚类。对于方法(i)我们构造和词典中词语数量相同的桶,然后对于给定的语句我们将其散列到所包含词语的对应的桶中。(i)的问题在于记忆\(\mathbf{m}_i\)只有当和\(I(x)\)至少一个相同的词语时才被考虑。方法(ii)试图通过聚类解决这个问题。在训练嵌入式矩阵\(U_O\)之后,我们使用K-近邻进行聚类词向量\((U_O)_i\),所以给出了K个桶。然后我们将语句散列到所有包含词语落入的桶中。因为词向量会和其同义词相接近从而彼此聚类到一起,所以我们将会对这些相似的记忆进行打分。输入和记忆之间匹配的词语都会按照定义被打分。选择\(K\)控制速度准确权衡。

3.4 建模写入时间

我们可以将何时一个内存被写入加入到我们的模型中。这对于回答固定事实的问题(“What is the capital of France?”)并不重要,但当回答一个如图1的故事相关的问题时却是重要的。对于给定的\(\mathbf{m}_j\),实现这个的一种明显方式就是向表示\(\Phi_x\)\(\Phi_y\)中加入编码\(j\)的额外特征,假设\(j\)遵循写时间(也就是,没有内存槽被重写)。然而,需要处理的事绝对时间而非相对时间。我们对以下过程有着更多的成功经验:代替打分输入,候选对利用上述的\(s\)学习一个定义在三元组上的函数\(s_{O_t}(x,y,y')\)

\[s_{O_t}(x,y,y') = \Phi_x(x)^{\top}U_{O_t}^{\top}U_{O_t}(\Phi_y(y)-\Phi_y(y')+\Phi_t(x,y,y')) \tag{10} \]

\(\Phi_t(x,y,y')\)使用三个新的特征,取值0或1:是否\(x\)\(y\)更加久,\(x\)\(y‘\)更加久,\(y\)\(y’\)更加久。(也就是,我们将所有的\(\Phi\)维度扩展3位,如果不适用的时候都设置位0)现在,如果\(\Phi_t(x,y,y')>0\),模型更偏于选择\(y\),如果\(\Phi_t(x,y,y')<0\),模型更偏于选择\(y'\)。eq.2和eq.3中的argmax替换为在\(i=1,\cdots,N\)的循环,在每步中持续赢取内存记忆(\(y\)或者\(y'\)),即总是比较当前赢得的内存和下一个内存\(\mathbf{m}_i\)。如果把时间特征移走,这个过程等价于之前的argmax。更多的细节将在附录C中讨论。

3.5 建模生词

即使对于阅读量很大的人类来说,生词也是经常遇到。比如指环王中的单词“Boromir”。一个机器学习模型如何来处理这个问题?一个可能的方式是使用语言模型:通过生词周围的词语,来预测可能的单词,并假设这个生词和预测的单词语义相似。我们提出的方法正是采用这个想法,但是我们将其融入到我们的网络\(s_O\)\(s_R\)而不是作为一个单独的步骤。

具体地,对于我们看到的单词,我们将其周围词保存在词袋中,一个词袋保存左侧的词语,另一个保存右侧的词语。任何未知的词语都可以这样表示。所以我们将特征表示\(D\)\(3|W|\)扩展到\(5|W|\)来对这些上下文进行建模(每个词袋有\(|W|\)个特征)。我们的模型在训练阶段使用一种“dropout”技术来处理新的单词:\(d\%\)的时间我们装作从未读取过任何词语,不使用\(n\)维嵌入表示这个词语,而是用它的上下文来代替。

3.6 精准匹配和生词

由于低维度\(n\)嵌入式模型无法进行精准的词汇匹配。一个解决方案是对词对\(x,y\)进行如下打分来替代之前的:

\[\Phi_x(x)^{\top}U^{\top}U\Phi_y(y) + \lambda\Phi_x(x)^{\top}\Phi_y(y) \tag{11} \]

也就是,向学习到的嵌入式分数加入“词袋”的匹配(使用一个混合参数\lambda)。另一个,相关的方法还是在这个\(n-\)维的嵌入式空间里,但是为特征表示\(D\)扩展更多的匹配特征,比如,一个词汇。一个匹配的特征意味着这个词在\(x\)\(y\)中都有出现。也就是,我们使用\(\Phi_x(x)^{\top}U^{\top}U\Phi_y(y,x)\)进行打分,其中\(\Phi_y\)实际建立在\(x\)基础上:如果\(y\)中的词语与\(x\)中的一次匹配,我们就将这些匹配特征设置为1。未登录词可以使用相似的方法建模,在上下文词汇中使用匹配特征。最后,\(D=8|W|\)

posted @ 2017-10-21 15:02  天文-HITer  阅读(2690)  评论(0编辑  收藏  举报