BERT语言模型详解【Encoder-Only】
NLP-大语言模型学习系列目录
一、注意力机制基础——RNN,Seq2Seq等基础知识
二、注意力机制【Self-Attention,自注意力模型】
三、Transformer图文详解【Attention is all you need】
四、大语言模型的Scaling Law【Power Low】
五、大语言模型微调方法详解【全量微调、PEFT、LoRA、Adapter】
六、BERT模型详解
文章目录
- NLP-大语言模型学习系列目录
- 一、Introduction
- 二、模型架构
- 三、模型输入输出
- 1 输入
- 2 输出
- 四、预训练
- 1 Masked LM
- 2 NSP
- 五、微调
- 1 文本分类任务
- 2 序列标注任务
- Bert 参数计算
- 1 计算公式
- 2 BERTBASE
- 3 BERTLARGE
- 参考资料
本文介绍经典的Encoder-only模型——BERT,需要对Transformer结构有一定的了解,可以参考本系列博客的前面几章如Transformer图文详解【Attention is all you need】。
BERT的原论文为:BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
一、Introduction
传统的语言模型,如标准的自回归模型,通常只能基于从左到右或从右到左的单向上下文生成文本。BERT创新性地引入了“掩蔽语言模型”(MLM),它会随机遮盖输入句子中的某些词,然后让模型预测这些被遮盖的词,从而实现了对句子中双向上下文的理解。这种方式克服了单向语言模型的局限性,使得BERT能够在更广泛的上下文中学习更丰富的语言表示。
MLM的灵感来自Cloze任务,Cloze任务是一种语言填空测试,要求人们在句子中填补缺失的词。
论文贡献:
- 我们展示了双向预训练对于语言表示的重要性。与Radford等人(2018)使用单向语言模型进行预训练不同,BERT使用掩蔽语言模型来实现深度双向预训练表示。这也不同于Peters等人(2018a),他们通过独立训练的从左到右和从右到左的语言模型进行浅层拼接。
- 我们证明了预训练的表示能够减少对许多高度工程化的特定任务架构的需求。BERT是第一个基于微调的表示模型,它在大量的句子级别和词级别任务中达到了最新的性能水平,超越了许多特定任务的架构。
- BERT在11项自然语言处理任务上得到了最好结果。代码和预训练模型可在https://github.com/google-research/bert获取
二、模型架构
BERT采用了Transformer架构,特别是Transformer的编码器部分。把多个Transformer Encoder组装起来,就构成了BERT。
在论文中,作者分别用12个和24个Transformer Encoder组装了两套BERT模型,两套模型的参数总数分别为110M和340M,文末给出了模型参数的详细计算过程。
三、模型输入输出
1 输入
BERT 的输入可以包含一个句子对 (句子 A 和句子 B),也可以是单个句子。同时 BERT 增加了一些有特殊作用的标志位:
- [CLS] 标志放在第一个句子的首位,经过 BERT 得到的的表征向量 C 可以用于后续的分类任务。
- [SEP] 标志用于分开两个输入句子,例如输入句子 A 和 B,要在句子 A,B 后面增加 [SEP] 标志。
- [MASK] 标志用于遮盖句子中的一些单词,将单词用 [MASK] 遮盖之后,再利用 BERT 输出的 [MASK] 向量预测单词是什么。
例如给定两个句子 “my dog is cute” 和 “he likes palying” 作为输入样本,BERT 会转为 “[CLS] my dog is cute [SEP] he likes play ##ing [SEP]”。BERT 里面用了 WordPiece 方法,会将单词拆成子词单元 (SubWord),所以有的词会拆出词根,例如 “palying” 会变成 “paly” + “##ing”。如果不对英文词进行类似词根抽取,词表过大,不容易拟合。
BERT 得到要输入的句子后,需要将字符输入转换为向量输入。BERT的向量输入表示由三个嵌入向量的相加组成,分别是标记嵌入(Token Embedding)、段嵌入(Segment Embedding)和位置嵌入(Position Embedding)。这三部分嵌入向量共同作用,用于向模型提供丰富的输入信息。
- Token Embedding:每个词或标记(token)会被转换成一个固定大小的向量表示。BERT使用的是WordPiece分词,输入的每个子词都会有一个对应的向量。
- Segment Embedding:BERT可以处理两个句子作为输入(用于问答或句子间关系推理任务),因此它引入了“段嵌入”来区分每一个单词属于句子 A 还是句子 B,如果只输入一个句子就只使用 EA。
- 位置嵌入(Position Embedding):编码单词出现的位置,与 Transformer 使用固定的公式计算不同,BERT 的 Position Embedding 也是通过学习得到的,在 BERT 中,假设句子最长为 512。
值得注意的是在BERT中,上面三个向量都是通过学习得到(不像Transformer的位置编码是固定的)。
2 输出
BERT的输出和输入对应,每一个输入会有一个输出的Embedding向量,向量大小为hidden_size (BERT Base中的768),如下图所示:
然后根据不同的下游任务,可以在输出的embedding后面加入其他的神经网络层。比如分类任务,这个向量现在可以用作我们所选择的分类器的输入,如下图所示:
四、预训练
预训练过程主要有两个任务:
- Masked Language Model(完形填空)
- Next Sentence Prediction (预测下一个句子)
1 Masked LM
我们先回顾一下以往语言模型的预训练方法,使用句子 “我/喜欢/学习/自然/语言/处理” 为例。在训练语言模型的时候通常需要进行一些 Mask 操作,防止信息泄露问题,信息泄露指在预测单词 “自然” 的时候,提前得知 “自然” 的信息。
- ELMo:ELMo 在训练的时候使用 biLSTM,预测 “自然” 的时候,前向 LSTM 会 Mask “自然” 之后的所有单词,使用上文 “我/喜欢/学习” 预测;后向 LSTM 会 Mask “自然” 之前的单词,使用下文 “语言/处理” 进行预测。然后再将前向 LSTM 和后向 LSTM 的输出拼接在一起,因此 ELMo 是将上下文信息分隔开进行预测的,而不是同时利用上下文信息进行预测。
- OpenAI GPT:OpenAI GPT 是另外一种使用 Transformer 训练语言模型的框架,但是 OpenAI GPT 使用的是 Transformer 的 Decoder,是一种单向的结构。预测 “自然” 的时候只使用上文 “我/喜欢/学习”,Decoder 中包含了 Mask 操作,将当前预测词之后的单词都 Mask。
下图显示了 BERT 和 ELMo、OpenAI GPT 的区别。
BERT 的作者认为在预测单词时,要同时利用单词 left (上文) 和 right (下文) 信息才能最好地预测。将 ELMo 这种分别进行 left-to-right 和 right-to-left 的模型称为 shallow bidirectional model (浅层双向模型),BERT 希望在 Transformer Encoder 结构上训练出一种深度双向模型 deep bidirectional model,因此提出了 Mask LM 这种方法进行训练。
Mask LM 是用于防止信息泄露的,例如预测单词 “自然” 的时候,如果不把输入部分的 “自然” Mask 掉,则预测输出的地方是可以直接获得 “自然” 的信息。
BERT 在训练时只预测 [Mask] 位置的单词,这样就可以同时利用上下文信息。然而,这带来了一个问题:在微调阶段,实际任务中并不会有 [MASK] 标记的出现,这就导致预训练和微调存在一定的不匹配。
为了缓解这种不匹配,BERT的预训练策略采取了一个平衡方案:
- 80%时间用 [MASK] 替换:大多数情况下,模型会按照预期进行 [MASK] 掩蔽,这帮助模型学会预测被mask的词。
- 10%时间用随机词替换:为了避免模型过度依赖 [MASK] 标记,10%的情况下会用随机词替换。这样可以让模型学会处理意外的输入,从而增强鲁棒性。
- 10%时间保持不变:模型不会对词进行mask,这可以帮助模型学会保留和处理正常的词序列输入。
例如原文中给的一个例子:
在句子 “my dog is hairy”,选择了单词 “hairy” 进行 Mask,则:
- 80% 的概率,将句子 “my dog is hairy” 转换为句子 “my dog is [Mask]”
- 10% 的概率,将单词 “hairy” 替换成另一个随机词,例如 “apple”。将句子 “my dog is hairy” 转换为句子 “my dog is apple”
- 10% 的概率,保持句子为 “my dog is hairy” 不变
这种训练方法通过在预训练时随机选择一些词进行掩蔽或替换,使得模型无法确定哪些词会被遮盖或替换。这样,模型必须为所有输入词都生成上下文相关的表示,以确保在遇到任何词被掩蔽时,都能有足够的信息来预测原始词。这就增强了模型的上下文感知能力,即使在某些词被替换或掩蔽的情况下,它仍然能够基于上下文推断出合理的语义。
同时,随机替换只占1.5%的标记(即15%的标记中有10%被随机替换),比例较小,足以引入一些多样性来训练模型的鲁棒性(处理意外输入的能力),但又不会显著影响模型的整体语言理解能力。
为什么是8:1:1这个比例,作者做了消融实验来得到的,如下图所示,8:1:1这个比例在所有设置里效果最好.
输入的Sequence经过BERT得到输出的Embedding后,怎么预测Mask位置的词呢,如下图所示,还会接一个FFN+Softmax得到每个词的概率,概率最大的作为预测的词。
最终的损失函数只计算被mask掉那个token。
2 NSP
BERT 的第二个预训练任务是 Next Sentence Prediction (NSP),即下一句预测,给定两个句子 A 和 B,要预测句子 B 是否是句子 A 的下一个句子。
BERT 在进行训练的时候,从文本语料库中随机选择50%正确语句对和50%错误语句对进行训练,然后通过 [CLS] 标志位的输出 C 预测句子 A 的下一句是不是句子 B。
-
输入 = [CLS] 我 喜欢 玩 [Mask] 联盟 [SEP] 我 最 擅长 的 [Mask] 是 亚索 [SEP]
类别 = B 是 A 的下一句
-
输入 = [CLS] 我 喜欢 玩 [Mask] 联盟 [SEP] 今天 天气 很 [Mask] [SEP]
类别 = B 不是 A 的下一句
NSP预测方法如下图所示:
BERT模型通过对Masked LM任务和Next Sentence Prediction任务进行联合训练,使模型输出的每个字/词的向量表示都能尽可能全面、准确地刻画输入文本(单句或语句对)的整体信息,为后续的微调任务提供更好的模型参数初始值。
五、微调
经过预训练后得到的模型可以用来微调各类任务,具体来说就是会根据下游任务的特点针对对应的BERT输出做一些处理,以满足任务的需求。
1 文本分类任务
单文本分类任务。前面提到,BERT模型在文本前插入一个[CLS]
符号,并将该符号对应的输出向量作为整篇文本的语义表示,用于文本分类,如下图所示。对[CLS]
对应的输出接一个线性分类器,可以用于文章的情感分类之类的任务,这个线性分类器的参数需要根据微调数据集进行学习。
2 序列标注任务
序列标注任务的实际应用场景包括:命名实体识别、中文分词、新词发现(标注每个字是词的首字、中间字或末字)、答案抽取(答案的起止位置)等。对于该任务,BERT模型利用文本中每个Token对应的输出向量对该Token进行标注(分类),如下图所示在每个Token对应的输出向量后,再接一个分类器用于标注、分类。
Bert 参数计算
1 计算公式
BERT模型的大小,主要是指模型的参数数量。参数数量的计算取决于Transformer模型中的几个关键组成部分:嵌入层、多头自注意力机制、前馈网络,以及其他少量的参数。下面我们来具体分析如何计算这些参数。
- 嵌入层参数:
BERT的输入是词嵌入向量,它包括词嵌入、位置嵌入和段落嵌入。假设词汇表的大小为 V V V,嵌入向量的维度为 H H H(即隐藏层大小)。
- 词嵌入参数: V × H V \times H V×H
- 位置嵌入参数:通常BERT的最大序列长度是512,所以位置嵌入参数为 512 × H 512 \times H 512×H
- 段落嵌入参数:BERT使用两段输入(A和B),所以段落嵌入的参数为 2 × H 2 \times H 2×H
因此,嵌入层的总参数量为:
V
×
H
+
512
×
H
+
2
×
H
V \times H + 512 \times H + 2 \times H
V×H+512×H+2×H
- 多头自注意力机制的参数:
在每一层Transformer块中,自注意力机制的参数可以分为查询向量(Query)、键向量(Key)、**值向量(Value)三个矩阵的线性变换,以及最后的输出投影层。对于每一个注意力头,每个矩阵的大小为 H × ( H / A ) H \times (H / A) H×(H/A),其中 A A A 是注意力头的数量。
每个自注意力头的参数为:
- Query矩阵: H × ( H / A ) H \times (H / A) H×(H/A)
- Key矩阵: H × ( H / A ) H \times (H / A) H×(H/A)
- Value矩阵: H × ( H / A ) H \times (H / A) H×(H/A)
总共有
A
A
A 个注意力头,因此自注意力层的参数总量为:
3
×
(
H
×
(
H
/
A
)
)
×
A
=
3
×
H
×
H
3 \times (H \times (H / A)) \times A = 3 \times H \times H
3×(H×(H/A))×A=3×H×H
此外,多头自注意力层的输出会经过一个线性变换回到原始维度
H
H
H,这会引入额外的参数:
H
×
H
H \times H
H×H
因此,每个自注意力模块的参数总量为:
4
×
H
×
H
4 \times H \times H
4×H×H
- 前馈网络的参数:
每个Transformer块还有一个前馈神经网络,通常由两层全连接网络组成。第一层的输出维度为
4
H
4H
4H,然后再映射回
H
H
H 维。前馈网络的参数总量为:
H
×
4
H
+
4
H
×
H
=
8
H
2
H \times 4H + 4H \times H = 8H^2
H×4H+4H×H=8H2
- 每层Transformer块的总参数:
每层的总参数量为:
4
H
2
(自注意力层)
+
8
H
2
(前馈网络)
=
12
H
2
4H^2 \text{(自注意力层)} + 8H^2 \text{(前馈网络)} = 12H^2
4H2(自注意力层)+8H2(前馈网络)=12H2
- 总参数量:
假设有 L L L 层Transformer块,那么模型的总参数量为:
- 嵌入层参数:
- 词嵌入: V × H V \times H V×H
- 位置嵌入: 512 × H 512 \times H 512×H
- 段落嵌入: 2 × H 2 \times H 2×H
- Transformer层参数: L × 12 H 2 L \times 12H^2 L×12H2
因此,总参数量公式为:
总参数
=
V
×
H
+
512
×
H
+
2
×
H
+
L
×
12
H
2
\text{总参数} = V \times H + 512 \times H + 2 \times H + L \times 12H^2
总参数=V×H+512×H+2×H+L×12H2
2 BERTBASE
BERTBASE网络的参数设置:
- L = 12 L = 12 L=12, H = 768 H = 768 H=768, A = 12 A = 12 A=12
- 词汇表大小 V ≈ 30 , 000 V \approx 30,000 V≈30,000
嵌入层参数:
30
,
000
×
768
+
512
×
768
+
2
×
768
=
23
,
040
,
000
+
393
,
216
+
1
,
536
=
23
,
434
,
752
30,000 \times 768 + 512 \times 768 + 2 \times 768 = 23,040,000 + 393,216 + 1,536 = 23,434,752
30,000×768+512×768+2×768=23,040,000+393,216+1,536=23,434,752
每层Transformer块的参数:
12
×
76
8
2
=
12
×
589
,
824
=
7
,
077
,
888
12 \times 768^2 = 12 \times 589,824 = 7,077,888
12×7682=12×589,824=7,077,888
总Transformer层参数:
12
×
7
,
077
,
888
=
84
,
934
,
656
12 \times 7,077,888 = 84,934,656
12×7,077,888=84,934,656
因此,BERTBASE的总参数量大约为:
23
,
434
,
752
+
84
,
934
,
656
=
108
,
369
,
408
≈
110
M
23,434,752 + 84,934,656 = 108,369,408 \approx 110M
23,434,752+84,934,656=108,369,408≈110M
3 BERTLARGE
BERTLARGE网络的参数设置:
- L = 24 L = 24 L=24, H = 1024 H = 1024 H=1024, A = 16 A = 16 A=16
嵌入层参数:
30
,
000
×
1024
+
512
×
1024
+
2
×
1024
=
30
,
720
,
000
+
524
,
288
+
2
,
048
=
31
,
246
,
336
30,000 \times 1024 + 512 \times 1024 + 2 \times 1024 = 30,720,000 + 524,288 + 2,048 = 31,246,336
30,000×1024+512×1024+2×1024=30,720,000+524,288+2,048=31,246,336
每层Transformer块的参数:
12
×
102
4
2
=
12
×
1
,
048
,
576
=
12
,
582
,
912
12 \times 1024^2 = 12 \times 1,048,576 = 12,582,912
12×10242=12×1,048,576=12,582,912
总Transformer层参数:
24
×
12
,
582
,
912
=
301
,
989
,
888
24 \times 12,582,912 = 301,989,888
24×12,582,912=301,989,888
因此,BERTLARGE的总参数量大约为:
31
,
246
,
336
+
301
,
989
,
888
=
333
,
236
,
224
≈
340
M
31,246,336 + 301,989,888 = 333,236,224 \approx 340M
31,246,336+301,989,888=333,236,224≈340M
总结:通过计算嵌入层、自注意力层、前馈网络以及总的Transformer层数,可以得出BERT模型的总参数量。
参考资料
- https://lulaoshi.info/deep-learning/attention/bert.html
- 彻底理解Google BERT
- 图解BERT模型:从零开始构建BERT
- The Illustrated BERT