系列文章目录

第一章 姿态描述之旋转矩阵、欧拉角、四元数

第二章 坐标系与位姿变换

第三章 手眼标定

第四章 机器人正逆运动学与路径规划

第五章 图解Transformer架构

第六章 图解Bert

第七章 BERT代码解析与实战

第八章 强化学习


目录

系列文章目录

前言

一、BERT的应用

二、模型结构

1. 模型输入

2. 模型输出

三、预训练任务

1.完形填空(Masked Language Model)

2. 相邻句子判断

四、BERT特征提取

五、拓展阅读

1. 对比CNN

2. 词嵌入(Embedding)进展

3. OpenAI Transformer Decoder-only

六、BERT:Decoder到Encoder

总结


前言

参考:图解BERT

        BERT在2018年被提出,BERT模型一出现就打破了多个自然语言处理任务的最好记录。BERT的论文发布不久后,BERT团队公开了模型的代码,并提供了基于大规模新书数据集预训练完成的模型下载。BERT的模型代码和模型参数的开源,使得任何一个NLP从业者,都可以基于这个强大的模型组件搭建自己的NLP系统,也节省了从零开始训练语言处理模型所需要的时间、精力、知识和资源。

        如下图所示,BERT首先在大规模无监督语料上进行预训练,然后在预训练好的参数基础上增加一个与任务相关的神经网络层,并在该任务的数据上进行微调训,最终取得很好的效果。BERT的这个训练过程可以简述为:预训练+微调(finetune),已经成为最近几年最流行的NLP解决方案的范式。


提示:该博客仅为个人学习记录,若有错误之处,还请批评指正

一、BERT的应用

        要想很好的理解BERT,最好先理解一下BERT的使用场景,明确一下输入和输出,最后再详细学习BERT的内在模型结构和训练方法。因此,在介绍模型本身涉及的BERT相关概念之前,让我们先看看如何直接应用BERT。

  • 下载在无监督语料上预训练好的BERT模型,一般来说对应了3个文件:BERT模型配置文件(用来确定Transformer的层数,隐藏层大小等),BERT模型参数,BERT词表(BERT所能处理的所有token)。
  • 针对特定任务需要,在BERT模型上增加一个任务相关的神经网络,比如一个简单的分类器,然后在特定任务监督数据上进行微调训练。(学习率较小,训练epoch数量较少,对模型整体参数进行轻微调整)

        先来看一下如何使用BERT进行句子分类, 假设我们的句子分类任务是:判断一个邮件是“垃圾邮件”或者“非垃圾邮件”,如下图所示。当然除了垃圾邮件判断,也可以是其他NLP任务,比如:

  • 输入:电影或者产品的评价。输出:判断这个评价是正面的还是负面的。
  • 输入:两句话。输出:两句话是否是同一个意思。

垃圾邮件分类

        如下图所示,为了能够使用BERT进行句子分类,我们在BERT模型上增加一个简单的classifier层,由于这一层神经网络参数是新添加的,一开始只能随机初始化它的参数,所以需要用对应的监督数据来训练这个classifier。由于classifier是连接在BERT模型之上的,训练的时候也可以更新BERT的参数。

         BERT论文展示了BERT在多种任务上的应用,如下图所示。可以用来判断两个句子是否相似,判断单个句子的情感,用来做抽取式问答,用来做序列标注。

2. 词嵌入(Embedding)进展

BERT应用

二、模型结构

        BERT原始论文提出了BERT-base和BERT—large两个模型,base的参数量比large少一些,可以形象的表示为下图的样子。        

BERT base和large

        回顾一下Transformer,BERT模型结构基本上就是Transformer的encoder部分,BERT-base对应的是12层encoder,BERT-large对应的是24层encoder。

BERT encoder

1. 模型输入

        BERT模型输入有一点特殊的地方是在一句话最开始拼接了一个[CLS] token,如下图所示。这个特殊的[CLS] token经过BERT得到的向量表示通常被用作当前的句子表示。除了这个特殊的[CLS] token,其余输入的单词类似Transformer。BERT将一串单词作为输入,这些单词在多层encoder中不断向上流动,每一层都会经过 Self-Attention和前馈神经网络。

