Too good to be true : สิ่งที่ต้องระวังเมื่อพบผลลัพธ์ที่แม่นยำผิดปกติจากโมเดล Machine Learning ตอนที่ 1 : Data Leakage และData Splitting
ThaiKeras (15 April 2020)
สวัสดีครับเพื่อนๆ เพื่อนๆ คิดว่าอะไรสำคัญที่สุดใน Machine/Deep Learning? การสร้าง Model ที่ใหม่ล่าสุดในปัญหาที่ซับซ้อนสูง หรือการมี Resources ชั้นเลิศที่สามารถฝึกสอนโมเดลได้รวดเร็วที่สุด สองเรื่องนี้สำคัญมากๆ จริงๆ โดยเฉพาะในยุค Deep Learningนี้ที่มีโมเดลที่ดีมัก “ลึก” คือมีความซับซ้อนสูงและใช้เวลานานในการฝึกสอน
อย่างไรก็ดี Deep Learning Platforms ใหม่ๆ ได้ช่วยแบ่งเบาภาระสองเรื่องนี้ไปเยอะมากๆ ปัจจุบันเราสามารถสร้างโมเดลที่ประสิทธิภาพสูงสุด เช่น EfficientNet ในฝั่ง Computer Vision หรือ Transformer ในฝั่ง NLP ด้วยการเขียนโปรแกรมเพียงไม่กี่บรรทัดเท่านั้น รวมทั้ง Platforms เช่น Kaggle หรือ Colab ก็มี GPUs (หรือกระทั่ง TPUs) ให้ใช้ได้แทบจะฟรี
(ดูความหมายศัพท์ต่างๆ ของ Deep Learning เหล่านี้ได้ที่นี่ครับ — https://thaikeras.com/2018/guide-tour-series/ )
เรื่องที่สำคัญกว่าสองเรื่องข้างต้น ก็คือ “การออกแบบกระบวนการเรียนรู้” เพื่อให้แน่ใจได้ว่า “ความรู้ที่ได้จากโมเดล” นั้น “สามารถใช้งานได้ในโลกความเป็นจริง”
ตัวอย่างเช่น ในทางการแพทย์ พบว่าหลายๆ งานที่เคลมว่าสามารถตรวจสอบโรคได้แม่นยำเกือบ 100%พอไปใช้งานจริง แทบจะทำนายโรคแบบสุ่มๆ
ในทางธุรกิจหรือการเงิน ก็เช่นกัน หลายโมเดลที่ทำนายยอดขายในทางทดลองได้แม่นยำแทบจะ 100% หรือ ระบบที่ทำกำไรบนตลาดหุ้นได้สูง ก็ใช้ไม่ได้จริงเมื่อมาทดลองใช้กับยอดขายหรือ ราคาหุ้นในอนาคต (ไม่ใช่ ข้อมูลในอดีต) ผลลัพธ์ที่เก่งกาจเกินไปในการทดลอง (แต่ใช้งานจริงไม่ได้) บางครั้งเราเรียกว่า “too good to be true” ครับ
สาเหตุหนึ่งที่พบบ่อยที่สุดของ “too good to be true” มักเกิดจากเรื่องที่ง่าย แต่ลึกซึ้งนั่นคือเรื่อง “การแบ่งข้อมูล” (Data Splitting) ซึ่งถ้าทำผิดพลาดจะก่อให้เกิดปัญหาที่เรียกว่า “Data Leakage” หรือ “ข้อมูลรั่ว” ที่ทำให้ผลลัพธ์ที่เราวัดเองนั้นยอดเยี่ยม แต่ใช้งานจริงไม่ได้ครับ
ในการจะทำความเข้าใจปรากฏการณ์นี้ต้องขอเริ่มจากพื้นฐานคือ Data splitting ก่อน
ว่าด้วย Data Splitting
เป็นเรื่องที่สอนในบทเรียนทุกหลักสูตรอยู่แล้วว่าในการสร้างโมเดล Machine Learning นั้นเราจำเป็นต้องแบ่ง “ข้อมูล” ออกเป็นสองกลุ่ม หรือ data splitting โดย
ส่วนแรกคือ Training Data เพื่อเอาไว้สอนโมเดลให้มีความรู้ในปัญหา และ
ส่วนที่สองคือ Validation Data (หรือในบางครั้งอาจเรียกว่า Test Data) ออกจากการสร้างโมเดล เพื่อเอาไว้ทดสอบความแม่นยำของโมเดลหลังจากเรียนรู้
ในตำรา Machine Learning หรือ AI ขั้นพื้นฐานนั้น มักไม่ลงรายละเอียดตรงนี้ เพื่อความง่ายในการสอน มักจะสมมติว่าการแบ่งข้อมูลนั้นไม่จำเป็นต้องเอาใจใส่อะไรมาก เพียงแต่ “แบ่งข้อมูลแบบสุ่ม” (Random Splitting) ก็เพียงพอแล้ว (ซึ่งในความเป็นจริง เพียงพอในบางปัญหาเท่านั้น)
ซึ่งการแบ่ง validation data นี้ทำง่ายมากๆ ในแง่การเขียนโปรแกรม ใน library ชั้นนำเช่น SciKit-Learn เรา “แบ่งข้อมูลแบบสุ่ม” ได้ด้วยคำสั่งเพียง 1 บรรทัด นั่นคือคำสั่ง train_test_split หรือ Kfold
แต่การเขียนโปรแกรมง่ายๆ 1 บรรทัดนี้ “ชี้เป็นชี้ตาย” project ทั้งหมดของเราที่เราต้องใช้เวลาพัฒนาอีกหลายเดือนข้างหน้าทันที ถ้าเราแบ่งข้อมูลไม่ดีและเกิด Data Leakage ขึ้น ทำให้วัดผลผิด ทิศผิดทาง เวลาที่พัฒนาโมเดลหลายเดือนนั้น เอาไปใช้งานไม่ได้จริง และแทบจะสูญเวลาเปล่าๆ ทันที เปรียบเสมือนเราตั้งใจเดินทางไปเชียงใหม่แต่แผนที่ของเราชี้ไปที่ยะลา เป็นต้น
ในบทความนี้ขออนุญาตยกตัวอย่างที่สำคัญตัวอย่างแรกและพบบ่อยที่ทำให้การแบ่งข้อมูลแบบสุ่มใช้ไม่ได้ผล หรือเกิด Leakage ขึ้นทั้งใน Computer Vision และ NLP และตัวอย่าง too good to be true อื่นๆ ขอยกยอดไปในบทความหน้าครับ
สาเหตุสำคัญของ Leakage คือข้อมูล training และ validation อาจมีความเกี่ยวข้องกันโดยไม่ตั้งใจ
===ตัวอย่างใน Computer Vision===
ตัวอย่างที่พบบ่อยคือ งานทำนายด้านการแพทย์ต่างๆ เช่น ทำนายโรคปอดจากภาพ X-Ray หรือทำนายเลือดคั่งในสมองจาก CT-Scan หรือข้อมูลอะไรก็ได้ที่
“คนไข้หนึ่งคนอยู่ในหลาย examples”
ตัวอย่างเช่น ในปัญหา CT-Scan ของสมองนั้น คนไข้หนึ่งคนจะมีภาพ slice ของสมองอยู่มากมาย ซึ่งแต่ละภาพ slice นั้นนับเป็น 1 training example แต่ว่าในแต่ละ slice ของคนไข้คนเดียวกันนั้น มีความคล้ายคลึงกันอยู่ ดังนั้น การ split แบบ random ตรงๆ จะทำให้บาง slice ไปอยู่ใน training set และบาง slice อยู่ใน validation set ซึ่งทำให้โมเดลแทนที่จะวิเคราะห์แผลในสมอง อาจจะจำรูปร่างสมองของคนไข้คนนั้นไปเลย แล้วทำนายผล ซึ่งทำให้ได้ผลดีมากใน valid set แต่ใช้ไม่ได้เลยในการวิเคราะห์สมองคนไข้ใหม่ๆ ที่ไม่เคยเห็นมาก่อน
ในภาพ X-Ray ปอดหรือ Data อื่นๆ ก็ทำนองเดียวกัน คนไข้หนึ่งคนอาจมีข้อมูลกระจายไปหลาย examples ดังนั้นการแบ่งข้อมูลแบบ random ตรงๆ นั้นใช้ไม่ได้ เนื่องจากโมเดลจะไป “จำ” ลักษณะเฉพาะของคนไข้ แทนที่จะเรียนรู้ธรรมชาติของ “ความผิดปกติ”
การแบ่งข้อมูลที่ถูกต้องในกรณีนี้ ก็คือต้องแบ่งแบบสุ่ม โดยยึดจากคนไข้เป็นหลัก ถ้าคนไข้คนหนึ่งอยู่ใน training set หรือ valid set แล้ว examples ทั้งหมดที่เกี่ยวข้องกับคนไข้คนนั้น ต้องเข้าไปอยู่ใน set เดียวกันด้วย เพื่อป้องกัน data leakage
นอกจากนี้ปัญหา Leakage บางครั้งลึกซึ้งและดูยากมาก เช่น ในบทความก่อนๆ เราพูดถึงเรื่อง DeepFake Detection ซึ่งในปัญหานี้ก็เกิด leakage ได้เช่นเดียวกัน และทำให้โมเดลหลายโมเดลทำงานได้ไม่ดี ผู้สนใจสามารถดูได้ที่นี่ครับ
https://www.kaggle.com/c/deepfake-detection-challenge/discussion/126691
=== ตัวอย่างใน NLP ===
ตัวอย่างทำนองนี้เกิดขึ้นกับปัญหา NLP ได้หลายรูปแบบเช่นกัน ตัวอย่างแรกที่ดูออกได้ง่าย เช่น ปัญหา คัดกรอง Spam mail นั่นคือ เรามีตัวอย่าง email หลายแสนฉบับและเราต้องการสร้างโมเดลที่คัดแยกได้ว่า email ไหนเป็น spam mail โดยแต่ละ email คือ 1 examples
ปัญหานี้ดูเผินๆ ก็เหมือนว่าการแบ่งข้อมูลแบบสุ่ม หรือ random splitting ก็น่าจะใช้ได้ แต่ในความเป็นจริงแล้ว spam mail จากผู้ส่งคนเดียวกันหรือกลุ่มเดียวกันอาจมีข้อความเดียวกัน หรือคล้ายกันมาก ทำให้ถ้าเราแบ่งข้อมูลแบบสุ่ม spam mails จากแหล่งเดียวกันนี้อาจเข้าไปอยู่ทั้งใน training และ valid set ได้เช่นกัน และทำให้โมเดล NLP ของเราแทนที่จะทำความเข้าใจว่า ลักษณะของ spam mail เป็นอย่างไร โมเดลของเราก็อาจจะเพียง “จำ” รูปประโยคแทน ว่ารูปประโยคแบบนี้คือ spam mail และทำให้โมเดลของเราล้มเหลวเมื่อไปเจอ spam mail ที่ใช้รูปประโยคใหม่ๆ เป็นต้น
ตัวอย่างถัดมาที่ ดูยากกว่า spam mail เล็กน้อยคือ ปัญหา ”วิเคราะห์คุณภาพของ Question-Answering” (ปัญหานี้ Goolge ตั้งชื่อว่า QUEST) ซึ่งเป็นปัญหาที่ยากขึ้นมาอีกขั้น โดยในปัญหานี้ในแต่ละ examples จะประกอบไปด้วยสองประโยค คือ ประโยคคำถาม และประโยคคำตอบ และ labels ของแต่ะ examples จะบ่งชี้ว่าคำถามมีคุณภาพหรือไม่ และคำตอบมีคุณภาพหรือไม่ (ดูรายละเอียดปัญหาฉบับเต็มได้ที่ https://www.kaggle.com/c/google-quest-challenge )
บางครั้งถ้าเราเทียบ strings “ทั้งหมด” ของแต่ละ examples ตรงๆ เราจะไม่พบว่ามี example คล้ายกัน เราก็จะไม่คิดว่าน่าจะเกิด Data Leakage แต่ในความจริงแล้ว Data leakage สามารถเกิดขึ้นได้ เพราะว่าคำถามหนึ่งคำถาม อาจมีคำตอบหลายคำตอบทำให้เกิด 2 examples ดังต่อไปนี้
Training example 1 : คำถาม A คำตอบ B
Training example 2 : คำถาม A คำตอบ C
ซึ่ง label “คุณภาพของคำถาม” ทั้ง example 1 และ 2 นั้นคือตัวเดียวกัน ดังนั้นถ้า example1 อยู่ใน training data และ example 2 อยู่ใน valid data ก็จะทำให้เกิด data leakage ขึ้นเช่นกัน
การแบ่งข้อมูลที่ถูกต้องในตัวอย่างข้างต้น
การแบ่งกลุ่มข้อมูลลักษณะนี้ (ทั้งใน computer vision และ NLP)
แทนที่จะใช้ train_test_split หรือ Kfolds ต้องใช้ GroupShuffleSplit หรือ GroupKFold แทนครับ โดยระบุ Group = “id ของคนไข้”หรือ “id ของคำถาม” ในปัญหา QUEST เป็นต้น
ซึ่งคำสั่งเช่น GroupKFold นี้จะแบ่งกลุ่มให้ id หนึ่งๆ อยู่ในเพียงเซ็ตเดียว (train หรือ valid) ได้อย่างถูกต้อง
โดยผมมีเขียนตัวอย่างการใช้ GroupKFold ในปัญหา QUEST ไว้สามารถดูได้ใน kernel นี้ครับ
https://www.kaggle.com/ratthachat/quest-cv-analysis-on-different-splitting-methods
จากปัญหา leakage ทำนองนี้ถ้าเราพบ results ที่ too good to be true หรือ results จากมือใหม่ที่เล่าว่างานของตนชนะหรือดีกว่างานจากแล็บชั้นนำมากๆ เราอาจต้องสงสัยเรื่อง leakage ไว้ก่อน
ในทางปฏิบัติ Data Scientists ระดับโลก (เช่นมือวางระดับต้นๆ ใน Kaggle) มักพูดอยู่เสมอว่า “ก่อน“ ที่จะเริ่มคิดเรื่องออกแบบเรื่องโมเดลนั้น ต้องใช้เวลาทำความเข้าใจข้อมูล การวัดผล และการแบ่งข้อมูล validation ให้ถ่องแท้ก่อน ไม่เช่นนั้นจะไม่เริ่มแก้ปัญหาเด็ดขาด บางครั้งใช้เวลาทำความเข้าใจเรื่องเหล่านี้หลายสัปดาห์ทีเดียว ก่อนจะเริ่มสร้างโมเดลเพื่อฝึกสอน data
(เช่น บทสัมภาษณ์ “BestFitting” มือวางอันดับ 1 ของโลก เจ้าของฉายา “God of Kaggle” https://medium.com/kaggle-blog/profiling-top-kagglers-bestfitting-currently-1-in-the-world-58cc0e187b )
ยังมีกรณี too good to be true ซึ่งเกิดจาก leakage หรือการ design การทดลองไม่ดี ซึ่งจะขอยกยอดไปเล่าในตอนถัดไปครับ
ดูเพิ่ม
- Split ทั้งหมดใน Scikit-learn
2. นักวิจัยสถิติพบงานตีพิมพ์ในวารสารวิชาการทางการแพทย์ที่ เกิด leakage มากกว่า 10 ผลงาน
- ดูเรื่อง data leakage เพิ่มเติม
https://www.kaggle.com/alexisbcook/data-leakage
https://towardsdatascience.com/data-leakage-in-machine-learning-10bdd3eec742
บทความของเรา case study เพิ่มเติมบน DeepFake detection ครับ