跳转至

模块M02: 计算机视觉基础

阶段: Stage 4 - 深度学习 预计学习时间: 3-4小时(理论)+ 3-4小时(实践) 难度: ⭐⭐⭐⭐ 中高等


📚 学习目标

完成本模块后,你将能够:

  • ✅ 理解卷积神经网络(CNN)的核心原理与架构设计
  • ✅ 掌握卷积层、池化层、全连接层的作用与计算过程
  • ✅ 熟悉经典CNN架构(LeNet、AlexNet、VGG、ResNet、EfficientNet)
  • ✅ 理解目标检测的核心概念(YOLO、Faster R-CNN、DETR)
  • ✅ 掌握图像分割技术(U-Net、Mask R-CNN、Segment Anything)
  • ✅ 能够使用预训练模型进行迁移学习与模型微调

🎯 核心知识点

1. 卷积神经网络 (CNN) 基础

1.1 为什么需要 CNN?

传统的全连接神经网络在处理图像时存在以下问题:

问题示例

输入图像: 224×224×3 (RGB) = 150,528 像素
全连接层: 150,528 × 1000 = 1.5亿个参数!

CNN 的三大优势: 1. 参数共享 (Parameter Sharing): 同一个卷积核在整个图像上滑动 2. 局部连接 (Local Connectivity): 每个神经元只连接局部区域 3. 平移不变性 (Translation Invariance): 对物体位置不敏感

1.2 卷积层 (Convolutional Layer)

卷积操作是 CNN 的核心,用于提取图像特征。

数学定义:

Output[i,j] = Σ Σ Input[i+m, j+n] · Kernel[m,n] + bias
             m n

参数说明: - Kernel Size: 卷积核大小(常用 3×3, 5×5, 7×7) - Stride: 步长(默认1,影响输出尺寸) - Padding: 填充('SAME'保持尺寸,'VALID'不填充) - Channels: 输入/输出通道数

输出尺寸计算:

Output_height = (Input_height - Kernel_height + 2 × Padding) / Stride + 1
Output_width  = (Input_width  - Kernel_width  + 2 × Padding) / Stride + 1

示例

import torch.nn as nn

# 输入: (batch_size, 3, 224, 224)
conv = nn.Conv2d(
    in_channels=3,      # RGB 3通道
    out_channels=64,    # 输出64个特征图
    kernel_size=3,      # 3×3卷积核
    stride=1,           # 步长1
    padding=1           # 填充1(保持尺寸)
)
# 输出: (batch_size, 64, 224, 224)

特征提取层次: - 浅层卷积: 检测边缘、角点、纹理 - 中层卷积: 检测形状、部件(眼睛、轮子) - 深层卷积: 检测复杂对象(人脸、汽车)

可视化:参见 notebooks/stage4/03-cnn-image-classification.ipynb 第1节

1.3 池化层 (Pooling Layer)

池化用于降低特征图的空间维度,减少计算量和过拟合。

两种常用池化

池化类型 操作 优点 缺点 适用场景
Max Pooling 取区域最大值 保留显著特征、鲁棒性强 丢失位置信息 目标检测、分类
Average Pooling 取区域平均值 平滑特征、保留背景 特征不明显 全局特征提取

示例

# Max Pooling (2×2窗口,stride=2)
maxpool = nn.MaxPool2d(kernel_size=2, stride=2)
# 输入: (batch, 64, 224, 224)
# 输出: (batch, 64, 112, 112)  # 尺寸减半

1.4 批归一化 (Batch Normalization)

批归一化加速训练、提高稳定性。

公式:

BN(x) = γ · (x - μ) / √(σ² + ε) + β
其中: - μ: 批次均值 - σ²: 批次方差 - γ, β: 可学习参数 - ε: 数值稳定项 (通常1e-5)

优点: - 缓解梯度消失/爆炸 - 允许更大学习率 - 减少对初始化的依赖 - 一定程度的正则化效果

