Shreya Gherani:BERT庖丁解牛(Neo Yan翻译)

BERT是双向转换器(Bi-Transformer)的缩写。这是谷歌在2018年末开发并发布的一种新型语言模型。BERT等经过预处理的语言模型在问答、命名实体识别、自然语言推理、文本分类等自然语言处理任务中发挥着重要作用。

BERT是多层的双向转换器堆叠,编码机制只要微调就可以运作,文章一开始有必要回顾下Transformer的架构。

Transformer的前世今生

2017年,谷歌发表了一篇题为《Attention is all your need》的论文,该论文提出了一种基于注意力的结构来处理机器翻译相关序列模型相关的问题。传统的神经机器翻译大多采用循环神经网络(RNN)或卷积神经网络(CNN)作为编解码器的元模型。然而,谷歌基于注意力的Transformer编解码模型跳出了传统的RNN和CNN范式。该模型具有非常高的并行性,在提高翻译性能的同时,训练速度也挺快。

让我们把时光倒流几年,回到attention机制真正起源与全部秘密所在。

什么是注意力(Attention)机制

注意力(Attention)机制可以看作是模糊存储的一种形式。模型的隐层算是某种存储器,模型选择从内存中检索内容。在我们深入关注之前,让我们简要回顾一下序列-序列(Seq2Seq)模型。传统的机器翻译基本上是基于Seq2Seq模型的。该模型分为编码器层和解码器层,由RNN或RNN变体(LSTM、GRU等)组成。编码器的最后隐状态产生编码向量。编码向量用来封装所有输入元素的信息,以帮助解码器做出准确的预测。输出的编码向量充当模型中解码器部分的初始隐藏状态。Seq2Seq模型的主要瓶颈是需要将源序列的全部内容压缩到一个固定大小的向量中。如果文本稍长,很容易丢失文本的一些信息。为了解决这一问题,人们开始着手关注并解决问题。注意力机制通过允许解码器回溯源序列的隐藏状态,然后提供加权平均值作为解码器的附加输入来缓解这一问题。顾名思义,使用注意力机制,模型在解码阶段会选择最适合当前节点的上下文作为输入。

https://jalammar.github.io/images/seq2seq_7.mp4

(Seq2Seq模型演讲视频) 注意力机制模型与传统的Seq2Seq模型有两个主要的区别:

首先,编码器会向解码器提供更多的数据,在这一过程中,编码器将向解码器提供所有节点的隐层状态,而不仅仅只是编码器最后一个节点的隐层状态。

其次,解码器绝非一股脑儿使用所有编码器提供的隐层状态,而是会采取一种选择机制为当前位置适配最合适的状态。为此,解码器会计算每个隐藏状态的分数值并对分数进行Softmax打分,来确定哪个隐藏状态与当前节点最密切相关,某个隐层状态如果具有更高相关性,将会得到更大多分数,而相关性较小的隐层状态则分数会比较低。接下来,隐层状态会跟每个自身的softmax分数做点乘,softmax分数高的隐状态会被放大,softmax分数低的隐状态会被抑制。这个操作会在Attention模型解码器每个时间步长上都被执行。

https://jalammar.github.io/images/attention_process.mp4

(关于Attention模型演讲视频)

让我们看看传统RNN模型Attention(Seq2Seq)执行的步骤:

1.双层RNN模型的解码端置位嵌入向量(Embedding)以<END>(表征结束>的令牌(token),并对隐状态初始化。

2.解码端的RNN接收输入,产生输出和新的隐状态向量h4,当前输出会被放弃。

3.注意力步骤:采用编码器的隐状态向量和解码器生成的隐状态向量h4产生内容向量C4。

4.将隐状态向量h4和内容向量C4拼接成一个新的向量。

5.把新的向量传给一个前向连接网络(跟模型一起训练)。

6.全连接层的输出表征当前时间上输出的词。

7.执行下一步。

https://jalammar.github.io/images/attention_tensor_dance.mp4?source=post_page---------------------------

(Attention张量工作视频)

Attention论文里的Transformer

Transformer模型采用了一种编解码架构。在谷歌第一篇关于Attention 论文里,编码端有6层编码器,解码端有6层解码器。结构如图所示:

图1. 编解码器架构

编码器由两层组成,一层是自注意力(self-attention层),另一层是前馈神经网络。在sel-attention层当前节点不仅关注当前单词,还能获得上下文语义。解码器除包含编码器里有的两层网络,还在两层中间添加了一个额外的注意力层(Attention Layer),以帮助当前节点获得需要关注的内容。

以下是Attention模型的架构。

图2. Transformer结构图

我们将回顾下每个模块。

自注意力

自注意力机制主要是关注学习对当前词和其周围词相关的某种人类的理解。

首先,自注意力机制会去计算三种向量。在《Attention is all your need》论文中,向量的维数为512维。有查询向量、键向量和值向量三种向量。这三个向量是通过乘以字嵌入向量和一个随机初始化矩阵(论文中的维数为(64,512)产生的,反向传播过程中更新矩阵值。

图3. Self-Attention里各种向量

接下来,需要计算自注意力机制的零碎值,当我们在编码某个位置上单词时,零碎值会决定对输入句子的其余部分,需要给予多少关注(权重)。计算零碎值方法使用查询向量和键向量。计算的结果会除以一个常数。论文中的值为8,通常这个值会是更新矩阵列数的平方根,也就是64的平方根8。然后我们对所有的分数进行softmax计算,其结果是每个词与当前位置上的单词的相关性。毫无疑问,当前位置的词的相关值自然是大的。最后一步要将值向量与Softmax结果相乘,并把它们相加。这将得到当前节点的self-attention权重。

图4. 自注意力运行机制图解

通过查询和键值之间相似度比较,可以得到权重分布等值,这一操作被叫作比例伸缩点乘(scaled dot production)。

多头Attention机制

Self-Attention机制更令人炫目的特性绝非初始化三个查询、键、值三个矩阵(Q、K、V)那么直线条,它还有一个多头Attention机制。转换器会更新8组分块数据,最后的输出结果是8个矩阵,见图5。

图5. 多头Attention机制

前馈神经网络不能直接使用8个矩阵,所以需要把8个矩阵转换为一个矩阵,先把它们拼接起来,再把这个大的矩阵乘以一个初始化的随机矩阵。详细过程见图6。

图6. 多头Attention机制是如何转换为独一的输出矩阵的

一般来说,Transformer有三种不同的方法来使用多头Attention机制。

1. 在“编码器-解码器注意力层”中,查询向量来自上一个解码器层,键向量和值向量由当前编码器输出给定。这允许解码器中的每个位置都能对应到输入序列。这模仿了Seq2Seq模型(通常是双层RNN)中的典型编码器-解码器注意(Attention)机制。

2. 编码器本身也有自注意力层(Self Attention Layer)。在这个自注意力层中间,所有的键、值和查询都来源于前一个编码器的输出。编码器中的每个位置都可以回溯到前一个编码器的对应位置。3. 类似地,解码器中的自注意力层也允许解码器中词向量位置能回溯到之前的解码器对应位置。为了保持自回归特性,我们需要防止解码器中的左信息流,因此,在伸缩点乘(scaled dot production)操作里,我们会屏蔽softmax函数非法值(通常是无穷大)。这将在我们关于解码器掩码部分进行更详细的探讨。

位置编码目前为止,我们依然无法让Transformer模型理解输入序列里的词顺序。为了解决这一问题,可以在Transformer模型的编码器和解码器输入端加入一个额外的位置编码向量。位置编码向量的维度等于嵌入向量的维度,嵌入向量会附加上位置编码向量,作为下一个神经网络层的输入。位置编码有多个选项,固定的或可学习的。

图7. 位置编码

残差连接与层归一化

在编码器和解码器里,都会有一个残差连接(Residual Connection)模块,残差连接层紧接着做层归一化(Layer Normalization)。跳跃连接和残差连接允许绕过非线性激活函数直接进行梯度传导。非线性激活函数在某些权重的情况下会发生梯度消失或梯度爆炸情况。跳跃连接允许坐“直通车”直接进行梯度的前向传播和反向传播。

层归一化用来解决深度学习里内部相关变量偏移(Internal Covariance Shift)的问题。内部相关变量偏移问题主要指代神经网络中发生的共变转移,比如,第三层的统计分布相对于第二层的统计分布发生偏移。之所以会产生这样的现象,是因为随着网络学习和权重的更新,网络中特定层的输出分布会发生变化。较高层为了适应这种漂移,将被迫降低学习速度。在对神经网络中的输入进行归一化之后,我们可以不用担心输入特征会发生畸变。为了理解层归一化(Layer Normalization),可以将其与批归一化(Batch Normalibation)进行比较。一个少量批处理数据会由多个具有相同数量的特性的示例组成。少量批处理数据可以是多维的矩阵或张量-一个轴表示批次,另一个表示特征维度。批归一化使整个批处理维度的输入特性规范化。层归一化的主要特点是它能对各特征之间的输入进行规范化。在批归一化中,统计信息是按批次(Batch)进行处理的,对批处理中的每个示例(Instance)都是普适的。相反,在层归一化中,统计数据是跨特性计算的,每个示例都是相互独立的。

图8. 批归一化(Batch Normalizaiton)与层归一化(Layer Normalization)

残差连接与层归一化双剑合璧之后的Transformer如图9所示。

图9. 残差连接加上层归一化后的Transformer

解码器

回到Transformer架构图,我们可以看到解码器部分类似于编码器部分,但在底部有一个基于多头注意力机制的Mask。Mask表示一个掩码,它遮盖某些值(),使其在参数更新时不会起作用。Transformer模型中有两种掩码-填充掩码(padding mask)和顺序掩码(sequence mask)。填充掩码用于可伸缩点乘(scaled dot production)操作,序列掩码仅用于解码器的自注意力(self-attention)操作。填充掩码主要解决输入序列变长的问题。具体来说,我们在一个较短的序列之后填充零。当然,如果输入序列太长,将截取左侧有意义的内容,多余的内容将被直接丢弃。因为这些填充物的位置其实是没有意义的,自注意力机制应该避免将资源用到这些位置,为此,需要做一些处理。具体的方法是在这些位置的值中添加一个非常大的负数(负无穷大),这样在Softmax之后,这些位置的概率将接近于0!填充掩码实际上是一个张量,每个值都是布尔值,我们需要处理的位置的布尔值为假(False)。

序列掩码可以确保解码器无法看到未来的信息。也就是说,对于序列,在相关的时间步长中,我们解码的输出应该只依赖当前时间t之前的输出,而不是t之后的输出。这是Transformer转换器结构所特有的,因为Transformer无法像RNN可以顺序地输入序列。在这里,所有的输入序列都集中在一起,如果没有掩码,多头注意力会考虑解码器输入序列里每一个位置。Transformer会生成一个上三角矩阵,上三角矩阵的下三角部分为零,将该矩阵同输入序列做乘法。在解码器的自注意力操作部分,会使用可伸缩点乘操作(scaled dot production)。填充掩码加上序列掩码构成了注意力掩码,在其他情况下,注意力掩码就是填充掩码。

另一个要注意的细节是,解码器会将输入右移。这样做的一个原因是,我们不希望我们的模型训练只是在复制解码器的输入,而是说,在给定编码器序列和特定的解码器序列情况下,模型可以预测下一个单词/字符。如果我们不改变解码序列,模型可能只会做到简单地“复制”解码器输入,解码器位置i的输入单词/字符变成输出位置i的目标单词/字符。因此,通过将解码器输入移位,模型在看到1......i-1位置的单词和字符的情况下预测i位置上的单词和字符。这使我们的模型避免进入那种复制/粘贴模式。我们在句子的第一个位置标记一个起始的令牌(token),如果不这样做,因为右移,该位置将是空的。依样画葫芦,我们在句子的最后一个位置也会加上一个令牌来表征序列的结束,并将其添加到输出的目标序列中去。

输出层

解码器的操作完成之后,要将运算得到的结果映射到输入的单词上,模型会在最后加上一个全连接层与softmax层。

模型最后的线性层是一个简单的全连接层,它将解码器堆栈产生的向量投影到一个更大的向量中,称为逻辑(Logits)向量。假设Transformer模型理解10000个独特的英语单词(模型的“输出词汇表”),这是从它的训练数据集中学到的。逻辑向量里就会有10000个单元(cell)每个单元会根据相关分数对应到唯一的单词。这样线性层的输出就可以解释成某种词汇了。然后,softmax层将这些分数转化为概率(和为1.0的正数)。概率最高的单元格将被挑出来,在这个时间步长上,输出相关的单词。

图10. Transformer模型最后的全连接层与softmax层

让我们回头看看BERT吧

BERT的架构是基于Transformer的。它是一个深层的、双向的神经网络。BERT的技术创新就在于把双层Transformer结构应用到语言模型上。它与之前那种自左向右或自右向左进行顺序训练学习文本序列的尝试完全不同。它采用了一种新颖的遮蔽语言模型(Masked Language Modeling),这实现了之前不可能的双向训练机制。在Transformer原生模型里,它分成了两个部分,编码器读取文本输入,解码器进行预测。BERT的目标是去生成一个语言模型,因此只需要Transformer 的编码器。

谷歌最初发布了两版BERT模型,如图11所示。这里,L表示Transformer的层数,H表示输出的尺寸,A表示多头注意力层的数量。在这两种版本中,前馈神经网络的被大小设置为4层。

BERTBASE::L=12,,H=768, A=12, 总参数量110M。

BERTLARGE::L=24,H=1024, A=16,,总参数量340M。

使用BERT模型有两个阶段:预训练阶段与微调阶段。在预训练阶段,模型基于未标记的数据完成预先设置任务训练。在微调阶段,模型基于预训练权重初始化并着手面向下游任务。不同的下游任务有各自独立的模型,即使他们由一套相同权重初始化。BERT可以通过统一的架构抽取出独特的特征,然后面向不同的任务。因为参数需要微调,预训练架构与下游架构之间存在着差异。

图11. BERT预训练模型与微调模型

预训练的BERT

BERT的预训练阶段有两个无监督预测任务:遮蔽语言模型(Masked Language Modeling)和下一句预测(Next Sentence Predictiom)。

掩蔽语言模型(Masked Language Modeling)-由于BERT使用双向Tramsformer和多层自注意力机制,为了训练出深度的双向表示学习模型,BERT会对输入标记的某些百分比(论文中为15%)进行随机掩蔽,然后对这些遮蔽的标记进行预测。如同标准的语言模型所操作的,与遮蔽的掩码标记所对应的最终隐层向量被输入到词汇表上的输出的softmax函数中。与从左到右进行训练语言模型不同,对目标函数进行最大似然估计的模型允许表示融合上下文,这使得对深层的、双向的Transformer进行预训练成为可能。这允许获得双向预训练模型,但缺点是预训练和微调之间存在不匹配,这是因为掩码遮蔽(masked)的令牌(token)在微调过程中不会出现。为了缓解这一弊病作者并不总是用实际的[MASK]标记替换“掩码”字。训练中,可以随机划分出15%的数据,对其令牌位置进行预测。如果某一位置的令牌(token)被选中了,(1)80%的情况下被替换为[MASK]令牌(token);(2)10%的情况下替换为随机令牌(token);(3)剩下10%情况保持不变。BERT损失函数主要考虑对被遮蔽(Masked)掉的词预测,忽略了对非非遮蔽词预测。因此,模型的收敛速度会比定向模型慢,但这一缺憾,相比增强的上下文感知能力,是微不足道的。

下一句预测——为了理解句子关系和词之间的语义关系,BRRT将下一句预测变成一个二分类问题,这样就可以通过现有的语料库进行文本生成。有A和B两类句子,其中50%的B类样本是A的下一句,剩下50%作为负样本也是能学习到句子之间的相关性。这样的方法在一些自然语言处理任务比如问答(QA)和自然语言推理(NLI)中被大量采用,使得预训练模型可以更好适应这类任务。

为了让模型在训练中区分出这两种句子,需要对输入进行些预处理。

1)每个句子的开头会加入[CLS]令牌(token),结束部分插入[SEP]令牌(token)。

2)在每个标记中添加表示句子A或句子B的句嵌入,句嵌入在概念上可以看作一种大小为2的词汇表。3)每个token上加入位置嵌入,位置嵌入的概念与实施策略在介绍Transformer模型的章节被描述过。

