Notifications
Clear all

Quick, Draw! Doodle Recognition Challenge — มาทำนายภาพวาดจากลายเส้นบ้านๆ กันเถอะ

3 ข้อความ
2 Users
2 Likes
13.3 K Views
nameless
(@nameless)
Member Moderator
เข้าร่วมเมื่อ: 7 years ago
ข้อความ: 7
Topic starter  

ในโพสต์นี้จะขอเล่าย้อนกลับไปถึงการแข่งขัน Quick, Draw! ของ Kaggle [link] ที่ทีม ThAIKeras ได้เข้าร่วมเมื่อช่วงปลายเดือนพฤศจิกา ปีที่ผ่านมา (ค.ศ. 2018) ซึ่งก็นับว่าเป็นรายการแรกที่ทีมของเราเข้าร่วมลงแข่งเลยครับ เราตัดสินใจเข้าร่วมก่อนจะสิ้นสุดการแข่งขันเป็นเวลาราวๆ สองสัปดาห์ ซึ่งก็นับว่ามีเวลาน้อย เมื่อเทียบกับการที่ต้องรับมือกับข้อมูลจำนวนมหาศาลสำหรับการแข่งขันครั้งนี้ แต่ก็ทำให้พวกเราได้เรียนรู้อะไรเพิ่มขึ้นอีกหลายด้านเกี่ยวกับการใช้ deep learning สำหรับงาน computer vision จากโจทย์ของจริง และเก็บเกี่ยวประสบการณ์ภาคปฏิบัติได้อย่างเต็มเม็ดเต็มหน่วย

หลายท่านคงจะรู้จักเกมของ Google ที่ชื่อว่า Quick, Draw! กันดีอยู่แล้ว เกมนี้สามารถเข้าไปเล่นกันได้ที่นี่ครับ [link] โดยตัวเกมจะให้ผู้ใช้ทำการวาดรูป ตามโจทย์ที่โปรแกรมกำหนดให้ แล้ว AI ของ Google ก็จะทำการทายว่ารูปที่เราวาดนั้นเป็นรูปอะไร ซึ่ง Google ก็จะเก็บรูปที่พวกเราวาดกันไว้ เพื่อนำไปฝึกสอน AI ตัวนี้ให้เก่งขึ้นเรื่อยๆ

การที่เราๆ ท่านๆ ได้เข้าไปเล่นเกมนี้กัน ทำให้ฐานข้อมูลรูปภาพในขณะนี้มีมากกว่าหนึ่งพันล้านรูปแล้ว [link] โดย Google ได้ทำการจัดเตรียมและเปิดเผยข้อมูลออกมาประมาณ 50 ล้านรูป [link] และจัดการแข่งขันขึ้นใน Kaggle โดยใช้ข้อมูลเหล่านี้ด้วย

Data

ในการแข่งขันครั้งนี้ มีจำนวน training data ทั้งสิ้น 49,707,579 รูปภาพ และจำนวน test data เท่ากับ 112,199 รูปภาพ โดย 9% ของ test data นี้ จะถูกนำไปคำนวณเป็นคะแนน public leaderboard ส่วนที่เหลืออีก 91% จะถูกนำไปคำนวณเป็นคะแนน private leaderboard ซึ่งคะแนนส่วนหลังจะประกาศหลังจากเสร็จสิ้นการแข่งขัน และนำมาใช้จัดอันดับที่แท้จริง (เหมือนการแข่งขัน Kaggle รายการอื่นๆ ทั่วๆ ไป)

จำนวนคลาสของข้อมูลชุดนี้มีทั้งสิ้น 340 คลาส โดยแต่ละคลาสจะมีข้อมูลราวๆ ตั้งแต่ 113K ภาพ ไปจนถึง 340K ภาพ ซึ่งก็นับว่าเป็นชุดข้อมูลที่แต่ละคลาสค่อนข้างสมดุลกัน และมีข้อมูลให้ใช้เทรนได้เป็นจำนวนมาก จนแทบไม่เกิดปัญหา overfitting ขึ้นเลย (ซึ่งจะกล่าวถึงในหัวข้อ validation ต่อไปครับ)

raw data มีการจัดเก็บอยู่ในรูปแบบตามลิงก์นี้ [link] นั่นคือเมื่อผู้ใช้ลากเส้นแต่ละครั้ง (แต่ละ stroke) จะมีข้อมูลพิกัด x และ y รวมทั้งเวลา t เก็บไว้ เป็นลำดับเรียงตามการวาด และเมื่อผู้ใช้ลากเส้นใหม่ ก็จะทำการเก็บลำดับใหม่ต่อไปเรื่อยๆ