放置位置

# 推荐顺序: Conv → BN → Activation
conv = nn.Conv2d(3, 64, 3, padding=1)
bn = nn.BatchNorm2d(64)
relu = nn.ReLU()
# 前向传播: x → conv(x) → bn(x) → relu(x)


2. 经典 CNN 架构演进

2.1 LeNet-5 (1998)

历史意义: 第一个成功的卷积神经网络,用于手写数字识别(MNIST)。

网络结构:

输入(32×32×1)
Conv(5×5, 6) → AvgPool(2×2) → Conv(5×5, 16) → AvgPool(2×2)
FC(120) → FC(84) → FC(10)

参数量: ~60K 特点: 简单、易训练,现代标准看已过时

2.2 AlexNet (2012)

历史意义: ImageNet 2012冠军,开启深度学习时代。

关键创新: 1. 使用 ReLU 激活函数(替代Sigmoid/Tanh) 2. 引入 Dropout 正则化(防止过拟合) 3. 使用 数据增强(随机裁剪、翻转) 4. GPU并行训练(双GPU架构)

网络结构:

输入(224×224×3)
Conv(11×11, 96, stride=4) → MaxPool(3×3, stride=2)
Conv(5×5, 256) → MaxPool(3×3, stride=2)
Conv(3×3, 384) → Conv(3×3, 384) → Conv(3×3, 256)
MaxPool(3×3, stride=2) → FC(4096) → Dropout(0.5)
FC(4096) → Dropout(0.5) → FC(1000)

参数量: ~60M ImageNet Top-5错误率: 15.3% (2012年)

2.3 VGG-16/19 (2014)

核心思想: "更深" + "小卷积核"(全部用3×3)

为什么用小卷积核? - 2个3×3卷积 = 1个5×5卷积(感受野相同) - 但参数更少:2×(3×3) = 18 < 1×(5×5) = 25 - 更多非线性(2个ReLU vs 1个ReLU)

VGG-16 结构:

输入(224×224×3)
Conv3-64 × 2 → MaxPool
Conv3-128 × 2 → MaxPool
Conv3-256 × 3 → MaxPool
Conv3-512 × 3 → MaxPool
Conv3-512 × 3 → MaxPool
FC(4096) → FC(4096) → FC(1000)

参数量: VGG-16 ~138M, VGG-19 ~144M ImageNet Top-5错误率: 7.3%

缺点: 参数量巨大、训练慢、显存占用高

2.4 ResNet (2015)

核心创新: 残差连接 (Residual Connection),解决深层网络退化问题。

残差块 (Residual Block):

         输入 x
      ┌───────┐
      │  恒等  │
      └───────┘
    ┌─────────────┐
    │ Conv-BN-ReLU│
    │ Conv-BN     │
    └─────────────┘
        x + F(x)  ← 残差连接
        ReLU

数学表达:

y = F(x) + x
其中 F(x) 是残差映射(2-3层卷积)。

为什么有效? - 如果恒等映射最优,网络只需学习 F(x) = 0 - 梯度可以直接通过快捷连接传播,缓解梯度消失

ResNet家族: | 模型 | 层数 | 参数量 | ImageNet Top-5 错误率 | |------|------|--------|---------------------| | ResNet-18 | 18 | 11.7M | ~10.8% | | ResNet-34 | 34 | 21.8M | ~9.2% | | ResNet-50 | 50 | 25.6M | ~7.1% | | ResNet-101 | 101 | 44.5M | ~6.4% | | ResNet-152 | 152 | 60.2M | ~5.7% |

代码示例:

class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, 3, stride, padding=1)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)

        # 快捷连接(如果维度不匹配,需要1×1卷积调整)
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, 1, stride),
                nn.BatchNorm2d(out_channels)
            )
        else:
            self.shortcut = nn.Identity()

    def forward(self, x):
        residual = self.shortcut(x)
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += residual  # 残差连接
        return F.relu(out)