如果后一句与前一句有联系,那么:1)整个序列将被输入到Transformer模型里。

2)通过在一个简单的分类层中学习矩阵的权重与偏置,[CLS]令牌(token)转换为一个2*1的向量。

3)通过softmax函数输出是否下一句的概率。

在BERT模型里,遮蔽语言模型和下一句预测任务是同时训练的,最小化联合损失函数以完成对两个策略的训练任务。

令牌化(tokenization)-这不是说BERT把单词看作某个token。相反,它着眼于WordPieces,这意味着一个单词可以被分解成多个子单词。这种标记在处理新词时候是非常棒的,它有助于更好地表达复杂的词汇。

BERT模型输入

BERT的输入可以是单词序列中的单个句子或句子对(例如,[问题、答案])。对于给定的词,它的输入表示可以由三个部分嵌入(Embedding)求和组成。嵌入向量的图片如图12所示:

图12. BERT模型的嵌入

令牌嵌入(Token Embeddings)表示词向量,第一个词用[CLS]作为标记,可以用在随后的分类任务里,如果是非分类任务,CLS记号可以忽略不计。分割嵌入(Segment Embedding)用来区分两个不同的句子,BERT的预训练既致力于产生语言模型,也会训练一个把两句句子作为输入的分类模型。位置嵌入(Positional Embedding)编码词顺序。BERT对NLP下游任务微调

