概述

反向传播(Backpropagation)是训练深度神经网络的核心算法,通过链式法则高效计算损失函数对参数的梯度。梯度流理论则分析梯度在深度网络中如何传播,以及为何某些架构能够有效训练。1


反向传播算法

链式法则回顾

对于复合函数

对于多元复合函数:

计算图表示

深度神经网络可以表示为计算图,每个节点代表一个操作:

输入 x → [线性变换 W₁] → z₁ = W₁x → [激活 σ] → a₁ = σ(z₁)
    → [线性变换 W₂] → z₂ = W₂a₁ → [激活 σ] → a₂ = σ(z₂)
    → ... → [线性变换 W_L] → z_L = W_La_{L-1} → [损失 ℓ]

反向传播核心思想

前向传播:从输入到输出,计算每个中间节点的值。

反向传播:从输出到输入,使用链式法则计算每个参数对损失的梯度。

单层神经网络反向传播

设单层网络为:

损失函数为

1. 前向传播

def forward(x, W, b, y):
    z = np.dot(W, x) + b      # 线性变换
    a = sigmoid(z)             # 激活
    loss = cross_entropy(y, a) # 损失
    cache = (x, W, b, z, a)    # 缓存用于反向传播
    return loss, cache

2. 反向传播

def backward(dout, cache):
    x, W, b, z, a = cache
    n = x.shape[0]
    
    # 损失函数梯度 (以交叉熵 + softmax 为例)
    dloss = a - y  # (batch, hidden)
    
    # 对 z = Wx + b 的梯度
    dz = dloss * sigmoid_prime(z)  # (batch, hidden)
    
    # 对权重 W 的梯度: dℓ/dW = dz^T · x
    dW = np.dot(dz.T, x) / n       # (hidden, input)
    
    # 对偏置 b 的梯度
    db = np.sum(dz, axis=0) / n    # (hidden,)
    
    # 对输入 x 的梯度 (传递到前一层)
    dx = np.dot(W.T, dz)           # (batch, input)
    
    return dx, dW, db

多层网络反向传播

矩阵形式的反向传播

设网络有 层,第 层为:

损失函数梯度

其中 称为误差项

误差反向传播

PyTorch 实现

import torch
import torch.nn as nn
import torch.nn.functional as F
 
class DeepMLP(nn.Module):
    """多层感知机的反向传播"""
    def __init__(self, layer_sizes):
        super().__init__()
        self.layers = nn.ModuleList()
        for i in range(len(layer_sizes) - 1):
            self.layers.append(nn.Linear(layer_sizes[i], layer_sizes[i+1]))
    
    def forward(self, x):
        self.activations = [x]  # 保存激活值
        
        for i, layer in enumerate(self.layers):
            z = layer(self.activations[-1])
            self.activations.append(z)
            if i < len(self.layers) - 1:  # 除了最后一层都激活
                self.activations[-1] = F.relu(z)
        
        return self.activations[-1]
    
    def backward(self, y, y_pred):
        """手动反向传播(用于理解)"""
        batch_size = y.shape[0]
        
        # 损失梯度 (MSE)
        dLoss = 2 * (y_pred - y) / batch_size
        
        # 存储梯度
        self.grads = []
        
        # 从后往前传播
        d_out = dLoss
        
        for l in reversed(range(len(self.layers))):
            # 对激活的梯度
            if l < len(self.layers) - 1:
                d_out = d_out * F.relu derivative(self.activations[l+1])
            
            # 权重梯度
            dW = torch.outer(d_out, self.activations[l])
            db = d_out.clone()
            
            # 存储梯度
            self.grads.insert(0, (dW, db))
            
            # 传递到下一层
            d_out = self.layers[l].weight.T @ d_out
        
        return self.grads
 
def relu_derivative(x):
    """ReLU 导数"""
    return (x > 0).float()

梯度流分析

深度网络梯度问题

梯度消失(Vanishing Gradient)

当网络很深时,梯度在反向传播过程中指数衰减:

,则:

时,梯度指数衰减。

梯度爆炸(Exploding Gradient)

时,梯度指数增长,导致训练不稳定。

梯度流稳定性条件

雅可比矩阵范数

