查看全课大纲(第 53 / 69 节)
  1. 1.NLP 的研究领域、应用与学习路径
  2. 2.自然语言的基本术语
  3. 3.信息论基础:信息熵与交叉熵
  4. 4.语言的进化与语义变迁
  5. 5.主观概率、客观概率与条件概率
  6. 6.贝叶斯原理与推理
  7. 7.随机变量:二项式、期望方差与联合概率
  8. 8.常见概率分布
  9. 9.辛普森悖论与贝叶斯解题实例
  10. 10.语言模型:语言概率与词袋模型
  11. 11.n-gram 语言模型(二元 / 三元 / N-Gram)
  12. 12.语言模型评价:交叉熵、困惑度与插值
  13. 13.朴素贝叶斯文本分类:垃圾邮件
  14. 14.拼写检查
  15. 15.中文分词:构词法、最大匹配、最优路径与 Jieba
  16. 16.词表示问题与语义相似度
  17. 17.TF-IDF 权重处理
  18. 18.One-Hot 表达与词表示的局限
  19. 19.Word2Vec 核心思想
  20. 20.Word2Vec 训练过程
  21. 21.Skip-gram 模型详解
  22. 22.词性标注任务与标注方法(规则 + N-Gram)
  23. 23.EM 算法与混合模型
  24. 24.高斯混合模型 GMM
  25. 25.从混合模型到隐马尔可夫模型
  26. 26.隐马尔可夫模型(HMM)推导与维特比解码
  27. 27.线性链条件随机场 CRF
  28. 28.贝叶斯网络与图模型
  29. 29.隐马尔可夫链与 HMM 推导
  30. 30.PLSA 主题模型
  31. 31.LSI 与 SVD 向量表达
  32. 32.LDA 与狄利克雷分布
  33. 33.主题模型实验复现(困惑度 / 分类 / 相似度 / 延展)
  34. 34.上下文无关文法(CFG)与句法分析
  35. 35.概率上下文无关文法 PCFG
  36. 36.统计机器翻译:词对齐与翻译模型
  37. 37.统计机器翻译中的对齐模型与参数估计
  38. 38.从统计机器翻译到神经机器翻译
  39. 39.神经元与反向传播速览(指向深度学习课)
  40. 40.CNN 原理及其在 NLP 中的应用
  41. 41.循环神经网络的基本原理与 Elman / Jordan 网络
  42. 42.LSTM:长短期记忆网络的核心思想与分步实现
  43. 43.Encoder-Decoder 框架
  44. 44.Seq2Seq 与早期注意力机制
  45. 45.注意力机制的背景与动机
  46. 46.注意力模型实现原理与匹配函数
  47. 47.记忆网络:组成与计算
  48. 48.注意力机制的应用与历史延展
  49. 49.Transformer 结构:Encoder-Decoder 栈与 Self-Attention
  50. 50.Multi-Head Attention、FFNN 与位置编码
  51. 51.层归一化、损失函数与 Universal Transformer 延展
  52. 52.预训练范式:从 ELMo 到 BERT
  53. 53.BERT 与掩码语言模型
  54. 54.BERT 下游任务微调与 GLUE 口径
  55. 55.GPT 与自回归语言建模
  56. 56.GPT-1 到 GPT-4 的演进
  57. 57.指令微调与 RLHF
  58. 58.分词器:BPE、WordPiece 与中文词表
  59. 59.上下文窗口与长文本处理
  60. 60.能力边界、成本与适用场景
  61. 61.文本分类实战:从朴素贝叶斯到逻辑回归
  62. 62.命名实体识别实战
  63. 63.文本生成与摘要实战
  64. 64.语义检索与向量库实战
  65. 65.RAG 知识库问答
  66. 66.NLP 评估指标:BLEU / ROUGE / BERTScore 与人工评估
  67. 67.幻觉与事实性问题
  68. 68.文本与知识结合(知识图谱压缩)
  69. 69.跨媒体信息检索与未来展望

BERT 与掩码语言模型

小象实战讲义 · 自然语言处理

