卷积神经网络(CNN)架构演进

核心定位: 计算机视觉领域的基石架构,从 1998 年 LeNet 到 2024 年的现代变体。 关联笔记: 神经网络神经网络层类型详解Transformer


1. CNN 核心原理

1.1 三大核心思想

1.1.1 局部连接(Local Connectivity)

传统全连接的问题:

  • 224×224 RGB 图像 = 150,528 输入维度
  • 连接到 1000 个神经元 = 150M 参数(仅第一层!)

卷积解决方案:

  • 3×3 卷积核 = 仅 9 个权重
  • 在整个图像上共享 → 27 个参数(3×3×3 通道)

感受野(Receptive Field):

  • 浅层:检测边缘、纹理
  • 中层:检测部件(眼睛、轮子)
  • 深层:检测完整对象(人脸、汽车)

1.1.2 权重共享(Weight Sharing)

核心假设: 图像的统计特性在空间上是平移不变的。

优势:

  • 参数量减少 1000× 以上
  • 天然的数据增强(平移不变性)
  • 更好的泛化能力

1.1.3 层次化表征(Hierarchical Representation)

低级特征 (浅层)          中级特征 (中层)        高级特征 (深层)
边缘、颜色   →   纹理、简单形状   →   部件、对象
[Conv 1-2]       [Conv 3-4]           [Conv 5+]

1.2 标准 CNN 架构组件

输入图像 (H×W×3)
    ↓
[卷积层] → 特征提取
    ↓
[批归一化] → 加速收敛
    ↓
[激活函数] → 非线性
    ↓
[池化层] → 降采样
    ↓
[重复多次]
    ↓
[全局池化 / 展平]
    ↓
[全连接层] → 分类
    ↓
输出 (类别概率)

1.3 关键操作详解

池化层(Pooling)

类型操作优点缺点现代趋势
Max Pooling取窗口最大值保留显著特征丢失位置信息逐渐被淘汰
Avg Pooling取窗口平均值平滑特征弱化显著特征全局池化常用
Stride Conv步长=2 的卷积可学习降采样参数略多主流选择
空间金字塔多尺度池化多尺度特征融合计算复杂特定任务使用

2024 趋势: 使用 stride=2 卷积 替代池化,让网络自己学习最优降采样。


2. 经典架构演进史

2.1 LeNet-5 (1998) - 起源

作者: Yann LeCun
数据集: MNIST 手写数字
创新: 首次将卷积、池化、全连接组合成端到端系统

架构:

输入 (32×32×1)
  ↓
Conv (5×5, 6 filters) → Avg Pool
  ↓
Conv (5×5, 16 filters) → Avg Pool
  ↓
FC (120) → FC (84) → FC (10)

参数量: ~60K
特点: Sigmoid 激活、平均池化

历史意义: 证明了 CNN 的可行性,但因计算资源限制未广泛应用。


2.2 AlexNet (2012) - 深度学习爆发

作者: Alex Krizhevsky, Ilya Sutskever, Geoffrey Hinton
数据集: ImageNet (1.2M 图像, 1000 类)
创新: GPU 训练、ReLU、Dropout、数据增强

架构:

输入 (224×224×3)
  ↓
Conv1 (11×11, stride=4, 96) → ReLU → MaxPool → LRN
  ↓
Conv2 (5×5, 256) → ReLU → MaxPool → LRN
  ↓
Conv3 (3×3, 384) → ReLU
Conv4 (3×3, 384) → ReLU
Conv5 (3×3, 256) → ReLU → MaxPool
  ↓
FC (4096) → Dropout → FC (4096) → Dropout → FC (1000)

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

关键技术:

  1. ReLU 激活: 比 Sigmoid 快 6 倍训练
  2. Dropout (0.5): 防止过拟合
  3. 数据增强: 随机裁剪、翻转、颜色抖动
  4. 双 GPU 并行: 分组卷积雏形
  5. 局部响应归一化(LRN): 后被 BatchNorm 取代

影响: 开启深度学习时代,证明深层网络 + 大数据 + GPU 的潜力。


2.3 VGGNet (2014) - 深度与简洁

