จะปรับปรุงการจดจำตัวเลขของแบบจำลองที่ได้รับการฝึกฝนเกี่ยวกับ MNIST ได้อย่างไร?


12

ฉันทำงานเกี่ยวกับการรับรู้ด้วยตัวเลขหลายหลักด้วยมือJavaโดยใช้OpenCVห้องสมุดสำหรับการประมวลผลล่วงหน้าและการแบ่งส่วนและKerasรูปแบบการฝึกอบรมเกี่ยวกับ MNIST (ด้วยความแม่นยำ 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

@Micka ฉันใช้รุ่นที่ได้รับการประมวลผลล่วงหน้า (binarized, inverted, dilated) คนที่ตรงกับชุดฝึกอบรม MNIST มีตัวอย่างของหมายเลข "1" หลังจากประมวลผลล่วงหน้าในโพสต์ของฉัน
youngpanda

คำตอบ:


5

ฉันเชื่อว่าปัญหาของคุณคือกระบวนการขยาย ฉันเข้าใจว่าคุณต้องการทำให้ขนาดภาพปกติ แต่คุณไม่ควรแบ่งสัดส่วนคุณควรปรับขนาดให้ใหญ่สุดที่ต้องการโดยแกนเดียว (ขนาดที่อนุญาตให้ปรับขนาดใหญ่ที่สุดโดยไม่ปล่อยให้ขนาดแกนอื่นเกินขนาดสูงสุด) และเติม ด้วยสีพื้นหลังส่วนที่เหลือของภาพ ไม่ใช่ว่า "MNIST มาตรฐานไม่เห็นหมายเลขหนึ่งซึ่งดูเหมือนกับกรณีทดสอบของคุณ" คุณทำให้รูปภาพของคุณดูเหมือนตัวเลขที่ผ่านการฝึกอบรมที่แตกต่างกัน (สิ่งที่ได้รับการยอมรับ)

ทับซ้อนของแหล่งที่มาและรูปภาพที่ถูกประมวลผล

หากคุณรักษาสัดส่วนภาพให้ถูกต้อง (แหล่งที่มาและหลังการประมวลผล) คุณจะเห็นว่าคุณไม่ได้ปรับขนาดภาพ แต่ "บิดเบือน" มันอาจเป็นผลมาจากการขยายที่ไม่เป็นเนื้อเดียวกันหรือการปรับขนาดที่ไม่ถูกต้อง


ฉันเชื่อว่า @SiR มีน้ำหนักบ้างลองไม่เปลี่ยนอัตราส่วนของตัวอักษรตัวเลข
ZdaR

ขออภัยฉันไม่ได้ติดตาม คุณคิดว่ากระบวนการขยายหรือกระบวนการปรับขนาดของฉันเป็นปัญหาหรือไม่ ฉันปรับขนาดภาพในการเริ่มต้นด้วยบรรทัดนี้Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);เท่านั้น อัตราส่วนการปันส่วนยังคงเหมือนเดิมฉันจะแบ่งสัดส่วนได้อย่างไร
youngpanda

@SiR เพื่อตอบการแก้ไขของคุณด้านบน: ใช่ฉันไม่เพียง แต่ปรับขนาดภาพฉันใช้การดำเนินการที่แตกต่างกันหนึ่งในนั้นคือการขยายภาพซึ่งเป็นลักษณะทางสัณฐานวิทยาซึ่งทำให้เกิด "การบิดเบือน" เล็กน้อยเนื่องจากเป็นบริเวณที่สว่างภายใน ภาพเป็น“ เติบโต” หรือคุณหมายถึงการปรับขนาดในท้ายที่สุดฉันสร้างภาพที่ 28x28 ได้ไหม
youngpanda

@youngpanda คุณอาจพบการสนทนาที่นี่stackoverflow.com/questions/28525436/…น่าสนใจ อาจให้เบาะแสว่าทำไมวิธีการของคุณจึงไม่ให้ผลลัพธ์ที่ดี
384

@SiR ขอบคุณสำหรับการเชื่อมโยงฉันคุ้นเคยกับ LeNet แต่มันก็ดีที่จะอ่านอีกครั้ง
youngpanda

5

แล้วมีการโพสต์คำตอบบางอย่าง แต่พวกเขาไม่ตอบคำถามที่เกิดขึ้นจริงของคุณเกี่ยวกับpreprocessing ภาพ

ในตาของฉันฉันไม่เห็นปัญหาที่สำคัญกับการใช้งานของคุณตราบใดที่มันเป็นโครงการการศึกษาทำได้ดี

