共计 1831 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点
刚开始学习 CNN 时,反向传播部分总是让人头大。特别是卷积层和池化层的梯度计算,和全连接层有很大不同。以下几个问题困扰了我很久:

- 感受野和梯度传播有什么关系?
- 池化层如何进行反向传播?
- 局部连接和权重共享如何影响梯度计算?
理解这些问题对正确实现 CNN 至关重要。下面我就从数学推导开始,一步步解析 CNN 的梯度下降过程。
数学推导
卷积层梯度计算
假设我们有一个简单的卷积层,输入为 $X$,卷积核为 $W$,输出为 $Y$,偏置为 $b$。前向传播可以表示为:
$$Y = X * W + b$$
在反向传播时,我们需要计算损失函数 $L$ 对 $W$ 和 $b$ 的梯度。根据链式法则:
$$\frac{\partial L}{\partial W} = \frac{\partial L}{\partial Y} \cdot \frac{\partial Y}{\partial W}$$
$$\frac{\partial L}{\partial b} = \frac{\partial L}{\partial Y} \cdot \frac{\partial Y}{\partial b}$$
由于权重共享特性,同一个卷积核在不同位置的梯度需要累加。这是和全连接层最大的区别。
池化层梯度计算
以最大池化为例,反向传播时梯度只传递给前向传播时被选中的最大值位置,其他位置的梯度为 0。这可以表示为:
$$\frac{\partial L}{\partial X_{i,j}} = \begin{cases}
\frac{\partial L}{\partial Y_{k,l}}, & \text{如果}X_{i,j}\text{是池化窗口中的最大值} \
0, & \text{其他情况}
\end{cases}$$
代码验证
自定义卷积层实现
import torch
import torch.nn as nn
import torch.nn.functional as F
class CustomConv2d(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size):
super().__init__()
self.weight = nn.Parameter(torch.randn(out_channels, in_channels, kernel_size, kernel_size))
self.bias = nn.Parameter(torch.zeros(out_channels))
def forward(self, x):
return F.conv2d(x, self.weight, self.bias)
梯度验证
# 创建自定义卷积层
conv = CustomConv2d(3, 16, 3)
# 注册 hook 打印梯度
conv.weight.register_hook(lambda grad: print(f'Custom conv weight grad: {grad.mean()}'))
# 对比 PyTorch 原生卷积层
native_conv = nn.Conv2d(3, 16, 3)
native_conv.weight.data = conv.weight.data.clone()
native_conv.weight.register_hook(lambda grad: print(f'Native conv weight grad: {grad.mean()}'))
# 测试输入
x = torch.randn(1, 3, 32, 32)
# 前向传播
y1 = conv(x)
y2 = native_conv(x)
# 计算损失并反向传播
loss1 = y1.sum()
loss1.backward()
loss2 = y2.sum()
loss2.backward()
避坑指南
- 忘记 zero_grad:每次反向传播前要记得清零梯度,否则梯度会累加
- stride 设置不当:过大的 stride 可能导致梯度形状不匹配
- padding 计算错误:错误的 padding 会影响输出尺寸和梯度传播
性能优化
im2col 算法可以将卷积操作转换为矩阵乘法,大幅提升计算效率。下面是内存占用对比:
| 方法 | 内存占用(MB) | 计算时间(ms) |
|---|---|---|
| 直接卷积 | 120 | 15.2 |
| im2col | 180 | 8.7 |
虽然 im2col 会增加内存使用,但计算速度明显提升。
延伸思考
- 当使用空洞卷积时,梯度计算会有哪些变化?
- 分组卷积 (Group Convolution) 的梯度计算与普通卷积有何不同?
希望这篇文章能帮助初学者更好地理解 CNN 的梯度下降过程。记住,理解原理后,实现起来就会容易很多。
