1. 权重初始化的艺术:从Xavier到Kaiming

第一次训练神经网络时,我盯着随机初始化的权重矩阵发呆:这些看起来毫无规律的数值,真的能引导模型找到最优解吗?后来才明白,好的开始确实是成功的一半。权重初始化就像给迷宫的探索者一张初始地图,既不能让他困在原地,也不能让他过早陷入死胡同。

Xavier初始化(又称Glorot初始化)的核心思想很直观:让每一层的输入和输出的方差保持一致。想象你在传递一个包裹,每次交接时都希望包裹的大小重量不变。具体实现时,对于全连接层,我们从均值为0、方差为2/(nin+nout)的分布中采样权重。这里nin和nout分别代表输入和输出的神经元数量。用PyTorch实现只需要一行代码:

torch.nn.init.xavier_uniform_(layer.weight)

但Xavier有个隐藏假设:激活函数是线性的。当使用ReLU这类"砍掉"负半轴的函数时,2015年提出的Kaiming初始化表现更好。它的秘诀在于调整方差计算时考虑激活函数的特性。比如ReLU会"杀死"一半神经元,所以方差计算时要加倍补偿:

# 针对ReLU的初始化
torch.nn.init.kaiming_normal_(layer.weight, mode='fan_in', nonlinearity='relu')

实测发现,在ResNet50这样的深层网络中,Kaiming初始化能使第一轮迭代的loss下降速度提升30%。不过要注意,现代深度学习框架已经内置了智能初始化策略,当你直接使用nn.Linear等模块时,默认采用的往往就是经过优化的初始化方案。

2. 正则化策略的双面性

正则化就像给模型戴上的"紧箍咒",既要防止它过度放飞自我(过拟合),又不能限制得太死(欠拟合)。L2正则化是最常见的"温柔约束",它在损失函数中添加权重平方和的惩罚项。有趣的是,在SGD优化器中,L2正则化完全等价于权重衰减(weight decay)。

这里有个容易踩的坑:当使用Adam等自适应优化器时,L2正则化与权重衰减在数学上不再等价。2019年ICLR论文《Decoupled Weight Decay Regularization》专门讨论了这个问题。正确的做法是:

# 使用AdamW而不是Adam
optimizer = torch.optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)

L1正则化会产生稀疏解,适合特征选择场景。我在一个医疗影像项目中,通过L1正则化自动筛选出10%的关键特征区域。组合使用L1+L2的Elastic Net在表格数据中效果显著,但在深度学习中较少使用。

3. Dropout的智能应用

第一次见到Dropout时,我觉得这想法太疯狂了——随机关闭神经元就像让飞行员在训练时随机蒙住眼睛。但正是这种"破坏性"训练让模型变得更强健。关键是要理解两点:训练时按概率p随机置零,测试时却要乘以(1-p)来保持期望值一致。

PyTorch的实现很简洁:

self.drop = nn.Dropout(p=0.2)

但在实际项目中我发现几个经验法则:

  • 靠近输入层的p值要小(通常0.1-0.2)
  • 大型模型比小型模型更能受益于Dropout
  • 配合BatchNorm使用时需要谨慎,可能出现训练/测试不一致的情况

在Transformer架构中,Dropout几乎无处不在:注意力权重、前馈网络、甚至嵌入层。有趣的是,Vision Transformer中常用的DropPath技术,是直接随机丢弃整个网络分支,效果比传统Dropout更好。

4. 标准化层的演进史

BatchNorm(BN)的出现彻底改变了深度学习的训练方式。我记得第一次在ResNet中使用BN时,学习率可以调高10倍而不会爆炸。BN的核心是沿着batch维度做归一化:

nn.BatchNorm2d(num_features=64)

但随着应用场景扩展,BN的局限也显现出来:

  • batch较小时统计量不准确
  • 不适合RNN等序列模型
  • 在分布式训练中需要同步统计量

于是衍生出各种变体:

  • LayerNorm(LN)在自然语言处理中成为标配
  • InstanceNorm(IN)对风格迁移任务效果惊艳
  • GroupNorm(GN)成为小batch场景下的救星

最近在训练一个医疗影像模型时,batch size只能设为4,使用GN配合权重标准化取得了比BN更好的效果。这里有个技巧:将GN的group数设为通道数的约数(如32通道设为8组),计算效率最高。

5. 学习率调优的智慧

学习率可能是最需要精心调节的超参数。我从最初的固定学习率,到StepLR,再到CosineAnnealingLR,踩过的坑可以写本书。现在最爱的是一周期学习率(One Cycle LR),它像聪明的教练,先让模型快速热身,再逐渐加大强度,最后精细调整:

scheduler = torch.optim.lr_scheduler.OneCycleLR(
    optimizer, 
    max_lr=0.01,
    steps_per_epoch=len(train_loader),
    epochs=10
)

另一个秘诀是学习率预热(Warmup),特别适合Transformer类模型。前1000步从0线性增加到目标学习率,能有效稳定训练初期。配合Adam优化器时,记得把betas参数从默认的(0.9,0.999)调整为(0.9,0.98),这是BERT等大模型验证过的组合。

6. 标签平滑的温柔哲学

分类任务中,硬标签(如[0,0,1])会迫使模型过度自信。标签平滑(Label Smoothing)给非目标类分配少量概率,相当于告诉模型:"正确答案很可能是这个,但也别完全否定其他可能"。在PyTorch中实现很简单:

criterion = nn.CrossEntropyLoss(label_smoothing=0.1)

这个小小的改变让模型在ImageNet上的top-5准确率提升了0.2%,更重要的是显著改善了对抗样本的鲁棒性。不过要注意,当标签平滑遇到知识蒸馏时,需要调整教师模型的温度参数,否则会抵消平滑效果。

7. 梯度裁剪的安全网

训练RNN时最怕梯度爆炸,前一秒loss还在正常下降,下一秒就变成NaN了。梯度裁剪就像给训练过程装上安全阀:

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

在Transformer中,我通常设为0.5到1.0之间。有趣的是,梯度裁剪不只是安全措施,还能提升模型性能——它相当于动态调整学习率,在陡峭区域自动减速。对于非常大的batch size训练,配合自适应裁剪阈值效果更好。

更多推荐