作者: Karen Simonyan, Andrew Zisserman (牛津大学)
创新: 统一使用 3×3 卷积,网络深度达到 16-19 层

设计哲学:

  • 小卷积核: 全部使用 3×3 (偶尔 1×1)
  • 深层堆叠: 两个 3×3 等效于一个 5×5 感受野,但参数更少
  • 规则结构: 每个阶段通道数翻倍,分辨率减半

VGG-16 架构:

[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

参数量: 138M (非常大!)
Top-5 错误率: 7.3%

优势:

  • 架构简单,易于理解和复现
  • 通用特征提取能力强(迁移学习常用)

缺点:

  • 参数量巨大,推理慢
  • 全连接层占 90% 参数

现代改进: 用全局平均池化替代 FC 层(GoogLeNet 启发)。


2.4 GoogLeNet / Inception v1 (2014) - 多尺度特征

作者: Christian Szegedy et al. (Google)
创新: Inception 模块,多尺度并行卷积

Inception 模块:

输入
 ├─→ 1×1 Conv ─────────────────────┐
 ├─→ 1×1 Conv → 3×3 Conv ──────────┤
 ├─→ 1×1 Conv → 5×5 Conv ──────────┤→ Concat → 输出
 └─→ 3×3 MaxPool → 1×1 Conv ───────┘

关键思想:

  1. 多尺度: 同时捕获不同尺度特征
  2. 1×1 卷积: 降维减少计算量(bottleneck)
  3. 并行分支: 网络自动学习特征组合

参数量: 仅 5M(比 VGG 少 27 倍!)
Top-5 错误率: 6.67%

辅助分类器:

  • 中间层添加分类头,缓解梯度消失
  • 推理时移除

后续版本:

  • Inception v2/v3: 分解卷积(7×7 → 1×7 + 7×1),BatchNorm
  • Inception v4: 结合 ResNet 思想
  • Inception-ResNet: 残差连接 + Inception 模块

2.5 ResNet (2015) - 残差革命 ⭐

作者: Kaiming He et al. (微软亚洲研究院)
论文: “Deep Residual Learning for Image Recognition” (CVPR 2016 最佳论文)
创新: 残差连接,突破深度瓶颈

核心问题:退化现象(Degradation)

  • 网络越深,训练误差反而上升(非过拟合)
  • 原因:梯度消失 + 优化困难

残差块(Residual Block):

输入 x
   ↓
   ├─────────────────→ (恒等映射)
   ↓                     ↓
Conv → BN → ReLU     [相加]
   ↓                     ↓
Conv → BN        ──→  输出 (x + F(x))

数学表达:

其中 是残差映射,比直接学习 更容易。

为什么有效?

  1. 梯度高速公路: 反向传播时梯度可直接流经恒等映射
  2. 易于优化: 学习残差 比学习 简单
  3. 深度可扩展: 训练 1000+ 层成为可能

ResNet-50 架构:

Conv1 (7×7, 64, stride=2) → MaxPool
  ↓
[残差块] × 3  (64 通道)
[残差块] × 4  (128 通道, stride=2 降采样)
[残差块] × 6  (256 通道, stride=2 降采样)
[残差块] × 3  (512 通道, stride=2 降采样)
  ↓
Global Avg Pool → FC (1000)

瓶颈设计(Bottleneck Block):

1×1 Conv (降维) → 3×3 Conv → 1×1 Conv (升维)
参数量减少 50%,适合深层网络

ResNet 系列:

模型层数参数量Top-5 错误率
ResNet-181811M~10%
ResNet-343421M~8%
ResNet-505026M6.71%
ResNet-10110145M6.44%
ResNet-15215260M6.16%

影响: 成为 CV 领域默认基线,几乎所有任务都使用 ResNet 主干。


2.6 DenseNet (2017) - 密集连接

作者: Gao Huang et al. (康奈尔/清华)
创新: 每层连接到前面所有层

密集块(Dense Block):

层1 输出: x1
层2 输入: [x0, x1]  → 输出: x2
层3 输入: [x0, x1, x2] → 输出: x3
...

数学表达:

优势:

  1. 特征复用: 所有层共享特征
  2. 参数高效: 比 ResNet 少 50% 参数
  3. 梯度流动: 每层都有直接监督

挑战:

  • 显存占用高(需存储所有中间特征)
  • 推理速度慢

应用: 参数受限场景,医学图像分析


2.7 MobileNet 系列 (2017-2019) - 移动端优化

目标: 在手机、嵌入式设备上运行

MobileNet v1 (2017)

核心: 深度可分离卷积(Depthwise Separable Convolution)

参数量对比:

  • 标准 3×3 卷积:
  • 深度可分离:
  • 减少 8-9 倍参数!

MobileNet v2 (2018)

创新: 倒残差结构(Inverted Residual)+ 线性瓶颈

标准残差块:  宽 → 窄 → 宽
倒残差块:    窄 → 宽 → 窄
             ↓
        1×1 扩展 (6×)
             ↓
        3×3 Depthwise
             ↓
        1×1 投影 (线性激活)

为什么 “倒” ?

  • 低维特征保留在残差路径
  • 高维空间做非线性变换

MobileNet v3 (2019)

优化:

  • NAS(神经架构搜索)自动设计
  • h-swish 激活函数(比 ReLU 精度高,比 Swish 快)
  • SE 模块(Squeeze-and-Excitation)

性能:

  • MobileNet v3-Large: 5.4M 参数,75% ImageNet 准确率
  • 比 v2 快 15%,精度提升 3%

2.8 EfficientNet (2019) - 复合缩放

作者: Mingxing Tan, Quoc Le (Google Brain)
创新: 统一缩放深度、宽度、分辨率

传统扩展问题:

  • 只加深:梯度消失
  • 只加宽:饱和效应
  • 只提高分辨率:特征不足

复合缩放法则: 约束:

EfficientNet-B0 基础架构:

  • MBConv 块(MobileNet v2 改进)
  • Squeeze-and-Excitation (SE) 模块
  • Swish 激活函数

EfficientNet 系列:

模型参数量FLOPsTop-1 准确率
EfficientNet-B05.3M0.4B77.1%
EfficientNet-B766M37B84.4%
EfficientNet v2-L120M56B85.7%

优势:

  • 参数效率极高
  • 训练速度快(v2 改进)

2.9 ConvNeXt (2022) - CNN 的反击 🔥

作者: Facebook AI Research (FAIR)
动机: Vision Transformer 崛起后,重新审视 CNN 潜力

设计理念: 将 ResNet 逐步 “现代化”,借鉴 Swin Transformer 设计

改进步骤:

  1. 训练策略: AdamW + 数据增强(Mixup, Cutmix, RandAugment)
  2. 宏观设计: 改变 stage 比例 (3:4:6:3 → 3:3:9:3)
  3. ResNeXt 化: 使用分组卷积(depthwise)
  4. 倒瓶颈: 先扩展再压缩(类似 Transformer FFN)
  5. 大卷积核: 7×7 深度卷积(提升全局感受野)
  6. 各种微调:
    • ReLU → GELU
    • BatchNorm → LayerNorm
    • 减少激活函数使用
    • 分离降采样层

ConvNeXt-Base 架构:

[Stem] (4×4 Conv, stride=4)
  ↓
[Stage 1] × 3  (96 通道)
[Stage 2] × 3  (192 通道)
[Stage 3] × 9  (384 通道)
[Stage 4] × 3  (768 通道)
  ↓
Global Avg Pool → FC

性能对比(ImageNet-1K):

模型参数量FLOPsTop-1 准确率
ResNet-5025M4.1G76.2%
Swin-T (ViT)29M4.5G81.3%
ConvNeXt-T29M4.5G82.1%
Swin-B88M15.4G83.5%
ConvNeXt-B89M15.4G83.8%

结论: 纯 CNN 架构依然具有竞争力!


2.10 RepVGG (2021) - 训练 / 推理解耦

创新: 训练时多分支,推理时合并为单路

训练时架构:

输入
 ├─→ 3×3 Conv ──┐
 ├─→ 1×1 Conv ──┤→ 相加 → BN → ReLU
 └─→ Identity ──┘

推理时转换:

  • 将三个分支合并为单个 3×3 卷积
  • 利用结构重参数化(Structural Re-parameterization)

优势:

  • 训练:多分支加速收敛
  • 推理:单路极致高效(无跳连开销)

应用: 工业部署(端侧推理)


3. 设计原则与模式

3.1 感受野设计

目标: 在保持参数量的同时扩大感受野

方法感受野参数量代表架构
大卷积核 (7×7)749AlexNet
小卷积堆叠 (3×3×2)518VGG
空洞卷积 (3×3, d=2)79DeepLab
深度可分离取决于堆叠层数极少MobileNet
全局池化 + 注意力全局SENet

2024 趋势:

  • 大核深度卷积(7×7, 11×11)+ 倒瓶颈
  • 动态卷积(根据输入调整卷积核)

3.2 降采样策略

演进历程:

  1. Max/Avg Pooling → 固定操作,无学习能力
  2. Strided Convolution → 可学习,主流选择
  3. 分离降采样层 → ConvNeXt 设计,稳定训练

最佳实践(2024):

# 推荐:分离的降采样层
nn.Sequential(
    nn.LayerNorm(channels),
    nn.Conv2d(channels, channels*2, kernel_size=2, stride=2)
)

3.3 通道数设计

经验法则:

  • 初始:64 或 96
  • 每次降采样:通道数翻倍
  • 深层:512 或 768

通道注意力(Channel Attention):

# Squeeze-and-Excitation (SE) Module
global_pool → FC (压缩 r=16) → ReLU → FC (扩展) → Sigmoid → 逐通道加权

效果: 1% 参数提升 1-2% 准确率


4. 现代优化技术

4.1 归一化技术

方法特点适用场景代表模型
Batch Norm批间归一化,加速收敛大批次 CNNResNet
Layer Norm逐样本归一化,不依赖批次TransformerConvNeXt
Group Norm通道分组归一化小批次YOLO v4
Instance Norm逐通道逐样本归一化风格迁移CycleGAN

2024 推荐:

  • 分类任务:Batch Norm(批次 ≥ 16)
  • 检测/分割:Group Norm(批次小)
  • 迁移到 ViT 风格:Layer Norm

4.2 激活函数

演进:

Sigmoid/Tanh → ReLU → LeakyReLU → ELU → GELU → Swish/SiLU

2024 主流:

  • GELU: Transformer 和现代 CNN 默认
  • SiLU (Swish): 性能略优于 GELU
# GELU (Gaussian Error Linear Unit)
gelu(x) = x * Φ(x)  # Φ 是标准正态分布的累积分布函数
 
# SiLU (Sigmoid Linear Unit)
silu(x) = x * sigmoid(x)

4.3 正则化技术

技术原理效果使用建议
Dropout随机丢弃神经元防过拟合小数据集
DropPath随机丢弃残差块深层网络ViT / 深层 CNN
数据增强Mixup, CutMix, RA大幅提升必用
标签平滑Soft label缓解过信分类任务
权重衰减L2 正则稳定训练AdamW 内置

Mixup 示例:

lambda = Beta(alpha, alpha).sample()
mixed_input = lambda * x1 + (1 - lambda) * x2
mixed_target = lambda * y1 + (1 - lambda) * y2

4.4 训练技巧(2024 最佳实践)

优化器:

  • AdamW (weight_decay=0.05)
  • 学习率:base_lr × batch_size / 256

学习率调度:

# Cosine Annealing with Warmup
warmup_epochs = 5
total_epochs = 300
 
for epoch in range(total_epochs):
    if epoch < warmup_epochs:
        lr = base_lr * (epoch + 1) / warmup_epochs
    else:
        lr = 0.5 * base_lr * (1 + cos(π * (epoch - warmup_epochs) / (total_epochs - warmup_epochs)))

数据增强:

  • RandAugment(自动增强)
  • Mixup (alpha=0.2)
  • CutMix (alpha=1.0)
  • Random Erasing

混合精度训练:

from torch.cuda.amp import autocast, GradScaler
 
scaler = GradScaler()
 
with autocast():
    output = model(input)
    loss = criterion(output, target)
 
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()

5. CNN vs Vision Transformer

5.1 核心差异

维度CNNVision Transformer (ViT)
归纳偏置强(局部性、平移不变性)弱(纯数据驱动)
感受野逐层增长第一层即全局
数据需求中等(100K-1M 图像可训练)大(需 100M+ 或强预训练)
计算复杂度(标准注意力)
推理速度慢(但可优化)
参数效率低(大模型才有优势)
可解释性中(特征图可视化)高(注意力图)
迁移学习更好(自监督预训练)

5.2 性能对比(ImageNet-1K)

模型类型代表模型参数量Top-1 准确率推理速度 (GPU)
经典 CNNResNet-5025M76.2%~5ms
现代 CNNConvNeXt-B89M83.8%~8ms
混合架构CoAtNet-3168M84.5%~12ms
纯 ViTViT-B/1686M77.9%~15ms
分层 ViTSwin-B88M83.5%~10ms
高效 ViTEfficientViT28M82.1%~7ms

5.3 何时选择 CNN?

推荐使用 CNN 的场景:

  1. 数据量有限 (< 10万图像)
  2. 推理效率优先 (边缘设备、实时系统)
  3. 参数预算紧张 (< 50M 参数)
  4. 图像尺寸小 (< 224×224)
  5. 密集预测任务 (分割、检测)

推荐使用 ViT 的场景:

  1. 大规模数据 (> 百万图像)
  2. 超大模型 (> 1B 参数)
  3. 多模态任务 (CLIP 风格)
  4. 自监督预训练
  5. 高分辨率图像 (> 512×512)

5.4 混合架构趋势

CoAtNet (2021): CNN (浅层) + Transformer (深层)

优势:

  • 浅层 CNN:高效提取局部特征
  • 深层 ViT:全局建模与长距离依赖

2024 混合设计:

Stage 1-2: 卷积块 (局部特征)
Stage 3-4: Transformer 块 (全局关系)
输出: 同时具备效率和表达力

6. 2024-2025 前沿进展

6.1 高效架构设计

6.1.1 FastViT / EfficientViT

目标: 将 ViT 推理速度降低到 CNN 水平

技术:

  • 混合 Token Mixer(卷积 + 注意力)
  • 分层降采样
  • 重参数化技巧

6.1.2 RepLKNet (2022)

创新: 超大卷积核(31×31)+ 结构重参数化

发现:

  • 大核深度卷积可以替代注意力
  • 需要特殊初始化和训练策略

6.2 神经架构搜索(NAS)

AutoML 方向:

  • Once-for-All (OFA) 网络
  • 超网训练 + 子网搜索
  • 硬件感知 NAS(针对特定芯片优化)

6.3 自监督学习

对比学习:

  • SimCLR / MoCo / BYOL / SwAV
  • 在无标签数据上预训练

掩码图像建模(MIM):

  • MAE (Masked Autoencoders)
  • 类似 BERT 的预训练方式
  • ViT 主导,但 CNN 也在探索

6.4 模型压缩与部署

技术原理压缩比精度损失适用场景
量化FP32 → INT8< 1%通用
剪枝移除冗余权重2-10×1-3%结构化剪枝
蒸馏大模型教小模型10×+2-5%需训练数据
低秩分解矩阵分解2-3×< 1%卷积层
NAS 搜索自动小型化定制最优需大量资源

TensorRT / ONNX Runtime: 工业部署标配

6.5 长尾分布与鲁棒性

挑战:

  • ImageNet 等数据集类别均衡,真实世界不平衡
  • 对抗样本攻击

解决方案:

  • 类别重采样 / 损失函数加权
  • 对抗训练 (Adversarial Training)
  • 测试时增强 (TTA)

7. 实践指南

7.1 模型选择决策树

问题:选择哪个 CNN 架构?
│
├─ 精度优先 (ImageNet 83%+)
│  ├─ 不限资源 → ConvNeXt-Large / CoAtNet
│  └─ 中等资源 → EfficientNet-B7 / ConvNeXt-Base
│
├─ 速度优先 (推理 < 10ms)
│  ├─ 端侧设备 → MobileNet v3 / EfficientNet-B0
│  └─ 服务器 → ResNet-50 / ConvNeXt-Tiny
│
├─ 平衡性能
│  ├─ 小数据集 → ResNet-50 (预训练) + 数据增强
│  └─ 大数据集 → EfficientNet-B3 / ConvNeXt-Small
│
└─ 特殊需求
   ├─ 实时视频 → RepVGG / YOLO 系列
   ├─ 高分辨率 → EfficientNet (自适应分辨率)
   └─ 可解释性 → ResNet (清晰的特征层次)

7.2 迁移学习最佳实践

步骤:

  1. 选择预训练模型

    import timm
    model = timm.create_model('convnext_base', pretrained=True, num_classes=num_classes)
  2. 冻结策略

    # 冻结前 N 层
    for name, param in model.named_parameters():
        if 'head' not in name:  # 不冻结分类头
            param.requires_grad = False
  3. 学习率设置

    • 主干网络:base_lr × 0.1
    • 新增层:base_lr × 1.0
  4. 渐进解冻

    • Epoch 1-10:冻结所有层
    • Epoch 11-20:解冻最后一个 stage
    • Epoch 21+:全网络微调

7.3 数据集大小与架构选择

数据集大小推荐架构训练策略
< 1KResNet-18 预训练强数据增强 + 冻结大部分层
1K - 10KResNet-50 预训练适度数据增强 + 微调
10K - 100KEfficientNet-B3标准增强 + 全量微调
100K - 1MConvNeXt-Small从头训练或微调
> 1MConvNeXt-Base/Large从头训练

7.4 常见错误与调试

问题可能原因解决方案
训练 loss 不下降学习率过大降低 10× 重试
验证精度远低于训练过拟合增加数据增强 / Dropout
显存溢出批次太大减小批次 + 梯度累积
梯度爆炸/消失初始化/归一化问题使用预训练权重 / 检查 BN
推理速度慢模型过大量化 / 剪枝 / 换轻量模型
预训练权重加载失败类别数不匹配num_classes 参数传入

7.5 性能优化 Checklist

训练阶段:

  • 使用 AMP (自动混合精度)
  • DataLoader num_workers=4+, pin_memory=True
  • 使用 torch.compile() (PyTorch 2.0+)
  • 批次大小尽量大(配合梯度累积)
  • 多 GPU 训练(DDP / FSDP)

推理阶段:

  • 模型转 model.eval()torch.no_grad()
  • 批量推理而非逐张
  • TorchScript / ONNX 导出
  • TensorRT 加速(NVIDIA GPU)
  • 量化(PTQ / QAT)

8. 代码实现示例

8.1 从零构建 ResNet Block

import torch
import torch.nn as nn
 
class BasicBlock(nn.Module):
    """ResNet 基础块(用于 ResNet-18/34)"""
    expansion = 1
    
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, 3, stride, 1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        
        self.conv2 = nn.Conv2d(out_channels, out_channels, 3, 1, 1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)
        
        # 下采样分支(如果需要)
        self.downsample = None
        if stride != 1 or in_channels != out_channels:
            self.downsample = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, 1, stride, bias=False),
                nn.BatchNorm2d(out_channels)
            )
    
    def forward(self, x):
        identity = x
        
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)
        
        out = self.conv2(out)
        out = self.bn2(out)
        
        if self.downsample is not None:
            identity = self.downsample(x)
        
        out += identity  # 残差连接
        out = self.relu(out)
        
        return out
 
 
