VGG证明了"更深的网络更好",ResNet解决了深层网络的训练难题。本课深入分析这两个划时代的架构设计。
VGG的核心思想:使用更小的3×3卷积核,但堆叠更多层。
2个3×3卷积的感受野 = 1个5×5卷积
3个3×3卷积的感受野 = 1个7×7卷积
优势:更少参数 + 更多非线性
| 配置 | 层数 | 参数量 |
|---|---|---|
| VGG11 | 11 | 133M |
| VGG13 | 13 | 133M |
| VGG16 | 16 | 138M |
| VGG19 | 19 | 144M |
H(x) = F(x) + x
网络学习残差 F(x) = H(x) - x
如果恒等映射最优,F(x)=0比学习H(x)=x更容易
①梯度可以直接通过shortcut流向浅层②恒等映射是简单解③不会因为加深而变差
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}")
标准残差: y = F(x) + x
预激活残差: y = F(BN(ReLU(x))) + x (ResNet-v2)
密集连接: y = [x, F₁(x), F₂([x,F₁(x)]), ...] (DenseNet)
| 模型 | 深度 | 参数量 | ImageNet Top-1 |
|---|---|---|---|
| ResNet-18 | 18 | 11.7M | 69.8% |
| ResNet-50 | 50 | 25.6M | 76.1% |
| ResNet-101 | 101 | 44.5M | 77.4% |
| ResNeXt-101 | 101 | 44.5M | 79.6% |
| EfficientNet-B7 | - | 66M | 84.3% |
💡 推荐资源:
用BasicBlock组装完整的ResNet-18,在CIFAR10上训练。
实现DenseBlock,理解密集连接的优势。
对比ResNet-18和ResNet-50的精度和训练速度。
| 模型 | 创新点 | 改进 |
|---|---|---|
| ResNet | 残差连接 | 支持100+层 |
| ResNet-v2 | 预激活 | 更好的梯度流 |
| ResNeXt | 分组卷积 | 更多通道、更好精度 |
| ResNeSt | 分裂注意力 | 多尺度特征融合 |
| ConvNeXt | 借鉴Transformer | 纯CNN媲美ViT |
虽然Transformer(ViT)在CV中越来越流行,但CNN仍有优势:
理解本课内容需要将其置于更大的知识网络中。每个核心概念都不是孤立的,它们相互关联、相互支撑:
建立正确的直觉比记住公式更重要:
1. 维度直觉:高维空间中,数据分布与低维直觉截然不同(维度诅咒)
2. 信息瓶颈:神经网络逐层压缩信息,保留任务相关的、丢弃无关的
3. 流形假设:高维数据实际分布在低维流形上,学习即发现流形结构
4. 偏差-方差权衡:简单模型欠拟合(高偏差),复杂模型过拟合(高方差)
本课作为CNN阶段的核心内容,与前后课程紧密关联:
| 关联课程 | 关联点 | 协同效应 |
|---|---|---|
| 前序课程 | 提供了本课的基础知识 | 循序渐进的理解 |
| 后续课程 | 本课内容是后续的基础 | 逐步构建能力 |
| 平行课程 | 同一阶段的互补知识 | 多角度深入理解 |
| 实战项目 | 综合运用所有知识 | 理论与实践结合 |
💡 准备面试时,确保能回答以下问题:
掌握本课内容后,可以通过以下方式继续深入:
将本课涉及的关键公式整理如下,方便回顾和记忆。理解每个公式背后的直觉比死记硬背更重要——试着用自然语言解释每个公式在做什么。
| 概念A | 概念B | 关键区别 |
|---|---|---|
| 参数 | 超参数 | 参数通过训练学习,超参数需要手动设定 |
| 训练误差 | 泛化误差 | 训练误差衡量拟合程度,泛化误差衡量预测能力 |
| 偏差 | 方差 | 偏差是系统性误差,方差是随机波动 |
| L1正则 | L2正则 | L1产生稀疏解,L2产生平滑解 |
| BatchNorm | LayerNorm | BN沿batch维度,LN沿特征维度 |
以下是本课核心概念的标准PyTorch实现模板,可以直接用于实际项目:
💡 调试建议:当结果不符合预期时,先检查数据,再检查损失函数,最后检查模型结构。90%的问题出在前两个环节。
残差连接让你突破深度限制——VGG和ResNet的智慧!