ในการแข่งขันครั้งนี้มีทั้ง raw data และ simplified data ให้ได้เลือกใช้ โดย simplified data จะทำการ preprocessing เบื้องต้น และตัดจุดที่ไม่สำคัญออกไป [link] ในการแข่งขันครั้งนี้ ทีมของเราได้เลือกใช้ simplified data เนื่องจากเวลาและทรัพยากรที่จำกัด หากใช้ raw data จะทำให้มีข้อมูลที่ต้องนำมาประมวลผลเยอะเกินไป อีกทั้งยังเห็นว่า simplified data แทบจะไม่ได้ทำให้ information ที่สำคัญในการจำแนกคลาสสูญเสียไป

Evaluation

การคิดคะแนนในการแข่งขันนี้ จะให้เราทายได้ 3 คลาส แล้วคำนวณค่า Mean Average Precision @ 3 (MAP@3) โดยในแต่ละภาพ เราจะทายคลาสที่คิดว่าใช่ที่สุดอยู่เป็นอันดับแรก และคลาสที่น่าจะใช่รองลงมาเป็นอันดับที่สองและสามตามลำดับ สำหรับ average precision โดยนิยาม จะคำนวณได้จากพื้นที่ใต้ precision-recall curve [link] ในกรณีนี้ที่มีคลาสจริงเพียงคลาสเดียว และมีคลาสที่ทาย 3 คลาส ค่า average precision จะลดรูปเป็นสูตรง่ายๆ ได้ว่า ถ้าทายถูกตั้งแต่ตัวแรก จะได้คะแนนเป็น 1 ถ้าทายถูกในตัวที่ 2 และ 3 ก็จะได้คะแนนเป็น 0.5 และ 0.33... ตามลำดับ และถ้าทายไม่ถูกเลยก็จะได้คะแนนเป็น 0 ครับ

Preprocessing

เนื่องจากข้อมูลตั้งต้นมีการเก็บภาพคลาสเดียวกันไว้ในไฟล์เดียวกัน จึงต้องนำข้อมูลมาทำการ shuffling ให้คละกันก่อนนำไปเทรน โดยกระบวนการนี้เราได้ใช้โค้ดของ beluga [link] แต่ในโค้ดนี้ได้เลือกข้อมูลมา 30K ภาพต่อคลาส เราได้เปลี่ยนเป็นให้ใช้ข้อมูลทั้งหมดมาเป็น training data โดยกันคลาสละ 80 ภาพ ไว้เป็น validation data

ในด้านของกระบวนการหลักนั้น เราก็ตั้งต้นจาก kernel ของ beluga [link] อีกเช่นเดียวกัน ส่วน preprocessing ที่สำคัญคือการสร้างลำดับของจุดและเส้นให้เป็นรูปภาพก่อนนำเข้า CNN ซึ่งในส่วนนี้ beluga ได้ใส่ information ของลำดับเส้น ให้เป็น intensity ของ pixel ไว้ด้วย (นั่นคือ เส้นที่วาดตอนแรกเป็นสีดำ และเส้นที่วาดในภายหลังจะเป็นสีเทาลงเรื่อยๆ) เราได้ทำการปรับในส่วนนี้เพิ่มขึ้น เช่น

- นำภาพที่วาดเสร็จ 40% + ภาพที่วาดเสร็จ 70% + ภาพที่วาดเสร็จสมบูรณ์ = ประกอบกันเป็นภาพสาม channel

- นำภาพที่มี information ของลำดับเส้น + ภาพที่มี information ของลำดับจุดในแต่ละเส้น + ภาพที่วาดเป็นเส้นสีดำปกติ = ประกอบกันเป็นภาพสาม channel

- นำภาพที่มี information ของลำดับเส้นครบถ้วน + ภาพที่มี information ของลำดับเส้นเพียงครึ่งเดียว + ภาพที่วาดเป็นเส้นสีดำปกติ = ประกอบกันเป็นภาพสาม channel

มีผู้เข้าแข่งขันหลายคน เช่น Heng CherKeng ได้ค้นพบตรงกันว่าถ้า image size และ batch size มีค่าสูง จะทำให้ได้ค่าความถูกต้องเพิ่มขึ้น [link] เราจึงทำการเพิ่ม image size เป็น 128 นอกจากนี้ เรายังได้ลดขนาดเส้นให้เล็กลง ตามคำแนะนำของ Heng CherKeng ด้วย [link]

