- 1. 论文概述
- 2. 背景:为什么需要双向
- 3. BERT 架构
- 4. 输入表示(Input Representation)
- 5. 预训练方法论
- 6. 微调策略
- 7. 核心公式汇总
- 8. 实验结果
- 9. 代码示例:HuggingFace Transformers
- 10. BERT 的影响与后续研究
- 11. 局限与启示
- 12. 结语
- 参考资料
1. 论文概述
"BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding" 是 2018 年 10 月由 Google AI Language 团队的 Jacob Devlin、Ming-Wei Chang、Kenton Lee、Kristina Toutanova 发表的论文,此后在 NAACL 2019 上被评为 Best Long Paper。
BERT 是 Bidirectional Encoder Representations from Transformers 的缩写,顾名思义,它是将 Transformer 的 Encoder 进行双向预训练所得到的语言表示模型。核心思路很简单:先在大规模无监督文本语料上学习双向上下文,然后针对具体任务只做少量微调即可。
这一简单的方法在 11 个 NLP 基准上同时取得了 State-of-the-Art(SOTA)。它把 GLUE 分数推高到 80.5%(绝对提升 7.7%),SQuAD v1.1 F1 达到 93.2(提升 1.5 个百分点),SQuAD v2.0 F1 达到 83.1(提升 5.1 个百分点)。BERT 是在 NLP 领域确立了 预训练 + 微调 范式的论文,也成为了此后几乎所有语言模型的出发点。
2. 背景:为什么需要双向
2.1 预训练语言表示的两条路径
在 BERT 之前,利用预训练语言表示的方法大致分为两条路径。
Feature-based 方法(ELMo)
Peters 等人(2018)提出的 ELMo(Embeddings from Language Models)分别独立训练 Forward LSTM 与 Backward LSTM,再将两个方向的 hidden state 连接(concatenation)起来,生成依赖上下文的词表示。ELMo 的表示被用作下游任务的输入特征,每个任务都需要单独设计架构。
其中 与 分别是 Forward、Backward LSTM 第 层的 hidden state, 是可学习的权重。其核心局限在于 Forward 与 Backward 是 独立训练 的,两个方向的信息无法在更深的层中相互作用。
微调方法(OpenAI GPT)
Radford 等人(2018)提出的 GPT(Generative Pre-Training)使用 Transformer Decoder 以 Left-to-Right Language Modeling 的方式进行预训练,再针对下游任务对整个模型做微调。这种方式的优点是每个任务的架构改动被降到最低,但存在只能 单向(Left-to-Right)编码上下文的根本局限。
2.2 单向的局限
要理解 "The bank of the river" 中「bank」一词的含义,不仅要看「bank」左边的「The」,还要看右边的「river」。Left-to-Right 模型在编码「bank」时无法参照「river」,这正是 GPT-1 的根本局限。
ELMo 虽然考虑了两个方向,但它是独立训练 Forward 与 Backward 后再简单连接,属于 "浅层(shallow)双向"。真正的双向表示,应当是左右两侧的上下文在每一层都同时相互作用、共同学习。
2.3 三种方法的比较
| 特性 | ELMo | GPT-1 | BERT |
|---|---|---|---|
| 架构 | Bi-LSTM | Transformer Decoder | Transformer Encoder |
| 方向性 | 浅层双向(独立训练) | 单向(Left-to-Right) | 深层双向 |
| 预训练目标 | Forward + Backward LM | Left-to-Right LM | MLM + NSP |
| 下游应用方式 | Feature-based | 微调 | 微调 |
| 任务专属架构 | 每个任务都需要单独设计 | 改动最小 | 改动最小 |
| 参数量 | 94M | 117M | 110M(Base) / 340M(Large) |
BERT 的核心贡献,正是把这两种方法的优点结合了起来。它像 GPT 一样基于微调,也像 ELMo 一样利用双向上下文,但不是各自独立训练,而是 在所有层中同时(deeply) 学习双向上下文。
3. BERT 架构
3.1 Transformer Encoder Stack
BERT 只使用 Vaswani 等人(2017)提出的 Transformer 架构中的 Encoder 部分,不使用 Decoder。每个 Encoder 层由以下两个 Sub-layer 构成。
- Multi-Head Self-Attention:输入序列中的每个 token 都双向参照其他所有 token
- Position-wise Feed-Forward Network:对每个 token 独立执行非线性变换
每个 Sub-layer 都应用了 Residual Connection 与 Layer Normalization。
由于不使用 Decoder,也就没有 Auto-regressive Masking,所有位置的 token 都可以自由参照其他所有位置的 token。这正是 BERT 能够成为 真正的双向(deeply bidirectional) 模型的结构基础。
3.2 BERT-Base vs BERT-Large
论文提出了两种模型规模。
| 设置 | BERT-Base | BERT-Large |
|---|---|---|
| Layers () | 12 | 24 |
| Hidden Size () | 768 | 1,024 |
| Attention Heads () | 12 | 16 |
| Feed-Forward Size | 3,072 () | 4,096 () |
| 总参数量 | 110M | 340M |
BERT-Base 被有意设计为与 GPT-1 具有相同的模型规模(层数、Hidden Size、Attention Head 数),目的是为了公平比较——证明性能提升的原因是 预训练方法论的差异,而非架构规模。
每个 Attention Head 的维度为 。BERT-Base 中为 ,BERT-Large 中为 ,两个模型每个 Head 的维度都同为 64。
3.3 激活函数:GELU
BERT 没有使用 Transformer 原始论文中的 ReLU,而是采用 GELU(Gaussian Error Linear Unit)作为激活函数。
与 ReLU 不同,GELU 会根据输入值平滑地激活,在负值区间也保留一定的梯度。此后它成为了 GPT-2、RoBERTa 等大多数基于 Transformer 的模型的标准激活函数。
4. 输入表示(Input Representation)
BERT 的输入表示由三种 Embedding 相加构成。这一设计让单一模型能够同时处理单句和句子对任务。
4.1 WordPiece Tokenization
BERT 使用包含 30,000 个 WordPiece token 的词表。WordPiece 是 Byte-Pair Encoding(BPE)的变体,它不是按频率、而是按似然(likelihood)来合并子词。
例如,"playing" 会被拆分为 "play" + "##ing"。"##" 前缀表示该 token 不是单词的开头,而是前一个 token 的延续。这种方式的优点如下。
- 解决 OOV(Out-of-Vocabulary)问题:任何单词都可以用子词组合来表示
- 保留形态信息:"play" 与 "playing" 共享公共 token "play"
- 控制词表规模:用 30,000 个有限词表即可覆盖所有文本
4.2 特殊 token:[CLS] 与 [SEP]
BERT 使用两种特殊 token。
[CLS] (Classification Token):添加在每个输入序列的最前面。在分类任务中,这个 token 的最终 hidden state 被用作整个序列的聚合(aggregate)表示,因为通过 Self-Attention,序列中所有 token 的信息都会汇聚到 [CLS] 上。
[SEP] (Separator Token):用于分隔两个句子。在单句任务中添加在句子末尾,在句子对任务中则添加在两个句子之间以及第二个句子的末尾。
输入格式示例如下。
单句: [CLS] I love NLP [SEP]
句子对: [CLS] How old are you ? [SEP] I am 25 years old . [SEP]
4.3 三种 Embedding 之和
每个 token 的输入表示,由以下三种 Embedding 按 element-wise 相加 生成。
Token Embedding:针对 WordPiece token 的可学习 Embedding,词表规模为 30,000 时,对应一个大小为 的 Embedding 矩阵。
Segment Embedding:用于区分输入属于句子 A 还是句子 B 的可学习 Embedding。属于句子 A 的 token 用 表示,属于句子 B 的 token 用 表示。单句任务中所有 token 都使用 。
Position Embedding:编码序列内位置信息的可学习 Embedding。与 Transformer 原始论文中的 Sinusoidal Positional Encoding 不同,BERT 使用的是 可学习(learned) 的 Position Embedding,最大序列长度为 512。
这三种 Embedding 之和会被输入到 Transformer Encoder 的第一层。
5. 预训练方法论
BERT 的预训练由两个无监督任务构成,这两个任务的设计正是 BERT 论文的核心贡献。
5.1 Masked Language Model (MLM)
动机:双向训练的困境
标准 Language Model 预测的是 ,即给定之前的 token 时下一个 token 的概率,这本质上是 Left-to-Right 的。如果直接训练 Bidirectional Language Model,就会出现每个 token 间接"看到自己"的问题——在多层网络中,双向上下文会循环流动,导致预测目标词的信息发生泄漏。
BERT 用受 Cloze Task(完形填空)启发而来的 Masked Language Model 解决了这一困境。
15% 遮蔽策略
在每个训练序列中,随机选取 15% 的 WordPiece token 进行遮蔽(masking),训练目标就是预测被选中 token 的原始单词。
但如果只是简单地替换成 [MASK] token,就会产生问题:微调时 [MASK] token 并不会出现在输入中,因此预训练与微调之间会产生 不一致(mismatch)。
80/10/10 规则
为缓解这种不一致,被选中的 15% token 会按以下比例进行处理。
| 比例 | 处理方式 | 示例(从 "my dog is hairy" 中选中 "hairy" 时) |
|---|---|---|
| 80% | 替换为 [MASK] token | my dog is [MASK] |
| 10% | 替换为随机 token | my dog is apple |
| 10% | 保留原始 token | my dog is hairy |
具体来看,这一策略的效果如下。
- 80% [MASK]:让模型学习从上下文还原原始单词
- 10% Random:让模型对每个输入 token 是否为真实单词保持不确定,从而迫使模型在任何位置都维持正确的表示
- 10% Unchanged:缩小与微调时实际观察到的输入之间的差距
论文的 Ablation Study 表明 80/10/10 的比例是最优的;若只使用 100% [MASK],在 Feature-based 方法下性能会大幅下降。
MLM Loss 函数
MLM 的损失函数只在被遮蔽的位置计算 Cross-Entropy Loss。
其中 是被遮蔽 token 的索引集合, 是经过遮蔽处理的输入序列, 是模型在被遮蔽位置 预测出原始 token 的概率。
具体来说,会先将每个被遮蔽位置的最终 hidden state 投影到词表大小,再应用 Softmax。
其中 是输出权重矩阵, 是词表大小(30,000)。
MLM 的缺点是每个 batch 中只预测 15% 的 token,因此收敛速度比一般的 Left-to-Right LM 更慢。不过论文通过实验证明,性能提升足以抵消这一代价,且绰绰有余。
5.2 Next Sentence Prediction (NSP)
动机
Question Answering(QA)或 Natural Language Inference(NLI)这类任务,需要理解两个句子之间的 关系,而这种关系很难仅靠单纯的 Language Modeling 学到。
训练方法
NSP 是一个二分类任务。给定两个句子 A 和 B,模型要预测 B 是否真的是紧跟在 A 之后的句子(IsNext),还是从语料库中随机选取的句子(NotNext)。
- 50%:B 是 A 的真实下一句(IsNext)
- 50%:B 是从语料库中随机选取的句子(NotNext)
[CLS] The man went to [MASK] store [SEP] He bought a gallon [MASK] milk [SEP]
Label: IsNext
[CLS] The man [MASK] to the store [SEP] Penguin [MASK] are flight ##less birds [SEP]
Label: NotNext
把 [CLS] token 的最终 hidden state 输入二分类器进行预测。
NSP 的效果与争议
论文的 Ablation Study 显示,去掉 NSP 后,QNLI(-3.5%)、MNLI(-0.5%)、SQuAD(-0.6%)出现了显著的性能下降,尤其是在句子对关系推理很重要的任务上,下降幅度更为明显。
然而,此后的 RoBERTa(Liu et al., 2019)研究对 NSP 的效果提出了质疑。RoBERTa 在去掉 NSP 的情况下依然取得了比 BERT 更高的性能,并主张 NSP 的效果更多取决于训练数据的构成方式。
5.3 整体预训练损失
最终的预训练损失是 MLM 与 NSP 损失之和。
5.4 预训练数据与设置
| 设置 | 值 |
|---|---|
| 训练数据 | BooksCorpus(800M 词) + English Wikipedia(2,500M 词) |
| 总数据规模 | 约 16GB 文本 |
| 词表规模 | 30,000 个 WordPiece token |
| 最大序列长度 | 512 token |
| Batch Size | 256 个序列(128,000 token/批) |
| 训练步数 | 1,000,000 步(约 40 个 epoch) |
| Optimizer | Adam (, , ) |
| Learning Rate | 1e-4(10,000 步 warmup 后 linear decay) |
| Dropout | 0.1(所有层) |
| Activation | GELU |
| Hardware | 4 个 Cloud TPU Pod(16 个 TPU 芯片,BERT-Base) / 16 个 Cloud TPU Pod(64 个 TPU 芯片,BERT-Large) |
| 训练时间 | BERT-Base:4 天,BERT-Large:4 天 |
6. 微调策略
BERT 的微调出奇地简单:只需在预训练好的 BERT 上加一个任务专属的输出层,再对整个模型做端到端(End-to-End)微调。相比预训练,微调非常快,大多数任务 用单块 Cloud TPU 一小时以内、用 GPU 数小时以内即可完成。
6.1 微调超参数
以下超参数范围在大多数任务上都表现良好。
| 超参数 | 推荐范围 |
|---|---|
| Batch Size | 16、32 |
| Learning Rate (Adam) | 5e-5、4e-5、3e-5、2e-5 |
| Epochs | 2、3、4 |
| Dropout | 0.1(与预训练相同) |
6.2 句子/句子对分类(Classification)
在情感分析(SST-2)、自然语言推理(MNLI、RTE)、句子相似度(STS-B、MRPC、QQP)等任务中,会把 [CLS] token 的最终 hidden state 输入分类器。
其中 , 是标签数量。
对于句子对任务,输入格式如下。
[CLS] Sentence A [SEP] Sentence B [SEP]
用 Segment Embedding 区分句子 A 与 B,[CLS] 的表示则编码了两个句子之间的关系。
6.3 问答(Question Answering, QA)
在 SQuAD(Stanford Question Answering Dataset)中,给定问题和文章后,模型要预测答案在文章中对应区间(span)的起始与结束位置。
[CLS] Question [SEP] Passage [SEP]
训练起始位置向量 与结束位置向量 ,并对文章中每个 token 计算起始/结束概率。
其中 是 token 的最终 hidden state。候选答案 span 的得分由 ()计算得出。
SQuAD v2.0 还需要处理无答案的情况:当没有答案时,起始与结束位置都设为 [CLS],以此计算"无答案"的概率。
6.4 命名实体识别(Named Entity Recognition, NER)
在 CoNLL-2003 NER 任务中,每个 token 会被分类为 Person、Organization、Location、Miscellaneous、Other 之一。此时输入分类器的是 每个 token 的最终 hidden state,而非 [CLS] 的表示。
对于被 WordPiece 拆分出的子词 token,通常用第一个子词的预测结果作为该单词的标签。
6.5 Sequence Labeling 的推广
包括 NER 在内,POS Tagging、Chunking 等所有需要为序列中每个 token 打标签的任务,都可以套用同样的方式。因为 BERT 的每个 token 表示已经充分编码了双向上下文,所以即便没有 CRF(Conditional Random Field)这类额外的序列建模层,也能取得很高的性能。
6.6 Feature-based 方法
除了微调之外,BERT 也可以用作 Feature-based 方法:将 Transformer Encoder 冻结(freeze),抽取特定层的 hidden state 作为特征,输入到另外的模型中。
论文将 Feature-based 方式应用于 CoNLL-2003 NER 任务的结果显示,将最后 4 层的 hidden state 连接(concatenation)起来的表示达到了 96.1% F1(dev),非常接近微调方式的 96.4% F1(dev)。这说明 BERT 预训练得到的表示本身就蕴含着非常丰富的语言信息。
7. 核心公式汇总
7.1 Self-Attention (Scaled Dot-Product Attention)
这是 BERT 每个 Encoder 层中执行的 Self-Attention 公式。
其中 、、, 是每个 Attention Head 的维度。
7.2 Multi-Head Attention
BERT-Base 使用 个 head,BERT-Large 使用 个 head。
7.3 Feed-Forward Network
这是每个 Attention Sub-layer 之后的 Position-wise FFN。
其中 、,是内部维度为 Hidden Size 4 倍的 Bottleneck 结构。
7.4 MLM Loss(重新整理)
这是针对被遮蔽 token 集合 的 Cross-Entropy Loss。
其中 是被遮蔽位置 的最终 hidden state, 是输出 Embedding 矩阵的第 行,。
7.5 NSP Loss
其中 是真实标签。
7.6 整体预训练目标
8. 实验结果
8.1 GLUE Benchmark
GLUE(General Language Understanding Evaluation)是由 9 个 NLU 任务构成的基准。BERT-Large 取得了 80.5% 的 GLUE 总分,相比既有 SOTA 实现了 7.7% 的绝对提升。
| 任务 | 指标 | BERT-Base | BERT-Large | 既有 SOTA |
|---|---|---|---|---|
| MNLI-m / MNLI-mm | Accuracy | 84.6 / 83.4 | 86.7 / 85.9 | 80.6 / 80.1 |
| QQP | F1 | 71.2 | 72.1 | 66.1 |
| QNLI | Accuracy | 90.5 | 92.7 | 87.4 |
| SST-2 | Accuracy | 93.5 | 94.9 | 93.2 |
| CoLA | Matthews Corr | 52.1 | 60.5 | 35.0 |
| STS-B | Spearman Corr | 85.8 | 86.5 | 81.0 |
| MRPC | F1 | 88.9 | 89.3 | 86.0 |
| RTE | Accuracy | 66.4 | 70.1 | 61.7 |
| WNLI | Accuracy | - | 65.1 | 65.1 |
尤其令人印象深刻的是,CoLA 相比既有 SOTA 取得了 25.5% 的绝对提升。CoLA(Corpus of Linguistic Acceptability)是判断句子语法可接受性的任务,需要深层的语言理解能力。
8.2 SQuAD v1.1
SQuAD v1.1(Stanford Question Answering Dataset)是从文章中抽取答案区间的 Extractive QA 任务。
| 模型 | EM (Exact Match) | F1 |
|---|---|---|
| 既有 SOTA(单模型) | 84.4 | 91.0 |
| BERT-Large(单模型) | 84.1 | 90.9 |
| 既有 SOTA(集成) | 86.7 | 91.7 |
| BERT-Large + TriviaQA(集成) | 87.4 | 93.2 |
利用 TriviaQA 数据的集成模型取得了 93.2 的 F1,首次超过了人类表现(91.2 F1)。
8.3 SQuAD v2.0
SQuAD v2.0 是在 v1.1 基础上增加了 无法回答的问题(unanswerable questions) 的版本。
| 模型 | EM | F1 |
|---|---|---|
| 既有 SOTA | 73.7 | 77.0 |
| BERT-Large(单模型) | 80.0 | 83.1 |
相比既有 SOTA,F1 提升了 6.1 个百分点。在判断问题是否可回答这项能力上,BERT 的双向上下文理解表现出了特别的优势。
8.4 SWAG
SWAG(Situations With Adversarial Generations)是一个常识推理任务,需要从 4 个候选项中选出最适合接在给定句子后面的后续句子。
| 模型 | Dev Accuracy | Test Accuracy |
|---|---|---|
| Human | - | 88.0 |
| ESIM + ELMo | 51.9 | 52.7 |
| OpenAI GPT | - | 78.0 |
| BERT-Base | 81.6 | - |
| BERT-Large | 86.6 | 86.3 |
BERT-Large 领先 GPT 8.3%,逼近人类表现(88.0%)。
8.5 Ablation Study:预训练任务的影响
论文以 BERT-Base 为基准,分析了各预训练任务的重要性。
| 设置 | MNLI-m | QNLI | MRPC | SST-2 | SQuAD F1 |
|---|---|---|---|---|---|
| BERT-Base (MLM + NSP) | 84.4 | 88.4 | 86.7 | 92.7 | 88.5 |
| No NSP | 83.9 | 84.9 | 86.5 | 92.6 | 87.9 |
| LTR (Left-to-Right) & No NSP | 82.1 | 84.3 | 77.5 | 92.1 | 77.8 |
| LTR + BiLSTM & No NSP | 82.1 | 84.1 | 75.7 | 91.6 | 84.9 |
核心发现如下。
- 去掉 NSP:QNLI 下降 3.5%,在句子对关系很重要的任务上影响较大
- Left-to-Right:相比双向,SQuAD 上 F1 下降 10.7,双向性对 token 级别任务至关重要
- LTR + BiLSTM:即便加上 BiLSTM,也无法替代双向预训练
8.6 Ablation Study:模型规模的影响
| 设置 | L | H | A | Params | MNLI-m | MRPC | SST-2 | SQuAD F1 |
|---|---|---|---|---|---|---|---|---|
| 3-layer | 3 | 768 | 12 | 45M | 77.9 | 79.8 | 88.4 | 75.6 |
| 6-layer | 6 | 768 | 12 | 67M | 80.6 | 84.3 | 91.1 | 83.7 |
| BERT-Base | 12 | 768 | 12 | 110M | 84.4 | 86.7 | 92.7 | 88.5 |
| BERT-Large | 24 | 1024 | 16 | 340M | 86.6 | 87.8 | 93.7 | 91.3 |
模型规模增大后,所有任务的性能都得到了一致的提升。特别值得注意的是,即使在小数据集(MRPC:3,600 个训练样本)上,更大的模型依然表现更好。这说明预训练提供了充分的知识,即使微调数据很少,大模型也能被有效训练出来。
9. 代码示例:HuggingFace Transformers
9.1 用 BERT 做 Masked Language Model 预测
from transformers import BertTokenizer, BertForMaskedLM
import torch
# 加载模型与分词器
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
model = BertForMaskedLM.from_pretrained("bert-base-uncased")
model.eval()
# 准备被遮蔽的句子
text = "The capital of France is [MASK]."
inputs = tokenizer(text, return_tensors="pt")
# 找到 [MASK] 的位置
mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]
# 预测
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits
# 提取 Top-5 预测 token
mask_logits = logits[0, mask_token_index, :]
top5_tokens = torch.topk(mask_logits, 5, dim=1)
print(f"输入: {text}")
print("Top-5 预测:")
for i, (token_id, score) in enumerate(
zip(top5_tokens.indices[0], top5_tokens.values[0])
):
token = tokenizer.decode([token_id])
print(f" {i+1}. {token} (score: {score:.4f})")
输出示例:
输入: The capital of France is [MASK].
Top-5 预测:
1. paris (score: 18.2341)
2. lyon (score: 12.1456)
3. lille (score: 10.8923)
4. toulouse (score: 10.5678)
5. marseille (score: 10.3210)
9.2 句子分类(Sentiment Analysis)微调
from transformers import BertTokenizer, BertForSequenceClassification
from transformers import Trainer, TrainingArguments
from datasets import load_dataset
# 加载数据集(SST-2)
dataset = load_dataset("glue", "sst2")
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
# 分词
def tokenize_function(examples):
return tokenizer(
examples["sentence"],
padding="max_length",
truncation=True,
max_length=128,
)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
# 初始化模型(二分类)
model = BertForSequenceClassification.from_pretrained(
"bert-base-uncased",
num_labels=2,
)
# 训练设置(论文推荐的超参数)
training_args = TrainingArguments(
output_dir="./results",
num_train_epochs=3, # 论文推荐: 2-4
per_device_train_batch_size=32, # 论文推荐: 16 或 32
learning_rate=2e-5, # 论文推荐: 2e-5 ~ 5e-5
weight_decay=0.01,
warmup_ratio=0.1,
evaluation_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="accuracy",
)
# 用 Trainer 训练
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
)
trainer.train()
9.3 问答(Question Answering)
from transformers import BertTokenizer, BertForQuestionAnswering
import torch
tokenizer = BertTokenizer.from_pretrained("bert-large-uncased-whole-word-masking-finetuned-squad")
model = BertForQuestionAnswering.from_pretrained("bert-large-uncased-whole-word-masking-finetuned-squad")
model.eval()
# 问题与文章
question = "What is BERT?"
context = """BERT is a language representation model developed by Google.
It stands for Bidirectional Encoder Representations from Transformers.
BERT is designed to pre-train deep bidirectional representations from
unlabeled text by jointly conditioning on both left and right context
in all layers."""
# 分词
inputs = tokenizer(question, context, return_tensors="pt")
# 预测
with torch.no_grad():
outputs = model(**inputs)
start_logits = outputs.start_logits
end_logits = outputs.end_logits
# 提取答案
start_idx = torch.argmax(start_logits)
end_idx = torch.argmax(end_logits)
answer_tokens = inputs["input_ids"][0][start_idx : end_idx + 1]
answer = tokenizer.decode(answer_tokens)
print(f"问题: {question}")
print(f"答案: {answer}")
# 输出: 答案: a language representation model developed by google
9.4 命名实体识别(NER)
from transformers import pipeline
# 创建 NER pipeline
ner_pipeline = pipeline(
"ner",
model="bert-base-cased", # NER 任务中大小写区分很重要
aggregation_strategy="simple",
)
text = "Google released BERT in 2018 at their Mountain View headquarters."
results = ner_pipeline(text)
for entity in results:
print(f"Entity: {entity['word']:20s} | "
f"Label: {entity['entity_group']:5s} | "
f"Score: {entity['score']:.4f}")
9.5 提取 BERT Embedding(Feature-based 方法)
from transformers import BertModel, BertTokenizer
import torch
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
model = BertModel.from_pretrained("bert-base-uncased", output_hidden_states=True)
model.eval()
text = "BERT produces contextualized word embeddings."
inputs = tokenizer(text, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
# 所有层的 hidden states(共 13 个:embedding + 12 layers)
all_hidden_states = outputs.hidden_states # tuple of (batch, seq_len, 768)
# 用 [CLS] token 表示句子(最后一层)
sentence_embedding = outputs.last_hidden_state[:, 0, :]
print(f"句子 embedding 维度: {sentence_embedding.shape}") # (1, 768)
# 论文方式: 连接最后 4 层(Feature-based)
last_4_layers = torch.cat(
[all_hidden_states[i] for i in [-4, -3, -2, -1]], dim=-1
)
print(f"最后 4 层连接后的维度: {last_4_layers.shape}") # (1, seq_len, 3072)
10. BERT 的影响与后续研究
BERT 给 NLP 领域带来了范式转变。"预训练 + 微调"这一方法论如今已经成为 NLP 的标准,为克服 BERT 的局限,大量后续研究相继涌现。
10.1 RoBERTa (Liu et al., 2019, Meta AI)
Robustly Optimized BERT Pretraining Approach。在不改变 BERT 架构的前提下,仅通过 优化训练方法论 就大幅提升了性能。
主要变更:
- 去掉 NSP:通过实验证明 NSP 反而会损害性能
- Dynamic Masking:BERT 在数据预处理阶段就固定(static)了遮蔽方式,而 RoBERTa 每个 epoch 都会改变遮蔽模式
- 更多数据:从 16GB 增加到 160GB,扩大 10 倍(加入 CC-News、OpenWebText、Stories)
- 更大的 batch:从 256 增加到 8,000
- 更长的训练:50,000 个 BPE 词表、更多训练步数
结果:在 GLUE、SQuAD、RACE 上的所有任务中都超过了 BERT-Large,说明 BERT 的预训练此前 严重欠训练(undertrained)。
10.2 ALBERT (Lan et al., 2019, Google)
A Lite BERT。这是一个聚焦参数效率的模型。
主要技巧:
- Factorized Embedding Parameterization:将词表 Embedding 维度()与 Hidden 维度()分离,用 代替 来削减参数。例如当 时, 个参数会减少到
- Cross-layer Parameter Sharing:所有 Transformer 层共享同一组参数,在大幅削减参数量的同时把性能下降控制到最小
- Sentence Order Prediction (SOP):用判断两个连续句子顺序是否正确的任务取代 NSP。这是一个比 NSP 更难的任务,对学习句子间关系更有效
结果:以比 BERT-Large 少 18 倍的参数,取得了相近甚至更好的性能。
10.3 DistilBERT (Sanh et al., 2019, Hugging Face)
利用 Knowledge Distillation 压缩 BERT 而得到的模型。
- BERT-Base 40% 的体积(66M 参数,6 层)
- 比 BERT-Base 快 60%
- 保留了 BERT-Base 97% 的性能
- 训练时使用了模仿 Teacher(BERT-Base)Soft Label 的 Distillation Loss
- 在 SQuAD v1.1 上取得 86.9 F1(BERT-Base:88.5 F1)
成为了适合部署在移动端、边缘设备上的轻量模型的起点。
10.4 ELECTRA (Clark et al., 2020, Google/Stanford)
Efficiently Learning an Encoder that Classifies Token Replacements Accurately。从根本上改进了 BERT MLM 训练低效问题的模型。
核心思路:
- Generator-Discriminator 结构:由小型 Generator(MLM)填补被遮蔽的 token,再由 Discriminator 判断每个 token 是原始的还是 Generator 生成的
- 在所有 token 上学习:BERT 只在 15% 被遮蔽的 token 上学习,而 ELECTRA 在 所有 token 上学习"原始 vs 替换"的判别,训练效率大幅提升
- Replaced Token Detection (RTD):一个比 MLM 更高效的预训练目标
结果:在相同计算预算下超过了 BERT、RoBERTa、ALBERT,尤其在小型模型(ELECTRA-Small)上,效率差距被拉到最大。
10.5 DeBERTa (He et al., 2020, Microsoft)
Decoding-enhanced BERT with Disentangled Attention。改进了 Attention 机制本身的模型。
主要创新:
- Disentangled Attention:以往的 BERT 会先把 Token Embedding 与 Position Embedding 相加(sum)再做 Attention,而 DeBERTa 把 Content 与 Position 分离开,分别做 Attention,再结合 Content-to-Content、Content-to-Position、Position-to-Content 三种 Attention
- Enhanced Mask Decoder:在解码层注入绝对位置信息,同时利用相对位置与绝对位置信息
结果:在同等模型规模下取得了比 BERT、RoBERTa 更高的性能,尤其仅用一半的训练数据就超越了既有模型,并在 SuperGLUE 上超过了人类表现。
10.6 后续研究谱系小结
BERT (2018)
├── RoBERTa (2019) ─── 训练方法论优化
├── ALBERT (2019) ─── 参数效率
├── DistilBERT (2019) ─── Knowledge Distillation
├── ELECTRA (2020) ─── 训练效率 (RTD)
├── DeBERTa (2020) ─── Attention 机制改进
├── SpanBERT (2020) ─── Span 级遮蔽
├── ERNIE (2019) ─── Knowledge-enhanced 预训练
└── ModernBERT (2024) ─── 应用现代架构
11. 局限与启示
11.1 BERT 的局限
1. 预训练与微调的不一致 (Pre-train/Fine-tune Discrepancy)
[MASK] token 只出现在预训练中,不会出现在微调中。80/10/10 策略缓解了这一问题,但没有从根本上解决。ELECTRA 的 RTD 用更优雅的方式解决了这个问题。
2. 假设被遮蔽 token 之间相互独立
MLM 是独立地预测各个被遮蔽的 token。例如当 "New York" 整体被遮蔽时,"New" 与 "York" 的预测是各自独立进行的,但实际上这两个 token 之间存在很强的依赖关系。XLNet(Yang et al., 2019)试图用 Permutation Language Modeling 解决这一问题。
3. 训练效率低
由于每个 batch 只预测 15% 的 token,收敛需要更多训练步数。ELECTRA 通过在所有 token 上学习,大幅改善了这一低效问题。
4. 最大序列长度限制
512 token 的最大长度限制不适合处理长文档。由于 Self-Attention 的 复杂度,很难大幅增加序列长度。Longformer、BigBird 等模型用 Sparse Attention 缓解了这一问题。
5. 不适合生成任务
由于 BERT 是 Encoder-only 模型,无法直接用于文本生成(摘要、翻译、对话等)。生成任务更适合用 GPT 系列(Decoder-only)或 T5、BART 系列(Encoder-Decoder)模型。
6. NSP 的效果存疑
RoBERTa 的实验揭示,NSP 反而可能损害性能。问题在于 NSP 本身是一个过于简单的任务——随机选取的句子仅凭主题(topic)差异就很容易被区分开。
11.2 从论文中获得的启示
1. 简单想法的力量
BERT 的核心思路(学习双向上下文)出奇地简单。它并非依靠复杂的架构创新,仅凭改变预训练目标(MLM)就取得了戏剧性的性能提升。
2. Scaling 的效果
Ablation Study 表明,增大模型规模与数据规模会带来持续一致的性能提升。这为此后的 Scaling Law 研究以及 GPT-3 等大规模模型的开发奠定了基础。
3. 确立 Transfer Learning 范式
正如 ImageNet 预训练曾经变革了 Computer Vision,BERT 证明了在 NLP 中,利用预训练语言模型应当成为 一切任务的出发点。从此不再需要为每个任务从零开始设计架构。
4. 公平比较的重要性
把 BERT-Base 设计成与 GPT-1 相同的规模,清楚地表明性能提升的原因是方法论的差异,而不是架构规模。这是研究论文实验设计中的一条重要启示。
12. 结语
BERT 自 2018 年发表以来,彻底改变了 NLP 领域的格局。它用 Masked Language Model 这一优雅的方法,实现了双向上下文学习这个简单却强大的想法,并通过同时横扫 11 个基准证明了其效果。
BERT 最大的遗产并不是某个具体基准上的数字,而是在 NLP 领域确立了 "先在大规模无监督语料上预训练,再用少量标注数据微调" 这一范式。这一范式此后成为了从 GPT-3、T5、PaLM 直到 LLaMA 等现代语言模型的基石。
当然,BERT 也有它的局限。遮蔽不一致、训练低效、序列长度限制等问题,后来都被 RoBERTa、ELECTRA、Longformer 等后续研究逐一解决。但所有这些后续研究都建立在 BERT 这个起点之上,这一事实本身,就说明了 BERT 论文的历史重要性。
参考资料
- Devlin, J., Chang, M.W., Lee, K., & Toutanova, K. (2018). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. NAACL 2019. https://arxiv.org/abs/1810.04805
- 论文全文(HTML 版本): https://ar5iv.labs.arxiv.org/html/1810.04805
- ACL Anthology: https://aclanthology.org/N19-1423/
- Vaswani, A. et al. (2017). Attention Is All You Need. NeurIPS 2017. https://arxiv.org/abs/1706.03762
- Peters, M. et al. (2018). Deep contextualized word representations (ELMo). NAACL 2018. https://arxiv.org/abs/1802.05365
- Radford, A. et al. (2018). Improving Language Understanding by Generative Pre-Training (GPT). https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf
- Liu, Y. et al. (2019). RoBERTa: A Robustly Optimized BERT Pretraining Approach. https://arxiv.org/abs/1907.11692
- Lan, Z. et al. (2019). ALBERT: A Lite BERT for Self-supervised Learning of Language Representations. https://arxiv.org/abs/1909.11942
- Sanh, V. et al. (2019). DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter. https://arxiv.org/abs/1910.01108
- Clark, K. et al. (2020). ELECTRA: Pre-training Text Encoders as Discriminators Rather Than Generators. https://arxiv.org/abs/2003.10555
- He, P. et al. (2020). DeBERTa: Decoding-enhanced BERT with Disentangled Attention. https://arxiv.org/abs/2006.03654
- Jay Alammar, The Illustrated BERT, ELMo, and co.: https://jalammar.github.io/illustrated-bert/
- HuggingFace Transformers Documentation: https://huggingface.co/docs/transformers
현재 단락 (1/429)
"BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding" 是 2018 年 10 月由 Go...