DL · 章节 5
深度学习 5 —— 卷积进阶:模型动物园、迁移学习与目标检测
课程章节 · 中央理工里尔学院 · Pascal Yim · 2026 年
我们已经走过卷积神经网络的两章:从二维卷积、池化与简单的 LeNet 风格分类,到 BatchNorm、Dropout、数据增强这些"实战工具"。本章我们要做两件事。第一,正视一个不再回避的事实——今天没有人从零开始训练一个 ResNet。深度学习社区已经积累了一整套预训练模型,它们的权重就是新任务的起点;掌握迁移学习 (transfer learning) 因此成为计算机视觉的入门必修。第二,我们要把视野从"图里有什么"扩展到"图里哪里有什么":这就是目标检测 (object detection)。本章末尾我们将看到 YOLO 是如何用一次前向传播解决滑动窗口的低效问题,并在 Roboflow / Ultralytics 工具链下完成一次完整训练。
1. 模型动物园与基础模型
1.1 什么是模型动物园
模型动物园 (model zoo) 是深度学习框架附带的一组预训练网络集合。在 PyTorch 中,这个动物园住在 torchvision.models 之下;TensorFlow / Keras 也提供 tf.keras.applications。每一个"动物"都有三样东西打包好了:
- 一个经过验证的网络架构,
- 一组在大规模数据集 (通常是 ImageNet,1000 类、约 120 万张图像) 上已优化的权重,
- 一份基准性能报告 (Top-1、Top-5 精度、参数量、FLOPs)。
视觉领域的"常驻嘉宾"包括:ResNet、VGG、DenseNet、MobileNet、EfficientNet,以及最近加入的 ConvNeXt、ViT、Swin Transformer。每个家族都解决了一类工程问题——ResNet 解决了"非常深"网络的梯度消失,MobileNet 在手机端追求轻量,EfficientNet 用复合缩放在精度/参数量曲线上推到了帕累托前沿。
核心原则:不要从零开始。一个 ResNet-18 在 ImageNet 上的训练成本是几百 GPU 小时;同样这个 ResNet-18 你在 Kaggle 上几行代码就能拿到,而且那一组权重对绝大多数视觉任务都已经"看懂了世界"。
1.2 从动物园到基础模型
这一思路在过去五年被推到了极致,产出了所谓的基础模型 (foundation models)。基础模型的特征是:在海量数据上训练,常常采用自监督或弱监督目标 (例如 CLIP 用 4 亿对图文,DINO 用对比学习),并且具备一种通用能力——只需少量样本就能适应任意下游任务。
视觉领域基础模型的"实用祖先",其实正是 ImageNet 时代的预训练 CNN。从概念上看,二者只是规模不同:
| 昨天 (CNN 时代) | 今天 (基础模型时代) |
|---|---|
| ImageNet 上的 ResNet | CLIP / DINOv2 / SAM / ViT-Huge |
| 迁移学习 (fine-tuning) | 适配 (adaptation) / fine-tuning |
| 特征提取 (feature extraction) | 提示 (prompting) / 适配器 (adapters) / LoRA |
无论尺度多大,公式始终是一样的:
先学习通用表示,再用少量数据高效适配到具体任务。
2. 迁移学习的两种经典策略
2.1 第一层学纹理,最后一层学决策
迁移学习之所以可行,是因为深度网络的层级表示呈现出一种规律性。靠近输入的层学到的是通用低级特征——边缘、颜色块、方向纹理;往中间走是中级模式——角点、轮纹、简单形状;靠近输出的层则编码任务特定的高级语义——"这是一只波斯猫的脸"。这个规律意味着:换任务时,前面的层基本可以直接复用,只有最后几层需要重新学习。
关键直觉:迁移学习其实是在说——通用视觉知识不必每次都重新发明,只需保留住,然后在它的基础上加一个小脑袋。
2.2 策略 A:特征提取 (feature extraction)
第一种、也是最简单的策略叫特征提取:
- 把预训练模型的所有参数冻结 (
requires_grad = False), - 把最后的分类头换成新的
nn.Linear(in_features, num_classes), - 只训练新分类头。
这相当于把预训练 CNN 当作一个固定的特征提取器:它把图像压缩成一个 512 维或 2048 维的向量,我们只需要在这个向量上学习一个线性分类器。
优点:训练极快 (只更新几千个参数),需要的数据极少,几乎没有过拟合的风险。缺点是当新任务的图像分布与 ImageNet 差距较大时 (例如医学影像、卫星遥感),通用特征可能不够细致,精度上限有限。
2.3 策略 B:微调 (fine-tuning)
第二种策略叫微调:
- 解冻部分或全部主干层,
- 用一个小学习率 (典型值 到 ),
- 让网络在新任务上继续学习。
微调的精髓在于"小步走"。预训练权重已经在一个不错的极小值附近;如果用大学习率,它们会被新数据"打飞",反而毁掉所有有价值的初始化。常见的折衷做法是分阶段解冻 (gradual unfreezing):先只训分类头,等收敛之后再解冻最后一两个 block,最后才考虑全部解冻。
| 特征提取 | 微调 | |
|---|---|---|
| 速度 | 很快 | 较慢 |
| 数据量需求 | 几百样本即可 | 需要更多 |
| 过拟合风险 | 极低 | 中等 |
| 性能上限 | 中等 | 高 |
3. PyTorch 实战:用 ResNet 做英特尔图像分类
3.1 任务与数据
英特尔图像分类数据集 (Intel Image Classification) 是 Kaggle 上一个经典的入门数据,六个自然场景类别——buildings, forest, glacier, mountain, sea, street,每张图 150×150 RGB,训练集约 14 000 张、测试集约 3 000 张。我们要训练一个 ResNet,把每张图归到正确的场景类别。
这一节我们将对照两种模式:
- 模式 A:ResNet-18,随机初始化,从零开始训练。
- 模式 B:ResNet-18,ImageNet 预训练权重,只训练替换后的
fc层 (即特征提取)。
3.2 数据管线:ImageFolder + 224×224
ResNet 是为 224×224 输入设计的,我们需要把 150×150 的原图缩放上去。同时要做的是正确归一化——这一点很关键,且常被初学者忽略。
from torchvision import datasets, transforms # 模式 A:随机初始化,简单归一化即可 tf_a = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)), ]) # 模式 B:ImageNet 预训练,必须用 ImageNet 的均值和方差 tf_b = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)), ]) train_set = datasets.ImageFolder("seg_train/seg_train", transform=tf_b) test_set = datasets.ImageFolder("seg_test/seg_test", transform=tf_b)
常见陷阱:用了 ImageNet 预训练权重却忘了用 ImageNet 的归一化常数。这会让网络看到的输入分布与训练时不符,精度会莫名其妙地差 5–10 个百分点。
ImageFolder 会按子目录名自动分配类别索引,顺序按字母排序。因此 train_set.classes 给出的就是 ['buildings', 'forest', 'glacier', ...],与训练时的整数标签一一对应。
3.3 释放显存的好习惯
在 Kaggle 这种共享环境下、或在一个 notebook 里反复实验时,显存会逐步被旧的张量占据,迟早撞上 CUDA out of memory。开始训练前的标准动作:
import gc, torch gc.collect() torch.cuda.empty_cache()
gc.collect() 触发 Python 的垃圾回收,torch.cuda.empty_cache() 把已释放但仍被 PyTorch 缓存的显存还给 CUDA 调度器。两者配合,能让你在不重启内核的情况下大幅延长 notebook 寿命。
3.4 模式 A:从零训练 ResNet
import torch.nn as nn from torchvision import models from torch.utils.data import DataLoader device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 关键:weights=None 表示不加载预训练权重 model_a = models.resnet18(weights=None) model_a.fc = nn.Linear(model_a.fc.in_features, 6) model_a = model_a.to(device) train_loader = DataLoader(train_set, batch_size=32, shuffle=True, num_workers=2) test_loader = DataLoader(test_set, batch_size=32, shuffle=False, num_workers=2) optimizer = torch.optim.Adam(model_a.parameters(), lr=1e-3) loss_fn = nn.CrossEntropyLoss()
随机初始化的 ResNet-18 有约 1100 万个参数。训练 10 epoch 在英特尔数据集上大约能拿到 75–80% 的测试精度,而且每个 epoch 都很慢——网络是真的在"从零学习视觉"。
3.5 模式 B:特征提取式迁移学习
model_b = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) # 冻结所有主干参数 for param in model_b.parameters(): param.requires_grad = False # 替换并训练新分类头 model_b.fc = nn.Linear(model_b.fc.in_features, 6) model_b = model_b.to(device) # 只把分类头交给优化器 optimizer = torch.optim.Adam(model_b.fc.parameters(), lr=1e-3)
注意两个细节:只把 model_b.fc.parameters() 传给优化器,这样即便有人不小心忘了 requires_grad=False,优化器也不会去更新主干;学习率可以稍大一些,因为我们只在训练一个线性层,等价于一个普通的 logistic 回归。
在同样的硬件上,模式 B 训练 5 epoch 即可达到 88–92% 的测试精度——比模式 A 快两到三倍、且精度高一大截。这是迁移学习最直观的胜利。
3.6 想做微调?解冻最后一个 block
如果想再榨出几个点的精度,可以在特征提取收敛之后,解冻 layer4 和 fc,用更小的学习率继续训练:
for name, param in model_b.named_parameters(): if "layer4" in name or "fc" in name: param.requires_grad = True else: param.requires_grad = False optimizer = torch.optim.Adam( filter(lambda p: p.requires_grad, model_b.parameters()), lr=1e-5 )
这种"先冻结再轻微解冻"的策略,在小数据集上几乎是教科书式的最佳实践。
3.7 把网络画出来:torchviz
理解 ResNet 的最好方式是看一眼它的计算图。torchviz 可以画出从 x 到 y 的所有算子节点,残差连接、batch norm、ReLU 一目了然:
from torchviz import make_dot import torch x = torch.randn(1, 3, 224, 224).to(device) y = model(x) make_dot(y, params=dict(model.named_parameters()))
在 Kaggle 上需要先 !pip install torchviz。生成的图很大,建议保存为 PDF 慢慢看。
4. 从分类到检测:为什么 CNN 还不够
4.1 两个问题的对照
我们现在很自然地会问:既然 ResNet 能告诉我图里"有什么",它能不能告诉我每个东西"在哪里"?
分类回答的是一个全局问题:
图中是什么?
image → [class]
目标检测回答的是一个密集且结构化的问题:
哪些对象、在什么位置、属于什么类别?
image → [(class_1, box_1), (class_2, box_2), ...]
每一个边界框 (bounding box) 由四个数描述——中心 加上宽度 和高度 ,通常归一化到 ,以便对图像缩放保持稳定。
4.2 分类 CNN 的局限
一个为分类训练的 CNN 通过一连串卷积、池化和 stride,逐步压缩空间分辨率,最终把整张图变成一个 1000 维向量。这一过程的代价是:
- 精细的空间位置信息被丢弃,
- 网络的输出本质上是单一全局答案。
它擅长"识别",不擅长"定位"。
4.3 朴素方案:滑动窗口
最容易想到的方法是把分类 CNN 改造成检测器——
用一个滑动窗口扫遍整张图,把每个子图喂给 CNN,看哪些位置上分类器给出高置信度。
这种方法概念清晰,但在工程上完全不可行:
- 一张 600×600 的图、100 种窗口尺寸 × 上千个位置,等于一次推理要跑成千上万次 CNN;
- 完全无法做到实时;
- 大量重复计算,浪费 GPU。
R-CNN、Fast R-CNN 用区域提议 (region proposals) 缓解了这个问题,但根本性的转折要等 YOLO。
4.4 YOLO 的核心思想
YOLO (You Only Look Once) 的口号就是它的设计哲学:
整张图,一次前向传播,直接预测所有框。
它的做法是:
- 把输入图划成一个 的网格,
- 对每个网格 cell 直接输出:若干候选框的 、置信度、以及类别概率,
- 整个过程是一个端到端可训练的 CNN——损失函数是定位误差 + 分类误差 + 置信度误差的加权和。
范式转变:不再是"把 CNN 在图像上挪动",而是"把检测任务投影到 CNN 的输出空间"。从此一次前向就够了。
正因为只跑一次网络、没有滑动窗口、没有重复计算,YOLO 才能在普通 GPU 上做到实时检测 (>30 FPS),这也是它至今仍统治工业界的原因。
5. 用 Ultralytics YOLO 训练自定义检测器
5.1 YOLO 数据格式
要训练 YOLO,你的数据要按一个非常特定的目录结构组织:
dataset_yolo/
├── images/
│ ├── train/
│ ├── val/
│ └── test/ (可选)
├── labels/
│ ├── train/
│ ├── val/
│ └── test/ (可选)
└── data.yaml
每张图像 images/train/foo.jpg 都对应一个同名的 labels/train/foo.txt,里面每行一个目标:
class_id x_center y_center width height
四个坐标都归一化到 ,坐标系原点在左上角, 是框的几何中心而不是角点。data.yaml 文件描述类别名和路径:
path: /kaggle/input/my-dataset-yolo train: images/train val: images/val nc: 3 names: ['cat', 'dog', 'bird']
5.2 标注工具:Roboflow
手工写 .txt 标签当然很痛苦。Roboflow 是目前最成熟的免费工具:在网页上画框,它自动转换并以 YOLOv8 格式导出,zip 包或一键推送到 Kaggle。Kaggle 上绝大多数"开箱即用"的 YOLO 数据集都来自 Roboflow。
典型工作流:
- 上传图像到 Roboflow,
- 画框标注 (或导入已有 COCO/Pascal VOC 标注),
- 选择 YOLOv8 导出格式,
- 下载或直接挂载到 Kaggle Notebook。
5.3 训练 YOLOv8
Ultralytics 把整个训练管道收敛成几行代码:
!pip install -q ultralytics
from ultralytics import YOLO import torch print("CUDA available:", torch.cuda.is_available()) # nano 版本——小、快、Kaggle 友好 model = YOLO("yolov8n.pt") model.train( data="/kaggle/input/my-dataset-yolo/data.yaml", imgsz=640, epochs=20, batch=16, workers=2, )
yolov8n.pt 已经在 COCO 数据集 (80 类、12 万图像) 上预训练过——这又是一次迁移学习。imgsz=640 是 YOLOv8 的默认输入大小,batch=16 在 16 GB 显存上比较安全;如果撞 OOM,先把 batch 砍半再考虑降 imgsz=512。
5.4 评估与预测
metrics = model.val( data="/kaggle/input/my-dataset-yolo/data.yaml", imgsz=640 ) print(metrics)
输出的关键指标:
- mAP@0.5:IoU 阈值为 0.5 时的平均精度均值;
- mAP@0.5:0.95:IoU 从 0.5 到 0.95 步长 0.05 的平均,COCO 官方主指标;
- precision / recall:每类的精度与召回率。
预测新图像同样一行:
model.predict( source="/kaggle/input/my-dataset-yolo/images/val", imgsz=640, conf=0.25, save=True, )
带框的可视化结果会自动保存到 runs/detect/predict/,可以直接在 notebook 里 IPython.display.Image 展示。
实战建议:刚起步时,先用
yolov8n.pt+imgsz=640+epochs=20走通一次端到端流程;再调imgsz、模型大小 (n→s→m→l→x)、epochs这三个旋钮。绝大多数实际问题都不需要yolov8x,nano 或 small 已经够用。
6. 本章小结
我们从一个工程现实出发——没人从零开始训 CNN——介绍了模型动物园和预训练权重的全部价值。迁移学习通过两条策略 (特征提取与微调) 让我们在小数据集、小预算下也能拿到 SOTA 级别的精度。在英特尔图像分类的对照实验里,我们看到了"随机初始化训 10 个 epoch"输给了"加载预训练 + 只训 fc 头训 5 个 epoch",这是迁移学习最朴素也最有说服力的展示。
后半章我们离开"图里有什么"的世界,进入"哪里有什么"——目标检测。滑动窗口的朴素方案告诉我们朴素思路为何不可行,YOLO 的"一次前向、端到端预测所有框"则代表了视觉任务的范式转变。借助 Ultralytics + Roboflow 工具链,我们已经能在不到二十行代码内训练自己的检测器。
至此,卷积网络这一板块正式告一段落。下一站,我们将转向序列建模——RNN、LSTM,以及把整个 AI 领域重塑了的 Transformer。在那之前,你已经具备了视觉深度学习一线工程师的基本工具箱。
练习
- 特征提取 vs 微调:在英特尔图像分类上,分别实现模式 A (随机初始化)、模式 B (特征提取) 和模式 C (微调最后一个 block)。绘制三条测试精度曲线,讨论它们的训练时间、最终精度和过拟合表现。
- 归一化常数的影响:用 ImageNet 预训练权重,但故意使用
Normalize(0.5, 0.5, 0.5)而不是 ImageNet 的均值方差。测试精度下降多少?给出你的解释。 - 冻结/解冻探索:写一个函数
freeze_until(model, layer_name),冻结 ResNet 中给定层之前的所有参数。在英特尔数据集上画一张"解冻深度 vs 测试精度"的曲线,讨论最佳解冻深度。 - 从 ResNet-18 到 ResNet-50:换一个更大的预训练模型,精度提升多少?训练时间增加多少?讨论这个权衡。
- 可视化决策:用
torchviz画出微调后的 ResNet-18 的计算图,并用 Grad-CAM 可视化它在英特尔数据集上"看哪里"做出判断。 - 手写 YOLO 标签:挑一张图,手动写出三个目标的
.txt标签 (中心、宽高都归一化)。然后用 OpenCV 把这些框画回图上,验证你写对了。 - YOLOv8 训练:从 Roboflow 选一个小数据集 (例如口罩检测、车牌检测),用 YOLOv8n 训练 20 epoch,记录
mAP@0.5与mAP@0.5:0.95。把epochs加到 50,精度还能提多少? - 批量大小与显存:在 YOLO 训练中固定
imgsz=640,把batch从 4 加到 32 (倍增)。绘制"batch size vs 单 epoch 时长 vs 显存占用"。从中你看出了什么样的工程权衡?
拓展阅读
- He, K. 等. Deep Residual Learning for Image Recognition. CVPR 2016.——ResNet 原论文,残差连接的诞生。
- Yosinski, J. 等. How Transferable Are Features in Deep Neural Networks? NeurIPS 2014.——迁移学习理论分析的奠基之作。
- Howard, J. & Ruder, S. Universal Language Model Fine-tuning for Text Classification (ULMFiT). ACL 2018.——分阶段解冻策略的源头。
- Redmon, J. 等. You Only Look Once: Unified, Real-Time Object Detection. CVPR 2016.——YOLO 创世论文。
- Bochkovskiy, A. 等. YOLOv4 / Ultralytics. YOLOv5–YOLOv8 文档——工程化演进的完整脉络。
- Lin, T.-Y. 等. Microsoft COCO: Common Objects in Context. ECCV 2014.——目标检测的标准基准数据集。
- Bommasani, R. 等. On the Opportunities and Risks of Foundation Models. Stanford HAI, 2021.——基础模型概念的系统综述。
- Hu, E. 等. LoRA: Low-Rank Adaptation of Large Models. ICLR 2022.——基础模型时代"特征提取"的现代变体。
- PyTorch 官方文档. Transfer Learning Tutorial 与 torchvision.models——本章实验代码的官方参考。
- Roboflow Blog & Ultralytics Docs——每周更新的检测/分割实战手册。