手写MNIST神经网络模型收敛异常及数值溢出问题求助
MNIST神经网络训练异常问题解决
核心问题分析
你遇到的准确率下降、收敛到单一数字,以及exp溢出和无效除法警告,根源在于两个关键错误:
- Softmax函数的数值稳定性缺失和维度计算错误
- 预测函数的维度处理逻辑混乱
具体错误点及修复方案
1. 修复Softmax数值溢出与维度错误
原Softmax函数中,sum(np.exp(z))会将所有样本的输出值求和为一个标量,而非对每个样本单独计算归一化;同时未做数值稳定处理,当z中出现较大数值时,np.exp(z)会溢出为inf,导致inf/inf得到nan,破坏梯度计算。
修改后的Softmax函数:
def Softmax(z): # 数值稳定:减去每个样本的最大值,避免exp溢出 z_stable = z - np.max(z, axis=1, keepdims=True) exp_z = np.exp(z_stable) # 按行求和,保证每个样本单独归一化(保持m×1维度以支持广播) return exp_z / np.sum(exp_z, axis=1, keepdims=True)
2. 修复预测函数的维度逻辑
原get_predictions函数按列取最大值索引(axis=0),配合a2.T转置的方式逻辑混乱,容易出错。正确的做法是直接对每个样本(行)取最大值索引:
def get_predictions(A2): # A2为m×10,每行对应一个样本的10类概率,取每行最大值索引 return np.argmax(A2, axis=1)
同时在gradient_descent中修改调用代码,去掉不必要的转置:
predictions = get_predictions(a2)
3. 优化权重初始化
原初始化方式(np.random.rand(...) - 0.5)的方差过大,容易导致网络初始阶段输出值过大,加剧Softmax溢出风险。改用He初始化(适配ReLU激活函数),同时偏置初始化为0更稳定:
def init_param(): # He初始化:针对ReLU激活,权重方差为2/输入维度 w1 = np.random.randn(784,20) * np.sqrt(2/784) b1 = np.zeros((1,20)) w2 = np.random.randn(20,10) * np.sqrt(2/20) b2 = np.zeros((1,10)) return w1, b1, w2, b2
4. 可选:调整学习率
原学习率0.01偏小,修复上述问题后可以尝试提高到0.1或0.05,加速收敛。
修改后的完整代码
import numpy as np import pandas as pd # 假设data已加载(比如通过pd.read_csv读取MNIST数据集) m, n = data.shape x_train = data[0:m,1:785] y_train = data[0:m,0] x_train = x_train / 255 # 归一化输入 def init_param(): w1 = np.random.randn(784,20) * np.sqrt(2/784) b1 = np.zeros((1,20)) w2 = np.random.randn(20,10) * np.sqrt(2/20) b2 = np.zeros((1,10)) return w1, b1, w2, b2 def ReLU(z): return np.maximum(z,0) def Softmax(z): z_stable = z - np.max(z, axis=1, keepdims=True) exp_z = np.exp(z_stable) return exp_z / np.sum(exp_z, axis=1, keepdims=True) def f_propagation(a0,w1,b1,w2,b2): z1 = a0.dot(w1) + b1 a1 = ReLU(z1) z2 = a1.dot(w2) + b2 a2 = Softmax(z2) return z1, a1, z2, a2 def dev_ReLU(z): return z > 0 def one_hotencode(y): y_hat = np.zeros((np.size(y),10)) y_hat[np.arange(y.size), y] = 1 return y_hat def b_propagation(x,y,z1,w1,a1,z2,w2,a2): y_hat = one_hotencode(y) dadc = a2 - y_hat dw2 = 1/m * (a1.T.dot(dadc)) db2 = 1/m * np.sum(dadc, axis=0) dw1 = 1/m * x.T.dot((w2.dot(dadc.T).T * dev_ReLU(z1))) db1 = 1/m * np.sum((w2.dot(dadc.T).T * dev_ReLU(z1)), axis=0) return dw2, db2, dw1, db1 def update_param(w1, b1, w2, b2, dw1, db1, dw2, db2, alpha): w1 = w1 - alpha * dw1 b1 = b1 - alpha * db1 w2 = w2 - alpha * dw2 b2 = b2 - alpha * db2 return w1, b1, w2, b2 def get_predictions(A2): return np.argmax(A2, axis=1) def get_accuracy(predictions, Y): return np.sum(predictions == Y) / Y.size def gradient_descent(x, y, alpha=0.1, iterations=500): w1, b1, w2, b2 = init_param() for i in range(iterations): z1, a1, z2, a2 = f_propagation(x,w1,b1,w2,b2) dw2, db2, dw1, db1 = b_propagation(x,y,z1,w1,a1,z2,w2,a2) w1, b1, w2, b2 = update_param(w1, b1, w2, b2, dw1, db1, dw2, db2, alpha) if i % 10 == 0: print(f"Iteration: {i}") predictions = get_predictions(a2) acc = get_accuracy(predictions, y) print(f"Accuracy: {acc:.4f}") return w1, b1, w2, b2 w1, b1, w2, b2 = gradient_descent(x_train, y_train, 0.1, 500)
效果说明
修复后,Softmax的数值溢出问题会消失,模型不会再收敛到单一数字,准确率会逐步上升(通常能达到90%以上)。
内容的提问来源于stack exchange,提问作者ENGSSG
相关产品推荐
相关产品推荐

