动手学深度学习——BERT微调
1. 前言
前面我们已经把 BERT 这一条线基本串起来了:
BERT
BERT代码
BERT预训练数据代码
BERT预训练代码
到这里,我们已经知道:
BERT 最强的地方,不只是模型本身,而是“预训练 + 微调”这套范式。
前面的几节主要解决的是:
模型怎么搭
预训练数据怎么构造
预训练时 MLM 和 NSP 怎么联合训练
但真正到了实际应用中,一个更关键的问题是:
预训练好的 BERT,怎么拿去做具体任务?
这就引出了这一节的主题:
BERT 微调(Fine-tuning)
这一节非常重要。
因为它对应的是现代 NLP 最核心的一种使用方式:
先拿大规模语料把 BERT 预训练好,
再在具体任务上加一个很小的任务头,
最后用该任务数据对整个模型做联合训练。
这就是为什么 BERT 能成为一个通用语言理解底座。
2. 什么是微调
“微调”两个字,最简单的理解就是:
在已经预训练好的模型基础上,再用目标任务数据做进一步训练。
注意,这里不是“从零训练一个新模型”,
也不是“只拿预训练模型当固定特征提取器”。
BERT 微调通常做的是:
把预训练好的参数作为初始化,然后在下游任务上继续更新参数。
也就是说:
BERT 主体参数会继续学
新加的任务输出层也会一起学
所以 Fine-tuning 的核心不是“冻结不动”,
而是:
带着预训练知识,整体适配新任务。
3. 为什么 BERT 不直接从零做下游任务
因为从零训练成本高,而且往往效果差。
假设你现在要做一个下游任务,比如:
情感分析
文本分类
自然语言推理
问答
命名实体识别
如果完全从零训练,那么模型必须同时学会:
第一,语言基本规律
例如:
词义
语法
句子结构
上下文关系
第二,任务本身规律
例如:
正负情感怎么区分
两句话是否矛盾
哪个片段是答案
这就很浪费。
而 BERT 的优势就在于:
它已经在海量语料上学会了大量通用语言知识。
所以下游任务只需要在这个基础上“最后再对准目标任务”,
这当然会更高效、更稳。
4. BERT 微调的核心思想是什么
一句话概括:
预训练阶段学通用语言表示,微调阶段学具体任务决策边界。
也就是说:
预训练
解决的是“语言本身”的问题。
微调
解决的是“这个任务要怎么用这些语言表示”的问题。
这就是现代 NLP 很经典的分工方式:
通用能力先学好
任务能力后适配
所以微调并不是在重新学一遍语言,
而是在已有语言理解能力基础上做任务特化。
5. BERT 微调和“特征提取”有什么区别
这个点很容易混。
特征提取(Feature Extraction)
通常是:
预训练模型参数固定不动
只把它输出的表示拿出来
再喂给一个额外分类器
这种方式更像把 BERT 当一个静态工具。
微调(Fine-tuning)
通常是:
BERT 主体参数也参与更新
任务头参数也参与更新
整个模型端到端一起学
所以微调更强的原因在于:
它允许预训练模型根据新任务再做适配。
这通常比只拿固定特征会更有效。
6. BERT 微调通常怎么做
大体流程可以概括成 4 步:
第一步:加载预训练好的 BERT
把已经学好通用表示的参数作为起点。
第二步:在顶层接一个任务相关输出头
例如:
分类层
序列标注层
问答输出层
第三步:喂入下游任务数据
例如分类样本、句对样本、标注样本。
第四步:联合训练
BERT 主体和任务头一起更新。
所以微调的关键就在于:
任务变了,输出头变;
但底层语言表示主体基本保留。
7. 为什么说 BERT 是“通用底座”
因为很多不同 NLP 任务,
都可以共享同一个 BERT 编码器主体,
只是顶层任务头不一样。
例如:
文本分类
取[CLS]表示,接一个全连接分类器。
句对匹配
也是取[CLS]表示,做二分类或多分类。
序列标注
对每个 token 的表示分别做分类。
问答
对每个位置预测“是不是答案起点/终点”。
所以 BERT 的通用性就在于:
底层统一,顶层多变。
这正是“预训练模型”最有价值的地方。
8. BERT 微调时最常见的任务类型有哪些
通常可以分成两大类。
第一类:句级任务
即整段输入对应一个标签。
例如:
情感分类
文本主题分类
自然语言推理
句子相似度判断
这类任务通常会用:
[CLS]位置的输出表示做分类。
第二类:词级任务
即序列中每个位置都要有一个标签。
例如:
命名实体识别
词性标注
分词
这类任务通常会用:
每个 token 对应位置的输出表示
分别做分类。
所以不同任务微调时,
最大的差别通常就在输出头设计。
9. 为什么句级任务通常用[CLS]
因为在 BERT 输入中:
[CLS]被放在最前面,
经过多层 Transformer 编码后,
它的输出表示会融合整段输入信息。
所以它很适合作为:
整句或整段输入的综合表示
因此在句级任务中,常见做法就是:
取
[CLS]对应向量接一个线性层
输出类别概率
这是一种非常简单但非常有效的微调方式。
10. 句级任务微调头一般长什么样
最基础的写法通常很简单:
h_cls = encoded_X[:, 0, :] y_hat = classifier(h_cls)这里:
encoded_X是 BERT 编码器输出encoded_X[:, 0, :]就是[CLS]位置表示classifier通常是一个全连接层
如果是二分类任务,输出维度就是 2;
如果是多分类任务,输出维度就是类别数。
所以你会发现:
BERT 微调时,新增的任务头往往很小。
这也从侧面说明,真正强的是 BERT 主体学到的表示。
11. 词级任务为什么不能只看[CLS]
因为词级任务需要:
对每个 token 单独做判断
例如命名实体识别里:
“张三” 是人名
“北京” 是地名
其他词可能是 O
这时候显然不能只看整句一个[CLS]表示。
而必须拿:
每个 token 对应位置的输出表示
分别做分类。
所以词级任务微调一般是:
Y_hat = classifier(encoded_X)其中对每个位置都输出标签分布。
12. BERT 微调为什么通常比从零训练效果好
原因可以从三个层面理解。
第一,参数初始化更好
BERT 已经不是随机参数,
而是有大量语言知识的参数。
第二,样本效率更高
下游任务哪怕数据不算很多,
也能借助预训练知识取得不错效果。
第三,泛化能力更强
因为模型在大语料中见过各种语言现象,
不容易只记住小数据集里的表面模式。
所以微调的核心优势,本质上就是:
把大规模语料上学到的共性知识迁移到具体任务。
13. 微调时 BERT 主体参数一定全部更新吗
通常是会更新的。
这也是标准 fine-tuning 的定义。
不过实践中也有不同策略:
标准微调
BERT 主体更新
任务头也更新
部分冻结
冻结部分底层层
只更新高层和任务头
只训练任务头
把 BERT 当特征提取器
但如果按照“典型 BERT 微调”来讲,
最常见的默认理解还是:
整个模型联合训练
这样模型能更充分适应新任务。
14. 为什么微调学习率通常要更小
因为预训练好的 BERT 参数已经很有价值。
如果学习率太大,容易把这些学好的知识一下“冲坏”。
所以微调时常见做法是:
学习率比从零训练更小
训练 epoch 不一定很多
更注意稳定优化
这背后的直觉很简单:
微调不是大改造,而是细修正。
所以“fine-tuning”里这个 fine,
其实就有“精细调整”的意思。
15. 为什么微调任务头可以很简单
因为 BERT 主体已经承担了最难的部分:
学高质量上下文化表示
所以在很多任务里,新增输出头只需要做很简单的映射,例如:
一个线性层
或少量 MLP
就足够了。
这和早期很多需要复杂任务特定结构的模型形成鲜明对比。
也就是说,BERT 的强大之处在于:
把复杂度尽量前移到预训练阶段,
让下游任务变得更轻。
16. BERT 微调时输入格式还和预训练一样吗
大体思路类似,但会根据任务稍作调整。
单句任务
通常输入格式是:
[CLS] sentence [SEP]句对任务
通常输入格式是:
[CLS] sentence_A [SEP] sentence_B [SEP]然后继续构造:
token ids
segment ids
valid lengths
所以你会发现:
BERT 预训练和微调在输入组织形式上是相通的。
这也是它迁移方便的一个原因。
17. 为什么自然语言推理特别适合拿来讲 BERT 微调
因为自然语言推理(NLI)本身就是一个非常典型的句对理解任务。
它的输入通常是:
前提句(premise)
假设句(hypothesis)
目标是判断两者关系,例如:
蕴含
矛盾
中立
这和 BERT 的句对输入形式高度契合:
[CLS] premise [SEP] hypothesis [SEP]所以在教学中,NLI 非常适合作为 BERT 微调案例。
这也是为什么你目录后面紧接着就是:
自然语言推理数据集
BERT 微调代码
18. BERT 微调和前面 BERT 预训练的关系怎么理解
这两者一定要串起来看。
预训练
目标是:
在大规模无标注数据上学通用语言表示
微调
目标是:
在小得多的任务标注数据上,把这些表示调到更适合具体任务
所以这不是两个割裂阶段,
而是一个非常完整的 pipeline:
先学“通用能力”
再学“任务能力”
这正是 BERT 影响整个 NLP 范式的核心。
19. 李沐这一节最想让你理解什么
这一节最核心的,不是立刻写很多代码,
而是先建立下面这条主线:
第一,BERT 的真正威力在“预训练 + 微调”
不是只看预训练本身。
第二,不同下游任务的区别主要体现在输出头
而不是都要重新设计底层模型。
第三,句级任务常用[CLS]表示
词级任务则用每个 token 的表示。
第四,微调通常会联合更新 BERT 主体和任务头
这是标准 fine-tuning 思路。
所以这一节其实是在完成一个非常关键的认知转变:
BERT 不是某个固定任务模型,
而是一个可迁移到很多任务的语言底座。
20. 本节总结
这一节我们学习了 BERT 微调,核心内容可以总结为以下几点。
20.1 BERT 微调是在预训练好的模型基础上适配具体下游任务
而不是从零开始训练。
20.2 下游任务通常只需在 BERT 顶部增加一个较小的任务头
这体现了 BERT 作为通用底座的价值。
20.3 句级任务通常使用[CLS]表示
例如文本分类、自然语言推理等。
20.4 词级任务通常使用每个 token 的输出表示
例如命名实体识别、序列标注等。
20.5 微调是“预训练 + 微调”范式中的第二步,也是 BERT 真正落地应用的关键
21. 学习感悟
这一节特别重要,因为它让你真正明白:
预训练模型的价值,不在于“训练完放在那里”,
而在于“它能被迁移到很多具体任务里”。
BERT 之所以成为一个时代转折点,
并不是因为它只在某一个 benchmark 上强,
而是因为它把 NLP 任务统一到了一种更优雅的工作流上:
先学语言
再做任务
这种范式影响一直延续到今天。