class BottleneckBlock(nn.Module):
    """ResNet 瓶颈块(用于 ResNet-50/101/152)"""
    expansion = 4
    
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()
        # 1×1 降维
        self.conv1 = nn.Conv2d(in_channels, out_channels, 1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        
        # 3×3 特征提取
        self.conv2 = nn.Conv2d(out_channels, out_channels, 3, stride, 1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)
        
        # 1×1 升维
        self.conv3 = nn.Conv2d(out_channels, out_channels * self.expansion, 1, bias=False)
        self.bn3 = nn.BatchNorm2d(out_channels * self.expansion)
        
        self.relu = nn.ReLU(inplace=True)
        
        self.downsample = None
        if stride != 1 or in_channels != out_channels * self.expansion:
            self.downsample = nn.Sequential(
                nn.Conv2d(in_channels, out_channels * self.expansion, 1, stride, bias=False),
                nn.BatchNorm2d(out_channels * self.expansion)
            )
    
    def forward(self, x):
        identity = x
        
        out = self.relu(self.bn1(self.conv1(x)))
        out = self.relu(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))
        
        if self.downsample is not None:
            identity = self.downsample(x)
        
        out += identity
        out = self.relu(out)
        
        return out

8.2 使用 timm 库快速实验

import timm
import torch
 
