Làm cách nào để cải thiện nhận dạng chữ số của một mô hình được đào tạo trên MNIST?


12

Tôi đang làm việc với nhận dạng nhiều chữ số được in bằng tay Java, sử dụng OpenCVthư viện để tiền xử lý và phân đoạn, và một Kerasmô hình được đào tạo trên MNIST (với độ chính xác 0,98) để nhận dạng.

Sự công nhận dường như hoạt động khá tốt, ngoài một điều. Mạng thường không nhận ra được cái nào (số "một"). Tôi không thể biết liệu điều đó xảy ra do quá trình tiền xử lý / triển khai phân đoạn không chính xác hay nếu một mạng được đào tạo trên MNIST tiêu chuẩn chỉ không thấy số một giống như các trường hợp thử nghiệm của tôi.

Đây là những chữ số có vấn đề trông như thế nào sau khi tiền xử lý và phân đoạn:

nhập mô tả hình ảnh ở đâytrở thành nhập mô tả hình ảnh ở đâyvà được phân loại là 4.

nhập mô tả hình ảnh ở đâytrở thành nhập mô tả hình ảnh ở đâyvà được phân loại là 7.

nhập mô tả hình ảnh ở đâytrở thành nhập mô tả hình ảnh ở đâyvà được phân loại là 4. Và như thế...

Đây có phải là một cái gì đó có thể được sửa chữa bằng cách cải thiện quá trình phân khúc? Hay đúng hơn là bằng cách tăng cường tập huấn luyện?

Chỉnh sửa: Tăng cường tập huấn luyện (tăng dữ liệu) chắc chắn sẽ giúp ích, mà tôi đã thử nghiệm, câu hỏi về tiền xử lý chính xác vẫn còn.

Quá trình tiền xử lý của tôi bao gồm thay đổi kích thước, chuyển đổi sang thang độ xám, nhị phân, đảo ngược và giãn nở. Đây là mã:

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);

Hình ảnh được xử lý trước sau đó được phân đoạn thành các chữ số riêng lẻ như sau:

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
bạn có đang sử dụng mặt nạ hoặc các pixel màu xám gốc (mặt nạ?) làm đầu vào cho phân loại của bạn không?
Micka

@Micka Tôi đang sử dụng phiên bản đã xử lý trước (binarized, inverted, giãn). Những người phù hợp với tập huấn luyện của MNIST. Có những ví dụ về số "1" sau khi tiền xử lý trong bài viết của tôi.
youngpanda

Câu trả lời:


5

Tôi tin rằng vấn đề của bạn là quá trình giãn nở. Tôi hiểu rằng bạn muốn bình thường hóa kích thước hình ảnh, nhưng bạn không nên phá vỡ tỷ lệ, bạn nên thay đổi kích thước thành tối đa theo một trục (một trục cho phép tỷ lệ lại lớn nhất mà không để kích thước trục khác vượt quá kích thước tối đa) và điền vào với màu nền phần còn lại của hình ảnh. Không phải là "MNIST tiêu chuẩn chưa thấy số một giống như trường hợp thử nghiệm của bạn", bạn làm cho hình ảnh của mình trông giống như các số được đào tạo khác nhau (những cái được nhận dạng)

Chồng chéo nguồn và hình ảnh được xử lý

Nếu bạn duy trì tỷ lệ khung hình chính xác cho hình ảnh của mình (nguồn và xử lý hậu kỳ), bạn có thể thấy rằng bạn không chỉ thay đổi kích thước hình ảnh mà còn "bóp méo" nó. Nó có thể là kết quả của sự giãn nở không đồng nhất hoặc thay đổi kích thước không chính xác


Tôi tin rằng @SiR có trọng lượng nào đó, Hãy thử không thay đổi tỷ lệ khung hình của chữ số.
ZdaR

Xin lỗi, tôi không làm theo. Bạn có nghĩ rằng quá trình giãn nở hoặc quá trình thay đổi kích thước của tôi là vấn đề? Tôi chỉ thay đổi kích thước hình ảnh lúc đầu với dòng này Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);. Ở đây khẩu phần khía cạnh giữ nguyên, tôi phá vỡ tỷ lệ ở đâu?
youngpanda

