1 BERT的诞生背景
2018年谷歌团队发布了BERT(Bidirectional Encoder Representations from Transformers)模型,在自然语言处理领域引领了技术性的革命。BERT创新性地使用预训练+微调模式,先用预训练在大量无标注的语料中进行自监督学习,学习到语言的通用规律,然后在少量有标注的数据中进行有监督学习,适配更精细化的任务。这种模式一直影响到最新的大模型结构,预训练后的模型在大模型中也称为基座模型。
在BERT之前,GPT等模型只能从左到右或从右到左处理单向语言顺序,但BERT可以处理双向上下文信息,因为Encoder中的Attention是双向的,从而能全面理解语义。
2 BERT的结构解析
BERT是基于Transformer中的Encoder搭建的,建议读者先理解Transformer。可参考笔者对Transformer的解析,详见Transformer解析——(三)Encoder-CSDN博客
BERT的输入、输出与Transformer类似,输入由Embedding等模块处理得到词向量,输出也是。BERT的中间逻辑是由多个Encoder拼接起来的,2个版本BERT-base和BERT-large分别使用12层和24层Encoder,他们的参数量分别为1.1亿和3.4亿。
与Transformer相比有几个特点:
2.1 输入词向量增加[CLS]和[SEP]
输入为两个句子,结构为[CLS]+句子A+[SEP]+句子B。
[CLS]是因为微调阶段有时要做分类(CLS即为classification的缩写),比如做情感分析,用第一个向量接入后续的分类模块,输出该句是正向情感还是负向情感,因此第一个向量设置成特殊的[CLS],表示该句整体的情感等类别。
[SEP]是为了分割不同的句子(SEP即separator的缩写)。
SEP使得BERT处理到此时能感知到边界,但Transformer是并行输入的,并没有顺序,为什么还要用SEP分割?
并行处理并不意味着模型无法识别位置信息,有位置编码的存在,BERT还是可以感知到位置的,只是使用并行计算加速了效率而已。
同时,SEP作为一个特殊的token,也会与其他token计算注意力,从而利用上分隔符的信息。比如句首或句尾的词与SEP可能存在某种关联。
段嵌入已经可以标明各个句子的组成部分,为什么还要用SEP分割?
段嵌入不直接标明边界。虽然段嵌入和位置编码能够让模型推理出句子在哪一个位置结束,但是这种推理不如SEP直接。
2.2 增加了Segment Embeddings段嵌入
在输入时,除了做Embedding和Positional Encoding以外,还增加了段嵌入,因为需要输入成对的句子,用段嵌入表示当前token属于第一个句子还是第二个句子。
2.3 词嵌入使用WordPiece方法
Transformer并未规定词嵌入的方法,但BERT中的词嵌入是固定的WordPiece。将单词与词汇表对照,如果不在词汇表中,就继续分割成子词,直到在词汇表命中。
2.4 参数量的计算
以下是两种模型各模块的参数量
模块 | 计算过程 | BERT-base参数量 | BERT-large参数量量 |
词嵌入 | base:词表大小30522,词嵌入维度768,则词嵌入矩阵为30522*768 large:词嵌入维度是1024,则30522*1024,以下同理将768替换为1024 |
2344W | 3125W |
位置嵌入 | 最大位置编码是512,每个位置都要生成一个768(词嵌入维度)长度的编码,则512*768 | 39W | 52W |
段嵌入 | 一般有2个段,则2*768 | 1536 | 2048 |
嵌入后的层归一化 | 层归一化计算均值和标准差,这是输入数据本身的特征,但需要学习的是缩放因子和偏移因子,同样每个词嵌入维度都需要学习这2个参数,因此2*768 | 1536 | 2048 |
嵌入层总计 | 2383W | 3178W | |
多头注意力线性变换 | 假设12个头,每个头的维度是768/12=64,QKV都需要线性变换矩阵,则需要3*768*64,有12个头,还需要再乘12 | 176W | 314W |
多头注意力拼接后线性变换 | 将12个头的结果拼接起来还需要经过一个线性变换层,因此需要768*768 | 58W | 104W |
多头注意力的层归一化 | 2*768 | 1536 | 2048 |
FNN | base:有两层全连接层,隐含层神经元是3072,因此参数是768*3072+3072*768 large:隐含层是4096 |
471W | 838W |
FNN的层归一化 | 2*768 | 1536 | 2048 |
Transformer模块总计 | 705W | 1258W | |
MEM输出层 | 通常对[CLS]做分类,是一个全连接层,将词向量维度拓展到词表维度,768*30522,考虑偏置需要再加上30522 | 2347W | 3128W |
总计 | 嵌入层+Transformer模块*数量(12或24)+输出层 | 1.3亿,官方参数1.1亿,可能未包括MEM任务 |
3.6亿,官方参数3.4亿,可能未包括MEM任务 |
3 BERT的预训练任务
BERT先利用大规模的语料,进行自监督的预训练任务,从而产生对语义的理解。因为做无监督学习,不需要人工标注,因此可以使用的语料十分庞大,互联网中各种信息均可输入到BERT中。随后,BERT利用特定领域的数据做微调,特定领域的数据一般带有人工标注的标签,所以是有监督学习。
自监督,无监督,有监督,半监督的关系
类别 特点 举例 有监督 使用了输出数据(标签)数据
使用带标签的训练数据预测新样本的标签。这里的标签可以是数值(即回归问题)或标签(分类问题)
决策树,神经网络,K近邻等 无监督 无标注数据
直接对输入数据进行建模
聚类
自监督 特殊的无监督。从无监督数据中构造出有监督数据训练,一般用作预训练(因为没标签,所以只能学习通用的特征),后续接下游任务 BERT预训练
AutoEncoder
半监督 用少量有标签数据和大量无标签数据训练。
因为一般打标签需要耗费人力,所以数据量较少,无标签数据较多。网络上所有的文字材料都可以认为是无标签的数据
BERT预训练+微调
3.1 Masked Language Model掩码语言模型
预训练时,采用MLM(掩码语言模型)方法,即按照15%比例对输入的token进行掩码(遮盖)。
这是因为预训练是自监督训练,没有标注的数据可利用。既然没有条件,就自己创造条件训练,很自然的想法就是遮盖其中的词让机器自己预测。
这部分需要掩码的Token中,有80%替换成特殊token[MASK],10%概率替换成词表中另一随机的token,10%概率保持不变。
这是因为微调阶段没有[MASK]的训练,如果每句话都有token替换成[MASK],会造成预训练和微调的机制不一样。
此外,有10%的概率替换成词表另一随机的token,也能保证模型的健壮性,当遇到脏数据时也可以处理。
训练的目标是模型能还原出被遮盖的单词,即将[MASK]处输出的向量喂给FNN,映射成词表的大小,随后接一个Softmax模组,使得预测概率最大的词应与实际被遮盖的词一致,使用交叉熵作为损失函数进行训练。
3.2 Next Sentence Prediction下一句预测
对于每一对输入的句子,模型需要判断第二句是否是第一句的下一句,从而学习到文本的连贯性。训练时,将连续的2段句子放在一起,模型的输出从[CLS]位置接入一个FNN,映射到二维输出,训练目标是模型输出1;将并非连续的2段句子放在一起,训练目标是模型输出0,这样模型就学习到了句子之间的关系。
后续有学者研究,发现这个预测方法用处一般。
3.3 Sentence Order Prediction句子顺序预测
后续有学者提出了SOP训练方法,目标是让模型学习两个句子的顺序,原始两个句子顺序是正样本,颠倒顺序作为负样本。注意NSP的正样本是原始文本中相邻的句子,而负样本是从不同文档或段落中随机拼接得到的句子,关注重点将放在前后语义是否一致上。但是负样本的语义可能是“八竿子打不着”的关系,模型不需要“很努力”就可以识别出来正负标签,降低训练效果。而SOP是将两个连续的句子调换顺序,关注重点将放在前后顺序的逻辑上,这对模型提出了更高要求,因此训练效果更好。
比如NSP的训练句子是“我今天去公园”,“太阳东升西落”,模型也许根据主语的关联程度就能轻易识别二者不是连续的句子。
而SOP的训练句子是“我推开门”。“我走了进来”,模型会学习到二者之间的逻辑顺序,难度更高,实现有效训练。
4 BERT预训练的意义
经过预训练,BERT“理解”了语言,其输出的词向量代表着语义,所以在词向量分布上,相同性质的词向量之间的距离也较近(比如“鸟”和“鱼”,“绿”和“红”)。但是即使是同一个词,在不同的上下文下可能表示不同的含义,BERT由于考虑了上下文注意力,因此会区分这样的词。
比如“苹果”既可以表示水果,又可以表示科技品牌。找到一组表示为水果的句子,记为A,将其中苹果输出的向量提取出来;找到一组表示为科技品牌的句子,记为B,将其中苹果输出的向量提取出来。计算A、B中该向量两两计算相关性,发现在A中向量之间相关性较高,在B中向量之间相关性较高(下图红圈位置),而在A和B之间相关性较低(下图蓝圈位置),这说明BERT能识别出“苹果”在句子中表示的是水果还是科技品牌。
但是也有不同的观点。
李宏毅老师课程中举了一个例子,他们使用BERT来做蛋白质、DNA、音乐的分类。
例如一串DNA由A、T、C、G组成(比如ATCGCTCA...),每个DNA有一个分类(EI、IE、N,不清楚具体生物意义,理解成3种分类即可),问题是给定一个DNA,预测该DNA属于哪个类别。
现在使用BERT应用到DNA分类问题中,将A、T、C、G分别对应一个随机的词汇(比如A对应we,T对应you,C对应he,G对应she),从而将DNA表示成一串文字(比如we you he he you she we...)。接下来将预训练好的BERT接入三分类的微调模块,用[CLS]生成分类,发现效果也很好。
蛋白质的分类问题(用氨基酸序列判断蛋白质类别)、音乐分类问题(音符序列判断音乐类型)同理,表现也都很好。
可以看到,即使给出了乱七八糟的句子,分类表现也比随机初始化的线性网络要好。所以BERT的能力不完全来自于对语义的理解,具体的机制尚待解释。
5 BERT的微调
虽然预训练是填空/识别语序,在经过预训练之后,BERT理解了语义,再做下游任务时就会游刃有余。就像是“学好数理化,走遍天下都不怕”,打好了数理化学科的基础后,再研究机器学习、金融等各种领域时就会简单很多。
微调训练时,下游模型的初始权重是采用随机化的方法,但BERT参数已在预训练时确定好,因此从预训练好的权重开始,使用梯度下降等方式训练,更新权重。
微调的任务可以是多种多样的,以下为一些例子:
类型 | 做法 |
情感分析 | 对[CLS]的输出接入一个分类或回归模块,输出情感正负值或者情感分数 |
词性标注 | 词性可处理成分类问题,对于输出的每个词都接入一个分类模块 |
前提和假设推理,即分析前一句是否为下一句的前提和假设 | 对[CLS]的输出接入一个0、1的分类模块,表示是/否 |
阅读理解,将问题和原文输入到模型,问题的答案必须可以直接在原文中搜索到 | 输出两个数字,表示原文的开始和结束位置,中间截取的部分即为答案 |
语音和视频 | 不限于文本,语音和视频也可以向量化 |
6 BERT的优缺点
优点:开创了预训练+微调模式,成为当前流行的大模型训练方式。微调时只需要在BERT下游接入一个特定任务的输出层,即可实现分类、回归等多种任务。
缺点:训练需要的资源较多。参数量上亿,在当时(2018年)算力资源有限,训练需要的成本较高。相对应地,模型处理长文本的能力有限。
7 扩展讨论
7.1 多语言BERT
Multi-lingual BERT(多语言BERT,也称mBERT),是BERT的多语言版本,模型结构与原始的BERT并无明显区别,只是训练数据集包含了104种语言的资料。
问题:对mBERT模型,使用英文微调,在中文测试集中测试,发现效果也很好
解答:不同语言描述同一事物的Embedding也很接近
问题:在英文资料上 BERT 可以做英文的填空题,在中文资料上 BERT 可以做中文的填空题。如果两种语言本质上都是词向量空间里的向量,可以认为是同一种语言,BERT如何知道自己要做的是英文还是中文的填空题?
解答:首先介绍一个实验,取出所有中文词的Embedding,取出所有英文词的Embedding,二者各自求平均,得到中英文平均的词向量,二者作差就代表了中英文之间的差距。接下来将“there is a cat”输入到BERT,得到词向量加上刚刚作差的向量,得到了“那有一猫”。因此,BERT得到的Embedding可能包含“属于哪个语言”信息。
7.2 BERT可以使用KV Cache吗
BERT不适用于KV Cache。回顾一下KV Cache是在计算Attention时构造了缓存,保存已经计算过的键值对,下一次需要计算时直接从该缓存中取出。而Encoder中并没有重复计算。只有使用了Decoder的模型(例如GPT)才会使用,因为模型输出一个文字后,要将该文字连带已输出的文本一起输入到Decoder中,这时已输出的文本之间Attention会重复计算,因此才需要KV Cache缓存下来,避免二次计算。
8 参考资料
李宏毅老师课程