# 列出所有可用模型
available_models = timm.list_models('*convnext*', pretrained=True)
print(f"Available models: {len(available_models)}")
 
# 创建模型
model = timm.create_model(
    'convnext_base',
    pretrained=True,
    num_classes=10,  # 自定义类别数
    drop_rate=0.1,   # Dropout
    drop_path_rate=0.1  # DropPath
)
 
# 查看模型信息
print(f"Parameters: {sum(p.numel() for p in model.parameters()) / 1e6:.2f}M")
 
# 推理
x = torch.randn(1, 3, 224, 224)
with torch.no_grad():
    output = model(x)
print(f"Output shape: {output.shape}")
 
# 提取特征(不包括分类头)
feature_extractor = timm.create_model('convnext_base', pretrained=True, num_classes=0)
features = feature_extractor(x)
print(f"Feature shape: {features.shape}")

8.3 完整训练循环

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torch.cuda.amp import autocast, GradScaler
import timm
 
# 1. 准备数据
train_loader = DataLoader(train_dataset, batch_size=128, shuffle=True, num_workers=4)
val_loader = DataLoader(val_dataset, batch_size=256, shuffle=False, num_workers=4)
 
# 2. 创建模型
model = timm.create_model('convnext_tiny', pretrained=True, num_classes=num_classes)
model = model.cuda()
 
