卷积神经网络(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 冠军)
关键技术:
- ReLU 激活: 比 Sigmoid 快 6 倍训练
- Dropout (0.5): 防止过拟合
- 数据增强: 随机裁剪、翻转、颜色抖动
- 双 GPU 并行: 分组卷积雏形
- 局部响应归一化(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×1 卷积: 降维减少计算量(bottleneck)
- 并行分支: 网络自动学习特征组合
参数量: 仅 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))
数学表达:
其中 是残差映射,比直接学习 更容易。
为什么有效?
- 梯度高速公路: 反向传播时梯度可直接流经恒等映射
- 易于优化: 学习残差 比学习 简单
- 深度可扩展: 训练 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-18 | 18 | 11M | ~10% |
| ResNet-34 | 34 | 21M | ~8% |
| ResNet-50 | 50 | 26M | 6.71% |
| ResNet-101 | 101 | 45M | 6.44% |
| ResNet-152 | 152 | 60M | 6.16% |
影响: 成为 CV 领域默认基线,几乎所有任务都使用 ResNet 主干。
2.6 DenseNet (2017) - 密集连接
作者: Gao Huang et al. (康奈尔/清华)
创新: 每层连接到前面所有层
密集块(Dense Block):
层1 输出: x1
层2 输入: [x0, x1] → 输出: x2
层3 输入: [x0, x1, x2] → 输出: x3
...
数学表达:
优势:
- 特征复用: 所有层共享特征
- 参数高效: 比 ResNet 少 50% 参数
- 梯度流动: 每层都有直接监督
挑战:
- 显存占用高(需存储所有中间特征)
- 推理速度慢
应用: 参数受限场景,医学图像分析
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 系列:
| 模型 | 参数量 | FLOPs | Top-1 准确率 |
|---|---|---|---|
| EfficientNet-B0 | 5.3M | 0.4B | 77.1% |
| EfficientNet-B7 | 66M | 37B | 84.4% |
| EfficientNet v2-L | 120M | 56B | 85.7% |
优势:
- 参数效率极高
- 训练速度快(v2 改进)
2.9 ConvNeXt (2022) - CNN 的反击 🔥
作者: Facebook AI Research (FAIR)
动机: Vision Transformer 崛起后,重新审视 CNN 潜力
设计理念: 将 ResNet 逐步 “现代化”,借鉴 Swin Transformer 设计
改进步骤:
- 训练策略: AdamW + 数据增强(Mixup, Cutmix, RandAugment)
- 宏观设计: 改变 stage 比例 (3:4:6:3 → 3:3:9:3)
- ResNeXt 化: 使用分组卷积(depthwise)
- 倒瓶颈: 先扩展再压缩(类似 Transformer FFN)
- 大卷积核: 7×7 深度卷积(提升全局感受野)
- 各种微调:
- 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):
| 模型 | 参数量 | FLOPs | Top-1 准确率 |
|---|---|---|---|
| ResNet-50 | 25M | 4.1G | 76.2% |
| Swin-T (ViT) | 29M | 4.5G | 81.3% |
| ConvNeXt-T | 29M | 4.5G | 82.1% |
| Swin-B | 88M | 15.4G | 83.5% |
| ConvNeXt-B | 89M | 15.4G | 83.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) | 7 | 49 | AlexNet |
| 小卷积堆叠 (3×3×2) | 5 | 18 | VGG |
| 空洞卷积 (3×3, d=2) | 7 | 9 | DeepLab |
| 深度可分离 | 取决于堆叠层数 | 极少 | MobileNet |
| 全局池化 + 注意力 | 全局 | 少 | SENet |
2024 趋势:
- 大核深度卷积(7×7, 11×11)+ 倒瓶颈
- 动态卷积(根据输入调整卷积核)
3.2 降采样策略
演进历程:
- Max/Avg Pooling → 固定操作,无学习能力
- Strided Convolution → 可学习,主流选择
- 分离降采样层 → 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 | 批间归一化,加速收敛 | 大批次 CNN | ResNet |
| Layer Norm | 逐样本归一化,不依赖批次 | Transformer | ConvNeXt |
| 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) * y24.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 核心差异
| 维度 | CNN | Vision Transformer (ViT) |
|---|---|---|
| 归纳偏置 | 强(局部性、平移不变性) | 弱(纯数据驱动) |
| 感受野 | 逐层增长 | 第一层即全局 |
| 数据需求 | 中等(100K-1M 图像可训练) | 大(需 100M+ 或强预训练) |
| 计算复杂度 | (标准注意力) | |
| 推理速度 | 快 | 慢(但可优化) |
| 参数效率 | 高 | 低(大模型才有优势) |
| 可解释性 | 中(特征图可视化) | 高(注意力图) |
| 迁移学习 | 好 | 更好(自监督预训练) |
5.2 性能对比(ImageNet-1K)
| 模型类型 | 代表模型 | 参数量 | Top-1 准确率 | 推理速度 (GPU) |
|---|---|---|---|---|
| 经典 CNN | ResNet-50 | 25M | 76.2% | ~5ms |
| 现代 CNN | ConvNeXt-B | 89M | 83.8% | ~8ms |
| 混合架构 | CoAtNet-3 | 168M | 84.5% | ~12ms |
| 纯 ViT | ViT-B/16 | 86M | 77.9% | ~15ms |
| 分层 ViT | Swin-B | 88M | 83.5% | ~10ms |
| 高效 ViT | EfficientViT | 28M | 82.1% | ~7ms |
5.3 何时选择 CNN?
✅ 推荐使用 CNN 的场景:
- 数据量有限 (< 10万图像)
- 推理效率优先 (边缘设备、实时系统)
- 参数预算紧张 (< 50M 参数)
- 图像尺寸小 (< 224×224)
- 密集预测任务 (分割、检测)
✅ 推荐使用 ViT 的场景:
- 大规模数据 (> 百万图像)
- 超大模型 (> 1B 参数)
- 多模态任务 (CLIP 风格)
- 自监督预训练
- 高分辨率图像 (> 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 | 4× | < 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 迁移学习最佳实践
步骤:
-
选择预训练模型
import timm model = timm.create_model('convnext_base', pretrained=True, num_classes=num_classes) -
冻结策略
# 冻结前 N 层 for name, param in model.named_parameters(): if 'head' not in name: # 不冻结分类头 param.requires_grad = False -
学习率设置
- 主干网络:base_lr × 0.1
- 新增层:base_lr × 1.0
-
渐进解冻
- Epoch 1-10:冻结所有层
- Epoch 11-20:解冻最后一个 stage
- Epoch 21+:全网络微调
7.3 数据集大小与架构选择
| 数据集大小 | 推荐架构 | 训练策略 |
|---|---|---|
| < 1K | ResNet-18 预训练 | 强数据增强 + 冻结大部分层 |
| 1K - 10K | ResNet-50 预训练 | 适度数据增强 + 微调 |
| 10K - 100K | EfficientNet-B3 | 标准增强 + 全量微调 |
| 100K - 1M | ConvNeXt-Small | 从头训练或微调 |
| > 1M | ConvNeXt-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 out8.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 开源资源
模型库:
- timm (PyTorch Image Models) - 700+ 预训练模型
- TorchVision - PyTorch 官方
- MMClassification - OpenMMLab
教程:
- CS231n: CNNs for Visual Recognition - Stanford
- Dive into Deep Learning - 交互式教材
10. 相关笔记
- 神经网络 - 总览
- 神经网络层类型详解 - 层的详细说明
- Transformer - 注意力机制
- 循环神经网络 - RNN/LSTM/GRU(待创建)
- 图神经网络 - GNN(待创建)
- 目标检测 - YOLO/Faster R-CNN
- 语义分割 - U-Net/DeepLab
最后更新: 2025-01-07
维护者: sean2077