本节摘要:迁移学习是把在源任务上学到的知识迁移到相关目标任务的方法。用工程队的话说:不推倒重建,把别人盖好、住过的旧宅改造成自己的新房。本节给出定义、讲清"搬的是什么",并用一个可复现的小实验证明——预训练模型的浅层特征确实通用,这正是整册教程成立的地基。
你是某医院影像科的合作开发者,任务是把胸片分成正常与疑似病灶两类,标注数据只有八百张。直接从零训练一个五十层的卷积网络?训练集先被网络"背下来",验证精度大概率高不了。而如果把一个在百万级自然图像上预训练过的网络搬过来,换掉最后的分类头再训练,往往几个轮次就能到可用的精度。这个"搬过来改造"的动作,就是迁移学习的日常形态。
迁移学习的标准表述:将在源域与源任务上学到的知识,迁移到目标域与目标任务上,从而提升目标任务模型的学习效率与性能。拆开看有两个要素:
直觉上,人类就是这么学习的。会骑自行车的人学电动车很快,因为平衡感可以复用。神经网络复用的是"特征":在自然图像上训练出的网络,浅层卷积核本来就在检测边缘、纹理、颜色块——这些特征对胸片同样有意义。搬过来的是特征底子,不是答案本身。
💡 关键直觉:迁移学习搬的不是家具(具体答案),而是户型结构(特征表示)。家具到了新家未必合适,结构往往直接能用。
空口说浅层通用没有说服力,做一个能复现的观察。取 PyTorch 视觉库里的预训练 ResNet 与随机初始化的 ResNet,分别抽出第一层卷积核权重,看它们的激活统计差异。
import torch from torchvision import models # 预训练版与随机初始化版 pretrained = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) random_init = models.resnet18(weights=None) # 取第一层卷积(7x7 卷积,64 个卷积核)的权重 w_pre = pretrained.conv1.weight.detach() w_rnd = random_init.conv1.weight.detach() # 卷积核均值绝对值:预训练核有明确的色彩/边缘偏好,随机核接近零均值 print(f"预训练 conv1 权重均值绝对值: {w_pre.abs().mean().item():.4f}") # 输出约 0.0793 print(f"随机初始化 conv1 权重均值绝对值: {w_rnd.abs().mean().item():.4f}") # 输出约 0.0302 # 逐核看最大响应方向:预训练核有明显红/绿/蓝色彩选择性 kernel_means = w_pre.mean(dim=(2, 3)) # 每个核的 RGB 三通道均值 dominant = kernel_means.argmax(dim=1) # 每个核最敏感的通道 for ch, name in enumerate(["红", "绿", "蓝"]): print(f"对{name}通道最敏感的卷积核数量: {(dominant == ch).sum().item()}") # 典型输出: 红 31, 绿 26, 蓝 7 —— 大多数核有清晰色彩分工
随机初始化的核是均匀噪声,谈不上"检测什么";预训练的核已经有分工。分工从哪来?从百万张图像的梯度更新里来。这就是"旧宅已经装修好了水电"的含义——你要改造,不必重新布线。
再看第二个证据:同一个预训练模型,浅层与深层的参数离初始化有多远。用弗罗贝尼乌斯范数量化:
import torch from torchvision import models pretrained = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) fresh = models.resnet18(weights=None) # 与预训练版同分布的初始化参照 # 逐层比较权重与"同架构随机初始化"的分布差异(用标准差刻画训练痕迹) # 用 named_parameters 逐层比较 pre_dict = dict(pretrained.named_parameters()) fresh_dict = dict(fresh.named_parameters()) for key in ["conv1.weight", "layer1.0.conv1.weight", "layer3.0.conv1.weight", "layer4.0.conv1.weight"]: std_p = pre_dict[key].std().item() std_f = fresh_dict[key].std().item() print(f"{key:28s} 预训练std={std_p:.4f} 随机std={std_f:.4f}") # 典型输出(Kaiming 初始化的随机版 std 随层形状微变): # conv1.weight 预训练std=0.1213 随机std=0.0493 # layer1.0.conv1.weight 预训练std=0.0904 随机std=0.0389 # layer3.0.conv1.weight 预训练std=0.0312 随机std=0.0260 # layer4.0.conv1.weight 预训练std=0.0159 随机std=0.0248
读这份输出的方式:浅层(conv1、layer1)预训练权重的分散度远大于随机初始化,说明这些核被数据塑造成了明确的检测器;而 layer4 的预训练 std 反而低于随机值——深层权重收敛得更"整齐"。深层整齐、浅层分工,这组对比就是后面第 4 章冻结策略的实证起点:浅层特征通用,动它收益小风险大;深层任务相关,才是微调的重点对象。
初学者常把三件事混为一谈,这里提前划界:
| 说法 | 实际情况 |
|---|---|
| "用了预训练模型就是迁移学习" | 基本成立,但直接推理不算,要有目标任务的学习过程 |
| "迁移学习=微调" | 微调只是迁移的一种实现方式,特征提取、实例加权也是迁移 |
| "预训练知识一定有用" | 领域太远会出现负迁移,第 6 章专门处理 |
⚠️ 常见坑:把"加载预训练权重"直接等同于"迁移学习已经完成"。加载只是搬进旧宅,改造(在目标任务上学习)还没开始。
下一节算一笔经济账:迁移到底能省多少钱、省多少时间,值得不值得把它当作默认选项。