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

自制双神经元Python网络反向传播失效求助

自制双神经元Python网络反向传播失效求助

我照着《Why machines learn》第330页的描述,自己写了一个双神经元的神经网络,但训练的时候loss完全不下降,实在搞不懂哪里出问题了!之前做单神经元模型的时候loss是正常下降的,换成双神经元就彻底没反应,换了单神经元能用的训练数据也不行,找ChatGPT问了也没解决问题,求各位大佬帮忙看看!

先贴一下我的训练代码和用到的辅助函数:

def training(x_data, labels, w11=0, w12=0, w2=0, b1=0, b2=0, alpha=0.1): 
    w11_list = []
    w12_list = []
    b1_list = []
    w2_list = []
    b2_list = []

    L_list = []
    
    for i in range(len(x_data)): 
        z1 = neuron1(x_data[i][0], x_data[i][1], w11=w11, w12=w12, b1=b1)
        z2 = neuron2(z1, w2=w2, b2=b2)
        yhat = a(z2) # calculate the output of the neural net

        e = labels[i] - yhat # calculate error (Labels[i]=true_value)
        
        L_list.append(e**2)

        w_n2 = -2*e*a(z1)*(yhat*(1-yhat)) # formular to update weight
        w2_list.append(w_n2) # added to list to take the mean
        b_n2 = -2*e*(yhat*(1-yhat)) # formular to update bias
        b2_list.append(b_n2) # added to list to take the mean

        w1_n1 = -2*e*x_data[i][0]*w2*(yhat*(1-yhat))*(a(z1)*(1-a(z1))) #... (same as above just other weights and biases so the formular changes)
        w11_list.append(w1_n1) # ... 
        w2_n1 = -2*e*x_data[i][1]*w2*(yhat*(1-yhat))*a(z1)*(1-a(z1)) # ...
        w12_list.append(w2_n1) # ...
        b_n1 = -2*e*w2*(yhat*(1-yhat))*(a(z1)*(1-a(z1))) # ...
        b1_list.append(b_n1) # ...


    w11_sum = sum(w11_list)/len(w11_list) # average w11 value
    delta_w11 = -alpha*w11_sum # multiplied with learning_rate=alpha
    w11 = w11 + delta_w11 # added to old w11 value

    w12_sum = sum(w12_list)/len(w12_list) # ... (basically the same as above but different weight)
    delta_w12 = -alpha*w12_sum # ...
    w12 = w12 + delta_w12 # ...

    b1_sum = sum(b1_list)/len(b1_list) # ...
    delta_b1 = -alpha*b1_sum  # ...
    b1 = b1 + delta_b1 # ...

    w2_sum = sum(w2_list)/len(w2_list) # ...
    delta_w2 = -alpha*w2_sum  # ...
    w2 = w2 + delta_w2 # ...

    b2_sum = sum(b2_list)/len(b2_list) # ...
    delta_b2 = -alpha*b2_sum # ...
    b2 = b2 + delta_b2 # ....

    L_mean = sum(L_list)/len(L_list) # calculating the loss
    print(L_mean)


    return w11, w12, w2, b1, b2   

辅助函数代码:

import math 

def neuron1(x1: input, x2: input, w11, w12, b1): 
    z = w11*x1+w12*x2+b1
    y = a(z)
    return y 

def neuron2(y, w2, b2): 
    z = w2*y+b2 
    y = a(z)
    return y 

def a(z): 
    return 1/(1+math.e**-z)

我打印了loss(代码里的L_mean),但它完全没变化。单神经元模型的时候loss是正常下降的,换成双神经元就彻底没反应,换了单神经元能用的训练数据也不行,实在搞不懂哪里错了!


问题排查与修改建议

我仔细捋了你的代码,发现几个核心问题,这应该就是loss不下降的原因:

1. 变量命名混乱导致梯度公式完全错误