模型输入

2. 模型输出

        BERT输入的所有token经过BERT编码后,会在每个位置输出一个大小为 hidden_size(在 BERT-base中是 768)的向量。

        对于上面提到的句子分类的例子,我们直接使用第1个位置的向量输出(对应的是[CLS])传入classifier网络,然后进行分类任务,如下图所示。

三、预训练任务

        知道了模型输入、输出、Transformer结构,那么BERT是如何无监督进行训练的呢?如何得到有效的词、句子表示信息呢?以往的NLP预训练通常是基于语言模型进行的,比如给定语言模型的前3个词,让模型预测第4个词。

1.完形填空(Masked Language Model)

        BERT是基于Masked language model进行预训练的:将输入文本序列的部分(15%)单词随机Mask掉,让BERT来预测这些被Mask的词语。如下图所示: 

这种训练方式最早可以追溯到Word2Vec时代,典型的Word2Vec算法便是:基于词C两边的A、B和D、E词来预测出词C。

2. 相邻句子判断

        除了masked language model,BERT在预训练时,还引入了一个新的任务:判断两个句子是否是相邻句子。如下图所示:输入是sentence A和sentence B,经过BERT编码之后,使用[CLS] token的向量表示来预测两个句子是否是相邻句子。

        注意事项:为了本文的描述方便,在前面的叙述中,均省略了BERT tokenize的过程,但读者朋友需要注意BERT实际上使用的是WordPieces作为最小的处理单元(采用的是wordpiece算法分词):token,而不是使用单词本身。在 WordPiece中,有些词会被拆分成更小的部分。关于WordPiece分词,本文不过多展开,感兴趣的读者可以阅读和学习subword tokenizer。另外,判断两个句子是否相邻这个任务在后来的研究中逐渐被淡化了,比如roberta模型在被提出的时候就不再使用该任务进行预训练了。

四、BERT特征提取

        由于BERT模型可以得到输入序列所对应的所有token的向量表示,因此不仅可以使用最后一层BERT的输出连接任务网络进行微调,还可以直接使用这些token的向量当作特征。比如,可以直接提取每一层encoder的token表示当作特征,输入现有的特定任务神经网络中进行训练。

BERT特征提取

        那么我们是使用最后一层的向量表示,还是前几层的,还是都使用呢?下图给出了一种试验结果:除了直接使用第一层的输出作为任务网络的输入外,其他组合都有不错的效果。

BERT特征选择

五、拓展阅读

1. 对比CNN

        对于那些有计算机视觉背景的人来说,根据BERT的编码过程,会联想到计算机视觉中使用VGGNet等网络的卷积神经网络+全连接网络做分类任务,如下图所示,基本训练方法和过程是类似的。

CNN

2. 词嵌入(Embedding)进展

        单词不能直接输入机器学习模型,而需要某种数值表示形式,以便模型能够在计算中使用。通过Word2Vec,我们可以使用一个向量(一组数字)来恰当地表示单词,并捕捉单词的语义以及单词和单词之间的关系(例如,判断单词是否相似或者相反,或者像 "Stockholm" 和 "Sweden" 这样的一对词,与 "Cairo" 和 "Egypt"这一对词,是否有同样的关系)以及句法、语法关系(例如,"had" 和 "has" 之间的关系与 "was" 和 "is" 之间的关系相同)。

        人们很快意识到,相比于在小规模数据集上和模型一起训练词嵌入,更好的一种做法是,在大规模文本数据上预训练好词嵌入,然后拿来使用。因此,我们可以下载由 Word2Vec 和 GloVe 预训练好的单词列表,及其词嵌入。下面是单词 "stick" 的 Glove 词嵌入向量的例子(词嵌入向量长度是 200)。

wrod vector

        单词 "stick" 的 Glove 词嵌入embedding向量表示:一个由200个浮点数组成的向量(四舍五入到小数点后两位)。由于这些向量都很长,且全部是数字,所以在文章中我使用以下基本形状来表示向量:

