自制双神经元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)
- 输出层w2的梯度:
你原来的公式里,错误地对已经激活过的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
额外建议
- 训练时可以多打印参数的梯度平均值,看是否存在梯度为0或极小的情况,方便排查
- 如果样本量较大,可以考虑换成随机梯度下降(每个样本后更新参数)或小批量梯度下降
- 若修改后loss仍不下降,可检查训练数据是否存在线性不可分的情况(双神经元结构可以解决简单非线性问题,比如XOR)
备注:内容来源于stack exchange,提问作者hayat11
相关产品推荐
相关产品推荐

