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

3层Sigmoid神经网络z1全为1、第一层权重不更新问题排查

3层全Sigmoid神经网络MNIST任务准确率卡在0.1问题排查

问题现象

  • 参照吴恩达机器学习课程,使用Python搭建3层全Sigmoid激活神经网络做MNIST手写数字预测,训练出现异常:迭代时代价函数持续下降,但分类准确率始终维持在0.1左右,等价于10分类随机猜测水平。
  • 调试定位核心异常:第一层激活值z1 = sigmoid(train_X @ theta1.T)几乎全部取值为1。原因是线性计算train_X @ theta1.T输出值过大,导致e^(-z)趋近于0,Sigmoid输出饱和到1;反向传播时theta1对应梯度项需要乘以z1*(1-z1)因子,此时1-z1趋近于0,梯度完全消失,参数无法有效更新。

已尝试的无效方案

  • 对输入数据集做正则化处理,无效果
  • 调整学习率alpha取值,无效果
  • 将theta1初始化为极小值(均匀分布采样后乘以0.000001):仅能解决第一次迭代z1全1的问题,后续迭代参数更新仍会将theta1推到导致z1饱和的取值范围
  • 怀疑前向/反向传播逻辑错误,但代码是参照课程Octave编程作业的可运行正确实现改写,原Octave版本可达到较高预测准确率

问题代码

def nn_forwardPropagation(train_X, train_Y, theta1, theta2, theta3):
    accuracy = 0
    J = 0
    #forward propagation, always adding 1's for the bias unit
    train_X = np.c_[ np.ones((np.shape(train_X)[0],1)), train_X]
    z1 = sigmoid(train_X@np.transpose(theta1))
    a1 = np.c_[ np.ones((np.shape(z1)[0],1)), z1]
    z2 = sigmoid(a1@np.transpose(theta2))
    a2 = np.c_[ np.ones((np.shape(z2)[0],1)), z2]
    #last step
    predValues = np.zeros((10,len(train_Y)))
    y1 = predValues.copy()
    predValues2 = y1.copy()
    for j in range(len(train_Y)):
        for i in range(np.shape(theta3)[0]):
            predValues[i,j] = sigmoid(a2[j,:]@np.transpose(theta3[i,:]))
        #making y1 our "target matrix" where we would like to see a 1 at the place of the right number and 0's everywhere else 
        y1[train_Y[j],j] = 1
        J += np.sum((np.transpose(-y1[:,j])@np.log(predValues[:,j]))  -  (1-np.transpose(y1[:,j]))@np.log(1 - predValues[:,j]))/len(train_Y)
        #in order to calculate accuracy, we just assume that the highest value in our predicted values dictates which value is "right" and looks if that's right
        predValues2[np.where(predValues[:,j] == max(predValues[:,j])),j] = 1 
        if np.array_equiv(predValues2[:,j], y1[:,j]):
            accuracy += 1/len(train_Y)
    #calculating the derivatives
    delta3 = predValues - y1  
    delta2 = np.transpose(delta3) @ theta3[:,1:] * z2 * (1-z2)
    delta1 = delta2 @ theta2[:,1:] * z1 * (1-z1)
    D3 = delta3 @ a2 
    D2 = np.transpose(delta2) @ a1
    D1 = np.transpose(delta1) @ train_X
    theta3_grad = D3 / len(train_Y)
    theta2_grad = D2 / len(train_Y)
    theta1_grad = D1 / len(train_Y)
    return J, theta3_grad, theta2_grad, theta1_grad, accuracy
    
def nn_runner(train_X, train_Y, iterations, alpha1, alpha2, alpha3):
    theta1 = np.random.rand(4,785)
    theta2 = np.random.rand(7,5)
    theta3 = np.random.rand(10,8)
    for i in range(1,iterations+1):
        J, theta3_grad, theta2_grad, theta1_grad, accuracy = nn_forwardPropagation(train_X, train_Y, theta1, theta2, theta3)
        theta3 -= alpha3*theta3_grad
        theta2 -= alpha2*theta2_grad
        theta1 -= alpha1*theta1_grad
        if i % 1 == 0:
            print("At iteration",i,":", J)
    print("This amounts to a total accuracy on training data of", accuracy)
    return theta3, theta2, theta1

核心错误与修复方案

1. 层维度与初始化逻辑直接导致z1饱和

这是z1全1的最直接原因:

  • 你定义的theta1形状为(4,785),意味着第一层仅4个隐藏神经元,对接785维输入(784像素+1偏置)。使用np.random.rand初始化时,参数取[0,1)区间的全正值,即使输入归一化到[0,1],785维全非负输入和全正权重做点积的期望值为785 * 0.5 * 0.5 ≈ 196,z值直接达到196,Sigmoid(196)必然饱和到1。
  • 修复:
    • 调整网络层维度到合理值,3层结构可参考:输入784维、第一隐藏层32神经元、第二隐藏层16神经元、输出10维,对应theta形状为theta1:(32,785)、theta2:(16,33)、theta3:(10,17)。
    • 替换初始化方法为零均值分布,适配Sigmoid的Xavier初始化即可:
      theta1 = np.random.randn(32, 785) * np.sqrt(1/785)
      theta2 = np.random.randn(16, 33) * np.sqrt(1/33)
      theta3 = np.random.randn(10, 17) * np.sqrt(1/17)
      
    • 确认输入归一化:MNIST像素值从0-255除以255缩放到0-1区间,避免输入值过大抬升线性输出量级。

2. 反向传播维度顺序混乱,梯度计算错误

你代码中张量维度顺序不统一,delta的形状、矩阵乘法转置逻辑多处错误,导致梯度量级完全失控,几次更新就会把参数推到饱和区间:

  • 统一所有张量维度顺序为(样本数, 神经元数),删除冗余的双重循环,前向传播全部向量化实现:
    # 前向传播示例
    m = train_X.shape[0]
    X = np.c_[np.ones((m,1)), train_X]
    z1 = X @ theta1.T
    a1 = np.c_[np.ones((m,1)), sigmoid(z1)]
    z2 = a1 @ theta2.T
    a2 = np.c_[np.ones((m,1)), sigmoid(z2)]
    z3 = a2 @ theta3.T
    a3 = sigmoid(z3) # 形状(m,10),和one-hot标签形状一致
    
  • 反向传播严格对齐维度:
    # 标签先转one-hot,形状(m,10)
    y_onehot = np.zeros((m,10))
    y_onehot[np.arange(m), train_Y] = 1
    delta3 = a3 - y_onehot # 形状(m,10)
    delta2 = delta3 @ theta3[:,1:] * sigmoid(z2) * (1-sigmoid(z2)) # 形状(m,16)
    delta1 = delta2 @ theta2[:,1:] * sigmoid(z1) * (1-sigmoid(z1)) # 形状(m,32)
    # 梯度计算
    theta3_grad = delta3.T @ a2 / m
    theta2_grad = delta2.T @ a1 / m
    theta1_grad = delta1.T @ X / m
    

3. 其他细节问题

  • 准确率计算部分np.where(predValues[:,j] == max(predValues[:,j]))的写法,在出现多个并列最大值时会将多个位置同时设为1,导致准确率统计错误,可直接用np.argmax获取预测标签对比。
  • 输出层双重循环效率极低,向量化实现后速度可提升数十倍,也能避免循环索引带来的隐蔽错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 11:51:21