MNIST模型准确率高但自制手写数字图片预测错误求助
我正在学习神经网络,首个练习是MNIST手写数字分类。模型在官方数据集上训练测试准确率约95%,但用画图工具自制的4张手写数字图片测试时,预测结果完全错误。我有C#、Java后端开发经验,但对Python和神经网络不熟悉。
我的代码
import tensorflow as tf from keras.datasets import mnist import keras import numpy as np import matplotlib.pyplot as plt import csv from PIL import Image import glob epochs = 10 learning_rate = 0.001 batch_size=16 def CreateAndTrainModel(epochs, learning_rate, batch_size): (x_train, y_train), (x_test, y_test) = mnist.load_data() x_train, x_test = tf.cast(x_train/255.0, tf.float32), tf.cast(x_test/255.0, tf.float32) x_train = np.array(x_train).reshape(-1,784) x_test = np.array(x_test).reshape(-1,784) model = tf.keras.models.Sequential() model.add(tf.keras.layers.Dense(784,input_dim=784)) model.add(tf.keras.layers.Dense(256, activation="relu")) model.add(tf.keras.layers.Dense(128, activation="relu")) model.add(tf.keras.layers.Dense(128, activation="relu")) model.add(tf.keras.layers.Dense(10, activation="softmax")) optimizer = keras.optimizers.SGD(learning_rate) model.compile(optimizer=optimizer, loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs) model.evaluate(x_test, y_test) mse, bias, var = bias_variance_decomp(model, x_train, y_train, x_test, y_test, loss='mse', num_rounds=200, random_seed=1) return model def GetData(): file = open("CSV\data.csv", "r") data = list(csv.reader(file, delimiter=",")) file.close() return data def getImageList(): image_list = [] for filename in glob.glob('Images/*.png'): im = Image.open(filename) image_list.append(im) return image_list def GetFileNameOnly(path): return path.filename.split('\\')[1].split('.')[0] def GetCorrectAnswerByFilePath(path): key = GetFileNameOnly(path) answer = -1 for dataValues in data: if (dataValues[0] == key): answer = dataValues[1] return answer def ShowAllImages(image_list_length): for i in range(image_list_length): print(GetFileNameOnly(image_list[i]), GetCorrectAnswerByFilePath(image_list[i])) plt.imshow(image_list[i]) plt.show() def PredictImage(image): image = tf.image.rgb_to_grayscale(image) image = np.array(image).reshape(-1, 784) predicted_answer = np.argmax(model.predict(image.reshape(-1, 784)), 1) return predicted_answer #Main data = GetData() model = CreateAndTrainModel(epochs, learning_rate, batch_size) print(data) image_list = getImageList() image_list_length = (len(image_list)) for image in image_list: correct_answer = GetCorrectAnswerByFilePath(image) fileName = GetFileNameOnly(image) predicted_answer = PredictImage(image) print("File Name: ", fileName, "Predicted Answer", predicted_answer, "Actual answer", correct_answer)
训练与测试结果
官方数据集训练测试结果:
Epoch 10/10
3750/3750 [] - 9s 2ms/step - loss: 0.1733 - accuracy: 0.9500
313/313 [] - 1s 2ms/step - loss: 0.1688 - accuracy: 0.9506
自制图片预测结果:
File Name: a Predicted Answer [3] Actual answer 4
File Name: b Predicted Answer [2] Actual answer 8
File Name: c Predicted Answer [3] Actual answer 1
File Name: d Predicted Answer [3] Actual answer 2
我尝试调整batch size、epochs、网络层数和神经元数量等参数,但大多情况预测结果仍不正确。怀疑可能的原因:1. 是否存在过拟合?2. 代码是否存在问题?3. MNIST模型是否不适用于此类自制图片?希望得到排查建议。
排查建议
1. 优先修正自制图片与MNIST的格式差异
MNIST数据集的图片有固定标准:28x28像素、白底黑字(背景白,数字黑,0为黑、255为白)、数字居中且占满大部分画布,你的自制图片大概率不符合这些要求:
- 检查尺寸:必须缩放到28x28,缩放时要保持比例,避免拉伸变形(比如先把数字缩到20x20,再填充到28x28)
- 检查颜色反转:如果你的图片是黑底白字,必须反转像素值(
255 - image),模型是在白底黑字的数据上训练的 - 检查通道:MNIST是单通道灰度图,PNG可能带透明通道,需转为纯灰度图
- 检查数字位置:确保数字居中,避免偏边或过小
2. 修复预测前的预处理逻辑错误
你的PredictImage函数有核心问题:
- 未对自制图片做归一化(除以255),训练时输入是
x_train/255.0,但预测时像素值还是0-255,输入分布完全不一致 - 直接用
tf.image.rgb_to_grayscale处理PIL图片可能导致维度混乱 - 修正后的示例代码:
def PredictImage(image): # 转为灰度图 image = image.convert('L') # 缩放到28x28 image = image.resize((28, 28)) # 转为numpy数组 image_np = np.array(image) # 若为黑底白字则反转颜色 # image_np = 255 - image_np # 归一化到0-1区间,和训练数据对齐 image_np = image_np / 255.0 # 调整形状为(1,784),匹配模型输入 image_np = image_np.reshape(-1, 784) # 预测(关闭冗余打印) predicted_answer = np.argmax(model.predict(image_np, verbose=0), 1) return predicted_answer
3. 排除过拟合可能
从训练和测试准确率来看,两者均为95%左右,没有出现训练准确率远高于测试的情况,因此过拟合不是当前问题的原因。
4. 优化模型细节
- 输入层
Dense(784, input_dim=784)无激活函数,可添加relu或直接删除该层 - SGD优化器在0.001的学习率下收敛较慢,建议换成
Adam优化器:
optimizer = keras.optimizers.Adam(learning_rate=learning_rate)
5. 验证预处理效果
在预测前显示预处理后的图片,确认它和MNIST的图片视觉一致:
def PredictImage(image): # ... 预处理步骤 ... plt.imshow(image_np.reshape(28,28), cmap='gray') plt.show() # ... 预测 ...
如果预处理后的图片和MNIST数字风格一致,预测结果大概率会正确。
内容的提问来源于stack exchange,提问作者EvgenieT