vector

        如果我们使用 Glove 的词嵌入表示方法,那么不管上下文是什么,单词 "stick" 都只表示为一个向量。一些研究人员指出,像 "stick" 这样的词有多种含义。为什么不能根据它使用的上下文来学习对应的词嵌入呢?这样既能捕捉单词的语义信息,又能捕捉上下文的语义信息。于是,语境化的词嵌入模型应运而生:ELMo。语境化的词嵌入,可以根据单词在句子语境中的含义,赋予不同的词嵌入。

ELMO

        ELMo没有对每个单词使用固定的词嵌入,而是在为每个词分配词嵌入之前,查看整个句子,融合上下文信息。它使用在特定任务上经过训练的双向LSTM来创建这些词嵌入。

ELMO embedding

        ELMo 在语境化的预训练这条道路上迈出了重要的一步。ELMo LSTM 会在一个大规模的数据集上进行训练,然后我们可以将它作为其他语言处理模型的一个部分,来处理自然语言任务。ELMo 通过训练,预测单词序列中的下一个词,从而获得了语言理解能力,这项任务被称为语言建模。要实现 ELMo 很方便,因为我们有大量文本数据,模型可以从这些数据中学习,而不需要额外的标签。

ELMO 训练

       

3. OpenAI Transformer Decoder-only

         随着Transformer论文和代码的发布,以及它在机器翻译等任务上取得的成果,开始让人们认为它是LSTM的替代品。一部分原因是:

        1. 因为 Transformer 可以比 LSTM 更好地处理长期依赖,

        2. Transformer可以对输入进行并行运算。

        2017年,基于Transformer的Encoder-Decoder展示了它在机器翻译上的威力。但怎么才能用它来做文本分类呢?你怎么才能使用它来预训练一个语言模型,并能够在其他任务上进行微调(下游任务是指那些能够利用预训练模型的监督学习任务)?

        沿着LSTM语言模型预训练的路子,将LSTM替换成Transformer结构后(相当于),直接语言模型预训练的参数给予下游任务监督数据进行微调,与最开始用于翻译seq2seq的Transformer对比来看,相当于只使用了Decoder部分。有了Transformer结构和语言模型任务设计,直接使用大规模未标记的数据不断得预测下一个词:只需要把 7000 本书的文字依次扔给模型 ,然后让它不断学习生成下一个词即可。

open ai模型预测下一个词

        现在,OpenAI Transformer 已经经过了预训练,它的网络层藏书经过很多次调整,可以很好地用向量表示文本了,我们开始使用它来处理下游任务。让我们先看下句子分类任务(把电子邮件分类为 ”垃圾邮件“ 或者 ”非垃圾邮件“):

open ai模型下游任务

        对于形形色色的NLP任务,OpenAI 的论文列出了一些列输入变换方法,可以处理不同任务类型的输入。下面这张图片来源于论文,展示了处理不同任务的模型结构和对应输入变换。

open ai微调

六、BERT:Decoder到Encoder

        OpenAI Transformer为我们提供了一个基于Transformer的预训练网络。但是在把LSTM换成Transformer 的过程中,有些东西丢失了。比如之前的ELMo的语言模型是双向的,但 OpenAI Transformer 只训练了一个前向的语言模型。我们是否可以构建一个基于 Transformer 的语言模型,它既向前看,又向后看(用技术术语来说 - 融合上文和下文的信息)?答案就是BERT:基于双向Transformer的encoder,在Masked language model上进行预训练,最终在多项NLP下游任务中取得了SOTA效果。

总结

   BERT是一种基于Transformer架构的预训练语言模型,通过完形填空和相邻句子判断任务进行无监督训练。其核心思想是先在大规模语料上进行预训练,再针对特定任务进行微调。BERT-base和BERT-large分别对应12层和24层Transformer编码器,输入时添加[CLS]标记用于句子表示。在多种NLP任务上取得突破性成果。该模型开创了预训练+微调的NLP处理范式,极大提升了自然语言处理的效果。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