使用Numpy实现两层神经网络遇异常:所有Y输出值相同,求排查
问题:手动用Numpy搭建两层神经网络,输出所有Y值均相同
我尝试仅使用Numpy搭建一个两层神经网络,网络结构为:第一层含2个sigmoid激活神经元,输出层含1个sigmoid激活神经元,但算法输出的所有Y值均相同,请问问题出在哪里?
使用的数据集为结构化表格数据,代码如下:
import numpy as np import pandas as pd import matplotlib.pyplot as plt def sigmoid(z): return 1/(1+np.exp(-z)) def crossentropy(Y,y): return -(Y*np.log(y)+(1-Y)*np.log(1-y)) path = 'D:/DS&ML/ML/data.csv' data = pd.read_csv(path) data = np.asarray(data) x = data[:,0:3] Y = data [:,3:] x = np.c_[x, np.ones(len(x))] m = len(x) a = 0.015 loss = [] # Layer 1 parameters t11 = np.random.rand(len(x[1]),1) t12 = np.random.rand(len(x[1]),1) # Layer 2 parameters t21 = np.random.rand(3,1) for i in range(200): # Layer 1 Forward Prop z11 = np.matmul(x,t11) z12 = np.matmul(x,t12) a11 = sigmoid(z11) a12 = sigmoid(z12) a1 = np.c_[a11,a12,np.ones(m)] #Layer 2 Forward Prop z21 = np.matmul(a1,t21) a21 = sigmoid(z21) #Loss Calculation J = (1/m)*np.sum(crossentropy(Y,a21)) loss.append(J) # Layer 2 Back Prop dt21 = (a/m)*np.sum((a21-Y)*a1,axis=0).reshape(3,1) # Layer 1 Back Prop da1 = (np.matmul((a21-Y),a1))[:,0:2] da11 = da1[:,0:1]*sigmoid(z11)*(1-sigmoid(z11)) da12 = da1[:,1:2]*sigmoid(z12)*(1-sigmoid(z12)) dt11 = (a/m)*np.sum((da11)*x,axis=0).reshape(4,1) dt12 = (a/m)*np.sum((da12)*x,axis=0).reshape(4,1) # Updating Parameters t11 = t11-dt11 t12 = t12-dt12 t21 = t21-dt21 plt.plot(loss) plt.show()
核心问题排查
1. 反向传播梯度计算错误(最关键)
反向传播逻辑多处错误,导致权重无法得到有效更新:
- 第一层误差计算完全错误:正确逻辑是先计算输出层误差
delta2 = a21 - Y(交叉熵+ sigmoid输出的梯度可简化,无需额外乘sigmoid导数),再通过输出层权重传递误差到第一层,结合第一层激活值的导数计算梯度。
错误代码:
正确计算方式:da1 = (np.matmul((a21-Y),a1))[:,0:2]delta2 = a21 - Y delta11 = delta2 * t21[0] * a11 * (1 - a11) delta12 = delta2 * t21[1] * a12 * (1 - a12) - 梯度求和逻辑的前提是误差计算正确,否则即使求和方式对,结果也无效。
2. 输入特征未做归一化
sigmoid在输入绝对值过大时会进入饱和区(输出趋近0或1),此时导数趋近0,权重几乎无法更新。如果输入特征尺度差异大,会导致z值直接饱和,所有输出趋同。
解决:先对输入做标准化:
# 先归一化,再添加偏置项 x = (x - np.mean(x, axis=0)) / np.std(x, axis=0) x = np.c_[x, np.ones(len(x))]
3. 权重初始化不合理
用np.random.rand()生成0-1均匀分布权重,容易让sigmoid初始就进入饱和区,导致梯度消失。建议用Xavier初始化适配输入维度:
t11 = np.random.randn(len(x[1]),1) * np.sqrt(1/len(x[1])) t12 = np.random.randn(len(x[1]),1) * np.sqrt(1/len(x[1])) t21 = np.random.randn(3,1) * np.sqrt(1/3)
4. 迭代次数不足
200次迭代对于神经网络来说太少,尤其是初始化或学习率不合适时,模型还未开始收敛,可尝试增加到10000次左右。
5. 学习率可能不合适
当前a=0.015可能偏小,导致权重更新过慢;也可能偏大导致震荡。可尝试调整为0.1或0.001,结合loss曲线观察效果。
修正后的反向传播核心代码
for i in range(10000): # 前向传播部分不变 z11 = np.matmul(x,t11) z12 = np.matmul(x,t12) a11 = sigmoid(z11) a12 = sigmoid(z12) a1 = np.c_[a11,a12,np.ones(m)] z21 = np.matmul(a1,t21) a21 = sigmoid(z21) J = (1/m)*np.sum(crossentropy(Y,a21)) loss.append(J) # 修正后的反向传播 delta2 = a21 - Y dt21 = (a/m) * np.sum(delta2 * a1, axis=0).reshape(3,1) delta11 = delta2 * t21[0] * a11 * (1 - a11) delta12 = delta2 * t21[1] * a12 * (1 - a12) dt11 = (a/m) * np.sum(delta11 * x, axis=0).reshape(4,1) dt12 = (a/m) * np.sum(delta12 * x, axis=0).reshape(4,1) # 参数更新 t11 -= dt11 t12 -= dt12 t21 -= dt21
内容的提问来源于stack exchange,提问作者Pragadeesh S B
相关产品推荐
相关产品推荐

