CNN 卷积神经网络
CNN 核心概念
卷积操作
python
# PyTorch 卷积
nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1)
# 数学含义:卷积核在图像上滑动,做点积运算
# 输入: (B, C, H, W) → 输出: (B, F, H', W')卷积核(Filter/Kernel):
- 3×3、5×5、7×7 大小
- 深度等于输入通道数
- 学习参数:权重矩阵 + 偏置
步长(Stride):
- 卷积核每次移动的像素数
- stride=1: 输出尺寸不变
- stride=2: 输出尺寸减半
填充(Padding):
- padding=1: 周围补一圈0
- 保持空间尺寸
- 边缘信息不被丢弃
池化层
python
# 最大池化
nn.MaxPool2d(kernel_size=2, stride=2)
# 平均池化
nn.AvgPool2d(kernel_size=2, stride=2)
# 全局池化(压缩到单个值)
nn.AdaptiveAvgPool2d((1, 1)) # 输出 (B, C, 1, 1)特征图(Feature Map)
输入图像: 224×224×3 (RGB)
卷积层1: 224×224×64 (64个特征图)
池化层1: 112×112×64 (尺寸减半)
卷积层2: 112×112×128
池化层2: 56×56×128
卷积层3: 56×56×256
池化层3: 28×28×256
...
展平 → 全连接 → 输出经典 CNN 架构
LeNet-5 (1998)
输入(32×32)
→ Conv(6, 5×5)
→ AvgPool(2×2)
→ Conv(16, 5×5)
→ AvgPool(2×2)
→ Flatten(400)
→ FC(120)
→ FC(84)
→ FC(10)手写数字识别 MNIST,5万参数。
AlexNet (2012)
输入(224×224×3)
→ Conv(96, 11×11, stride=4) → MaxPool(3×3)
→ Conv(256, 5×5) → MaxPool(3×3)
→ Conv(384, 3×3)
→ Conv(384, 3×3)
→ Conv(256, 3×3) → MaxPool(3×3)
→ Flatten(9216) → FC(4096) → FC(4096) → FC(1000)ImageNet 2012 冠军,深度学习复兴标志。创新点:
- ReLU 激活
- Dropout
- GPU 并行训练
- 数据增强
VGGNet (2014)
VGG-16:
输入(224×224×3)
→ Conv(64)×2 → MaxPool
→ Conv(128)×2 → MaxPool
→ Conv(256)×3 → MaxPool
→ Conv(512)×3 → MaxPool
→ Conv(512)×3 → MaxPool
→ Flatten → FC(4096) → FC(4096) → FC(1000)
VGG-19: 多了几个 Conv(512)特点:小卷积核(3×3)堆叠,深度更深。1.38亿参数。
GoogLeNet / Inception (2014)
Inception 模块:
↓
┌─────────────┐
│ 1×1 Conv │ 1×1 Conv 降维
│ 3×3 Conv │ 3×3 Conv
│ 5×5 Conv │ 5×5 Conv
│ 3×3 MaxPool│ 1×1 Conv
└─────────────┘
↓
通道拼接并行多尺度卷积,Inception v1 用了 22 层,参数只有 500 万(比 AlexNet 少 12 倍)。
ResNet (2015)
残差块:
输入 ──→ Conv ──→ Conv ──→ + ──→ 输出
└──→ 1×1 Conv ──→ 1×1 Conv ──→┘
(快捷连接)F(x) = H(x) - x # 学习残差,而非直接学习映射
H(x) = F(x) + x # 实际输出 = 预测 + 输入ImageNet 2015 冠军,解决了深层网络梯度消失问题。152 层,VGG-16 的 8 倍深,复杂度更低。
python
# PyTorch ResNet
from torchvision.models import resnet50, resnet101, resnet152
model = resnet50(pretrained=True)DenseNet (2017)
密集块:
每一层都与其他所有层连接
x₀ → Conv → Concat(x₀, x₁) → Conv → Concat(x₀,x₁,x₂) → ...特征复用,减少参数。
EfficientNet (2019)
复合缩放:
深度 d: depth_coefficient
宽度 w: width_coefficient
分辨率 r: resolution_coefficient
φ: 缩放因子
depth = d^φ
width = w^φ
resolution = r^φ更高效的精度-参数权衡。
CNN 核心问题
梯度消失/爆炸
python
# 解决方案
# 1. 残差连接 (ResNet)
# 2. 批量归一化 (BatchNorm)
nn.BatchNorm2d(num_features)
# 3. 权重初始化
torch.nn.init.kaiming_normal_(layer.weight)过拟合
python
# 1. Dropout
nn.Dropout(p=0.5) # 训练时随机断开50%连接
# 2. 数据增强
transforms.RandomHorizontalFlip()
transforms.RandomCrop(224, padding=32)
# 3. L2 正则
optimizer = torch.optim.Adam(lr=1e-3, weight_decay=1e-4)计算量优化
python
# Depthwise Separable Convolution (MobileNet)
# 普通卷积: 3×3 × in_channels × out_channels
# Depthwise: 3×3 × in_channels + 1×1 × in_channels × out_channels
# 参数量减少约 8-9 倍
# 1×1 卷积降维 (Inception)
nn.Conv2d(192, 64, 1) # 1×1 压缩通道常用 CNN 框架
python
import torchvision.models as models
# 图像分类预训练模型
resnet18 = models.resnet18(pretrained=True)
resnet50 = models.resnet50(pretrained=True)
vgg16 = models.vgg16(pretrained=True)
efficientnet_b0 = models.efficientnet_b0(pretrained=True)
mobilenet_v2 = models.mobilenet_v2(pretrained=True)
# 修改最后一层
num_features = model.fc.in_features
model.fc = nn.Linear(num_features, 10) # 10类输出迁移学习
python
# 1. 直接用预训练特征(不训练)
for param in model.parameters():
param.requires_grad = False
model.fc = nn.Linear(512, 10)
# 2. 微调(训练所有层)
for param in model.parameters():
param.requires_grad = True
# 3. 微调(训练后面几层)
for param in model.layer4.parameters():
param.requires_grad = False
# 训练
optimizer = torch.optim.Adam(fc_params, lr=1e-3)代码示例:图像分类
python
import torch
import torch.nn as nn
import torchvision.transforms as transforms
from torchvision import models
class ImageClassifier(nn.Module):
def __init__(self, num_classes=10):
super().__init__()
# 使用预训练 ResNet
self.backbone = models.resnet18(pretrained=True)
# 冻结前面层
for param in self.backbone.parameters():
param.requires_grad = False
# 替换分类头
self.backbone.fc = nn.Linear(512, num_classes)
def forward(self, x):
return self.backbone(x)
# 数据
transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
# 训练
model = ImageClassifier()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
for epoch in range(10):
for images, labels in dataloader:
outputs = model(images)
loss = criterion(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()CNN 面试要点
1. 卷积核大小/步长/填充对输出尺寸的影响
output_size = (input_size + 2*padding - kernel_size) / stride + 1
2. 1×1 卷积的作用
- 降维/升维
- 通道间信息融合
- 减少参数量
3. 残差连接为什么有效
- 缓解梯度消失
- 让网络更容易学习恒等映射
- 允许深层网络训练
4. VGG vs ResNet
- VGG: 小卷积核堆叠
- ResNet: 残差学习
5. 常见正则化
- Dropout
- BatchNorm
- 数据增强
- L2 正则