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

基于MNIST训练的Python AI模型自定义图片识别准确率低问题求助

MNIST手写数字模型适配自有图片及数据增强报错问题

一、模型现状与问题

基于MNIST数据集训练的0-9数字识别模型,训练/测试准确率达97.99%,但识别自有手写数字图片时准确率不足20%,典型错误包括:数字2被识别为7、数字3被识别为6、数字5被识别为2。

自有手写数字示例:
自有手写数字示例

原始训练及识别代码:

import keras
from keras.datasets import mnist
import matplotlib.pyplot as plt
import PIL
from PIL import Image
(train_images,train_labels),(test_images,test_labels) = mnist.load_data()

from keras import models
from keras import layers
network = models.Sequential()
network.add(layers.Dense(512,activation='relu',input_shape=(28*28,)))
network.add(layers.Dense(10,activation='softmax'))

network.compile(optimizer='rmsprop',
                loss='categorical_crossentropy',
                metrics=['accuracy'])

train_images = train_images.reshape((60000,28*28))
train_images = train_images.astype('float32')/255
test_images = test_images.reshape((10000,28*28))
test_images = test_images.astype('float32')/255

from keras.utils import to_categorical
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

network.fit(train_images,train_labels,epochs= 3 ,batch_size=128)
test_loss , test_acc = network.evaluate(test_images,test_labels)
print('test_acc:',test_acc)
network.save('m_lenet.h5')

# 识别自有图片部分
import numpy as np
from keras.models import load_model
model = load_model('/content/m_lenet.h5')

picPath = '/content/02_a.png'
img = Image.open(picPath)
reIm = img.resize((28,28),Image.ANTIALIAS)
plt.imshow(reIm)
plt.savefig('/content/result.png')

im1 = np.array(reIm.convert("L"))
im1 = im1.reshape((1,28*28))
im1 = im1.astype('float32')/255

predict_x=model.predict(im1) 
classes_x=np.argmax(predict_x,axis=1)

print ("---------------------------------")
print ('predict as:')
print (predict_x)
print ("")
print ('predict number as:')
print (classes_x)
print ("---------------------------------")
print ("Original img : ")

二、解决方案疑问

目前想到两个优化方向:

  • 将自有带标注图片加入训练集
  • 增加模型层数

请问是否有其他可行方案?若仅以上两个方案,需告知具体实现方法(如如何将自有标注图片加入训练流程)。

三、数据增强代码报错问题

尝试用Keras的ImageDataGenerator做数据增强,修改后的代码如下:

import keras
from keras.datasets import mnist
import matplotlib.pyplot as plt
import PIL
from PIL import Image
(train_images,train_labels),(test_images,test_labels) = mnist.load_data()

from keras import models
from keras import layers
network = models.Sequential()
network.add(layers.Dense(512,activation='relu',input_shape=(28*28,)))
network.add(layers.Dense(10,activation='softmax'))

network.compile(optimizer='rmsprop',
                loss='categorical_crossentropy',
                metrics=['accuracy'])

train_images = train_images.reshape((60000,28*28))
train_images = train_images.astype('float32')/255
test_images = test_images.reshape((10000,28*28))
test_images = test_images.astype('float32')/255

from keras.utils import to_categorical
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

network.fit(train_images,train_labels,epochs= 3 ,batch_size=128)

# 数据增强部分
from tensorflow.keras.preprocessing.image import ImageDataGenerator
data_generator = ImageDataGenerator(rotation_range=10,
                   width_shift_range=8,
                   height_shift_range=8,
                   brightness_range=[0.6,1.1],
                   zoom_range=.15,
                   validation_split=.2,
                   rescale=1./255)

train_dataset = data_generator.flow(train_images, train_labels, batch_size=32, subset='training')
validation_dataset = data_generator.flow(train_images, train_labels, batch_size=32, subset='validation')

network.fit(train_dataset, validation_data=validation_dataset, epochs=30)
test_loss , test_acc = network.evaluate(test_images,test_labels)
print('test_acc:',test_acc)
network.save('m_lenet.h5')

# 识别自有图片部分
import numpy as np
from keras.models import load_model
model = load_model('/content/m_lenet.h5')

picPath = '/content/02_a.png'
img = Image.open(picPath)
reIm = img.resize((28,28),Image.ANTIALIAS)
plt.imshow(reIm)
plt.savefig('/content/result.png')

im1 = np.array(reIm.convert("L"))
im1 = im1.reshape((1,28*28))
im1 = im1.astype('float32')/255

predict_x=model.predict(im1) 
classes_x=np.argmax(predict_x,axis=1)

print ("---------------------------------")
print ('predict as:')
print (predict_x)
print ("")
print ('predict number as:')
print (classes_x)
print ("---------------------------------")
print ("Original img : ")

运行时报错:

Epoch 1/3
469/469 [==============================] - 10s 15ms/step - loss: 0.2555 - accuracy: 0.9268
Epoch 2/3
469/469 [==============================] - 5s 10ms/step - loss: 0.1023 - accuracy: 0.9695
Epoch 3/3
469/469 [==============================] - 5s 10ms/step - loss: 0.0678 - accuracy: 0.9796
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-11-476f532516e9> in <module>
     51                    rescale=1./255)
     52 
---> 53 train_dataset = data_generator.flow(train_images, train_labels, batch_size=32, subset='training')
     54 validation_dataset = data_generator.flow(train_images, train_labels, batch_size=32, subset='validation')
     55 

1 frames
/usr/local/lib/python3.7/dist-packages/keras/preprocessing/image.py in __init__(self, x, y, image_data_generator, batch_size, shuffle, sample_weight, seed, data_format, save_to_dir, save_prefix, save_format, subset, ignore_class_split, dtype)
    675           'Input data in `NumpyArrayIterator` '
    676           'should have rank 4. You passed an array '
    677           'with shape', self.x.shape)
    678     channels_axis = 3 if data_format == 'channels_last' else 1
    679     if self.x.shape[channels_axis] not in {1, 3, 4}:

ValueError: ('Input data in `NumpyArrayIterator` should have rank 4. You passed an array with shape', (48000, 784))

解决方案

一、自有图片识别准确率低的优化方案

1. 自有标注图片加入训练集的具体实现

步骤:
  • 数据准备:将自有手写图片整理为28x28单通道灰度图,对应每个图片创建标注标签(0-9),确保数据分布均衡(每个数字的样本数尽量接近)。
  • 数据预处理:
    import os
    import numpy as np
    from PIL import Image
    from keras.utils import to_categorical
    
    # 加载自有数据集
    own_data = []
    own_labels = []
    own_data_dir = "/path/to/your/own/data"  # 自有图片目录,按数字分文件夹或文件名标注
    
    for filename in os.listdir(own_data_dir):
        if filename.endswith(".png") or filename.endswith(".jpg"):
            # 读取图片并预处理
            img = Image.open(os.path.join(own_data_dir, filename)).convert("L")
            img = img.resize((28,28), Image.ANTIALIAS)
            img_array = np.array(img)
            # 反转颜色(如果自有图片是白底黑字,MNIST是黑底白字)
            img_array = 255 - img_array
            own_data.append(img_array)
            # 从文件名或目录获取标签,示例:文件名如"2_001.png",取第一个字符
            label = int(filename.split("_")[0])
            own_labels.append(label)
    
    # 转为numpy数组并归一化
    own_data = np.array(own_data).reshape((len(own_data), 28*28)).astype('float32')/255
    own_labels = to_categorical(np.array(own_labels), 10)
    
    # 合并MNIST训练集和自有数据集
    combined_train_images = np.concatenate((train_images, own_data), axis=0)
    combined_train_labels = np.concatenate((train_labels, own_labels), axis=0)
    
  • 重新训练模型:使用合并后的数据集进行训练,可以选择从头训练或在原模型基础上微调。

2. 其他优化方案

  • 输入预处理对齐:确保自有图片的预处理逻辑和MNIST完全一致,包括颜色反转(MNIST是黑底白字,若自有图片是白底黑字必须反转)、归一化方式(除以255)、尺寸(28x28)。
  • 改用卷积神经网络(CNN):当前使用的全连接网络对图像空间特征提取能力弱,CNN更适合手写数字识别,比如经典的LeNet-5结构,能有效提升对自有图片的泛化能力。
  • 增加正则化:在模型中加入Dropout层或L2正则,防止模型过拟合到MNIST数据集,提升泛化性:
    network = models.Sequential()
    network.add(layers.Dense(512,activation='relu',input_shape=(28*28,), kernel_regularizer=keras.regularizers.l2(0.001)))
    network.add(layers.Dropout(0.5))
    network.add(layers.Dense(10,activation='softmax'))
    
  • 模型微调:先在MNIST上训练基础模型,再用自有数据集进行小批量、低学习率的微调,让模型适配自有手写风格。

二、数据增强代码报错的解决

报错原因:

ImageDataGenerator要求输入为4维张量(样本数,高度,宽度,通道数),但当前train_images被reshape为(60000,784)的2维数组,不符合要求。同时全连接网络使用数据增强的收益极低,建议改用CNN结构配合数据增强。

修改后的代码:

import keras
from keras.datasets import mnist
from keras import models
from keras import layers
from keras.utils import to_categorical
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 加载数据
(train_images,train_labels),(test_images,test_labels) = mnist.load_data()

# 调整数据形状为4维(样本数,28,28,1),适配CNN和数据增强
train_images = train_images.reshape((60000,28,28,1)).astype('float32')
test_images = test_images.reshape((10000,28,28,1)).astype('float32')

# 标签转独热编码
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

# 定义CNN模型
network = models.Sequential()
network.add(layers.Conv2D(32, (3,3), activation='relu', input_shape=(28,28,1)))
network.add(layers.MaxPooling2D((2,2)))
network.add(layers.Conv2D(64, (3,3), activation='relu'))
network.add(layers.MaxPooling2D((2,2)))
network.add(layers.Flatten())
network.add(layers.Dense(64, activation='relu'))
network.add(layers.Dense(10, activation='softmax'))

network.compile(optimizer='rmsprop',
                loss='categorical_crossentropy',
                metrics=['accuracy'])

# 初始化数据增强生成器
data_generator = ImageDataGenerator(rotation_range=10,
                   width_shift_range=0.1,
                   height_shift_range=0.1,
                   brightness_range=[0.6,1.1],
                   zoom_range=0.15,
                   validation_split=0.2,
                   rescale=1./255)

# 生成训练和验证数据集
train_dataset = data_generator.flow(train_images, train_labels, batch_size=32, subset='training')
validation_dataset = data_generator.flow(train_images, train_labels, batch_size=32, subset='validation')

# 训练模型
network.fit(train_dataset, validation_data=validation_dataset, epochs=10)

# 测试模型
test_loss , test_acc = network.evaluate(test_images/255, test_labels)
print('test_acc:',test_acc)
network.save('mnist_cnn_aug.h5')

关键修改点:

  1. 保留图片的28x28形状,并增加通道维度(单通道灰度图),转为4维张量。
  2. 移除提前归一化的代码,改用ImageDataGenerator的rescale参数统一处理。
  3. 替换为CNN模型,充分发挥数据增强的作用。
  4. 调整位移参数为相对值(如width_shift_range=0.1),适配图片尺寸。

内容的提问来源于stack exchange,提问作者user20396381

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 06:11:00