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

时间:2019-10-15 16:23:35

标签: java opencv machine-learning image-recognition mnist

我正在使用JavaOpenCV库进行预处理和分割,以及在MNIST上训练的Keras模型(精度为0.98)来进行手印多位识别。承认。

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

以下是经过预处理和细分后出现问题的数字:

enter image description here变为enter image description here,并归类为4

enter image description here变为enter image description here,并归类为7

enter image description here变为enter image description here并归类为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);
}

4 个答案:

答案 0 :(得分:5)

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

Overlap of the source and processed images

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

答案 1 :(得分:5)

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

轮到我,只要您的研究项目做得好,我认为您的实施不会出现任何重大问题。

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

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

要形象化这个想法,请参见以下图片(来自OpenCV教程:12):

膨胀: original symbol and dilated one

关闭: original symbol and closed one

希望有帮助。

答案 2 :(得分:4)

因此,您需要一种复杂的方法,才能根据先前的结果进行计算级联的每一步。在您的算法中,您具有以下功能:

  1. 图像预处理

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

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

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

  1. 学习算法

要提高识别质量,您可以在训练过程中使用cross-validation。这有助于您避免训练数据出现overfitting的问题。例如,您可以阅读this article,其中解释了如何与Keras结合使用。

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

  1. ANN体系结构

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

我希望这会有所帮助。

答案 3 :(得分:1)

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

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

  2. 我对细分预处理进行了重要更改。得到各个轮廓后,我首先将图像尺寸规格化以适合20x20像素框(就像它们在MNIST中一样)。之后,我使用质量中心将框居中在28x28图像的中间(对于二进制图像,这是两个维度的平均值)。

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