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

全连接神经网络反向传播实现求助:损失初期变化后停滞

全连接神经网络反向传播与实现错误排查

以下是针对你代码中导致损失停滞的关键错误点,以及对应的修正方案:

一、激活函数实现错误

1. Sigmoid导数递归调用死循环

原代码中sigmoid的导数实现会无限递归调用自身,导致无法正确计算导数:

def sigmoid(x: np.ndarray, derivative=False) -> np.ndarray:
    return  sigmoid(x) * (1. - sigmoid(x)) if derivative else 1. / (1. + np.exp(-x))

修正方案:直接使用sigmoid的输出值计算导数,避免递归:

def sigmoid(x: np.ndarray, derivative=False) -> np.ndarray:
    sig = 1. / (1. + np.exp(-x))
    return sig * (1. - sig) if derivative else sig

2. ReLU的条件判断与输入修改错误

  • 原代码用~derivative判断,Python中~是按位取反,布尔值True取反为-2(仍视为真),导致导数分支永远不会执行;
  • 修改输入数组x会破坏前向传播中存储的Z值,影响后续计算。

原错误代码:

def relu(x: np.ndarray, derivative=False) -> np.ndarray:
    if ~derivative:
        return np.maximum(0, x)
    x[x<=0] = 0
    x[x>0] = 1
    return x

修正方案:

def relu(x: np.ndarray, derivative=False) -> np.ndarray:
    if not derivative:
        return np.maximum(0, x)
    # 创建副本避免修改原数据
    x_copy = x.copy()
    x_copy[x_copy <= 0] = 0
    x_copy[x_copy > 0] = 1
    return x_copy

二、损失函数的小问题

原MSE函数返回的是平方误差而非均值,虽然不是损失停滞的核心原因,但不符合函数命名:

def mse(y: np.ndarray, y_pred: np.ndarray, derivative=False) -> np.ndarray:
    if derivative:
        return 2 * (y_pred - y)
    # 加入均值计算
    return np.mean(np.square(y_pred - y))

三、神经网络类核心错误

1. 缺少偏置(Bias)初始化与计算

全连接层必须包含偏置项,原代码仅初始化权重W,导致模型表达能力严重不足:
修正random_initializers:

def random_initializers(self) -> None:
    self.num_layers = len(self.layer_sizes)-1
    self.parameters = {}
    for i in range(self.num_layers):
        # 初始化权重
        self.parameters[f"W{i+1}"] = np.random.randn(self.layer_sizes[i+1], self.layer_sizes[i]) * np.sqrt(1. / self.layer_sizes[i])
        # 初始化偏置
        self.parameters[f"b{i+1}"] = np.zeros((self.layer_sizes[i+1], 1))

2. 前向传播缺少偏置项

原forward中Z的计算未加偏置,修正后:

def forward(self, X: np.ndarray) -> np.ndarray:
    # 确保输入为列向量,适配矩阵运算
    self.parameters['A0'] = X.reshape(-1, 1)
    for i in range(self.num_layers):
        W = self.parameters[f"W{i+1}"]
        b = self.parameters[f"b{i+1}"]
        self.parameters[f"Z{i+1}"] = np.dot(W, self.parameters[f"A{i}"]) + b
        self.parameters[f"A{i+1}"] = self.activation_functions[i](self.parameters[f"Z{i+1}"])
    return self.parameters[f"A{self.num_layers}"]

3. 反向传播核心逻辑错误

原反向传播的误差初始化、梯度计算、循环索引全部存在问题:
修正backward函数:

def backward(self, loss_prime):
    update = {}
    num_layers = self.num_layers
    # 输出层误差:损失导数 × 输出层激活函数导数
    delta = loss_prime * self.activation_functions[-1](self.parameters[f"Z{num_layers}"], derivative=True)
    # 输出层权重与偏置的梯度
    update[f"W{num_layers}"] = np.dot(delta, self.parameters[f"A{num_layers-1}"].T)
    update[f"b{num_layers}"] = delta
    
    # 从倒数第二层反向遍历到第一层
    for i in range(num_layers-1, 0, -1):
        # 当前层误差:上一层权重转置 × 上一层误差 × 当前层激活函数导数
        delta = np.dot(self.parameters[f"W{i+1}"].T, delta) * self.activation_functions[i-1](self.parameters[f"Z{i}"], derivative=True)
        # 当前层权重与偏置的梯度
        update[f"W{i}"] = np.dot(delta, self.parameters[f"A{i-1}"].T)
        update[f"b{i}"] = delta
    return update

4. 权重更新逻辑错误

原代码遍历权重数组的每一行更新,逻辑冗余且可能出错,直接整体更新即可:
修正update_weights:

def update_weights(self, update: dict[str, np.ndarray]) -> None:
    for key, value in update.items():
        self.parameters[key] -= self.learning_rate * value

5. 训练循环的细节问题

  • 原fit中未将每个epoch的平均损失存入历史记录;
  • 确保输入数据形状正确,且每个epoch后重置损失值:
def fit(self, X: np.ndarray, y: np.ndarray ) -> None:
    self.hist = {"loss": [], "frame": []}
    self.random_initializers()
    # 确保y为列向量
    y = y.reshape(-1, 1)
    for epoch in tqdm(range(self.epochs)):
        total_loss = 0.0
        for i, x in enumerate(X):
            pred = self.forward(x)
            # 累加单个样本损失
            total_loss += self.loss(y[i], pred)
            # 损失对预测值的导数
            loss_prime = self.loss(y[i], pred, derivative=True)
            update = self.backward(loss_prime)
            self.update_weights(update)
        # 记录当前epoch的平均损失
        avg_loss = total_loss / len(X)
        self.hist["loss"].append(avg_loss)

其他注意事项

  • 确认learning_rate已在类中定义(原代码未显示,需确保初始化时传入或设置默认值);
  • 如果使用ReLU激活,建议改用He初始化(即np.sqrt(2. / self.layer_sizes[i]))调整权重方差,避免神经元死亡;
  • 若仍存在损失停滞,可尝试调整学习率、增加模型层数/神经元数量,或检查输入数据是否做了归一化处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 15:10:58