@SiR trả lời về các chỉnh sửa của bạn ở trên: có, tôi không chỉ thay đổi kích thước hình ảnh, tôi áp dụng các thao tác khác nhau, một trong số đó là sự giãn nở, đó là một hình thái, gây ra "biến dạng" nhẹ vì nó gây ra các vùng sáng trong hình ảnh để phát triển ". Hay bạn có nghĩa là thay đổi kích thước cuối cùng, nơi tôi thực hiện các hình ảnh 28x28?
youngpanda

@youngpanda, bạn có thể tìm thấy cuộc thảo luận ở đây stackoverflow.com/questions/28525436/ . Thú vị. Nó có thể cho bạn manh mối tại sao cách tiếp cận của bạn không mang lại kết quả tốt
SiR

@SiR cảm ơn bạn đã liên kết, tôi quen thuộc với LeNet, nhưng thật tuyệt khi đọc lại
youngpanda

5

Đã có một số câu trả lời được đăng nhưng không ai trong số họ trả lời câu hỏi thực tế của bạn về tiền xử lý hình ảnh .

Đến lượt tôi, tôi không thấy bất kỳ vấn đề quan trọng nào với việc triển khai của bạn miễn là đó là một dự án nghiên cứu, được thực hiện tốt.

Nhưng một điều cần chú ý bạn có thể bỏ lỡ. Có các hoạt động cơ bản trong hình thái toán học: xói mòn và giãn nở (được sử dụng bởi bạn). Và có các hoạt động phức tạp: sự kết hợp khác nhau của những cái cơ bản (ví dụ: mở và đóng). Liên kết Wikipedia không phải là tài liệu tham khảo CV tốt nhất, nhưng bạn có thể bắt đầu với nó để có ý tưởng.

Thông thường tốt hơn là sử dụng mở thay vì xói mònđóng thay vì giãn nở vì trong trường hợp này, hình ảnh nhị phân ban đầu thay đổi ít hơn nhiều (nhưng đạt được hiệu quả mong muốn của việc làm sạch các cạnh sắc hoặc lấp đầy các khoảng trống). Vì vậy, trong trường hợp của bạn, bạn nên kiểm tra đóng (sự giãn nở hình ảnh theo sau là sự xói mòn với cùng một hạt nhân). Trong trường hợp hình ảnh siêu nhỏ 8 * 8 bị thay đổi rất nhiều khi bạn giãn ngay cả với hạt nhân 1 * 1 (1 pixel nhiều hơn 16% hình ảnh) ít hơn trên hình ảnh lớn hơn).

Để hình dung ý tưởng, hãy xem các bức ảnh sau (từ hướng dẫn OpenCV: 1 , 2 ):

sự giãn nở: biểu tượng gốc và giãn một

đóng cửa: biểu tượng gốc và đóng một

Hy vọng nó giúp.


Cảm ơn bạn đã nhập! Trên thực tế nó không phải là một dự án nghiên cứu, vậy thì vấn đề sẽ là gì? .. Hình ảnh của tôi khá lớn khi tôi áp dụng độ giãn nở, 8x8 không phải là kích thước của hình ảnh, đó là yếu tố thay đổi kích thước cho chiều cao và chiều rộng. Nhưng nó vẫn có thể là một lựa chọn cải tiến để thử các hoạt động toán học khác nhau. Tôi không biết về việc mở và đóng cửa, tôi sẽ thử! Cảm ơn bạn.
youngpanda

Lỗi của tôi, đọc sai kích thước thay đổi cuộc gọi như với kích thước mới 8 * 8. Trong trường hợp bạn muốn sử dụng OCR trong thế giới thực, bạn sẽ xem xét tùy chọn chuyển đổi học tập mạng gốc của bạn trên dữ liệu điển hình cho khu vực bạn sử dụng. Ít nhất là kiểm tra xem nó có cải thiện độ chính xác hay không, nói chung là nên làm.
f4f

Một thứ khác để kiểm tra là thứ tự tiền xử lý: grayscale-> binary-> inverse-> resize. Thay đổi kích thước là một hoạt động tốn kém và tôi không thấy cần phải áp dụng nó cho hình ảnh màu. Và phân đoạn biểu tượng có thể được thực hiện mà không phát hiện đường viền (với chi phí thấp hơn) nếu bạn có một số định dạng đầu vào cụ thể nhưng có thể khó thực hiện.
f4f

