“变压器”同学,你有点过分了啊_java


英语不成,大脑不灵,怎么在21世纪前行?还好,翻译“外挂”处处通人情!


近日,一歪果友人求助我帮他翻译一句中文十级“考题”:

“校长说:‘校服上除了校徽别别别的,让你们别别别的别别别的你非得别别的。’”


“变压器”同学,你有点过分了啊_java_02


 我和我的小伙伴们都惊呆了,原来汉语组合还有这种“神操作”。

等我念念通顺先……

 

“变压器”同学,你有点过分了啊_java_03


然而早在半年前,科大讯飞推出的晓译2.0翻译机已经将这句话翻译得妥妥的了。在央视《机智过人》的舞台上,撒贝宁用这句话考验晓译翻译机,没想到人工智能只用一秒钟便“语出惊人”,精准翻译!

 

如何破译“上帝”立下的语言魔咒,实现不同语种之间的无障碍沟通,一直是翻译界乃至全人类的梦想。双语转换中的一些代词、多义词一直是翻译的重难点,也是AI翻译的突破口。早在2015年,斯坦福大学毕业的Spence Green 以及John DeNero就已经创立了一个名为Lilt的AI翻译平台。这款基于变压器神经网络学习的翻译软件,可用于30多种语言,甚至可以将翻译速度提高五倍!


“变压器”同学,你有点过分了啊_java_04

 什么是变压器?


你以为这是电线杆上的“变压器”?NO!


“变压器”是一种不断被应用的神经网络结构。OpenAI、DeepMind等都将这项技术用于自身程序开发。不久前,AlphaGo的亲兄弟AlphaStar,就用“变压器”打败了“星际争霸”的顶级玩家。


变压器的开发是为了解决序列转换问题,或者说神经机器翻译问题。其任务是将输入序列转换为输出序列。这一技术可以实现语音识别、文本到语音的转换等。


“变压器”同学,你有点过分了啊_java_05


转导序列。输入用绿色表示,模型用蓝色表示,输出用紫色表示。


对于执行序列转换的模型来说,有一些记忆是必要的。例如,我们把下面的句子翻译成另一种语言(法语):


“The Transformers”是一个日本[[硬核朋克]]乐队。乐队成立于1968年,当时正值日本音乐史上的鼎盛时期。


在本例中,第二句中的“the band”指的是第一句中引入的“TheTransformers”乐队。当你在第二句中读到这支乐队时,你知道它指的是“TheTransformers”。由于机器无法识别人类语言的具体语境,代词的具体含义往往分辨不清。这正是翻译工作的难点。


要翻译这样的句子,模型需要找出这些依赖关系和连接。复发性神经网络(RNNs)和卷积神经网络(CNNs)由于其各自的特点,一直被用来解决这一问题。现在让我们来回顾一下这两种体系结构及其缺点。


“变压器”同学,你有点过分了啊_java_06

复发性神经网络RNNs


RNNs中有循环,允许信息持续存在。


“变压器”同学,你有点过分了啊_java_07


输入表示为x_t


在上图中,我们看到部分神经网络A,输入xt和输出ht。这一循环允许信息从一个步骤传递到下一个步骤。


我们可以从不同角度看这一循环:一个周期性神经网络可以被看作是同一个网络的多个副本,每个网络将一个消息传递给一个后继网络。思考一下如果我们展开循环会发生什么:


“变压器”同学,你有点过分了啊_java_08

一个展开的RNN


这种链式性质表明,递归神经网络与序列和列表明显相关。这样,如果我们想翻译一些文本,我们可以将每个输入设置为文本中的单词。RNNs将前一个单词的信息传递给下一个可以使用和处理该信息的网络。


下图展示了一个“序列到序列”的模型如何使用RNNs工作。在这个过程中,每个单词都被单独处理,通过将一个隐藏状态传递给解码阶段生成结果句,然后在解码阶段输出结果。


“变压器”同学,你有点过分了啊_java_09

“变压器”同学,你有点过分了啊_java_10

长期依赖的问题


想象一个试图根据前面的单词预测下一个单词的语言模型。如果我们想预测“天空中的云”这句话的下一个单词,我们不需要进一步的语境。很明显,下一个单词是sky。