在前面的章节里,我们已经掌握了词向量、RNN/LSTM、注意力机制与 Transformer 的基本结构。本节进入预训练模型这一主题,讨论一个具有代表性的 encoder 类模型——BERT。它要解决的核心问题是:如何从无标注文本中预训练出深层双向的上下文表示,使得下游任务只需要在预训练参数上加一个输出层做微调,就能迁移到分类、自然语言推断、问答等多种任务。本节先假定读者已掌握 Transformer 的自注意力结构,重点讲 BERT 的预训练机制与下游微调的基本口径;更细的微调实践与现代预训练模型的演进留到后续章节。

💡 核心导读

  • 问题定位:静态词向量一词一向量,无法表达多义词在不同上下文中的含义;此前的上下文表示要么只拼接左右两个方向的 LSTM 特征,要么只能从左到右建模。
  • 核心思路:BERT 以多层双向 Transformer encoder 为骨架,在所有层同时结合左右两侧上下文,联合预训练深度表示。
  • 两个预训练任务:掩码语言模型(MLM)随机掩盖部分词元并预测原词;下一句预测(NSP)判断两句是否为真实相邻关系。
  • 两种规格:论文给出 BERT-base 与 BERT-large 两档,层数、隐层维度、注意力头数与参数量均可对照论文表格复核。
  • 迁移方式:预训练完成后,下游任务用预训练参数初始化,仅新增任务相关的输出层联合微调。

从上下文表示说起

静态词向量的局限

早期的词向量(如 Word2Vec)为每个词在词典中分配一个固定向量,同一个词在不同句子里共用同一个表示。这无法区分多义词:一个词的含义往往要由它两侧的上下文共同决定。

在 BERT 之前,已经出现过两类上下文表示方案:

  • ELMo:分别训练从左到右和从右到左的两层 LSTM,再把两个方向的隐层特征拼接起来供下游使用,属于特征拼接式方法。
  • OpenAI GPT:采用从左到右的 Transformer,每个词元只能注意其左侧上下文,适合生成式任务。

BERT 的不同之处在于:它用双向 Transformer encoder,在所有层同时结合左右两侧上下文来建模表示。这一架构选择基于 Vaswani et al. 2017 提出的 Transformer 结构(Attention Is All You Need,arXiv:1706.03762)。架构定位与三者对比,可对照 BERT 论文第 3 节的架构示意图(Devlin et al., 2018,arXiv:1810.04805,§3 Model Architecture)。

BERT 在流程中的两步

BERT 的框架分为两步:

  1. 预训练(Pre-training):在无标注语料上按预训练任务训练,得到通用的上下文表示与参数。
  2. 微调(Fine-tuning):用预训练参数初始化模型,再在下游任务的标注数据上联合微调全部参数;每个下游任务对应一个单独的微调模型。

预训练架构与下游架构差异很小,这也是 BERT 能较方便迁移到多种任务的原因之一(Devlin et al., 2018,arXiv:1810.04805,§3 BERT)。

模型架构与两种规格

记号与规模档位

BERT 是多层双向 Transformer encoder。论文用 L 表示 Transformer 块层数,H 表示隐层维度,A 表示自注意力头数;前馈网络维度统一取 4H。论文主要报告两种规模(Devlin et al., 2018,arXiv:1810.04805,§3 Model Architecture):

规格层数 L隐层 H注意力头 A前馈维度 4H总参数量
BERT-base12768123072110M
BERT-large241024164096340M

论文说明,base 档刻意取与 OpenAI GPT 相近的体量以便对照,二者架构上最关键的差异是注意力掩码方向:BERT 用双向自注意力,GPT 用只能看左侧的受限自注意力。

输入表示

BERT 的输入需要同时表示单句和句对。它使用 WordPiece 分词,词表规模为 30,000 token。每个词元的输入嵌入由三部分相加构成:词元嵌入、句段嵌入(区分句 A 与句 B)、位置嵌入(Devlin et al., 2018,arXiv:1810.04805,§3 Input/Output Representations)。

