Pytorch深入浅出(十三)之模型微调

随着深度学习的发展,模型规模越来越大。当前主流的 CNN / Transformer 模型,往往拥有千万甚至上亿级参数 ,并且通常是在 ImageNet-1K / 11K / 21K 等超大规模数据集上训练得到的。

然而,在真实业务场景中,我们往往只有几千到几万样本。如果在这种数据规模下,从零开始训练一个大型模型,几乎必然会遇到以下问题:

  • 模型参数量巨大 → 严重过拟合
  • 收敛缓慢甚至无法收敛
  • 训练成本高、效果不稳定
    这就引出了一个核心思想:
    能否复用别人已经学到的"知识",而不是从零开始?

一、为什么需要模型微调(Fine-tuning)?

假设一个实际需求: 从图像中识别不同种类的椅子,并向用户推荐购买链接

一种"理想但不现实"的方案是:

  • 为 100 种椅子
  • 每种拍摄 1000 张图片
  • 构建一个 10 万规模的数据集
  • 从零训练一个深度网络

但现实是:

  • 数据采集和标注成本极高
  • 数据规模仍远小于 ImageNet
  • 复杂模型极易过拟合

1.数据不够,是深度学习的常态

ImageNet的构建耗费了数百万美元和多年人力成本

在大多数应用场景下,数据不足是默认前提,而不是例外

2.知识是可以迁移的

以 CNN 为例:

  • 底层卷积核:学习边缘、角点、纹理
  • 中层:学习形状、局部结构
  • 高层:学习语义组合(物体部件、整体)

在 ImageNet 上训练得到的模型,虽然分类目标不同,但其低层与中层特征具有极强的通用性

👉 模型参数(权重)本质上就是模型学到的"知识"

👉 迁移学习 = 把这些知识迁移到新任务中

二、Model Finetune:模型的迁移学习

模型微调(Fine-tuning)是迁移学习最常见、也最实用的一种形式。
核心思想 非常简单:

不从零训练模型,而是在一个"已经学会通用特征"的模型基础上,针对新任务进行再训练。

在 PyTorch 中,我们通常直接使用官方提供的预训练模型

三、模型微调的流程

step1:加载源模型(预训练模型)

  • 源模型是在大规模数据集(如 ImageNet)上训练好的模型

step2:构建目标模型

  • 保留源模型的特征提取部分
  • 移除或替换原来的输出层

step3:添加新的输出层

  • 输出维度 = 目标数据集类别数
  • 参数随机初始化

step4:在目标数据集上训练

  • 在目标数据集上训练目标模型
  • 从头训练输出层,而其余层的参数都是基于源模型的参数冻结/微调得到的

三、模型微调训练方法

在 PyTorch 中,模型微调本质上就是 "如何加载预训练模型 + 如何控制哪些参数参与训练"。根据是否更新特征提取层(backbone),微调方式大致可以分为两类:

  • 仅训练新加入的分类层(特征提取)
  • 在预训练权重基础上整体微调(Fine-tuning)

1.使用已有模型结构

PyTorch 的 torchvision.models 提供了大量在 ImageNet 等大型数据集上训练好的模型结构与权重,常用于迁移学习任务。

python 复制代码
import torchvision.models as models

resnet18 = models.resnet18(pretrained=True)
alexnet = models.alexnet(pretrained=True)
vgg16 = models.vgg16(pretrained=True)
squeezenet = models.squeezenet1_0(pretrained=True)
densenet = models.densenet161(pretrained=True)
mobilenet_v2 = models.mobilenet_v2(pretrained=True)

