一句话总结
BERT = Transformer Encoder + 两个巧妙的预训练任务(遮词填空 + 句对判断).
先在海量文本上预训练出"通用语言理解能力",再用少量标注数据微调到具体任务--这个范式让 NLP 从"每个任务从零训练"跨入了"站在巨人肩膀上"的时代.
前置回顾
上一篇我们理解了 Transformer 的核心机制:Self-Attention 让每个词直接看到所有其他词,多头注意力捕获多种关系,位置编码保留词序. BERT 就是取了 Transformer 的 Encoder 部分,加上独特的预训练策略,打造出了一个"通用语言理解引擎".
预训练 + 微调:范式转变
在 BERT 之前,NLP 任务的典型流程是:
|
关键转变:语言的通用知识(语法,语义,常识)只需要学一次. 具体任务只需要在预训练好的基础上做"微调"--调整最后几层的参数,让模型适配特定任务.
预训练 = 大学四年通识教育. 学了数学,英语,计算机基础,不针对任何具体岗位.
微调 = 入职后的岗前培训. 用几周时间学习公司的业务系统和代码规范. 四年通识 + 几周培训 = 上手快,效果好.
相比之下,旧范式就像不上大学,每换一个岗位都从零学起.
BERT 的架构:只用 Encoder
BERT 的全称是 Bidirectional Encoder Representations from Transformers. 关键词是 Bidirectional(双向):
|
架构参数:
| 参数 | BERT-base | BERT-large |
|---|---|---|
| Transformer 层数 | 12 | 24 |
| 隐藏维度 | 768 | 1024 |
| 注意力头数 | 12 | 16 |
| 参数量 | 1.1 亿 | 3.4 亿 |
| 最大序列长度 | 512 | 512 |
对群聊管理场景,BERT-base 就够了. 消息短(通常 < 50 字),任务不复杂(分类),BERT-base 的 1.1 亿参数绰绰有余.
预训练任务一:MLM(遮词填空)
Masked Language Model(MLM)是 BERT 最核心的预训练任务. 思路极简:随机遮住输入中 15% 的词,让模型猜被遮住的是什么.
|
15% 的遮盖中,又做了细分处理(防止模型只学会处理 [MASK] 标记):
| 操作 | 比例 | 目的 |
|---|---|---|
| 替换为 [MASK] | 80% | 主要的训练信号 |
| 替换为随机词 | 10% | 让模型学会"发现异常词" |
| 保持不变 | 10% | 让模型学会"确认正确的词" |
MLM 的聪明之处
MLM 不需要任何人工标注. 训练数据就是普通文本--随机遮住一些词就自动生成了"输入-答案"对. 这意味着可以用海量的未标注文本(维基百科,新闻,书籍)进行训练,数据量几乎无限.
BERT 原始论文使用了 33 亿词的英文语料(英文维基 + BooksCorpus). 中文 BERT 使用了类似量级的中文语料.
预训练任务二:NSP(下一句预测)
Next Sentence Prediction(NSP)让模型判断两个句子是否是连续的:
|
NSP 让模型学习句子之间的逻辑关系. 输入格式用 [SEP] 分隔两个句子:
|
NSP 的争议
后续研究(RoBERTa 论文)发现 NSP 任务对下游效果的帮助有限,甚至可能有害. RoBERTa 去掉了 NSP,只保留 MLM,效果反而更好. 不过理解 NSP 有助于理解 BERT 的输入格式(为什么有 [CLS] 和 [SEP]).
BERT 的输入表示
BERT 的输入由三个 Embedding 相加而成:
|
| Embedding | 作用 | 维度 |
|---|---|---|
| Token Embedding | 每个 token 的语义向量 | 词表大小 × 768 |
| Position Embedding | 编码位置信息(第几个词) | 512 × 768 |
| Segment Embedding | 区分第一句 / 第二句(用于 NSP) | 2 × 768 |
对单句分类任务(如对骂检测),所有 token 的 Segment Embedding 都是 s_A. Segment Embedding 主要在句对任务中发挥作用.
中文 BERT 变体
原始 BERT 的中文处理比较粗糙--按单字分词("管理员" → "管""理""员"). 后续的中文优化版本解决了这个问题:
chinese-bert-wwm-ext
Whole Word Masking(WWM):遮盖时以完整词为单位,而非单字.
|
ext 表示使用了更大的训练语料(Extended),效果进一步提升.
chinese-roberta-wwm-ext
在 chinese-bert-wwm-ext 基础上,引入 RoBERTa 的改进:
| 改进点 | BERT | RoBERTa |
|---|---|---|
| NSP 任务 | 有 | 去掉(实验证明无用) |
| 训练数据量 | 标准量 | 更多数据,更多 epoch |
| Mask 策略 | 静态(训练前固定) | 动态(每个 epoch 重新随机) |
| batch size | 256 | 更大(8K),训练更稳定 |
群聊场景推荐
对群聊管理任务,推荐 hfl/chinese-roberta-wwm-ext. 它在中文理解任务上表现最稳定,社区使用广泛,HuggingFace 上可以直接下载. 如果模型体积敏感,可以用 hfl/chinese-roberta-wwm-ext-small(6 层,参数量减半).
为什么不从零训练
一个直觉上的疑问:为什么不拿自己的群聊数据从头训练一个模型?
|
预训练模型已经理解了中文的语法,词义,搭配关系,常见表达模式. 微调只需要教它一件事:"在群聊场景中,什么算对骂,什么算正常". 这就像让一个精通中文的人学一个新的分类标准--比教一个完全不懂中文的人快得多.
从零训练 = 自己写一个 HTTP 框架再开发业务. 能做,但耗时巨大,且未必比成熟框架好.
微调预训练模型 = 用 Gin/Echo 框架写业务逻辑. 框架处理了路由,中间件,序列化等通用问题,你只需要写业务 handler.
微调的工作原理
微调(Fine-tuning)在预训练 BERT 的基础上做两件事:
- 加一个任务头:在 [CLS] 输出上接一个分类层
- 用标注数据训练:整个模型(包括 BERT 本身的参数)都参与训练
|
训练时的学习率策略很关键:
| 层 | 学习率 | 原因 |
|---|---|---|
| 分类头(新加的) | 较大(如 1e-3) | 从随机参数开始,需要大步更新 |
| BERT 高层(第 9-12 层) | 中等(如 2e-5) | 需要适配任务,但不能偏离太远 |
| BERT 低层(第 1-4 层) | 很小或冻结 | 低层编码通用语法知识,不需要大改 |
微调的常见错误
学习率太大(如 1e-3 应用到整个 BERT)会"灾难性遗忘"--预训练学到的语言知识被冲掉,模型退化成在小数据上过拟合的模型. 典型症状:训练集 accuracy 99%,测试集只有 60%.
经验值:BERT 部分的学习率用 2e-5 到 5e-5,这是社区反复验证过的安全区间.
预训练与微调的参数对比
直观看看两个阶段的规模差异:
| 维度 | 预训练 | 微调 |
|---|---|---|
| 数据量 | 33 亿+ 词 | 1000-10000 条 |
| 标注 | 不需要(自监督) | 需要人工标注 |
| 训练时间 | 数天(多卡) | 30 分钟-2 小时(单卡) |
| 学习率 | 1e-4 | 2e-5 |
| 目标 | 通用语言理解 | 特定任务 |
| 谁做 | Google / 哈工大等团队 | 我们(业务方) |
预训练是别人做好的"基础设施",微调才是我们真正需要做的工作.
从 BERT 到下一步
本篇搞清楚了 BERT 的原理和预训练范式. 下一篇进入实战:用 HuggingFace Transformers 库加载预训练的中文 BERT,在群聊数据上完成一次完整的微调训练.
|
快速回顾
- 预训练 + 微调:通用知识学一次,具体任务只需少量数据微调. NLP 从"每次从零开始"变成"站在巨人肩膀上"
- MLM(遮词填空):随机遮住 15% 的词让模型预测,不需要人工标注,可以用海量文本训练
- NSP(下一句预测):判断两句话是否连续,学习句间关系. 后续研究证明可以去掉
- 双向 Encoder:BERT 每个词同时看到左右上下文,适合分类等理解任务
- 中文推荐:
hfl/chinese-roberta-wwm-ext,WWM 按完整词遮盖,RoBERTa 改进训练策略 - 微调学习率:BERT 部分用 2e-5 到 5e-5,太大会"灾难性遗忘"
动手练习
- 加载预训练模型:用 HuggingFace
AutoModel.from_pretrained("hfl/chinese-roberta-wwm-ext")下载模型,打印模型结构,数一下参数量 - MLM 实验:用
pipeline("fill-mask", model="hfl/chinese-roberta-wwm-ext")测试遮词填空. 输入 "管理员[MASK]了发广告的用户",看模型预测的 top-5 候选词是否合理 - 对比中文变体:分别用
bert-base-chinese和chinese-roberta-wwm-ext做相同的 MLM 实验,对比预测结果的质量 - 参数量计算:根据 config.json 中的架构参数(vocab_size, hidden_size, num_layers, intermediate_size),手动计算 Embedding 层 + 12 层 Transformer + 分类头的参数量,和
model.num_parameters()的结果对比验证