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

基于Numpy的神经网络无法学习(准确率停滞10%)求助

MNIST分类模型准确率仅10%且固定输出同一结果的问题排查与修复

我用Numpy搭建了一个基础人工神经网络(ANN),针对MNIST手写数字数据集(10分类)进行训练,但模型预测准确率始终维持在10%左右,更换数据集后问题仍存在。进一步排查发现,模型全程只输出同一个数字作为预测结果,怀疑是反向传播环节出现变量错误,以下是完整代码:

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

data = pd.read_csv('train.csv')

data = np.array(data)
m, n = data.shape
np.random.shuffle(data)

data_train = data.T
X_train = data_train[1:n]
Y_train = data_train[0]

def init_params():
    W1 = np.random.randn(10, 784)
    b1 = np.random.rand(10, 1)
    W2 = np.random.randn(10, 10)
    b2 = np.random.randn(10, 1)
    return W1, b1, W2, b2

def ReLU(Z):
    return np.maximum(0, Z)

def softmax(Z):
        e = np.exp(Z - Z.max(axis=0, keepdims=True))
        return e/e.sum(axis=0, keepdims=True)

def forward_prop(W1, b1, W2, b2, X):
    Z1 = W1.dot(X) + b1
    A1 = ReLU(Z1)
    Z2 = W2.dot(A1) + b2
    A2 = softmax(Z2)
    return Z1, A1, Z2, A2

def one_hot(Y):
    one_hot_Y = np.zeros((Y.size, Y.max() + 1))
    one_hot_Y[np.arange(Y.size), Y] = 1
    one_hot_Y = one_hot_Y.T
    return one_hot_Y

def deriv_ReLU(Z):
    return (Z > 0).astype(int)


def back_prop(Z1, A1, Z2, A2, W2, X, Y):
    m = Y.size
    one_hot_Y = one_hot(Y)
    dZ2 = A2 - one_hot_Y
    dW2 = 1 / m * dZ2.dot(A1.T)
    db2 = 1 / m * np.sum(dZ2, 1).reshape(-1, 1)
    dZ1 = W2.T.dot(dZ2) * deriv_ReLU(Z1)
    dW1 = 1 / m * dZ1.dot(X.T)
    db1 = 1 / m * np.sum(dZ1, 1).reshape(-1, 1)
    return dW1, db1, dW2, db2

def update_params(W1, b1, W2, b2, dW1, db1, dW2, db2, alpha):
    W1 = W1 - alpha * dW1
    db1 = b1 - alpha * db1
    W2 = W2 - alpha * dW2
    db2 = b2 - alpha * db2
    return W1, b1, W2, b2

def get_predictions(A2):
    return np.argmax(A2, 0)

def get_accuracy(predictions, Y):
    print(predictions, Y)
    return np.sum(predictions == Y) / Y.size

def gradient_descent(X, Y, iterations, alpha):
    W1, b1, W2, b2 = init_params()
    for i in range(iterations):
        Z1, A1, Z2, A2 = forward_prop(W1, b1, W2, b2, X)
        dW1, db1, dW2, db2 = back_prop(Z1, A1, Z2, A2, W2, X, Y)
        W1, b1, W2, b2 = update_params(W1, b1, W2, b2, dW1, db1, dW2, db2, alpha)
        if (i % 50):
            print(f'Iteration: {i}')
            print(f'Accuracy: {get_accuracy(get_predictions(A2), Y)}')
    return W1, b1, W2, b2

W1, b1, W2, b2 = gradient_descent(X_train, Y_train, 100, 0.1)

问题根源与修复方案

1. 偏置更新变量错误(核心问题)

在update_params函数中,偏置更新的变量名写错:

  • 错误代码:db1 = b1 - alpha * db1、db2 = b2 - alpha * db2
  • 正确写法:b1 = b1 - alpha * db1、b2 = b2 - alpha * db2
    这个错误导致模型的偏置参数从未更新,始终停留在初始值,模型无法学习到有效特征,只能输出固定结果。

