PyTorch 深度学习专题【左扬精讲】—— 目标检测与语义分割:CNN 的下游任务应用
PyTorch 深度学习专题【左扬精讲】—— 目标检测与语义分割:CNN 的下游任务应用
掌握了经典 CNN 架构(LeNet、AlexNet、VGGNet、GoogLeNet、ResNet、MobileNet、DenseNet)之后,一个关键问题是:这些特征提取网络是如何被应用于实际的计算机视觉下游任务的?
图像分类只告诉我们 "图像里有什么",而目标检测需要回答 "在哪里、是什么",语义分割需要回答 "每个像素属于哪个类别"。
本文将系统讲解目标检测(两阶段与单阶段检测器)和语义分割(FCN、DeepLabV3、U-Net)的架构设计与 PyTorch 官方 API 使用方法。
torchvision.models.detection.fasterrcnn_resnet50_fpn ← 两阶段检测器(Faster R-CNN + FPN)
torchvision.models.detection.fasterrcnn_resnet50_fpn_v2 ← 改进版 Faster R-CNN(V2)
torchvision.models.detection.ssd300_vgg16 ← 单阶段检测器(SSD + VGG 主干)
torchvision.models.detection.retinanet_resnet50_fpn ← 单阶段检测器(RetinaNet + FPN)
torchvision.models.detection.maskrcnn_resnet50_fpn ← 实例分割(Mask R-CNN)
torchvision.models.segmentation.fcn_resnet50 ← 语义分割(FCN + ResNet-50)
torchvision.models.segmentation.fcn_resnet101 ← 语义分割(FCN + ResNet-101)
torchvision.models.segmentation.deeplabv3_resnet50 ← 空洞卷积语义分割(DeepLabV3)
torchvision.models.segmentation.deeplabv3_resnet101 ← DeepLabV3 + ResNet-101
PyTorch目标检测语义分割Faster R-CNNSSDRetinaNetFCNDeepLabV3U-Nettorchvision
学习重点
- 必须掌握
- Faster R-CNN 两阶段检测流程:Backbone → RPN → RoI Pooling → 检测头
- Anchor 机制与 NMS 原理
- FCN 全卷积化与上采样的设计
- 语义分割的评测指标:mIoU 与 pixel accuracy
- torchvision.detection 和 torchvision.segmentation 的 API 使用
- 理解即可
- 单阶段检测器(SSD、RetinaNet)与两阶段检测器的权衡
- DeepLabV3 的 ASPP 多尺度空洞卷积
- Grad-CAM 特征可视化的原理
目录
- 从图像分类到下游任务的架构演进
- 目标检测:两阶段 vs 单阶段
- 两阶段检测器:Faster R-CNN 详解
- 单阶段检测器:SSD 与 RetinaNet
- 语义分割:逐像素分类
- 全卷积网络:FCN
- DeepLabV3:空洞卷积的多尺度分割
- U-Net:编码器-解码器结构
- 特征图可视化:理解 CNN 学到了什么
- FAQ(20 组)
- Roadmap 预告
一、从图像分类到下游任务的架构演进
Why — 为什么 CNN 架构可以直接迁移到下游任务?
图像分类网络(如 ResNet、VGG)通过大量数据学习了通用的视觉特征——边缘、纹理、形状、物体部件等。这些层次化的特征在目标检测和语义分割中同样适用。因此,下游任务通常以预训练的分类网络作为骨干(Backbone),在其基础上添加任务特定的头部(Head)。这种"预训练 Backbone + 任务 Head"的设计范式大幅降低了训练成本。
三大下游任务的本质区别
- 图像分类(Image Classification):整张图像输出一个类别标签,不需要空间定位。
- 目标检测(Object Detection):同时预测多个物体的类别标签和边界框(Bounding Box),输出 N 个 [x, y, w, h, class] 元组。
- 语义分割(Semantic Segmentation):为图像中每个像素预测一个类别标签,输出与输入分辨率相同的分割掩码。
统一架构模式:Backbone + Head
- Backbone:预训练的分类 CNN(如 ResNet-50),负责从图像中提取层次化特征
- Head:任务特定的输出头,负责将特征图转换为最终预测结果
本节小结
- 迁移学习:预训练 Backbone 学到的通用视觉特征可以直接迁移到检测和分割任务
- Backbone + Head:统一的架构设计范式,Backbone 提取特征,Head 完成任务特定输出
二、目标检测:两阶段 vs 单阶段
目标检测的核心挑战是什么?
图像分类是 "单标签输出",而目标检测需要同时解决定位和分类两个问题——一张图中可能有多个不同类别的物体,每个物体需要用边界框精确框出。核心难点在于:物体的数量、位置、尺度都是未知的。
两阶段检测器(Two-Stage Detector)
第一阶段(RPN)生成高质量候选区域(Region Proposals),第二阶段对候选区域进行分类和边界框精修。代表模型:R-CNN → Fast R-CNN → Faster R-CNN。速度较慢但精度高。
单阶段检测器(One-Stage Detector)
直接在特征图的每个位置预测物体类别和边界框,无需生成候选区域。代表模型:SSD、RetinaNet、YOLO。速度快但对小物体召回率较低。
Anchor 机制(锚框机制)
两阶段和单阶段检测器都依赖 Anchor(锚框)来枚举可能的物体尺度和长宽比。通常在每个特征图位置预设 3 种尺度 × 3 种长宽比 = 9 个 Anchor 框。模型学习的是每个 Anchor 与真实物体(Ground Truth)之间的偏移量(Offset),而非直接预测坐标。
两阶段 vs 单阶段对比
| 维度 | 两阶段(Faster R-CNN) | 单阶段(SSD / RetinaNet) |
|---|---|---|
| 代表模型 | Faster R-CNN | SSD、RetinaNet、YOLO |
| 检测流程 | RPN 生成候选区 → 分类+精修 | 一步直接回归类别和坐标 |
| 速度 | 较慢(5-20 FPS) | 较快(25-100+ FPS) |
| 精度 | 高(尤其小物体) | 略低(类别不平衡问题) |
| Anchor 数量 | 约 2,000(RPN 筛选后) | 大量(数十万,覆盖密集) |
本节小结
- 两阶段:RPN 生成候选区 → RoI Pooling → 检测头,精度高,速度较慢
- 单阶段:一步到位预测类别和坐标,速度快,小物体召回率相对较低
- Anchor:枚举预设尺度和长宽比,模型学习偏移量而非绝对坐标
三、两阶段检测器:Faster R-CNN 详解
What — Faster R-CNN 是什么?
Faster R-CNN 由 Shaoqing Ren 等人在 2015 年提出(论文《Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks》),是两阶段目标检测的里程碑。它通过将区域候选网络(Region Proposal Network, RPN)嵌入到主干网络中,实现了候选区域生成和目标检测的端到端联合训练,消除了传统方法中依赖外部区域候选算法(如 Selective Search)的瓶颈。
Why — Faster R-CNN 为什么分两个阶段?
问题:直接对所有可能的区域做分类,计算量巨大且背景框过多
如果对图像中每个滑动窗口位置都做分类,200×200 图像就有 4 万个位置,每个位置还要枚举多个尺度和长宽比——计算量根本无法承受。Faster R-CNN 的解决方案是:先用轻量级的 RPN 网络快速筛选出最有可能是物体的区域(Region Proposals),再用更精确的检测头对这些候选区域做分类和精修。
端到端联合训练的意义
RPN 和检测头共享主干网络的卷积特征(Feature Pyramid Network, FPN 提供多尺度特征),两者可以联合优化,RPN 学到的候选区域质量直接影响检测精度。
输入图像 (H×W×3)│▼
┌─────────────────────────────────────────────────────┐
│ Backbone(ResNet-50 + FPN) │
│ → 提取多尺度共享特征图(P2/P3/P4/P5/P6) │
└─────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────┐
│ RPN(Region Proposal Network) │
│ 输入:特征图每个位置预设 9 个 Anchor(3尺度×3比) │
│ 输出:每个 Anchor 的 objectness score(前景/背景) │
│ + bounding box regression(坐标偏移量) │
│ → NMS 筛选,保留约 1000-2000 个候选框 │
└─────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────┐
│ RoI Align(双线性插值对齐,替代 RoI Pooling) │
│ → 将不同尺寸的候选框映射为固定尺寸(如 7×7) │
└─────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────┐
│ Detection Head(R-CNN Head,Sibling Head) │
│ 固定尺寸特征 → 并行两支: │
│ ① 分类分支(FC+Softmax)→ K+1 类概率 │
│ ② 边界框精修(FC+smooth L1)→ K 类坐标偏移 │
└─────────────────────────────────────────────────────┘│▼输出:[boxes, scores, labels](每张图 N 个检测结果)
RPN 的工作原理
RPN 在特征图的每个空间位置放置 k 个 Anchor 框(通常 k=9:3 种尺度 × 3 种长宽比),对每个 Anchor 预测两个值:objectness score(该位置是否包含物体)和 4 个 bounding box regression 偏移量(x, y, w, h 的调整量)。RPN 的损失函数为:
- 分类损失:对每个 Anchor 的 objectness 做二分类交叉熵(前景 vs 背景)
- 回归损失:对标记为正样本的 Anchor 做 smooth L1 回归
NMS(非极大值抑制)
同一物体可能被多个重叠的 Anchor 预测,NMS 按 objectness score 降序排列所有候选框,保留与最高分框 IoU(Intersection over Union)低于阈值的框,抑制重叠框。典型阈值为 IoU=0.7。
import torch
import torchvision.models.detection as detection
from torchvision.models.detection import (fasterrcnn_resnet50_fpn_v2,FasterRCNN_ResNet50_FPN_V2_Weights,FasterRCNN_MobileNet_V3_Large_FPN_Weights,FasterRCNN_MobileNet_V3_Large_320_FPN_Weights,
)# 推荐使用 V2 版本(基于 Benchmarking Detection Transfer Learning with Vision Transformers)
model = fasterrcnn_resnet50_fpn_v2(weights=FasterRCNN_ResNet50_FPN_V2_Weights.DEFAULT
)
model.eval()# 输入:图像列表(Tensor[C, H, W] 或 PIL.Image)
# 输出:每张图一个字典,包含 boxes, labels, scores
images = [torch.rand(3, 640, 480)]
with torch.no_grad():predictions = model(images)
print(predictions[0].keys()) # dict_keys(['boxes', 'labels', 'scores'])
print(f"检测到 {len(predictions[0]['boxes'])} 个物体")
print(f"类别: {predictions[0]['labels']}")
print(f"置信度: {predictions[0]['scores']}")# 自定义类别数(默认 91 类 COCO)
num_classes = 21 # Pascal VOC 20 类 + 背景
model = fasterrcnn_resnet50_fpn_v2(weights=None, num_classes=num_classes)# MobileNetV3 主干(轻量版)
mobilenet_model = detection.fasterrcnn_mobilenet_v3_large_fpn(weights=FasterRCNN_MobileNet_V3_Large_FPN_Weights.DEFAULT
)# Low-resolution 移动端版本
mobilenet_320_model = detection.fasterrcnn_mobilenet_v3_large_320_fpn(weights=FasterRCNN_MobileNet_V3_Large_320_FPN_Weights.DEFAULT
)
Faster R-CNN 的输出 boxes 格式为 [x_min, y_min, x_max, y_max](像素坐标),labels 为类别索引(COCO 数据集共 91 类),scores 为 [0, 1] 之间的置信度分数。推理时需要将 model.eval() 切换到评估模式,以禁用 Dropout 和 BatchNorm 的训练行为。
Faster R-CNN V1 vs V2 的区别
| 特性 | V1 | V2 |
|---|---|---|
| 骨干网络 | ResNet-50 + FPN | ResNet-50 + FPN(改进训练配方) |
| RoI 处理 | RoI Pooling(量化取整) | RoI Align(双线性插值,无量化误差) |
| COCO Box mAP | 约 37.9% | 约 47.4%(显著提升) |
| 出处 | Faster R-CNN 原论文 | Benchmarking Detection Transfer Learning with Vision Transformers |
V2 版本的改进主要来自:RoI Align 替代 RoI Pooling(消除量化误差)、更优的训练超参数、以及新的数据增强策略。
本节小结
- Faster R-CNN:两阶段检测,Backbone 共享 RPN 和检测头的特征,RPN 生成候选区,检测头做分类+精修
- RPN:在特征图每个位置用 Anchor 预测前景/背景得分和坐标偏移量
- RoI Align:双线性插值对齐,解决 RoI Pooling 的量化误差问题
- V2 版本:改进训练配方,COCO mAP 从约 37.9% 提升至约 47.4%
四、单阶段检测器:SSD 与 RetinaNet
What — 为什么需要单阶段检测器?
单阶段检测器(如 SSD、RetinaNet、YOLO)的核心优势是速度。Faster R-CNN 在 RPN 阶段需要生成和筛选大量候选框,导致推理速度受限于检测头的计算。而单阶段检测器跳过了候选区生成阶段,直接在特征图上预测类别和边界框,实现实时检测(25-100+ FPS)。
SSD(Single Shot MultiBox Detector)的设计
SSD 在主干网络(如 VGG-16)输出的多个尺度特征图上同时做检测:浅层特征图空间分辨率大(适合检测小物体),深层特征图语义信息丰富(适合检测大物体)。SSD 在每个特征图位置预设多个 Anchor,直接预测每个 Anchor 的类别置信度和边界框偏移量。
RetinaNet 与 Focal Loss
单阶段检测器的核心问题是"类别不平衡":背景 Anchor 占绝对多数,导致正样本(物体)梯度信号被淹没。RetinaNet 提出了 Focal Loss,通过降低易分类样本的权重,使模型专注于难分类的正样本和困难负样本:
Focal Loss = -α_t · (1 - p_t)^γ · log(p_t)
# α_t: 类别平衡权重
# p_t: 模型预测的置信度
# γ (gamma): 聚焦参数,γ越大越关注难分类样本
# 典型值:α=0.25, γ=2
当模型对某样本的置信度 p_t 很高时,(1-p_t)^γ 趋近于 0,该样本的损失贡献被大幅压低,实现了自动的难例挖掘。
import torch
import torchvision.models.detection as detection
from torchvision.models.detection import (ssd300_vgg16, retinanet_resnet50_fpn,SSD300_VGG16_Weights, RetinaNet_ResNet50_FPN_Weights,
)# SSD300(以 VGG-16 为骨干,300×300 输入)
ssd_model = ssd300_vgg16(weights=SSD300_VGG16_Weights.DEFAULT)
ssd_model.eval()# RetinaNet(以 ResNet-50 + FPN 为骨干)
retinanet_model = retinanet_resnet50_fpn(weights=RetinaNet_ResNet50_FPN_Weights.DEFAULT)
retinanet_model.eval()# 推理示例
images = [torch.rand(3, 640, 480)]
with torch.no_grad():ssd_pred = ssd_model(images)retinanet_pred = retinanet_model(images)print(f"SSD 检测数: {len(ssd_pred[0]['boxes'])}")
print(f"RetinaNet 检测数: {len(retinanet_pred[0]['boxes'])}")# 两种模型均输出: boxes, labels, scores
YOLO 的特殊性
YOLO(You Only Look Once)是最流行的单阶段检测器之一,但 PyTorch 官方 torchvision 不包含 YOLO 实现。社区常用的 YOLO 实现主要依赖 Ultralytics 的 ultralytics 库(支持 YOLOv8/v11 等),通过 pip install ultralytics 安装。实际项目中推荐使用 Ultralytics 官方库加载 YOLO 模型。
本节小结
- SSD:多尺度特征图并行检测,浅层检测小物体,深层检测大物体
- RetinaNet:Focal Loss 解决单阶段检测的类别不平衡问题
- YOLO:不包含在 torchvision 官方库中,需使用 ultralytics 等第三方库
五、语义分割:逐像素分类
What — 语义分割与实例分割有何不同?
语义分割(Semantic Segmentation)为图像中每个像素分配一个类别标签,同一类别的不同物体不加区分(如两张人脸都标记为"人")。实例分割(Instance Segmentation)更进一步,不仅分类每个像素,还区分同类物体的不同个体(如将两张人脸分别标记为"人_1"和"人_2")。Mask R-CNN 是实例分割的代表性模型。
语义分割的核心挑战:如何恢复空间分辨率?
CNN 中的池化(MaxPool)和步长卷积会不断缩小特征图的空间尺寸(如 224→112→56→28→7),而语义分割需要输出与输入相同分辨率的分割掩码。解决方案主要有两种:
- 上采样(Upsampling):通过转置卷积(Transposed Convolution)或双线性插值将特征图放大回原始分辨率
- 空洞卷积(Atrous / Dilated Convolution):在不下采样损失分辨率的前提下扩大感受野(DeepLabV3 采用此策略)
语义分割的评测指标
- mIoU(Mean Intersection over Union):各类别 IoU 的平均值,是最核心的分割评测指标
- Pixel Accuracy:正确分类像素占总像素的比例
IoU 的计算方式
对于类别 c:IoU_c = |预测为 c 的像素 ∩ 真实为 c 的像素| / |预测为 c 的像素 ∪ 真实为 c 的像素|。mIoU 是所有类别 IoU 的算术平均。典型值:FCN-ResNet50 在 VOC 上约 60.5%,DeepLabV3-ResNet50 约 66.4%。
本节小结
- 语义分割:为每个像素分配类别标签,输出 H×W 的分割掩码
- 实例分割:区分同类物体的不同个体(如 Mask R-CNN)
- mIoU:语义分割的核心评测指标,各类别的平均交并比
六、全卷积网络:FCN
What — FCN 是什么?
FCN(Fully Convolutional Network)由 Jonathan Long 等人在 2014 年提出(论文《Fully Convolutional Networks for Semantic Segmentation》),是深度学习语义分割的开山之作。FCN 的核心创新是将图像分类网络(如 VGG-16)的全连接层替换为卷积层,使网络可以接受任意尺寸的输入并输出对应尺寸的分割掩码。
Why — 为什么分类网络不能直接用于分割?
问题:全连接层要求固定尺寸输入,且输出是向量而非空间图
VGG-16 等分类网络的最后几层是全连接层(4096→4096→1000),输出是 1000 维向量而非空间特征图。全连接层丢弃了所有空间信息,无法做逐像素预测。FCN 的解决方案是将全连接层等效替换为 1×1 卷积:
- VGG-16 的 FC1(4096 维)→ Conv1×1(输入 512×7×7 → 输出 4096×1×1)
- VGG-16 的 FC2(4096 维)→ Conv1×1(输入 4096 → 输出 4096)
- VGG-16 的 FC3(1000 维)→ Conv1×1(输入 4096 → 输出 1000)
替换后网络输出变为 H/32 × W/32 × 1000 的特征图(H 和 W 为输入图像的高和宽)。
上采样策略
FCN 通过转置卷积(Transposed Convolution,也称反卷积)将 1/32 分辨率的特征图上采样 32 倍回原始分辨率。FCN-32s 直接一步上采样 32 倍,FCN-16s 将 1/16 和 1/32 特征图融合后再上采样 16 倍,FCN-8s 则融合 1/8、1/16、1/32 三层,精度最高。
import torch
import torchvision.models.segmentation as segmentation
from torchvision.models.segmentation import (fcn_resnet50, fcn_resnet101,FCN_ResNet50_Weights, FCN_ResNet101_Weights,
)# FCN-ResNet50(以 ResNet-50 为骨干)
fcn_model = fcn_resnet50(weights=FCN_ResNet50_Weights.DEFAULT)
fcn_model.eval()# FCN-ResNet101(更深骨干,精度更高)
fcn_model101 = fcn_resnet101(weights=FCN_ResNet101_Weights.DEFAULT)# 输入预处理(权重自带的 transforms)
preprocess = FCN_ResNet50_Weights.DEFAULT.transforms()
x = torch.rand(1, 3, 520, 520) # Batch of 1
x_processed = preprocess(x)# 前向传播
with torch.no_grad():output = fcn_model(x_processed)["out"]
print(f"输出尺寸: {output.shape}") # torch.Size([1, 21, 520, 520])
# 21 通道:Pascal VOC 20 类 + 背景# 获取每个像素的预测类别
pred_labels = output.argmax(dim=1) # torch.Size([1, 520, 520])
print(f"分割掩码形状: {pred_labels.shape}") # H×W 分割图# 自定义类别数
custom_model = segmentation.fcn_resnet50(weights=None, num_classes=5)# 显存估算:FCN-ResNet50 约 35.3M 参数,152.72 GFLOPS
total_params = sum(p.numel() for p in fcn_model.parameters())
print(f"FCN-ResNet50 参数量: {total_params:,}") # 35,322,218
FCN 的输出 output["out"] 形状为 [B, num_classes, H, W],每张图每个通道对应一个类别的分割概率图。argmax(dim=1) 在通道维度取最大值,得到每个像素的预测类别索引。output.softmax(dim=1) 可以将通道维度转换为概率分布,便于计算置信度。
FCN 的性能指标(COCO-VOC 子集)
| 模型 | 参数量 | GFLOPS | mIoU | Pixel Acc |
|---|---|---|---|---|
| FCN-ResNet50 | 35.3M | 152.72 | 60.5% | 91.4% |
| FCN-ResNet101 | 54.3M | 232.74 | 63.7% | 91.9% |
权重在 COCO 数据集的 Pascal VOC 子集(20 类)上训练,resize_size=[520],归一化参数为 mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]。
本节小结
- FCN:将分类网络的全连接层替换为 1×1 卷积,实现全卷积化,可处理任意尺寸输入
- 转置卷积:将低分辨率特征图上采样回原始分辨率
- 多尺度融合:FCN-8s 融合 1/8、1/16、1/32 特征图,精度优于 FCN-32s
- FCN-ResNet50:mIoU 60.5%,Pixel Acc 91.4%
七、DeepLabV3:空洞卷积的多尺度分割
What — DeepLabV3 是什么?
DeepLabV3 由 Google 团队提出(论文《Rethinking Atrous Convolution for Semantic Image Segmentation》),是当前最主流的语义分割架构之一。DeepLabV3 的核心创新是空洞卷积(Atrous Convolution / Dilated Convolution),在不下采样特征图的前提下扩大感受野,同时保持高空间分辨率。
Why — 为什么 FCN 的上采样方案有局限性?
问题:多次下采样导致空间细节丢失,上采样无法完全恢复
FCN 的多次下采样(stride=2)导致 224×224 图像最终只有 7×7 的特征图,虽然上采样可以恢复尺寸,但在此过程中丢失的细节(如物体边界、细小结构)无法通过上采样完全恢复。DeepLabV3 的解决方案是:使用空洞卷积,在不降低分辨率的情况下增大感受野。
空洞卷积的工作原理
标准卷积(dilation=1)的卷积核元素之间间隔 0;空洞卷积(dilation=r)在元素之间插入 r-1 个空洞(零值)。对于 k×k 卷积核,空洞卷积的有效感受野为 k + (k-1)×(r-1)。例如,3×3 卷积核在 dilation=2 时,有效感受野相当于 5×5,但参数量仍为 3×3。
ASPP(Atrous Spatial Pyramid Pooling)模块
DeepLabV3 的 ASPP 模块在特征图上并行使用多个不同空洞率的卷积分支(dilation=12, 24, 36),同时捕捉多尺度语义信息:
- 大 dilation(如 36):大物体、大尺度特征
- 小 dilation(如 12):小物体、细粒度特征
- Image-level pooling:全局上下文信息
import torch
import torchvision.models.segmentation as segmentation
from torchvision.models.segmentation import (deeplabv3_resnet50, deeplabv3_resnet101, deeplabv3_mobilenet_v3_large,DeepLabV3_ResNet50_Weights, DeepLabV3_ResNet101_Weights,DeepLabV3_MobileNet_V3_Large_Weights,
)# DeepLabV3-ResNet50(以 ResNet-50 为骨干,含 ASPP 模块)
deeplabv3_model = deeplabv3_resnet50(weights=DeepLabV3_ResNet50_Weights.DEFAULT)
deeplabv3_model.eval()# DeepLabV3-ResNet101(更深骨干,精度更高)
deeplabv3_model101 = deeplabv3_resnet101(weights=DeepLabV3_ResNet101_Weights.DEFAULT)# DeepLabV3-MobileNetV3-Large(轻量版)
deeplabv3_mobilenet = deeplabv3_mobilenet_v3_large(weights=DeepLabV3_MobileNet_V3_Large_Weights.DEFAULT
)# 推理
preprocess = DeepLabV3_ResNet50_Weights.DEFAULT.transforms()
x = torch.rand(1, 3, 520, 520)
x_processed = preprocess(x)with torch.no_grad():output = deeplabv3_model(x_processed)["out"]
print(f"输出尺寸: {output.shape}") # torch.Size([1, 21, 520, 520])# 获取分割结果
pred_labels = output.argmax(dim=1)# 参数量对比:DeepLabV3-ResNet50 约 42.0M
total_params = sum(p.numel() for p in deeplabv3_model.parameters())
print(f"DeepLabV3-ResNet50 参数量: {total_params:,}") # 42,004,074
DeepLabV3 的 model(x)["out"] 输出与 FCN 相同格式的分割 logits(未归一化),model(x)["aux"] 是辅助损失头的输出(训练时使用,推理时被丢弃)。ASPP 模块中的多分支空洞卷积是 DeepLabV3 在 Pascal VOC 上达到 66.4% mIoU 的关键。
DeepLabV3 性能指标(COCO-VOC 子集)
| 模型 | 骨干网络 | 参数量 | GFLOPS | mIoU | Pixel Acc |
|---|---|---|---|---|---|
| DeepLabV3-ResNet50 | ResNet-50 | 42.0M | 178.72 | 66.4% | 92.4% |
| DeepLabV3-ResNet101 | ResNet-101 | 60.1M | 256.38 | 67.4% | 92.4% |
| DeepLabV3-MobileNetV3 | MobileNetV3-Large | 3.5M | 12.37 | 60.3% | 91.2% |
本节小结
- DeepLabV3:通过空洞卷积(dilation>1)在不下采样分辨率的前提下扩大感受野
- ASPP:多尺度空洞卷积分支并行,捕捉不同尺度的语义信息
- mIoU 66.4%(ResNet-50),比 FCN-ResNet50(60.5%)高约 6 个百分点
八、U-Net:编码器-解码器结构
What — U-Net 是什么?
U-Net 由 Olaf Ronneberger 等人在 2015 年提出(论文《U-Net: Convolutional Networks for Biomedical Image Segmentation》),最初为生物医学图像分割设计,因其简洁有效的编码器-解码器结构成为医学影像、遥感分割等领域的标准 Baseline。U-Net 的名称来源于其对称的 U 形架构。
Why — 为什么需要编码器-解码器架构?
问题:语义分割需要同时满足"语义理解"和"空间定位"两个目标
深层特征图有丰富的语义信息(知道"是什么"),但空间分辨率低(不知道"在哪里");浅层特征图空间分辨率高(知道"在哪里"),但语义信息弱。编码器-解码器架构通过下采样提取深层语义特征,再通过上采样恢复空间分辨率,同时通过 Skip Connection 将浅层的空间细节信息传递到深层。
U-Net 的核心设计
- 编码器(下采样路径):交替堆叠 3×3 卷积 + ReLU + 2×2 MaxPool,逐步降低分辨率、提高通道数(如 64→128→256→512)
- 解码器(上采样路径):交替堆叠 3×3 转置卷积(2× 上采样)+ Concat(与编码器同尺度特征拼接)+ 3×3 卷积,逐步恢复分辨率
- Skip Connection:编码器每个阶段的特征图直接传递给解码器对应阶段(Concat 而非 Add),保留空间细节
Skip Connection 为什么用 Concat 而不是 Add?
ResNet 的残差连接使用 Add(逐元素相加),通道数不变。U-Net 的 Skip Connection 使用 Concat(沿通道维度拼接),使解码器能同时利用来自编码器的浅层细节(高分辨率)和来自深层上采样的语义特征(丰富通道信息),通道数翻倍。这是 U-Net 保留边界细节的关键。
import torch
import torch.nn as nnclass DoubleConv(nn.Module):"""连续两个 3x3 卷积 + BN + ReLU"""def __init__(self, in_ch, out_ch):super().__init__()self.conv = nn.Sequential(nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1),nn.BatchNorm2d(out_ch),nn.ReLU(inplace=True),nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1),nn.BatchNorm2d(out_ch),nn.ReLU(inplace=True),)def forward(self, x):return self.conv(x)class UNet(nn.Module):def __init__(self, in_channels=3, num_classes=1, base_ch=64):super().__init__()# 编码器(下采样)self.enc1 = DoubleConv(in_channels, base_ch) # 64self.enc2 = DoubleConv(base_ch, base_ch*2) # 128self.enc3 = DoubleConv(base_ch*2, base_ch*4) # 256self.enc4 = DoubleConv(base_ch*4, base_ch*8) # 512self.pool = nn.MaxPool2d(kernel_size=2, stride=2)# 瓶颈层self.bottleneck = DoubleConv(base_ch*8, base_ch*16) # 1024# 解码器(上采样)self.up4 = nn.ConvTranspose2d(base_ch*16, base_ch*8, kernel_size=2, stride=2)self.dec4 = DoubleConv(base_ch*16, base_ch*8) # Concat 后通道翻倍self.up3 = nn.ConvTranspose2d(base_ch*8, base_ch*4, kernel_size=2, stride=2)self.dec3 = DoubleConv(base_ch*8, base_ch*4)self.up2 = nn.ConvTranspose2d(base_ch*4, base_ch*2, kernel_size=2, stride=2)self.dec2 = DoubleConv(base_ch*4, base_ch*2)self.up1 = nn.ConvTranspose2d(base_ch*2, base_ch, kernel_size=2, stride=2)self.dec1 = DoubleConv(base_ch*2, base_ch)# 输出层:1×1 卷积到指定类别数self.out_conv = nn.Conv2d(base_ch, num_classes, kernel_size=1)def forward(self, x):# 编码器e1 = self.enc1(x)e2 = self.enc2(self.pool(e1))e3 = self.enc3(self.pool(e2))e4 = self.enc4(self.pool(e3))# 瓶颈b = self.bottleneck(self.pool(e4))# 解码器(每个阶段:上采样 → Concat → DoubleConv)d4 = self.dec4(torch.cat([self.up4(b), e4], dim=1)) # Concat 保留细节d3 = self.dec3(torch.cat([self.up3(d4), e3], dim=1))d2 = self.dec2(torch.cat([self.up2(d3), e2], dim=1))d1 = self.dec1(torch.cat([self.up1(d2), e1], dim=1))return self.out_conv(d1)model = UNet(in_channels=3, num_classes=21)
x = torch.randn(1, 3, 224, 224)
print(model(x).shape) # torch.Size([1, 21, 224, 224])
U-Net 的 torch.cat([up_feature, encoder_feature], dim=1) 是其核心设计:编码器特征(高分辨率、低通道)与解码器特征(低分辨率、高通道)拼接后,再通过 DoubleConv 融合。转置卷积(ConvTranspose2d)的 stride=2 使特征图尺寸翻倍,与对应编码器层的分辨率精确对齐。
U-Net 变体概览
- U-Net++(Nested U-Net):在编码器和解码器之间增加了密集的嵌套 Skip Connection,融合更多尺度的特征,提升边界精度
- ResU-Net:在 U-Net 的每个 DoubleConv 块中引入残差连接,缓解深层网络的梯度问题
- 3D U-Net:将 2D 卷积替换为 3D 卷积,直接处理体数据(如 CT、MRI)
- TransU-Net:用 Transformer 编码器替代 ResNet 作为骨干,将自注意力机制引入医学图像分割
本节小结
- U-Net:对称 U 形架构,编码器下采样提取语义,解码器上采样恢复空间分辨率
- Skip Connection:Concat 而非 Add,使解码器同时获得高分辨率和丰富语义
- 转置卷积:stride=2 的转置卷积实现特征图尺寸翻倍
- 核心优势:在小数据集(如医学影像)上也能取得优秀效果
九、特征图可视化:理解 CNN 学到了什么
为什么需要特征图可视化?
CNN 常被称为"黑箱"——我们很难直观理解一个神经元在响应什么。特征图可视化通过将 CNN 的中间层激活映射回输入图像,帮助我们理解网络在每一层学到了什么样的视觉模式(如边缘、纹理、物体部件、完整物体)。这既是模型调试工具,也是模型可解释性(Explainable AI)研究的基础。
方法一:直接激活可视化
取某层的特征图,直接可视化其空间热力图。通过 PyTorch 的 Hook 机制可以在不修改模型的情况下提取任意层的输出:
import torch
import torchvision.models as models
import matplotlib.pyplot as pltmodel = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
model.eval()# 注册 Hook:提取 conv1 层的输出
activations = {}
def get_activation(name):def hook(module, input, output):activations[name] = output.detach()return hook# 在目标层注册 Hook
model.conv1.register_forward_hook(get_activation("conv1"))# 前向传播
x = torch.randn(1, 3, 224, 224)
with torch.no_grad():_ = model(x)# 可视化 conv1 的 64 个通道(部分)
act = activations["conv1"][0] # [64, 112, 112]
fig, axes = plt.subplots(4, 8, figsize=(16, 8))
for i, ax in enumerate(axes.flat):ax.imshow(act[i].numpy(), cmap="viridis")ax.axis("off")
plt.suptitle("ResNet-50 conv1 激活图(64 通道)")
plt.tight_layout()
plt.show()
方法二:CAM(Class Activation Mapping)
CAM 通过分类层的权重,将最后一个卷积层的特征图加权求和,生成类别相关的热力图。CAM 只适用于以 Global Average Pooling + 全连接层结尾的分类网络(如原始的 GoogLeNet)。公式:
CAM_c(x, y) = Σ_k w_k^c · f_k(x, y)
# w_k^c: 第 k 个通道对类别 c 的分类权重(来自 GAP 后的 FC 层)
# f_k(x, y): 最后一层卷积在 (x, y) 位置的第 k 个通道激活
方法三:Grad-CAM(Gradient-weighted Class Activation Mapping)
Grad-CAM 是 CAM 的通用化版本,通过计算类别得分对特征图的梯度作为权重,适用于任意包含卷积层的网络:
import torch
import torchvision.models as modelsmodel = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
model.eval()# 目标层(通常是最后一个卷积层)
target_layer = model.layer4[-1].conv2
activations = {}
gradients = {}# Forward hook:保存特征图
def forward_hook(module, input, output):activations["feature"] = output.detach()# Backward hook:保存梯度
def backward_hook(module, grad_input, grad_output):gradients["grad"] = grad_output[0].detach()handle_fwd = target_layer.register_full_backward_hook(forward_hook)
handle_bwd = target_layer.register_full_backward_hook(backward_hook)# 前向 + 反向
x = torch.randn(1, 3, 224, 224, requires_grad=True)
output = model(x)
# 选择置信度最高的类别
target_class = output.argmax(dim=1).item()
# 反向传播(只对目标类别求梯度)
model.zero_grad()
output[0, target_class].backward()# 提取特征图和梯度
feature = activations["feature"] # [B, C, H, W]
grad = gradients["grad"] # [B, C, H, W]# 全局平均池化梯度作为通道权重
weights = grad.mean(dim=(2, 3)) # [B, C]
# 加权求和
cam = (weights[0, :, None, None] * feature[0]).mean(dim=0)
cam = torch.relu(cam) # ReLU 只保留正响应
# 上采样到原始图像尺寸
cam = torch.nn.functional.interpolate(cam.unsqueeze(0).unsqueeze(0),size=(224, 224),mode="bilinear",align_corners=False
)[0, 0]# 可视化
import matplotlib.pyplot as plt
plt.imshow(cam.numpy(), cmap="jet")
plt.axis("off")
plt.show()handle_fwd.remove()
handle_bwd.remove()
Grad-CAM 的核心思想是:用梯度衡量每个特征图通道对目标类别的重要程度,然后对特征图做加权求和并用 ReLU 过滤负响应,最终上采样到原始图像尺寸。热力图中高亮区域即为网络判断该类别时关注的关键区域。
主流 CAM 类方法对比
| 方法 | 适用条件 | 是否需要梯度 | 特点 |
|---|---|---|---|
| CAM | GAP + FC 分类头 | 否 | 简单快速,但只适用特定架构 |
| Grad-CAM | 任意卷积网络 | 是 | 通用性好,PyTorch Hook 实现 |
| Grad-CAM++ | 任意卷积网络 | 是(二阶) | 梯度加权改进,边界更精确 |
| Score-CAM | 任意网络 | 否 | 无需梯度,用置信度变化衡量重要性 |
本节小结
- Hook 机制:register_forward_hook / register_full_backward_hook 提取任意层的激活和梯度
- CAM:用 GAP 后的 FC 权重对特征图加权,生成类别激活图
- Grad-CAM:用梯度作为通道权重,是 CAM 的通用化版本,适用于任意 CNN
- ReLU + 上采样:保留正响应热力图,上采样到原始图像尺寸可视化
十、FAQ(20 组)
FAQ — 精选 20 问,深入理解下游任务架构
Q1. RoI Pooling 和 RoI Align 有什么区别?
RoI Pooling 通过量化取整将候选框映射到特征图,存在两次量化误差;RoI Align 使用双线性插值精确采样,完全消除了量化误差。 RoI Pooling 的过程:将候选框区域划分为固定数量的子区域(如 7×7),对每个子区域做最大池化。由于候选框坐标通常是浮点数,取整会引入位置偏差(通常偏移 0-1 个像素)。RoI Align 通过在每个采样点使用双线性插值计算浮点位置的特征值,保留了亚像素级精度。Mask R-CNN 和 Faster R-CNN V2 均使用 RoI Align。
Q2. Anchor 机制中的 IoU(Intersection over Union)是什么?
IoU 是预测框与真实框的交集面积除以并集面积,衡量两个框的重叠程度,取值范围 [0, 1]。 IoU = |B_pred ∩ B_gt| / |B_pred ∪ B_gt|。在 RPN 中,IoU > 0.7 的 Anchor 标记为正样本(包含物体),IoU < 0.3 的标记为负样本,介于两者之间的忽略。IoU=1 表示完全重叠,IoU=0 表示完全不相交。
Q3. 为什么单阶段检测器(如 SSD)的小物体召回率通常低于两阶段检测器?
单阶段检测器在深层特征图上做预测,空间分辨率低(通常为输入的 1/32),小物体在特征图上只有几个像素甚至完全消失。 SSD 在多个尺度特征图上检测,其中浅层特征图负责小物体,但浅层的语义信息不足,容易被背景干扰。两阶段检测器通过 RPN 可以对小候选区做二次精修,小物体信息在 RoI Align 后有专门的特征表达。FPN(Feature Pyramid Network)通过多尺度特征融合改善了单阶段检测器的小物体检测能力。
Q4. NMS(Non-Maximum Suppression)是如何工作的?
NMS 按置信度降序排列所有检测框,选择最高分的框作为保留框,删除与其 IoU 超过阈值的其余框,重复此过程直到处理完所有框。 典型流程:输入检测框列表 [{score=0.9, box=A}, {score=0.8, box=B}, ...] → 保留 A(最高分),删除与 A 的 IoU > 0.7 的框 → 剩余最高分为 B,重复。通常 IoU 阈值设为 0.5~0.7:过低会过度抑制重叠的相邻物体,过高会导致同一物体的多个重复框被保留。
Q5. FCN 的"全卷积化"是如何实现的?
FCN 将 VGG 等分类网络的全连接层等效替换为 1×1 卷积层,使网络输出从固定长度的向量变为空间特征图。 具体来说:输入 512×7×7 的全连接层(7×7 是 VGG 在 224×224 输入下的特征图尺寸),等效为输入通道 512、输出通道 4096 的 1×1 卷积。这样替换后,网络可以接受任意 H×W 的输入图像,输出 H/32 × W/32 × num_classes 的分割 logits。
Q6. 为什么 DeepLabV3 使用空洞卷积而不是更多的上采样?
空洞卷积在保持特征图高分辨率的同时扩大感受野,避免了上采样无法恢复的细节损失。 如果只用 FCN 的上采样方法,需要经过多次下采样(每次分辨率减半)再上采样回来。以 ResNet 为例,stride=2 的卷积导致 224→56 的分辨率损失,即使上采样 4 倍也达不到原始精度。空洞卷积在 dilation=2 时感受野相当于 5×5(比标准 3×3 大),但分辨率不变,使 DeepLabV3 在 Pascal VOC 的边界分割精度显著优于 FCN。
Q7. U-Net 的 Skip Connection 为什么用 Concat 而不是 Add?
Concat 拼接保留了编码器特征的全部信息(通道数翻倍),Add 则只是特征融合(通道数不变),后者会丢失一半的浅层细节。 U-Net 需要在解码器中同时获得高分辨率空间信息(来自编码器)和丰富的语义通道信息(来自解码器上采样路径)。使用 Concat 后 DoubleConv 可以学习如何最优地组合这两部分信息。如果使用 Add,两部分信息已经被混合,解码器的表达能力受限。
Q8. 语义分割中 mIoU 优于 Pixel Accuracy 的原因是什么?
Pixel Accuracy 是全局正确像素比例,容易被占面积大的类别(如背景)主导;mIoU 是各类别 IoU 的平均值,公平地衡量每个类别的分割质量。 举例:如果图像 90% 是背景(IoU=90%),10% 是目标(IoU=10%),Pixel Accuracy = 82%,但 mIoU = (90%+10%)/2 = 50%,后者更真实地反映了目标类别的分割质量。mIoU 是语义分割的事实标准评测指标。
Q9. 为什么 RetinaNet 需要 Focal Loss,而两阶段检测器不需要?
单阶段检测器中正样本(物体)Anchor 远少于负样本(背景)Anchor,类别极度不平衡,Focal Loss 通过降低易分类样本的权重自动平衡。 两阶段检测器的 RPN 已经筛选出约 2000 个候选区,其中正负样本比例相对可控。而 SSD/RetinaNet 直接在数十万个 Anchor 上做预测,99% 以上的 Anchor 是负样本(背景),标准交叉熵损失中负样本的梯度会淹没正样本的梯度。Focal Loss 的 (1-p_t)^γ 因子自动降低了高置信度负样本的贡献,使模型专注于难分类的样本。
Q10. FPN(Feature Pyramid Network)的多尺度特征融合是如何工作的?
FPN 通过自顶向下的路径和横向连接,将深层特征图上采样后与同尺度的浅层特征图逐元素相加,融合高语义和低语义的特征。 FPN 的结构:主干网络的深层特征图(如 C5)经过 1×1 卷积降维后上采样 2 倍,与同分辨率的浅层特征图(C4)逐元素相加,得到 P4。重复此过程得到 P3、P2。所有金字塔层级(P2-P6)用于 RPN 或检测头,实现多尺度目标检测:浅层 P2 负责小物体,深层 P5 负责大物体。
Q11. Faster R-CNN 中 RPN 和检测头是否可以独立训练?
Fast R-CNN 的 RPN 和检测头是独立训练的,而 Faster R-CNN 可以端到端联合训练,两者的卷积特征完全共享。 早期 Fast R-CNN 使用 Selective Search 生成候选区,独立于检测头训练。Faster R-CNN 将 RPN 嵌入主干网络,两者共享 ResNet-FPN 的卷积特征,通过交替优化(alternating training)联合训练:先训练 RPN,再固定 RPN 用其候选区训练检测头,循环迭代。现代实现通常使用近似联合训练(4 步交替优化),在端到端反向传播中同时优化 RPN 和检测头。
Q12. 语义分割的输出如何从 logits 转换为可视化的分割图?
对分割 logits 的通道维度取 argmax 得到每个像素的类别索引,再通过颜色映射表将索引转换为彩色分割图。 步骤:output = model(x)["out"](形状 [B, num_classes, H, W])→ pred = output.argmax(dim=1)(形状 [B, H, W])→ 创建一个调色板(palette),如 VOC 有 21 类对应的 RGB 值 → 将 pred 中的类别索引替换为对应颜色 → 转换为 PIL Image 或 matplotlib 可视化。也可以用 softmax 得到概率分布后取 argmax,或直接用 PIL Image 的 putpalette 方法。
Q13. 为什么 U-Net 在小数据集上表现良好?
U-Net 的 Skip Connection 将编码器的浅层特征直接传递给解码器,这些浅层特征是通用的视觉模式(如边缘、纹理),不依赖大量数据即可学习,因此 U-Net 在小数据集上也能有效训练。 同时,U-Net 的参数量适中(医学影像数据集通常只有几百到几千张标注图像),不易过拟合。而 VGG-16 等大型分类 Backbone 在小数据集上容易过拟合。U-Net 的编码器可以使用在大型数据集(如 ImageNet)上预训练的 ResNet 权重作为初始化,进一步提升小数据集上的表现。
Q14. Grad-CAM 为什么选择最后一个卷积层而不是更浅的层?
最后一个卷积层在空间分辨率和语义信息之间达到最佳平衡——既有足够的空间细节(物体的大致轮廓),又有丰富的类别语义信息。 浅层(如 conv1)的激活图空间精度高但语义弱,热力图只能显示边缘和纹理等通用视觉模式,对类别没有区分性。深层(如 fc 层)有强语义但空间信息已完全丢失,无法定位。Grad-CAM 论文建议使用最后一个卷积层(对 ResNet 是 layer4),以获得既有类别区分性(语义)又有空间定位能力的热力图。
Q15. 目标检测中 One-stage 和 Two-stage 各自的适用场景是什么?
Two-stage(Faster R-CNN)适合精度优先的场景(自动驾驶、医疗影像);One-stage(SSD/RetinaNet/YOLO)适合速度优先的场景(视频监控、移动端推理)。 自动驾驶中对小物体(行人、锥桶)的召回率要求极高,且帧率要求在 15-30 FPS,通常使用 Faster R-CNN + FPN 或 Cascade R-CNN。视频监控需要实时处理大量帧,One-stage 检测器(YOLO 系列)在 30-100+ FPS 下仍能保持可接受的精度,是工业界的主流选择。实时性要求极高时,可选 YOLOv8-nano 等极致轻量模型(仅约 3M 参数)。
Q16. 为什么语义分割在物体边界处的精度通常最低?
物体边界处两类像素交错分布(背景与前景交替),特征图的感受野通常覆盖多个像素,导致边界像素的激活同时受到两侧类别的影响,难以准确分类。 同时,CNN 的多次下采样会平滑边界信息,上采样难以精确恢复。解决方向包括:使用空洞卷积保持高分辨率(DeepLabV3);在边界处使用专门的损失权重(如 boundary loss);使用 U-Net 的多尺度 Skip Connection 保留边界细节;使用 CRF(条件随机场)等后处理做边界精细化。
Q17. FCN-8s 相比 FCN-32s 精度更高的原因是什么?
FCN-8s 融合了更浅层的高分辨率特征图,这些特征图包含更精确的空间位置信息,能恢复更多边界细节。 FCN-32s 一步上采样 32 倍,信息损失严重。FCN-16s 融合 1/16 和 1/32 特征图后上采样 16 倍。FCN-8s 进一步融合 1/8 特征图,上采样仅 8 倍,空间精度最高。以 224×224 图像为例:FCN-32s 的分割图上采样 32 倍回到原始分辨率,FCN-8s 上采样 8 倍(28→224),中间差值更少,边界更精确。
Q18. Mask R-CNN 与 Faster R-CNN 的核心区别是什么?
Mask R-CNN 在 Faster R-CNN 的检测头基础上增加了并行的分割分支(Mask Head),输出每个候选区的二值分割掩码,实现实例分割。 Faster R-CNN 的检测头输出每个候选区的类别标签和边界框坐标。Mask R-CNN 在此基础上,从 RoI Align 提取的特征上并行接入一个 FCN(通常 4 个 3×3 卷积层),输出 K 个二值掩码(K 为类别数),每个掩码对应一个候选区的某个类别的像素级分割。Mask R-CNN 在 RoI Align 中使用双线性插值(替代 RoI Pooling)保证了像素级分割的空间对齐精度。
Q19. 目标检测中的 AP(Average Precision)是如何计算的?
AP 是 Precision-Recall 曲线下面积,衡量检测器在不同置信度阈值下的整体精度。 计算步骤:对所有检测框按置信度降序排列 → 按序逐一加入,绘制 PR 曲线(每个检测框要么是 TP(与 GT 的 IoU > 阈值),要么是 FP)→ AP = PR 曲线下面积。典型阈值:IoU=0.5(AP@.5)和 IoU∈[0.5:0.95](AP@[.5:.95])。COCO 数据集使用 AP@[.5:.95] 作为主要评测指标,mAP 是所有类别 AP 的平均值。
Q20. 为什么 DeepLabV3 的 ASPP 模块需要多个不同的空洞率?
不同尺度的物体需要不同的空洞率来捕捉,多个分支并行可以同时覆盖大、中、小不同尺度的语义信息。 空洞卷积的感受野公式为 k + (k-1)×(d-1)。对于 3×3 卷积核(k=3),torchvision 默认 ASPP 三个分支的空洞率分别为 d=12、d=24、d=36,对应感受野为:d=12 时 3+2×11=25,d=24 时 3+2×23=49,d=36 时 3+2×35=73。大物体(如建筑物)需要大空洞率(d=36,感受野约 73 像素)才能覆盖整体;小物体(如交通标志)需要小空洞率(d=12,感受野约 25 像素)才能保持局部精度。ASPP 通过 dilation=(12, 24, 36) 的并行分支,类似于空间金字塔池化(SPP),使网络能够同时处理多尺度的语义信息。注:原始 DeepLabV3 论文使用 (6, 12, 18),torchvision 实现调整为 (12, 24, 36),两者原理一致,只是感受野整体扩大了 2 倍。
FAQ 总结
- RoI Align:双线性插值,消除量化误差,替代 RoI Pooling
- Anchor + IoU:预设候选框,按 IoU 匹配真值框筛选正负样本
- Focal Loss:自动降低易分类样本权重,解决单阶段类别不平衡
- FCN 全卷积化:1×1 卷积替换 FC,输出空间分割图而非向量
- 空洞卷积:dilation>1 扩大感受野,不下采样保留分辨率
- U-Net Skip Concat:编码器特征与解码器拼接,保留空间细节
- Grad-CAM:梯度加权特征图,可用于任意 CNN 的可解释性分析
十一、Roadmap 预告
下篇预告:《注意力机制与 Transformer:在视觉任务中超越 CNN》
从 ResNet 到 Faster R-CNN,我们见证了 CNN 在视觉任务中的强大能力。但近年来,注意力机制和 Vision Transformer(ViT)正在逐步超越传统 CNN,成为新的主流架构:
- Vision Transformer(ViT):将图像切分为 Patch,Transformer 编码器替代 CNN 主干
- Swin Transformer:层次化 Transformer,多尺度特征自然适配下游任务
- DETR:端到端目标检测,Transformer 编码器-解码器架构,无需 Anchor 和 NMS
- SegFormer:基于 Transformer 的语义分割,高效且高精度
- CLIP / 视觉-语言模型:将视觉理解和自然语言统一建模
敬请期待!