稳定训练的条件

  • :梯度既不消失也不爆炸
  • 这要求权重初始化恰好在临界状态

信号传播理论(Signal Propagation)

随机网络分析

考虑一个无限宽的全连接网络,权重从均值为 0、方差为 的分布初始化( 为层宽)。

前向传播

定理(均值方差保持):

如果输入 各分量独立同分布,均值为 0,方差为 ,且权重初始化满足上述方差,则每一层的激活值也满足各分量独立同分布,且:

激活函数的影响

激活函数 临界值行为
ReLU2.0激活值方差增长,需要
Tanh1.0激活值方差稳定在
Sigmoid1.0饱和导致方差收缩

反向传播

类似地,梯度满足:

其中 是偏置的方差。

边缘稳定性(Edge of Chaos)

定义:当 时,网络处于边缘稳定性(Edge of Chaos)状态。

条件
状态Ordered (有序)Chaotic (混沌)
激活值收缩到 0指数增长
梯度消失爆炸
特征学习浅层表示随机特征

关键洞察:只有处于边缘稳定性状态时,信息才能有效传播,同时保持特征学习能力。


深度线性网络的精确分析

线性网络梯度流

对于没有激活函数的线性网络

谱范数乘积

梯度范数与权重矩阵的谱范数乘积相关:

初始化策略的合理性

Xavier 初始化(2010):

对于线性网络,这使得 ,梯度流稳定。

He 初始化(2015):

对于 ReLU 网络,这补偿了 ReLU 消除一半激活值的影响。


残差网络的梯度流

残差连接的作用

考虑残差块:

梯度传递:

关键性质:即使 很小,梯度仍然包含 (单位矩阵),保证梯度不会完全消失!

数学证明

设从第 层到第 层的梯度:

其中

引理 的特征值至少包含 个 1。

这解释了为何残差网络能够训练数千层。


层归一化与梯度流

层归一化前向/反向

class LayerNormManual:
    def __init__(self, normalized_shape, eps=1e-5):
        self.gamma = nn.Parameter(torch.ones(normalized_shape))
        self.beta = nn.Parameter(torch.zeros(normalized_shape))
        self.eps = eps
    
    def forward(self, x):
        # x: (batch, seq_len, features) 或 (batch, features)
        mean = x.mean(dim=-1, keepdim=True)
        var = x.var(dim=-1, keepdim=True, unbiased=False)
        x_norm = (x - mean) / torch.sqrt(var + self.eps)
        return self.gamma * x_norm + self.beta
    
    def backward(self, dout):
        # 层归一化的反向传播
        N = dout.shape[-1]
        
        dgamma = (dout * self.x_norm).sum(dim=-1)
        dbeta = dout.sum(dim=-1)
        
        dx_norm = dout * self.gamma
        
        dvar = (-0.5 * N * dx_norm * (self.x - self.mean)).sum(dim=-1) / (self.var + self.eps)**1.5
        
        dx = 2/N * dx_norm * (self.x - self.mean) + dvar * 2 * (self.x - self.mean) / N
        
        return dx, dgamma, dbeta

层归一化对梯度的影响

层归一化将每层的激活值标准化到固定范围,间接稳定梯度流:

  1. 保持激活值方差稳定
  2. 减少内部协变量偏移
  3. 使优化 landscape 更平滑

优化器的梯度流效应

SGD 与动量

class SGDWithMomentum:
    def __init__(self, params, lr=0.01, momentum=0.9):
        self.params = params
        self.lr = lr
        self.momentum = momentum
        self.v = {}  # 速度
    
    def step(self, grads):
        for i, (name, p) in enumerate(self.params.items()):
            if name not in self.v:
                self.v[name] = torch.zeros_like(p)
            
            # 动量更新
            self.v[name] = self.momentum * self.v[name] + grads[name]
            
            # 参数更新
            p.data -= self.lr * self.v[name]

动量的梯度平滑效应

有效梯度 是历史梯度的指数加权平均:

当方向一致时,加速;当方向不一致时,平滑。

自适应优化器与梯度流

Adam 的梯度流效应:

class Adam:
    def __init__(self, params, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
        self.params = params
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        self.m = {}  # 一阶矩估计
        self.v = {}  # 二阶矩估计
        self.t = 0  # 时间步
    
    def step(self, grads):
        self.t += 1
        
        for name, p in self.params.items():
            # 一阶矩(梯度的指数移动平均)
            self.m[name] = self.beta1 * self.m.get(name, 0) + (1 - self.beta1) * grads[name]
            
            # 二阶矩(梯度平方的指数移动平均)
            self.v[name] = self.beta2 * self.v.get(name, 0) + (1 - self.beta2) * grads[name]**2
            
            # 偏差修正
            m_hat = self.m[name] / (1 - self.beta1**self.t)
            v_hat = self.v[name] / (1 - self.beta2**self.t)
            
            # 更新
            p.data -= self.lr * m_hat / (torch.sqrt(v_hat) + self.eps)

关键洞察:Adam 的二阶动量项 自适应地调整每个参数的学习率,对于梯度幅度大的参数降低学习率,对于梯度幅度小的参数增大学习率。


梯度流的可视化分析

奇异值分解分析

def analyze_gradient_flow(model, x, y):
    """分析网络的梯度流"""
    model.eval()
    
    # 前向传播
    output = model(x)
    loss = F.cross_entropy(output, y)
    
    # 反向传播
    loss.backward()
    
    results = {
        'layer_grads': [],
        'singular_values': [],
        'grad_norms': []
    }
    
    for name, param in model.named_parameters():
        if param.grad is not None:
            grad_norm = param.grad.norm().item()
            results['grad_norms'].append((name, grad_norm))
            
            # 奇异值分析(对权重矩阵)
            if param.dim() == 2:
                # 权重矩阵的奇异值
                U, S, V = torch.svd(param.data)
                results['singular_values'].append((name, S.cpu().numpy()))
                
                # 梯度与权重的对齐程度
                grad_proj = (param.grad * param).sum() / (param.grad.norm() * param.norm())
                results['layer_grads'].append((name, grad_proj.item()))
    
    return results
 
# 可视化
import matplotlib.pyplot as plt
 
def plot_gradient_flow(results):
    """可视化梯度流"""
    names = [r[0] for r in results['grad_norms']]
    norms = [r[1] for r in results['grad_norms']]
    
    plt.figure(figsize=(12, 6))
    plt.bar(range(len(names)), norms)
    plt.xticks(range(len(names)), names, rotation=45)
    plt.xlabel('Layer')
    plt.ylabel('Gradient Norm')
    plt.title('Gradient Flow Analysis')
    plt.yscale('log')
    plt.tight_layout()
    plt.show()

诊断指标

指标理想值问题诊断
梯度范数稳定消失或爆炸
权重-梯度对齐优化困难
奇异值分布集中在 1 附近条件数差
激活值方差稳定协变量偏移

实践中的梯度流优化

1. 适当的权重初始化

def custom_init(model):
    for m in model.modules():
        if isinstance(m, nn.Linear):
            nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
            if m.bias is not None:
                nn.init.constant_(m.bias, 0)
        elif isinstance(m, nn.Conv2d):
            nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')

2. 梯度裁剪

# 范数裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
 
# 值裁剪
torch.nn.utils.clip_grad_value_(model.parameters(), clip_value=1.0)

3. 学习率调度

# 学习率预热 + 余弦衰减
scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(
    optimizer, T_0=10, T_mult=2
)
 
# 阶梯衰减
scheduler = torch.optim.lr_scheduler.StepLR(
    optimizer, step_size=30, gamma=0.1
)

4. 残差连接

class ResidualBlock(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim, dim),
            nn.ReLU(),
            nn.Linear(dim, dim)
        )
    
    def forward(self, x):
        return x + self.net(x)  # 残差连接

理论前沿:Mean-Field 理论

无限宽网络极限

当网络宽度 时,网络的行为可以被精确描述。

核心结果

  1. 神经网络的高斯过程极限:无限宽网络等价于一个高斯过程
  2. 批量归一化的效果:引入层间相关性,破坏独立性假设
  3. 训练的收敛轨迹:可以在无限宽极限下精确预测

训练动态的闭式解

在 Mean-Field 极限下,参数的训练轨迹可以用常微分方程描述:

其中 是神经正切核定义的损失 landscape。


参考


相关阅读

Footnotes

  1. Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning. MIT Press.