เรียกได้ว่าในการแข่งขันครั้งนี้ทีมของพวกเรายืนบนบ่าของยักษ์อย่างแท้จริง โดยเฉพาะยักษ์อย่างคุณ beluga ที่เขียนโค้ดตั้งต้นชั้นดีมาให้ และคุณ Heng CherKeng ที่มักจะมาแนะนำทิปและเทคนิคต่างๆ การแข่งขันครั้งนี้ที่พวกเราลุล่วงลงไปได้ ก็ต้องขอขอบคุณผู้เข้าแข่งขันท่านอื่นๆ ที่ร่วมกันอภิปรายแลกเปลี่ยนความรู้และปูทางเอาไว้ด้วยครับ

Models

ในงานด้าน computer vision นั้น มี architecture ของ deep learning ให้เลือกใช้ได้เป็นจำนวนมาก ทั้งนี้ได้มีผู้เปรียบเทียบ architecture ต่างๆ ในด้านความถูกต้อง จำนวนการคำนวณที่ต้องใช้ และขนาดของ parameter ไว้ [link] โดยเราได้ยึดรูปนี้เป็นหลัก แล้วพยายามเลือกโมเดลที่ให้ความถูกต้องสูง แต่ไม่กินเวลาและทรัพยากรมากจนเกินไป โมเดลที่เราได้เลือกใช้ก็จะมีดังนี้ครับ

MobileNet [link]

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

MobileNet ได้มีการใช้ depthwise separable convolution ซึ่งแทนที่จะทำ convolution ปกติ ที่ทุก channel จาก input ไป output เป็น dense connection คือในแต่ละ output channel ก็จะทำ convolution ทั้ง volume เลย ก็เปลี่ยนให้เป็นการทำ convolution ทีละ channel ก่อน (depthwise convolution) แล้วจึงทำ 1x1 convolution ให้ได้จำนวน channel ตามต้องการ (pointwise convolution) ทำให้ลดจำนวนการคำนวณที่ใช้ลงไปได้มาก

Xception [link]

ผู้คิดค้น Xception ก็คือ François Chollet ซึ่งเป็นผู้พัฒนา Keras นี่เอง โดย Xception นั้นมาก่อน MobileNet และใช้ depthwise separable convolution เช่นเดียวกัน แต่ต่างจาก depthwise separable convolution แบบดั้งเดิมตรงที่จะทำ pointwise convolution ก่อน แล้วจึงค่อยทำ depthwise convolution โดยได้แรงบันดาลใจมาจากโมเดล Inception ที่ทำ 1x1 convolution เพื่อลดจำนวน channel ลงก่อน

และในการแข่งขันครั้งนี้ โมเดล Xception ของเราเป็นโมเดลเดี่ยวที่ให้ค่าความถูกต้องสูงที่สุดเลยครับ

SE-ResNeXt

ในส่วนของ SE-ResNeXt เป็นการผสมทั้ง SE [link] และ ResNeXt [link] เข้าด้วยกัน สำหรับ ResNeXt นี้ ก็คือ ResNet [link] ที่ในแต่ละบล็อก จะทำการแตก channel ออกตามจำนวนที่เรียกว่า cardinality เพื่อที่จะแยกคำนวณ channel ออกเป็นกลุ่มๆ ซึ่งจะเหมือนกับ grouped convolution ที่ AlexNet ได้เคยทำไว้

ส่วน SE มาจาก Squeeze-and-Excitation Networks โดย network ตัวนี้จะรับ input ที่เข้ามา แล้วบีบอัดให้แต่ละ channel มีค่าเหลือเพียงค่าเดียว จากนั้นก็ลดจำนวน channel ลง แล้วขยายขึ้นมาให้เท่าเดิม (คล้ายๆ กระบวนการ autoencoder) แล้วจึงนำค่านี้ไปคูณกับ input ดั้งเดิม เหมือนเป็น weight ที่ให้ความสำคัญกับแต่ละ channel ไม่เท่ากัน

ในตอนแรกเราคาดว่าโมเดลนี้จะให้ค่าความถูกต้องสูง แต่เมื่อลองใช้จริงพบว่าเทรนได้ช้า จึงได้ยกเลิกการเทรนไป เพื่อเปิดพื้นที่ให้โมเดลอื่นที่มีลุ้นมากกว่า (คือ MobileNet และ Xception) ถึงแม้ภายหลังเราจะเปิดเซิร์ฟเวอร์ใหม่และใช้ multi GPU เพื่อเทรน SE-ResNeXt โดยเฉพาะ แต่ความถูกต้องก็ลู่เข้าไม่ทันเวลา