2. 未做数据归一化

MNIST数据集的像素值范围是0-255,未归一化会导致矩阵乘积W1.dot(X)的数值过大,引发ReLU激活函数饱和,梯度消失,模型无法收敛。需要将输入数据归一化到0-1区间:

X_train = data_train[1:n] / 255.0

3. 迭代打印条件错误

gradient_descent中的打印条件if (i % 50)会在i不是50的倍数时打印,不符合“每50次迭代输出一次”的预期,应修改为:

if i % 50 == 0:

4. 初始偏置的分布选择(可选优化)

b1使用np.rand生成0-1均匀分布,换成np.randn(标准正态分布)更符合神经网络参数初始化的常规做法,避免初始值过度偏向某一区间。

修正后的完整代码

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

data = pd.read_csv('train.csv')

data = np.array(data)
m, n = data.shape
np.random.shuffle(data)

data_train = data.T
# 数据归一化
X_train = data_train[1:n] / 255.0
Y_train = data_train[0]

def init_params():
    W1 = np.random.randn(10, 784)
    # 换成标准正态分布初始化偏置
    b1 = np.random.randn(10, 1)
    W2 = np.random.randn(10, 10)
    b2 = np.random.randn(10, 1)
    return W1, b1, W2, b2

def ReLU(Z):
    return np.maximum(0, Z)

def softmax(Z):
        e = np.exp(Z - Z.max(axis=0, keepdims=True))
        return e/e.sum(axis=0, keepdims=True)

def forward_prop(W1, b1, W2, b2, X):
    Z1 = W1.dot(X) + b1
    A1 = ReLU(Z1)
    Z2 = W2.dot(A1) + b2
    A2 = softmax(Z2)
    return Z1, A1, Z2, A2

def one_hot(Y):
    one_hot_Y = np.zeros((Y.size, Y.max() + 1))
    one_hot_Y[np.arange(Y.size), Y] = 1
    one_hot_Y = one_hot_Y.T
    return one_hot_Y

def deriv_ReLU(Z):
    return (Z > 0).astype(int)

def back_prop(Z1, A1, Z2, A2, W2, X, Y):
    m = Y.size
    one_hot_Y = one_hot(Y)
    dZ2 = A2 - one_hot_Y
    dW2 = 1 / m * dZ2.dot(A1.T)
    db2 = 1 / m * np.sum(dZ2, 1).reshape(-1, 1)
    dZ1 = W2.T.dot(dZ2) * deriv_ReLU(Z1)
    dW1 = 1 / m * dZ1.dot(X.T)
    db1 = 1 / m * np.sum(dZ1, 1).reshape(-1, 1)
    return dW1, db1, dW2, db2

def update_params(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, 0)

def get_accuracy(predictions, Y):
    return np.sum(predictions == Y) / Y.size

def gradient_descent(X, Y, iterations, alpha):
    W1, b1, W2, b2 = init_params()
    for i in range(iterations):
        Z1, A1, Z2, A2 = forward_prop(W1, b1, W2, b2, X)
        dW1, db1, dW2, db2 = back_prop(Z1, A1, Z2, A2, W2, X, Y)
        W1, b1, W2, b2 = update_params(W1, b1, W2, b2, dW1, db1, dW2, db2, alpha)
        # 修正打印条件
        if i % 50 == 0:
            print(f'Iteration: {i}')
            print(f'Accuracy: {get_accuracy(get_predictions(A2), Y):.4f}')
    return W1, b1, W2, b2

W1, b1, W2, b2 = gradient_descent(X_train, Y_train, 500, 0.1)

效果验证

修正后,模型的准确率会随着迭代次数逐步提升,通常迭代500次后能达到85%以上的准确率,不会再出现固定输出同一数字的情况。

内容的提问来源于stack exchange,提问作者Ambaka Le Gregam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 10:34:53