NumPy实现MNIST神经网络准确率10%与Softmax溢出问题排查
问题排查结果
你的代码有3个核心bug,直接导致模型不收敛和exp溢出:
- Softmax实现完全错误
一是没有做数值平移,当Z的取值较大时np.exp(Z)会直接超出浮点数上限触发溢出;二是求和时没有指定axis=0按单个样本维度归一化,而是把整个batch所有值求和成单个标量做除法,输出的概率完全不满足单样本和为1的要求,模型根本无法学习有效特征。
修正后的数值稳定版Softmax:def Softmax(self, Z): # 每个样本减去自身最大值,避免exp溢出,计算结果和原公式数学等价 Z_shift = Z - np.max(Z, axis=0, keepdims=True) exp_Z = np.exp(Z_shift) return exp_Z / np.sum(exp_Z, axis=0, keepdims=True) - 反向传播ReLU导数调用错误
你已经写了ReLU求导的方法ReLu_Derv但反向传播时根本没调用,反而用了ReLU激活后的输出self.a1乘梯度——ReLU输出大于0的部分是原始线性值,不是1,直接乘会导致梯度计算完全错误。另外你写的ReLu_Derv里用了不存在的np.greaterthan接口,直接修正为:
反向传播中计算e1的行对应修改为:def ReLu_Derv(self, Z): return (Z > 0).astype(int)e1 = self.w2.T.dot(e2) * self.ReLu_Derv(self.Z1) - 冗余参数不影响运行但建议清理:
NeuralNetwork类的__init__方法传入的n_in/n_out参数完全没有被使用,层维度全是硬编码,后续调整网络结构时很容易传参出错。
把上述bug修复后,训练时准确率会正常上升,不会再出现exp溢出问题。另外你当前隐层仅设置了10个神经元,容量较低,可以适当调大到32/64/128获得更高准确率。
通用神经网络调试技巧
- 维度优先校验:每一层前向、反向传播的张量shape提前算好预期值,写完每一步先打印shape核对,绝大多数低级错误都是维度不匹配、聚合函数错选轴导致的。
- 小样本过拟合测试:正式训练前先取10~100个样本的极小数据集训练,验证模型能否把准确率训到接近100%,如果小样本都无法拟合,说明代码逻辑存在硬伤,不用直接跑全量数据浪费时间。
- 梯度校验:反向传播写完后,用数值梯度方法(给每个参数加减1e-8的极小值,计算损失变化率)和反向传播得到的解析梯度对比,两者误差小于1e-7才能证明反向传播逻辑正确。
- 激活/损失输出校验:比如Softmax输出每个样本的类概率和必须为1,随机初始化下10分类任务的交叉熵损失应该在
-ln(0.1)≈2.3附近,如果数值偏差极大,对应模块肯定写错了。 - 数值稳定性检查:所有涉及
exp、log、除法的运算,必须做边界处理:比如Softmax减最大值、计算log时给预测值加1e-8避免log(0)、除法分母加1e-8避免除零。
内容的提问来源于stack exchange,提问作者sebc
相关产品推荐
相关产品推荐