2.5 EfficientNet (2019)

核心思想: 复合缩放 (Compound Scaling) - 同时调整深度、宽度、分辨率。

传统方法 vs 复合缩放:

传统方法:
- 加深度: ResNet-50 → ResNet-101
- 加宽度: ResNet-50 (width×1.5)
- 加分辨率: 224×224 → 299×299

复合缩放:
depth = α^φ, width = β^φ, resolution = γ^φ
约束: α · β² · γ² ≈ 2

EfficientNet家族: | 模型 | 参数量 | ImageNet Top-1准确率 | 推理速度 | |------|--------|---------------------|---------| | EfficientNet-B0 | 5.3M | 77.1% | 基准 | | EfficientNet-B1 | 7.8M | 79.1% | 1.2× | | EfficientNet-B3 | 12M | 81.6% | 1.8× | | EfficientNet-B7 | 66M | 84.3% | 6.1× |

优势: 在相同计算量下准确率更高,或在相同准确率下速度更快。


3. 目标检测 (Object Detection)

目标检测 = 分类 + 定位

输出格式:

[
    {"class": "cat", "bbox": [x1, y1, x2, y2], "confidence": 0.95},
    {"class": "dog", "bbox": [x3, y3, x4, y4], "confidence": 0.88}
]

3.1 两阶段检测器: Faster R-CNN

流程:

输入图像
特征提取网络 (Backbone: ResNet/VGG)
RPN (区域建议网络) → 生成候选框
ROI Pooling → 对齐特征
分类 + 边界框回归

优点: 准确率高 缺点: 速度慢(~5 FPS)

3.2 单阶段检测器: YOLO 系列

核心思想: 将检测问题转化为回归问题,一次性预测所有边界框。

YOLO v1-v11 演进: | 版本 | 发布年份 | 关键创新 | mAP@0.5 | 速度 | |------|---------|---------|---------|------| | YOLOv1 | 2016 | 单阶段检测 | 63.4% | 45 FPS | | YOLOv3 | 2018 | 多尺度预测 | 57.9% | 30 FPS | | YOLOv5 | 2020 | 工程优化 | 65.8% | 140 FPS | | YOLOv8 | 2023 | Anchor-free | 53.9% | 80 FPS | | YOLOv11 | 2024 | Transformer融合 | 54.7% | 90+ FPS |

YOLOv11 架构:

输入(640×640×3)
CSPDarknet53 Backbone
PAN-FPN (特征金字塔)
Decoupled Head (解耦头)
  ├─ 分类分支
  └─ 回归分支

应用场景: 实时视频检测、自动驾驶、工业质检

3.3 基于Transformer: DETR

核心创新: 将目标检测视为 集合预测问题,无需NMS后处理。

架构:

输入图像 → CNN Backbone → Flatten
Transformer Encoder
Transformer Decoder (N个查询向量)
并行预测N个对象(class + bbox)

优点: 端到端训练、无需手工设计anchor 缺点: 训练慢、小目标检测效果一般


4. 图像分割 (Image Segmentation)

4.1 语义分割 (Semantic Segmentation)

目标: 为每个像素分配类别标签(不区分实例)

经典架构: U-Net

编码器(下采样)          解码器(上采样)
     ↓                       ↑
Conv-Pool ─────跳跃连接───→ UpConv-Concat
     ↓                       ↑
Conv-Pool ─────跳跃连接───→ UpConv-Concat
     ↓                       ↑
Conv-Pool                UpConv-Concat

跳跃连接作用: 融合高分辨率浅层特征与高语义深层特征。

应用: 医学影像分割、自动驾驶场景理解

4.2 实例分割 (Instance Segmentation)

目标: 区分同类别的不同实例

Mask R-CNN:

Faster R-CNN
  ├─ 分类分支
  ├─ 边界框回归分支
  └─ 掩码分支 (FCN) ← 新增