序列首词元固定为特殊分类符 [CLS],其最终隐层向量用作整句的聚合表示;句对用特殊分隔符 [SEP] 隔开,并为每个词元标注属于句 A 还是句 B。序列长度不超过 512 个 token。

# BERT 输入表示 = 词元嵌入 + 句段嵌入 + 位置嵌入(固定小向量演示求和结构,真实嵌入由预训练得到)
import numpy as np

token_emb = np.array([0.21, -0.35, 0.10])
segment_emb = np.array([0.05, 0.02, -0.03])
position_emb = np.array([-0.08, 0.11, 0.06])

input_emb = token_emb + segment_emb + position_emb
print(f"输入嵌入: {input_emb.round(4)}")
输入嵌入: [ 0.18 -0.22  0.13]

两个预训练任务

掩码语言模型(MLM)

双向建模如果直接让模型预测每个词,模型可以间接地看到自己要预测的词。MLM(Masked Language Model)的做法是:在每个序列中随机掩码 15% 的 WordPiece token,让模型根据左右上下文预测被掩盖位置的原词。

被选中的位置按三种方式处理(Devlin et al., 2018,arXiv:1810.04805,§3.1 Pre-training BERT):

  • 80%:替换为 [MASK];
  • 10%:替换为一个随机词元;
  • 10%:保持原词不变。

这样做是为了缓解 [MASK] 这个符号在微调阶段不会出现的问题:保留一部分原词、替换一部分随机词,迫使模型对每个位置都保持上下文预测能力,而不是只依赖掩码符。

# MLM 掩码策略演示(固定小词典与固定选中位置;训练时 15% 抽取与替换由随机过程完成)
tokens = ["the", "man", "went", "to", "store", "and", "bought", "milk", "yesterday", "morning"]
# 预先选定被处理的位置(演示用固定下标;真实训练按 15% 随机抽取)
selected = {1, 4, 7}
# 三个分支的固定落点:80% -> [MASK];10% -> 随机替换词(此处手工给定一个替换词);10% -> 保持原词
replace = {1: "[MASK]", 4: "apple", 7: None}

masked = []
for i, tok in enumerate(tokens):
    if i not in selected:
        masked.append(tok)
    elif replace[i] is None:
        masked.append(tok)
    else:
        masked.append(replace[i])

print("原序列:", " ".join(tokens))
print("掩码后:", " ".join(masked))
print("被选位置:", sorted(selected))
原序列: the man went to store and bought milk yesterday morning
掩码后: the [MASK] went to apple and bought milk yesterday morning
被选位置: [1, 4, 7]

下一句预测(NSP)

仅靠 MLM 学习词级表示,还不足以建模句子之间的关系。NSP(Next Sentence Prediction)是一个二分类任务:构造句对时,50% 的情况下句 B 是句 A 的真实下一句(IsNext),50% 的情况下句 B 是语料中的随机句(NotNext)。模型借助 [CLS] 向量判断两句是否相邻,从而学习句子级的连贯关系(Devlin et al., 2018,arXiv:1810.04805,§3.1 Pre-training BERT 与 §A.2 Pre-training Procedure)。

预训练过程与下游微调

预训练口径

论文预训练语料为 BooksCorpus(8 亿词)与英文维基百科,合计约 33 亿词。训练 batch 为 256 个序列,每序列 512 token,即每个 batch 约 128,000 个 token;训练 1,000,000 步,约相当于在该语料上 40 个 epoch。优化器为 Adam,学习率 1e-4,β1=0.9、β2=0.999,L2 权重衰减 0.01,前 10,000 步学习率 warmup 后线性衰减;各层 dropout 取 0.1,激活函数为 GELU(Devlin et al., 2018,arXiv:1810.04805,§A.2 Pre-training Procedure)。

为节省算力,前 90% 的训练步使用序列长度 128,后 10% 的步使用长度 512,以学习位置嵌入。

下游微调口径

