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

手写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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 10:24:54