输出: 每个实例的类别 + 边界框 + 像素级掩码

4.3 全景分割 (Panoptic Segmentation)

目标: 语义分割 + 实例分割的统一

应用: 自动驾驶(需要识别道路、行人、车辆等)

4.4 最新进展: Segment Anything (SAM)

核心思想: 零样本分割,输入提示(点/框/文本)即可分割任意对象。

架构:

图像编码器 (ViT) + 提示编码器 + 掩码解码器

应用: 交互式标注、视频对象分割


5. 迁移学习 (Transfer Learning)

5.1 为什么需要迁移学习?

挑战: - 从零训练大型CNN需要百万级标注数据 - 训练时间长(几天到几周) - 计算资源昂贵(多卡GPU)

解决方案: 使用在ImageNet上预训练的模型,迁移到目标任务。

5.2 迁移学习策略

策略1: 固定特征提取器(数据量<1000)

# 加载预训练ResNet
model = torchvision.models.resnet50(pretrained=True)

# 冻结所有卷积层
for param in model.parameters():
    param.requires_grad = False

# 只训练最后的全连接层
model.fc = nn.Linear(2048, num_classes)

策略2: 微调 (Fine-tuning)(数据量1000-10000)

model = torchvision.models.resnet50(pretrained=True)

# 冻结前几层,微调后几层
for name, param in model.named_parameters():
    if "layer4" in name or "fc" in name:
        param.requires_grad = True
    else:
        param.requires_grad = False

策略3: 全网络微调(数据量>10000)

model = torchvision.models.resnet50(pretrained=True)
model.fc = nn.Linear(2048, num_classes)

# 使用较小学习率
optimizer = torch.optim.Adam([
    {'params': model.fc.parameters(), 'lr': 1e-3},       # 新层大学习率
    {'params': model.layer4.parameters(), 'lr': 1e-4},   # 后层中学习率
    {'params': model.layer3.parameters(), 'lr': 1e-5}    # 前层小学习率
])

5.3 预训练模型来源

PyTorch Hub:

import torch
model = torch.hub.load('pytorch/vision:v0.10.0', 'resnet50', pretrained=True)

Torchvision Models:

from torchvision import models
resnet50 = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
efficientnet_b0 = models.efficientnet_b0(weights=models.EfficientNet_B0_Weights.IMAGENET1K_V1)

Hugging Face Transformers (Vision Transformer):

from transformers import ViTModel
model = ViTModel.from_pretrained('google/vit-base-patch16-224')


🛠️ 实践环节

任务1: 手写CNN图像分类器

目标: 从零实现一个CNN,在CIFAR-10上达到70%+准确率

关键代码 (notebooks/stage4/03-cnn-image-classification.ipynb 第3节):

class SimpleCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 32, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2),

            nn.Conv2d(32, 64, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2),

            nn.Conv2d(64, 128, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(128 * 4 * 4, 256),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(256, num_classes)
        )

    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        return x

任务2: 使用预训练模型进行迁移学习

目标: 在自定义数据集上微调ResNet-50,超越从零训练的模型

步骤: 1. 加载预训练ResNet-50 2. 替换最后的全连接层 3. 冻结前80%的层 4. 使用小学习率微调 5. 对比从零训练 vs 迁移学习的收敛速度

预期结果: - 从零训练: 需要50+ epochs达到60%准确率 - 迁移学习: 5 epochs即可达到80%准确率

任务3: 可视化CNN特征图

目标: 理解CNN每层学到的特征

可视化内容: - 第1层: 边缘检测器(水平、垂直、对角线) - 第3层: 纹理检测器 - 第5层: 形状检测器 - 最后一层: 高级语义特征

代码:

# 提取中间层特征
activations = {}
def get_activation(name):
    def hook(model, input, output):
        activations[name] = output.detach()
    return hook

model.layer1.register_forward_hook(get_activation('layer1'))
model.layer2.register_forward_hook(get_activation('layer2'))