在这种情况下,相关信息和需要的位置之间的差异很小,RNNs可以学习使用过去的信息,并找出这个句子的下一个单词。


“变压器”同学,你有点过分了啊_java_11


但在某些情况下,我们需要更多的上下文。例如,假设你想预测文章的最后一个单词:“I grew up inFrance…I speak fluent…”最近的信息表明,下一个单词可能是一种语言,但如果我们想缩小范围,我们需要法国的语境,那就在文本的后面。


“变压器”同学,你有点过分了啊_java_12


当相关信息与需要它的点之间的差距变得非常大时,RNNs就会无效。这是因为信息每一步都在传递,链条越长,链条上的信息丢失的可能性越大。


理论上,RNNs可以学习这种长期依赖关系。但实际上似乎并没有学会这些。LSTM作为一种特殊的RNN,试图解决这类问题。


“变压器”同学,你有点过分了啊_java_13

长短时记忆(LSTM)


当安排一天的日程时,我们会优先安排约会。如果有什么重要的事情,我们可以取消一些会议,优先安排更重要的事情。


RNNs不会这样做。每当添加新信息时,它都会通过应用一个函数完全转换现有信息。整个信息都被修改了,并没有考虑什么是更重要的,什么是不重要的。


而LSTMs则通过乘法和加法对信息进行小的修改。使用LSTMs,信息通过“单元状态”的机制流动。在这个过程中,LSTMs可以选择性地记住重要的或忘记不太重要的事情。


LSTM的内部流程如下:


“变压器”同学,你有点过分了啊_java_14


每个单元格将前面的单元格状态和输出作为输入x_t(在句子到句子的转换中是一个单词),它操作这些输入并基于它们生成一个新的单元格状态和一个输出。

 

在单元格状态下,那些对于翻译一个单词很重要的整句的信息可以在翻译时从一个单词传递到另一个单词。


LSTMs的问题


那些发生在RNNs上的问题,同样也发生在LSTMs上,也就是说,当句子太长时,LSTMs也不那么理想。发生这种情况的原因是,将上下文与当前正在处理的单词保持距离的可能性随着距离的增加呈指数递减。


当句子很长时,模型往往会忘记序列中较远位置的内容。RNNs和LSTMs的另一个问题是,处理句子时很难并行化,必须逐词处理。不仅如此,还没有长期和短期依赖关系的模型。综上所述,LSTMs和RNNs存在3个问题:


  • 顺序计算抑制并行化

  • 没有对长期和短期依赖关系进行显式建模

  • 位置之间的“距离”是线性的


“变压器”同学,你有点过分了啊_java_15

Attention

 

为了解决其中的一些问题,研究人员发明了一个关注特定单词的技术。


当人们翻译一个句子时,会特别注意正在翻译的单词。当抄录录音时,译者会仔细听正在记录的片段。如果需要描述所在的房间,译者会一边描述一边环顾四周。


神经网络可以通过 Attention 来实现同样的行为,只关注给定信息的一部分。例如,一个RNN可以处理另一个RNN的输出。在每个时间步长上,它都聚焦于另一个RNN的不同位置。

为了解决这些问题,Attention是一种用于神经网络的技术。对于RNNs来说,不是只将整个句子编码为隐藏状态,而是每个单词都有对应的隐藏状态,并一直传递到解码阶段。然后,在RNN的每一步都使用隐藏状态进行解码。下面的gif展示了这是如何发生的。


“变压器”同学,你有点过分了啊_java_16


绿色的部分称为编码阶段,紫色的部分称为解码阶段。

 

为了使解码更精确,它需要通过Attention考虑输入的每一个单词。因为一个句子中的每个单词中可能都有相关的信息。


为了在序列转导中引起对RNNs的注意,我们将编码和解码分为两个主要步骤,一个步骤用绿色表示,另一个用紫色表示。绿色的步骤称为编码步骤,紫色的步骤称为解码步骤。


“变压器”同学,你有点过分了啊_java_17


绿色的步骤负责在输入中创建隐藏状态。我们没有像在使用Attention之前那样只将一个隐藏状态传递给解码器,而是将句子中每个“单词”产生的所有隐藏状态传递给解码阶段。解码阶段使用每个隐藏状态,找出网络应该注意的地方。