สำหรับทุกโมเดลที่กล่าวถึงข้างต้น เนื่องจากการแข่งขันในครั้งนี้มีข้อมูลมาให้เป็นจำนวนมากแล้ว เราจึงไม่ได้นำส่วนของ pre-trained ImageNet มาใช้ครับ

LSTM

เราใช้ LSTM เพื่อหวังจะเพิ่มความหลากหลายในการทำ ensemble โดยโมเดลที่เป็น CNN ในข้างต้นจะรับข้อมูลแบบรูปภาพ แต่ LSTM ซึ่งเป็น RNN จะรับลำดับของการวาดเข้ามา แล้วพิจารณาทีละจุด ให้อารมณ์คล้ายๆ กับเกมลากเส้นต่อจุด แล้วทายว่าเป็นรูปอะไร หากเปรียบเทียบกันจะเหมือนว่า CNN จะมองในเชิง space มากกว่า ในขณะที่ RNN จะมองในเชิงเวลา

โค้ด LSTM ที่เราใช้ดัดแปลงมาจาก kernel ของคุณ HuyenNguyen [link] โดยเพิ่ม feature ที่เป็นระยะห่างและทิศทางระหว่างจุดเข้าไปด้วย ผู้เข้าแข่งขันคนอื่นพบว่าผลของ LSTM ไม่ดีเทียบเท่าของโมเดลอื่นๆ ที่เป็น CNN ซึ่งโมเดล LSTM ของเราก็ให้ผลเช่นนั้นเช่นกัน แต่ทั้งนี้อาจเป็นเพราะว่าเราได้เทรนตัว LSTM ทีหลัง ซึ่งมีเวลาให้เทรนน้อย ถ้าเทรนนานกว่านี้ผลอาจจะดีขึ้นได้

Optimization

ในส่วนนี้จะมี parameter ที่สำคัญคือ batch size โดยในปัญหานี้ ถ้าให้ batch size มีค่าน้อย (เช่น 32 64 หรือ 128) อัตราส่วนภาพที่เป็น noise (คือภาพที่วาดแล้วดูไม่ค่อยออก) ในแต่ละ minibatch จะสูง ส่งผลให้การอัพเดต weight แต่ละครั้งอาจจะไปในทางที่ผิด และได้ผลลัพธ์ที่ไม่ดี พวกเราจึงพยายามปรับให้ batch size มีค่าสูงที่สุด เท่าที่ memory ของ GPU จะรองรับได้ แต่ค่า batch size นี้ ก็ขึ้นอยู่กับ image size และ parameter ของ model ด้วย ถ้าใช้ image size ขนาดใหญ่ และโมเดลที่มีความซับซ้อนสูง ก็จะทำให้ batch size มีค่าลดลง

ต่อมาเราได้พบว่ามีวิธีเพิ่มค่า batch size ได้ สองวิธีดังนี้ครับ

- ปรับให้ทำหลายๆ minibatch แล้วค่อยอัพเดต weight ทีเดียว ซึ่งก็จะมีผู้เขียนโค้ดที่ดัดแปลง Keras ไว้ให้ทำแบบนี้ได้อยู่ครับ [link]

- เราสามารถเซ็ต Keras ให้ทำ multi GPU ได้ง่ายๆ [link] ซึ่งก็จะสามารถเพิ่ม batch size ให้เป็นหลายเท่าตามจำนวน GPU ได้เลย (ในปัจจุบัน ทาง Sony สามารถเทรน ImageNet ได้ในเวลา 224 วินาที โดยใช้ Tesla V100 GPU จำนวน 2176 ตัว และมี batch size เริ่มต้นที่ 34K ครับ [link])

นอกจากนี้ เราได้นำ LR finder [link] มาใช้ เพื่อช่วยในการหา learning rate ที่เหมาะสม ในแต่ละช่วงของการเทรน ส่วน setting อื่นๆ เช่น loss function ก็เป็น categorical crossentropy ธรรมดา และตัว optimizer ก็เป็น Adam ปกติครับ

Validation

