Kaggle Competition สถานที่สำหรับแข่งขันด้าน Machine Learning
ก่อนอื่นขอกล่าวถึงการแข่งขันเพื่อทำความเข้าใจโจทย์และชุดข้อมูลที่เราต้องใช้ในการทำแมชชีนเลิร์นนิ่งกันก่อน การแข่งนี้มีชื่อว่า Spaceship Titanic จัดโดย Kaggle อยู่ในหมวดหมู่การแข่งขันสำหรับผู้เริ่มต้น โดยโจทย์จะมีอยู่ว่า ในปี 2912 ทักษะด้านวิทยาศาสตร์ข้อมูลเป็นสิ่งจำเป็นมากในการไขความลับของจักรวาล ซึ่งเราได้รับสัญญาณที่ผิดปกติจากที่ที่ห่างไกลออกไป 4 ปีแสง และเมื่อเดือนที่แล้วได้มีการเปิดให้บริการเรือโดยสารระหว่างดวงดาวชื่อว่า Spaceship Titanic โดยในการเดินทางครั้งนี้มีผู้โดยสารทั้งหมด 13,000 คน ทว่ากลับพบเหตุการณ์ประหลาดทำให้ผู้โดยสารบางส่วนถูกย้ายไปยังมิติอื่น ดังนั้นเพื่อการช่วยเหลือแล้วเราต้องทำนายว่าผู้โดยสารคนไหนบบ้างที่ถูกย้ายไปยังมิติอื่นด้วยเหตุการณ์ประหลาดนี้ โดยใช้ข้อมูลที่กู้คืนมาได้จากคอมพิวเตอร์ของยานอวกาศ! ฉะนั้นกล่าวสั้น ๆ ก็คือโจทย์งาน Binary Classification โดยทำนายว่าผู้โดยสารคนไหนบ้างที่ถูกย้ายไปยังมิติอื่น
รายละเอียดของฟีเจอร์ในชุดข้อมูลมีดังนี้
- PassengerId คือ รหัสประจำตัวของผู้โดยสารแต่ละคน โดยจะอยู่ในรูปแบบ gggg_pp เช่น 0123_55 เป็นต้น ซึ่ง gggg นั้นบ่งบอกถึงกลุ่มของผู้โดยสาร และ pp หมายถึงหมายเลขของผู้โดยสารคนนั้น ๆ ในกลุ่ม โดยส่วนใหญ่ผู้โดยสารในกลุ่มเดียวกันมักเป็นครอบครัวเดียวกันแต่ก็ไม่เสมอไป
- HomePlanet คือ ดาวเคราะห์ที่ผู้โดยสารขึ้นยานมา โดยทั่วไปแล้วจะเป็นดาวเคราะห์บ้านเกิดด้วย
- CryoSleep คือ ค่าที่ไว้บอกว่าผู้โดยสารคนนั้นนอนในแคปซูลแบบ Cryo Sleep ในห้องโดยสารของพวกเขาหรือไม่
- Cabin คือ หมายเลขห้องโดยสาร จะอยู่ในรูปแบบ deck/num/side เช่น A/2/P เป็นต้น โดย side นั้นจะเป็นมีแค่ P (Port) หรือ S (Starboard) เท่านั้น
- Destination คือ จุดหมายปลายทางที่ผู้โดยสารจะเดินทางไป
- Age คือ อายุของผู้โดยสาร
- VIP คือ ระบุถึงสถานะของผู้โดยสารว่าได้จ่ายเงินเพื่อบริการแบบ VIP หรือไม่
- RoomService, FoodCourt, ShoppingMall, Spa, VRDeck คือค่าใช้จ่ายที่ผู้โดยสารถูกเรียกเก็บจากการใช้งานสิ่งอำนวยความสะดวกบนยาน
- Name คือ ชื่อและนามสกุลของผู้โดยสาร
ค่าที่เราต้องทำการทำนาย
- Transported คือ ค่าที่บอกว่าผู้โดยสารถูกส่งไปยังมิติอื่นหรือไม่
ขั้นตอนการทำงานในโจทย์นี้
เริ่มลงมือทำ Machine Learning เพื่อช่วยเหลือผู้โดยสาร!
เรียกใช้งาน Library และโหลดชุดข้อมูลเข้า DataFrame
Exploratory Data Analysis (EDA)
- ตรวจสอบขนาดของชุดข้อมูล และดูภาพรวมของฟีเจอร์ต่าง ๆ โดย Train จะมีขนาด 8693 แถว 14 คอลัมน์ และจะเห็นได้ว่าเกือบทุกคอลัมน์มีข้อมูลไม่ครบถ้วน (Missing Values)
2. เรียกดูตัวอย่างของชุดข้อมูล และดูค่าสถิติเบื้องต้นของฟีเจอร์ที่เป็น Numerical
3. ตรวจสอบดูว่าชุดข้อมูลนี้มี Missing Values เป็นเท่าไหร่ โดยจะเห็นได้ว่ามี Missing อยู่ทั้งหมด 2324 ค่า คิดเป็น 1.9% ของข้อมูลทั้งหมด (ในชุด Train) และดูว่าในแต่ละคอลัมน์มี Missing เป็นเท่าไหร่บ้าง
4. ตรวจสอบดูค่า Unique ในแต่ละคอลัมน์ เพราะส่วนใหญ่แล้วฟีเจอร์เป็นประเภท Categorical จึงต้องการทราบว่ามีหมวดหมู่เป็นเท่าไหร่กันบ้าง
5. สร้างฟังก์ชันสำหรับวาดกราฟแท่งพร้อมระบุจำนวนของข้อมูลบนแต่ละแท่ง
6. ตรวจสอบจำนวนของผู้โดยสารที่ถูกย้ายไปยังมิติอื่น เพื่อให้แน่ใจว่าชุดข้อมูลสำหรับเทรนนี้จะไม่มีปัญหาเรื่อง Imbalance Class (การที่มีคลาสใดคลาสหนึ่งมากจนเกินไป
เช่น ถ้าข้อมูลสำหรับสอนโมเดลของเรามีแต่ข้อมูลที่ผู้โดยสารถูกย้ายไปมิติอื่นแน่นอนว่าโมเดลมันก็จะเรียนรู้แต่ฟีเจอร์ของผู้ที่โดนย้าย และจะไม่รู้เลยว่าคนที่ไม่โดนย้ายไปมิติอื่นมีฟีเจอร์เป็นอย่างไร)
7. ฟีเจอร์ HomePlanet ส่วนใหญ่แล้วผู้โดยสารจะมาจาก Earth และ Europa กับ Mars รองลงมาตามลำดับ ผู้โดยสารที่มาจาก Europa ส่วนใหญ่จะถูกย้ายไปยังมิติอื่นเป็นสองเท่าของคนที่ไม่ถูกย้าย, ผู้โดยสารที่มาจาก Earth ส่วนใหญ่จะไม่ถูกย้ายไปยังมิติอื่น, ผู้โดยสารที่มาจาก Mars นั้นมีจำนวนของผู้ที่ถูกย้ายและไม่ถูกย้ายไม่ต่างกันมากนัก
8. ฟีเจอร์ CryoSleep ส่วนใหญ่ผู้โดยสารไม่นอนใน Cryo ค่านี้ค่อนข้างสอดคล้องกับการถูกย้ายไปมิติอื่นพอสมควรโดยจะเห็นได้ว่าผู้โดยสารที่ไม่นอนใน Cryo นั้นส่วนใหญ่จะไม่ถูกย้ายไปยังมิติอื่น ในทางกลับกันผู้โดยสารที่นอนใน Cryo นั้นส่วนใหญ่จะถูกย้ายไปยังมิติอื่น
9. ฟีเจอร์ Destination นั้นผู้โดยสารส่วนใหญ่จะเดินทางไป TRAPPIST-1e และมีเดินไปทางไป 55 Cancri e กับ PSO J318.5-22 เป็นส่วนน้อย ผู้โดยสารที่เดินทางไป TRAPPIST-1e ส่วนใหญ่จะไม่ถูกย้ายไปยังมิติอื่น ผู้โดยสารที่เดินทางไป 55 Cancri e ส่วนใหญ่จะถูกย้ายไปยังมิติอื่น และผู้โดยสารที่เดินทางไป PSO J318.5-22 นั้นมีจำนวนผู้ที่ถูกย้ายและไม่ถูกย้ายพอ ๆ กัน
10. ฟีเจอร์ VIP ผู้โดยสารส่วนใหญ่ไม่ได้จ่ายเงินเพื่อเป็น VIP
11. ดูการกระจายตัวของอายุผู้โดยสาร ส่วนใหญ่จะอยู่ในช่วงวัย 20 ปี ถึง 30 ปีตอนต้น
12. สำหรับฟีเจอร์ค่าใช้จ่ายอื่น ๆ จากสิ่งอำนวยความสะดวกนั้นจะมีค่าเป็น 0 ไม่ค่อยมีการใช้จ่ายสักเท่าไหร่
13. ตรวจสอบความสัมพันธ์ระหว่างฟีเจอร์ (ที่เป็น Numerical Data) กับค่า Transported (ค่าที่ต้องการทำนาย) ซึ่งจะเห็นได้ว่าฟีเจอร์การใช้จ่ายต่าง ๆ กับอายุนั้นไม่ได้ส่งผลอะไรกับค่าที่จะทำนายมากนัก
Feature Engineering
หลังจากทำความเข้าฟีเจอร์ต่าง ๆ กันมาประมาณหนึ่งแล้วขั้นตอนต่อมาคือการเตรียมฟีเจอร์ให้เหมาะสมกับโมเดลเพื่อให้สามารถเทรนโมเดลและทำนายผลได้ ไปจนถึงช่วยเพิ่มความถูกต้องแม่นยำในการทำนายของโมเดลอีกด้วย
ฟังก์ชันสำหรับแยก Cabin และ PassengerId เพื่อสร้างเป็นฟีเจอร์ใหม่
1. ในที่นี้เราต้องการที่จะทดลองหลาย ๆ อย่างกับชุดข้อมูลก่อน ฉะนั้นจึงทำการ Copy ชุดข้อมูล train เก็บไว้ในตัวแปร X และตัด Transported (ค่าที่ต้องการทำนาย) ออกเก็บไว้ในตัวแปร y จากนั้น
- สร้างคอลัมน์ deck, num, size โดยแยกข้อมูลจากคอลัมน์ Cabin ออกจากกันและเก็บลงคอลัมน์ตามลำดับ
- สร้างคอลัมน์ group, numInGroup โดยแยกข้อมูลจากคอลัมน์ PassengerId (ตามที่อธิบายไว้ว่า gggg คือเลขกลุ่ม pp คือเลขประจำตัว)
- สร้างคอลัมน์ totalBill โดยรวมค่าใช้จ่ายจาก RoomService + FoodCourt + ShoppingMall + Spa + VRDeck (วิธีการเหล่านี้เรียกว่า Building-Up and Breaking-Down Features)
- สุดท้ายคือดรอปคอลัมน์ที่ไม่ได้ใช้แล้วได้แก่ PassengerId, Name
2. สร้างฟีเจอร์เพิ่มอีก 2 ฟีเจอร์ดังนี้
- ฟีเจอร์ family โดยกำหนดจากจำนวนของคนในกลุ่ม ถ้าคนในกลุ่มมีมากกว่า 3 คนขึ้นไปให้จัดเป็นครอบครัวเดียวกัน
- ฟีเจอร์ isAdult โดยกำหนดว่าถ้า Age มากกว่า 20 ให้จัดเป็นผู้ใหญ่
3. สิ่งที่จะทำถัดไปทำคือ Mutual Information Score เพื่อวัดความสัมพันธ์ระหว่างฟีเจอร์และค่าที่จะทำนาย เริ่มจากแปลงคอลัมน์ที่เป็น Categorical Data ด้วยวิธีการ Label Encoding และกำหนดให้ฟีเจอร์เหล่านี้ให้เป็น Discrete Features (ค่าที่ไม่ต่อเนื่อง)
4. วัดค่า MI Score ด้วย mutual_info_classif ของ Scikit-Learn จะได้คะแนนว่าฟีเจอร์นั้นสัมพันธ์กับค่าที่จะทำนายมากน้อยแค่ไหน (ในที่นี้การจัดการฟีเจอร์และวิธีการเข้ารหัสที่ผมใช้อาจจะไม่เหมาะสมนัก แนะนำว่าควรจัดการให้เหมาะกับข้อมูลและเป้าหมายมากกว่านี้ครับ แต่สำหรับขั้นตอนที่เราต้องทำ Mutual Information นี้เป็นสิ่งที่ควรทำมากครับ) อ่านเพิ่มเติมเรื่อง Mutual Information ได้ที่นี่
5. แสดงผลคะแนน Mutual Information จะเห็นว่า Cabin และ group มีคะแนนสูงมากจึงควรพิจารณาฟีเจอร์เหล่านี้ให้ดีในการนำไปเทรนโมเดล และถ้าไม่นับฟีเจอร์ที่สร้างขึ้นมาใหม่นั้น CryoSleep จะเป็นฟีเจอร์ที่มีผลกับค่าเป้าหมายมากที่สุด
Data Preparation
1. เริ่มที่ชุดข้อมูล Train ในที่นี้เราใช้วิธีจัดการ Missing Values ด้วยการ Drop แถวที่ไม่มีข้อมูลออกทั้งหมด จากนั้นแบ่งเป็นชุดสำหรับ train และชุดตรวจสอบ valid (สำหรับวัดความแม่นยำของโมเดล) และเตรียมฟังก์ชันสำหรับสร้างคอลัมน์ใหม่แบบที่เราทำไปในขั้นตอน Feature Engineering
2. เตรียมฟังก์ชันสำหรับแปลงข้อมูลที่เป็น Categorical ให้เป็นชุดตัวเลขด้วยวิธีการ One Hot Encoding โดยจะแปลงฟีเจอร์เหล่านั้นและนำไปสร้างเป็น DataFrame ใหม่อีกอันร่วมกันกับข้อมูลที่เป็น Numerical อ่านเพิ่มเติม One-hot Encoding
3. แปลงชุดข้อมูล X_train ให้พร้อมสำหรับใช้สอนโมเดล โดยเริ่มจากรีเซตค่า Index ก่อน เพราะตอนที่สร้างคอลัมน์มีพบปัญหาเรื่อง KeyError อยู่บ้างเพราะเราแบ่งข้อมูล จากนั้นสร้างฟีเจอร์ totalBill, deck, num, size, group, numIngroup, family, isAdult เช่นเดียวกับที่ทำตอน Feature Engineering และดรอปคอลัมน์ PassengerId, Name, Cabin ทิ้งไป สุดท้ายคือแปลง DataFrame ให้อยู่ในรูปแบบของฟีเจอร์ One Hot Encoding กับฟีเจอร์ Numerical รวมกัน
4. แปลงชุดข้อมูล X_valid ให้พร้อมสำหรับใช้ตอนทดลองทำนายผลลัพธ์เพื่อตรวจสอบความแม่นยำของโมเดล โดยทำทุกอย่างเช่นเดียวกันกับของ X_train
5. แปลงข้อมูล Train (ทั้งหมด) เตรียมไว้สำหรับใช้เพื่อสอนโมเดลครั้งสุดท้ายก่อนส่งคำตอบ
6. แปลงชุดข้อมูล Test สำหรับไว้ใช้ทำนายค่าและส่งคำตอบให้กับ Kaggle
Model
1. เตรียมฟังก์ชันสำหรับประเมินผลโมเดล โดยฟังก์ชัน modelEvaluation จะประเมินผลด้วย Accuracy, Precision, Recall, F1, และ ROC AUC Score ส่วนฟังก์ชัน plotROCCurve จะใช้พล็อตกราฟ ROC ซึ่งเมทริกซ์เหล่านี้มีไว้วัดความแม่นยำของงานด้าน Classification
2. ตัวอย่างการเทรนโมเดลและประเมินผลโมเดล ในโปรเจคของผมนี้โมเดลที่ทำคะแนนได้ดีที่สุดบน Kaggle คือ RandomForestClassifier โดยใช้พารามิเตอร์คือ n_estimators=100, max_depth=8 (ค่านี้ได้จากการ Fine Tuning แบบง่าย ๆ) จากนั้นสอนโมเดลด้วยข้อมูล X_train ที่ผ่านขั้นตอนการแปลงข้อมูลแล้ว พร้อมกับเฉลยชุด Y_train และเมื่อเทรนเสร็จสิ้นเราก็ลองให้โมเดลทำนายผลลัพธ์โดยใช้ข้อมูลชุดตรวจสอบ X_valid แปลงข้อมูลเรียบร้อยแล้ว สุดท้ายคือการประเมินผลเบื้องต้นโดยการนำเฉลยสำหรับชุดตรวจสอบมาเปรียบเทียบกับค่าที่โมเดลทำนายออกมา (ผลเฉลยที่เราแบ่งไว้ก่อนหน้านี้ เทียบกับ ค่าทำนายที่ได้จากการใช้ข้อมูลชุดตรวจสอบ) ซึ่งผลลัพธ์เป็นดังภาพ
3. บางครั้งโมเดลอาจจะยังมีความแม่นยำไม่เท่ากับที่เราต้องการ วิธีหนึ่งที่จะช่วยเพิ่มความแม่นยำได้คือการ Fine Tuning โดยในที่นี้สาธิตวิธีการเบื้องต้นว่าเราจะหาพารามิเตอร์ที่เหมาะสมของโมเดล Random Forest โดยใช้ Grid Search CV หลัก ๆ คือประกาศชื่อพารามิเตอร์พร้อมค่าพารามิเตอร์ที่จะทดสอบ จากนั้นเรียกใช้ GridSearchCV(Model, Parameters Dictionary) และ fit ด้วยข้อมูลชุดเดียวกันกับที่เรา train โมเดลในขั้นตอนก่อนหน้า สุดท้ายเราจะได้พารามิเตอร์ที่เหมาะสมที่สุด (ทั้งนี้หากจะ Fine Tune จริง ๆ ควรศึกษารายละเอียดและการควบคุมพารามิเตอร์ให้ดี และแนะนำว่าไม่ควรลงแรงกับขั้นตอนนี้มากนักเพราะใช้เวลานานมาก ๆ อีกทั้งจะให้ผลลัพธ์ได้ดีขึ้นจริงหรือไม่นั้นก็บอกยาก ควรเน้นที่การจัดการข้อมูลและทำ Feature Engineering ให้ดี ๆ มากกว่า)
ส่งคำตอบให้ Kaggle
เทรนโมเดลซ้ำอีกครั้งด้วยข้อมูลในชุด train ทั้งหมด และทำการทำนายผลลัพธ์โดยใช้ชุดข้อมูล test ที่ Kaggle เตรียมไว้ให้ จากนั้นเก็บค่าทำนายลงใน DataFrame ตามรูปแบบที่ Kaggle ต้องการ และเซฟเป็นไฟล์ CSV นำส่งให้กับ Kaggle
เมื่อส่งเสร็จสิ้น Kaggle จะทำการตรวจผลลัพธ์ของเราและให้คะแนน จากนั้นเราก็จะถูกจัดอันดับบน Leaderboard เป็นอันเสร็จพิธี (ตอนนี้คะแนนติดอยู่แค่ Top 50% ถ้าจะให้อยู่ 50 อันดับแรกนั้นโมเดลต้องทำคะแนนได้ประมาณ 0.8145 ขึ้นไป)
สุดท้ายนี้เป็นตารางเปรียบเทียบประสิทธิภาพโมเดลแบบต่าง ๆ ที่ลองใช้