Super AI Engineer : Thai MNIST Handwritten Digits Classification

Natchapol Patamawisut
5 min readOct 16, 2020

--

#รหัส 22p21c0039

สวัสดีครับผม Blue เป็นนักศึกษาวิศวกรรมศาสตร์คอมพิวเตอร์ที่ KMUTT ครั้งนี้มาเขียนเกี่ยวกับการบ้านในสัปดาห์ที่ 4 ของการเข้าร่วมโครงการ Super AI Engineer ซึ่งโจทย์ในครั้งนี้พอเห็นชื่อแล้ว ถ้าใครเคยเล่นกับ Machine Learning หรือ ลงคอร์สเรียนออนไลน์ด้าน Machine Learning สักหน่อยก็คงจะยิ้มหวานเลยทีเดียว เพราะ Dataset ที่ให้มาเล่นในครั้งนี้ก็คือ MNIST ( Modified National Institute of Standards and Technology ) dataset หรือถ้าให้พูดง่ายๆคือ ชุดข้อมูลลายมือเขียนตัวเลขนั้นเอง

https://www.amirhf.com/post/tf-vgg13-mnist/

แต่!!! ข้อมูลในครั้งนี้จะไม่ใช่ MNIST ธรรมดา แต่เป็นข้อมูล Thai MNIST ที่ท่านสมาชิกโครงการ Super AI Engineer ทั้งหลายได้ร่วมกันเขียนขึ้นหลายหมื่นภาพ

และ ต้องขอขอบพระคุณท่านอาจารย์ ปริญญา สงวนสัตย์ จาก PIM ที่ได้ทำเว็ปสำหรับเก็บข้อมูลตัวเลขไทยลายมือเขียนจากสมาชิกโครงการ 🙏

Thai MNIST Classification

พูดถึงโจทย์ในการบ้านครั้งนี้ก็จะคล้ายๆกับเวลาที่เราเล่นกับ MNIST Dataset ทั่วๆไปคือการทำ Image classification ว่าแต่ละภาพเป็นภาพของตัวเลขอะไร แต่ถ้าเป็นแบบนั้นคิดว่าคงง่ายไป อาจารย์เลยให้ ทำอีกอย่างคือ Number Expression ที่จะมีภาพ 3 features กับ class ผลลัพท์ตั้งแต่ 0–99 ก็คือให้เราทำ 2 Steps

  1. ทำนายว่าแต่ละภาพใน test set เป็นภาพตัวเลขอะไรแล้วเก็บตาราง map นั้นไว้
  2. นำภาพจาก ทั้ง 3 features ไป map กับข้อมูลในข้อ 1 เพื่อให้ได้ตัวเลขออกมา จากนั้นนำ ตัวเลข 3 features ไปเข้าอีก model หรือ function เพื่อให้ได้ผลลัพท์สุดท้ายที่มี 0–99 classes
ภาพ 3 features และ ผลลัพท์

Thai MNIST Data

Thai MNIST dataset

ก่อนจะเริ่มเขียน model ขั้นแรกก็ต้องทำความเข้าใจข้อมูลก่อน โดยข้อมูลที่ได้มาในครั้งนี้เป็นข้อมูล “ตัวเลขไทย” ลายมือเขียนตั้งแต่เลข 0–9 ซึ่งมีชุด train อยู่ 8255 ภาพ และ ชุด test สำหรับ submit บน Kaggle อยู่ 4274 ภาพ

ซึ่งลายมือแต่ละท่านก็จะแตกต่างกันไปจนถึงบางภาพที่เป็นเลขพิสดาร เช่น

เลข 6 ของชาว Egypt
เลขป้า (แปด + ห้า)

และที่ “สุดปัง” คือภาพนี้

เกินแล้วพี่

ถัดจากข้อมูลที่เป็นภาพทั้งหมดไปแล้วก็ยังมีอีกข้อมูลคือ ข้อมูลมูล Rules ที่มี 3 features และ ผลลัพท์ที่ได้จากการนำ 3 features นั้นมาเข้า function บางอย่าง

หลังจากเข้าใจข้อมูลคร่าวๆแล้ว ก็ไปลงลึกในข้อมูล และ เริ่ม coding กันเลย

Colab:https://colab.research.google.com/drive/10FY-slybxeL1f3DRzxmYl1ngaYiTIoTi?usp=sharing

Github:https://github.com/BankNatchapol/SuperAIEngineer/blob/master/week4/22p21c0039_W4HW1_Natchapol_12102020.ipynb