你在训练函数里把神经元1的激活后输出命名成了z1,但z通常用来表示神经元未激活的加权和,激活后的值应该叫a1。这个命名混乱直接导致你在计算梯度时用错了变量:
比如你写的w_n2 = -2*e*a(z1)*(yhat*(1-yhat)),这里的z1已经是激活后的值了,你再套一层a(z1)就变成了对激活值二次激活,完全不符合反向传播的导数逻辑!

2. 反向传播的梯度推导错误

基于正确的变量定义(z为未激活加权和,a为激活后输出),重新梳理梯度公式:

  • 神经元1:z1 = w11*x1 + w12*x2 + b1,a1 = a(z1)
  • 神经元2:z2 = w2*a1 + b2,yhat = a(z2)
  • 损失L = (label - yhat)²,对应的梯度应该是:
    • 输出层w2的梯度:-2*e * yhat*(1-yhat) * a1
    • 输出层b2的梯度:-2*e * yhat*(1-yhat)
    • 隐藏层w11的梯度:-2*e * x1 * w2 * yhat*(1-yhat) * a1*(1-a1)
    • 隐藏层b1的梯度:-2*e * w2 * yhat*(1-yhat) * a1*(1-a1)

你原来的公式里,错误地对已经激活过的z1再次求激活函数的导数,导致梯度计算完全失效。

3. 冗余的神经元函数增加复杂度

neuron1和neuron2函数完全可以合并或者直接内联到训练逻辑里,避免变量传递时的混淆。


修改后的代码示例

import math 

def a(z): 
    return 1/(1+math.e**-z)

def training(x_data, labels, w11=0, w12=0, w2=0, b1=0, b2=0, alpha=0.1): 
    w11_grads = []
    w12_grads = []
    b1_grads = []
    w2_grads = []
    b2_grads = []
    L_list = []
    
    for i in range(len(x_data)): 
        # 明确区分未激活加权和(z)与激活后输出(a)
        x1, x2 = x_data[i][0], x_data[i][1]
        z1 = w11 * x1 + w12 * x2 + b1
        a1 = a(z1)
        z2 = w2 * a1 + b2
        yhat = a(z2)
        
        e = labels[i] - yhat
        L_list.append(e**2)
        
        # 计算各参数的梯度
        dyhat_dz2 = yhat * (1 - yhat)  # sigmoid导数
        # 输出层参数梯度
        dL_dw2 = -2 * e * dyhat_dz2 * a1
        dL_db2 = -2 * e * dyhat_dz2
        # 隐藏层参数梯度
        da1_dz1 = a1 * (1 - a1)
        dyhat_dz1 = dyhat_dz2 * w2 * da1_dz1
        dL_dw11 = -2 * e * dyhat_dz1 * x1
        dL_dw12 = -2 * e * dyhat_dz1 * x2
        dL_db1 = -2 * e * dyhat_dz1
        
        # 保存梯度用于后续平均
        w11_grads.append(dL_dw11)
        w12_grads.append(dL_dw12)
        b1_grads.append(dL_db1)
        w2_grads.append(dL_dw2)
        b2_grads.append(dL_db2)
    
    # 批量梯度下降:平均梯度后更新参数
    w11 -= alpha * sum(w11_grads)/len(w11_grads)
    w12 -= alpha * sum(w12_grads)/len(w12_grads)
    b1 -= alpha * sum(b1_grads)/len(b1_grads)
    w2 -= alpha * sum(w2_grads)/len(w2_grads)
    b2 -= alpha * sum(b2_grads)/len(b2_grads)
    
    L_mean = sum(L_list)/len(L_list)
    print(f"当前loss: {L_mean}")
    
    return w11, w12, w2, b1, b2

额外建议

  1. 训练时可以多打印参数的梯度平均值,看是否存在梯度为0或极小的情况,方便排查
  2. 如果样本量较大,可以考虑换成随机梯度下降(每个样本后更新参数)或小批量梯度下降
  3. 若修改后loss仍不下降,可检查训练数据是否存在线性不可分的情况(双神经元结构可以解决简单非线性问题,比如XOR)

备注:内容来源于stack exchange,提问作者hayat11

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:20:27