แต่สิ่งหนึ่งที่ควรสังเกตคุณอาจพลาด มีการดำเนินงานขั้นพื้นฐานในลักษณะทางคณิตศาสตร์: การกัดเซาะและการขยาย (ใช้โดยคุณ) และมีการดำเนินการที่ซับซ้อน: ชุดค่าผสมพื้นฐานหลายอย่าง (เช่นการเปิดและปิด) ลิงก์ Wikipediaไม่ใช่ข้อมูลอ้างอิง CV ที่ดีที่สุด แต่คุณอาจเริ่มต้นด้วยเพื่อให้ได้แนวคิด

โดยปกติแล้วจะเป็นการดีกว่าที่จะใช้การเปิดแทนการกัดเซาะและการปิดแทนการขยายเนื่องจากในกรณีนี้ภาพไบนารีดั้งเดิมจะเปลี่ยนไปน้อยกว่ามาก (แต่ถึงผลที่ต้องการในการทำความสะอาดขอบคมหรือช่องว่างเติม) ดังนั้นในกรณีของคุณคุณควรตรวจสอบการปิด (การขยายภาพตามด้วยการกัดเซาะด้วยเคอร์เนลเดียวกัน) ในกรณีที่ภาพขนาดเล็กพิเศษ 8 * 8 ถูกปรับเปลี่ยนอย่างมากเมื่อคุณขยายภาพแม้ในขณะที่เคอร์เนล 1 * 1 (1 พิกเซลมากกว่า 16% ของภาพ) ซึ่งน้อยกว่าในภาพขนาดใหญ่)

หากต้องการเห็นภาพความคิดดูรูปต่อไปนี้ (จากบทช่วยสอน OpenCV: 1 , 2 ):

ขยาย: สัญลักษณ์ดั้งเดิมและพองหนึ่ง

ปิด: สัญลักษณ์ดั้งเดิมและปิดหนึ่ง

หวังว่ามันจะช่วย


ขอบคุณสำหรับการป้อนข้อมูล! จริงๆแล้วมันไม่ใช่โครงการศึกษาดังนั้นจะเป็นปัญหาอะไร? .. ภาพของฉันค่อนข้างใหญ่เมื่อฉันใช้การขยายภาพ 8x8 ไม่ใช่ขนาดของภาพมันเป็นปัจจัยในการปรับขนาดความสูงและความกว้าง แต่ยังคงเป็นตัวเลือกการปรับปรุงเพื่อลองการดำเนินการทางคณิตศาสตร์ที่แตกต่างกัน ฉันไม่รู้เกี่ยวกับการเปิดและปิดฉันจะลอง! ขอบคุณ.
youngpanda

ความผิดของฉันการโทรปรับขนาดผิดพลาดเหมือนเดิมด้วยขนาด 8 * 8 เหมือนขนาดใหม่ ในกรณีที่คุณต้องการใช้ OCR ในโลกแห่งความเป็นจริงคุณจะต้องพิจารณาตัวเลือกในการถ่ายโอนการเรียนรู้สุทธิดั้งเดิมของคุณบนข้อมูลทั่วไปไปยังพื้นที่ที่คุณใช้งาน อย่างน้อยตรวจสอบว่าปรับปรุงความถูกต้องโดยทั่วไปควรทำ
f4f

สิ่งที่ต้องตรวจสอบก็คือลำดับการประมวลผลล่วงหน้า: grayscale-> binary-> inverse-> resize การปรับขนาดเป็นการดำเนินการที่มีค่าใช้จ่ายสูงและฉันไม่เห็นความต้องการในการปรับใช้กับภาพสี และการแบ่งส่วนสัญลักษณ์อาจทำได้โดยไม่มีการตรวจจับเส้นขอบ (โดยมีบางอย่างที่มีราคาถูกกว่า) หากคุณมีรูปแบบการป้อนข้อมูลบางอย่าง
f4f

หากฉันมีชุดข้อมูลอื่นนอกเหนือจาก MNIST ฉันสามารถลองถ่ายโอนการเรียนรู้ :) ฉันจะพยายามเปลี่ยนลำดับการประมวลผลล่วงหน้าและกลับไปหาคุณ ขอบคุณ! ฉันยังไม่พบตัวเลือกที่ง่ายไปกว่าการตรวจจับเส้นขอบสำหรับปัญหาของฉัน ...
24419

1
ตกลง. คุณสามารถรวบรวมชุดข้อมูลด้วยตัวเองจากภาพที่คุณจะใช้ OCR เพราะเป็นวิธีปฏิบัติทั่วไป
f4f

4

ดังนั้นคุณต้องมีวิธีการที่ซับซ้อนทำให้ทุกขั้นตอนของการคำนวณของคุณขึ้นอยู่กับผลลัพธ์ก่อนหน้า ในอัลกอริทึมของคุณคุณมีคุณสมบัติต่อไปนี้:

  1. การประมวลผลภาพล่วงหน้า

ดังที่กล่าวไว้ก่อนหน้านี้หากคุณใช้การปรับขนาดแล้วคุณจะสูญเสียข้อมูลเกี่ยวกับอัตราส่วนภาพของภาพ คุณต้องทำการประมวลผลภาพตัวเลขซ้ำเพื่อให้ได้ผลลัพธ์เดียวกันกับกระบวนการฝึกอบรม

วิธีที่ดีกว่าถ้าคุณเพิ่งครอบตัดรูปภาพด้วยรูปภาพขนาดคงที่ ในตัวแปรนั้นคุณไม่จำเป็นต้องใช้รูปทรงในการค้นหาและปรับขนาดภาพหลักก่อนกระบวนการฝึกอบรม จากนั้นคุณสามารถทำการเปลี่ยนแปลงเล็กน้อยในอัลกอริธึมการครอบตัดของคุณเพื่อการจดจำที่ดีขึ้น: ค้นหารูปร่างและใส่ตัวเลขโดยไม่ต้องปรับขนาดที่กึ่งกลางของกรอบรูปภาพที่เกี่ยวข้องเพื่อรับรู้

นอกจากนี้คุณควรให้ความสำคัญกับอัลกอริทึม binarization ฉันเคยมีประสบการณ์การศึกษาผลกระทบของค่าเกณฑ์ binarization ต่อข้อผิดพลาดในการเรียนรู้: ฉันสามารถพูดได้ว่านี่เป็นปัจจัยที่สำคัญมาก คุณอาจลองอัลกอริทึมอื่นของการไบนาไนเซชันเพื่อตรวจสอบแนวคิดนี้ ตัวอย่างเช่นคุณอาจใช้ห้องสมุดนี้สำหรับการทดสอบอัลกอริทึม binarization สำรอง

  1. ขั้นตอนวิธีการเรียนรู้

เพื่อปรับปรุงคุณภาพของการจดจำคุณใช้การตรวจสอบข้ามในกระบวนการฝึกอบรม สิ่งนี้จะช่วยให้คุณหลีกเลี่ยงปัญหาการoverfittingสำหรับข้อมูลการฝึกอบรมของคุณ ตัวอย่างเช่นคุณอาจอ่านบทความนี้ซึ่งอธิบายวิธีใช้กับ Keras

บางครั้งอัตราการวัดความแม่นยำที่สูงขึ้นไม่ได้พูดอะไรเกี่ยวกับคุณภาพการรู้จำจริงทำให้ ANN ที่ผ่านการฝึกอบรมไม่พบรูปแบบในข้อมูลการฝึกอบรม อาจเชื่อมต่อกับกระบวนการฝึกอบรมหรือชุดข้อมูลอินพุตตามที่อธิบายไว้ข้างต้นหรืออาจเกิดจากสถาปัตยกรรม ANN เลือก

  1. สถาปัตยกรรม ANN

มันเป็นปัญหาใหญ่ วิธีกำหนดสถาปัตยกรรม ANN ที่ดีขึ้นเพื่อแก้ปัญหาได้อย่างไร ไม่มีวิธีทั่วไปในการทำสิ่งนั้น แต่มีสองสามวิธีในการเข้าใกล้อุดมคติ ตัวอย่างเช่นคุณสามารถอ่านหนังสือเล่มนี้ ช่วยให้คุณมีวิสัยทัศน์ที่ดีขึ้นสำหรับปัญหาของคุณ นอกจากนี้คุณอาจพบที่นี่บางสูตรการวิเคราะห์พฤติกรรมเพื่อให้พอดีกับจำนวนชั้นซ่อน / องค์ประกอบสำหรับ ANN ของคุณ นอกจากนี้ที่นี่คุณจะพบภาพรวมเล็กน้อยสำหรับสิ่งนี้

ฉันหวังว่าสิ่งนี้จะช่วยได้


1. หากฉันเข้าใจคุณอย่างถูกต้องฉันไม่สามารถครอบตัดขนาดที่แน่นอนเป็นรูปภาพของตัวเลขหลายหลักและทุกกรณีมีขนาด / สถานที่แตกต่างกัน ฯลฯ หรือคุณหมายถึงบางสิ่งที่แตกต่างกันหรือไม่? ใช่ฉันลองวิธีการ binarization ที่แตกต่างกันและพารามิเตอร์ tweaked ถ้านั่นคือสิ่งที่คุณหมายถึง 2. ที่จริงแล้วการรับรู้เกี่ยวกับ MNIST นั้นยอดเยี่ยมไม่มีอะไรเกิดขึ้นมากเกินไปความแม่นยำที่ฉันกล่าวถึงคือความแม่นยำในการทดสอบ เครือข่ายหรือการฝึกอบรมไม่เป็นปัญหา 3. ขอบคุณสำหรับการเชื่อมโยงทั้งหมดฉันมีความสุขมากกับสถาปัตยกรรมของฉัน แต่แน่นอนว่ามีพื้นที่สำหรับการปรับปรุงอยู่เสมอ
youngpanda