Preprocessing

หลังจากที่ได้ดูข้อมูลไปคร่าวๆแล้ว ก็จะเห็นปัญหาในข้อมูลอยู่พอสมควร ดังนั้นเราจะมาทำการ preprocess ให้ข้อมูลอยู่ในรูปแบบที่ดีที่สุดกันครับ

Drop weird values

โดยประเด็นแรกที่จะทำคือการ drop ข้อมูลที่พิสดารออก เพื่อไม่ให้ model เรียนรู้อะไรที่ผิดความเป็นจริงเกินไป ซึ่งต้องขอบคุณท่านจากทางไลน์กลุ่มที่แชร์ drop lists มาไว้ให้

โดยข้อมูลใน drop lists จะเป็นชื่อของภาพ ทำให้เราสามารถนำมาตัดภาพนั้นออกจาก train set ได้เลย

Prepare data loader

ในการนำข้อมูล feed เข้า model เราสามารถใช้ dataloader เพื่อลดการใช้ memory ได้ โดยขั้นแรกทำการเตรียม Dataset class เพื่อใช้ ดึงข้อมูลจาก folder ให้อยู่ใน format ที่เราต้องการ

ถัดมาในการใช้ dataloader เราสามารถใส่ transformation ให้กับ image ของเราได้ ซึ่ง transformation ที่ผมเลือกใช้มีดังนี้

  1. Grayscale เนื่องจาก shape ของภาพเมื่อ feed เข้ามาผมพบว่ามีจำนวน channel เป็น 4 ดังนั้นผมจึงปรับให้เหลือ 1 ก่อนที่จะไปทำ step ถัดไป
  2. RandomAffine เป็นการทำ Data Augmentation ซึ่งในขั้นตอนนี้ผมคิดว่าเราไม่สามารถทำอะไรได้มากนัก เพราะ object ในภาพบางภาพมีขนาดเล็ก และ อยู่ใกล้ขอบมาก ถ้าเราทำ Augment บางลักษณะอาจจะทำให้ข้อมูลหายไปเลยก็ได้ ดังนั้นผมจึงทำแค่ Shear และ Rotation ด้วยองศาไม่เยอะ

3. Erode เป็น transformation ที่ผมเขียนเพื่อทำ erode ด้วย cv2 โดยคาดหวังว่ามันจะลด noise และ ทำให้เส้นบางลง

Class Erode
https://vovkos.github.io/doxyrest-showcase

4. Crop เป็น Class ที่ผมเขียนเพื่อ Crop ภาพไปที่ object ภายในเพื่อตัดปัญหาตำแหน่งของ object ภายในภาพ

Class Crop
ภาพที่ถูก Crop

5. interpolate เพื่อลดขนาดของภาพ เนื่องจากขนาดภาพในตอนแรกเป็น 620x620 ซึ่งมีขนาดใหญ่มากทำให้การ train model นานไปด้วย จึทำการลดขนาดลงให้เหลือ 128x128 พอ

หลังจากสร้าง transformation ทั้งหมดเรียบร้อยแล้วก็นำมาใส่ใน function Dataloader เพื่อทำให้ dataset กลายเป็น dataloader

Step 1 : Model and Workflow

ใน step แรกของการทำนายผล เป็นช่วงการสร้าง model เพื่อ Classify ภาพ โดยโครงสร้างของ model ที่ได้หลังจากการปรับแต่งหลายๆอย่าง จะอยู่ในลักษณะนี้

Ensemble model

model ที่ผมใช้จะเป็นการใช้ปริมาณเข้าสู้(55) โดยการนำ pretrained model ที่มีให้ใน Pytorch มาใช้ร่วมกัน และทำ Hard Voting ในตอนจบซึ่งผลลัพท์จากการโหวด คาดว่าจะเป็นผลลัพท์ที่ดีกว่าการใช้งาน model เดี่ยวๆ เพราะเราเชื่อว่าประชาธิปไตยสามารถพาให้ความแม่นยำของ model เราก้าวไปข้างหน้าได้

จุดที่ยากในการทำ model ลักษณะนี้คือใช้เวลาที่นานกว่าจะ train ได้ครบทุก model และ อาจจะไม่ได้ผลลัพท์ที่ดีขึ้นจาก Voting ก็เป็นได้ถ้า model เล็กๆที่เราใช้นั้นมีตัวที่ทำนายผลได้ไม่ดี

