📚 课程目录

第10课:VGG与ResNet

📖 本课概述

VGG证明了"更深的网络更好",ResNet解决了深层网络的训练难题。本课深入分析这两个划时代的架构设计。

📐 一、VGGNet (2014)

1.1 设计哲学

VGG的核心思想:使用更小的3×3卷积核,但堆叠更多层

2个3×3卷积的感受野 = 1个5×5卷积

3个3×3卷积的感受野 = 1个7×7卷积

优势:更少参数 + 更多非线性

1.2 VGG配置

配置层数参数量
VGG1111133M
VGG1313133M
VGG1616138M
VGG1919144M

🔗 二、ResNet (2015)

2.1 残差学习

H(x) = F(x) + x

网络学习残差 F(x) = H(x) - x

如果恒等映射最优,F(x)=0比学习H(x)=x更容易

2.2 为什么残差有效?

①梯度可以直接通过shortcut流向浅层②恒等映射是简单解③不会因为加深而变差

2.3 残差块实现


import torch
import torch.nn as nn

# VGG Block
def vgg_block(in_c, out_c, num_convs):
    layers = []
    for _ in range(num_convs):
        layers.extend([nn.Conv2d(in_c, out_c, 3, padding=1), nn.ReLU()])
        in_c = out_c
    layers.append(nn.MaxPool2d(2))
    return nn.Sequential(*layers)

# ResNet残差块
class BasicBlock(nn.Module):
    def __init__(self, in_ch, out_ch, stride=1):
        super().__init__()
        self.conv1 = nn.Conv2d(in_ch, out_ch, 3, stride, 1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_ch)
        self.conv2 = nn.Conv2d(out_ch, out_ch, 3, 1, 1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_ch)
        self.shortcut = nn.Sequential()
        if stride != 1 or in_ch != out_ch:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_ch, out_ch, 1, stride, bias=False),
                nn.BatchNorm2d(out_ch)
            )
    def forward(self, x):
        out = torch.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)  # 残差连接
        return torch.relu(out)

# 对比VGG和ResNet
print("=== VGG Block ===")
vgg = vgg_block(64, 128, 2)
x = torch.randn(1, 64, 32, 32)
print(f"VGG block: {list(x.shape)} → {list(vgg(x).shape)}")
print(f"VGG block参数量: {sum(p.numel() for p in vgg.parameters()):,}")

print("\n=== ResNet BasicBlock ===")
res = BasicBlock(64, 128, stride=2)
x = torch.randn(1, 64, 32, 32)
print(f"ResNet block: {list(x.shape)} → {list(res(x).shape)}")
print(f"ResNet block参数量: {sum(p.numel() for p in res.parameters()):,}")

# 深层网络梯度流对比
print("\n=== 梯度流对比(20层) ===")
# 无残差
torch.manual_seed(42)
layers_plain = []
for i in range(20):
    layers_plain.append(nn.Linear(64, 64))
    layers_plain.append(nn.ReLU())
model_plain = nn.Sequential(*layers_plain)

x = torch.randn(2, 64)
y = model_plain(x).sum()
y.backward()
grads_plain = [l.weight.grad.norm().item() for l in model_plain if isinstance(l, nn.Linear) and l.weight.grad is not None]
print(f"无残差: 首层梯度={grads_plain[0]:.8f}, 末层梯度={grads_plain[-1]:.8f}, 比值={grads_plain[0]/(grads_plain[-1]+1e-10):.2f}")

