创作理由:看了很多,我就发现很多都绕不开梯度问题,很多东西都说可以减轻训练过程中的梯度消失问题(比如某些激活函数、Resunit残差单元),如果你的神经网络梯度问题不再是问题,或者你解决的很好,那就很牛逼了,今天我就对这个论论,集集资料。

是什么?

  梯度问题一般分两种:

  • 梯度消失:梯度层层传播时变得越来越小,导致前面几层的参数几乎无法更新,网络“学不会”基础特征。
  • 梯度爆炸:梯度传播中变得越来越大,导致参数更新剧烈震荡,训练发散。

这两种问题都是在深层网络中进行反向传播时出现的。



为啥这么重要呢?

1. 它影响模型“能否训练起来”
  • 如果梯度太小,前面的层无法学习;如果太大,模型根本不收敛。反正就是训练都是个问题,更何谈预测了。

     2. 它影响训练效率和效果

  • 比如你想训练一个识别手写数字的网络,结果训练了半天发现Loss下降非常慢,或者训练根本就无法收敛。


怎么优化?

  ①残差网络:自从残差网络出现,如今的深度网络很轻松就能够构建几百层,千层也不用担心梯度消失过快的问题,原因就在于跨层连接,而非直来直去,其中残差单元如下图所示:

梯度问题的重要性_深度网络

import torch
import torch.nn as nn
import torch.nn.functional as F

class ResidualBlock(nn.Module):
    def __init__(self, in_channels):
        super(ResidualBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1)

    def forward(self, x):
        return F.relu(self.conv2(F.relu(self.conv1(x))) + x)  # 残差连接

# 测试
x = torch.randn(1, 64, 32, 32)  # 输入
block = ResidualBlock(64)
y = block(x)
print(y.shape)  # 输出大小保持不变
②ReLU 等部分激活函数:相较于 sigmoid,ReLU 在正区间恒为1,梯度不会消失,训练更稳定。
import torch
import torch.nn as nn
import torch.nn.functional as F

class SimpleMLP(nn.Module):
    def __init__(self):
        super(SimpleMLP, self).__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 10)

    def forward(self, x):
        return self.fc2(F.relu(self.fc1(x)))  # 使用ReLU激活

# 测试
x = torch.randn(1, 784)  # 输入
model = SimpleMLP()
out = model(x)
print(out.shape)  # 输出是10个类别


   ③优化方法集合

方法类别

具体技术或策略

原理概述

激活函数优化

ReLU, LeakyReLU, ELU 等

保持非零梯度

网络结构设计

ResNet, DenseNet, HighwayNet

保证梯度流畅传播

参数初始化

Xavier, He

防止前向/反向过程中方差偏移

正则化方法

BatchNorm, LayerNorm

稳定分布,防止梯度震荡

优化器改进

Adam, RMSProp

自适应调整学习率,应对不同梯度规模

梯度裁剪

Gradient Clipping

限制最大梯度,防止爆炸

损失函数设计

Focal Loss(分类中处理样本不均衡)


间接解决梯度不平衡问题