BERT:预训练双向 Transformer 编码器如何用一句话改写 NLP 的任务范式

  • 关联论文:1810.04805
  • 作者:flyP
  • 更新:2026-11-04

一句话结论

BERT 没有发明新的架构,它只是把「掩码语言模型(MLM)+ 双向 Transformer + 端到端微调」三件早已存在的零件组装成一台统一的预训练机器,结果在 11 个 NLP 任务上刷出 SOTA——并由此确立了整个 NLP 领域此后五年的标准作业流程(SOP)。

它解决的真问题

在 2018 年之前,NLP 领域被两套割裂的范式主导:

范式 A:任务专属架构 + 词向量。ELMo 那一路( Peters et al., 2018a)是「特征派」——先用一个任务无关的 RNN 语言模型学出向量,再把这些向量喂给每个下游任务各自设计的模型(QA 一个 BiDAF、分类一个 LSTM、NER 一个 BiLSTM-CRF)。每个任务都要写网络、改输入层、重新训练。论文原文明确说这种做法是 "feature-based approach",即任务模型需要自己再学一遍。

范式 B:条件语言模型(GPT)。Radford et al., 2018 走的是「微调派」——预训练一个从左到右的 LM,然后把最后一个 hidden state 接一个输出层。但它有结构性天花板:标准条件语言模型只能单向训练,否则每个词都能间接「看见自己」,目标词变得可平凡预测。

BERT 要解决的正是这个矛盾:如何训练一个每一层都能同时看左右上下文的深度双向模型。论文第 3.1 节给出的直觉论证很干净——「深度双向模型严格来说比单向模型、或左右 LSTM 拼接的浅层模型更强」。

第二重问题是「一个模型服务所有任务」。BERT 的答案是:把 self-attention 当作统一接口。文本对不需要「先各自编码再做双向 cross-attention」(Parikh et al., 2016;Seo et al., 2017 那套两阶段做法),只要把两段文本拼成一个序列,self-attention 天然就包含了两句之间的双向交互。

核心方法:三个机制讲清楚

机制一:MLM——用「挖坑」绕开双向训练的自泄露

这是全篇最核心的一招。机制设计如下:

从输入序列中随机挑 15% 的 WordPiece token 位置作为预测目标。但不是全部替换成 [MASK],而是按论文 verbatim 比例分配:

if (第 i 个 token 被选中):
    80% 概率 → 替换成 [MASK]
    10% 概率 → 替换成一个随机 token
    10% 概率 → 保持原 token 不变
然后用该位置的最终 hidden vector T_i 经 softmax 预测**原始** token,交叉熵损失

为什么非要搞这个 80/10/10 的混合?这是为了缓解 pretrain-finetune mismatch——论文原话是「[MASK] token 在微调阶段根本不出现」,如果训练时 100% 用 [MASK],模型会学会过度依赖这个万能占位符的表示(学界俗称 [MASK] artifact),微调时突然没有这个符号就会掉点。

附录 C.2 的消融是本文最有说服力的实验之一(Dev 集结果,verbatim):

Mask Same Rnd MNLI NER (fine-tune) NER (feature-based)
80% 10% 10% 84.2 95.4 94.9
100% 0% 0% 84.3 94.9 94.0
80% 0% 20% 84.1 95.2 94.6
80% 20% 0% 84.4 95.2 94.7
0% 20% 80% 83.7 94.8 94.6
0% 0% 100% 83.6 94.9 94.6

这是本文必须明说的负面发现:在 MNLI 上,纯 [MASK](100/0/0)拿到 84.3,反而比 80/10/10 的 84.2 高 0.1;在 NER fine-tune 上也是 94.9 > 95.4 的反例不成立——纯 Mask 方案是 94.9 < 95.4。也就是说 80/10/10 的收益主要体现在 feature-based 那一列(94.9 vs 94.0,+0.9),即「不能调整表征」的场景;换成端到端微调,混合策略的优势被抹平甚至倒挂。论文自己的表述是「The purpose of the masking strategies is to reduce the mismatch...」,但数字表明这个收益在微调范式下并不稳定。这条实验是后来 RoBERTa(1907.11692)复现研究直接质疑的目标之一。