ในการแข่งขันครั้งนี้มีข้อดีตรงที่ว่าพวกเราไม่พบปัญหาเกี่ยวกับ overfitting เลย โดย learning curve ของ training data กับ validation data แทบจะเป็นเส้นเดียวกัน ทั้งคะแนนจาก validation data ก็สอดคล้องกับคะแนนใน leaderboard โดยคะแนน MAP@3 ของ validation data จะน้อยกว่าของ leaderboard อยู่ราวๆ 0.05 ทั้งนี้อาจเป็นเพราะว่าข้อมูลของ leaderboard มีการคัดกรองภาพที่เป็น noise ออกแล้ว ทั้งนี้คะแนนของ public leaderboad กับ private leaderboard ก็มีค่าใกล้เคียงกันมาก ทำให้เราสามารถรู้ได้เลยว่าถ้าเทรนขึ้นแล้ว จะสามารถทำให้คะแนนในการแข่งขันดีขึ้นได้แน่นอน

Ensemble

เราได้ใช้ classifier ที่มี architecture แตกต่างกัน และที่ใช้ feature แตกต่างกัน มาทำเป็น ensemble นอกจากนี้ ก็มีการใช้ snapshot ensemble [link] เข้าช่วยด้วย ซึ่งสุดท้ายแล้ว ก็ได้ classifier ทั้งสิ้นจำนวน 15 ตัวประกอบกัน

ในการทำ ensemble ได้ทดลองใช้แบบการหาค่าเฉลี่ยธรรมดา และการให้ค่า weight กับแต่ละ classifier โดยเรียน weight จาก validation data เราพบว่าการใช้ weight ทำให้คะแนนของ leaderboard สูงขึ้นนิดนึง ในทศนิยมตำแหน่งที่สี่ครับ

Hardware

เรื่องสำคัญอีกเรื่องนึงที่จะไม่พูดถึงไม่ได้ก็คือเรื่องของ hardware เนื่องจากการแข่งขันครั้งนี้ต้องรันหนักพอสมควร ถ้าใช้ kernel ของ Kaggle หรือ Google Colab ก็จะมีข้อจำกัดอยู่หลายอย่าง เดิมทีเรามีเซิร์ฟเวอร์อยู่เครื่องนึง ซึ่งในขณะนั้นก็รันงานอื่นๆ เอาไว้ด้วย เมื่อลงทำการแข่งขันรายการนี้ พวกเราเลยตัดสินใจเปิดเซิร์ฟเวอร์ใหม่ เพื่อจะได้ GPU เพิ่มขึ้นอีก 4 ตัว มาใช้เทรนงานนี้โดยเฉพาะ

เราใช้บริการคลาวด์ของ Azure อยู่ ซึ่งมี DSVM [link] ที่เป็น virtual machine สำหรับทำงานด้านนี้ ทำให้สามารถเปิดเซิร์ฟเวอร์แล้วใช้งานได้ทันที ไม่ต้องเซ็ตอัพอะไรยุ่งยาก พวกเราจึงไม่ต้องเสียเวลากับเรื่องที่เกี่ยวกับ hardware มากเกินไป ในภายหลัง ช่วงสัปดาห์สุดท้ายของการแข่งขัน เราอยากจะเปิดเซิร์ฟเวอร์อีกเครื่องนึง เพื่อรันโมเดลให้ได้มากขึ้น แต่ปรากฏว่าโควตา CPU เต็มแล้ว ต้องส่งคำขอเพิ่มโควตาไป แม้จะใช้เวลาเดินเรื่องอยู่ราวๆ 2-3 วัน แต่ฝ่าย support ของ Microsoft ก็ให้บริการเป็นอย่างดี ต้องขอขอบคุณไว้ ณ ที่นี้ด้วยครับ

 

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

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

This topic was modified 6 years ago by nameless
This topic was modified 6 years ago by The Neural Engineer

   
อ้างอิง
The Neural Engineer
(@neural-engineer)
Honorable Member Admin
เข้าร่วมเมื่อ: 7 years ago
ข้อความ: 412
 

ภาพแสดง trade-off ของ accuracy vs. efficiency จากแหล่งข้อมูลตามที่คุณไร้นามอ้างถึงข้างบนครับ


   
nameless reacted
ตอบกลับอ้างอิง
The Neural Engineer
(@neural-engineer)
Honorable Member Admin
เข้าร่วมเมื่อ: 7 years ago
ข้อความ: 412
 

เสริมครับ : ตัวอย่างภาพวาดใน doodle ที่ระบบของเราต้องทำนาย แต่ละภาพเป็น input ที่โมเดลของเราจะได้รับ

และ output ของโมเดลคือ class ที่ถูกต้อง (เช่นในรูปนี้ ต้องทำนายว่าเป็น ‘ผึ้ง’ เป็นต้น)


   
ตอบกลับอ้างอิง
Share: