Bagaimana cara meningkatkan pengenalan angka terhadap model yang dilatih tentang MNIST?

12

Saya bekerja pada pengenalan multi-digit yang dicetak dengan tangan Java, menggunakan OpenCVperpustakaan untuk preprocessing dan segmentasi, dan Kerasmodel yang dilatih tentang MNIST (dengan akurasi 0,98) untuk pengakuan.

Pengakuan itu tampaknya bekerja cukup baik, terlepas dari satu hal. Jaringan sering gagal mengenali yang (nomor "satu"). Saya tidak tahu apakah itu terjadi karena preprocessing / implementasi segmentasi yang tidak benar, atau jika jaringan yang dilatih tentang MNIST standar belum melihat nomor satu yang terlihat seperti kasus pengujian saya.

Inilah tampilan digit bermasalah setelah preprocessing dan segmentasi:

masukkan deskripsi gambar di sinimenjadi masukkan deskripsi gambar di sinidan diklasifikasikan sebagai 4.

masukkan deskripsi gambar di sinimenjadi masukkan deskripsi gambar di sinidan diklasifikasikan sebagai 7.

masukkan deskripsi gambar di sinimenjadi masukkan deskripsi gambar di sinidan diklasifikasikan sebagai 4. Dan seterusnya...

Apakah ini sesuatu yang bisa diperbaiki dengan memperbaiki proses segmentasi? Atau lebih tepatnya dengan meningkatkan set pelatihan?

Sunting: Meningkatkan set pelatihan (augmentasi data) pasti akan membantu, yang sudah saya uji, pertanyaan tentang preprocessing yang benar masih ada.

Preprocessing saya terdiri dari pengubahan ukuran, konversi ke skala abu-abu, binarisasi, inversi, dan pelebaran. Berikut kodenya:

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

Gambar yang telah diproses kemudian dibagi menjadi beberapa digit sebagai berikut:

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);
}
youngpanda
sumber
1
Anda menggunakan masker atau piksel abu-abu asli (tertutup?) sebagai masukan untuk klasifikasi Anda?
Micka
@Micka Saya menggunakan versi preprocessed (binarized, inverted, dilated). Yang cocok dengan set pelatihan MNIST. Ada contoh angka "1" setelah preprocessing di posting saya.
youngpanda

Jawaban:

5

Saya percaya bahwa masalah Anda adalah proses pelebaran. Saya mengerti bahwa Anda ingin menormalkan ukuran gambar, tetapi Anda tidak boleh mematahkan proporsi, Anda harus mengubah ukuran hingga maksimum yang diinginkan oleh satu sumbu (yang memungkinkan skala ulang terbesar tanpa membiarkan dimensi sumbu lain melebihi ukuran maksimum) dan mengisi dengan warna latar belakang sisa gambar. Bukannya "MNIST standar hanya belum melihat nomor satu yang terlihat seperti kasus pengujian Anda", Anda membuat gambar Anda terlihat seperti nomor terlatih yang berbeda (yang diakui)

Tumpang tindih dari sumber dan gambar yang diproses

Jika Anda mempertahankan rasio aspek gambar yang benar (sumber dan pasca-pemrosesan), Anda dapat melihat bahwa Anda tidak hanya mengubah ukuran gambar tetapi "mengubah" gambar. Ini bisa merupakan hasil dari pelebaran non-homogen atau pengubahan ukuran yang salah

Pak
sumber
Saya percaya bahwa @SiR memiliki bobot, Cobalah untuk tidak mengubah rasio aspek literal numerik.
ZdaR
Maaf, saya tidak begitu mengikuti. Apakah Anda pikir proses pelebaran saya atau proses pengubahan ukuran adalah masalahnya? Saya hanya mengubah ukuran gambar di awal dengan baris ini Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);. Di sini aspek rasio tetap sama, di mana saya harus mematahkan proporsi?
youngpanda
@ SiR sebagai jawaban pada suntingan Anda di atas: ya, saya tidak hanya mengubah ukuran gambar, saya menerapkan operasi yang berbeda, salah satunya adalah pelebaran, yang merupakan salah satu morfologis, yang menyebabkan sedikit "distorsi" karena menyebabkan daerah terang dalam suatu gambar untuk "tumbuh". Atau maksud Anda mengubah ukuran di bagian paling akhir, tempat saya membuat gambar
28x28
@youngpanda, Anda mungkin menemukan diskusi di sini stackoverflow.com/questions/28525436/… menarik. Mungkin memberi Anda petunjuk mengapa pendekatan Anda tidak membawa hasil yang baik
SiR
@ SiR terima kasih atas tautannya, saya kenal dengan LeNet, tapi senang membaca lagi
youngpanda
5

Sudah ada beberapa jawaban yang diposting tetapi tidak satupun dari mereka menjawab pertanyaan Anda yang sebenarnya tentang preprocessing gambar .

Pada gilirannya saya tidak melihat masalah signifikan dengan implementasi Anda selama ini adalah proyek studi, dilakukan dengan baik.

Tapi satu hal yang perlu diperhatikan yang mungkin Anda lewatkan. Ada operasi dasar dalam morfologi matematika: erosi dan pelebaran (digunakan oleh Anda). Dan ada operasi yang kompleks: berbagai kombinasi yang dasar (mis. Pembukaan dan penutupan). Tautan Wikipedia bukan referensi CV terbaik, tetapi Anda bisa mulai dengan itu untuk mendapatkan ide.

Biasanya lebih baik menggunakan pembukaan daripada erosi dan penutupan bukan pelebaran karena dalam hal ini gambar biner asli berubah jauh lebih sedikit (tetapi efek yang diinginkan dari membersihkan tepi tajam atau mengisi celah tercapai). Jadi dalam kasus Anda, Anda harus memeriksa penutupan (pelebaran gambar diikuti oleh erosi dengan kernel yang sama). Dalam kasus gambar ekstra-kecil 8 * 8 sangat dimodifikasi ketika Anda melebar bahkan dengan kernel 1 * 1 (1 piksel lebih dari 16% gambar) yang kurang pada gambar yang lebih besar).

Untuk memvisualisasikan ide, lihat gambar berikut (dari tutorial OpenCV: 1 , 2 ):

pelebaran: simbol asli dan melebar

penutupan: simbol asli dan yang tertutup

Semoga ini bisa membantu.

f4f
sumber
Terima kasih atas masukannya! Sebenarnya ini bukan proyek studi, jadi apa masalahnya nanti? .. Gambar saya cukup besar ketika saya menerapkan pelebaran, 8x8 bukan ukuran gambar, itu faktor ukuran untuk tinggi dan lebar. Tapi itu masih bisa menjadi opsi perbaikan untuk mencoba berbagai operasi matematika. Saya tidak tahu tentang membuka dan menutup, saya akan mencobanya! Terima kasih.
youngpanda
Kesalahan saya, salah baca panggilan ukuran seperti itu dengan 8 * 8 sebagai ukuran baru. Jika Anda ingin menggunakan OCR di dunia nyata, Anda harus mempertimbangkan opsi transfer untuk mempelajari jaringan asli Anda pada data yang khas untuk area penggunaan Anda. Setidaknya periksa apakah itu meningkatkan akurasi, umumnya itu harus dilakukan.
f4f
Hal lain yang perlu diperiksa adalah urutan preprocessing: grayscale-> binary-> inverse-> resize. Mengubah ukuran adalah operasi yang mahal dan saya tidak melihat perlunya menerapkannya pada gambar berwarna. Dan segmentasi simbol dapat dilakukan tanpa deteksi kontur (dengan sesuatu yang lebih murah) jika Anda memiliki beberapa format input spesifik tetapi mungkin sulit untuk diterapkan.
f4f
Jika saya memiliki dataset lain selain dari MNIST, saya dapat mencoba mentransfer pembelajaran :) Saya akan mencoba mengubah urutan preprocessing dan kembali kepada Anda. Terima kasih! Saya belum menemukan opsi yang lebih mudah daripada deteksi kontur untuk masalah saya ...
youngpanda
1
Baik. Anda dapat mengumpulkan dataset sendiri dari gambar yang akan Anda gunakan OCR pada itu adalah praktik umum.
f4f
4

Jadi, Anda memerlukan pendekatan yang kompleks karena setiap langkah kaskade komputasi Anda berdasarkan hasil sebelumnya. Dalam algoritme Anda, Anda memiliki fitur berikut:

  1. Pra-pemrosesan gambar

Seperti disebutkan sebelumnya, jika Anda menerapkan pengubahan ukuran, maka Anda kehilangan informasi tentang rasio aspek gambar. Anda harus melakukan pemrosesan ulang gambar digit yang sama untuk mendapatkan hasil yang sama dengan yang tersirat dalam proses pelatihan.

Cara yang lebih baik jika Anda hanya memotong gambar dengan gambar ukuran tetap. Dalam varian itu Anda tidak perlu mencari dan mengubah ukuran gambar digit kontur sebelum proses pelatihan. Kemudian, Anda dapat membuat sedikit perubahan pada algoritme krop Anda untuk mengenali lebih baik: mudah menemukan kontur dan letakkan digit Anda tanpa mengubah ukuran di tengah bingkai gambar yang relevan untuk pengakuan.

Anda juga harus lebih memperhatikan algoritma binarisasi. Saya memiliki pengalaman mempelajari efek nilai ambang binarisasi pada kesalahan belajar: Saya dapat mengatakan bahwa ini adalah faktor yang sangat signifikan. Anda dapat mencoba algoritma binarisasi lainnya untuk memeriksa ide ini. Misalnya, Anda dapat menggunakan perpustakaan ini untuk menguji algoritma binarisasi alternatif.

  1. Algoritma pembelajaran

Untuk meningkatkan kualitas pengakuan Anda menggunakan validasi silang pada proses pelatihan. Ini membantu Anda menghindari masalah overfitting untuk data pelatihan Anda. Misalnya Anda dapat membaca artikel ini di mana dijelaskan bagaimana menggunakannya dengan Keras.

Kadang-kadang tingkat akurasi yang lebih tinggi tidak mengatakan apa-apa tentang kualitas pengakuan yang sebenarnya karena JST yang terlatih tidak menemukan pola dalam data pelatihan. Ini mungkin terhubung dengan proses pelatihan atau dataset input seperti yang dijelaskan di atas, atau mungkin disebabkan oleh arsitektur JST pilih.

  1. Arsitektur JST

Ini masalah besar. Bagaimana cara mendefinisikan arsitektur JST yang lebih baik untuk menyelesaikan tugas? Tidak ada cara umum untuk melakukan hal itu. Tetapi ada beberapa cara untuk lebih mendekati ideal. Misalnya Anda bisa membaca buku ini . Ini membantu Anda untuk membuat visi yang lebih baik untuk masalah Anda. Anda juga dapat menemukan di sini beberapa rumus heuristik yang sesuai dengan jumlah lapisan / elemen tersembunyi untuk JST Anda. Juga di sini Anda akan menemukan sedikit gambaran untuk ini.

Saya harap ini akan membantu.

Egor Zamotaev
sumber
1. Jika saya memahami Anda dengan benar, saya tidak dapat memotong ke ukuran tetap, ini adalah gambar multi-digit, dan semua kasing berbeda dalam ukuran / tempat, dll. Atau apakah Anda bermaksud sesuatu yang berbeda? Ya, saya mencoba metode binarisasi yang berbeda dan parameter tweak, jika itu yang Anda maksud. 2. Sebenarnya pengakuan pada MNIST bagus, tidak ada overfitting, akurasi yang saya sebutkan adalah akurasi tes. Baik jaringan atau pelatihannya tidak menjadi masalah. 3. Terima kasih untuk semua tautannya, saya cukup senang dengan arsitektur saya, tentu saja selalu ada ruang untuk perbaikan.
youngpanda
Ya, Anda mengerti. Tetapi Anda selalu memiliki kemungkinan untuk membuat dataset Anda lebih bersatu. Dalam kasus Anda, lebih baik memangkas gambar dengan kontur seperti yang sudah Anda lakukan. Tetapi setelah itu akan lebih baik untuk hanya memperluas gambar digit Anda ke ukuran terpadu sesuai dengan ukuran maksimal gambar digit dengan skala x dan y. Anda bisa memilih pusat wilayah kontur digit untuk melakukan hal itu. Ini akan memberikan data input Anda lebih bersih untuk algoritma pelatihan Anda.
Egor Zamotaev
Apakah maksud Anda saya harus melewati pelebaran? Pada akhirnya saya sudah memusatkan gambar, ketika saya menerapkan perbatasan (50 px di setiap sisi). Setelah itu saya mengubah ukuran setiap digit menjadi 28x28, karena ini adalah ukuran yang kami butuhkan untuk MNIST. Apakah maksud Anda saya dapat mengubah ukuran menjadi 28x28 secara berbeda?
youngpanda
1
Ya, pelebaran tidak diinginkan. Kontur Anda mungkin memiliki rasio yang berbeda berdasarkan tinggi dan lebar, itu sebabnya Anda diperlukan dalam peningkatan algoritma Anda di sini. Setidaknya Anda harus membuat ukuran gambar dengan rasio yang sama. Karena Anda memiliki ukuran gambar input 28x28, Anda harus menyiapkan gambar dengan rasio 1: 1 yang sama dengan skala x dan y. Anda seharusnya tidak mendapatkan batas 50 px untuk setiap sisi gambar, tetapi batas X, Y px yang memenuhi syarat: contourSizeX + borderSizeX == contourSizeY + borderSizeY. Itu saja.
Egor Zamotaev
Saya sudah mencoba tanpa pelebaran (lupa menyebutkan di pos). Itu tidak mengubah hasil apa pun ... Nomor perbatasan saya adalah percobaan. Idealnya saya perlu angka saya agar sesuai dengan kotak 20x20 (ukuran dinormalisasi seperti itu dalam dataset) dan setelah itu menggesernya menggunakan pusat massa ...
youngpanda
1

Setelah beberapa penelitian dan percobaan, saya sampai pada kesimpulan bahwa preprocessing gambar itu sendiri bukan masalah (saya memang mengubah beberapa parameter yang disarankan, seperti misalnya ukuran dan bentuk pelebaran tetapi mereka tidak penting untuk hasil). Namun, apa yang membantu adalah 2 hal berikut:

  1. Seperti @ f4f perhatikan, saya perlu mengumpulkan dataset saya sendiri dengan data dunia nyata. Ini sudah sangat membantu.

  2. Saya membuat perubahan penting pada preprocessing segmentasi saya. Setelah mendapatkan kontur individual, pertama-tama saya menormalkan ukuran gambar agar pas dengan 20x20kotak pixel (seperti yang ada di dalamnya MNIST). Setelah itu saya memusatkan kotak di tengah 28x28gambar menggunakan pusat massa (yang untuk gambar biner adalah nilai rata-rata di kedua dimensi).

Tentu saja, masih ada kasus segmentasi yang sulit, seperti angka yang tumpang tindih atau terhubung, tetapi perubahan di atas menjawab pertanyaan awal saya dan meningkatkan kinerja klasifikasi saya.

youngpanda
sumber