例如,在将“Je suis etudiant”翻译成英语时,要求解码步骤在翻译时查看不同的单词。


“变压器”同学,你有点过分了啊_java_18


这张动图显示了在将“Je suisetudiant”翻译成英语时,每个隐藏状态的权重。颜色越深,代表单词的权重越大。


或者,当翻译“L 'accord sur la zone economique europeenne a etesigne en aout 1992”这句话的时候。从法语译为英语时,代表了每个输入的关注程度。


“变压器”同学,你有点过分了啊_java_19


图例是将“L 'accord sur lazone economique europeenne a ete signe en aout 1992”这句话翻译为英文的过程。


但我们讨论的这些问题仍然没有解决RNNs使用Attention的问题。例如,并行处理输入(单词)是不可能的。对于大量的文本来说,这一过程增加了翻译文本的时间。


“变压器”同学,你有点过分了啊_java_20

卷积神经网络


卷积神经网络有助于解决这些问题,我们可以:


  • 并行化很简单(每层)

  • 利用当地的依赖关系

  • 位置之间的距离是对数的


卷积神经网络是目前最流行的序列转导神经网络,如Wavenet和Bytenet。




Wavenet模型是一个卷积神经网络(CNN)。


卷积神经网络能够并行工作是因为:输入的每个单词都可以同时被处理,并不一定依赖于之前要翻译的单词。不仅如此,CNN的输出单词和任何输入之间的“距离”都是log(N)的顺序——这是从输出到输入生成的树的高度的大小(你可以在上面的图片中看到它)。这比一个RNN的输出和一个N阶输入的距离要好得多。


问题是卷积神经网络并不一定能帮助解决翻译句子依赖关系的问题。这就是为什么变压器被创造出来的原因,它们是两个CNNs与Attention的结合。


“变压器”同学,你有点过分了啊_java_21

变压器


为了解决并行化问题,变压器试图利用卷积神经网络和Attention模型来解决这一问题。Attention提高了模型从一个序列转换到另一个序列的速度。


让我们来看看变压器是如何工作的。变压器是一种使用Attention来提高速度的模型。更具体地说,它使用了Self-Attention。


“变压器”同学,你有点过分了啊_java_22

图例为变压器


在内部,变压器的体系结构与前面的模型类似。但是变压器由六个编码器和六个解码器组成。


“变压器”同学,你有点过分了啊_java_23


这六个编码器之间具有相同的体系结构。解码器亦然。


每个编码器由两层组成:Self-Attention和前馈神经网络(feedForward Neural Network)。


“变压器”同学,你有点过分了啊_java_24


编码器的输入首先流经一个Self-Attention层。它帮助编码器在对特定的单词编码时查看输入语句中的其他单词。解码器具有这两层,但它们之间是一个Attention层,帮助解码器将注意力集中到输入语句的相关部分。


“变压器”同学,你有点过分了啊_java_25


“变压器”同学,你有点过分了啊_java_26

Self-Attention


让我们开始看看各种向量/张量,以及它们如何在这些分量之间流动,从而将一个训练过的模型的输入转换成输出。与一般的NLP应用程序一样,我们首先使用嵌入算法将每个输入单词转换为向量。


“变压器”同学,你有点过分了啊_java_27


每个单词都嵌入到大小为512的向量中。我们用这些简单的框框表示这些向量。


嵌入只发生在最下面的编码器。所有编码器共有的抽象概念是,它们接收一个大小为512的向量列表。


在底部的编码器中是embeddings这个词,但是在其他编码器中,它是在下面的编码器的输出。在我们的输入序列中嵌入单词之后,每个单词都会流经编码器的这两层。


“变压器”同学,你有点过分了啊_java_28


在这里,我们开始看到变压器的一个关键属性,每个位置的单词都会流经它在编码器中的特定路径。在Self-Attention中,这些路径之间存在依赖关系。然而,前馈层没有这些依赖关系,因此可以在流经前馈层时并行执行各种路径。


接下来,我们将把示例转换成一个更短的句子,并查看在编码器的每个子层中发生了什么。


Self-Attention


让我们先来看看如何使用向量来计算Self-Attention,然后再来看看它是如何通过矩阵实现的。


“变压器”同学,你有点过分了啊_java_29


找出一个句子中单词之间的关系,并给予正确的Attention。


计算Self-Attention的第一步是从每个编码器的输入向量中创建三个向量(在本例中,是每个单词的嵌入)。因此,对于每个单词,我们创建一个查询向量、一个关键向量和一个值向量。这些向量是通过将嵌入和我们在训练过程中训练的三个矩阵相乘而得到的。


注意,这些新向量的维数比嵌入向量小。其维数为64,而嵌入和编码器的输入/输出向量维数为512。它们不必更小,只是一种可以使多头Attention的计算基本保持不变的架构选择。


“变压器”同学,你有点过分了啊_java_30


x1乘以WQ权重矩阵得到q1(即与该单词相关的“查询”向量)。我们最终为输入语句中的每个单词创建一个“查询”、一个“键”和一个“值”的投影。


什么是“查询”、“键”和“值”向量?


它们是对计算和思考Attention很有用的抽象概念。当你了解到Attention是如何计算的,你就会知道关于这些向量所扮演的角色。


计算Attention的第二步是计算分数。假设我们在计算这个例子中第一个单词“思考”的Self-Attention。我们需要用这个单词给输入句子中的每个单词打分。当我们在某个位置编码一个单词时,分数决定了我们要把多少注意力放在输入句子的其他部分。


评分是通过查询向量与我们评分的单词的键向量的点积来计算的。如果我们处理位置1的单词的self-attention,第一个分数就是q1和k1的点积。第二个分数是q1和k2的点积。


“变压器”同学,你有点过分了啊_java_31


第三步和第四步是将分数除以8(论文中使用的关键向量的维数的平方根64,这导致了更稳定的梯度,这里可能有其他的值,但这是默认值),然后通过softmax操作传递结果。Softmax将这些分数标准化,使它们都是正的并且和为1。


“变压器”同学,你有点过分了啊_java_32


这个softmax分数决定了每个单词在这个位置的表达量。很明显,这个位置的单词将拥有最高的softmax分数,但有时关注与当前单词相关的另一个单词是有用的。


第五步是将每个值的向量乘以softmax分数(准备对它们求和)。这里的直觉是保持我们想要关注的单词的值不变,以及那些让无关单词的值不变(例如,将它们乘以0.001这样的小数字)。


第六步是对加权值向量求和。这将在此位置生成self-attention层的输出(对于第一个单词)。


“变压器”同学,你有点过分了啊_java_33


这就是Self-Attention计算的结论。得到的向量是一个我们可以发送到前馈神经网络的向量。然而,实际中为了加快处理速度,这种计算是以矩阵的形式进行的。现在我们已经直观地看到了对于单词级别的计算。


多线程的Attention


变压器基本就是这样工作的,当然,还有一些其他细节可以使它们更好地工作。例如,变压器不再只在一个维度上关注彼此,而是使用了多线程的Attention的概念。

其背后的想法是,无论你什么时候翻译一个单词,你可能会根据你所问的问题的类型对每个单词给予不同的注意。下面的图片解释了这意味着什么。例如,当你在“I kick the ball”这个句子中翻译“kick”时,你可以问“Who kick”。根据答案的不同,对这一单词的翻译可能会发生变化。或者问其他问题,比如“做了什么?”等……


“变压器”同学,你有点过分了啊_java_34

“变压器”同学,你有点过分了啊_java_35


“变压器”同学,你有点过分了啊_java_36


位置编码


变压器上的另一个重要步骤是在编码每个单词时添加位置编码。编码每个单词的位置都是相关的,因为每个单词的位置与最终的翻译结果相关。

 

“变压器”同学,你有点过分了啊_java_37

留言 点赞 发个朋友圈

我们一起分享AI学习与发展的干货


编译组:王心玉、陈孔亮

相关链接:

 https://towardsdatascience.com/transformers-141e32e69591

如需转载,请后台留言,遵守转载规范


推荐文章阅读


ACL2018论文集50篇解读

EMNLP2017论文集28篇论文解读

2018年AI三大顶会中国学术成果全链接

ACL2017 论文集:34篇解读干货全在这里

10篇AAAI2017经典论文回顾


长按识别二维码可添加关注

读芯君爱你


“变压器”同学,你有点过分了啊_java_38