Nếu tôi có một tập dữ liệu khác ngoài MNIST, tôi có thể thử chuyển học :) Tôi sẽ cố gắng thay đổi thứ tự tiền xử lý và lấy lại cho bạn. Cảm ơn bạn! Tôi chưa tìm thấy tùy chọn nào dễ dàng hơn phát hiện đường viền cho vấn đề của mình ...
youngpanda

1
Đồng ý. Bạn có thể tự thu thập dữ liệu từ những hình ảnh mà bạn sẽ sử dụng OCR theo cách thông thường.
f4f

4

Vì vậy, bạn cần một cách tiếp cận phức tạp gây ra từng bước trong tầng tính toán của bạn dựa trên các kết quả trước đó. Trong thuật toán của bạn, bạn có các tính năng tiếp theo:

  1. Tiền xử lý hình ảnh

Như đã đề cập trước đó, nếu bạn áp dụng thay đổi kích thước, thì bạn sẽ mất thông tin về tỷ lệ khung hình của hình ảnh. Bạn phải thực hiện cùng một quá trình xử lý lại các hình ảnh chữ số để có được kết quả tương tự như đã được ngụ ý trong quá trình đào tạo.

Cách tốt hơn nếu bạn chỉ cắt hình ảnh bằng hình ảnh kích thước cố định. Trong biến thể đó, bạn sẽ không cần tìm đường viền và thay đổi kích thước hình ảnh chữ số trước khi quá trình đào tạo. Sau đó, bạn có thể thực hiện một chút thay đổi trong thuật toán cắt ảnh của mình để nhận biết rõ hơn: đơn giản tìm đường viền và đặt chữ số của bạn mà không thay đổi kích thước ở giữa khung hình có liên quan để nhận dạng.

Ngoài ra, bạn nên chú ý nhiều hơn đến thuật toán binarization. Tôi đã có kinh nghiệm nghiên cứu ảnh hưởng của các giá trị ngưỡng nhị phân đến lỗi học tập: tôi có thể nói rằng đây là một yếu tố rất quan trọng. Bạn có thể thử một thuật toán nhị phân khác để kiểm tra ý tưởng này. Ví dụ, bạn có thể sử dụng thư viện này để thử nghiệm các thuật toán nhị phân thay thế.

  1. Thuật toán học tập

Để cải thiện chất lượng công nhận, bạn sử dụng xác nhận chéo trong quá trình đào tạo. Điều này giúp bạn tránh được vấn đề quá mức cho dữ liệu đào tạo của bạn. Ví dụ, bạn có thể đọc bài viết này trong đó giải thích cách sử dụng nó với Keras.

Đôi khi, tỷ lệ đo chính xác cao hơn không nói lên điều gì về chất lượng nhận dạng thực sự khiến ANN được đào tạo không tìm thấy mẫu trong dữ liệu đào tạo. Nó có thể được kết nối với quá trình đào tạo hoặc tập dữ liệu đầu vào như được giải thích ở trên hoặc có thể do kiến ​​trúc ANN chọn.

  1. Kiến trúc ANN

Đó là một vấn đề lớn. Làm thế nào để xác định kiến ​​trúc ANN tốt hơn để giải quyết nhiệm vụ? Không có cách phổ biến để làm điều đó. Nhưng có một vài cách để đến gần với lý tưởng. Ví dụ bạn có thể đọc cuốn sách này . Nó giúp bạn thực hiện một tầm nhìn tốt hơn cho vấn đề của bạn. Ngoài ra, bạn có thể tìm thấy ở đây một số công thức heuristic để phù hợp với số lượng lớp / phần tử ẩn cho ANN của bạn. Cũng ở đây bạn sẽ tìm thấy một cái nhìn tổng quan nhỏ cho việc này.

Tôi hy vọng điều này sẽ giúp.


