我正在创建一个用于对数字图像进行分类的机器学习模型。我已经使用Tensorflow和Keras使用内置的tf.keras.datasets.mnist数据集训练了模型。该模型可以很好地与mnist数据集本身的测试图像配合使用,但是我想提供自己的图像。我正在为该模型提供的图像是从验证码中提取的,因此它们将遵循类似的模式。我在this公共google驱动器文件夹中包含了一些图像示例。当我提供这些图像时,我注意到该模型不是很准确,并且我对原因有一些猜测。


图像的背景在图像中产生过多的噪点。
该数字未居中。
图像不是MNIST训练集的颜色格式(黑色背景白色文本)。


我想问一下如何删除背景并居中,以减少图像中的噪点,从而实现更好的分类。

这是我正在使用的模型:

import tensorflow as tf
from tensorflow import keras

mnist = keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

class Stopper(keras.callbacks.Callback):
    def on_epoch_end(self, epoch, log={}):
        if log.get('acc') >= 0.99:
            self.model.stop_training = True
            print('\nReached 99% Accuracy. Stopping Training...')

model = keras.Sequential([
    keras.layers.Flatten(),
    keras.layers.Dense(1024, activation=tf.nn.relu),
    keras.layers.Dense(10, activation=tf.nn.softmax)])

model.compile(
    optimizer=tf.train.AdamOptimizer(),
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy'])

x_train, x_test = x_train / 255, x_test / 255

model.fit(x_train, y_train, epochs=10, callbacks=[Stopper()])


这是我将图像导入张量流的方法:

from PIL import Image
img = Image.open("image_file_path").convert('L').resize((28, 28), Image.ANTIALIAS)
img = np.array(img)
model.predict(img[None,:,:])


我还包括了MNIST数据集here中的一些示例。我想要一个脚本来尽可能将图像转换为MNIST数据集格式。另外,由于我必须对无限数量的图像执行此操作,因此,如果您能提供用于此转换的全自动方法,将不胜感激。非常感谢你。

最佳答案

您需要使用与要测试的图像相似的数据集进行训练。 MNIST数据是手写数字,不会类似于计算机生成的验证码数据字体。

您需要做的是获得与您所预测的相似的验证码数据目录(最好从您将输入到最终模型的相同来源)。捕获数据是一项艰巨的任务,在开始获取有用的东西之前,每个标签可能需要约300-400张图像。

重要说明:您的模型将永远仅与提供给模型的训练数据一样好。试图用不良的训练数据来建立一个好的模型是一种纯粹的挫折

解决您的一些想法:


  [该模型不是很准确,因为]图像的背景在图片中产生了太多的噪点。


这是真的。如果图像数据有噪声,并且未使用图像中的任何噪声训练神经网络,则当遇到这种类型的失真时,它将不会识别出强模式。解决此问题的一种可能方法是拍摄干净的图像,并在图像上接受噪声处理(在噪声中类似于在真实的验证码中看到的噪声),然后再将其发送给训练。


  [该模型不是很准确,因为]该数字未居中。


同样的道理也是如此。如果所有训练数据都居中,则将针对该属性对模型进行过调,并做出错误的猜测。如果您没有能力手动捕获和分类良好的数据采样,请遵循与上面类似的模式。


  [该模型不是很准确,因为]图像在MNIST训练集的颜色格式(黑色背景白色文本)中不准确。


您可以通过在处理之前/对输入的颜色进行标准化/归一化处理之前对数据应用二进制阈值来解决此问题。根据验证码中的噪声量,您可能会得到更好的结果,允许数字和噪声保留其某些颜色信息(仍保持灰度并标准化,只是不应用阈值)。



另外,我建议使用卷积网络而不是线性网络,因为它可以更好地区分2D要素(例如边和角)。即在使用keras.layers.Conv2D展平之前使用keras.layers.Flatten

请参见此处的出色示例:Trains a simple convnet on the MNIST dataset.

model = tf.keras.models.Sequential(
    [
        tf.keras.layers.Conv2D(
            32,
            kernel_size=(3, 3),
            activation=tf.nn.relu,
            input_shape=input_shape,
        ),
        tf.keras.layers.Conv2D(64, (3, 3), activation=tf.nn.relu),
        tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),
        tf.keras.layers.Dropout(0.25),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(128, activation=tf.nn.relu),
        tf.keras.layers.Dropout(0.5),
        tf.keras.layers.Dense(
            num_classes, activation=tf.nn.softmax
        ),
    ]
)


我已经使用此设置来读取视频游戏画面中的字体,并且通过设置10,000张图像的测试集,我达到了99.98%的准确性,在训练中使用了一半数据集的随机抽样,并使用总集来计算准确性。

08-24 21:06