如何提高在MNIST上训练的模型的数字识别能力?


12

我正在使用进行手印多位数识别Java,使用OpenCV库进行预处理和分割,并使用KerasMNIST训练的模型(精度为0.98)进行识别。

除了一件事之外,这种识别似乎效果很好。网络经常无法识别那些(数字“一”)。我不知道这是否是由于分割的预处理/不正确的实现而发生的,或者是否是在标准MNIST上训练的网络没有看到看起来像我的测试用例的第一名。

这是经过预处理和分割后出现问题的数字的样子:

在此处输入图片说明成为在此处输入图片说明并归类为4

在此处输入图片说明成为在此处输入图片说明并归类为7

在此处输入图片说明成为在此处输入图片说明并归类为4。等等...

通过改进细分过程,可以解决此问题吗?还是通过增强培训设置?

编辑:增强训练集(数据扩充)肯定会有所帮助,这已经在我测试中,正确预处理的问题仍然存在。

我的预处理包括调整大小,转换为灰度,二值化,反转和膨胀。这是代码:

Mat resized = new Mat();
Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);

Mat grayscale = new Mat();
Imgproc.cvtColor(resized, grayscale, Imgproc.COLOR_BGR2GRAY);

Mat binImg = new Mat(grayscale.size(), CvType.CV_8U);
Imgproc.threshold(grayscale, binImg, 0, 255, Imgproc.THRESH_OTSU);

Mat inverted = new Mat();
Core.bitwise_not(binImg, inverted);

Mat dilated = new Mat(inverted.size(), CvType.CV_8U);
int dilation_size = 5;
Mat kernel = Imgproc.getStructuringElement(Imgproc.CV_SHAPE_CROSS, new Size(dilation_size, dilation_size));
Imgproc.dilate(inverted, dilated, kernel, new Point(-1,-1), 1);

然后将预处理后的图像分割成各个数字,如下所示:

List<Mat> digits = new ArrayList<>();
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(preprocessed.clone(), contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

// code to sort contours
// code to check that contour is a valid char

List rects = new ArrayList<>();

for (MatOfPoint contour : contours) {
     Rect boundingBox = Imgproc.boundingRect(contour);
     Rect rectCrop = new Rect(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height);

     rects.add(rectCrop);
}

for (int i = 0; i < rects.size(); i++) {
    Rect x = (Rect) rects.get(i);
    Mat digit = new Mat(preprocessed, x);

    int border = 50;
    Mat result = digit.clone();
    Core.copyMakeBorder(result, result, border, border, border, border, Core.BORDER_CONSTANT, new Scalar(0, 0, 0));

    Imgproc.resize(result, result, new Size(28, 28));
    digits.add(result);
}

1
您是使用蒙版还是使用(蒙版?)原始灰度像素作为分类的输入?
米卡,

@Micka我正在使用预处理(二值化,反转,扩张)版本。符合MNIST训练集的人。在我的帖子中有一些经过预处理的数字“ 1”的示例。
youngpanda

Answers:


5

我相信您的问题是扩张过程。我了解您希望规范化图像尺寸,但是您不应该破坏比例,您应该将一个轴调整为最大尺寸(该尺寸允许最大的缩放比例,而另一轴尺寸不超过最大尺寸)并填充具有背景色的图像的其余部分。并不是说“标准MNIST没看到过看起来像测试用例的数字”,而是让图像看起来像不同的受过训练的数字(被识别出的数字)

源图像和已处理图像的重叠

如果您维护了图像的正确长宽比(源图像和后处理图像),则可以看到您不仅调整了图像的大小,而且“扭曲了”它。可能是非均匀膨胀或尺寸调整不正确的结果


我相信@SiR具有一定的分量,请不要更改数字文字的长宽比。
ZdaR

抱歉,我不太了解。您认为我的扩张过程或尺寸调整过程是问题吗?我仅在此行开头调整图像大小Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);。这里的宽高比保持不变,我在哪里打破比例?
youngpanda

@SiR在上面的编辑中的回答:是的,我不只是调整图像的大小,而是应用不同的操作,其中之一是膨胀,这是一种形态学操作,会导致轻微的“变形”,因为它会导致图像中的明亮区域。图像“增长”还是您要在最后调整大小,我将图像制作为28x28?
youngpanda

@youngpanda,您可能会在这里发现讨论stackoverflow.com/questions/28525436/…很有趣。它可能会给您一个线索,说明您的方法为何无法带来良好的效果
SiR

@SiR谢谢您的链接,我熟悉LeNet,但很高兴再次阅读
youngpanda

5

已经发布了一些答案,但没有一个答案能回答您关于以下内容的实际问题 图像预处理的

就我而言,只要您的研究项目做得很好,我就不会发现您的实施存在任何重大问题。

但是要注意的一件事是您可能会错过。数学形态学中有一些基本运算:腐蚀和膨胀(您使用)。并且存在复杂的操作:基本操作的各种组合(例如,打开和关闭)。 Wikipedia链接不是最佳的简历参考,但是您可以先从中获得灵感。

通常,最好使用开孔而不是腐蚀,再用闭合而不是膨胀,因为在这种情况下,原始二进制图像的变化要小得多(但是达到了清洁尖锐边缘或填充间隙的理想效果)。因此,在您的情况下,您应检查关闭(图像扩散,然后使用相同的内核进行侵蚀)。如果即使使用1 * 1内核进行放大(如果1像素大于图像的16%),在放大时会大大修改8 * 8的小图像(在较大的图像上较小)。

以可视化的想法看下面的图片(来自OpenCV的教程:12):

扩张: 原始符号和膨胀的

闭幕: 原始符号和封闭符号