对每个下游的NLP任务,我们只需要即插即用地给BERT模型给定输入输出,然后进行端到端参数微调就行了。在输入端,来自预训练模型的句子A和句子B可以类比释义中的句子对、逻辑推论中的建设前提、问答中的问题对。在输出端,将令牌(token)表示输入到输出层,用来完成令牌(token)级任务,比如序列标记或问题回答,输出层接收[CLS]标志,用于诸如推断蕴含和情感分析之类的分类任务。

图13. BERT微调模型示意图

BERT只需要给核心模块加入一些小的层就可以适应广泛的语言任务。

1. 情感分析之类的分类任务可以类比于预训练阶段的下一句预测,在Transformer的顶层加入一个带有[CLS]令牌(token)的分类层。

2. 在问答任务(比如SQuAD v1.1)上,软件接收一组表示问题的文本序列,提供一组表示回答的文本序列。使用BERT,问答模型可以加入两个额外的向量,一个表示回答的开始,一个表示回答的结束,来进行学习。

3. 在命名实体识别(Named Entity Recognition (NER))任务中,软件接收到一组文本序列,要从文本中找出对应的各种实体(人名、机构名、日期)。一个使用BERT的命名实体识别模型可以将输出向量里每个令牌(token)送入分类层预测其对应的NER标签。

用于特征提取的BERT

BERT不仅能进行微调,您还可以把预训练BERT当作一种基于上下文语境的词嵌入模型。可以把BERT预训练模型得到的嵌入向量馈送给紧接着的其他NLP模型——在诸如命名实体识别之类任务上,论文里的实验部分告知我们说,这样干的效果并不逊色于微调BERT模型。

图14. BERT通过Transformer的编码器生成基于上下文语境的嵌入向量(Contexualized Embeddings)

哪一个最适合作为基于上下文嵌入向量呢?这取决于任务。本文研究了六种选择(BERT微调模型得分96.4):

图15. 在CoNLL-2003命名实体识别任务数据集上不同的BERT变种模型的Dev F1分数

希望您喜欢读这篇文章,就像我喜欢写这篇文章一样!感谢网络上那些优秀的资源,帮助我在写作的时候掌握BERT模型的概念。

原文发布于微信公众号 - CreateAMind(createamind)

原文发表时间:2019-08-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券