teach.pascalyim.com
目录

DL · 章节 5

深度学习 5 —— 卷积进阶:模型动物园、迁移学习与目标检测

在 Kaggle 上打开

课程章节 · 中央理工里尔学院 · 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 上的 ResNetCLIP / 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)

第二种策略叫微调:

  • 解冻部分或全部主干层,
  • 用一个小学习率 (典型值 10410^{-4}10510^{-5}),
  • 让网络在新任务上继续学习

微调的精髓在于"小步走"。预训练权重已经在一个不错的极小值附近;如果用大学习率,它们会被新数据"打飞",反而毁掉所有有价值的初始化。常见的折衷做法是分阶段解冻 (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

如果想再榨出几个点的精度,可以在特征提取收敛之后,解冻 layer4fc,用更小的学习率继续训练:

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 可以画出从 xy 的所有算子节点,残差连接、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) 由四个数描述——中心 (x,y)(x, y) 加上宽度 ww 和高度 hh,通常归一化[0,1][0, 1],以便对图像缩放保持稳定。

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) 的口号就是它的设计哲学:

整张图,一次前向传播,直接预测所有框。

它的做法是:

  • 把输入图划成一个 S×SS \times S 的网格,
  • 对每个网格 cell 直接输出:若干候选框的 (x,y,w,h)(x, y, w, h)、置信度、以及类别概率,
  • 整个过程是一个端到端可训练的 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

四个坐标都归一化到 [0,1][0, 1],坐标系原点在左上角,(xcenter,ycenter)(x_{center}, y_{center}) 是框的几何中心而不是角点。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。

典型工作流:

  1. 上传图像到 Roboflow,
  2. 画框标注 (或导入已有 COCO/Pascal VOC 标注),
  3. 选择 YOLOv8 导出格式,
  4. 下载或直接挂载到 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。在那之前,你已经具备了视觉深度学习一线工程师的基本工具箱。

练习

  1. 特征提取 vs 微调:在英特尔图像分类上,分别实现模式 A (随机初始化)、模式 B (特征提取) 和模式 C (微调最后一个 block)。绘制三条测试精度曲线,讨论它们的训练时间、最终精度和过拟合表现。
  2. 归一化常数的影响:用 ImageNet 预训练权重,但故意使用 Normalize(0.5, 0.5, 0.5) 而不是 ImageNet 的均值方差。测试精度下降多少?给出你的解释。
  3. 冻结/解冻探索:写一个函数 freeze_until(model, layer_name),冻结 ResNet 中给定层之前的所有参数。在英特尔数据集上画一张"解冻深度 vs 测试精度"的曲线,讨论最佳解冻深度。
  4. 从 ResNet-18 到 ResNet-50:换一个更大的预训练模型,精度提升多少?训练时间增加多少?讨论这个权衡。
  5. 可视化决策:用 torchviz 画出微调后的 ResNet-18 的计算图,并用 Grad-CAM 可视化它在英特尔数据集上"看哪里"做出判断。
  6. 手写 YOLO 标签:挑一张图,手动写出三个目标的 .txt 标签 (中心、宽高都归一化)。然后用 OpenCV 把这些框画回图上,验证你写对了。
  7. YOLOv8 训练:从 Roboflow 选一个小数据集 (例如口罩检测、车牌检测),用 YOLOv8n 训练 20 epoch,记录 mAP@0.5mAP@0.5:0.95。把 epochs 加到 50,精度还能提多少?
  8. 批量大小与显存:在 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 Tutorialtorchvision.models——本章实验代码的官方参考。
  • Roboflow Blog & Ultralytics Docs——每周更新的检测/分割实战手册。