机制二:NSP——一个「几乎白送」的句间关系任务

50% 概率取语料中真实相邻的句 B(标 IsNext),50% 概率取语料中随机句 B(标 NotNext),用 [CLS] 位置的表示 C 做二分类。

设计动机是下游任务里有大量「理解两个句子关系」的需求(QA 的 question-passage、NLI 的 hypothesis-premise),而纯 LM 目标学不到。但成本极低——不需要任何标注,从任何单语语料里就能自动生成。

论文脚注 5 提到:最终模型在 NSP 上达到 97%-98% 的准确率。这个数字本身就是一个警告信号:一个能在 97-98% 上做对的二分类任务,其训练信号的信息量极低,模型几乎不可能从中学到真正精细的句间语义推理。这一点在四年后被 RoBERTa 直接推翻(见下文「与同方向工作的关系」)。

机制三:统一输入/输出接口——一个 [CLS] 打天下

  • 输入表示 = token embedding + segment embedding + position embedding 三者相加
  • 句间分隔:两个句子用 [SEP] 分开,并给每个 token 加一个 learned segment embedding(A 还是 B)
  • 分类任务:取序列首位 [CLS] 的最终 hidden state C ∈ R^H,送进一个输出层
  • token 级任务(NER、QA):取对应位置的 T_i,送进输出层

微调端的映射规则极其机械,这也是 BERT「概念上简单」这个评价的由来:

预训练输入          →  下游任务输入
(A, B) 句对         →  (paraphrase 对) / (hypothesis, premise) / (question, passage) / (text, ∅)
[CLS] 表示          →  分类任务输出层
token 表示 T_i      →  token 级任务输出层(序列标注、抽取式 QA)

微调时端到端更新全部参数,不做任何层冻结。

架构与训练配置(verbatim)

  • BERT_BASE:L=12, H=768, A=12, Total Parameters=110M
  • BERT_LARGE:L=24, H=1024, A=16, Total Parameters=340M
  • 前馈层尺寸固定为 4H(即 3072 和 4096)
  • WordPiece 词表 30,000 token
  • 预训练语料:BooksCorpus(800M words)+ English Wikipedia(2,500M words);Wikipedia 只取正文段落,忽略 lists、tables、headers
  • 论文特别强调:必须用文档级语料而非打乱的句子级语料(如 Billion Word Benchmark),才能抽出长的连续序列
  • 优化:Adam,β1=0.9,β2=0.999,ε=1e-6,L2 weight decay 0.01
  • 学习率:前 10,000 steps warmup 到峰值 1e-4,然后线性衰减
  • Dropout 0.1(所有层与 attention weights),激活函数 GELU
  • 预训练 1,000,000 steps,batch 256 序列,最大长度 512 token

伪代码:把 BERT 的训练循环写出来

# ===== Phase 1: Pre-training =====
for step in range(1_000_000):
    batch = next(dataloader)           # 每个样本: [CLS] A [SEP] B [SEP]
    # --- 构造输入表示 ---
    E = TokenEmb(ids) + SegmentEmb(seg) + PosEmb(pos)   # 三者相加
    # --- 构造 MLM 标签 (15% 选中) ---
    for i in selected_positions(0.15):                   # 随机挑 15%
        p = rand()
        if   p < 0.8: ids[i] = MASK_ID
        elif p < 0.9: ids[i] = rand_token()
        else:         ids[i] = ids[i]                  # 保持原样
        labels[i] = original_id                        # 监督信号始终是原词
    # --- NSP ---
    labels_nsp = 0 if B_is_next else 1
    # --- Forward ---
    H = TransformerEncoder(E, n_layers=L, n_heads=A)   # 全部层都双向
    loss_mlm  = CrossEntropy(H[selected_positions], labels[selected_positions])
    loss_nsp  = CrossEntropy(H[0], labels_nsp)         # H[0] 是 [CLS] 的表示
    loss = loss_mlm + loss_nsp
    # --- Optimizer ---
    lr = warmup_linear(step, peak=1e-4, warmup=10_000, total=1_000_000)
    (loss + 0.01 * l2_penalty).backward()
    AdamW(lr, betas=(0.9, 0.999), eps=1e-6).step()

