基于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')
关键修改点:
- 保留图片的28x28形状,并增加通道维度(单通道灰度图),转为4维张量。
- 移除提前归一化的代码,改用
ImageDataGenerator的rescale参数统一处理。 - 替换为CNN模型,充分发挥数据增强的作用。
- 调整位移参数为相对值(如
width_shift_range=0.1),适配图片尺寸。
内容的提问来源于stack exchange,提问作者user20396381
相关产品推荐
相关产品推荐