ใช่คุณเข้าใจแล้ว แต่คุณก็มีโอกาสที่จะทำให้ชุดข้อมูลของคุณเป็นหนึ่งเดียวกันมากขึ้น ในกรณีของคุณดีกว่าที่จะครอบตัดรูปภาพของตัวเลขตามรูปทรงที่คุณทำอยู่แล้ว แต่หลังจากนั้นจะเป็นการดีกว่าที่จะขยายภาพตัวเลขของคุณให้เป็นขนาดรวมตามขนาดสูงสุดของภาพหลักในระดับ x และ y คุณสามารถเลือกจุดศูนย์กลางของภูมิภาคที่มีรูปร่างเป็นหลักเพื่อทำสิ่งนั้น มันจะให้ข้อมูลที่สะอาดยิ่งขึ้นสำหรับอัลกอริทึมการฝึกอบรมของคุณ
Egor Zamotaev

คุณหมายความว่าฉันต้องข้ามการขยายออกหรือไม่ ในที่สุดฉันก็อยู่ตรงกลางภาพเมื่อฉันใช้ชายแดน (50 px ในแต่ละด้าน) หลังจากนั้นฉันปรับขนาดแต่ละหลักเป็น 28x28 เนื่องจากนี่เป็นขนาดที่เราต้องการสำหรับ MNIST คุณหมายถึงฉันสามารถปรับขนาดเป็น 28x28 ต่างกันหรือไม่?
youngpanda

1
ใช่การขยายเป็นที่ไม่พึงประสงค์ รูปทรงของคุณอาจมีอัตราส่วนที่แตกต่างกันตามความสูงและความกว้างนั่นคือเหตุผลที่คุณจำเป็นต้องปรับปรุงอัลกอริทึมของคุณที่นี่ อย่างน้อยคุณควรปรับขนาดภาพด้วยอัตราส่วนเดียวกัน เนื่องจากคุณมีขนาดภาพอินพุต 28x28 คุณต้องจัดเตรียมภาพที่มีอัตราส่วน 1: 1 เท่ากันโดยใช้อัตราส่วน x และ y คุณไม่ควรได้รับเส้นขอบ 50 px สำหรับแต่ละด้านของภาพ แต่เป็นเส้นขอบ X, Y px ที่ตรงตามเงื่อนไข: contourSizeX + borderSizeX == contourSizeY + borderSizeY นั่นคือทั้งหมด
Egor Zamotaev

ฉันพยายามแล้วโดยไม่มีการขยาย (ลืมพูดถึงในโพสต์) มันไม่ได้เปลี่ยนผลลัพธ์ใด ๆ ... หมายเลขเส้นขอบของฉันคือการทดลอง เป็นการดีที่ฉันจะต้องการตัวเลขของฉันเพื่อให้พอดีกับกล่อง 20x20 (ขนาดปกติเช่นในชุดข้อมูล) และหลังจากนั้นเปลี่ยนมันโดยใช้ศูนย์กลางของมวล ...
youngpanda

1

หลังจากการวิจัยและการทดลองฉันได้ข้อสรุปว่าภาพก่อนการประมวลผลตัวเองไม่ใช่ปัญหา (ฉันเปลี่ยนพารามิเตอร์ที่แนะนำบางอย่างเช่นขนาดขยายและรูปร่าง แต่ไม่ได้มีความสำคัญต่อผลลัพธ์) อย่างไรก็ตามสิ่งที่ช่วยได้มี 2 สิ่งต่อไปนี้:

  1. ตามที่ @ f4f สังเกตเห็นฉันจำเป็นต้องรวบรวมชุดข้อมูลของตัวเองด้วยข้อมูลจริง สิ่งนี้ช่วยได้อย่างมากแล้ว

  2. ฉันทำการเปลี่ยนแปลงที่สำคัญสำหรับการแบ่งกลุ่มการประมวลผลล่วงหน้าของฉัน หลังจากได้รับส่วนบุคคลฉันเริ่มปรับขนาดรูปภาพให้พอดีกับ20x20กล่องพิกเซล (ปกติอยู่ในรูปMNIST) หลังจากนั้นฉันจัดกึ่งกลางของกล่องที่อยู่ตรงกลางของ28x28รูปภาพโดยใช้จุดศูนย์กลางของมวล (ซึ่งสำหรับภาพไบนารีเป็นค่าเฉลี่ยของทั้งสองมิติ)

แน่นอนว่ายังมีกรณีการแบ่งเซ็กเมนต์ที่ยากลำบากเช่นการทับซ้อนกันหรือตัวเลขที่เชื่อมต่อ แต่การเปลี่ยนแปลงข้างต้นตอบคำถามเริ่มต้นของฉันและปรับปรุงประสิทธิภาพการจัดหมวดหมู่ของฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.