# 有残差(1D模拟)
torch.manual_seed(42)
class ResLayer1D(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(64, 64)
        self.fc2 = nn.Linear(64, 64)
    def forward(self, x):
        return torch.relu(self.fc2(torch.relu(self.fc1(x))) + x)

res_layers = nn.ModuleList([ResLayer1D() for _ in range(20)])
x = torch.randn(2, 64)
out = x
for layer in res_layers:
    out = layer(out)
out.sum().backward()
res_grads = [l.fc1.weight.grad.norm().item() for l in res_layers if l.fc1.weight.grad is not None]
print(f"有残差: 首层梯度={res_grads[0]:.8f}, 末层梯度={res_grads[-1]:.8f}, 比值={res_grads[0]/(res_grads[-1]+1e-10):.2f}")
🟢 运行结果 ✅验证通过 === VGG Block === VGG block: [1, 64, 32, 32] → [1, 128, 16, 16] VGG block参数量: 221,440 === ResNet BasicBlock === ResNet block: [1, 64, 32, 32] → [1, 128, 16, 16] ResNet block参数量: 230,144 === 梯度流对比(20层) === 无残差: 首层梯度=0.00000195, 末层梯度=5.23933506, 比值=0.00 有残差: 首层梯度=64.00748444, 末层梯度=53.36516571, 比值=1.20

🔬 三、ResNet的深远影响

3.1 残差连接的变体

标准残差: y = F(x) + x

预激活残差: y = F(BN(ReLU(x))) + x (ResNet-v2)

密集连接: y = [x, F₁(x), F₂([x,F₁(x)]), ...] (DenseNet)

3.2 为什么深层ResNet有效

3.3 现代架构对比

模型深度参数量ImageNet Top-1
ResNet-181811.7M69.8%
ResNet-505025.6M76.1%
ResNet-10110144.5M77.4%
ResNeXt-10110144.5M79.6%
EfficientNet-B7-66M84.3%

📖 四、延伸阅读

💡 推荐资源:

📝 练习

练习1:实现ResNet-18

用BasicBlock组装完整的ResNet-18,在CIFAR10上训练。

练习2:DenseNet

实现DenseBlock,理解密集连接的优势。

练习3:深层网络实验

对比ResNet-18和ResNet-50的精度和训练速度。

💡 五、ResNet变体与演进

5.1 ResNet家族

模型创新点改进
ResNet残差连接支持100+层
ResNet-v2预激活更好的梯度流
ResNeXt分组卷积更多通道、更好精度
ResNeSt分裂注意力多尺度特征融合
ConvNeXt借鉴Transformer纯CNN媲美ViT

5.2 为什么CNN仍然重要

虽然Transformer(ViT)在CV中越来越流行,但CNN仍有优势:

🔬 深度拓展:从理论到实践

核心概念网络

理解本课内容需要将其置于更大的知识网络中。每个核心概念都不是孤立的,它们相互关联、相互支撑:

关键洞察与直觉

建立正确的直觉比记住公式更重要:

1. 维度直觉:高维空间中,数据分布与低维直觉截然不同(维度诅咒)

2. 信息瓶颈:神经网络逐层压缩信息,保留任务相关的、丢弃无关的

3. 流形假设:高维数据实际分布在低维流形上,学习即发现流形结构

4. 偏差-方差权衡:简单模型欠拟合(高偏差),复杂模型过拟合(高方差)

与其他课程的关联

本课作为CNN阶段的核心内容,与前后课程紧密关联:

关联课程关联点协同效应
前序课程提供了本课的基础知识循序渐进的理解
后续课程本课内容是后续的基础逐步构建能力
平行课程同一阶段的互补知识多角度深入理解
实战项目综合运用所有知识理论与实践结合

面试常见问题

💡 准备面试时,确保能回答以下问题:

  1. 用简单语言解释本课核心概念
  2. 画出关键算法/结构的流程图
  3. 比较不同方法的优缺点
  4. 举出实际应用场景
  5. 讨论常见误解和注意事项

进阶学习路径

掌握本课内容后,可以通过以下方式继续深入:

📐 补充:知识体系梳理

核心公式速查

将本课涉及的关键公式整理如下,方便回顾和记忆。理解每个公式背后的直觉比死记硬背更重要——试着用自然语言解释每个公式在做什么。

易混淆概念辨析

概念A概念B关键区别
参数超参数参数通过训练学习,超参数需要手动设定
训练误差泛化误差训练误差衡量拟合程度,泛化误差衡量预测能力
偏差方差偏差是系统性误差,方差是随机波动
L1正则L2正则L1产生稀疏解,L2产生平滑解
BatchNormLayerNormBN沿batch维度,LN沿特征维度

代码模板

以下是本课核心概念的标准PyTorch实现模板,可以直接用于实际项目:

💡 调试建议:当结果不符合预期时,先检查数据,再检查损失函数,最后检查模型结构。90%的问题出在前两个环节。

🔗

成就解锁:深层探索者

残差连接让你突破深度限制——VGG和ResNet的智慧!