เจ็บแค่ไหนก็ต้องฝืนยิ้มว่าไม่เป็นไร
ทุกคนเคยโกหกกันไหมครับ ไม่ว่าจะเป็นการโกหกจากสาเหตุอะไรก็ตาม
เพราะมนุษย์นั้นเป็นสิ่งมีชีวิตที่ค่อนข้างซับซ้อนซ่อนเงื่อน พวกเราจึงมักจะมีกลไกในการป้องกันตัวเองอยู่เสมอ การโกหกก็คือหนึ่งในกลไกนั้นครับ การปกปิดสิ่งที่เราคิดอยู่เพื่อให้พ้นสถานการณ์อันคับขันก็ไม่ต่างจากการแกล้งตายของสัตว์ตัวน้อยเมื่อเจอผู้ล่าตัวใหญ่ แต่เพราะสมองเราคิดแบบนึง และสีหน้าท่าทางกลับแสดงอีกแบบนึง จึงไม่แปลกครับ ที่กลไกนี้จะไม่ได้สมบูรณ์ 100% และมีช่องโหว่เกิดขึ้น
สมมติว่าในหัวเรามีเจ้าตัวอารมณ์พวกนี้คุมเหมือนบอร์ดบริหารแบบในเรื่อง Inside Out ( ในหนังจะบอกว่าคนเรานั้นมีเจ้าตัวอารมณ์ทั้ง 5 คอยคุมเราอยู่ เวลาเกิดการกระทำอะไรขึ้น ก็มาจากเจ้าพวกนี้เป็นคนสั่ง ) แล้วถ้าเกิดว่าการโกหกนั้น เจ้าอารมณ์พวกนี้ดันคิดต่างและทะเลาะกัน ถ้าในจังหวะที่กำลังชุลมุนกันอยู่ ต้าว Anger เผลอเอามือไปโดนปุ่มบางปุ่มเข้าล่ะ แน่นอน ร่างกายของเราก็ต้องมีรีแอ็คชั่นที่เกิดจากปุ่มเจ้าปัญหานั้นแน่ ๆ แต่จะเกิดนานเท่าไหร่ก็ขึ้นอยู่กับอีก 4 ตัวที่เหลือจะกู้สถานการณ์กลับมาได้เร็วแค่ไหน โชคดีครับที่เจ้าตัวอารมณ์ของเราฝึกรับมือกับเหตุการณ์แบบนี้มาดีจึงแก้ได้อย่างฉับไวและอาจจะไวเพียงแค่เสี้ยววิจนมนุษย์คนอื่นไม่ทันได้สังเกต เหตุการณ์แบบนี้แหละครับที่ทำให้เกิด Micro-expression ขึ้น
ผมขอตั้งชื่อไทยให้ Micro-expression ว่า “อารมณ์เสี้ยววิ” ละกันครับ เพราะอารมณ์นี้มักแสดงบนใบหน้าแบบที่เราไม่รู้ตัว ควบคุมไม่ได้และใช้เวลาแค่ประมาณ 0.5 วิเท่านั้นเอง โดยมักเป็นอารมณ์ที่เรารู้สึกในขณะนั้นจริง ๆ แต่อาจจะต้องปั้นสีหน้าหรือโกหกเอาไว้ แต่ก็นั่นแหละครับ หลุดออกมาอยู่ดี 😓 ซึ่งพอมันเร็วจนมนุษย์ทั่วไปไม่สามารถสังเกตได้เนี่ย ผมก็เลยคิดว่าเราน่าจะลองให้เจ้า AI นวัตกรรมสุดแสนฉลาดช่วยหาให้ ก็เลยออกมาเป็นโปรเจคนี้ครับ Micro-expression recognition หรือ การตรวจหาอารมณ์เสี้ยววิด้วย AI ( ชื่อเท่ได้เพียงแค่เติม AI 😉)
Problem statement — เหตุผลที่ต้องใช้ AI แก้ปัญหา แต่จะมีกี่เหตุผลเธอก็เลือกเขา
เพราะว่า Micro-expression เป็นการแสดงอารมณ์ที่รู้สึกจริง ๆ แต่ต้องโกหกเอาไว้ และเกิดขึ้นเร็วมากจนมนุษย์ทั่วไปไม่สามารถรับรู้ได้เลย ถ้าสมมติมีโมเดลที่สามารถตรวจและระบุอารมณ์นี้ได้อย่างแม่นยำ ก็จะสามารถจับอารมณ์ที่โกหกอยู่ได้และมีประโยชน์เป็นอย่างมากเช่นการจับโกหกในการนำไปเป็นพยานหลักฐานของสืบสวนคดีต่าง ๆ
การสร้าง Machine learning หรือ AI ในการตรวจหาก็จะมี process คร่าว ๆ ประมาณนี้
- เก็บสะสม data — SAMM dataset
- จัดการกับ data ที่หามาได้ — Try different input and cross validation
- หา model ที่เหมาะกับ data — CNN
- เทรน model นั้นด้วย data ของเรา
- ประเมินผล model — with human baseline
Data collection — การเก็บข้อมูล ก็ยังง่ายกว่าเก็บหัวใจที่ถูกเธอเหยียบย่ำขึ้นมา
เนื่องจาก Micro-expression เป็นเรื่องที่คนทำน้อย จึงทำให้ dataset หาได้ค่อนข้างยากและส่วนใหญ่ต้องส่ง email ไปขออนุญาตคนทำ dataset ก่อน จึงทำให้ผมหาได้แค่ SAMM dataset เราลองมาดูข้อมูลของ dataset นี้กัน
- ประกอบไปด้วยรูปภาพที่แคปมาจากวิดีโอที่ถ่ายด้วยกล้องความเร็ว 200 fps
- มีทั้งหมด 29 subjects 159 samples แต่ละ sample มีรูปประมาณ 30-100 รูป
- มี .mat file ของทุก samples ให้
- มีอารมณ์ทั้งหมด 7 อารมณ์ได้แก่ Happiness, Sadness, Anger, Disgust, Contempt, Fear และ Other
- มี Onset frame ( หมายเลขเฟรมที่เริ่มอัดคลิป ), Apex frame ( หมายเลขเฟรมที่เป็นจุดพีคของการเกิด micro-expression ), Offset frame ( หมายเลขเฟรมที่จบการอัดคลิป ) และ Duration ( จำนวนเฟรมที่จับได้ ซึ่งสอดคล้องกับจำนวนรูปในไฟล์)
- มี Action units ( หมายเลขตำแหน่งต่าง ๆ ของใบหน้าที่เกิดการขยับ )
และเมื่อเราได้ SAMM dataset มาแล้ว ต่อไปก็ถึงเวลาจัดการกับข้อมูลนี้
Preparing ( or preprocessing ) and cleaning data — จัดการจัดใจกับข้อมูล
ผมขอเรียก SAMM dataset ว่า แซม ละกันนะครับ 😄 เพราะเจ้าแซมของเราเนี่ยมีข้อมูลที่เราอาจจะไม่ได้ใช้อยู่เยอะมาก เราจึงต้องมาจัดการกับข้อมูลแต่ละตัว ว่าตัวไหนที่เราสามารถใช้ประโยชน์ได้ โดยข้อมูลที่ผมคิดว่าน่าจะเป็นประโยชน์กับเรานอกเหนือจาก Estimated Emotion ที่เป็นพระเอกในงานนี้ ก็คือ Apex frame และ Onset frame โดยเฉพาะ Apex frame ที่บ่งบอกว่าจุดที่เกิดอารมณ์นี้มากที่สุดหรือใบหน้าขยับมากที่สุดอยู่ที่เฟรมไหน จึงทำให้เป็นประโยชน์อย่างมากในการนำข้อมูลนี้มาใช้
หลังจากคัดว่าควรใช้ข้อมูลอะไร ก็มานั่งแก้ typo ที่อาจจะมีอยู่บ้างเล็กน้อยใน dataset และเตรียมหา input ของ model นี้
เพราะด้วยจำนวนข้อมูลที่มีอยู่น้อยมากกกกกกกกกกกกกกกกกกกกกกกกกกกกกกกและจำนวนอารมณ์จาก samples ก็ไม่เท่ากัน ( Imbalanced ) แบบมาก ๆๆๆๆๆๆๆๆๆ เลยต้องลองหาหลาย ๆ วิธีเพื่อได้โมเดลที่ดีที่สุด
เริ่มด้วยการตัด emotion ที่มีจำนวน data น้อยมาก ๆ ออกไปซึ่งได้แก่ Sadness, Fear และ Disgust ทำให้โมเดลเราจะทำนายได้แค่ 5 classes [ Anger, Contempt, Happiness, Surprise, Other] ( papers ส่วนใหญ่ก็ใช้ 5 classes นี้ )
จากนั้นผมได้ลอง input หลาย ๆ แบบ โดยทำไปทั้งหมด 4 แบบ
- Original Apex frame — ใช้เฟรม ณ จุด Apex มาเป็น input
- Difference of Apex frame and Onset frame — เนื่องด้วยเราได้แปลงข้อมูลภาพของเราเป็นตัวเลขเมื่อนำเข้าโมเดล จึงลองเอาผลต่างของ Apex กับ Onset มาเป็น input เพื่อแสดงว่าภาพตอนพีคกับตอนเริ่มต่างกันยังไง
- Optical flow of Apex frame and Onset frame — เปลี่ยนจากผลต่างตัวเลขของ 2 เฟรมมาเป็น Optical flow ซึ่งก็คือแพทเทิร์นการขยับของภาพ โดยจะแสดงเป็นกลุ่มการไหลของแสง
- Optical flow of Apex frame and Onset frame + Original Apex frame — คือการนำทั้ง Optical flow เมื่อกี้และภาพของ input แบบที่ 1 มารวมกันและนำไปเป็น input
** Warning ภาพต่อไปนี้อาจจะมีเนื้อหาที่น่ากลัว ผมพยายามเลือกรูปที่ไม่ค่อยน่ากลัวแล้วครับ แต่ก็ยังน่ากลัวอยู่ T^T **
และผมได้ลองการแบ่ง train-validation data ทั้ง
- K-folds cross validation — เพื่อใช้ประโยชน์ข้อมูลที่มีอยู่น้อยนิดให้มากที่สุดโดยแบ่งข้อมูลเป็นหลาย ๆ เซตและแยกกันเทรน ทำให้โมเดลได้เห็นข้อมูลหลายแบบมากขึ้น โดยใช้ Stratified Group เพราะทำให้ในการแบ่ง folds แต่ละครั้ง เกิดการแบ่งขึ้นโดยที่ sample ของแต่ละอารมณ์ เกิดความสมดุลกันมากที่สุด
- LOSO ( Leave one subject out ) — เป็นการแบ่ง data โดยใช้ subject เป็นตัวแบ่งหลัก โดยจะมีจำนวนเซตทั้งหมดตามจำนวน subject ที่มี และแต่ละเซตก็จะมี subject เพียงคนเดียวที่เป็น validation data ส่วนที่เหลือจะเป็น training data ทำให้ผลัดกันเป็น validation ครบทุกคน ( วิธีนี้เป็นวิธีที่ใน papers ส่วนใหญ่ใช้กันจึงทำให้สามารถใช้ผลลัพธ์จาก LOSO ไปเปรียบเทียบได้ )
เมื่อได้ input และแบ่ง train-validation set แล้วต่อไปก็คือการเลือกใช้โมเดลนั่นเองคร้าบบบบ
Choosing the model — เลือกโมเดลที่ใช่ที่ชอบเพื่อเธอ
การตรวจจับ Micro-expression ถือเป็น 1 ในปัญหาของ Image classification ผมจึงเลือก CNN ( Convolutional Neural Network ) มาเป็นโมเดลของเรา โดยใช้เป็นโมเดล ResNet-18 ที่ pretrained มาจาก EfficientFace ซึ่งเทรนมาจาก MS-Celeb-1M และมีจำนวน classes มากถึง 12666 ที่เลือกใช้โมเดลตัวนี้เพราะได้ pretrained มาจาก dataset ที่เกี่ยวข้องกับใบหน้า ทำให้เวลาเอามาใช้ตรวจจับ Micro-expression ก็จะสามารถจับอารมณ์ได้เยอะขึ้น
Training model with SAMM — โมเดลยังมูฟออนไป epoch ต่อไป แต่ทำไมเรายังอยู่ที่เธอ
ส่วนนี้ก็ไม่มีอะไรมากครับ แค่ลองเอา training data มาใส่ในโมเดลและลองปรับพวก parameters ต่าง ๆ เพื่อให้ accuracy ออกมามากที่สุด ซึ่งตอนเทรนก็มีทั้ง validation loss เพิ่มขึ้น ลดลง เหวี่ยงไปเหวี่ยงมาเป็นรถไฟเหาะเลยครับ ตอนเทรนจะสังเกตได้ว่า model สามารถทำ accuracy กับ training set ได้ถึง 90–100% เลยครับแต่สำหรับ validation set ก็จะวนอยู่ประมาณ 40–60%
Evaluating model — โมเดลดีหรือไม่ ไม่รู้ รู้แค่เธอบอกว่าเราดีเกินไป
การที่จะประเมินว่าโมเดลเราดีหรือไม่ สามารถทำได้หลายวิธีครับ แต่ในโปรเจคนี้เราจะใช้
- Accuracy — บอกความแม่นยำของโมเดลโดยหาได้จาก จำนวน sample ที่ทายถูกหารด้วยจำนวน sample ทั้งหมด แต่เพราะ data ของเรา imbalance จึงทำให้ไม่สามารถดูแค่ค่า Accuracy อย่างเดียวได้ เพราะถ้าเกิดโมเดลทำนาย class ที่มีจำนวนเยอะถูก ก็จะเป็นการดึงให้ Accuracy สูงกว่าความเป็นจริงได้
- F1 score — ด้วยการที่ F1 score จะคิดคะแนนจากการดูจำนวนในแต่ละคลาสด้วย ทำให้ค่อนข้างแม่นยำกว่า Accuracy
และผมได้ลอง plot Confusion Matrix มาดู เพื่อ visualize ให้เห็นไปเลยว่าแนวโน้มของการทำนายเป็นแบบไหน
แต่แน่นอนว่าการที่จะบอกได้ว่าดีหรือไม่ ก็จำเป็นจะต้องมีตัวเปรียบเทียบหรือ Baseline ซึ่งในโปรเจคนี้มีทั้ง Human baseline และ Random baseline
Human baseline ได้ให้อาสาสมัครจำนวน 29 คน ลองทำแบบทดสอบในการแยก Micro-expression ดู โดยแบบทดสอบมีทั้งหมด 10 ข้อและได้ผลลัพธ์ดังนี้
- Accuracy เฉลี่ยทุกข้ออยู่ที่ 27.59%
- และมี Accuracy เฉลี่ยแต่ละข้อดังนี้
Random baseline เกิดจากการสุ่มเลขระหว่าง 0 ถึง 4 เป็นจำนวน 10 เลข โดยสุ่มทั้งหมด 30 เซต เพื่อให้ใกล้เคียงกับ Human baseline ซึ่งได้ผลลัพธ์ดังนี้
- Accuracy เฉลี่ยทุกข้ออยู่ที่ 21.38%
- มี Accuracy เฉลี่ยแต่ละข้อดังนี้
ซึ่งพอมาเทียบกับโมเดลของเราที่เทรนโดยยังไม่เคยเห็น subject ทุกคนในแบบทดสอบ พบว่าโมเดลสามารถทำนายถูก 6 ข้อจาก 10 ข้อหรือคิด Accuracy เป็น 60%
เมื่อนำมาวิเคราะห์ในแต่ละข้อพบว่า ข้อที่มนุษย์ได้ accuracy สูงกว่าข้ออื่น ๆ โมเดลก็สามารถทำนายข้อพวกนั้นได้ถูกต้อง
ส่วนโมเดลที่เทรนแบบ LOSO ก็ได้ผลลัพธ์ประมาณนี้ครับ
Confusion matrix ของ LOSO
จะเห็นได้ว่าในช่อง diagonal ของแต่ละแถวจะมีค่ามากที่สุดในแถวนั้น ๆ ( แต่มันก็ยังกระจายอยู่ดี 😥 ) ซึ่งอันนี้คือโมเดลของผมที่มีค่า F1 score มากที่สุด เพราะส่วนใหญ่โมเดลอื่นจะสามารถจับ Anger กับ Other ได้ แต่ Contempt, Happiness, Surprise กลับจับได้น้อย โดยเฉพาะ Contempt ซึ่งน่าจะมาจากการที่อารมณ์นี้มักจะเกิดการขยับน้อยและส่วนใหญ่จะเกิดบริเวณมุมปาก ทำให้โมเดลอาจจะสับสนกับอารมณ์อื่น ๆ ที่เกิดบริเวณมุมปากอย่าง Happiness และ Anger ได้
Error analysis
ปัญหาหลัก ๆ ของโปรเจคนี้ที่ทำให้ accuracy ไม่ได้สูงมากเกิดจาก
- จำนวนข้อมูลที่มีน้อย
- ความ Imbalance ของข้อมูลในแต่ละ class
- sample บางคลิป แทบไม่ขยับหรือขยับน้อยมาก
- sample บางคลิป มีตำแหน่งหน้า ที่ไม่ตรงกันใน Apex frame กับ Onset frame ทำให้ optical flow ฟุ้งกระจาย
สามารถไปลอง model เล่น ๆ ได้ที่ Link..
และส่ง feedback หรือคำแนะนำได้ที่ Link…
ในอนาคต ถ้ามีโอกาสได้ทำเกี่ยวกับเรื่องนี้อีก ผมก็อาจจะไปสนใจในส่วนของ Action Units เพราะถือเป็นอีก feature ที่ค่อนข้างมีความแน่นอนในการตรวจหาอารมณ์
สุดท้ายนี้ขอขอบคุณโครงการ AI Builders พี่ตอย พี่ปิงและพี่เมนเทอร์ทุกคนที่คอยให้ความรู้และแบ่งปันประสบการณ์ซึ่งมีส่วนช่วยให้โปรเจคนี้ผ่านไปได้ด้วยดี ขอบคุณมากครับ
พระอาทิตย์ขึ้นแล้ว แต่ accuracy ยังไม่ขึ้นเลย