图像识别模型与训练策略:基于 PyTorch 的分类任务
·
💡 问题引入:模型好坏只取决于网络结构吗?
在进行图像分类任务时,很多同学一上来就死磕模型结构,比如研究 ResNet 到底有几层、卷积核怎么卷。但在真实工程中,模型能不能训练好,往往不只取决于网络结构,更取决于数据增强、迁移学习、学习率控制和最佳模型保存等训练策略。
本文将以花卉分类任务(Flower-102)为例,带你走通一套基于 PyTorch 的通用训练流程。

1. 数据工程:从原始像素到张量
数据预处理阶段有两个关键点:数据增强(增加多样性)和标准化(加速收敛)。
1.1 数据增强(Data Augmentation)
针对小样本任务,通过 transforms 模拟不同拍摄形态:
-
ToTensor():将像素从 [0, 255] 压缩至 [0, 1] 的归一化过程。
-
Normalize():按照 ImageNet 的均值和标准差执行标准化(Standardization),使数据分布符合正态分布。
from torchvision import transforms, datasets
from torchvision.models import resnet18, ResNet18_Weights
# 训练集:让模型看到“同一类目标的各种变体”
train_transforms = transforms.Compose([
transforms.Resize(128),
transforms.RandomRotation(45), # 模拟多角度拍摄
transforms.RandomResizedCrop(96), # 模拟遮挡与距离远近
transforms.RandomHorizontalFlip(), # 镜像增强
transforms.ToTensor(), # 归一化:[0, 255] -> [0, 1]
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # 标准化
])

2. 迁移学习:分阶段权重控制
直接训练一个几十层的深层网络很难收敛。我们采取“先学后教”的策略。
2.1 阶段一:特征提取层冻结
我们保留 ResNet-18 的“大脑”(卷积特征提取层),只训练它的“嘴巴”(全连接分类层)。
import torch.nn as nn
# 使用新版 torchvision 写法代替 pretrained=True
model = resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)
# 冻结特征提取层:requires_grad = False
for param in model.parameters():
param.requires_grad = False
# 重构分类层
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, 102)

2.2 阶段二:全量解冻与微调(Fine-tuning)
当分类层初步稳定后,解冻全量参数,并使用更小的学习率(如 1e-4)进行全局打磨。
3. 训练核心机制:保存“最强瞬间”
模型训练并不是 Epoch 越多越好。为了应对过拟合,我们必须在验证集上执行“实时监控”。
3.1 学习率调节器与权重备份
import copy
from torch.optim import lr_scheduler
# 每 7 个 Epoch 学习率衰减一次(高尔夫进洞前的减速过程)
exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
best_acc = 0.0
best_model_wts = copy.deepcopy(model.state_dict()) # 备份最佳模型权重
# 在训练循环中执行判断
if phase == 'val' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(model.state_dict())

4. 可视化测试:将 Tensor 还原为图像
模型识别完后输出的是标准化的张量,展示前必须还原。
import numpy as np
import matplotlib.pyplot as plt
def imshow(inp, title=None):
inp = inp.numpy().transpose((1, 2, 0))
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
# 逆运算还原图像:X = X' * std + mean
inp = std * inp + mean
inp = np.clip(inp, 0, 1)
plt.imshow(inp)
完整训练流程回顾(总结)
为了方便大家理解,我们将上述散乱的代码逻辑串联成一个完整的伪代码模板:
# 1. 准备阶段
load_pretrain_model() -> freeze_layers() -> define_new_fc()
# 2. 训练主循环
for epoch in range(num_epochs):
# 训练阶段
train_one_epoch() # 前向传播 -> 计算 Loss -> 反向传播 -> 更新权重
# 验证阶段
validate_model() # 计算 Val Acc
# 策略执行
if current_acc > best_acc:
save_best_model_weights() # 打捞当前最强权重
scheduler.step() # 学习率按需衰减
# 3. 结束阶段
load_best_weights() -> test_and_visualize()
💡 本文与前文(架构解析篇)的核心差异
| 维度 | 前期文章:ResNet 内部架构解析 | 本次总结:模型训练与微调策略 |
| 关注点 | 卷积层内部的数学、残差块原理 | 预处理、调参、保存、微调的工程闭环 |
| 代码重点 | BasicBlock 类、1x1 卷积维度对齐 | weights 参数、requires_grad、StepLR |
| 数据规模 | 28x28 医学小图(验证网络逻辑) | 复杂背景真实花卉分类(工程落地) |
| 最终目标 | 解释模型为什么“深而不退化” | 掌握如何练出一款“高准确率模型” |
结语:深度学习不只是关于网络层数的堆叠,更多的是关于如何通过精细的策略来引导数据流。希望这套分类任务的训练模板能让你在实际项目中事半功倍!
更多推荐

所有评论(0)