共计 2925 个字符,预计需要花费 8 分钟才能阅读完成。
CNN 基础结构回顾
卷积神经网络(CNN)主要由卷积层、池化层和全连接层组成。在训练过程中,反向传播算法通过计算损失函数对网络参数的梯度,并利用梯度下降法更新参数。具体来说,反向传播通过链式法则将误差从输出层逐层传播回输入层。

数学上,链式法则可以表示为:
$$\frac{\partial L}{\partial W^{(l)}} = \frac{\partial L}{\partial z^{(l)}} \cdot \frac{\partial z^{(l)}}{\partial W^{(l)}}$$
其中,$L$ 是损失函数,$W^{(l)}$ 是第 $l$ 层的权重,$z^{(l)}$ 是该层的输入。
权重更新过程
权重更新的核心公式为:
$$W = W – \alpha \cdot \frac{\partial L}{\partial W}$$
其中,$\alpha$ 是学习率。在 PyTorch 中,这一过程可以通过以下代码实现:
import torch
import torch.nn as nn
import torch.optim as optim
# 自定义卷积层
class CustomConv2d(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size):
super(CustomConv2d, self).__init__()
self.weight = nn.Parameter(torch.randn(out_channels, in_channels, kernel_size, kernel_size))
self.bias = nn.Parameter(torch.randn(out_channels))
def forward(self, x):
return nn.functional.conv2d(x, self.weight, self.bias)
# 初始化模型和优化器
model = CustomConv2d(1, 1, 3)
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 模拟输入和标签
input = torch.randn(1, 1, 28, 28)
target = torch.randn(1, 1, 26, 26)
# 前向传播和反向传播
output = model(input)
loss = nn.functional.mse_loss(output, target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
完整训练代码
以下是一个完整的训练代码示例,包含自定义卷积层的梯度计算、学习率衰减策略和梯度裁剪:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim.lr_scheduler import StepLR
# 自定义卷积层
class CustomConv2d(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size):
super(CustomConv2d, self).__init__()
self.weight = nn.Parameter(torch.randn(out_channels, in_channels, kernel_size, kernel_size))
self.bias = nn.Parameter(torch.randn(out_channels))
def forward(self, x):
return nn.functional.conv2d(x, self.weight, self.bias)
# 初始化模型、优化器和学习率调度器
model = CustomConv2d(1, 1, 3)
optimizer = optim.SGD(model.parameters(), lr=0.01)
scheduler = StepLR(optimizer, step_size=10, gamma=0.1)
# 模拟数据集
inputs = [torch.randn(1, 1, 28, 28) for _ in range(100)]
targets = [torch.randn(1, 1, 26, 26) for _ in range(100)]
# 训练循环
for epoch in range(50):
for input, target in zip(inputs, targets):
optimizer.zero_grad()
output = model(input)
loss = nn.functional.mse_loss(output, target)
loss.backward()
# 梯度裁剪
nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
scheduler.step()
print(f'Epoch {epoch}, Loss: {loss.item()}')
生产环境陷阱
梯度爆炸的 NAN 检测方案
在训练过程中,梯度爆炸可能导致参数更新过大,最终产生 NAN 值。可以通过以下代码检测和防止梯度爆炸:
for param in model.parameters():
if torch.isnan(param.grad).any():
print('Gradient explosion detected!')
param.grad.data.zero_()
不同初始化方法对收敛速度的影响
不同的参数初始化方法会显著影响模型的收敛速度。常见的初始化方法包括:
- Xavier 初始化:适用于 sigmoid 和 tanh 激活函数
- Kaiming 初始化:适用于 ReLU 激活函数
# Xavier 初始化
nn.init.xavier_uniform_(model.weight)
# Kaiming 初始化
nn.init.kaiming_uniform_(model.weight, mode='fan_in', nonlinearity='relu')
数值梯度验证
使用 torch.autograd.gradcheck 可以验证自定义层的梯度计算是否正确:
test_input = torch.randn(1, 1, 5, 5, requires_grad=True)
test = torch.autograd.gradcheck(model, test_input, eps=1e-6, atol=1e-4)
print('Gradient check passed:', test)
扩展思考
SGD vs Adam 优化器
- SGD:简单、容易调参,但可能陷入局部最优
- Adam:自适应学习率,通常收敛更快,但需要更多内存
二阶优化方法
L-BFGS 等二阶优化方法利用了损失函数的曲率信息,适合小批量数据和光滑优化问题,但在深度学习中应用较少,主要因为计算复杂度高。
总结
本文详细介绍了 CNN 训练中的反向传播机制和参数更新原理,并提供了完整的 PyTorch 实现代码。通过理解这些底层机制,开发者可以更好地调试和优化深度学习模型。在实际应用中,还需要注意梯度爆炸、参数初始化等问题,并根据任务特点选择合适的优化器。