# 3. 损失函数和优化器
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
optimizer = optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.05)
 
# 4. 学习率调度
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)
 
# 5. 混合精度训练
scaler = GradScaler()
 
# 6. 训练循环
for epoch in range(epochs):
    model.train()
    train_loss = 0.0
    train_correct = 0
    
    for images, labels in train_loader:
        images, labels = images.cuda(), labels.cuda()
        
        optimizer.zero_grad()
        
        # 混合精度前向传播
        with autocast():
            outputs = model(images)
            loss = criterion(outputs, labels)
        
        # 反向传播
        scaler.scale(loss).backward()
        scaler.step(optimizer)
        scaler.update()
        
        # 统计
        train_loss += loss.item()
        train_correct += (outputs.argmax(1) == labels).sum().item()
    
    # 验证
    model.eval()
    val_loss = 0.0
    val_correct = 0
    
    with torch.no_grad():
        for images, labels in val_loader:
            images, labels = images.cuda(), labels.cuda()
            
            with autocast():
                outputs = model(images)
                loss = criterion(outputs, labels)
            
            val_loss += loss.item()
            val_correct += (outputs.argmax(1) == labels).sum().item()
    
    # 打印结果
    print(f"Epoch {epoch+1}/{epochs}")
    print(f"Train Loss: {train_loss/len(train_loader):.4f}, Acc: {train_correct/len(train_dataset):.4f}")
    print(f"Val Loss: {val_loss/len(val_loader):.4f}, Acc: {val_correct/len(val_dataset):.4f}")
    
    scheduler.step()

9. 扩展阅读

9.1 必读论文

经典:

  • LeNet: “Gradient-Based Learning Applied to Document Recognition” (1998)
  • AlexNet: “ImageNet Classification with Deep CNNs” (2012)
  • VGG: “Very Deep Convolutional Networks” (2014)
  • ResNet: “Deep Residual Learning for Image Recognition” (2015)

现代:

  • EfficientNet: “EfficientNet: Rethinking Model Scaling” (2019)
  • ConvNeXt: “A ConvNet for the 2020s” (2022)
  • RepLKNet: “Scaling Up Your Kernels to 31×31” (2022)

9.2 开源资源

模型库:

教程:


10. 相关笔记


最后更新: 2025-01-07
维护者: sean2077