微调时用预训练参数初始化,再在标注数据上联合微调。对于分类类任务,取 [CLS] 对应的最终隐层向量 C∈RHC \in \mathbb{R}^{H},与分类权重 W∈RK×HW \in \mathbb{R}^{K \times H} 计算 softmax 交叉熵,新增的参数主要就是这一层分类权重。对于问答类任务,把问题与段落打包成一个序列,仅新增起点向量 SS 与终点向量 EE,用每个词元表示与二者的点积经 softmax 选出答案跨度(Devlin et al., 2018,arXiv:1810.04805,§3.2 Fine-tuning BERT 与 §4.1 GLUE)。

论文中 BERT-large 报告的结果为 2018 年当时的数值,仅作历史参照,不代表当前榜单,也不代表 base 档成绩:GLUE 80.5%、MultiNLI 86.7%、SQuAD v1.1 Test F1 93.2、SQuAD v2.0 Test F1 83.1(均为 large 档;Devlin et al., 2018,arXiv:1810.04805,摘要与 §4 各任务表)。

今天怎么做

BERT 之后,encoder 类预训练模型继续演进:例如 RoBERTa 去掉 NSP 任务、使用更大规模数据与更长训练;DeBERTa 改进注意力中内容与位置的解耦表示。再往后,线上主干逐步转向更大规模的预训练模型与 decoder 架构。本节只做历史定位与机制讲解,这些后续模型的细节不在本节展开。

延伸阅读

  1. Devlin, J., Chang, M.-W., Lee, K., Toutanova, K. (2018). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. arXiv:1810.04805(NAACL 2019)。架构规格见 §3,输入表示见 §3 Input/Output Representations,MLM/NSP 见 §3.1,预训练超参见 §A.2,下游结果见 §4。
  2. Vaswani, A. et al. (2017). Attention Is All You Need. arXiv:1706.03762。BERT encoder 骨架所基于的 Transformer 原始结构。

📝 动手练一练

  1. 概念辨析:为什么 MLM 不把被选中位置全部替换成 [MASK],而要保留 10% 原词、替换 10% 随机词?这与微调阶段 [MASK] 不再出现有什么关系?
👉 点击查看参考答案

如果被选中位置全部替换为 [MASK],模型在预训练阶段学到的是「看到 [MASK] 就预测原词」的专门模式;但微调阶段的真实句子里并不存在 [MASK] 符号,模型在分布上会不适应。保留 10% 原词,迫使模型即使位置词没有被替换也要结合上下文判断其表示;替换 10% 随机词,则迫使模型不能只依赖当前词元本身,而要依靠上下文纠正错误词元。三者混合让预训练表示更贴近微调时的真实输入分布。

  1. 对照理解:BERT-base(L=12, H=768, A=12)与 BERT-large(L=24, H=1024, A=16)在层数、隐层维度、头数和参数量上有何差别?前馈维度 4H 分别是多少?
👉 点击查看参考答案
  • base:12 层,隐层 768,12 个头,前馈维度 4×768=3072,总参数 110M。
  • large:24 层,隐层 1024,16 个头,前馈维度 4×1024=4096,总参数 340M。

large 在深度、宽度和头数上都大于 base,论文在多个下游任务上观察到 large 稳定优于 base;实际选用时要结合任务数据规模与算力预算权衡。

本章小结

本节围绕 BERT 与掩码语言模型展开。BERT 以双向 Transformer encoder 为骨架,通过 MLM 与 NSP 两个任务在无标注语料上预训练深层双向上下文表示;输入由词元、句段、位置三种嵌入相加构成,词表为 30,000 WordPiece。论文给出 base 与 large 两档规格,下游任务用预训练参数初始化后只加一个输出层联合微调。需要强调,本节引用的成绩均为论文 2018 年报告值,用于理解方法由来,不能当作当前主流水平;更细的微调实践与现代预训练模型的演进留到后续章节。

📋 行动清单

  • 说清 BERT 相比静态词向量、ELMo、GPT 在上下文建模方向上的差异。
  • 复述 MLM 的 15% 掩码比例与 80/10/10 三种替换分支,以及 NSP 的二分类构造。
  • 对照论文表格记住 base/large 的层数、隐层维度、头数、前馈维度与参数量。
  • 理解下游微调只新增任务输出层、其余参数联合微调的基本口径。

—— 小象教研组