1. Nếu tôi hiểu bạn một cách chính xác, tôi không thể cắt theo kích thước cố định, đó là hình ảnh của một số có nhiều chữ số và tất cả các trường hợp đều khác nhau về kích thước / địa điểm, v.v. Hay bạn có ý gì khác? Có, tôi đã thử các phương pháp nhị phân khác nhau và các tham số được điều chỉnh, nếu đó là ý bạn. 2. Trên thực tế, sự công nhận trên MNIST là rất tốt, không có tình trạng quá mức xảy ra, độ chính xác mà tôi đã đề cập là độ chính xác kiểm tra. Không phải mạng hoặc đào tạo của nó là vấn đề. 3. Cảm ơn tất cả các liên kết, tôi khá hài lòng với kiến ​​trúc của mình, tất nhiên luôn có chỗ để cải thiện.
youngpanda

Vâng, bạn nhận được nó. Nhưng bạn luôn có khả năng làm cho tập dữ liệu của bạn thống nhất hơn. Trong trường hợp của bạn, tốt hơn là cắt hình ảnh các chữ số theo các đường viền như bạn đã làm. Nhưng sau đó, sẽ tốt hơn nếu chỉ mở rộng hình ảnh chữ số của bạn thành kích thước hợp nhất theo kích thước tối đa của hình ảnh chữ số theo tỷ lệ x và y. Bạn có thể thích trung tâm của khu vực đường viền chữ số để làm điều đó. Nó sẽ cung cấp dữ liệu đầu vào sạch hơn cho thuật toán đào tạo của bạn.
Egor Zamotaev

Bạn có nghĩa là tôi phải bỏ qua sự giãn nở? Cuối cùng, tôi đã căn giữa hình ảnh, khi tôi áp dụng đường viền (50 px cho mỗi bên). Sau đó, tôi thay đổi kích thước mỗi chữ số thành 28x28, vì đây là kích thước chúng ta cần cho MNIST. Bạn có nghĩa là tôi có thể thay đổi kích thước thành 28x28 khác nhau?
youngpanda

1
Có, sự giãn nở là không mong muốn. Đường viền của bạn có thể có các tỷ lệ khác nhau theo chiều cao và chiều rộng, đó là lý do tại sao bạn cần cải thiện thuật toán của mình ở đây. Ít nhất bạn nên tạo kích thước hình ảnh với cùng tỷ lệ. Vì bạn có kích thước hình ảnh đầu vào 28x28, bạn phải chuẩn bị hình ảnh với tỷ lệ 1: 1 theo tỷ lệ x và y. Bạn không nên có đường viền 50 px cho mỗi cạnh ảnh, mà là các đường viền X, Y px thỏa mãn điều kiện: contourSizeX + BorderSizeX == contourSizeY + BorderSizeY. Đó là tất cả.
Egor Zamotaev

Tôi đã thử mà không có sự giãn nở (quên đề cập trong bài). Nó không thay đổi bất kỳ kết quả nào ... Số biên giới của tôi là thử nghiệm. Lý tưởng nhất là tôi sẽ cần các chữ số của mình để vừa với hộp 20x20 (được chuẩn hóa kích thước như trong tập dữ liệu) và sau đó thay đổi nó bằng cách sử dụng trung tâm khối lượng ...
youngpanda

1

Sau một số nghiên cứu và thử nghiệm, tôi đã đi đến kết luận rằng bản thân quá trình tiền xử lý hình ảnh không phải là vấn đề (tôi đã thay đổi một số tham số được đề xuất, ví dụ như kích thước và hình dạng giãn nở nhưng chúng không quan trọng đối với kết quả). Điều gì đã giúp, tuy nhiên, có 2 điều sau đây:

  1. Như @ f4f nhận thấy, tôi cần thu thập dữ liệu của riêng mình với dữ liệu trong thế giới thực. Điều này đã giúp rất nhiều.

  2. Tôi đã thực hiện những thay đổi quan trọng đối với quá trình tiền xử lý phân khúc của mình. Sau khi nhận được các đường viền riêng lẻ, trước tiên tôi bình thường hóa kích thước hình ảnh để vừa với 20x20hộp pixel (khi chúng ở trong MNIST). Sau đó, tôi căn giữa hộp ở giữa 28x28hình ảnh bằng cách sử dụng tâm khối lượng (đối với hình ảnh nhị phân là giá trị trung bình trên cả hai chiều).

Tất nhiên, vẫn có những trường hợp phân đoạn khó khăn, chẳng hạn như các chữ số chồng chéo hoặc được kết nối, nhưng những thay đổi ở trên đã trả lời câu hỏi ban đầu của tôi và cải thiện hiệu suất phân loại của tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.