# ===== Phase 2: Fine-tuning (以分类任务为例) =====
for task in [MNLI, QQP, QNLI, SST-2, CoLA, STS-B, MRPC, RTE]:
    model = deepcopy(pretrained_bert)
    head = Linear(H, num_labels)                        # 只加一层
    for epoch in range(num_epochs):
        for batch in task_train_loader:
            H = model(input_ids, segment_ids, token_type_ids)
            loss = CrossEntropy(head(H[0]), labels)      # [CLS] → 分类
            loss.backward(); optimizer.step()

关键实验与数据

GLUE 测试集(Table 1,verbatim,来自 GLUE 评测服务器)

System MNLI-(m/mm) QQP QNLI SST-2 CoLA STS-B MRPC RTE Average
Pre-OpenAI SOTA 80.6/80.1 66.1 82.3 93.2 35.0 81.0 86.0 61.7 74.0
BiLSTM+ELMo+Attn 76.4/76.1 64.8 79.8 90.4 36.0 73.3 84.9 56.8 71.0
OpenAI GPT 82.1/81.4 70.3 87.4 91.3 45.4 80.0 82.3 56.0 75.1
BERT_BASE 84.6/83.4 71.2 90.5 93.5 52.1 85.8 88.9 66.4 79.6
BERT_LARGE 86.7/85.9 72.1 92.7 94.9 60.5 86.5 89.3 70.1 82.1

论文脚注 9 明确说明:Average 列与官方 GLUE score 略有不同,因为排除了有问题的 WNLI 数据集;BERT 与 GPT 均为单模型单任务,QQP/MRPC 报 F1,STS-B 报 Spearman 相关,其余报准确率。

摘要中点名的四个数字(verbatim 引号)

  • GLUE score "80.5%"("7.7% point absolute improvement")——注意这是摘要里的官方 GLUE score,与 Table 1 的 "Average" 列(82.1)口径不同,两者不可混用
  • MultiNLI accuracy "86.7%"("4.6% absolute improvement")
  • SQuAD v1.1 Test F1 "93.2"("1.5 point absolute improvement")
  • SQuAD v2.0 Test F1 "83.1"("5.1 point absolute improvement")

训练步数消融(附录 C.1,Figure 5)

原文问答形式:「BERT_BASE 真的需要 128,000 words/batch × 1,000,000 steps 这么大量的预训练吗?」答:「Yes,BERT_BASE 在 1M steps 相比 500k steps 的 MNLI 上多出接近 1.0% 的准确率。」

同时承认:MLM 收敛比 LTR 慢(因为每个 batch 只预测 15% 的词),但「在绝对准确率上,MLM 模型几乎立刻就开始超过 LTR 模型」。

微调成本

「所有结果都可以在单块 Cloud TPU 上最多 1 小时内复现,GPU 上几小时。」具体例子:「BERT 的 SQuAD 模型可以在单块 Cloud TPU 上训练约 30 分钟达到 Dev F1 91.0%。」

亮点与局限

真正的亮点(结构性贡献)

  1. MLM 是训练深度双向模型的通用解法——这个 trick 的价值远大于 BERT 本身,后来所有 encoder-only 预训练模型(RoBERTa、ALBERT、DeBERTa)都沿用。
  2. 统一接口让「换任务」变成「换输入输出」。这是范式级贡献:从此做 NLP 任务的默认动作从「设计架构」变成「写数据 loader」。
  3. 端到端微调 + 极低成本(1 小时 TPU)让学术小组第一次能负担得起「每个任务都从预训练模型起步」的实验范式。
  4. 参数量友好:BERT_BASE 只有 110M,与 GPT 同量级(论文明确说明这个尺寸是为了与 GPT 公平对比才选的)。