ผลลัพท์จาก 1 model

ซึ่งจากผลลัพท์ที่ผมได้ ก็ถือว่าการทำ ensemble ให้ผลลัพท์น่าพอใจในระดับหนึ่งทีเดียว

ensemble result

ต่อจากการสร้าง model ขึ้นมาผมก็รู้สึกค่อนข้างตันแล้วว่าจะทำอะไรเพิ่มอีกดี ซึ่งพอดีมีท่านในกลุ่มไลน์ได้บอกถึงการ skeletonize เพื่อหาโครงของเส้น ช่วยลดความหนาของเส้นได้

ผมจึงเกิดไอเดียในการนำ transformation ส่วนนี้มาเพิ่มเพื่อให้มีข้อมูลที่หลากหลายยิ่งขึ้น จึงเกิดเป็น workflow นี้ขึ้นมา

DoubleEnd Workflow

ขอตั้งชื่อว่า DoubleEnd (ฮา) โดยจากเดิมที่มีการใช้ข้อมูล ThaiMNIST แบบ Original feed เข้า Ensemble model ผมก็ได้เพิ่มเป็นข้อมูลที่มี transformation ต่างกัน 3 แบบ feed เข้า 3 Ensemble model แล้วให้เอาผลลัพท์ทั้ง 3 ตัวมาทำ Hard Voting อีกที

นอกจาก skeletonize ที่ใช้ในการหาเส้นโครงของภาพได้แล้ว ยังมี thin และ medial axis ที่ใช้ได้เหมือนกัน แต่ medial axis เมื่อใช้จะทำให้เกิด noise ค่อนข้างเยอะ ในครั้งนี้ผมจึงเลือกใช้เป็น skeletonize และ thin เท่านั้น

ต้องบอกเลยว่าใช้เวลาในการ run ต่อนข้างนาน ดังนั้นในตอน train อาจจะต้องนำ 3 transformation มา train แยกกัน

Visualize ด้วย Tensorboard เพื่อความ Cool

โดย trick อย่างหนึ่งที่สำคัญเวลา run colab ไว้นานๆ แล้วไม่อยากให้ disconnect คือ ให้เราใช้คำสั่ง JavaScript พิมพ์ลงบน console เพื่อสั่งให้คอยคลิกที่ปุ่ม connect อยุ่เรื่อยๆ

function ConnectButton(){
console.log("Connect pushed");
document.querySelector("#top-toolbar > colab-connect-button").shadowRoot.querySelector("#connect").click()
}
setInterval(ConnectButton,60000);

ซึ่งผลลัพท์หลังจากการ train อันยาวนาน ก็ถือว่าน่าจะแม่นยำพอสมควร เมื่อดูจากภาพ (ปล. score ดูจากผล test ทีเดียวเพราะ run นานเลยไม่อยาก test หลายรอบ)

Step 2 : Predicting Number Expression

ในขั้นตอนที่ 2 เรียกว่าเป็นการทำนายสมการ และ เงื่อนไขที่อาจารย์ได้ใส่เข้ามา โดยมี feature1,2,3 เป็น data ภาพเลข เมื่อนำ 3 features นี้มาทำอะไรบางอย่าง จะได้ ค่า predict ออกมาซึ่งมีค่าอยู่ในช่วง 0–99

ด้วยความเทพของพี่ๆในกลุ่มไลน์ ได้ช่วยกันทำ super feature engineering ให้แล้ว ได้ผลลัพท์เป็น function solver ดังนี้

จากนั้นก็นำ result ที่ได้จาก step แรกมา map เข้ากับรูปใน rules แล้วใช้ solver เพื่อหา predict แล้ว save file submit

เมื่อได้ผลลัพท์จาก transformation ชุดหนึ่งแล้ว ก็นำผลลัพท์อื่นมาทำ Voting ให้ได้เป็นผลลัพท์สุดท้ายที่จะนำไปส่งบน Kaggle

Trick นิดหน่อยที่ผมใช้ในการเช็คคำตอบคือนำผลลัพท์จากการส่งบน Kaggle รอบที่แล้วมาเทียบกับผลลัพท์รอบนี้ว่ามีอะไรแตกต่าง ซึ่งถือว่าช่วยในการตัดสินใจว่าจะส่งหรือไม่ส่งได้พอสมควร

--

--

Natchapol Patamawisut

Studying Computer Engineering at King Mongkut's University of Technology Thonburi