# 前向传播
_ = model(image)

# 可视化
plt.imshow(activations['layer1'][0, 0].cpu(), cmap='viridis')


📖 扩展阅读

经典论文

  1. ImageNet Classification with Deep CNNs (AlexNet, 2012)
  2. 链接: https://papers.nips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf
  3. 阅读时间: 1小时

  4. Deep Residual Learning for Image Recognition (ResNet, 2015)

  5. 链接: https://arxiv.org/abs/1512.03385
  6. 阅读时间: 1.5小时

  7. You Only Look Once: Unified, Real-Time Object Detection (YOLOv1, 2016)

  8. 链接: https://arxiv.org/abs/1506.02640
  9. 阅读时间: 1小时

  10. U-Net: Convolutional Networks for Biomedical Image Segmentation (2015)

  11. 链接: https://arxiv.org/abs/1505.04597
  12. 阅读时间: 45分钟

在线资源

实战项目推荐

完成本模块后,建议尝试以下项目:


❓ 常见问题 (FAQ)

Q1: CNN 为什么比全连接网络更适合处理图像?

A: 三个核心原因: 1. 参数共享: 同一个卷积核在整个图像上复用,减少参数量 2. 局部连接: 利用图像的空间局部性,每个神经元只关注局部区域 3. 平移不变性: 对物体在图像中的位置不敏感

Q2: 如何选择合适的CNN架构?

A: 根据应用场景选择: - 移动端/边缘设备: EfficientNet-B0, MobileNetV3 (参数量<10M) - 服务器端高精度: ResNet-101, EfficientNet-B7 (准确率优先) - 实时检测: YOLOv8, YOLOv11 (速度优先) - 通用场景: ResNet-50 (性能与速度平衡)

Q3: 迁移学习什么时候效果好?

A: 满足以下条件效果最佳: 1. 数据量小: <10,000张图像 2. 任务相似: 目标任务与ImageNet类似(都是自然图像) 3. 类别相关: 如ImageNet包含"狗"类别,迁移到狗种识别效果好

不适用场景: - 医学影像(X光、CT): 与自然图像差异大 - 卫星遥感图像: 与ImageNet差异大 - 建议: 寻找领域内的预训练模型(如ChexNet for医疗)

Q4: 如何调试CNN模型?

A: 5步排查法: 1. 过拟合单个batch: 确保模型有足够容量 2. 检查梯度: 使用torch.autograd.grad_check 3. 可视化特征图: 检查是否学到有意义的特征 4. 学习率调优: 使用学习率查找器(LR Finder) 5. 数据增强: 防止过拟合

Q5: 为什么模型在验证集上准确率很低?

A: 可能原因: 1. 过拟合: 训练集准确率高但验证集低 → 增加Dropout/正则化 2. 欠拟合: 训练集准确率也低 → 增加模型容量/训练更久 3. 数据泄露: 验证集分布与训练集不同 → 检查数据划分 4. 学习率太大: Loss震荡不收敛 → 降低学习率


✅ 学习检查清单

完成本模块后,你应该能够:

  • 解释卷积操作的数学原理并手动计算输出尺寸
  • 说出至少3个经典CNN架构及其关键创新点
  • 实现一个包含卷积层、池化层、BN层的简单CNN
  • 使用预训练ResNet-50进行迁移学习
  • 可视化CNN不同层的特征图
  • 解释残差连接为什么能解决梯度消失问题
  • 区分语义分割、实例分割、全景分割
  • 比较YOLO与Faster R-CNN的优缺点

⏭️ 下一步

完成本模块后,你可以:

  1. 继续学习: 模块M03: 自然语言处理基础
  2. 实战项目: 从项目列表中选择感兴趣的项目开始实践
  3. 深入研究: 阅读经典论文,理解最新技术进展

准备好了吗?打开 03-cnn-image-classification.ipynb 开始动手实践! 🚀