必须诚实标注的局限

  1. 【负面发现】80/10/10 掩码策略的收益在微调场景下不成立。附录 C.2 的数字显示纯 [MASK] 方案在 MNLI 上是 84.3 vs 84.2、NER fine-tune 上是 94.9 vs 95.4,混合策略的优势主要只体现在 feature-based 一列(94.9 vs 94.0)。这个消融是 2018 年的,后续被证明基本不成立。
  2. NSP 是一个信息量被高估的任务:模型自己就能做到 97-98% 准确率,这个损失项提供的有效监督信号极为有限。
  3. 【负面发现】BERT 严重训练不足。附录 C.1 显示 500k → 1M steps 还能再涨 1.0% MNLI 准确率,说明 1M steps 并非收敛点。这一条四年后被 RoBERTa 系统性放大为「BERT significantly undertrained」的论断。
  4. 预训练数据仅 16GB(BooksCorpus + Wikipedia),且用启发式分词规则预处理 + character-level BPE 30K 词表——RoBERTa 后来证明这两处都是可改进项。
  5. 长度限制 512 token,长文档任务(摘要、长文问答)需要额外处理。
  6. 单语、单任务评测,跨语言与多任务泛化不是本文的目标。

对工程落地的启发

  1. 优先做「换数据 loader」而不是「换模型」。BERT 之后,绝大多数 NLP 需求的第一反应应该是:冻结架构层,只写 Task #1(数据格式)与 Task #2(输出层)。
  2. MLM 的 80/10/10 是可以直接抄的配方,但不要盲信它比 100% Mask 更好——若你走的是端到端微调路线,附录 C.2 的消融数据已经给了你直接证据。
  3. [CLS] 的多层用途:分类、句对、以及 QA 中的 span 起点/终点预测都从它出发,所以为 [CLS] 保留足够的容量不是浪费。
  4. 预训练与微调的 scale 要匹配。BERT 附录明确说 1M steps 不够;如果你的算力允许,延长预训练几乎总是比微调技巧更划算——这是 RoBERTa 后来给出的更强版本。
  5. 微调成本的现实感:1 小时 TPU / 30 分钟出 SQuAD 91.0 F1,意味着「一个任务一套模型」的边际成本极低,可以放心做多模型 ensemble(RoBERTa 的 GLUE 榜单提交正是这么做的:每任务 ensemble 5-7 个模型)。
  6. 注意 segment embedding 的隐含前提:BERT 用 [SEP] + segment id 来表达句对语义,如果你的业务里「两段文本」不是简单拼接关系(比如多轮对话需要保留轮次顺序),不要照抄这套 segment 机制。
  7. 不要在 BERT 上直接堆业务逻辑。这篇论文真正留下的遗产是「接口标准化」,而不是「某个具体 checkpoint」。

与同方向工作的关系

上游/同时代

  • ELMo(Peters et al., 2018a):特征派路线,biLSTM 拼接。BERT 论文 Figure 3 明确对比了三者架构差异:只有 BERT 的所有层都联合依赖左右上下文;ELMo 是「两个独立单向 LSTM 输出的拼接」,GPT 是「left-to-right Transformer」。
  • OpenAI GPT(Radford et al., 2018):单向 + 微调。BERT_BASE 的 110M 尺寸就是为与 GPT 公平对比而设。
  • Transformer(Vaswani et al., 2017):BERT 的直接架构底座,论文称其为 "now ubiquitous"。
  • WordPiece(Wu et al., 2016):30K 词表的来源。
  • 涂顺序 LM / 去噪自编码器:论文把 MLM 类比为 "Cloze task"(Taylor, 1953),并把去噪自编码器(Vincent et al., 2008)作为对照——区别在于只预测被遮蔽的词,而非重建整个输入。