希望能帮助到你。


谢谢你的反馈!实际上,这不是一个研究项目,那又会是什么问题呢?..当我应用膨胀时,我的图像很大,8x8不是图像的大小,它是高度和宽度的大小调整因素。但是尝试不同的数学运算仍然可以作为一种改进选择。我不知道打开和关闭的情况,我会尝试一下!谢谢。
youngpanda

我的错,错误地读取了重新调整大小的调用,就像使用8 * 8作为新大小一样。如果您想在现实世界中使用OCR,则应考虑选择转移学习原始的使用网络数据的方法。至少检查它是否可以提高准确性,通常应该这样做。
f4f

要检查的另一件事是预处理顺序:灰度->二进制->逆->调整大小。调整大小是一项昂贵的操作,我认为无需将其应用于彩色图像。如果您有一些特定的输入格式,则可以在没有轮廓检测的情况下完成符号分割(成本较低),但是可能难以实现。
f4f

如果除MNIST之外还有其他数据集,我可以尝试转移学习:)我将尝试更改预处理顺序并返回给您。谢谢!对于我的问题,我没有比轮廓检测更容易的选择了……
youngpanda

1
好。您可以自己从将要使用OCR的图像中收集数据集,这是一种常见做法。
f4f

4

因此,您需要一种复杂的方法来使计算的每个步骤都基于先前的结果。在算法中,您具有以下功能:

  1. 图像预处理

如前所述,如果应用调整大小,则会丢失有关图像长宽比的信息。您必须对数字图像进行相同的重新处理,以获得与训练过程中相同的结果。

如果仅通过固定尺寸的图片裁切图像,则更好的方法。在该变体中,您无需在训练过程之前进行轮廓查找和调整数字图像的大小。然后,您可以对裁剪算法进行一些更改以更好地识别:只需找到轮廓并放置手指,而无需调整大小即可在相关图像帧的中心进行识别。

另外,您还应该更加注意二值化算法。我曾经研究过二值化阈值对学习错误的影响:我可以说这是一个非常重要的因素。您可以尝试使用其他二值化算法来检查此想法。例如,您可以使用此库来测试其他二值化算法。

  1. 学习算法

为了提高识别质量,您可以在训练过程中使用交叉验证。这可以帮助您避免过度拟合训练数据的问题。例如,您可以阅读这篇文章,其中解释了如何与Keras结合使用。

有时,较高的准确度度量无法说明真实的识别质量,这是因为受训练的ANN在训练数据中找不到模式。如上所述,它可能与训练过程或输入数据集有关,也可能是由ANN架构选择引起的。

  1. 人工神经网络架构

这是个大问题。如何定义更好的ANN架构来解决任务?没有通用的方法可以做到这一点。但是有几种方法可以接近理想。例如,您可以阅读这本书。它可以帮助您更好地解决问题。另外,您可能还会在这里找到一些启发式公式,以适合ANN的隐藏层/元素的数量。另外您会在此找到一些概述。

我希望这会有所帮助。


1.如果我对您的理解正确,则无法将其裁剪为固定大小,而是一张多位数的图片,并且所有情况下的大小/位置等均不同。或者您是说不同的意思吗?是的,如果您的意思是我尝试了不同的二值化方法和调整过的参数。2.实际上,在MNIST上的识别度很高,没有过拟合现象,我提到的精度是测试精度。网络或培训都不是问题。3.感谢所有的链接,尽管我对我的体系结构感到非常满意,但是总会有改进的余地。
youngpanda

是的,您明白了。但是您总是有可能使您的数据集更加统一。在您的情况下,最好像轮廓一样通过轮廓裁剪数字图像。但是在那之后,最好按照x和y比例将数字图像扩展到统一的大小,以符合数字图像的最大大小。您可能更喜欢手指轮廓区域的中心来执行此操作。它将为您的训练算法提供更干净的输入数据。
Egor Zamotaev

您是说我必须跳过膨胀吗?最后,当我应用边框(每侧50像素)时,我已经将图像居中。之后,我将每个数字的大小调整为28x28,因为这是MNIST所需的大小。您是说我可以将尺寸调整为28x28吗?
youngpanda

1
是的,扩张是不可取的。您的轮廓的高度和宽度比例可能不同,这就是为什么需要在此处改进算法的原因。至少您应该使图像尺寸具有相同的比率。由于您具有28x28的输入图片尺寸,因此必须按x和y比例以相同的1:1比例准备图像。您不应该为每个图片边获得50像素的边框,而应满足以下条件的X,Y像素边框:轮廓尺寸X + borderSizeX ==轮廓尺寸Y + borderSizeY。就这样。
Egor Zamotaev

我已经尝试了没有膨胀(忘记在帖子中提及)。它没有改变任何结果...我的边界号码是实验性的。理想情况下,我需要我的数字适合20x20的框(在数据集中按大小规格化),然后使用质心将其移位...
youngpanda

1

经过一些研究和实验,我得出的结论是图像预处理本身不是问题(我确实更改了一些建议的参数,例如膨胀大小和形状,但对结果并不重要)。但是,有帮助的是以下两个方面:

  1. 正如@ f4f所注意的那样,我需要使用真实数据收集自己的数据集。这已经极大地帮助了。

  2. 我对细分预处理进行了重要更改。获得单个轮廓后,我首先对图像进行尺寸规格化以适合20x20像素盒(如中所示MNIST)。之后,我使用质心将框居中于28x28图像的中间(对于二值图像,这是两个方向上的平均值)。

当然,仍然存在困难的分割情况,例如重叠的数字或相连的数字,但是上述更改回答了我的最初问题,并改善了分类性能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.