精华内容
下载资源
问答
  • BERT

    2021-02-23 08:12:33
    BERT
  • Bert

    2019-10-25 10:27:13
    Bert
  • bert

    2020-11-07 19:57:28
    关于bert的那些面试问题 BERT 的基本原理是什么? BERT 是怎么用 Transformer 的? BERT 的训练过程是怎么样的? 为什么 BERT 比 ELMo 效果好?ELMo 和 BERT 的区别是什么? BERT 有什么局限性? BERT 的输入和输出...

    关于bert的那些面试问题

    BERT 的基本原理是什么?
    BERT 是怎么用 Transformer 的?
    BERT 的训练过程是怎么样的?
    为什么 BERT 比 ELMo 效果好?ELMo 和 BERT 的区别是什么?
    BERT 有什么局限性?
    BERT 的输入和输出分别是什么?
    针对句子语义相似度/多标签分类/机器翻译翻译/文本生成的任务,利用 BERT 结构怎么做 fine-tuning?
    BERT 应用于有空格丢失或者单词拼写错误等数据是否还是有效?有什么改进的方法?
    BERT 的 embedding 向量如何得来的?
    BERT 模型为什么要用 mask?它是如何做 mask 的?其 mask 相对于 CBOW 有什么异同点?
    BERT 的两个预训练任务对应的损失函数是什么(用公式形式展示)?
    词袋模型到 word2vec 改进了什么?word2vec 到 BERT 又改进了什么?
    BERT 的MASK方式的优缺点?
    BERT中的NSP任务是否有必要?
    BERT深度双向的特点,双向体现在哪儿?
    BERT深度双向的特点,深度体现在哪儿?
    BERT中并行计算体现在哪儿?
    BERT中Transformer中的Q、K、V存在的意义?
    BERT中Transformer中Self-attention后为什么要加前馈网络?
    BERT中Transformer中的Self-attention多个头的作用?
    multi-head attention的具体结构
    Bert 采用哪种Normalization结构,LayerNorm和BatchNorm区别,LayerNorm结构有参数吗,参数的作用?
    wordpiece的作用
    如何优化BERT效果
    如何优化BERT性能
    NLP中的Embedding方法总结
    bert的变形

    BERT 的基本原理是什么?
    BERT 来自 Google 的论文Pre-training of Deep Bidirectional Transformers for Language Understanding,BERT 是“Bidirectional Encoder Representations from Transformers”的首字母缩写,整体是一个自编码语言模型(Autoencoder LM),并且其设计了两个任务来预训练该模型。

    第一个任务是采用 MaskLM 的方式来训练语言模型,通俗地说就是在输入一句话的时候,随机地选一些要预测的词,然后用一个特殊的符号[MASK]来代替它们,之后让模型根据所给的标签去学习这些地方该填的词。
    第二个任务在双向语言模型的基础上额外增加了一个句子级别的连续性预测任务,即预测输入 BERT 的两段文本是否为连续的文本,引入这个任务可以更好地让模型学到连续的文本片段之间的关系。
    最后的实验表明 BERT 模型的有效性,并在 11 项 NLP 任务中夺得 SOTA 结果。

    BERT 相较于原来的 RNN、LSTM 可以做到并发执行,同时提取词在句子中的关系特征,并且能在多个不同层次提取关系特征,进而更全面反映句子语义。相较于 word2vec,其又能根据句子上下文获取词义,从而避免歧义出现。同时缺点也是显而易见的,模型参数太多,而且模型太大,少量数据训练时,容易过拟合。

    BERT 是怎么用 Transformer 的?
    BERT 只使用了 Transformer 的 Encoder 模块,原论文中,作者分别用 12 层和 24 层 Transformer Encoder 组装了两套 BERT 模型,分别是:

    在这里插入图片描述

    其中层的数量(即,Transformer Encoder 块的数量)为 L,隐藏层的维度为H ,自注意头的个数为A 。在所有例子中,我们将前馈/过滤器(Transformer Encoder 端的feed-forward 层)的维度设置为4H ,即当 H=768时是3072;当H=1024是4096

    图示如下:
    在这里插入图片描述

    在这里插入图片描述
    需要注意的是,与 Transformer 本身的 Encoder 端相比,BERT 的 Transformer Encoder 端输入的向量表示,多了 Segment Embeddings。

    BERT 的训练过程是怎么样的?
    在论文原文中,作者提出了两个预训练任务:Masked LM 和 Next Sentence Prediction。

    1、 Masked LM
    Masked LM 的任务描述为:给定一句话,随机抹去这句话中的一个或几个词,要求根据剩余词汇预测被抹去的几个词分别是什么,如下图所示。

    在这里插入图片描述
    在这里插入图片描述

    BERT 模型的这个预训练过程其实就是在模仿我们学语言的过程,思想来源于完形填空的任务。具体来说,文章作者在一句话中随机选择 15% 的词汇用于预测。对于在原句中被抹去的词汇, 80% 情况下采用一个特殊符号 [MASK] 替换, 10% 情况下采用一个任意词替换,剩余 10% 情况下保持原词汇不变。

    这么做的主要原因是:在后续微调任务中语句中并不会出现 [MASK] 标记,而且这么做的另一个好处是:预测一个词汇时,模型并不知道输入对应位置的词汇是否为正确的词汇( 10% 概率),这就迫使模型更多地依赖于上下文信息去预测词汇,并且赋予了模型一定的纠错能力。上述提到了这样做的一个缺点,其实这样做还有另外一个缺点,就是每批次数据中只有 15% 的标记被预测,这意味着模型可能需要更多的预训练步骤来收敛。

    2、Next Sentence Prediction
    Next Sentence Prediction 的任务描述为:给定一篇文章中的两句话,判断第二句话在文本中是否紧跟在第一句话之后,如下图所示。
    在这里插入图片描述
    在这里插入图片描述

    这个类似于段落重排序的任务,即:将一篇文章的各段打乱,让我们通过重新排序把原文还原出来,这其实需要我们对全文大意有充分、准确的理解。

    Next Sentence Prediction 任务实际上就是段落重排序的简化版:只考虑两句话,判断是否是一篇文章中的前后句。在实际预训练过程中,文章作者从文本语料库中随机选择 50% 正确语句对和 50% 错误语句对进行训练,与 Masked LM 任务相结合,让模型能够更准确地刻画语句乃至篇章层面的语义信息。

    BERT 模型通过对 Masked LM 任务和 Next Sentence Prediction 任务进行联合训练,使模型输出的每个字 / 词的向量表示都能尽可能全面、准确地刻画输入文本(单句或语句对)的整体信息,为后续的微调任务提供更好的模型参数初始值。

    为什么 BERT 比 ELMo 效果好?ELMo 和 BERT 的区别是什么?
    1、 为什么 BERT 比 ELMo 效果好?
    从网络结构以及最后的实验效果来看,BERT 比 ELMo 效果好主要集中在以下几点原因:

    LSTM 抽取特征的能力远弱于 Transformer
    拼接方式双向融合的特征融合能力偏弱(没有具体实验验证,只是推测)
    其实还有一点,BERT 的训练数据以及模型参数均多余 ELMo,这也是比较重要的一点
    2、 ELMo 和 BERT 的区别是什么?
    ELMo 模型是通过语言模型任务得到句子中单词的 embedding 表示,以此作为补充的新特征给下游任务使用。因为 ELMO 给下游提供的是每个单词的特征形式,所以这一类预训练的方法被称为“Feature-based Pre-Training”。而 BERT 模型是“基于 Fine-tuning 的模式”,这种做法和图像领域基于 Fine-tuning 的方式基本一致,下游任务需要将模型改造成 BERT 模型,才可利用 BERT 模型预训练好的参数。

    BERT 有什么局限性?
    从 XLNet 论文中,提到了 BERT 的两个缺点,分别如下:

    BERT 在第一个预训练阶段,假设句子中多个单词被 Mask 掉,这些被 Mask 掉的单词之间没有任何关系,是条件独立的,然而有时候这些单词之间是有关系的,比如”New York is a city”,假设我们 Mask 住”New”和”York”两个词,那么给定”is a city”的条件下”New”和”York”并不独立,因为”New York”是一个实体,看到”New”则后面出现”York”的概率要比看到”Old”后面出现”York”概率要大得多。

    但是需要注意的是,这个问题并不是什么大问题,甚至可以说对最后的结果并没有多大的影响,因为本身 BERT 预训练的语料就是海量的(动辄几十个 G),所以如果训练数据足够大,其实不靠当前这个例子,靠其它例子,也能弥补被 Mask 单词直接的相互关系问题,因为总有其它例子能够学会这些单词的相互依赖关系。

    BERT 的在预训练时会出现特殊的[MASK],但是它在下游的 fine-tune 中不会出现,这就出现了预训练阶段和 fine-tune 阶段不一致的问题。其实这个问题对最后结果产生多大的影响也是不够明确的,因为后续有许多 BERT 相关的预训练模型仍然保持了[MASK]标记,也取得了很大的结果,而且很多数据集上的结果也比 BERT 要好。但是确确实实引入[MASK]标记,也是为了构造自编码语言模型而采用的一种折中方式。

    另外还有一个缺点,是 BERT 在分词后做[MASK]会产生的一个问题,为了解决 OOV 的问题,我们通常会把一个词切分成更细粒度的 WordPiece。BERT 在 Pretraining 的时候是随机 Mask 这些 WordPiece 的,这就可能出现只 Mask 一个词的一部分的情况,例如:
    在这里插入图片描述

    probability 这个词被切分成”pro”、”#babi”和”#lity”3 个 WordPiece。有可能出现的一种随机 Mask 是把”#babi” Mask 住,但是”pro”和”#lity”没有被 Mask。这样的预测任务就变得容易了,因为在”pro”和”#lity”之间基本上只能是”#babi”了。这样它只需要记住一些词(WordPiece 的序列)就可以完成这个任务,而不是根据上下文的语义关系来预测出来的。类似的中文的词”模型”也可能被 Mask 部分(其实用”琵琶”的例子可能更好,因为这两个字只能一起出现而不能单独出现),这也会让预测变得容易。

    为了解决这个问题,很自然的想法就是词作为一个整体要么都 Mask 要么都不 Mask,这就是所谓的 Whole Word Masking。这是一个很简单的想法,对于 BERT 的代码修改也非常少,只是修改一些 Mask 的那段代码。

    BERT 的输入和输出分别是什么?
    BERT 模型的主要输入是文本中各个字/词(或者称为 token)的原始词向量,该向量既可以随机初始化,也可以利用 Word2Vector 等算法进行预训练以作为初始值;输出是文本中各个字/词融合了全文语义信息后的向量表示,如下图所示(为方便描述且与 BERT 模型的当前中文版本保持一致,统一以「字向量」作为输入):

    在这里插入图片描述

    从上图中可以看出,BERT 模型通过查询字向量表将文本中的每个字转换为一维向量,作为模型输入;模型输出则是输入各字对应的融合全文语义信息后的向量表示。 此外,模型输入除了字向量(英文中对应的是 Token Embeddings),还包含另外两个部分:

    文本向量(英文中对应的是 Segment Embeddings):该向量的取值在模型训练过程中自动学习,用于刻画文本的全局语义信息,并与单字/词的语义信息相融合
    位置向量(英文中对应的是 Position Embeddings):由于出现在文本不同位置的字/词所携带的语义信息存在差异(比如:“我爱你”和“你爱我”),因此,BERT 模型对不同位置的字/词分别附加一个不同的向量以作区分
    最后,BERT 模型将字向量、文本向量和位置向量的加和作为模型输入。特别地,在目前的 BERT 模型中,文章作者还将英文词汇作进一步切割,划分为更细粒度的语义单位(WordPiece),例如:将 playing 分割为 play 和##ing;此外,对于中文,目前作者未对输入文本进行分词,而是直接将单字作为构成文本的基本单位。

    需要注意的是,上图中只是简单介绍了单个句子输入 BERT 模型中的表示,实际上,在做 Next Sentence Prediction 任务时,在第一个句子的首部会加上一个[CLS] token,在两个句子中间以及最后一个句子的尾部会加上一个[SEP] token。

    针对句子语义相似度/多标签分类/机器翻译翻译/文本生成的任务,利用 BERT 结构怎么做 fine-tuning?
    针对句子语义相似度的任务
    在这里插入图片描述

    实际操作时,上述最后一句话之后还会加一个[SEP] token,语义相似度任务将两个句子按照上述方式输入即可,之后与论文中的分类任务一样,将[CLS] token 位置对应的输出,接上 softmax 做分类即可(实际上 GLUE 任务中就有很多语义相似度的数据集)。

    针对多标签分类的任务

    多标签分类任务,即 MultiLabel,指的是一个样本可能同时属于多个类,即有多个标签。以商品为例,一件 L 尺寸的棉服,则该样本就有至少两个标签——型号:L,类型:冬装。

    对于多标签分类任务,显而易见的朴素做法就是不管样本属于几个类,就给它训练几个分类模型即可,然后再一一判断在该类别中,其属于那个子类别,但是这样做未免太暴力了,而多标签分类任务,其实是可以「只用一个模型」来解决的。

    利用 BERT 模型解决多标签分类问题时,其输入与普通单标签分类问题一致,得到其 embedding 表示之后(也就是 BERT 输出层的 embedding),有几个 label 就连接到几个全连接层(也可以称为 projection layer),然后再分别接上 softmax 分类层,这样的话会得到 loss1,loss2, … ,lossn,最后再将所有的 loss 相加起来即可。这种做法就相当于将 n 个分类模型的特征提取层参数共享,得到一个共享的表示(其维度可以视任务而定,由于是多标签分类任务,因此其维度可以适当增大一些),最后再做多标签分类任务。

    针对翻译的任务

    针对翻译的任务,我自己想到一种做法,因为 BERT 本身会产生 embedding 这样的“副产品”,因此可以直接利用 BERT 输出层得到的 embedding,然后在做机器翻译任务时,将其作为输入/输出的 embedding 表示,这样做的话,可能会遇到 UNK 的问题,为了解决 UNK 的问题,可以将得到的词向量 embedding 拼接字向量的 embedding 得到输入/输出的表示(对应到英文就是 token embedding 拼接经过 charcnn 的 embedding 的表示)。

    BERT 应用于有空格丢失或者单词拼写错误等数据是否还是有效?有什么改进的方法?
    BERT 应用于有空格丢失的数据是否还是有效?
    按照常理推断可能会无效了,因为空格都没有的话,那么便成为了一长段文本,但是具体还是有待验证。而对于有空格丢失的数据要如何处理呢?一种方式是利用 Bi-LSTM + CRF 做分词处理,待其处理成正常文本之后,再将其输入 BERT 做下游任务。

    BERT 应用于单词拼写错误的数据是否还是有效?

    如果有少量的单词拼写错误,那么造成的影响应该不会太大,因为 BERT 预训练的语料非常丰富,而且很多语料也不够干净,其中肯定也还是会含有不少单词拼写错误这样的情况。但是如果单词拼写错误的比例比较大,比如达到了 30%、50%这种比例,那么需要通过人工特征工程的方式,以中文中的同义词替换为例,将不同的错字/别字都替换成同样的词语,这样减少错别字带来的影响。例如花被、花珼、花呗、花呗、花钡均替换成花呗。

    BERT 的 embedding 向量如何得来的?
    以中文为例,「BERT 模型通过查询字向量表将文本中的每个字转换为一维向量,作为模型输入(还有 position embedding 和 segment embedding);模型输出则是输入各字对应的融合全文语义信息后的向量表示。」

    而对于输入的 token embedding、segment embedding、position embedding 都是随机生成的,需要注意的是在 Transformer 论文中的 position embedding 由 sin/cos 函数生成的固定的值,而在这里代码实现中是跟普通 word embedding 一样随机生成的,可以训练的。作者这里这样选择的原因可能是 BERT 训练的数据比 Transformer 那篇大很多,完全可以让模型自己去学习。

    BERT 模型为什么要用 mask?它是如何做 mask 的?其 mask 相对于 CBOW 有什么异同点?
    BERT 模型为什么要用 mask?
    BERT 通过在输入 X 中随机 Mask 掉一部分单词,然后预训练过程的主要任务之一是根据上下文单词来预测这些被 Mask 掉的单词。其实这个就是典型的 Denosing Autoencoder 的思路,那些被 Mask 掉的单词就是在输入侧加入的所谓噪音。类似 BERT 这种预训练模式,被称为 DAE LM。因此总结来说 BERT 模型 [Mask] 标记就是引入噪音的手段。

    关于 DAE LM 预训练模式,优点是它能比较自然地融入双向语言模型,同时看到被预测单词的上文和下文,然而缺点也很明显,主要在输入侧引入[Mask]标记,导致预训练阶段和 Fine-tuning 阶段不一致的问题。

    它是如何做 mask 的?
    给定一个句子,会随机 Mask 15%的词,然后让 BERT 来预测这些 Mask 的词,如同上述 10.1 所述,在输入侧引入[Mask]标记,会导致预训练阶段和 Fine-tuning 阶段不一致的问题,因此在论文中为了缓解这一问题,采取了如下措施:

    如果某个 Token 在被选中的 15%个 Token 里,则按照下面的方式随机的执行:

    80%的概率替换成[MASK],比如 my dog is hairy → my dog is [MASK]
    10%的概率替换成随机的一个词,比如 my dog is hairy → my dog is apple
    10%的概率替换成它本身,比如 my dog is hairy → my dog is hairy
    这样做的好处是,BERT 并不知道[MASK]替换的是这 15%个 Token 中的哪一个词(「注意:这里意思是输入的时候不知道[MASK]替换的是哪一个词,但是输出还是知道要预测哪个词的」),而且任何一个词都有可能是被替换掉的,比如它看到的 apple 可能是被替换的词。这样强迫模型在编码当前时刻的时候不能太依赖于当前的词,而要考虑它的上下文,甚至对其上下文进行”纠错”。比如上面的例子模型在编码 apple 是根据上下文 my dog is 应该把 apple(部分)编码成 hairy 的语义而不是 apple 的语义。

    其 mask 相对于 CBOW 有什么异同点?
    「相同点」:CBOW 的核心思想是:给定上下文,根据它的上文 Context-Before 和下文 Context-after 去预测 input word。而 BERT 本质上也是这么做的,但是 BERT 的做法是给定一个句子,会随机 Mask 15%的词,然后让 BERT 来预测这些 Mask 的词。

    「不同点」:首先,在 CBOW 中,每个单词都会成为 input word,而 BERT 不是这么做的,原因是这样做的话,训练数据就太大了,而且训练时间也会非常长。

    其次,对于输入数据部分,CBOW 中的输入数据只有待预测单词的上下文,而 BERT 的输入是带有[MASK] token 的“完整”句子,也就是说 BERT 在输入端将待预测的 input word 用[MASK] token 代替了。

    另外,通过 CBOW 模型训练后,每个单词的 word embedding 是唯一的,因此并不能很好的处理一词多义的问题,而 BERT 模型得到的 word embedding(token embedding)融合了上下文的信息,就算是同一个单词,在不同的上下文环境下,得到的 word embedding 是不一样的。

    为什么 BERT 中输入数据的[mask]标记为什么不能直接留空或者直接输入原始数据,在 self-attention 的 Q K V 计算中,不与待预测的单词做 Q K V 交互计算?

    这个问题还要补充一点细节,就是数据可以像 CBOW 那样,每一条数据只留一个“空”,这样的话,之后在预测的时候,就可以将待预测单词之外的所有单词的表示融合起来(均值融合或者最大值融合等方式),然后再接上 softmax 做分类。

    乍一看,感觉这个 idea 确实有可能可行,而且也没有看到什么不合理之处,但是需要注意的是,这样做的话,需要每预测一个单词,就要计算一套 Q、K、V。就算不每次都计算,那么保存每次得到的 Q、K、V 也需要耗费大量的空间。总而言之,这种做法确实可能也是可行,但是实际操作难度却很大,从计算量来说,就是预训练 BERT 模型的好几倍(至少),而且要保存中间状态也并非易事。其实还有挺重要的一点,如果像 CBOW 那样做,那么文章的“创新”在哪呢~

    BERT 的两个预训练任务对应的损失函数是什么(用公式形式展示)?
    BERT 的损失函数由两部分组成,第一部分是来自 Mask-LM 的「单词级别分类任务」,另一部分是「句子级别的分类任务」。通过这两个任务的联合学习,可以使得 BERT 学习到的表征既有 token 级别信息,同时也包含了句子级别的语义信息。具体损失函数如下:
    在这里插入图片描述

    其中 θ是 BERT 中 Encoder 部分的参数, θ1是 Mask-LM 任务中在 Encoder 上所接的输出层中的参数, θ2则是句子预测任务中在 Encoder 接上的分类器参数。因此,在第一部分的损失函数中,如果被 mask 的词集合为 M,因为它是一个词典大小 |V| 上的多分类问题,那么具体说来有:
    在这里插入图片描述

    在句子预测任务中,也是一个分类问题的损失函数:
    在这里插入图片描述

    因此,两个任务联合学习的损失函数是:

    在这里插入图片描述

    具体的预训练工程实现细节方面,BERT 还利用了一系列策略,使得模型更易于训练,比如对于学习率的 warm-up 策略,使用的激活函数不再是普通的 ReLu,而是 GeLu,也使用了 dropout 等常见的训练技巧。

    词袋模型到 word2vec 改进了什么?word2vec 到 BERT 又改进了什么?
    词袋模型到 word2vec 改进了什么?
    词袋模型(Bag-of-words model)是将一段文本(比如一个句子或是一个文档)用一个“装着这些词的袋子”来表示,这种表示方式不考虑文法以及词的顺序。「而在用词袋模型时,文档的向量表示直接将各词的词频向量表示加和」。通过上述描述,可以得出词袋模型的两个缺点:

    词向量化后,词与词之间是有权重大小关系的,不一定词出现的越多,权重越大。
    词与词之间是没有顺序关系的。
    而 word2vec 是考虑词语位置关系的一种模型。通过大量语料的训练,将每一个词语映射成一个低维稠密向量,通过求余弦的方式,可以判断两个词语之间的关系,word2vec 其底层主要采用基于 CBOW 和 Skip-Gram 算法的神经网络模型。

    因此,综上所述,词袋模型到 word2vec 的改进主要集中于以下两点:

    考虑了词与词之间的顺序,引入了上下文的信息
    得到了词更加准确的表示,其表达的信息更为丰富
    word2vec 到 BERT 又改进了什么?
    word2vec 到 BERT 的改进之处其实没有很明确的答案,如同上面的问题所述,BERT 的思想其实很大程度上来源于 CBOW 模型,如果从准确率上说改进的话,BERT 利用更深的模型,以及海量的语料,得到的 embedding 表示,来做下游任务时的准确率是要比 word2vec 高不少的。实际上,这也离不开模型的“加码”以及数据的“巨大加码”。再从方法的意义角度来说,BERT 的重要意义在于给大量的 NLP 任务提供了一个泛化能力很强的预训练模型,而仅仅使用 word2vec 产生的词向量表示,不仅能够完成的任务比 BERT 少了很多,而且很多时候直接利用 word2vec 产生的词向量表示给下游任务提供信息,下游任务的表现不一定会很好,甚至会比较差。

    BERT 的MASK方式的优缺点?
    BERT的mask方式:在选择mask的15%的词当中,80%情况下使用mask掉这个词,10%情况下采用一个任意词替换,剩余10%情况下保持原词汇不变。
    优点:
    1)被随机选择15%的词当中以10%的概率用任意词替换去预测正确的词,相当于文本纠错任务,为BERT模型赋予了一定的文本纠错能力;
    2)被随机选择15%的词当中以10%的概率保持不变,缓解了finetune时候与预训练时候输入不匹配的问题(预训练时候输入句子当中有mask,而finetune时候输入是完整无缺的句子,即为输入不匹配问题)。
    缺点:针对有两个及两个以上连续字组成的词,随机mask字割裂了连续字之间的相关性,使模型不太容易学习到词的语义信息。主要针对这一短板,因此google此后发表了BERT-WWM,国内的哈工大联合讯飞发表了中文版的BERT-WWM。

    BERT中的NSP任务是否有必要?
    答:在此后的研究(论文《Crosslingual language model pretraining》等)中发现,NSP任务可能并不是必要的,消除NSP损失在下游任务的性能上能够与原始BERT持平或略有提高。这可能是由于Bert以单句子为单位输入,模型无法学习到词之间的远程依赖关系。针对这一点,后续的RoBERTa、ALBERT、spanBERT都移去了NSP任务。

    BERT深度双向的特点,双向体现在哪儿?
    BERT使用Transformer-encoder来编码输入,encoder中的Self-attention机制在编码一个token的时候同时利用了其上下文的token,其中‘同时利用上下文’即为双向的体现,而并非想Bi-LSTM那样把句子倒序输入一遍。

    BERT深度双向的特点,深度体现在哪儿?
    针对特征提取器,Transformer只用了self-attention,没有使用RNN、CNN,并且使用了残差连接有效防止了梯度消失的问题,使之可以构建更深层的网络,所以BERT构建了多层深度Transformer来提高模型性能。

    BERT中并行计算体现在哪儿?
    不同于RNN计算当前词的特征要依赖于前文计算,有时序这个概念,是按照时序计算的,而BERT的Transformer-encoder中的self-attention计算当前词的特征时候,没有时序这个概念,是同时利用上下文信息来计算的,一句话的token特征是通过矩阵并行‘瞬间’完成运算的,故,并行就体现在self-attention。

    BERT中Transformer中的Q、K、V存在的意义?
    在使用self-attention通过上下文词语计算当前词特征的时候,X先通过WQ、WK、WV线性变换为QKV,然后如下式右边部分使用QK计算得分,最后与V计算加权和而得。

    倘若不变换为QKV,直接使用每个token的向量表示点积计算重要性得分,那在softmax后的加权平均中,该词本身所占的比重将会是最大的,使得其他词的比重很少,无法有效利用上下文信息来增强当前词的语义表示。而变换为QKV再进行计算,能有效利用上下文信息,很大程度上减轻上述的影响。

    BERT中Transformer中Self-attention后为什么要加前馈网络?
    由于self-attention中的计算都是线性了,为了提高模型的非线性拟合能力,需要在其后接上前馈网络。

    BERT中Transformer中的Self-attention多个头的作用?
    类似于cnn中多个卷积核的作用,使用多头注意力,能够从不同角度提取信息,提高信息提取的全面性。

    multi-head attention的具体结构
    BERT由12层transformer layer(encoder端)构成,首先word emb , pos emb(可能会被问到有哪几种position embedding的方式,bert是使用的哪种), sent emb做加和作为网络输入,每层由一个multi-head attention, 一个feed forward 以及两层layerNorm构成,一般会被问到multi-head attention的结构,具体可以描述为,一个768的hidden向量,被映射成query, key, value。 然后三个向量分别切分成12个小的64维的向量,每一组小向量之间做attention。

    hidden(768) -> query(768) -> 12 x 64

    hidden(768) -> key(768) -> 12 x 64

    hidden(768) -> val(768) -> 12 x 64

    然后query和key之间做attention,得到一个12乘以12的权重矩阵,然后根据这个权重矩阵加权val中切分好的12个64维向量,得到一个12 x 64的向量,拉平输出为768向量。

    Bert 采用哪种Normalization结构,LayerNorm和BatchNorm区别,LayerNorm结构有参数吗,参数的作用?
    采用LayerNorm结构,和BatchNorm的区别主要是做规范化的维度不同,BatchNorm针对一个batch里面的数据进行规范化,针对单个神经元进行,比如batch里面有64个样本,那么规范化输入的这64个样本各自经过这个神经元后的值(64维),LayerNorm则是针对单个样本,不依赖于其他数据,常被用于小mini-batch场景、动态网络场景和 RNN,特别是自然语言处理领域,就bert来说就是对每层输出的隐层向量(768维)做规范化,图像领域用BN比较多的原因是因为每一个卷积核的参数在不同位置的神经元当中是共享的,因此也应该被一起规范化。

    class BertLayerNorm(nn.Module):
    def init(self, hidden_size, eps=1e-5):
    super(BertLayerNorm, self).init()
    self.weight = nn.Parameter(torch.ones(hidden_size))
    self.bias = nn.Parameter(torch.zeros(hidden_size))
    self.variance_epsilon = eps

    def forward(self, x):
        u = x.mean(-1, keepdim=True)
        s = (x - u).pow(2).mean(-1, keepdim=True)
        x = (x - u) / torch.sqrt(s + self.variance_epsilon)
        return self.weight * x + self.bias
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    帖一个LayerNorm的实现,可以看到module中有weight和bias参数,以Sigmoid激活函数为例,批量归一化之后数据整体处于函数的非饱和区域, 只包含线性变换,破坏了之前学习到的特征分布。为了恢复原始数据分布,具体实现中引入了变换重构以及可学习参数w和b ,也就是上面的weight和bias,简而言之,规范化后的隐层表示将输入数据限制到了一个全局统一的确定范围,为了保证模型的表达能力不因为规范化而下降,引入了b是再平移参数,w是再缩放参数。(过激活函数前规范化,之后还原)

    wordpiece的作用
    wordpiece其核心思想是将单词打散为字符,然后根据片段的组合频率,最后单词切分成片段处理。和原有的分词相比,能够极大的降低OOV的情况,例如cosplayer, 使用分词的话如果出现频率较低则是UNK,但bpe可以把它切分吃cos play er, 模型可以词根以及前缀等信息,学习到这个词的大致信息,而不是一个OOV。

    wordpiece与BPE(Byte Pair Encoding)算法类似,也是每次从词表中选出两个子词合并成新的子词。与BPE的最大区别在于,如何选择两个子词进行合并:BPE选择频数最高的相邻子词合并,而WordPiece选择能够提升语言模型概率最大的相邻子词加入词表。

    如何优化BERT效果
    1 感觉最有效的方式还是数据。

    2 把现有的大模型ERNIE_2.0_large, Roberta,roberta_wwm_ext_large、roberta-pair-large等进行ensemble,然后蒸馏原始的bert模型,这是能有效提高的,只是操作代价比较大。

    3 BERT上面加一些网络结构,比如attention,rcnn等,个人得到的结果感觉和直接在上面加一层transformer layer的效果差不多。

    4 改进预训练,在特定的大规模数据上预训练,相比于开源的用百科,知道等数据训练的更适合你的任务,以及在训练后续mask的时候去mask低频词或者实体词(听说过有人这么做有收益,但没具体验证)。

    5 文本对抗,作者了解的不多。感兴趣可以看看BERT-ATTACK: Adversarial Attack Against BERT Using BERT

    如何优化BERT性能
    1 压缩层数,然后蒸馏,直接复用12层bert的前4层或者前6层,效果能和12层基本持平,如果不蒸馏会差一些。

    2 双塔模型(短文本匹配任务),将bert作为一个encoder,输入query编码成向量,输入title编码成向量,最后加一个DNN网络计算打分即可。

    3 int8预估,在保证模型精度的前提下,将Float32的模型转换成Int8的模型。

    4 提前结束,大致思想是简单的case前面几层就可以输出分类结果,比较难区分的case走完12层,但这个在batch里面计算应该怎么优化还没看明白,有的提前结束有的最后结束,如果在一个batch里面的话就不太好弄。感兴趣的可以看看BERT Loses Patience:Fast and Robust Inference with Early Exit

    转自:https://blog.csdn.net/tyutyansheng/article/details/108411448

    展开全文
  • 一文读懂BERT(原理篇)

    万次阅读 多人点赞 2019-04-19 08:37:17
    一文读懂BERT(从原理到实践) 2018年的10月11日,Google发布的论文《Pre-training of Deep Bidirectional Transformers for Language Understanding》,成功在 11 项 NLP 任务中取得 state of the art 的结果,赢得...

    一文读懂BERT(原理篇)

    2018年的10月11日,Google发布的论文《Pre-training of Deep Bidirectional Transformers for Language Understanding》,成功在 11 项 NLP 任务中取得 state of the art 的结果,赢得自然语言处理学界的一片赞誉之声。

    本文是对近期关于BERT论文、相关文章、代码进行学习后的知识梳理,仅为自己学习交流之用。因笔者精力有限,如果文中因引用了某些文章观点未标出处还望作者海涵,也希望各位一起学习的读者对文中不恰当的地方进行批评指正。

    1)资源梳理

    • BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

    • 论文原文:
      BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

    • 官方代码和预训练模型
      Github: https://github.com/google-research/bert

    • Google AI Blog:
      Open Sourcing BERT: State-of-the-Art Pre-training for Natural Language Processing

    • 第三方代码

      1. pytorch-pretrained-BERT
        Google官方推荐的PyTorch BERB版本实现,可加载Google预训练的模型:PyTorch version of Google AI’s BERT model with script to load Google’s pre-trained models

      2. BERT-pytorch
        另一个Pytorch版本实现:Google AI 2018 BERT pytorch implementation

      3. BERT-tensorflow
        Tensorflow版本:BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

      4. bert-chainer
        Chanier版本: Chainer implementation of “BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding”

      5. bert-as-service
        将不同长度的句子用BERT预训练模型编码,映射到一个固定长度的向量上:Mapping a variable-length sentence to a fixed-length vector using pretrained BERT model
        进一步是否可以做一个句子相似度计算服务?有没有同学一试?

      6. bert_language_understanding
        BERT实战:Pre-training of Deep Bidirectional Transformers for Language Understanding: pre-train TextCNN

      7. sentiment_analysis_fine_grain
        BERT实战,多标签文本分类,在 AI Challenger 2018 细粒度情感分析任务上的尝试:Multi-label Classification with BERT; Fine Grained Sentiment Analysis from AI challenger

      8. BERT-NER
        BERT实战,命名实体识别: Use google BERT to do CoNLL-2003 NER !

      9. BERT-keras
        Keras版: Keras implementation of BERT with pre-trained weights

      10. tbert
        PyTorch port of BERT ML model

    2)NLP发展史

    关于NLP发展史,特别推荐weizier大佬的NLP的巨人肩膀。学术性和文学性都很棒,纵览NLP近几年的重要发展,对各算法如数家珍,深入浅出,思路清晰,文不加点,一气呵成。
    现对NLP发展脉络简要梳理如下:
    在这里插入图片描述

    • 2001 - Neural language models(神经语言模型)
    • 2008 - Multi-task learning(多任务学习)
    • 2013 - Word embeddings(词嵌入)
    • 2013 - Neural networks for NLP(NLP神经网络)
    • 2014 - Sequence-to-sequence models
    • 2015 - Attention(注意力机制)
    • 2015 - Memory-based networks(基于记忆的网络)
    • 2018 - Pretrained language models(预训练语言模型)

    2001 - 神经语言模型

    第一个神经语言模型是Bengio等人在2001年提出的前馈神经网络,如图所示:
    在这里插入图片描述这个模型将从表C中查找到的n个单词作为输入向量表征。这种向量被现在的学者们称做“词嵌入”。这些词嵌入级联后被输入到一个隐藏层中,该隐藏层的输出又被输入到softmax层。更多关于模型的信息

    语言建模通常是应用RNN时的第一步,是一种非监督学习形式。尽管它很简单,但却是本文后面讨论的许多技术发展的核心:

    • 词嵌入:word2vec 的目标是简化语言建模。

    • sequence-to-sequence 模型:这种模型通过一次预测一个单词生成一个输出序列。

    • 预训练语言模型:这些方法使用来自语言模型的表述进行迁移学习。

    反过来讲,这意味着近年来 NLP 的许多重要进展都可以归结为某些形式的语言建模。为了“真正”理解自然语言,仅仅从文本的原始形式中学习是不够的。我们需要新的方法和模型。

    2008- 多任务学习

    多任务学习是在多个任务上训练的模型之间共享参数的一种通用方法。在神经网络中,可以通过给不同层施以不同的权重,来很容易地实现多任务学习。多任务学习的概念最初由Rich Caruana 在1993年提出,并被应用于道路跟踪和肺炎预测(Caruana,1998)。直观地说,多任务学习鼓励模型学习对许多任务有用的表述。这对于学习一般的、低级的表述形式、集中模型的注意力或在训练数据有限的环境中特别有用。详情请看这篇文章

    在2008年,Collobert 和 Weston 将多任务学习首次应用于 NLP 的神经网络。在他们的模型中,查询表(或单词嵌入矩阵)在两个接受不同任务训练的模型之间共享,如下面的图所示。
    在这里插入图片描述

    2013- 词嵌入

    用稀疏向量表示文本,即所谓的词袋模型在 NLP 有着悠久的历史。正如上文中介绍的,早在 2001年就开始使用密集向量表示词或词嵌入。Mikolov等人在2013年提出的创新技术是通过去除隐藏层,逼近目标,进而使这些单词嵌入的训练更加高效。虽然这些技术变更本质上很简单,但它们与高效的word2vec配合使用,便能使大规模的词嵌入训练成为可能。

    Word2vec有两种风格,如下面的图所示:连续字袋 CBOW 和 skip-gram。不过他们的目标不同:一个是根据周围的单词预测中心单词,而另一个则相反。
    在这里插入图片描述虽然这些嵌入在概念上与使用前馈神经网络学习的嵌入在概念上没有区别,但是在一个非常大的语料库上训练之后,它们就能够捕获诸如性别、动词时态和国家-首都关系等单词之间的特定关系,如下图所示。
    !

    2013 - NLP 神经网络

    2013 年和 2014 年是 NLP 问题开始引入神经网络模型的时期。使用最广泛的三种主要的神经网络是:循环神经网络、卷积神经网络和递归神经网络。

    循环神经网络(RNNs) 循环神经网络是处理 NLP 中普遍存在的动态输入序列的一个最佳的技术方案。Vanilla RNNs (Elman,1990)很快被经典的长-短期记忆网络(Hochreiter & Schmidhuber,1997)所取代,它被证明对消失和爆炸梯度问题更有弹性。在 2013 年之前,RNN 仍被认为很难训练;Ilya Sutskever 的博士论文为改变这种现状提供了一个关键性的例子。下面的图对 LSTM 单元进行了可视化显示。双向 LSTM(Graves等,2013)通常用于处理左右两边的上下文。
    在这里插入图片描述卷积神经网络(CNNs) 卷积神经网络本来是广泛应用于计算机视觉领域的技术,现在也开始应用于语言(Kalchbrenner等,2014;Kim等,2014)。文本的卷积神经网络只在两个维度上工作,其中滤波器(卷积核)只需要沿着时间维度移动。下面的图显示了NLP中使用的典型 CNN。
    在这里插入图片描述卷积神经网络的一个优点是它们比 RNN 更可并行化,因为其在每个时间步长的状态只依赖于本地上下文(通过卷积运算),而不是像 RNN 那样依赖过去所有的状态。使用膨胀卷积,可以扩大 CNN 的感受野,使网络有能力捕获更长的上下文(Kalchbrenner等,2016)。CNN 和 LSTM 可以组合和叠加(Wang等,2016),卷积也可以用来加速 LSTM(Bradbury等, 2017)。

    递归神经网络 RNN 和 CNN 都将语言视为一个序列。然而,从语言学的角度来看,语言本质上是层次化的:单词被组合成高阶短语和从句,这些短语和从句本身可以根据一组生产规则递归地组合。将句子视为树而不是序列的语言学启发思想产生了递归神经网络(Socher 等人, 2013),如下图所示
    在这里插入图片描述递归神经网络从下到上构建序列的表示,这一点不同于从左到右或从右到左处理句子的 RNN。在树的每个节点上,通过组合子节点的结果来计算新的结果。由于树也可以被视为在 RNN 上强加不同的处理顺序,所以 LSTM 自然地也被扩展到树上(Tai等,2015)。

    RNN 和 LSTM 可以扩展到使用层次结构。单词嵌入不仅可以在本地学习,还可以在语法语境中学习(Levy & Goldberg等,2014);语言模型可以基于句法堆栈生成单词(Dyer等,2016);图卷积神经网络可以基于树结构运行(Bastings等,2017)。

    2014-sequence-to-sequence 模型

    2014 年,Sutskever 等人提出了 sequence-to-sequence 模型。这是一个使用神经网络将一个序列映射到另一个序列的通用框架。在该框架中,编码器神经网络逐符号处理一个句子,并将其压缩为一个向量表示;然后,一个解码器神经网络根据编码器状态逐符号输出预测值,并将之前预测的符号作为每一步的输入,如下图所示。
    !
    机器翻译是对这个框架比较成功的应用。2016 年,谷歌宣布将开始用神经 MT 模型取代基于单片短语的 MT 模型(Wu等,2016)。根据 Jeff Dean 的说法,这意味着用 500 行神经网络模型替换 50 万行基于短语的MT代码。

    由于其灵活性,这个框架现在是自然语言生成任务的首选框架,其中不同的模型承担了编码器和解码器的角色。重要的是,解码器模型不仅可以解码一个序列,而且可以解码任意表征。例如,可以基于图像生成标题(Vinyals等,2015)(如下图所示)、基于表生成文本(Lebret等,2016)和基于应用程序中源代码更改描述(Loyola等,2017)。
    在这里插入图片描述sequence-to-sequence 学习甚至可以应用于 NLP 中输出具有特定结构的结构化预测任务。为了简单起见,输出被线性化,如下面的图所示,用于进行选区解析。神经网络已经证明了在有足够数量的训练数据进行选区分析(Vinyals等,2015)和命名实体识别(Gillick等, 2016)的情况下,直接学习可以产生这种线性化输出的能力。
    在这里插入图片描述

    2015- 注意力机制

    注意力机制(Bahdanau 等,2015)是神经网络机器翻译(NMT)的核心创新之一,也是使 NMT模型胜过经典的基于短语的MT系统的关键思想。sequence-to-sequence模型的主要瓶颈是需要将源序列的全部内容压缩为一个固定大小的向量。注意力机制通过允许解码器回头查看源序列隐藏状态来缓解这一问题,然后将其加权平均作为额外输入提供给解码器,如下面的图所示
    在这里插入图片描述
    注意力机制有很多不同的形式(Luong等,2015)。这里有一个简短的概述。注意力机制广泛适用于任何需要根据输入的特定部分做出决策的任务,并且效果不错。它已被应用于一致性解析(Vinyals等,2015)、阅读理解(Hermann等,2015)和一次性学习(Vinyals等,2016)等诸多领域。输入甚至不需要是一个序列,即可以包含其他表示,如图像字幕(Xu等,2015),如下图所示。注意力机制的一个额外的功能是,它提供了一种少见的功能,我们可以通过检查输入的哪些部分与基于注意力权重的特定输出相关来了解模型的内部工作方式。
    在这里插入图片描述

    2015 - 基于记忆的网络

    注意力机制可以看作是模糊记忆的一种形式。记忆由模型的隐藏状态组成,模型选择从记忆中检索内容。研究者们提出了许多具有更明确记忆的模型。这些模型有不同的变体,如神经图灵机(Graves等,2014)、记忆网络(Weston等,2015)和端到端记忆网络(Sukhbaatar等,2015)、动态记忆网络(Kumar等,2015)、神经微分计算机(Graves等,2016)和循环实体网络(Henaff等,2017)。

    记忆的访问通常基于与当前状态的相似度,类似于注意力,通常可以写入和读取。模型在如何实现和利用内存方面有所不同。例如,端到端记忆网络多次处理输入,并更新记忆以实现多个推理步骤。神经图灵机也有一个基于位置的寻址,这允许他们学习简单的计算机程序,如排序。基于记忆的模型通常应用于一些特定任务中,如语言建模和阅读理解。在这些任务中,长时间保存信息应该很有用。记忆的概念是非常通用的:知识库或表可以充当记忆,而记忆也可以根据整个输入或它的特定部分填充。

    2018 - 预训练语言模型

    预训练的词嵌入与上下文无关,仅用于初始化模型中的第一层。一系列监督型任务被用于神经网络的预训练。相反,语言模型只需要无标签的文本;因此,训练可以扩展到数十亿个tokens, new domains, new languages。预训练语言模型于 2015 年被首次提出(Dai & Le,2015);直到最近,它们才被证明在各种任务中效果还是不错的。语言模型嵌入可以作为目标模型中的特征(Peters等,2018),或者使用语言模型对目标任务数据进行微调(Ramachandranden等,2017; Howard & Ruder,2018)。添加语言模型嵌入可以在许多不同的任务中提供比最先进的技术更大的改进,如下面的图所示。

    在这里插入图片描述
    预训练的语言模型已经被证明可以用更少的数据进行学习。由于语言模型只需要无标记的数据,因此对于标记数据稀缺的低资源语言尤其有用。

    其他里程碑事件

    其他一些技术发展没有上面提到的那样流行,但仍然有广泛的影响。

    • 基于字符的表示
      在字符上使用 CNN 或 LSTM 以获得基于字符的词表示的做法现在相当普遍,特别是对于形态信息重要或有许多未知单词的丰富的语言和任务,效果更加明显。据我所知,序列标签使用基于字符的表示(Lample 等人,2016;普兰克等人,2016),可以减轻在计算成本增加的情况下必须处理固定词汇表的需要,并支持完全基于字符的 NMT (Ling 等人, 2016;Lee 等人,2017)。

    • 对抗学习
      对抗学习方法已经在 ML 领域掀起了风暴,在 NLP 中也有不同形式的应用。对抗性的例子越来越被广泛使用,它不仅是作为一种工具来探究模型和理解它们的失败案例,而且也使自身更加鲁棒(Jia & Liang, 2017)。(虚拟)对抗性训练,即最坏情况扰动(Miyato 等人,2017)和领域对抗性损失(Ganin 等人, 2016;Kim 等人,2017),同样可以使模型更加鲁棒。生成对抗网络(GANs)对于自然语言生成还不是很有效(Semeniuta 等人, 2018),但在匹配分布时很有用(Conneau 等人, 2018)。

    • 强化学习
      强化学习已被证明对具有时间依赖性的任务有效,例如在训练期间选择数据(Fang 等人, 2017;Wu 等人, 2018)和建模对话(Liu 等人, 2018)。RL 对于直接优化不可微的末端度量(如 ROUGE 或 BLEU)也有效,反而在汇总中优化替代损失(如交叉熵)(Paulus 等人, 2018;Celikyilmaz 等人,2018)和机器翻译场景效果就不明显了(Ranzato 等人,2016)。类似地,逆向强化学习在过于复杂而无法指定数据的情况下也很有用,比看图说话任务(Wang 等人, 2018)。

    3)BERT:一切过往, 皆为序章

    Attention机制讲解

    attention是一种能让模型对重要信息重点关注并充分学习吸收的技术,它不算是一个完整的模型,应当是一种技术,能够作用于任何序列模型中。本文较多引用了本篇文章思路,如感兴趣可以跳转学习。

    Seq2Seq

    在开始讲解Attention之前,我们先简单回顾一下Seq2Seq模型,传统的机器翻译基本都是基于Seq2Seq模型来做的,该模型分为encoder层与decoder层,并均为RNN或RNN的变体构成,如下图所示:
    在这里插入图片描述
    在encode阶段,第一个节点输入一个词,之后的节点输入的是下一个词与前一个节点的hidden state,最终encoder会输出一个context,这个context又作为decoder的输入,每经过一个decoder的节点就输出一个翻译后的词,并把decoder的hidden state作为下一层的输入。该模型对于短文本的翻译来说效果很好,但是其也存在一定的缺点,如果文本稍长一些,就很容易丢失文本的一些信息,为了解决这个问题,Attention应运而生。

    Attention

    Attention,正如其名,注意力,该模型在decode阶段,会选择最适合当前节点的context作为输入。Attention与传统的Seq2Seq模型主要有以下两点不同。

    1)encoder提供了更多的数据给到decoder,encoder会把所有的节点的hidden state提供给decoder,而不仅仅只是encoder最后一个节点的hidden state。
    在这里插入图片描述
    2)decoder并不是直接把所有encoder提供的hidden state作为输入,而是采取一种选择机制,把最符合当前位置的hidden state选出来,具体的步骤如下

    • 确定哪一个hidden state与当前节点关系最为密切

    • 计算每一个hidden state的分数值(具体怎么计算我们下文讲解)

    • 对每个分数值做一个softmax的计算,这能让相关性高的hidden state的分数值更大,相关性低的hidden state的分数值更低

    这里我们以一个具体的例子来看下其中的详细计算步骤:
    在这里插入图片描述
    把每一个encoder节点的hidden states的值与decoder当前节点的上一个节点的hidden state相乘,如上图,h1、h2、h3分别与当前节点的上一节点的hidden state进行相乘(如果是第一个decoder节点,需要随机初始化一个hidden state),最后会获得三个值,这三个值就是上文提到的hidden state的分数,注意,这个数值对于每一个encoder的节点来说是不一样的,把该分数值进行softmax计算,计算之后的值就是每一个encoder节点的hidden states对于当前节点的权重,把权重与原hidden states相乘并相加,得到的结果即是当前节点的hidden state。可以发现,其实Atttention的关键就是计算这个分值。

    明白每一个节点是怎么获取hidden state之后,接下来就是decoder层的工作原理了,其具体过程如下:

    第一个decoder的节点初始化一个向量,并计算当前节点的hidden state,把该hidden state作为第一个节点的输入,经过RNN节点后得到一个新的hidden state与输出值。注意,这里和Seq2Seq有一个很大的区别,Seq2Seq是直接把输出值作为当前节点的输出,但是Attention会把该值与hidden state做一个连接,并把连接好的值作为context,并送入一个前馈神经网络,最终当前节点的输出内容由该网络决定,重复以上步骤,直到所有decoder的节点都输出相应内容。
    在这里插入图片描述

    Attention模型并不只是盲目地将输出的第一个单词与输入的第一个词对齐。实际上,它在训练阶段学习了如何在该语言对中对齐单词(示例中是法语和英语)。Attention函数的本质可以被描述为一个查询(query)到一系列(键key-值value)对的映射。
    在这里插入图片描述
    在计算attention时主要分为三步,第一步是将query和每个key进行相似度计算得到权重,常用的相似度函数有点积,拼接,感知机等;然后第二步一般是使用一个softmax函数对这些权重进行归一化;最后将权重和相应的键值value进行加权求和得到最后的attention。目前在NLP研究中,key和value常常都是同一个,即key=value。
    在这里插入图片描述

    Transrofmer模型讲解

    接下来我将介绍《Attention is all you need》这篇论文。这篇论文是google机器翻译团队在2017年6月放在arXiv上,最后发表在2017年nips上,到目前为止google学术显示引用量为2203,可见也是受到了大家广泛关注和应用。这篇论文主要亮点在于
    1)不同于以往主流机器翻译使用基于RNN的seq2seq模型框架,该论文用attention机制代替了RNN搭建了整个模型框架。
    2)提出了多头注意力(Multi-headed attention)机制方法,在编码器和解码器中大量的使用了多头自注意力机制(Multi-headed self-attention)。
    3)在WMT2014语料中的英德和英法任务上取得了先进结果,并且训练速度比主流模型更快。

    《Attention Is All You Need》是一篇Google提出的将Attention思想发挥到极致的论文。这篇论文中提出一个全新的模型,叫 Transformer,抛弃了以往深度学习任务里面使用到的 CNN 和 RNN ,Bert就是基于Transformer构建的,这个模型广泛应用于NLP领域,例如机器翻译,问答系统,文本摘要和语音识别等等方向。关于Transrofmer模型的理解特别推荐一位国外博主文章《The Illustrated Transformer》

    Transformer总体结构

    和Attention模型一样,Transformer模型中也采用了 encoer-decoder 架构。但其结构相比于Attention更加复杂,论文中encoder层由6个encoder堆叠在一起,decoder层也一样。
    在这里插入图片描述每一个encoder和decoder的内部简版结构如下图
    在这里插入图片描述
    对于encoder,包含两层,一个self-attention层和一个前馈神经网络,self-attention能帮助当前节点不仅仅只关注当前的词,从而能获取到上下文的语义。decoder也包含encoder提到的两层网络,但是在这两层中间还有一层attention层,帮助当前节点获取到当前需要关注的重点内容。

    现在我们知道了模型的主要组件,接下来我们看下模型的内部细节。首先,模型需要对输入的数据进行一个embedding操作,(也可以理解为类似w2c的操作),enmbedding结束之后,输入到encoder层,self-attention处理完数据后把数据送给前馈神经网络,前馈神经网络的计算可以并行,得到的输出会输入到下一个encoder。
    在这里插入图片描述

    Self-Attention

    接下来我们详细看一下self-attention,其思想和attention类似,但是self-attention是Transformer用来将其他相关单词的“理解”转换成我们正常理解的单词的一种思路,我们看个例子:
    The animal didn't cross the street because it was too tired
    这里的it到底代表的是animal还是street呢,对于我们来说能很简单的判断出来,但是对于机器来说,是很难判断的,self-attention就能够让机器把it和animal联系起来
    在这里插入图片描述
    接下来我们看下详细的处理过程。

    1、首先,self-attention会计算出三个新的向量,在论文中,向量的维度是512维,我们把这三个向量分别称为Query、Key、Value,这三个向量是用embedding向量与一个矩阵相乘得到的结果,这个矩阵是随机初始化的,维度为(64,512)注意第二个维度需要和embedding的维度一样,其值在BP的过程中会一直进行更新,得到的这三个向量的维度是64低于embedding维度的。

    在这里插入图片描述
    那么Query、Key、Value这三个向量又是什么呢?这三个向量对于attention来说很重要,当你理解了下文后,你将会明白这三个向量扮演者什么的角色。

    2、计算self-attention的分数值,该分数值决定了当我们在某个位置encode一个词时,对输入句子的其他部分的关注程度。这个分数值的计算方法是Query与Key做点乘,以下图为例,首先我们需要针对Thinking这个词,计算出其他词对于该词的一个分数值,首先是针对于自己本身即q1·k1,然后是针对于第二个词即q1·k2
    在这里插入图片描述
    3、接下来,把点成的结果除以一个常数,这里我们除以8,这个值一般是采用上文提到的矩阵的第一个维度的开方即64的开方8,当然也可以选择其他的值,然后把得到的结果做一个softmax的计算。得到的结果即是每个词对于当前位置的词的相关性大小,当然,当前位置的词相关性肯定会会很大
    在这里插入图片描述
    4、下一步就是把Value和softmax得到的值进行相乘,并相加,得到的结果即是self-attetion在当前节点的值。
    在这里插入图片描述
    在实际的应用场景,为了提高计算速度,我们采用的是矩阵的方式,直接计算出Query, Key, Value的矩阵,然后把embedding的值与三个矩阵直接相乘,把得到的新矩阵Q与K相乘,乘以一个常数,做softmax操作,最后乘上V矩阵
    在这里插入图片描述
    在这里插入图片描述
    这种通过 query 和 key 的相似性程度来确定 value 的权重分布的方法被称为scaled dot-product attention。其实scaled dot-Product attention就是我们常用的使用点积进行相似度计算的attention,只是多除了一个(为K的维度)起到调节作用,使得内积不至于太大。
    在这里插入图片描述

    Multi-Headed Attention

    这篇论文更厉害的地方是给self-attention加入了另外一个机制,被称为“multi-headed” attention,该机制理解起来很简单,就是说不仅仅只初始化一组Q、K、V的矩阵,而是初始化多组,tranformer是使用了8组,所以最后得到的结果是8个矩阵。
    在这里插入图片描述
    在这里插入图片描述
    这给我们留下了一个小的挑战,前馈神经网络没法输入8个矩阵呀,这该怎么办呢?所以我们需要一种方式,把8个矩阵降为1个,首先,我们把8个矩阵连在一起,这样会得到一个大的矩阵,再随机初始化一个矩阵和这个组合好的矩阵相乘,最后得到一个最终的矩阵。
    在这里插入图片描述
    这就是multi-headed attention的全部流程了,这里其实已经有很多矩阵了,我们把所有的矩阵放到一张图内看一下总体的流程。
    在这里插入图片描述
    多头attention(Multi-head attention)整个过程可以简述为:Query,Key,Value首先进过一个线性变换,然后输入到放缩点积attention(注意这里要做h次,其实也就是所谓的多头,每一次算一个头,而且每次Q,K,V进行线性变换的参数W是不一样的),然后将h次的放缩点积attention结果进行拼接,再进行一次线性变换得到的值作为多头attention的结果。可以看到,google提出来的多头attention的不同之处在于进行了h次计算而不仅仅算一次,论文中说到这样的好处是可以允许模型在不同的表示子空间里学习到相关的信息,后面还会根据attention可视化来验证。
    在这里插入图片描述
    那么在整个模型中,是如何使用attention的呢?如下图,首先在编码器到解码器的地方使用了多头attention进行连接,K,V,Q分别是编码器的层输出(这里K=V)和解码器中都头attention的输入。其实就和主流的机器翻译模型中的attention一样,利用解码器和编码器attention来进行翻译对齐。然后在编码器和解码器中都使用了多头自注意力self-attention来学习文本的表示。Self-attention即K=V=Q,例如输入一个句子,那么里面的每个词都要和该句子中的所有词进行attention计算。目的是学习句子内部的词依赖关系,捕获句子的内部结构。

    在这里插入图片描述
    对于使用自注意力机制的原因,论文中提到主要从三个方面考虑(每一层的复杂度,是否可以并行,长距离依赖学习),并给出了和RNN,CNN计算复杂度的比较。可以看到,如果输入序列n小于表示维度d的话,每一层的时间复杂度self-attention是比较有优势的。当n比较大时,作者也给出了一种解决方案self-attention(restricted)即每个词不是和所有词计算attention,而是只与限制的r个词去计算attention。在并行方面,多头attention和CNN一样不依赖于前一时刻的计算,可以很好的并行,优于RNN。在长距离依赖上,由于self-attention是每个词和所有词都要计算attention,所以不管他们中间有多长距离,最大的路径长度也都只是1。可以捕获长距离依赖关系。
    在这里插入图片描述
    现在我们已经接触了attention的header,让我们重新审视我们之前的例子,看看例句中的“it”这个单词在不同的attention header情况下会有怎样不同的关注点(这里不同颜色代表attention不同头的结果,颜色越深attention值越大)。
    在这里插入图片描述
    当我们对“it”这个词进行编码时,一个注意力的焦点主要集中在“animal”上,而另一个注意力集中在“tired”(两个heads)
    但是,如果我们将所有注意力添加到图片中,可能有点难理解:
    在这里插入图片描述

    Positional Encoding

    到目前为止,transformer模型中还缺少一种解释输入序列中单词顺序的方法。为了处理这个问题,transformer给encoder层和decoder层的输入添加了一个额外的向量Positional Encoding,维度和embedding的维度一样,这个向量采用了一种很独特的方法来让模型学习到这个值,这个向量能决定当前词的位置,或者说在一个句子中不同的词之间的距离。这个位置向量的具体计算方法有很多种,论文中的计算方法如下
    在这里插入图片描述
    其中pos是指当前词在句子中的位置,i是指向量中每个值的index,可以看出,在偶数位置,使用正弦编码,在奇数位置,使用余弦编码。最后把这个Positional Encoding与embedding的值相加,作为输入送到下一层。
    在这里插入图片描述
    为了让模型捕捉到单词的顺序信息,我们添加位置编码向量信息(POSITIONAL ENCODING),位置编码向量不需要训练,它有一个规则的产生方式(上图公式)。

    如果我们的嵌入维度为4,那么实际上的位置编码就如下图所示:
    在这里插入图片描述
    那么生成位置向量需要遵循怎样的规则呢?

    观察下面的图形,每一行都代表着对一个矢量的位置编码。因此第一行就是我们输入序列中第一个字的嵌入向量,每行都包含512个值,每个值介于1和-1之间。我们用颜色来表示1,-1之间的值,这样方便可视化的方式表现出来:
    在这里插入图片描述
    这是一个20个字(行)的(512)列位置编码示例。你会发现它咋中心位置被分为了2半,这是因为左半部分的值是一由一个正弦函数生成的,而右半部分是由另一个函数(余弦)生成。然后将它们连接起来形成每个位置编码矢量。

    Layer normalization

    在transformer中,每一个子层(self-attetion,ffnn)之后都会接一个残差模块,并且有一个Layer normalization
    在这里插入图片描述
    在进一步探索其内部计算方式,我们可以将上面图层可视化为下图:
    在这里插入图片描述
    残差模块相信大家都很清楚了,这里不再讲解,主要讲解下Layer normalization。Normalization有很多种,但是它们都有一个共同的目的,那就是把输入转化成均值为0方差为1的数据。我们在把数据送入激活函数之前进行normalization(归一化),因为我们不希望输入数据落在激活函数的饱和区。

    说到 normalization,那就肯定得提到 Batch Normalization。BN的主要思想就是:在每一层的每一批数据上进行归一化。我们可能会对输入数据进行归一化,但是经过该网络层的作用后,我们的数据已经不再是归一化的了。随着这种情况的发展,数据的偏差越来越大,我的反向传播需要考虑到这些大的偏差,这就迫使我们只能使用较小的学习率来防止梯度消失或者梯度爆炸。

    BN的具体做法就是对每一小批数据,在批这个方向上做归一化。如下图所示:
    在这里插入图片描述
    可以看到,右半边求均值是沿着数据 batch_size的方向进行的,其计算公式如下:
    在这里插入图片描述
    那么什么是 Layer normalization 呢?它也是归一化数据的一种方式,不过 LN 是在每一个样本上计算均值和方差,而不是BN那种在批方向计算均值和方差!在这里插入图片描述
    下面看一下 LN 的公式:
    在这里插入图片描述
    到这里为止就是全部encoders的内容了,如果把两个encoders叠加在一起就是这样的结构,在self-attention需要强调的最后一点是其采用了残差网络中的short-cut结构,目的是解决深度学习中的退化问题。
    在这里插入图片描述

    Decoder层

    在这里插入图片描述
    上图是transformer的一个详细结构,相比本文一开始结束的结构图会更详细些,接下来,我们会按照这个结构图讲解下decoder部分。

    可以看到decoder部分其实和encoder部分大同小异,不过在最下面额外多了一个masked mutil-head attetion,这里的mask也是transformer一个很关键的技术,我们一起来看一下。

    Mask

    mask 表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。Transformer 模型里面涉及两种 mask,分别是 padding mask 和 sequence mask。

    其中,padding mask 在所有的 scaled dot-product attention 里面都需要用到,而 sequence mask 只有在 decoder 的 self-attention 里面用到。

    Padding Mask

    什么是 padding mask 呢?因为每个批次输入序列长度是不一样的也就是说,我们要对输入序列进行对齐。具体来说,就是给在较短的序列后面填充 0。但是如果输入的序列太长,则是截取左边的内容,把多余的直接舍弃。因为这些填充的位置,其实是没什么意义的,所以我们的attention机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。

    具体的做法是,把这些位置的值加上一个非常大的负数(负无穷),这样的话,经过 softmax,这些位置的概率就会接近0!

    而我们的 padding mask 实际上是一个张量,每个值都是一个Boolean,值为 false 的地方就是我们要进行处理的地方。

    Sequence mask

    文章前面也提到,sequence mask 是为了使得 decoder 不能看见未来的信息。也就是对于一个序列,在 time_step 为 t 的时刻,我们的解码输出应该只能依赖于 t 时刻之前的输出,而不能依赖 t 之后的输出。因此我们需要想一个办法,把 t 之后的信息给隐藏起来。

    那么具体怎么做呢?也很简单:产生一个上三角矩阵,上三角的值全为0。把这个矩阵作用在每一个序列上,就可以达到我们的目的。

    • 对于 decoder 的 self-attention,里面使用到的 scaled dot-product attention,同时需要padding mask 和 sequence mask 作为 attn_mask,具体实现就是两个mask相加作为attn_mask。
    • 其他情况,attn_mask 一律等于 padding mask。

    编码器通过处理输入序列启动。然后将顶部编码器的输出转换为一组注意向量k和v。每个解码器将在其“encoder-decoder attention”层中使用这些注意向量,这有助于解码器将注意力集中在输入序列中的适当位置:
    在这里插入图片描述
    完成编码阶段后,我们开始解码阶段。解码阶段的每个步骤从输出序列(本例中为英语翻译句)输出一个元素。
    以下步骤重复此过程,一直到达到表示解码器已完成输出的符号。每一步的输出在下一个时间步被送入底部解码器,解码器像就像我们对编码器输入所做操作那样,我们将位置编码嵌入并添加到这些解码器输入中,以表示每个字的位置。
    在这里插入图片描述

    输出层

    当decoder层全部执行完毕后,怎么把得到的向量映射为我们需要的词呢,很简单,只需要在结尾再添加一个全连接层和softmax层,假如我们的词典是1w个词,那最终softmax会输入1w个词的概率,概率值最大的对应的词就是我们最终的结果。
    在这里插入图片描述

    BERT原理详解

    从创新的角度来看,bert其实并没有过多的结构方面的创新点,其和GPT一样均是采用的transformer的结构,相对于GPT来说,其是双向结构的,而GPT是单向的,如下图所示
    在这里插入图片描述
    elmo:将上下文当作特征,但是无监督的语料和我们真实的语料还是有区别的,不一定的符合我们特定的任务,是一种双向的特征提取。

    openai gpt就做了一个改进,也是通过transformer学习出来一个语言模型,不是固定的,通过任务 finetuning,用transfomer代替elmo的lstm。
    openai gpt其实就是缺少了encoder的transformer。当然也没了encoder与decoder之间的attention。

    openAI gpt虽然可以进行fine-tuning,但是有些特殊任务与pretraining输入有出入,单个句子与两个句子不一致的情况,很难解决,还有就是decoder只能看到前面的信息。
    其次bert在多方面的nlp任务变现来看效果都较好,具备较强的泛化能力,对于特定的任务只需要添加一个输出层来进行fine-tuning即可。

    结构

    先看下bert的内部结构,官网最开始提供了两个版本,L表示的是transformer的层数,H表示输出的维度,A表示mutil-head attention的个数
    在这里插入图片描述
    如今已经增加了多个模型,中文是其中唯一一个非英语的模型。
    在这里插入图片描述
    从模型的层数来说其实已经很大了,但是由于transformer的残差(residual)模块,层数并不会引起梯度消失等问题,但是并不代表层数越多效果越好,有论点认为低层偏向于语法特征学习,高层偏向于语义特征学习。

    预训练模型

    首先我们要了解一下什么是预训练模型,举个例子,假设我们有大量的维基百科数据,那么我们可以用这部分巨大的数据来训练一个泛化能力很强的模型,当我们需要在特定场景使用时,例如做文本相似度计算,那么,只需要简单的修改一些输出层,再用我们自己的数据进行一个增量训练,对权重进行一个轻微的调整。

    预训练的好处在于在特定场景使用时不需要用大量的语料来进行训练,节约时间效率高效,bert就是这样的一个泛化能力较强的预训练模型。

    BERT的预训练过程

    接下来我们看看BERT的预训练过程,BERT的预训练阶段包括两个任务,一个是Masked Language Model,还有一个是Next Sentence Prediction。

    Masked Language Model

    MLM可以理解为完形填空,作者会随机mask每一个句子中15%的词,用其上下文来做预测,例如:my dog is hairy → my dog is [MASK]

    此处将hairy进行了mask处理,然后采用非监督学习的方法预测mask位置的词是什么,但是该方法有一个问题,因为是mask15%的词,其数量已经很高了,这样就会导致某些词在fine-tuning阶段从未见过,为了解决这个问题,作者做了如下的处理:

    • 80%的时间是采用[mask],my dog is hairy → my dog is [MASK]

    • 10%的时间是随机取一个词来代替mask的词,my dog is hairy -> my dog is apple

    • 10%的时间保持不变,my dog is hairy -> my dog is hairy

    那么为啥要以一定的概率使用随机词呢?这是因为transformer要保持对每个输入token分布式的表征,否则Transformer很可能会记住这个[MASK]就是"hairy"。至于使用随机词带来的负面影响,文章中解释说,所有其他的token(即非"hairy"的token)共享15%*10% = 1.5%的概率,其影响是可以忽略不计的。Transformer全局的可视,又增加了信息的获取,但是不让模型获取全量信息。
    注意:

    • 有参数dupe_factor决定数据duplicate的次数。
    • 其中,create_instance_from_document函数,是构造了一个sentence-pair的样本。对每一句,先生成[CLS]+A+[SEP]+B+[SEP],有长(0.9)有短(0.1),再加上mask,然后做成样本类object。
    • create_masked_lm_predictions函数返回的tokens是已经被遮挡词替换之后的tokens
    • masked_lm_labels则是遮挡词对应位置真实的label。

    Next Sentence Prediction

    选择一些句子对A与B,其中50%的数据B是A的下一条句子,剩余50%的数据B是语料库中随机选择的,学习其中的相关性,添加这样的预训练的目的是目前很多NLP的任务比如QA和NLI都需要理解两个句子之间的关系,从而能让预训练的模型更好的适应这样的任务。
    个人理解:

    • Bert先是用Mask来提高视野范围的信息获取量,增加duplicate再随机Mask,这样跟RNN类方法依次训练预测没什么区别了除了mask不同位置外;

    • 全局视野极大地降低了学习的难度,然后再用A+B/C来作为样本,这样每条样本都有50%的概率看到一半左右的噪声;

    • 但直接学习Mask A+B/C是没法学习的,因为不知道哪些是噪声,所以又加上next_sentence预测任务,与MLM同时进行训练,这样用next来辅助模型对噪声/非噪声的辨识,用MLM来完成语义的大部分的学习。
      在这里插入图片描述

    输入

    bert的输入可以是单一的一个句子或者是句子对,实际的输入值是segment embedding与position embedding相加,具体的操作流程可参考上面的transformer讲解。

    BERT的输入词向量是三个向量之和:

    Token Embedding:WordPiece tokenization subword词向量。
    Segment Embedding:表明这个词属于哪个句子(NSP需要两个句子)。
    Position Embedding:学习出来的embedding向量。这与Transformer不同,Transformer中是预先设定好的值。

    在这里插入图片描述

    总结

    在这里插入图片描述
    BERT的去除实验表明,双向LM和NSP带了的提升最大。

    在这里插入图片描述
    另一个结论是,增加模型参数数量可以提升模型效果。

    在这里插入图片描述
    BERT预训练模型的输出结果,无非就是一个或多个向量。下游任务可以通过精调(改变预训练模型参数)或者特征抽取(不改变预训练模型参数,只是把预训练模型的输出作为特征输入到下游任务)两种方式进行使用。BERT原论文使用了精调方式,但也尝试了特征抽取方式的效果,比如在NER任务上,最好的特征抽取方式只比精调差一点点。但特征抽取方式的好处可以预先计算好所需的向量,存下来就可重复使用,极大提升下游任务模型训练的速度。
    在这里插入图片描述
    后来也有其他人针对ELMo和BERT比较了这两种使用方式的精度差异。下面列出基本结论:

    在这里插入图片描述
    在这里插入图片描述
    总结下BERT的主要贡献:

    • 引入了Masked LM,使用双向LM做模型预训练。
    • 为预训练引入了新目标NSP,它可以学习句子与句子间的关系。
    • 进一步验证了更大的模型效果更好: 12 --> 24 层。
    • 为下游任务引入了很通用的求解框架,不再为任务做模型定制。
    • 刷新了多项NLP任务的记录,引爆了NLP无监督预训练技术。

    BERT是谷歌团队糅合目前已有的NLP知识集大成者,刷新11条赛道彰显了无与伦比的实力,且极容易被用于多种NLP任务。宛若一束烟花点亮在所有NLP从业者心中。更为可贵的是谷歌选择了开源这些,让所有从业者看到了在各行各业落地的更多可能性。

    BERT优点

    • Transformer Encoder因为有Self-attention机制,因此BERT自带双向功能

    • 因为双向功能以及多层Self-attention机制的影响,使得BERT必须使用Cloze版的语言模型Masked-LM来完成token级别的预训练

    • 为了获取比词更高级别的句子级别的语义表征,BERT加入了Next Sentence Prediction来和Masked-LM一起做联合训练

    • 为了适配多任务下的迁移学习,BERT设计了更通用的输入层和输出层

    • 微调成本小

    BERT缺点

    • task1的随机遮挡策略略显粗犷,推荐阅读《Data Nosing As Smoothing In Neural Network Language Models》

    • [MASK]标记在实际预测中不会出现,训练时用过多[MASK]影响模型表现;

    • 每个batch只有15%的token被预测,所以BERT收敛得比left-to-right模型要慢(它们会预测每个token)

    • BERT对硬件资源的消耗巨大(大模型需要16个tpu,历时四天;更大的模型需要64个tpu,历时四天。

    关于BERT最新的各领域应用推荐张俊林的Bert时代的创新(应用篇)

    思考

    • 个人并不认为文章是模型的改进,更认可为任务的设计改进。

    • 论文作者只比较了有没有task1的影响,并没有针对task2对比试验。提升是否来自好的预训练任务设计没有明说。

    • bert对nlp领域目前已有知识的有效“整合”,在硬件配置足够的情况下能提高nlp多领域性能

    BERT适用场景

    第一,如果NLP任务偏向在语言本身中就包含答案,而不特别依赖文本外的其它特征,往往应用Bert能够极大提升应用效果。典型的任务比如QA和阅读理解,正确答案更偏向对语言的理解程度,理解能力越强,解决得越好,不太依赖语言之外的一些判断因素,所以效果提升就特别明显。反过来说,对于某些任务,除了文本类特征外,其它特征也很关键,比如搜索的用户行为/链接分析/内容质量等也非常重要,所以Bert的优势可能就不太容易发挥出来。再比如,推荐系统也是类似的道理,Bert可能只能对于文本内容编码有帮助,其它的用户行为类特征,不太容易融入Bert中。

    第二,Bert特别适合解决句子或者段落的匹配类任务。就是说,Bert特别适合用来解决判断句子关系类问题,这是相对单文本分类任务和序列标注等其它典型NLP任务来说的,很多实验结果表明了这一点。而其中的原因,我觉得很可能主要有两个,一个原因是:很可能是因为Bert在预训练阶段增加了Next Sentence Prediction任务,所以能够在预训练阶段学会一些句间关系的知识,而如果下游任务正好涉及到句间关系判断,就特别吻合Bert本身的长处,于是效果就特别明显。第二个可能的原因是:因为Self Attention机制自带句子A中单词和句子B中任意单词的Attention效果,而这种细粒度的匹配对于句子匹配类的任务尤其重要,所以Transformer的本质特性也决定了它特别适合解决这类任务。

    从上面这个Bert的擅长处理句间关系类任务的特性,我们可以继续推理出以下观点:

    既然预训练阶段增加了Next Sentence Prediction任务,就能对下游类似性质任务有较好促进作用,那么是否可以继续在预训练阶段加入其它的新的辅助任务?而这个辅助任务如果具备一定通用性,可能会对一类的下游任务效果有直接促进作用。这也是一个很有意思的探索方向,当然,这种方向因为要动Bert的第一个预训练阶段,所以属于NLP届土豪们的工作范畴,穷人们还是散退、旁观、鼓掌、叫好为妙。

    第三,Bert的适用场景,与NLP任务对深层语义特征的需求程度有关。感觉越是需要深层语义特征的任务,越适合利用Bert来解决;而对有些NLP任务来说,浅层的特征即可解决问题,典型的浅层特征性任务比如分词,POS词性标注,NER,文本分类等任务,这种类型的任务,只需要较短的上下文,以及浅层的非语义的特征,貌似就可以较好地解决问题,所以Bert能够发挥作用的余地就不太大,有点杀鸡用牛刀,有力使不出来的感觉。

    这很可能是因为Transformer层深比较深,所以可以逐层捕获不同层级不同深度的特征。于是,对于需要语义特征的问题和任务,Bert这种深度捕获各种特征的能力越容易发挥出来,而浅层的任务,比如分词/文本分类这种任务,也许传统方法就能解决得比较好,因为任务特性决定了,要解决好它,不太需要深层特征。

    第四,Bert比较适合解决输入长度不太长的NLP任务,而输入比较长的任务,典型的比如文档级别的任务,Bert解决起来可能就不太好。主要原因在于:Transformer的self attention机制因为要对任意两个单词做attention计算,所以时间复杂度是n平方,n是输入的长度。如果输入长度比较长,Transformer的训练和推理速度掉得比较厉害,于是,这点约束了Bert的输入长度不能太长。所以对于输入长一些的文档级别的任务,Bert就不容易解决好。结论是:Bert更适合解决句子级别或者段落级别的NLP任务。

    如果有小伙伴坚持看到这里的话深表感谢,本来要继续写源码分析和具体的实践了。时间关系,等下周再抽时间写源码分析与实践部分吧。本文仅用于笔者自己总结自己BERT学习之路,期间引用很多专家学者的观点思路,深表感谢。第一次驾驭这么长的技术文章,每个知识点都想写点,感觉越写越乱。若有读者在阅读本文期间有不好的阅读体验深表歉意。

    展开全文
  • [NLP自然语言处理]谷歌BERT模型深度解析

    万次阅读 多人点赞 2018-10-15 17:49:18
    我的机器学习教程「美团」算法工程师带你入门机器学习 已经开始更新了,欢迎大家订阅~ 任何关于算法、编程、AI行业知识或博客内容的问题,可以随时扫码关注公众号「图灵的猫」,加入”...BERT模型代码已经发布,...

    任何关于算法、编程、AI行业知识或博客内容的问题,可以随时扫码关注公众号「图灵的猫」,加入”学习小组“,沙雕博主在线答疑~此外,公众号内还有更多AI、算法、编程和大数据知识分享,以及免费的SSR节点和学习资料。其他平台(知乎/B站)也是同名「图灵的猫」,不要迷路哦~

     

    BERT模型代码已经发布,可以在我的github: NLP-BERT--Python3.6-pytorch 中下载,请记得start哦


    目录

    一、前言

    二、如何理解BERT模型

    三、BERT模型解析

          论文的核心:详解BERT模型架构

          关键创新:预训练任务

          实验结果

    四、BERT模型的影响

    对BERT模型的观点

    参考文献


    一、前言

     

    最近谷歌搞了个大新闻,公司AI团队新发布的BERT模型,在机器阅读理解顶级水平测试SQuAD1.1中表现出惊人的成绩:全部两个衡量指标上全面超越人类,并且还在11种不同NLP测试中创出最佳成绩,包括将GLUE基准推至80.4%(绝对改进7.6%),MultiNLI准确度达到86.7% (绝对改进率5.6%)等。可以预见的是,BERT将为NLP带来里程碑式的改变,也是NLP领域近期最重要的进展。

    Rank

    谷歌团队的Thang Luong直接定义:BERT模型开启了NLP的新时代!

    从现在的大趋势来看,使用某种模型预训练一个语言模型看起来是一种比较靠谱的方法。从之前AI2的 ELMo,到 OpenAI的fine-tune transformer,再到Google的这个BERT,全都是对预训练的语言模型的应用。

    BERT这个模型与其它两个不同的是

    1. 它在训练双向语言模型时以减小的概率把少量的词替成了Mask或者另一个随机的词。我个人感觉这个目的在于使模型被迫增加对上下文的记忆。至于这个概率,我猜是Jacob拍脑袋随便设的。
    2. 增加了一个预测下一句的loss。这个看起来就比较新奇了。

    BERT模型具有以下两个特点:

    第一,是这个模型非常的深,12层,并不宽(wide),中间层只有1024,而之前的Transformer模型中间层有2048。这似乎又印证了计算机图像处理的一个观点——深而窄 比 浅而宽 的模型更好。

    第二,MLM(Masked Language Model),同时利用左侧和右侧的词语,这个在ELMo上已经出现了,绝对不是原创。其次,对于Mask(遮挡)在语言模型上的应用,已经被Ziang Xie提出了(我很有幸的也参与到了这篇论文中):[1703.02573] Data Noising as Smoothing in Neural Network Language Models。这也是篇巨星云集的论文:Sida Wang,Jiwei Li(香侬科技的创始人兼CEO兼史上发文最多的NLP学者),Andrew Ng,Dan Jurafsky都是Coauthor。但很可惜的是他们没有关注到这篇论文。用这篇论文的方法去做Masking,相信BRET的能力说不定还会有提升。

     

    二、如何理解BERT模型

    [1] BERT 要解决什么问题?

    通常情况 transformer 模型有很多参数需要训练。譬如 BERT BASE 模型: L=12, H=768, A=12, 需要训练的模型参数总数是 12 * 768 * 12 = 110M。这么多参数需要训练,自然需要海量的训练语料。如果全部用人力标注的办法,来制作训练数据,人力成本太大。

    受《A Neural Probabilistic Language Model》论文的启发,BERT 也用 unsupervised 的办法,来训练 transformer 模型。神经概率语言模型这篇论文,主要讲了两件事儿,1. 能否用数值向量(word vector)来表达自然语言词汇的语义?2. 如何给每个词汇,找到恰当的数值向量?

    Transformer

    这篇论文写得非常精彩,深入浅出,要言不烦,而且面面俱到。经典论文,值得反复咀嚼。很多同行朋友都熟悉这篇论文,内容不重复说了。常用的中文汉字有 3500 个,这些字组合成词汇,中文词汇数量高达 50 万个。假如词向量的维度是 512,那么语言模型的参数数量,至少是 512 * 50万 = 256M

    模型参数数量这么大,必然需要海量的训练语料。从哪里收集这些海量的训练语料?《A Neural Probabilistic Language Model》这篇论文说,每一篇文章,天生是训练语料。难道不需要人工标注吗?回答,不需要。

    我们经常说,“说话不要颠三倒四,要通顺,要连贯”,意思是上下文的词汇,应该具有语义的连贯性。基于自然语言的连贯性,语言模型根据前文的词,预测下一个将出现的词。如果语言模型的参数正确,如果每个词的词向量设置正确,那么语言模型的预测,就应该比较准确。天下文章,数不胜数,所以训练数据,取之不尽用之不竭。

    深度学习四大要素,1. 训练数据、2. 模型、3. 算力、4. 应用。训练数据有了,接下去的问题是模型。

     

    [2] BERT 的五个关键词 Pre-training、Deep、Bidirectional、Transformer、Language Understanding 分别是什么意思?

    《A Neural Probabilistic Language Model》这篇论文讲的 Language Model,严格讲是语言生成模型(Language Generative Model),预测语句中下一个将会出现的词汇。语言生成模型能不能直接移用到其它 NLP 问题上去?

    譬如,淘宝上有很多用户评论,能否把每一条用户转换成评分?-2、-1、0、1、2,其中 -2 是极差,+2 是极好。假如有这样一条用户评语,“买了一件鹿晗同款衬衫,没想到,穿在自己身上,不像小鲜肉,倒像是厨师”,请问这条评语,等同于 -2,还是其它?

    语言生成模型,能不能很好地解决上述问题?进一步问,有没有 “通用的” 语言模型,能够理解语言的语义,适用于各种 NLP 问题?BERT 这篇论文的题目很直白,《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》,一眼看去,就能猜得到这篇文章会讲哪些内容。

    这个题目有五个关键词,分别是 Pre-training、Deep、Bidirectional、Transformers、和 Language Understanding。其中 pre-training 的意思是,作者认为,确实存在通用的语言模型,先用文章预训练通用模型,然后再根据具体应用,用 supervised 训练数据,精加工(fine tuning)模型,使之适用于具体应用。为了区别于针对语言生成的 Language Model,作者给通用的语言模型,取了一个名字,叫语言表征模型 Language Representation Model。

    能实现语言表征目标的模型,可能会有很多种,具体用哪一种呢?作者提议,用 Deep Bidirectional Transformers 模型。假如给一个句子 “能实现语言表征[mask]的模型”,遮盖住其中“目标”一词。从前往后预测[mask],也就是用“能/实现/语言/表征”,来预测[mask];或者,从后往前预测[mask],也就是用“模型/的”,来预测[mask],称之为单向预测 unidirectional。单向预测,不能完整地理解整个语句的语义。于是研究者们尝试双向预测。把从前往后,与从后往前的两个预测,拼接在一起 [mask1/mask2],这就是双向预测 bi-directional。细节参阅《Neural Machine Translation by Jointly Learning to Align and Translate》。

    BERT 的作者认为,bi-directional 仍然不能完整地理解整个语句的语义,更好的办法是用上下文全向来预测[mask],也就是用 “能/实现/语言/表征/../的/模型”,来预测[mask]。BERT 作者把上下文全向的预测方法,称之为 deep bi-directional。如何来实现上下文全向预测呢?BERT 的作者建议使用 Transformer 模型。这个模型由《Attention Is All You Need》一文发明。

    这个模型的核心是聚焦机制,对于一个语句,可以同时启用多个聚焦点,而不必局限于从前往后的,或者从后往前的,序列串行处理。不仅要正确地选择模型的结构,而且还要正确地训练模型的参数,这样才能保障模型能够准确地理解语句的语义。BERT 用了两个步骤,试图去正确地训练模型的参数。第一个步骤是把一篇文章中,15% 的词汇遮盖,让模型根据上下文全向地预测被遮盖的词。假如有 1 万篇文章,每篇文章平均有 100 个词汇,随机遮盖 15% 的词汇,模型的任务是正确地预测这 15 万个被遮盖的词汇。通过全向预测被遮盖住的词汇,来初步训练 Transformer 模型的参数。然后,用第二个步骤继续训练模型的参数。譬如从上述 1 万篇文章中,挑选 20 万对语句,总共 40 万条语句。挑选语句对的时候,其中 2*10 万对语句,是连续的两条上下文语句,另外 2*10 万对语句,不是连续的语句。然后让 Transformer 模型来识别这 20 万对语句,哪些是连续的,哪些不连续。

    这两步训练合在一起,称为预训练 pre-training。训练结束后的 Transformer 模型,包括它的参数,是作者期待的通用的语言表征模型。

     

    三、BERT模型解析

    首先来看下谷歌AI团队做的这篇论文。

    BERT的新语言表示模型,它代表Transformer的双向编码器表示。与最近的其他语言表示模型不同,BERT旨在通过联合调节所有层中的上下文来预先训练深度双向表示。因此,预训练的BERT表示可以通过一个额外的输出层进行微调,适用于广泛任务的最先进模型的构建,比如问答任务和语言推理,无需针对具体任务做大幅架构修改。

    论文作者认为现有的技术严重制约了预训练表示的能力。其主要局限在于标准语言模型是单向的,这使得在模型的预训练中可以使用的架构类型很有限。

    在论文中,作者通过提出BERT:即Transformer的双向编码表示来改进基于架构微调的方法。

    BERT 提出一种新的预训练目标:遮蔽语言模型(masked language model,MLM),来克服上文提到的单向性局限。MLM 的灵感来自 Cloze 任务(Taylor, 1953)。MLM 随机遮蔽模型输入中的一些 token,目标在于仅基于遮蔽词的语境来预测其原始词汇 id。

    与从左到右的语言模型预训练不同,MLM 目标允许表征融合左右两侧的语境,从而预训练一个深度双向 Transformer。除了遮蔽语言模型之外,本文作者还引入了一个“下一句预测”(next sentence prediction)任务,可以和MLM共同预训练文本对的表示。

    论文的主要贡献在于:

    • 证明了双向预训练对语言表示的重要性。与之前使用的单向语言模型进行预训练不同,BERT使用遮蔽语言模型来实现预训练的深度双向表示。
    • 论文表明,预先训练的表示免去了许多工程任务需要针对特定任务修改体系架构的需求。 BERT是第一个基于微调的表示模型,它在大量的句子级和token级任务上实现了最先进的性能,强于许多面向特定任务体系架构的系统。
    • BERT刷新了11项NLP任务的性能记录。本文还报告了 BERT 的模型简化研究(ablation study),表明模型的双向性是一项重要的新成果。相关代码和预先训练的模型将会公布在goo.gl/language/bert上。

    BERT目前已经刷新的11项自然语言处理任务的最新记录包括:将GLUE基准推至80.4%(绝对改进7.6%),MultiNLI准确度达到86.7% (绝对改进率5.6%),将SQuAD v1.1问答测试F1得分纪录刷新为93.2分(绝对提升1.5分),超过人类表现2.0分。

    论文的核心:详解BERT模型架构

    本节介绍BERT模型架构和具体实现,并介绍预训练任务,这是这篇论文的核心创新。

    模型架构

    BERT的模型架构是基于Vaswani et al. (2017) 中描述的原始实现multi-layer bidirectional Transformer编码器,并在tensor2tensor库中发布。由于Transformer的使用最近变得无处不在,论文中的实现与原始实现完全相同,因此这里将省略对模型结构的详细描述。

    在这项工作中,论文将层数(即Transformer blocks)表示为L,将隐藏大小表示为H,将self-attention heads的数量表示为A。在所有情况下,将feed-forward/filter 的大小设置为 4H,即H = 768时为3072,H = 1024时为4096。论文主要报告了两种模型大小的结果:

    • BERT_{BASE} : L=12, H=768, A=12, Total Parameters=110M
    • BERT_{LARGE} : L=24, H=1024, A=16, Total Parameters=340M

    为了进行比较,论文选择了 BERT_{LARGE} ,它与OpenAI GPT具有相同的模型大小。然而,重要的是,BERT Transformer 使用双向self-attention,而GPT Transformer 使用受限制的self-attention,其中每个token只能处理其左侧的上下文。研究团队注意到,在文献中,双向 Transformer 通常被称为“Transformer encoder”,而左侧上下文被称为“Transformer decoder”,因为它可以用于文本生成。BERT,OpenAI GPT和ELMo之间的比较如图1所示。

    图1:预训练模型架构的差异。BERT使用双向Transformer。OpenAI GPT使用从左到右的Transformer。ELMo使用经过独立训练的从左到右和从右到左LSTM的串联来生成下游任务的特征。三个模型中,只有BERT表示在所有层中共同依赖于左右上下文。

    输入表示(input representation)

    论文的输入表示(input representation)能够在一个token序列中明确地表示单个文本句子或一对文本句子(例如, [Question, Answer])。对于给定token,其输入表示通过对相应的tokensegmentposition embeddings进行求和来构造。图2是输入表示的直观表示:

    图2:BERT输入表示。输入嵌入是token embeddings, segmentation embeddings 和position embeddings 的总和。

    具体如下:

    • 使用WordPiece嵌入(Wu et al., 2016)和30,000个token的词汇表。用##表示分词。
    • 使用学习的positional embeddings,支持的序列长度最多为512个token。
    • 每个序列的第一个token始终是特殊分类嵌入([CLS])。对应于该token的最终隐藏状态(即,Transformer的输出)被用作分类任务的聚合序列表示。对于非分类任务,将忽略此向量。
    • 句子对被打包成一个序列。以两种方式区分句子。首先,用特殊标记([SEP])将它们分开。其次,添加一个learned sentence A嵌入到第一个句子的每个token中,一个sentence B嵌入到第二个句子的每个token中。
    • 对于单个句子输入,只使用 sentence A嵌入。

    关键创新:预训练任务

    与Peters et al. (2018) 和 Radford et al. (2018)不同,论文不使用传统的从左到右或从右到左的语言模型来预训练BERT。相反,使用两个新的无监督预测任务对BERT进行预训练。

    任务1: Masked LM

    从直觉上看,研究团队有理由相信,深度双向模型比left-to-right 模型或left-to-right and right-to-left模型的浅层连接更强大。遗憾的是,标准条件语言模型只能从左到右或从右到左进行训练,因为双向条件作用将允许每个单词在多层上下文中间接地“see itself”。

    为了训练一个深度双向表示(deep bidirectional representation),研究团队采用了一种简单的方法,即随机屏蔽(masking)部分输入token,然后只预测那些被屏蔽的token。论文将这个过程称为“masked LM”(MLM),尽管在文献中它经常被称为Cloze任务(Taylor, 1953)。

    在这个例子中,与masked token对应的最终隐藏向量被输入到词汇表上的输出softmax中,就像在标准LM中一样。在团队所有实验中,随机地屏蔽了每个序列中15%的WordPiece token。与去噪的自动编码器(Vincent et al., 2008)相反,只预测masked words而不是重建整个输入。

    虽然这确实能让团队获得双向预训练模型,但这种方法有两个缺点。首先,预训练和finetuning之间不匹配,因为在finetuning期间从未看到[MASK]token。为了解决这个问题,团队并不总是用实际的[MASK]token替换被“masked”的词汇。相反,训练数据生成器随机选择15%的token。例如在这个句子“my dog is hairy”中,它选择的token是“hairy”。然后,执行以下过程:

    数据生成器将执行以下操作,而不是始终用[MASK]替换所选单词:

    • 80%的时间:用[MASK]标记替换单词,例如,my dog is hairy → my dog is [MASK]
    • 10%的时间:用一个随机的单词替换该单词,例如,my dog is hairy → my dog is apple
    • 10%的时间:保持单词不变,例如,my dog is hairy → my dog is hairy. 这样做的目的是将表示偏向于实际观察到的单词。

    Transformer encoder不知道它将被要求预测哪些单词或哪些单词已被随机单词替换,因此它被迫保持每个输入token的分布式上下文表示。此外,因为随机替换只发生在所有token的1.5%(即15%的10%),这似乎不会损害模型的语言理解能力

    使用MLM的第二个缺点是每个batch只预测了15%的token,这表明模型可能需要更多的预训练步骤才能收敛。团队证明MLM的收敛速度略慢于 left-to-right的模型(预测每个token),但MLM模型在实验上获得的提升远远超过增加的训练成本。

     

    任务2:下一句预测

    许多重要的下游任务,如问答(QA)和自然语言推理(NLI)都是基于理解两个句子之间的关系,这并没有通过语言建模直接获得。

    在为了训练一个理解句子的模型关系,预先训练一个二进制化的下一句测任务,这一任务可以从任何单语语料库中生成。具体地说,当选择句子A和B作为预训练样本时,B有50%的可能是A的下一个句子,也有50%的可能是来自语料库的随机句子。例如:

    Input = [CLS] the man went to [MASK] store [SEP]

    he bought a gallon [MASK] milk [SEP]

    Label = IsNext

    Input = [CLS] the man [MASK] to the store [SEP]

    penguin [MASK] are flight ##less birds [SEP]

    Label = NotNext

    团队完全随机地选择了NotNext语句,最终的预训练模型在此任务上实现了97%-98%的准确率

    实验结果

    如前文所述,BERT在11项NLP任务中刷新了性能表现记录!在这一节中,团队直观呈现BERT在这些任务的实验结果,具体的实验设置和比较请阅读原论文.

    图3:我们的面向特定任务的模型是将BERT与一个额外的输出层结合而形成的,因此需要从头开始学习最小数量的参数。在这些任务中,(a)和(b)是序列级任务,而(c)和(d)是token级任务。在图中,E表示输入嵌入,Ti表示tokeni的上下文表示,[CLS]是用于分类输出的特殊符号,[SEP]是用于分隔非连续token序列的特殊符号。

    图4:GLUE测试结果,由GLUE评估服务器给出。每个任务下方的数字表示训练样例的数量。“平均”一栏中的数据与GLUE官方评分稍有不同,因为我们排除了有问题的WNLI集。BERT 和OpenAI GPT的结果是单模型、单任务下的数据。所有结果来自https://gluebenchmark.com/leaderboardhttps://blog.openai.com/language-unsupervised/

    图5:SQuAD 结果。BERT 集成是使用不同预训练检查点和fine-tuning seed的 7x 系统。

    图6:CoNLL-2003 命名实体识别结果。超参数由开发集选择,得出的开发和测试分数是使用这些超参数进行五次随机重启的平均值。

     

    四、BERT模型的影响

    BERT是一个语言表征模型(language representation model),通过超大数据、巨大模型、和极大的计算开销训练而成,在11个自然语言处理的任务中取得了最优(state-of-the-art, SOTA)结果。或许你已经猜到了此模型出自何方,没错,它产自谷歌。估计不少人会调侃这种规模的实验已经基本让一般的实验室和研究员望尘莫及了,但它确实给我们提供了很多宝贵的经验:

    1. 深度学习就是表征学习 (Deep learning is representation learning):"We show that pre-trained representations eliminate the needs of many heavily engineered task-specific architectures". 在11项BERT刷出新境界的任务中,大多只在预训练表征(pre-trained representation)微调(fine-tuning)的基础上加一个线性层作为输出(linear output layer)。在序列标注的任务里(e.g. NER),甚至连序列输出的依赖关系都先不管(i.e. non-autoregressive and no CRF),照样秒杀之前的SOTA,可见其表征学习能力之强大。
    2. 规模很重要(Scale matters):"One of our core claims is that the deep bidirectionality of BERT, which is enabled by masked LM pre-training, is the single most important improvement of BERT compared to previous work". 这种遮挡(mask)在语言模型上的应用对很多人来说已经不新鲜了,但确是BERT的作者在如此超大规模的数据+模型+算力的基础上验证了其强大的表征学习能力。这样的模型,甚至可以延伸到很多其他的模型,可能之前都被不同的实验室提出和试验过,只是由于规模的局限没能充分挖掘这些模型的潜力,而遗憾地让它们被淹没在了滚滚的paper洪流之中。
    3. 预训练价值很大(Pre-training is important):"We believe that this is the first work to demonstrate that scaling to extreme model sizes also leads to large improvements on very small-scale tasks, provided that the model has been sufficiently pre-trained". 预训练已经被广泛应用在各个领域了(e.g. ImageNet for CV, Word2Vec in NLP),多是通过大模型大数据,这样的大模型给小规模任务能带来的提升有几何,作者也给出了自己的答案。BERT模型的预训练是用Transformer做的,但我想换做LSTM或者GRU的话应该不会有太大性能上的差别,当然训练计算时的并行能力就另当别论了。

    对BERT模型的观点
     

    0. high-performance的原因其实还是归结于两点,除了模型的改进,更重要的是用了超大的数据集(BooksCorpus 800M + English Wikipedia 2.5G单词)和超大的算力(对应于超大模型)在相关的任务上做预训练,实现了在目标任务上表现的单调增长

    1. 这个模型的双向和Elmo不一样,大部分人对他这个双向在novelty上的contribution 的大小有误解,我觉得这个细节可能是他比Elmo显著提升的原因。Elmo是拼一个左到右和一个右到左,他这个是训练中直接开一个窗口,用了个有顺序的cbow。

    2. 可复现性差:有钱才能为所欲为(Reddit对跑一次BERT的价格讨论)

    For TPU pods:
    
    4 TPUs * ~$2/h (preemptible) * 24 h/day * 4 days = $768 (base model)
    
    16 TPUs = ~$3k (large model)
    
    
    
    For TPU:
    
    16 tpus * $8/hr * 24 h/day * 4 days = 12k
    
    64 tpus * $8/hr * 24 h/day * 4 days = 50k

    最后他问到:For GPU:"BERT-Large is 24-layer, 1024-hidden and was trained for 40 epochs over a 3.3 billion word corpus. So maybe 1 year to train on 8 P100s? " ,然后这个就很interesting了。

    参考文献

    1. 知乎:如何评价谷歌最新的BERT模型

    2. 华尔街见闻:NLP历史突破

    3. OPENAI-Improving Language Understanding with Unsupervised Learning

    4. https://gluebenchmark.com/leaderboard

     

    >>>关于作者

    CSDN 博客专家,2019-CSDN百大博主,计算机(机器学习方向)博士在读,业余Kaggle选手,有过美团、腾讯算法工程师经历,目前就职于Amazon AI lab。喜爱分享和知识整合。

    关注微信公众号,点击“学习资料”菜单即可获取算法、编程资源以及教学视频,还有免费SSR节点相送哦。其他平台(微信/知乎/B站),欢迎关注同名公众号「图灵的猫」~

    展开全文
  • Bert-源码

    2021-03-19 18:18:23
    Bert
  • BERTBERTBERT!

    千次阅读 2020-12-20 21:46:25
    BERT的基本原理、模型架构、迁移特性、细节说明。BERT详解!

    从ELMO说起的预训练语言模型

    我们先来看一张图:

    image-20201215101643989

    从图中可以看到,ELMO其实是NLP模型发展的一个转折点,从ELMO开始,Pre-training+finetune的模式开始崭露头角并逐渐流行起来。最初的ELMO也只是为了解决word2vec不能表达”一词多义“的问题提出来的,它所代表的动态词向量的思想更是被不少任务拿来借鉴。它最大的贡献还是提出的预训练+微调的模式,这种迁移学习的思想几乎是它作为NLP模型发展转折点的一个重要原因。

    我们简单地看一下ELMO的模型结构(我加了红色标注便于直观理解):

    image-20201217003923670

    ELMO有一个很明显的特征就是它使用的是双层双向的LSTM,最后网络会产生一个动态词向量,它来自于不同的层输出的组合,由于不同层学习到的特征不一样,最后通过权重的调整向量表征的含义侧重点也不一样。与CV中的模型类似,越靠近输入层的层级学习到的特征越简单,比如W1可能是一些词性或字的特征;越靠近输出层的层级学习到的特征越高级,比如W3可能就是学习到一些句子级的特征。动态词向量偏重于哪一层取决于下游的任务,举个例子,序列标注任务多偏重于底层级一般效果就会更好一点。

    ELMO在预训练阶段得到的网络参数再拿到具体任务中进行微调,这种迁移学习的思想大大降低了一些任务对大数据量的依赖性,在节约成本的同时效果也很不错,这为后面的BERT的出现铺平了道路,也慢慢引领了一个时代的潮流。

    ELMO的缺点在其他文章中也有提及:ELMO 使用了 LSTM ,特征提取能力没有Transformer好;ELMO采取双向拼接这种融合特征的能力可能比 Bert 一体化的融合特征方式弱。但这都是事后视角了,了解即可。

    BERT详解

    BERT的总体结构

    先来看看BERT的结构框架:

    image-20201217014110062

    上面就是BERT的模型结构图了,是不是和ELMO长的有点像,模型的核心由BERT Encoder组成,BERT Encoder由多层BERT Layer组成,每一层的BERT Layer其实都是Transformer中的Encoder Block。再来回顾一下图:

    image-20201217015348332

    说到这里其实我们就已经了解到了BERT的一个大体形貌了,对于Transformer中的Encoder Block有点生疏的话可以再去回顾一下:Transformer 看这一篇就够了,相信可以回忆起来的。

    由于BERT使用的是Transformer中的Encoder Block,所以其本质还是一个特征提取器,只不过由于网络较深和Self-Attention机制的帮衬,它的特征提取/模型学习能力更强。也正是由于它只是Encoder的缘故,所以BERT也不具备生成能力,没法单独用BERT来解决NLG的问题,只能解决NLU的问题。

    BERT的输入

    还是老规矩,对着图说:

    image-20201217152751053

    如图所示,BERT模型输入的Embedding由Token Embeddings、Segment Embeddings和Position Embeddings相加而成,如下图。我们分别来看一下这三部分:

    • Token Embeddings:最传统的词向量,与之前我们一起学习的语言模型的输入一样,它是将token嵌入到一个高维空间中表示的结果。对这一部分的Embeddings,有几点需要注意:
    • token的选取与之前的Transformer不太一样,在这里采用的一种新的token形式,是一种子词粒度的表现形式,称之为subword。说的大白话一点就是中文还是字符级别的token,英文不再按照传统的空格分词,而是将部分单词再拆分成字根的形式,比如playing拆成play+ing。这样做最大的好处就是字典变小了,计算量也变小了,同时也在一定程度上解决了OOV的问题。

      常用的subword的方法可参见:深入理解NLP Subword算法:BPE、WordPiece、ULM

    • 输入token时,第一个token是一个[CLS]的特殊字符,可以用于下游的任务,比如分类任务。句子和句子的中间,以及句子的末尾会有一个特殊的符号[SEP];

    • Segment Embeddings:用来区别两种句子,因为预训练不光做LM还要做以两个句子为输入的分类任务,即主要给NSP任务使用。

    • Position Embeddings:和Transformer不一样,0-10的位置编码只能表示绝对位置,而Transformer中的正余弦函数还可以表示相对位置。(为什么不用正余弦了?因为BERT模型学习能力强。)

    对于BERT输入部分还有几点需要补充:

    1. 上图中标注了各个Embeddings对应的维度,768是BERT-base的词向量维度,BERT-Large与BERT-base的参数对比贴在这:

      【L:网络层数,H:隐藏层维度,A:Multi-Head Attention 多头头数】

      Bert-base: (L=12, H=768,A=12, Total Parameters=110M).(使用GPU内存:7G+)

      BERT-large: (L=24, H=1024,A=16, Total Parameters=340M).(使用GPU内存:32G+)

    2. BERT中输入的tokens的长度不能超过512,即一个句子最大的长度加上[CLS]和[SEP]不能超过512,这里可能就涉及到句子截取等工作。

    3. 一个思考:[CLS]或[SEP]多几个或者少几个可以吗?

      可以,因为网络用到的Transformer中有Self-Attention,每一个位置都可以学习到其他位置词的信息,所以在这一点上他们是一样的。多几个或少几个都是可以的,甚至可以一个token后面跟一个[SEP],训练完后拿出来做NER任务。

    How to Pre-training

    BERT的预训练过程主要分为两个部分:MLM(Mask Language Model,完形填空)和NSP(Next Sentence Prediction,句对预测)。接下来我们分别来看这两部分:

    MLM

    MLM本质还是一个语言模型,那么既然是一个语言模型,那它干的事必然就是【预测一句话合理性的概率】,换句话说就是利用上下文预测可能出现的词。由于LM是单向的,要不从左到右要不从右到左,很难做到结合上下文语义。为了改进LM,实现双向的学习,MLM通过对输入文本序列随机的mask,然后通过上下文来预测这个mask应该是什么词,至此解决了双向的问题。所以人们称之为完形填空,不同于之前LM是自回归问题,这里的MLM是一个自编码问题。目的就是学习语料中的各种特征。

    那么这个mask又是怎么做的呢?

    具体的做法是,对输入句子中的token以15%的概率进行随机选取(如选中"bed"这个token),再将选取出来的token以80%的概率替换成[MASK],10%的概率替换成替换成其他token(如“dad”),最后10%保持不变(依然是“bed”)。

    这样大费周章地做的目的是什么呢?

    其实是提高了模型的泛化能力,因为后面的微调阶段并不会做mask的操作,为了减少预训练和微调阶段输入分布不一致的问题导致的模型表达能力差的现象,故采用了这种策略。

    NSP

    NSP任务主要做的就是一件事:预测句子1与句子2挨在一起的概率。构建数据的方法是,对于句子1,句子2以50%的概率为句子1相连的下一句,以50%的概率在语料库里随机抽取一句。以此构建了一半正样本一半负样本。再用输出的CLS进行二分类,以此来判断输入的两个句子是不是前后相连的关系。

    How to finetune

    之前说过,迁移学习的思想是要将Pre-training阶段的主要成果应用在 finetune阶段,finetune充分应用了大规模预训练模型的优势,只在下游任务上再进行一些微调训练,就可以达到非常不错的效果。

    下面是BERT原文BERT:Pre-trainingofDeepBidirectionalTransformersfor LanguageUnderstanding中提及的微调阶段的四种不同类型的下游任务:

    image-20201219012436766

    在这里简述一下四种任务对应微调阶段的基本方法:

    • 句子对匹配(sentence pair classification):文本匹配类任务的输入是两个不同的句子,最终其实要实现的仍然是一个二分类的问题,若要用BERT实现,基本代码和流程与分类问题一致,全连接层输出维度是2。但实际工程应用上,直接采用BERT来做文本匹配问题的话最终效果不一定会好,一般解决文本匹配问题可以采用一些类似孪生网络的结构去解决,这块可以另外再去探究。
    • 文本分类(single sentence classification):文本分类是BERT最擅长做的事情了,最基本的做法就是将预训练的BERT加载后,同时在输出[CLS]的基础上加一个全连接层来做分类,全连接层输出的维度就是我们要分类的类别数。当然也可以在分类之前加一个其他的网络层以达到对应的目的。
    • 抽取式问答(question answering):注意由于BERT没有生成能力,所以只能做抽取式的问答。可以这样理解:这个回答其实是在一篇文章中找答案的过程,通过预测答案开始与结束位置在文中的id来进行训练。
    • 序列标注(single sentence tagging):由于一般的分词任务、词性标注和命名体识别任务都属于序列标注问题。这类问题因为输入句子的每一个token都需要预测它们的标签,所以序列标注是一个单句多label分类任务,BERT模型的所有输出(除去特殊符号)都要给出一个预测结果。

    BERT微调模型的设计

    针对不同的任务我们可以继续在bert的预训练模型基础上加一些网络的设计,比如文本分类上加一些cnn;比如在序列标注上加一些crf等等。

    下面只记录几点提要,具体使用还需深究代码。

    BERT+CNN:由于BERT中Attention能捕捉长距离的语义特征,而CNN中的滤波器与卷积操作更能捕捉局部特征,故BERT+CNN(+池化层)也是一个能很好互补的组合。相比之下就不太建议加RNN或者Attention之类的结构了,因为BERT里就带了类似的结构了。

    BERT+CRF:NER问题很好的一种解决方案。

    BERT的输出

    之前我们看到了BERT的输入是三种Embeddings的组合,那BERT模型的输出是什么呢。通过下图能够看出会有两种输出,一个对应的是红色框,也就是对应的[CLS]的输出,输出的shape是[batch size,hidden size];另外一个对应的是蓝色框,是所有输入的token对应的输出,它的shape是[batch size,seq length,hidden size],这其中不仅仅有[CLS]对于的输出,还有其他所有token对应的输出。

    image-20201220003953882

    在使用代码上就要考虑到底是使用第一种还是第二种作为输出了。大部分情况是是会选择[CLS]的输出,再进行微调的操作。不过有的时候使用所有token的输出也会有一些意想不到的效果。

    其他一些疑问与细节

    预训练模型为何会有输入长度512的限制?

    BERT模型要求输入句子的长度不能超过512,同时还要考虑[CLS]这些特殊符号的存在,实际文本的长度会更短。究其原因,随着文本长度的不断增加,计算所需要的显存也会成线性增加,运行时间也会随着增长。所以输入文本的长度是需要加以控制的

    在实际的任务中我们的输入文本一般会有两个方面,要不就是特别长,比如文本摘要、阅读理解任务,它们的输入文本是有可能超过512;另外一种就是一些短文本任务,如短文本分类任务。

    长文本该如何处理?

    说到长文本处理,最直接的方法就是截断

    由于 Bert 支持最大长度为 512 个token,那么如何截取文本也成为一个很关键的问题。How to Fine-Tune BERT for Text Classification?中给出了几种解决方法

    • head-only: 保存前 510 个 token (留两个位置给 [CLS] 和 [SEP] )
    • tail-only: 保存最后 510 个token
    • head + tail : 选择前128个 token 和最后382个 token

    作者是在IMDB和Sogou News数据集上做的试验,发现head+tail效果会更好一些。但是在实际的问题中,我们还是要人工的筛选一些数据观察数据的分布情况,视情况选择哪种截断的方法。

    除了上述截断的方法之外,还可以采用sliding window的方式做。

    用划窗的方式对长文本切片,分别放到BERT里,得到相对应的CLS,然后对CLS进行融合,融合的方式也比较多,可以参考以下方式:

    • max pooling最大池化
    • avg pooling平均池化
    • attention注意力融合
    • transformer等

    短文本该如何处理?

    在遇到一些短文本的NLP任务时,我们需要根据输入文本长度的分布情况重新选取max_sequence_length,最大输入文本长度的取值可以通过正态分布得出。

    什么是warmup?为什么要在设置学习率的时候采用的warmup策略?

    warmup是一种学习率优化方法(最早出现在ResNet论文中)。在模型训练之初选用较小的学习率,训练一段时间之后(如:10epoches或10000steps)使用预设的学习率进行训练,或逐渐减小。(BERT原文中前10000步会增长到1e-4, 之后再线性下降。)

    如下图的Linner Warmup:

    image-20201220010210086

    感性分析为何要使用warmup策略:

    1. 刚开始模型对数据完全不了解,这个时候步子太大,loss容易跑飞,此时需要使用小学习率摸着石头过河;
    2. 对数据了解了一段时间之后,可以使用大学习率朝着目标大步向前;
    3. 快接近目标时,使用小学习率进行精调探索,此时步子太大,容易错过目标点;

    不同学习率的设置

    在 fine-tune阶段使用过大的学习率,会打乱 pretrain 阶段学习到的句子信息,造成“灾难性遗忘”。BERT没有下游微调结构的,是直接用BERT去fine-tune时,BERT模型的训练和微调学习率取2e-5和5e-5效果会好一些。那如果微调的时候接了更多的结构,比如BERT+TextCNN,BERT+BiLSTM+CRF,此种情况下BERT的fine-tune学习率可以设置为5e-5, 3e-5, 2e-5。而下游任务结构的学习率可以设置为1e-4,让其比bert的学习更快一些。至于这么做的原因也很简单:BERT本体是已经预训练过的,即本身就带有权重,所以用小的学习率很容易fine-tune到最优点,而下接结构是从零开始训练,用小的学习率训练不仅学习慢,而且也很难与BERT本体训练同步。为此,我们将下游任务网络结构的学习率调大,争取使两者在训练结束的时候同步:当BERT训练充分时,下游任务结构也能够训练充分。

    BERT与GPT的区别?BERT可以做生成任务吗?

    BERT与GPT最大的区别是:BERT用的是Transformer中的Encoder部分,而GPT用的是Transformer中的Decoder部分。

    BERT加上Transformer中的Decoder部分就可以做生成任务了。

    BERT中的weight decay权重衰减是什么?作用?

    权重衰减等价于L2范数正则化。正则化通过为模型损失函数添加惩罚项使得学习的模型参数值较小,是常用的过拟合的常用手段。

    权重衰减并不是所有的权重参数都需要衰减,比如bias,和LayerNorm.weight就不需要衰减。

    BERT不同层的含义

    BERT Rediscovers the Classical NLP Pipeline中对于BERT不同层所学习到的不同信心做了一定的研究,总体来说会有以下发现:

    • BERT会在较低层编码更多语法信息,在较高层编码更多语义信息(像句法分析、实体识别这些比较简单的NLP任务更倾向于使用BERT底层的信息。而像关系分类等复杂的NLP任务更倾向于BERT的高层信息)。
    • 语法信息的体现比较局部化(Locolizable),语义信息的体现在各层中分布的比较均匀。
    • 该文章定性地分析证明在较低层产生的一些有歧义的决定可以在较高层被修正。

    BERT模型的迁移

    迁移特性

    《Linguistic Knowledge and Transferability of Contextual Representations》一文所做的工作提到部分关于BERT模型迁移特性的结论:

    对于难度比较低的任务,仅仅需要BERT的前几层就能得到更好的迁移学习能力。所以适当减少层数是有助于获得更好的迁移学习基础。在不去微调预训练模型的基础上,合理的选择层是有必要的。如果对于BERT-base(12层)来说,最佳的迁移层数经常在6-12层之间,对于比较难的任务,需要的层数会多一些。

    迁移策略

    那拿到一个BERT预训练模型后,我们会有两种选择:

    1. 把BERT当做特征提取器或者句向量,不在下游任务中微调。
    2. 把BERT做为下游业务的主要模型,在下游任务中微调。

    基于BERT模型的改进型模型简介

    Roberta模型:更具鲁棒性的、优化后的BERT模型。采用动态Mask策略,Roberta是把数据复制10份,每一份中采用不同的静态Mask操作,使得预训练的每条数据有了10种不同的Mask数据。在Roberta中,采用的是full-sentence的形式,每一个训练样本都是从一个文档中连续sample出来的,并且不采用NSP损失。从结论中发现效果会更好,说明NSP任务不是必须的。

    BERT-WWM:核心是全词Mask。主要更改了原预训练阶段的训练样本生成策略。在全词Mask中,如果一个完整的词的部分WordPiece子词被Mask,则同属该词的其他部分也会被Mask,即全词Mask。(例如“这个模型真好用”可能会被Mask成"这个模[MASK]真好用",而在WWM下会被Mask成“这个[MASK] [MASK]真好用”)。需要注意的是,这里的Mask指的是广义的Mask(替换成[MASK];保持原词汇;随机替换成另外一个词),并非只局限于单词替换成[MASK]标签的情况。

    芝麻街的初心

    自ELMO出来之后,后续又有例如BERT这样革命性的模型出现,再到后来,更多优秀的模型也被提出来,他们中有好多都像BERT致敬ELMO一样致敬了前面的模型,起的名字好多都来自于动画节目《芝麻街》,以下列举了这几个模型对应的人物形象以及论文链接(看了半天BERT着实是好看啊,哈哈)。致敬一群群推动NLP发展的创造者们,希望自己也能保持一颗孩童时代的好奇心,保持出发时的初心,一路向前。加油!

    ELMO BERT ERNIE Grover KERMIT Big Bird
    image-20201217011825930 image-20201217011915777 image-20201217012124932 image-20201217012049764 image-20201217013122001 image-20201217012323262

    除文中已提及文章外其他参考文章:

    The Bright Future of ACL/NLP

    Deep contextualized word representations

    ELMo原理解析及简单上手使用

    关于ELMo你不知道的一些细节

    深入理解NLP Subword算法:BPE、WordPiece、ULM

    Bert输入输出是什么

    BERT 详解

    展开全文
  • BERT模型实战之多文本分类(附源码)

    万次阅读 多人点赞 2019-03-21 11:00:36
    BERT模型也出来很久了,之前看了论文学习过它的大致模型(可以参考前些日子写的笔记NLP大杀器BERT模型解读),但是一直有杂七杂八的事拖着没有具体去实现过真实效果如何。今天就趁机来动手写一写实战,顺便复现一下...
  • 基于BERT预训练的中文命名实体识别TensorFlow实现

    万次阅读 多人点赞 2019-01-03 11:58:25
    BERT-BiLSMT-CRF-NER Tensorflow solution of NER task Using BiLSTM-CRF model with Google BERT Fine-tuning 使用谷歌的BERT模型在BLSTM-CRF模型上进行预训练用于中文命名实体识别的Tensorflow代码’ 代码已经托管...
  • Bert文本分类(基于keras-bert实现)

    万次阅读 多人点赞 2019-10-25 17:48:34
    中文预训练模型下载 当Bert遇上Keras:这可能是Bert最简单的打开姿势 keras-bert 不同模型的性能对比如下(可根据自己的数据选择合适的模型,模型越大需要训练的时间越长) 模型 开发集 测试集...
  • 基于BERT的蒸馏实验 参考论文《从BERT提取任务特定的知识到简单神经网络》 分别采用keras和pytorch基于textcnn和bilstm(gru)进行了实验 实验数据分割成1(有标签训练):8(无标签训练):1(测试) 在情感2分类...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,408
精华内容 5,763
热门标签
关键字:

BERT