传递pretrained参数

  • pretrained=True:加载官方提供的预训练权重(微调最常用
  • pretrained=False:仅加载网络结构,参数随机初始化(等价于从头训练)

⚠️ 注意事项:

  • 权重文件首次加载时会自动下载
  • 默认缓存路径:
    • Linux / macOS:~/.cache/torch/hub/checkpoints
    • Windows:C:\Users\<username>\.cache\torch\hub\checkpoints
  • 下载完成后,后续加载不会重复下载
  • 一般情况下预训练模型的下载会比较慢,我们也可以手动下载权重并加载:
python 复制代码
self.model = models.resnet50(pretrained=False)
self.model.load_state_dict(torch.load('./model/resnet50-19c8e357.pth'))

2.训练特定层

在迁移学习中,最常见、也是最稳妥的做法是:冻结预训练模型的特征提取层,仅训练新加入的分类层

① 冻结参数(关闭梯度计算)

在默认情况下,参数的属性.requires_grad = True,我们需要通过设置requires_grad = False冻结部分层。在PyTorch官方中提供了这样一个例程。

python 复制代码
def set_parameter_requires_grad(model, feature_extracting):
    if feature_extracting:
        for param in model.parameters():
            param.requires_grad = False
② 修改输出层(适配新任务)

ResNet18 为例,将 ImageNet 的 1000 类改为 10 类:

注意我们先冻结模型参数的梯度,再对模型输出部分的全连接层进行修改,这样修改后的全连接层的参数就是可计算梯度的。

python 复制代码
import torchvision.models as models

feature_extract = True
model = models.resnet18(pretrained=True)
# 冻结backbone
set_parameter_requires_grad(model, feature_extract)
# 修改模型
num_features = model.fc.in_features
model.fc = nn.Linear(in_features=num_features, out_features=10, bias=True)

之后在训练过程中,反向传播仍会执行完整计算图,但是参数更新则只会发生在fc层(requires_grad=True的层)。通过设定参数的requires_grad属性,我们完成了指定训练模型的特定层的目标,这对实现模型微调非常重要。

3.添加新的层

除了直接修改分类头外,还可以通过在预训练模型的基础上添加一层来实现微调。这种方式同样可以利用预训练模型的特征提取能力,同时通过新增层来适配新的分类任务。

① 添加新层

使用add_module()方法

以VGG16为例,假设原始模型是1000分类任务,现在需要将其改为10分类任务。可以在原始分类头的基础上添加一层Linear(1000, 10),将1000个输出映射到10个类别上。

python 复制代码
import torchvision.models as models
import torch.nn as nn
# 加载预训练的VGG16模型
vgg16_true = models.vgg16(pretrained=True)
# 冻结backbone(可选)
set_parameter_requires_grad(model, feature_extract=True)
# 添加新层
model.classifier.add_module("linear_layer", nn.Linear(1000, 10))
print(vgg16_true)


将添加的线性层加在classifier中

python 复制代码
import torchvision.models as models
import torch.nn as nn
# 加载预训练的VGG16模型
vgg16_true = models.vgg16(pretrained=True)
# 冻结backbone(可选)
set_parameter_requires_grad(model, feature_extract=True)
# 添加新层在classifier中
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000,10))
print(vgg16_true)
② 训练新层

在训练过程中,新增的linear_layer层的参数会参与更新,而其他层的参数(如Backbone)可以根据是否冻结来决定是否更新。

  • 冻结Backbone :如果冻结了Backbone,那么只有新增的linear_layer层的参数会更新。这种方式可以快速适应新任务,同时避免对预训练模型的特征提取能力进行过多调整。
  • 不冻结Backbone:如果选择不冻结Backbone,那么整个模型的参数都会参与更新。这种方式可能会进一步优化模型的性能,但需要更多的训练资源和时间。

总结

1.预训练模型 = 可迁移的知识

2.微调的核心是:加载权重 + 控制梯度

3.小数据场景下,优先选择:冻结 backbone + 只训练分类层

4.如果数据量较大,可以尝试:不冻结 Backbone,进行整体微调

相关推荐
檐下翻书1731 小时前
法律文书自动生成与逻辑校验
人工智能
Yuner20001 小时前
Python机器学习:从入门到精通
python
de之梦-御风1 小时前
【深度学习】模型从训练完成到产线运行的完整使用方式
人工智能·深度学习
Java后端的Ai之路1 小时前
【人工智能领域】-YOLO目标检测算法全解析(含大白话解释)
人工智能·yolo·目标检测·cnn
百家方案1 小时前
“十五五”智慧城市解决方案:从技术赋能到场景智治,再造城市生命共同体
人工智能·智慧城市
_codemonster1 小时前
深度学习实战(基于pytroch)系列完整目录
人工智能·深度学习
Amelia1111112 小时前
day47
python
RichardLau_Cx2 小时前
针对不同类别AI设计工具的核心使用方法
人工智能
程途拾光1582 小时前
制造业中的预测性维护与异常检测
人工智能
薛晓刚2 小时前
AI编程:爽感背后的成本与隐忧
人工智能·ai编程