深度学习进阶:预训练权重到底是个啥?看完这篇你就懂了(上篇)
很多初学者好奇:为什么别人用几十张照片就能训练一个不错的分类模型,而我从零开始却要上万张?秘密就在“预训练权重”里。本文将用最直白的方式,带你彻底搞懂它的定义、优势、适用场景,以及三种经典的微调策略。
你是否有这些困惑?
听人说“用 ImageNet 预训练模型微调一下,小数据集也能出好结果”,但到底什么是“预训练”?
为什么加载一个别人训练好的权重,自己只加一个分类头,效果就能那么好?
自己的数据集不大,但又想用深度学习模型,应该冻结哪些层?解冻哪些层?
别急,这篇文章就是为你准备的。我们会从零讲起,不涉及复杂的代码实现(那些放在下一篇实战中),但会帮你建立完整的概念框架,让你以后看任何迁移学习代码都能心中有数
什么是预训练权重?
一句话定义:预训练权重就是别人在大规模数据集(比如 ImageNet 上的 120 万张图片)上训练好的神经网络参数,包括每一层的权重weight和偏置bias。
这些参数已经“学会”了非常通用的视觉特征:
第一层能识别边缘、颜色、简单纹理
中间层能识别形状、物体部件(眼睛、轮子、羽毛)
深层能识别完整物体(猫、狗、汽车、飞机……)
你可以把这些权重作为你模型的初始参数,然后在自己的小数据集上继续训练(这叫微调)。相当于你站在了巨人的肩膀上,不需要重新从零学习那些基础特征。
为什么预训练权重这么香?
| 优势 | 具体说明 |
|---|---|
| 节省训练时间 | 从零训练 ResNet-50 需要几天甚至几周,微调只需几小时。 |
| 降低数据需求 | 没有预训练时,每类可能需要上千张图片;有预训练后,每类几十到几百张就能见效。 |
| 提升模型精度 | 小数据集上,使用预训练权重的模型准确率通常远高于随机初始化。 |
| 减轻过拟合 | 预训练的特征提取器已经高度泛化,数据少也不容易“死记硬背”。 |
| 降低硬件门槛 | 不再需要昂贵的多卡集群,一块普通 GPU 甚至 CPU 就能完成微调。 |
| 便于迁移学习 | 同一个预训练模型可以快速适配分类、检测、分割、OCR 等多种任务。 |
为什么预训练权重有效?
深度学习模型有一个重要特点:底层学通用特征,高层学者任务特定特征。
| 层级 | 学到的内容 | 通用性 |
|---|---|---|
| 浅层(前几层) | 边缘、颜色、简单纹理 | 几乎任何图像任务都通用 |
| 中层(中间几层) | 形状、物体部件(眼睛、轮子) | 在很多任务中可复用 |
| 深层(最后几层) | 完整物体类别(猫、狗、飞机) | 任务相关,需要替换 |
ImageNet 预训练模型已经完美学到了深浅层的通用特征。当我们去做“识别 5 种花”的任务时,只需要保留浅中层,替换掉最后的分类头(1000 类 → 5 类),再稍微调整一下高层特征即可。这就是知识迁移。
什么时候该用预训练权重?
| 场景 | 说明 |
|---|---|
| 小数据集** | 比如你自己只收集了 5 类花,每类 50 张照片。从头训练会严重过拟合,但使用预训练模型微调最后一层就能得到不错的效果。 |
| 任务相似 | 你的任务与预训练任务同属视觉领域(自然图像、医疗图像、遥感图像),底层特征可以复用。即使有一定差异(如自然图像 → X 光片),预训练仍有帮助。 |
| 快速原型开发 | 想快速验证一个新想法(如新损失函数、新数据增强),无需等待模型从零收敛。 |
| 竞赛或学术基准 | 绝大多数学术论文和 Kaggle 竞赛都以预训练模型为起点,追求更高精度。 |
| 资源受限环境 | 你无法承担长时间大规模训练,但又想得到一个不错的模型。 |
三种微调策略:你应该冻结哪些层?
加载预训练权重后,你需要决定哪些层的参数继续更新(可训练),哪些保持不变(冻结)。这取决于你拥有多少标注数据。
| 策略 | 冻结部分 | 可训练部分 | 需要的数据量 | 适用场景 |
|---|---|---|---|---|
| ① 特征提取器 | 所有预训练层 | 仅新分类头 | < 100 /类 | 极小数据集、快速验证 |
| ② 解冻高层 | 冻结底层,解冻最后 1~2 个 block | 顶层 + 新分类头 | 200~1000 /类 | 中等数据集,兼顾泛化与适配 |
| ③ 全模型微调 | 不冻结任何层 | 全部参数 | > 1000 /类 | 大数据集,追求最佳精度 |
冻结的层数越多,训练速度越快,占用的梯度内存也越小。
5.1 策略① 特征提取器(只训练新分类头)
代码示意:
# 先冻结所有预训练层 for param in model.parameters(): param.requires_grad = False # 替换分类头(新层默认 requires_grad=True) model.fc = nn.Linear(2048, 10) # 优化器只优化新分类头 optimizer = optim.Adam(model.fc.parameters(), lr=0.001)
5.2 策略② 解冻高层(例如解冻 ResNet 的最后一个残差块 layer4)
# 全部冻结 for param in model.parameters(): param.requires_grad = False # 解冻最后的高层 for param in model.layer4.parameters(): param.requires_grad = True # 替换分类头(默认可训练) model.fc = nn.Linear(2048, 10) # 优化器包含所有 requires_grad=True 的参数 optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-4)
5.3 策略③ 全模型微调
# 所有参数默认 requires_grad=True,无需冻结 model.fc = nn.Linear(2048, 10) # 使用较小的学习率(防止破坏预训练知识) optimizer = optim.Adam(model.parameters(), lr=0.001)
附resnet18模型图
现在你已经理解了预训练权重的灵魂——是什么、为什么好、该怎么用。但你可能还有一个疑问:这些权重到底怎么加载?如果模型结构改了怎么办?下篇预告:实战!