下游/后续

  • RoBERTa(1907.11692,本知识库同日另篇):复现研究,逐条拆掉 BERT 的四个「设计选择」——更长的训练、更大的 batch、去掉 NSP、更长的序列、动态掩码——结论是 "BERT was significantly undertrained",且 "can match or exceed the performance of every model published after it"。这直接证伪了 BERT 的 NSP 设计与 80/10/10 掩码策略。
  • XLNet(Yang et al., 2019):用 permutation language modeling 攻击 BERT 的「条件独立性」问题(MLM 假设被遮蔽 token 之间相互独立),但训练成本高得多。RoBERTa 后来证明:把 BERT 的配方调对,MLM 目标依然能打平 XLNet。
  • XLM(Lample & Conneau, 2019):跨语言扩展 BERT,也是最早质疑 NSP 必要性的工作之一。
  • 现代 LLM 主线(GPT 系列):出于自回归生成的需求,整体转向 decoder-only + next-token prediction;BERT 的 MLM 目标在 encoder-only 与 embedding 场景仍存续(现代 embedding 模型如 BGE、text-embedding-3 普遍采用类似 BERT 的双向 encoder 底座)。但需要说明:BERT 的 [MASK] token 机制今天基本只作为历史遗产存在——现代 embedding 模型早已改用 causal decoder + 双向注意力/反向前缀的组合(如 LLM2Vec 类做法)来避免 train-finetune mismatch。这是本文撰写时基于领域演进的判断,不属于 1810.04805 原文内容。

适合谁读

  • 刚入门的 NLP 工程师:这是理解「预训练 + 微调」范式的最短路径,建议配合 RoBERTa 一起读(BERT 告诉你搭了什么,RoBERTa 告诉你哪几件其实可以不搭)。
  • 做 RAG / 向量检索的团队:BERT 的双向编码与 [CLS] 聚合是绝大多数 embedding 模型的技术起点,理解它有助于判断何时需要换成 causal decoder 方案。
  • 要复现或微调老模型的老业务:2018-2021 年大量业务模型底座是 BERT,理解其输入格式(token/segment/position 三 embedding 相加、512 长度限制、30K WordPiece 词表)是迁移和排障的基础。
  • 做预训练复现或消融的研究者:附录 C.1/C.2 的设计(步数扫描、掩码策略网格)是可直接复用的实验骨架。
  • 不建议:想找「当前 SOTA 架构」的读者——BERT 的架构已被大量后续工作超越,它的价值在于范式与历史定位,而非当下竞争力。

证据边界与待核项

  • 本篇数字全部取自 arXiv:1810.04805v2 的 abstract 页与 HTML 全文(fetch 时间 2026-11-04)。GLUE 各任务数字来自 Table 1,掩码消融来自 Table 8,步数消融来自附录 C.1 与 Figure 5 的文字描述。
  • 待核:Figure 5 的具体曲线读数(MNLI Dev accuracy 随 k 的完整数值)需回查 PDF 中的图,HTML 渲染版只保留了坐标轴刻度与文字结论,本文未逐点抄录。
  • 待核:论文第 4 节各任务的详细结果表(SQuAD、MultiNLI、CoLA 等分任务对比、including the "Ablation" 表)在 HTML 版中部分以图/表混合形式呈现,本文只引用了摘要与 Table 1 的 verbatim 数字。
  • 本篇未包含:BERT 论文正文第 5.3 节对 CoLA 的详细分析(原文结论是 CoLA 是唯一一个 BERT_LARGE 仍低于既有系统的任务,且认为原因在于训练数据量小、训练步数不足——这一条已由 RoBERTa 的复现从「训练不足」角度重新解释)。
  • GitHub:BERT 原文使用的仓库为 google-research/bert;本篇未执行 fetch 验证其当前状态,仅作历史指认。RoBERTa 的 fairseq 仓库(github.com/pytorch/fairseq)已在对应篇目中记录。