CHRIS OLAH,SHAN CARTER 译:Krse Lee
原文:http://distill.pub/2016/augmented-rnns
译者序:
本文重点讲述的是一种被称为attention的方法,有的人将其译为“聚焦”,但觉得这种翻译将原文对神经网络拟人化的手法给扔掉了,因此保留了原来的称谓。Attention,顾名思义,就是让神经网络对部分内容引起注意,而忽略其他不重要的内容。目前我看到过这篇文章的两种译文版本,但都有不少缺陷,或是残缺,或是表述不清难以理解,因此汲取两个版本的优点对原文重新进行翻译。
Attention方法的通用的技巧是将原本不可微的离散数据结合一个attention分布变得可微,从而使得离散数据也可以作为神经网络的输入向量,本文阐述了attention方法的四个应用方向:神经图灵机、attentional接口、自适应计算时间模型、神经编程器,四种模型在应用attention方法时都有相似点和不同点,这也是这些方法的奇妙之处。这些增强的RNN模型在未来或许会有非常广阔的应用空间,值得大家细细品读。
另外英文原文中作者精心制作了用户可操作的动图帮助理解,译文中无法直接引用,所以需要的朋友请跳转到原文中自己体会。
============华丽的分割线==============
循环神经网络(Recurrent Neural Networks)是深度学习中的一个重要结构,它让神经网络能够作用于序列化的数据,比如文本、音频和视频。它们可以将一个序列浓缩为一个高度理解的模型、进行序列标注以及通过模型生成一个新的序列。
基础的RNN常常在长序列问题面前艰难挣扎,因为有“记忆消失”现象,但是有一个特殊变种LSTM(长短记忆网络)可以克服。这种模型被证明是十分强大的,在翻译、语音识别、图像捕捉等很多任务中取得了卓越的成果。因此,循环神经网络在过去的几年中被运用得非常广泛。
另一方面,可以看到近年来给RNN增加新特性的尝试在逐渐增多,主要有以下四个方向:
神经图灵机:拥有额外的记忆模块用来读或写。
Attentional接口:允许RNN对部分输入集中注意力。
自适应计算时间:允许在每个计算时间步骤中处理不同的计算量。
神经编码器:可以调用方法,构建程序。
单独来讲,这些技术都有效地扩展了RNN,但是真正引人注意的是它们可以结合在一块儿,就像是更广阔的空间中点的聚合。更进一步地说,它们依赖同一种底层技巧进行工作,这种技巧叫做attention。
我们猜测这些增强RNN将在未来几年扩展深度学习领域扮演十分重要的角色。
神经图灵机(Graves,et al.,2014)将一个RNN和一个额外的记忆模块相结合。对于神经网络,向量就是它们的自然语言,因此这个记忆模块是存放向量的数组。
那么,图灵机的Reading和Writing操作是怎么工作的呢?这里最大的挑战是我们想让这两种方法可微。通常地,可微指我们希望它们知道从哪儿读或者写到哪里去,因此需要学习从什么地方读或写。这个事情比较棘手,因为存储地址是几乎离散(不可微)的。NTMs采用了一个非常聪明的方法来解决这个问题:每一步中,它们从每一个位置读或写,只是程度不同。
举一个例子让我们关注Reading过程。RNN给出了attention 分布,这个分布描述了我们在传递过程中所关注的不同记忆位置的量的权重大小,而不是从一个特定的位置取得的量。这样,读操作的结果就是一个权重累加和。
相似的,Writing时我们对每个位置都不同程度地进行写操作。这里的attention distribution描述的是每一个位置的写入量。在这个操作中我们得到的一个位置的新值是旧的记忆内容和要写入的值的一个“凸组合”,这两者的位置由attention权重决定。
NTMs怎样确定对记忆模块中哪一个位置集中注意呢?事实上,它们结合了两种方法:基于内容的attention和基于位置的attention。基于内容的attention允许NTMs在记忆模块中搜索并关注相匹配的位置;而基于位置的attention允许在存储中进行相对运动,使得NTMs可以循环。
读和写的特性使得NTMs可以演算很多简单的算法,超越的先前的神经网络。举个例子,它们可以学习将一个长序列存储在存储单元中,然后循环它,反反复复。在它们做这件事的时候,我们可以观察它们读写的位置来更好的理解他们在做什么。
它们也能够学习模仿一个lookup table,甚至学习对数字进行排序(尽管它们有点欺骗的味道)!但在另一方面,仍然有一些它们无法完成的基本任务,比如数字相加或相乘。
从原始的NTM论文开始,不乏一些令人激动的论文介绍了相似的研究方向。The Neural GPU (Kaiser &Sutskever, 2015) 克服了NTM不能对数字进行相加或相乘的缺点。 Zaremba &Sutskever, 2016 训练NTMs 用来强化学习,以替代原始的可区分读或写。Neural Random Access Machines (Kurach et al., 2015)是基于指针进行工作的。一些论文阐释了使用不同的数据结构,比如栈和队列(Grefenstette etal. 2015; Joulin &Mikolov, 2015). 还有memorynetworks (Weston et al., 2014; Kumar et al., 2015) 是另一种解决相似问题的方法。
就目的性来说,这些模型能够完成上述任务,比如学习如何对数字相加,并且客观上讲这些任务并不难,传统的程序解决这些问题就像吃午餐一样容易。但是神经网络能做很多其他事情,像神经图灵机这样的模型似乎已经在它们的能力上有了深远的突破。
代码
对于这些模型有很多开源的实现。神经图灵机的实现包括 Taehoon Kim’s(TensorFlow), Shawn Tan’s (Theano), Fumin’s (Go), Kai Sheng Tai’s (Torch),以及Snip’s (Lasagne)。Neural GPU公开代码放在了 TensorFlowModels repository。Memory networks的开源实现包括 Facebook’s (Torch/Matlab), YerevaNN’s (Theano),以及Taehoon Kim’s (TensorFlow)。
当我要翻译一个句子,我会特别注意正在翻译的词;当我誊写一段录音时,我会仔细听我正要写下来的段落;如果你让我描述我所在的房间,那么我会瞥一眼我正要描述的物品。
使用attention方法能让神经网络作出相似的表现——对喂给它们信息的一个子集集中注意。例如,一个RNN能将另一个RNN的输出作为输入,在每一个时间步骤,它会关注另一个RNN的不同位置的输出。
我们希望attention是可微的,这样能够学习在什么位置集中注意力。为了做到这一步,我们使用和神经图灵机一样的技巧:对每一个位置都表示关注,只是程度不同。
这种attention RNN会生成一个query来描述它想要关注的位置,每一个元素同query点乘产生一个评分,描述元素和query匹配的契合度。这个评分会喂给一个softmax来创建attention分布。
RNNs中attention的一个用途就是翻译 (Bahdanau, et al. 2014)。传统的序列到序列模型不得不将整个输入浓缩为一个向量,然后再将它展开回去。Attention方法避免了这一点,允许下层RNN处理输入,然后生成相应的输出,传递它看到的每个词的信息。输出聚焦在与输入单词相关的一组词上(这样聚焦的结果作为上层RNN的输入)。
这种RNN间的attention有很多其他应用,比如语音识别 (Chan, etal. 2015),让一个RNN处理音频,另一个RNN在它上面滑动,关注attention操作后的相关部分。
这种attention的其他应用包括文本分析 (Vinyals, et al., 2014),它允许模型在生成语法树时去瞥一眼单词;会话模型 (Vinyals &Le, 2015),让模型在生成响应内容时关注之前的会话内容。
Attention也能够用在卷积神经网络和RNN之间的接口上。让RNN在每一步中关注图片的不同位置。首先,用一个卷积网络处理图像,抽取出高层次特征;然后运行一个RNN来生成图片的描述。在RNN生成描述的每一个单词时,它关注卷积网络对相关部分的解释。我们可以将这些工作进行可视化:
更广泛的,只要一个神经网络的输出中有重复结构,就能使用attentional接口连接该神经网络。
Attentional接口被证明是一种具有普适性并且十分强大的技术,正被越来越广泛地使用。
标准的RNN在每一个时间步骤中所做的计算量是相同的,这似乎跟我们的直观感觉不符,应对困难的问题难道不应该花更多的时间吗?这也限制了对长度为n的list,RNN进行的操作数目的上限为O(n)。
自适应计算时间模型 (Graves, 2016)是一种让RNN在每个时间步骤里进行不同数量计算的一种方法。其思想非常简单:允许RNN在一个时间步骤中进行多个步骤的操作。
为了让网络学习该进行多少步计算,我们希望每一次计算步骤数目是可微的。通过使用和之前相同的技巧来达到这一点:在步骤数目之上加了一个attention分布,而不是直接使用离散的数字决定运行的步骤数目。那么最终输出就是每一步输出的权重组合。
上图省略了一些细节,下图是三个完整时间步骤的完整过程。
看起来有些复杂,那么让我们一步一步的来运行这个过程吧。从高层看,我们仍然在运行RNN,并且输出状态的加权组合。
每一步的权重由一个“停止神经元”决定,这是一个sigmoid神经元,它用来监控RNN的状态并给出一个停止权重,我们可以把这个权重看成是这一步应该停止的概率。
我们有一个初始为1的预算,跟着下图上方的预算执行轨迹减去停止权重,当它小于阈值epsilon时,停止。
当我们停止时,可能还剩余一些预算,因为当预算小于epsilon就停止了,这些预算怎么处理呢?技术上,它会被留给未来的步骤,但我们并不想进行这些运算,因此它被分给了最后一步。
我们训练自适应时间模型时,给损失函数加上了一个“思考成本”,用来惩罚模型所使用的计算量。你把“思考成本”设置得越大,模型就会在降低计算时间上作出更多的权衡。
自适应计算时间是一种非常新的想法,我们相信伴随着相似的想法,它会变得更加重要。
代码
目前为止,貌似只有 MarkNeumann’s (TensorFlow)的自适应计算时间模型代码是开源的。
神经网络在处理很多任务上都表现得非常出色,但在一些基础的问题上仍然有很大的困难,例如算术,而这些问题用常规方法计算往往又是很轻松的。因此,将神经网络与常规程序相融合可能会是一个很好的方法,并且在两者的领域都能做得更好。
神经编程器(Neelakantan, et al., 2015) 是其中一种融合方法,它为了解决问题而学习如何创造程序。事实上,它学习生成这样的程序是不需要正确程序例子的,他会自己发现如何生产程序来完成某些任务。
论文中实际的模型通过生成类似于SQL语句的程序查询数据库表,从而回答关于数据库表的问题。然而有很多细节使得这个问题有一点复杂,所以让我们从想象一个更稍微简单的模型作为开始吧,给定一个算术表达式,生成一个程序来验证它。
生成的程序是一系列操作组成的序列,每一个操作定义了如何处理以前操作的输出,因此,一个操作可能是“将上两步操作的输出相加”。比起可以进行变量赋值和读取的程序,这可能更像是Unix中的pipe。
作为控制器的RNN每次生成程序的一个操作,在每一步中,控制器RNN输出下一个操作可能性的概率分布。例如,我们比较确信第一个时间步骤进行加法操作,接着在第二个时间步骤中难以决定是做乘法还是做除法,等等。
现在可以验证这些操作的结果分布了。我们使用attention技巧运行所有的操作,并对输出一起取加权平均值,而不是每一步运行一个单一的操作,权重是每一步运行各个操作的概率(即之前每一步输出的概率分布)。
只要能够对这些操作进行衍生,基于概率的程序输出就会变的多样。我们可以定义一个损失,然后给出正确值训练神经网络生成程序。按这种方法,神经编程器就不需要通过学习正确的程序样例来生成程序了,唯一的监督是程序应该输出的正确答案。
这就是神经编程器的核心思想,但是这篇论文中的版本是回答关于数据库表的问题,而不是算术表达式。这里有一些额外的处理技巧:
l 多种类型处理:这个神经编程器中的很多操作处理的不是标量数,而是类型。一些操作输出的是选中的表的列或者单元,只有当输出类型一致时才会合并。
l 引用输入:神经编程器需要回答像“有多少城市的人口大于1,000,000”的问题时,给出了带有人口数量列信息的城市列表。为了达到这一步,一些操作允许网络引用当前需要回答的问题中的常量,或者列的名称。在指针网络(Vinyals, etal., 2015)中,引用随着attention发生。
神经编程器并不是唯一让神经网络生成程序的方法,另一种更可爱的方法是神经编程-解释器(Reed & de Freitas, 2015) ,它完成了一些很有趣的任务,但是需要正确程序进行有监督训练。
我们觉得在传统程序和神经网络之间的鸿沟上搭建桥梁是非常重要的,尽管神经编程器肯定不是最终的方法,依然可以从它身上学到很多重要的东西。
代码
神经编程器目前没有看到开源实现,但是有神经编程-解释器的开源实现,作者是Ken Morishita (Keras)。
从某种意义上说,身边有一张纸的人会比没有的人聪明很多;懂得数学符号的人可以解决很多其他人不能解决的问题;与计算机接触使我们能够获得难以置信的专长,将其他人远远甩在后面。
一般来说,很多有意思的智慧表现形式是富有创造性的人类直觉和清脆细致的媒介相互作用的结果,比如语言和方程。有时候,这种媒介是物质存在,为我们存储信息,防止我们犯错误,或者承担繁重的计算任务。其他情况下,媒介是我们脑袋中的模型。无论哪种,似乎都是智慧的基础。
最近的机器学习成果开始有了这种味道,将神经网络的直觉与其他东西相结合。其中一种方法叫做“启发式搜索”,例如AlphaGo(Silver, etal., 2016) 有一个模型,是在神经网络指导下让Go工作并探索如何进行游戏。相似的,DeepMath(Alemi, etal., 2016) 使用神经网络作为思维来操纵数学表达式。本文介绍的“增强RNN”是另外一种方法,它将RNN与工程媒介相连接,目的是为了拓展它们的泛化能力。
同媒介交互自然包括了产生一个由采取行动、观察以及采取更多动作的序列。这样就有一个重大的挑战:我们怎么学习采取什么样的动作呢?这听起来像是一个强化学习问题,我们当然可以采用强化学习的方法。但是强化学习克服的是这个问题最困难的版本,这种方法也难以使用。Attention方法最好的一点就是对这个问题它给出了一种更简单的解决方法——通过程度的不同从所用动作中只选择部分。我们能设计媒介,比如NTM记忆模块,使得动作成为浮点值并可微,正是由于将离散量变得可微,Attention方法才可行。而强化学习让我们面前只有一条单一的路径,并只能从这条路径去学习(采取什么样的动作),Attention方法在分叉路口沿着每个方向前进,然后对每条路径的结果进行汇总。
Attention方法的主要缺陷是我们在每一步中采取了所有的动作,这使得在像NTM增加记忆单元时,开销呈线性地增长。可以想到的一种做法是让attention稀疏化,这样就只需要接触部分记忆单元。然而,这里面仍然有挑战,因为你可能想要关注记忆模块的内容,这使得必须去查看每个记忆单元。目前已经有一些初步的尝试去解决这个问题,比如 Andrychowicz& Kurach, 2016,但仍有很多工作待进行。如果真的出现了子线性时间内的attention方法,那将非常强大!
增强循环神经网络,以及其依赖的attention技术是非常令人激动的。我们期待看到更好的未来。