1.3 Fine-tuning是什么:对旧宅做局部翻新


1.3 Fine-tuning 是什么:对旧宅做局部翻新

本节摘要:Fine-tuning(微调)是在预训练模型参数的基础上,用目标任务数据继续训练、小幅更新权重的工序。关键字在"微":小学习率、有限轮数、常常只动部分层。本节讲清微调与从零训练、与特征提取的差别,用参数更新量的代码演示"微"在哪里,并给出微调的三个标配动作。

行话切入

工程队里说"微调",指的是:旧宅主体保留,把墙面重新粉刷、线路局部改线——房子还是那栋房子,但住起来合你的需求。放到模型上,就是以预训练权重为初始值,在你的目标数据上继续跑梯度下降。对比之下:

  • 从零训练:毛地皮上从地基开始盖,权重随机初始化
  • 特征提取:旧宅完全不动,只在门口接一个新门厅(新分类头),旧墙一毫米都不刮
  • 微调:旧宅主体保留,内部按需求翻新——多数参数会更新,但幅度受控

三者不是互斥的教条,而是同一光谱上的三档力度。第 4 章会把这三档展开成完整的施工模式选择,本节先把"微调"这一档的机理讲透。

"微"体现在哪:三个控制阀

微调最容易误解的地方是以为"继续训练"就等于微调。其实微调的精髓在约束,三个控制阀缺一不可:

阀一:小学习率。 从零训练常用千分之几量级的学习率;微调预训练骨干通常只用它的十分之一甚至更小。原因直白:预训练权重已经在一个好位置上,大步长会把好不容易学到的特征震碎。

import torch import torch.nn as nn # 演示学习率对预训练权重的破坏程度 torch.manual_seed(0) w = torch.ones(1000) # 假想这是已学好的某层权重 g = torch.randn(1000) * 0.1 # 一次典型的梯度 for lr in [1e-1, 1e-2, 1e-3, 1e-4]: w_new = w - lr * g drift = (w_new - w).norm().item() / w.norm().item() print(f"学习率 {lr:.0e}: 单步权重相对漂移 {drift:.4%}") # 输出: # 学习率 1e-01: 单步权重相对漂移 0.3162% # 学习率 1e-02: 单步权重相对漂移 0.0316% # 学习率 1e-03: 单步权重相对漂移 0.0032% # 学习率 1e-04: 单步权重相对漂移 0.0003% # 一次更新看似都小,但一个轮次上千步累积,大学习率的破坏是乘性放大的

阀二:有限轮数加早停。 微调通常几个轮次就收敛,训得越久越过拟合。第 4.6 节的监理工具就是为此准备的。

阀三:限定更新范围。 浅层冻结、深层微调,或者用极小的分层学习率,让"承重墙"几乎不动。

微调与特征提取的分界实验

两档力度的差别用参数量看最直观。以 ResNet-18 换十分类头为例:

import torch.nn as nn from torchvision import models def count_trainable(model): return sum(p.numel() for p in model.parameters() if p.requires_grad) # 力度一:特征提取(冻结骨干,只训新头) m1 = models.resnet18() for p in m1.parameters(): p.requires_grad = False # 整栋旧宅上保护罩 m1.fc = nn.Linear(512, 10) # 新门厅 print(f"特征提取可训练参数: {count_trainable(m1):,}") # 输出: 5,130 # 力度二:微调(骨干可训练 + 新头) m2 = models.resnet18() m2.fc = nn.Linear(512, 10) print(f"全量微调可训练参数: {count_trainable(m2):,}") # 输出: 11,231,130 # 差距约 2190 倍:微调翻新的是整栋楼的墙面,特征提取只装了个新门

读数:微调的可训练参数约一千一百二十三万,特征提取只有五千出头。参数量差三个数量级,但这不代表微调一定更好——数据只有几百张时,大参数量意味着大过拟合风险,此时"少动"反而安全。选择逻辑留给第 4 章,这里先建立手感。

微调的三个标配动作

不管视觉还是语言,一次标准微调流程都由三个动作构成:

  1. 换头:把预训练模型的输出层替换成适配目标任务的层(分类头、回归头或序列标注头)
  2. 续训:以预训练权重为初值,在目标数据上以小学习率训练
  3. 验收:用验证集监控,保存最优检查点,必要时早停
# 三个动作的最小骨架(视觉十分类) import torch import torch.nn as nn from torchvision import models model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) model.fc = nn.Linear(model.fc.in_features, 10) # 动作一:换头 optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4) # 动作二:小学习率续训 criterion = nn.CrossEntropyLoss() # model.train() # for x, y in dataloader: # 动作二:常规训练循环(略) # optimizer.zero_grad() # loss = criterion(model(x), y) # loss.backward(); optimizer.step() best_acc = 0.0 # for epoch in range(num_epochs): # 动作三:每轮验证,存最优(略) # acc = evaluate(model, val_loader) # if acc > best_acc: # best_acc = acc # torch.save(model.state_dict(), "best") print("三个动作:换头、小学习率续训、验证存优。骨架如此,细节在第4章。")

💡 关键直觉:微调不是"再训练一遍",而是"带着保护措施的继续训练"。保护措施(小步长、早停、冻结)才是它与从零训练的本质区别。

常见误解

  • "微调=迁移学习":微调是实现迁移的一种工序,特征提取、领域适应也是迁移
  • "微调一定比重训好":数据充足时未必,预训练偏置可能压低上限
  • "微调就是调超参数":行业黑话里"调参"指超参搜索,"微调"指权重继续训练,两码事

本节要点回顾

  • 定义:微调=以预训练权重为初值、在目标数据上受控地继续训练
  • 三个控制阀:小学习率、有限轮数加早停、限定更新范围——约束才是微调的灵魂
  • 量化手感:全量微调与特征提取的可训练参数差三个数量级(约一千一百二十三万对五千)
  • 三个标配动作:换头、续训、验证存优,视觉与语言通用
  • 与相邻概念的分界:比从零训练多保护,比特征提取多自由度

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U