You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python+Numpy实现通用神经网络反向传播算法的问题排查

问题诊断

你当前的反向传播实现存在两个核心错误,导致多层网络、带非线性激活时无法正常工作:

  1. 梯度传递逻辑完全错误:你用la.inv(weights[i].T @ weights[i]) @ weights[i].T @ (y - biases[i])传递梯度的写法,本质是用伪逆求解线性方程组反推上一层输出,仅在单层线性无激活网络的特殊场景下碰巧生效,完全不符合反向传播的链式求导规则。矩阵求逆计算成本高、数值稳定性差,本就不应该出现在标准反向传播流程中。
  2. 缺失激活函数导数项:反向传播跨层传递梯度时,必须乘以对应层激活函数的导数,这是网络拟合非线性关系的核心支撑,你的代码完全没有这部分计算,引入非线性激活后梯度方向必然错误。
    另外你当前用三层嵌套for循环更新权重的写法效率极低,完全可以用numpy矩阵运算替代,速度会提升数个量级。
反向传播的正确实现逻辑

首先明确标准前向传播的计算规则,你需要在保存各层激活输出的基础上,额外保存每一层的线性计算结果:

  • 对第i层,先计算线性输出:z_i = W_i @ a_{i-1} + b_i,其中a_{i-1}是上一层的激活输出,a_0为网络输入
  • 再经过激活函数得到当前层输出:a_i = activation(z_i)
  • 最后一层输出a_L为网络预测值,若使用MSE损失,损失为L = np.mean((y - a_L)**2)

反向传播的核心是从输出层开始,逐层向前传递损失对当前层线性输出z的梯度(记为delta),全程不需要矩阵求逆:

  • 输出层delta:delta_L = (a_L - y) * activation_derivative(z_L),若输出层用线性激活,导数为1,delta可直接简化为预测值减真实值
  • 隐藏层delta递推:delta_i = (W_i.T @ delta_{i+1}) * activation_derivative(z_i),其中*为逐元素乘法
  • 权重梯度:dW_i = delta_i @ a_{i-1}.T / batch_size,单样本训练时无需除以batch size
  • 偏置梯度:db_i = np.mean(delta_i, axis=batch_axis),单样本训练时db_i等于delta_i
修正后的代码参考
import numpy as np

def back_prop(y, layers, zs, weights, biases, activation_deriv, lr=1e-3):
    """
    参数说明
    ----------
    y: 样本真实标签
    layers: 各层激活输出列表,layers[0]为输入,layers[-1]为最终预测值
    zs: 各层线性输出z的列表,和layers一一对应
    weights/biases: 网络的权重、偏置参数列表
    activation_deriv: 激活函数的导数计算函数
    lr: 学习率
    """
    batch_size = y.shape[0]
    # 初始化输出层梯度
    delta = (layers[-1] - y) * activation_deriv(zs[-1])

    # 从最后一层向前逐层更新参数、传递梯度
    for i in range(len(weights)-1, -1, -1):
        a_prev = layers[i]
        # 计算梯度
        dW = delta @ a_prev.T / batch_size
        db = np.mean(delta, axis=0).reshape(biases[i].shape)
        # 参数更新
        weights[i] -= lr * dW
        biases[i] -= lr * db
        # 非输入层时继续向上传递梯度
        if i > 0:
            delta = (weights[i].T @ delta) * activation_deriv(zs[i-1])
    
    return weights, biases
调优注意事项
  • 你原来设置的学习率10e-8(即1e-7)过小,正常训练MSE损失的网络时,学习率通常在1e-2~1e-4区间,需要根据任务实际收敛情况调整。
  • 如果使用交叉熵损失配合Softmax输出层,输出层delta可直接简化为a_L - y,无需额外乘Softmax导数,数值稳定性更好。
  • 前向传播时必须同步保存每一层的线性输出z和激活输出a,否则无法正确计算激活函数的导数项。

内容的提问来源于stack exchange,提问作者Moiz Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 11:15:29