บทความนี้เป็นบทความที่ 3 ในบทความชุด
“สอนวิเคราะห์ข้อมูล [ฉบับจับมือทำ] ตั้งแต่เบื้องต้น ถึงการสร้างโมเดล Machine Learning ผ่านข้อมูล การ ซื้อ-ขาย ผลไม้เพื่อสุขภาพยอดฮิตอย่าง Avocado”
ก่อนที่จะเริมบทความความนี้ ขออนุญาติรวบรวมลิงก์บทความทั้งชุดนี้ให้ก่อนนะคะ เผื่อใครยังไม่ได้อ่าน 2 บทความแรก จะได้ไปติดตามได้ เนื่องจากบทความทั้ง 3 เป็นบทความที่ต่อเนื่องกัน จึงแนะนำให้อ่านเรียงตามลำดับ ดังนี้

1. สอนวิเคราะห์ข้อมูล [ฉบับจับมือทำ] ตั้งแต่เบื้องต้น ถึงการสร้างโมเดล Machine Learning ผ่านข้อมูล การ ซื้อ-ขาย ผลไม้เพื่อสุขภาพยอดฮิตอย่าง Avocado {Part 1: Descriptive Analysis}
2. สอนวิเคราะห์ข้อมูล [ฉบับจับมือทำ] ตั้งแต่เบื้องต้น ถึงการสร้างโมเดล Machine Learning ผ่านข้อมูล การ ซื้อ-ขาย ผลไม้เพื่อสุขภาพยอดฮิตอย่าง Avocado {Part 2: EDA}
3. สอนวิเคราะห์ข้อมูล [ฉบับจับมือทำ] ตั้งแต่เบื้องต้น ถึงการสร้างโมเดล Machine Learning ผ่านข้อมูล การ ซื้อ-ขาย ผลไม้เพื่อสุขภาพยอดฮิตอย่าง Avocado {Part 3: Machine Learning}
มาเริ่มบทความที่ 3 บทความสุดท้ายของเรากันเลยค่ะ

บทความที่ 3 นี้ เราจะพูดถึงขั้นตอนสุดท้ายของการวิเคราะห์ข้อมูลของเรากันแล้ว นั่นก็คือ การประยุกต์ใช้งาน Machine Learning ซึ่งเป็นหนึ่งในวิธีที่นิยมกันอย่างแพร่หลายในการพัฒนาระบบ AI ต่างๆ ซึ่ง เราก็จะพูดถึงเจ้า Machine Learning ทั้ง 2 ประเภทหลัก คือ Classification (การทำนายกลุ่มของข้อมูล) และ Regression (การทำนายค่าของข้อมูล) เลยค่ะ เพื่อให้ผู้อ่านได้เห็นภาพได้ง่ายขึ้นจากการเปรียบเทียบข้อแตกต่างของทั้ง 2 วิธีนี้
นอกจากบทความแล้ว จะมีการแจก Source Code และ Dataset ให้ไปลองทำกันแบบจุกๆ สนุก แบบมีสาระกันไปพร้อมๆกันค่ะ
มาเริ่มกันที่ import ไลบารี่ที่จำเป็นกันก่อน
# Machine Learning
from sklearn.model_selection import train_test_split
#Classification
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import LinearSVC
from sklearn.tree import DecisionTreeClassifier
# Regression
from sklearn.neighbors import KNeighborsRegressor
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lasso
from sklearn.linear_model import ElasticNet
from sklearn.tree import DecisionTreeRegressor, plot_tree
np.warnings.filterwarnings('ignore')
เรียกไลบารี่เรียบร้อยแล้ว ก็มาเริ่มทำงานกันได้เลย
Classification
การทำ Classification หรือ การทำนายกลุ่มของข้อมูลที่เราจะยกตัวอย่างมาให้ดูกันในวันนี้จะเป็นทำนายกลุ่มของชนิดอะโวคาโด้ (Type) ซึ่ง ในที่นี้มีอยู่ด้วยกัน 2 กลุ่ม คือ Conventional (อะโวคาโด้ทั่วไป) และ Organic (อะโวคาโด้ออแกนิกส์)

ก่อนอื่นมาเลือกฟีเจอร์ (Features) หรือ ข้อมูลที่ต้องการใช้ในการทำนายประเภทของอะโวคาโด้กันก่อนค่ะ ในที่นี้เนื่องจากเรามีคอลัมน์ไม่ได้มากจนเกินไป เราจะขอเลือกคอลัมน์คร่าวๆ กันแบบง่ายก่อนนะคะ
(โดยปกติแล้วการเลือก Features เป็นอีกขั้นตอนหนึ่งที่ยาก และใช้เวลาในการวิเคราะห์มาก เนื่องจากข้อมูลในปัจจุบันมักจะประกอบไปด้วยรายละเอียด (คอลัมน์) ที่เกี่ยวข้องมาก การคัดกรองว่าคอลัมน์ใดควรนำมาใช้งานจึงเป็นงานที่ใช้เวลามาก)
มาดูคอลัมน์ที่เรามีทั้งหมดกันก่อนค่ะ
avo.columns

1. จัดการฟีเจอร์
ก่อนอื่นเลย เราจะตัดฟีเจอร์ (คอลัมน์) ที่ไม่เกี่ยวข้อง หรือ ซ้ำซ้อนตามที่เราได้วิเคราะห์จากบทความก่อนหน้าออกก่อนค่ะ ซึ่งในที่นี้คือ Date (เราได้ทำการสร้างคอลัมน์ day, month, year ขึ้นทดแทนแล้ว), Total Bags_scaled และ AveragePrice_scaled (เป็นคอลัมน์ที่คำนวณเพื่อการวิเคราะห์ความสัมพันธ์ของข้อมูลจากบทความก่อนหน้า), year_month (เป็นคอลัมน์ที่สร้างขึ้นเพื่อประโยชน์ในการแสดงผลเท่านั้น), region (เป็น Text ที่ไม่สามารถนำมาคำนวณได้สำหรับการใช้งาน Machine Learning ถ้าต้องการใช้งานต้องเปลี่ยนเป็นตัวเลขก่อน) ดังนี้
avo_features = avo.drop(['Date', 'region', 'Total Bags_scaled', 'AveragePrice_scaled', 'year_month'], axis=1)
2. เลือกฟีเจอร์ที่ต้องการใช้งาน
เมื่อตัดคอลัมน์ที่ไม่ใช้งานแน่ๆ แล้วออก จากนั้นก็จะต้องมาจัดการกับข้อมูลกันค่ะ ในที่นี้ข้อมูลจะมีอยู่ 2 ส่วนด้วยกันคือ ข้อมูลที่จะใช้ในการทำนาย (features) และ ข้อมูลที่ต้องการทำนาย (target)
target = avo_features['type']
features = avo_features.drop(['type'], axis=1)
ในที่นี้ข้อมูลที่จะนำมาใช้ในการทำนายก็คือ ฟีเจอร์ หรือ คอลัมน์ทั้งหมดหลังจากตัดคอลัมน์ที่กล่าวข้างบนออกไป ดังนั้น จะมีทั้งหมด 13 คอลัมน์ ส่วนสิ่งที่ต้องการทำนาย หรือ target มีเพียงคอลัมน์เดียวเท่านั้น คือ type หรือ ชนิดของอะโวคาโด้ นั่นเอง
3. แบ่งข้อมูล Train - Test
ในการสร้างโมเดล Machine Learning โดยส่วนใหญ่จะทำการแบ่งข้อมูลทั้งหมดที่มี ออกเป็น 2-3 ส่วน ในที่นี้จะแบ่งออกเป็น 2 ส่วน คือ Train set (ชุดข้อมูลสอน) และ Test set (ชุดข้อมูลทดสอบ) ดังนี้
ข้อมูลชุดแรกคือ Training set จะใช้สำหรับการสร้างโมเดล Machine Learning ส่วน ข้อมูล Test set จะใช้สำหรับทดสอบว่าโมเดลที่สร้างขึ้น มีประสิทธิภาพเพียงใด นั่นเอง ซึ่งการแบ่งข้อมูลแบบนี้เป็นการแบ่งข้อมูลอย่างง่ายๆ ซึ่งถ้าต้องการทดสอบการทำงานที่ซับซ้อนขึ้น ข้อมูลก็จะสามารถถูกแบ่งออกมากกว่า 2 ชุดได้
X_train, X_test, y_train, y_test = train_test_split(features.values, target.values, random_state=0)
ในที่นี้จะใช้คำสั่ง train_test_split ซึ่งจะทำการแบ่งข้อมูล Train set และ Test set ออกโดยอัตโนมัติ โดยถ้าไม่มีการกำหนดขนาด คำสั่งนี้จะกำหนดขนาดของ Train set และ Test set เป็น 75% และ 25% ตามลำดับ

จากรูป จะเห็นว่า Train set และ Test set ถูกแบ่งออกเป็น 75% และ 25% ตามลำดับ (ในทางปฏิบัติข้อมูลจะถูกนำมา shuffle หรือ สับกันไปมาแบบสุ่มก่อน ทำให้ข้อมูลไม่ได้เรียงลำดับแบบเดียวกันกับตอนเริ่มต้น) ข้อมูลทั้งในชุดของ Train และ Test จะแบ่งออกเป็น 2 ส่วนคือ features (แทนด้วย X) และ target (แทนด้วย y) ซึ่งทำให้ X ของเราคือ ค่าข้อมูลที่เราจะใช้ในการทำนาย ส่วน y คือ ค่าข้อมูลที่เราต้องการใช้ X ทำนาย นั่นเอง
มาลองดูข้อมูลกัน
print(X_train[:3])
print(y_train[:3])
print(X_train.shape)
print(y_train.shape)

ในที่นี้เราเรียกดูข้อมูล X_train 3 แถวแรก จะเห็นว่า ข้อมูลแต่ละแถวจะเป็น array ของตัวเลข 13 ตัว ซึ่งก็คือ ค่าของทั้ง 13 features ของเรานั่นเอง (แสดงด้วย กรอบสีฟ้า)
สำหรับข้อมูลในกรอบสีแดง จะเป็น type ของอะโวคาโด้แต่ละแถว ในที่นี้ แสดงว่าข้อมูล 3 แถวแรกที่ใช้ในการสอนของเราก็คือ อะโวคาโดประเภททั่วไป 2 แถว และ ประเภทออแกนิกส์ 1 แถว นั่นเอง
ผลลัพธ์ 2 แถวสุดท้าย คือ มิติ (ขนาด) ของข้อมูล จะเห็นว่า ข้อมูลสอนมีจำนวน 13686 แถว ฟีเจอร์ที่ใช้สำหรับทำนาย มี 13 ฟีเจอร์ และ target มี 1 คอลัมน์ หรือ (13686 แถวๆ ละ 1 คอลัมน์)
เราสามารถเรียกดูตัวอย่างข้อมูล X_test ได้เช่นกัน โดยใช้คำสั่งเดียวกันด้านบน เพียงแค่เปลี่ยนตัวชื่อตัวแปรจาก X_train เป็น X_test เท่านั้น เมื่อเรียกดูเราจะเห็นว่าโครงสร้างของข้อมูลทั้งสองตัวนี้มีลักษณะเดียวกัน
หลังจากแบ่งข้อมูลออกเรียบร้อยแล้ว มาเริ่มใช้งานโมเดล Machine Learning กันดีกว่าค่ะ
Machine Learning
Decision Tree
ต้นไม้ตัดสินใจ หรือ Decision Tree เป็นอัลกอริทึ่มหนึ่งของ Machine Learning ที่สามารถนำมาใช้ได้กับปัญหาทั้งประเภท Classification และ Regression ในการทำงานจะอาศัยหลักการของการแตกกิ่งก้านของต้นไม้ ซึ่งสามารถเข้าใจได้ง่าย จึงได้รับความนิยมในการใช้งานพอสมควร โดยปกติแล้วจะนิยมใช้งานในรูปแบบของ ป่าตัดสินใจ หรือ Random Forest ที่ใช้ต้นไม้จำนวนมากในการทำงานร่วมกัน

ผู้อ่านที่สนใจ สามารถอ่านรายละเอียดการทำงานของ Decision Tree ได้ที่บทความด้านล่างค่ะ
จากต้นไม้แห่งการตัดสินใจ (Decision tree) สู่ ต้นไม้เพื่อการลงทุน (Investing tree)
Step 1. สร้างโมเดล Decision Tree
tree = DecisionTreeClassifier(max_depth=7, random_state=0)
การสร้าง Decision Tree สามารถกำหนดความลึกของต้นไม้ได้ โดยกำหนดตัวแปร max_depth ซึ่งถ้ากำหนดความลึกมาก เราจะได้ค่าความถูกต้องในการสอน (Train)โมเดลมากขึ้น แต่ก็มีความเสี่ยงที่จะทำให้ความถูกต้องจากการทดสอบ (Test) ลดลงด้วยเช่นกัน เนื่องมาจากปัญหาของการ Overfit
ในทางกลับกัน ถ้าเราสร้างต้นไม้ที่มีขนาดเล็ก หรือ ความลึกน้อยเกินไป ก็อาจจะทำให้โมเดลเกิดการ Underfit ได้ ซึ่งจะทำให้ความถูกต้องของทั้งการ Train และ Test ลดลงนั่นเอง
ผู้อ่านท่านใด สนใจศึกษารายละเอียดเพิ่มเติมเรื่อง overfit และ underfit สามารถอ่านได้ที่บทความด้านล่าง
Overfitting vs. Underfitting อธิบายด้วยตัวอย่าง ฉบับเข้าใจง่ายที่สุดในโลก
2. สอนโมเดล
โมเดลในขั้นตอนที่แล้วถูกสร้างขึ้นโดยใช้ค่ามาตรฐาน (Default) ซึ่งยังไม่ได้มีความสัมพันธ์ใดๆ กับข้อมูลของเรา ดังนั้น ในขั้นตอนนี้เราจะมาปรับโมเดลให้มีความเฉพาะเจาะจงกับข้อมูลของเรา ด้วยการสอน (Train) โมเดลกันค่ะ
ในการสอนโมเดล แน่นอนว่าเราก็จะใช้เฉพาะส่วนของข้อมูลสอน (Train set) ที่เราแบ่งไว้นั้นเองค่ะ
tree.fit(X_train, y_train)
การสอนโมเดล เราจะใช้ข้อมูล X_train และ y_train คู่กัน เนื่องจากต้องการโมเดลเรียนรู้ในการ map ข้อมูลสองชุดนี้นั่นเองค่ะ
print("training set score : {:.2f}".format(tree.score(X_train, y_train)))
จากนั้น เราสามารถเรียกดูประสิทธิภาพของการสอนโมเดลได้ โดยใช้คำสั่ง tree.score() จะได้ค่าความถูกต้องหรือ ประสิทธิภาพ ที่บ่งบอกว่า โมเดลสามารถเรียนรู้ข้อมูลสอนชุดนี้ได้ดีแค่ไหนออกมา

จะเห็นว่าประสิทธิภาพในการสอนโมเดลครั้งนี้มีค่าความถูกต้องที่สูง คือ 98% ซึ่งหมายความว่า โมเดล Decision Tree ดี แล้ว จะสามารถสรุปได้รึยังว่าโมเดลทำงานได้ดี?
แน่นอนว่า ยังไม่สามารถที่จะสรุปแบบนั้นได้ อย่างที่เคยกล่าวไปแล้วว่าในการเทรนโมเดลนั้น ถ้าเราจะทำให้ได้ความถูกต้องสูงๆ เลยก็สามารถทำได้ โดยการปล่อยให้โมเดลสร้างต้นไม้ที่มีขนาดใหญ่มากๆ นั่นเองค่ะ แต่การที่เราสร้างต้นไม้ที่ใหญ่มากๆ ก็มีโอกาสสูงที่จะทำให้เกิด Overfitting ได้ ดังนั้น การจะสรุปได้ว่าโมเดลของเราทำงานได้ดี จะต้องดูประสิทธิภาพของการสอน (Train) และทดสอบ (Test) ควบคู่กันไป ซึ่ง ณ ตอนนี้เราเพิ่งดูในส่วนของการสอนกันไป ต่อไปลองไปดูในส่วนของการทดสอบกันบ้างค่ะ
3. ทดสอบโมเดล
หลังจากที่โมเดลได้รับการสอนเรียบร้อยแล้ว เราก็จะมาทำการทดสอบกันค่าว่าโมเดลนี้มีประสิทธิภาพแค่ไหน โดยลองให้โมเดลทำงานกับข้อมูลที่ไม่เคยเห็นมาก่อน ซึ่งในที่นี้คือ ข้อมูลทดสอบ (Test set) ที่เราแบ่งไว้นั้นเอง
pred = tree.predict(X_test)
คำสั่งในใช้ในการทดสอบโมเดล คือ tree.predict() หรือพูดง่ายๆ ก็คือ การทดลองให้โมเดลทำนายข้อมูลชุด X_test ดูนั่นเอง ว่าจะทำนาย X_test แต่ละแถวว่าเป็นอะโวคาโด้ชนิดไหนบ้าง จากนั้นเก็บผลลัพธ์หรือ ชนิดอะโวคาโด้ที่ทำนายได้ ไว้ในตัวแปรที่ชื่อ pred
ลองมาดูข้อมูลในตัวแปร pred กันหน่อยค่ะ
print(pred)
print(pred.shape)

จะเห็นว่าตัวแปร pred เก็บผลลัพธ์จากการทำนายไว้ เช่น ทำนายข้อมูล X_test แถวที่ 1-3 ว่าเป็น organic, conventional และ conventional ตามลำดับ จำนวนผลลัพธ์ในการทำนายก็จะเท่ากับ 4563 ซึ่งเท่ากับจำนวนแถวของข้อมูลที่ใช้ในการทดสอบ หรือ X_test นั่นเอง

4. วัดประสิทธิภาพของโมเดล
เมื่อได้ผลลัพธ์จากการทำนายมาแล้ว มาลองวัดประสิทธิภาพของโมเดลนี้กัน โดยใช้คำสั่งที่ง่ายที่สุดเลยคือ tree.score ()
print("test set score: {:.2f}".format(tree.score(X_test, y_test)))

จะเห็นว่าค่าประสิทธิภาพที่วัดได้จากข้อมูลทดสอบนั้นก็มีค่าสูงเช่นกัน คือ 97% หมายความว่าโมเดลของเราสามารถทำงานได้ดีที่เดียวค่ะ
แล้วจะสามารถสรุปได้รึยังล่ะ ว่าโมเดลนี้ดีเลิศ???
จริงๆ ถ้าอยากจะสรุปก็ได้ระดับนึงนะคะ แต่จริงๆ แล้ว ก็ยังไม่ควรด่วนสรุปอีกเช่นกัน ถึงแม้อาจจะทำได้กับข้อมูลชุด แต่ข้อมูลไม่ได้มีความซับซ้อนเท่ากันทุกชุด ดังนั้น ฝึกวิธีคิดไว้ให้ชิด ว่าอย่าเชื่ออะไรง่าย! ให้ตั้งข้อสังเกตุไว้เสมอว่าการทดลองแบบนี้ เราเพียงแค่ทดลอง train – test บนข้อมูลบางส่วนเท่านั้น มีความเป็นไปได้ ว่าเราอาจจะเป็นคนดวงเฮง เลือกแบ่งข้อมูลได้ถูกต้องพอดี ทำให้ได้ผลลัพธ์ที่ดี แต่ถ้าอยากจะให้แน่ใจ อย่างน้อยๆ เราก็ควรจะทำ Cross validation แบ่งข้อมูลสอน และ ทดสอบให้หลากหลาย หรือ ใช้ชุดข้อมูลที่หลากหลายด้วยค่ะ

จบกันไปแล้วนะคะ กับ 4 ขั้นตอนง่ายๆ แบบพื้นฐานเลยของการใช้ Machine Learning ในการสร้างโมเดลเพื่อการทำนายกลุ่มของอะโวคาโด้ ก่อนจะไปทำงานกันอัลกอริทึ่มอื่นๆ ขอแถมการวิเคราะห์ Feature Importance หรือ ความสำคัญของฟีเจอร์กันซักหน่อยแล้วกันค่ะ
print("feature importance:")
feature_importance = pd.DataFrame(features.keys(), tree.feature_importances_)
feature_importance = feature_importance.sort_index(ascending=False)
print(feature_importance)
จากคำสั่ง tree.feature_importances_ เราจะได้ลิสของฟีเจอร์ และความสำคัญ หรือ พูดง่ายๆ ก็คือความมีส่วนร่วมในการทำนายครั้งนี้ออกมา จากนั้นเราจะจับฟีเจอร์เหล่านี้มาเรียงตามความสำคัญจากมากไปน้อยกัน จะได้ผลลัพธ์ ดังนี้

จกรูปจะเห็นว่า เมื่อเราจัดเรียงเรียบร้อยแล้ว ฟีเจอร์ที่มีความสำคัญหรือว่าส่งผลต่อการทำนายของโมเดลมากที่สุด ก็คือ Total Volume หรือจำนวนรวมของอะโวคาโด้ ซึ่งมีความสำคัญในการทำนายมากกว่าค่า AveragePrice หรือราคาเฉลี่ยเสียอีก
ถ้านึกกันง่ายๆ เราก็จะนึกว่า ถ้าจะทำนายประเภทของอะโวคาโด้ ตัวแปรที่สำคัญควรจะเป็น AveragePrice ถูกมั้ยคะ เพราะเราทราบว่าราคาของอะโวคาโด้สองชนิดนี้แตกต่างกันมาก จากการวิเคราะห์ในบทความในตอนที่ผ่านมา แต่จริงๆ แล้ว Total Volume กลับมาความสำคัญมากกว่า แสดงว่า ปริมาณการการซื้อขายต่างหาก ที่มีผลต่อการแยกประเภทของอะโวคาโด้ เช่น อาจจะเป็นไปได้ว่าผู้ซื้อที่ซื้ออะโวคาโด้ออแกนิกส์มีกำลังซื้อมากกว่า ถึงแม้ราคาจะแพง หรือ อะโวคาโด้ออแกนิกส์มีราคาสูง ปริมาณจำนวนการซื้อขายจึงลดต่ำลง ซึ่งในความเป็นจริงแล้ว จะเป็นแบบไหน ก็ต้องอาศัยการวิเคราะห์ความสัมพันธ์เพิ่มเติมนั่นเองค่ะ
แต่ในที่นี้ เราจะขออนุญาติให้ผู้อ่านไปวิเคราะห์เพิ่มกันเองนะคะ เพราะเราจะไปกันต่อที่อัลกอริทึ่มในการทำนายอื่นแล้วล่ะค่ะ มาลองดูกันแบบไวๆ นะคะว่า ถ้าเราทดลองเปลี่ยนอัลกอริทึ่มการทำนายเป็น kNN (K-Nearest Neighbors), SVM (Support Vector Machine) ผลลัพธ์จะเป็นอย่างไรกันบ้าง
kNN (K-Nearest Neighbors)
knn = KNeighborsClassifier(n_neighbors=12)
knn.fit(X_train, y_train)
print("training set score : {:.2f}".format(knn.score(X_train, y_train)))
print("test set score: {:.2f}".format(knn.score(X_test, y_test)))

SVM (Support Vector Machine)
svc = LinearSVC(C=211)
svc.fit(X_train, y_train)
print("training set score : {:.2f}".format(svc.score(X_train, y_train)))
print("test set score: {:.2f}".format(svc.score(X_test, y_test)))

จะเห็นว่าการใช้อัลกอริทึ่มอื่น เช่น k-NN หรือ SVM ก็ไม่ได้มีความยากลำบาก เนื่องจากเราได้ทำการจัดเตรียมข้อมูลที่จะใช้งานไว้เรียบร้อยแล้ว สิ่งที่ควรจะให้ความสนใจต่อไปก็คือ การปรับพารามิเตอร์ของแต่ละโมเดลให้เหมาะสม เช่น พารามิเตอร์ n_neighbors ของ k-NN และ c ของ SVM เป็นต้น ซึ่งรายละเอียดก็จะค่อนข้างเยอะ ถ้ามีคนสนใจ ว่างๆ จะเขียนเพิ่มให้นะคะ วันนี้เราไปต่อกันก่อนค่ะ
ผลที่ได้จาก k-NN ค่อนข้างใกล้เคียงกับ Decision Tree ของเรา คือ 97% ในขณะที่ SVM ให้ผลค่อนข้างที่จะน้อยกว่าอย่างเห็นได้ชัด คือ 83% และ 84% สำหรับการสอนและทดสอบ ตามลำดับ แต่อย่างที่กล่าวไปแล้วข้างต้นนะคะ ว่าเราไม่ควรจะด่วนสรุปทุกอัลกอริทึ่ม จะกว่าจะได้ทำการทดลองข้อมูลที่หลากหลายกว่านี้ค่ะ เช่น การแบ่งข้อมูลออกเป็นส่วนๆ แล้วทำการทดสอบ Cross Validation หรือ การใช้ข้อมูลอื่นๆ หรือ ใช้ Bias เข้ามาทดสอบ เป็นต้นค่ะ
5. การใช้โมเดลทำนายค่าที่ได้รับมาในอนาคต
เมื่อเราได้ทำการสร้างโมเดล และได้ทดสอบกันจนพอใจแล้ว เราก็จะ save โมเดลนั้นเก็บไว้ เมื่อถึงเวลาต้องใช้งาน ก็จะสามารถเรียกมาใช้งาน หรือ ใช้ในการทำนายข้อมูลที่เข้ามาใหม่ในอนาคตได้อย่างรวดเร็ว
ตัวอย่างเช่น
เรามีข้อมูลเข้ามาใหม่ และต้องการทำนายว่าข้อมูลการซื้อขายที่เข้ามานั้น เป็นการซื้อขายอะโวคาโด้ประเภทใด ระหว่างปกติกับออแกนิกส์ สิ่งที่เราต้องการก็คือ ข้อมูล ซึ่งในที่นี้เราต้องการข้อมูล ดังนี้

Trying new avos
new_avo = [1.58, 14000, 1176, 2321, 4778, 7438, 7000, 438, 0, 2018, 3, 5, 3]
ในที่นี้เราต้องการข้อมูลรายละเอียด 13 อย่าง ตามที่เราได้กำหนดฟีเจอร์ที่เราจะนำมาใช้ในการทำนายค่า ไว้ในขั้นตอนที่ 2 นั่นเอง จากนั้นเก็บค่าเหล่านั้นไว้ในตัวแปรที่ชื่อว่า new_avo
new_avo = np.array(new_avo)
print(new_avo.shape) # original shape
new_avo = new_avo.reshape(1,-1)
print(new_avo.shape) # new shape
เนื่องจากข้อมูลที่จะนำไปทำนายได้จะต้องมีขนาด หรือ มิติ (Dimension) ที่เหมาะสม ในที่นี้คือ (1,13) หรือ 1 แถว 13 หลักเสียก่อน จึงทำการใช้คำสั่ง reshape() เพื่อเปลี่ยน มิติของข้อมูลก่อน
print(tree.predict(new_avo))
print(knn.predict(new_avo))
print(svc.predict(new_avo))
เรามาลองใช้ทั้ง 3 โมเดลที่เราสร้างขึ้น คือ Decision Tree (tree), k-Nearest Neighbors (knn) และ SVM (svc) จะเห็นว่าผลลัพธ์ที่ได้เหมือนกัน ก็คือ ทั้ง 3 โมเดล รวมใจกันบอกว่า นี่ๆ ข้อมูลใหม่ที่เข้ามา พวกเราทำนายว่าเป็น อะโวคาโด้ ออแกนิกส์ นะ นั่นเองค่ะ
จบกันไปแล้วนะคะ กับการใช้ Machine Learning ในการทำนายในรูปแบบของ Classification ต่อไปเราจะมาดูว่า ถ้าจะใช้ในรูปแบบของ Regression ล่ะ จะต้องทำอย่างไรกันบ้าง
Regression
การทำ Regression หรือ การทำนายค่าของข้อมูลที่เราจะยกตัวอย่างมาให้ดูกันในวันนี้จะเป็นทำนายค่า AveragePrice หรือ ราคาเฉลี่ยของอะโวคาโด้กัน
ในส่วนของฟีเจอร์ที่จะใช้ในการทำงาน เราจะขอใช้ฟีเจอร์คล้ายๆ กับตอนที่ทำ Classification จะแตกต่างก็เพียงแค่เราต้องนำฟีเจอร์ Type หรือ ชนิดของอะโวคาโด้ออกก่อนค่ะ เนื่องจากคอลัมน์นี้มีค่าเป็น Categorical value หรือ ไม่ใช่ตัวเลข จึงไม่สามารถนำมาคำนวณได้นั่นเองค่ะ (ถ้าต้องการใช้งาน ต้องมีการแปลงค่า Categorical value เป็น Numerical value หรือ ตัวเลขก่อน ซึ่งเดี๋ยวจะทำให้ดูกันในต่อไปท้ายบทความ)
Data preparation
avo_features = avo.drop(['Date', 'region', 'Total Bags_scaled', 'AveragePrice_scaled', 'year_month', 'type'], axis=1)
target = avo_features['AveragePrice']
features = avo_features.drop(['AveragePrice'], axis=1)
จะเห็นความแตกต่างจากข้อมูลที่ใช้ตอนทำ Classification ยังมีอีกส่วนคือ ส่วนของ Target หรือ สิ่งที่เราต้องการทำนาย จะเปลี่ยนจาก Type (ชนิดของอะโวคาโด้) เป็น AveragePrice (ราคาเฉลี่ยของอะโวคาโด้) เนื่องจากเรากำลังจะทำ Regression กันนั่นเอง
ต่อมาก็จัดการแบ่งข้อมูล Train-Test ออกจากกัน โดยใช้คำสั่งเดิมได้เลยค่ะ
X_train, X_test, y_train, y_test = train_test_split(features.values, target.values, random_state=0)
เมื่อแบ่งข้อมูลเรียบร้อยแล้ว ขอรวบรัดแบบเร็วๆ เนื่องจากเป็นขั้นตอนที่ไม่ได้แตกต่างจากตอนทำ Classification มากนะคะ คือ เราต้องทำการสร้างโมเดล สอน ทดสอบ และ วัดประสิทธิภาพของโมเดล โมเดลแรกที่เราจะเริ่มทำ Regression ก็คือ Decision Tree
Decision Tree
อย่างที่กล่าวไปแล้วตอนทำ Classification ว่า โมเดล Decision Tree หรือ ต้นไม้ตัดสินใจนั้น สามารถใช้กับปัญหาแบบ Regression ได้ด้วย มาลองดูกันเลยค่ะ
TreeRegr = DecisionTreeRegressor(max_depth = 14, random_state = 42)
TreeRegr.fit(X_train, y_train)
print("training set score : {:.2f}".format(TreeRegr.score(X_train, y_train)))
print("test set score: {:.2f}".format(TreeRegr.score(X_test, y_test)))

ผลลัพธ์ที่ได้คือ Decision Tree มีประสิทธิภาพในการสอน และ ทดสอบอยู่ที่ประมาณ 94% และ 64% ตามลำดับ ซึ่งจะเห็นชัดเจนเลยว่า เมื่อเราใช้ต้นไม้ต้นนี้ในการทำนายค่า มันเกิดการ Overfitting ขึ้น สังเกตุได้จากการที่ค่าความถูกต้องของการสอนมีค่ามากกว่าการทดสอบอยู่มาก
ถ้าเรากลับไปดูที่โครงสร้างของต้นไม้ที่เรากำหนด ก็จะเห็นได้ว่า ผลแบบนี้ ไม่น่าแปลกใจเลย เพราะเรากำหนดความลึกของต้นไม้ หรือ max_depth ไว้ตั้ง 14 ชั้น ซึ่งมากกว่าตอนทำ Classification หนึ่งเท่า จึงทำให้ต้นไม้ของเรานั้นพยายามจดจำ แยกแยะรูปแบบของข้อมูลสอนมากเกินไป จนไม่สามารถปรับตัวให้ทำงานกับข้อมูลที่โมเดลไม่เคยเห็น หรือ ข้อมูลสอนได้นั้นเอง
แน่นอนว่าถ้าเราอยากจะลดปัญหานี้ ก็ต้องไปลองปรับค่า max_depth ให้ต่ำลงค่ะ การปรับนี้ควรจะช่วยลดการ Overfitting ได้ แต่ก็แน่นอน เราก็อาจจะตกใจนิดหน่อย เมื่อเห็นว่าค่าความถูกต้องของข้อมูลทดสอบลดลง แต่ก็ไม่เห็นไรค่ะ ความสำคัญของการสร้างโมเดล คือ การทำงานได้กับข้อมูลในอนาคตได้ดี ไม่ใช่การจดจำข้อมูลสอนซะหน่อย
Linear Regression
ต่อไปมาดูอัลกอริทึ่มตัวเล็ก ที่มักจะเป็นตัวเริ่มที่เราได้เรียนกันในห้องเรียน Linear Regression ถึงแม้จะเป็นอัลกอริทึ่มที่ดูเหมือนจะง่าย ไม่ซับซ้อน เรียนกันเป็นตัวแรกๆ แต่ก็ไม่ได้หมายความว่าจะไม่ดีนะคะ เท่าที่ผู้เขียนเคยเจอ ก็มีหลายๆ ปัญหาที่ไม่ต้องอาศัยอัลกอริทึ่มยากๆ ให้เสียเวลา ใช้แค่ Linear Regression ก็ทำงานได้ดีพอแล้ว เพียงแค่เรารู้จักปรับแต่โมเดลแค่นั้นเองค่ะ
มาลองกันเลยค่ะ ใช้โมเดลพื้นฐานที่ไม่มีการปรับแต่งอะไรเลย หรือ Default กันก่อนก็แล้วกัน
LinearRegr = LinearRegression().fit(X_train, y_train)
print('Linear Regression')
print("training set score : {:.2f}".format(LinearRegr.score(X_train, y_train)))
print("test set score: {:.2f}".format(LinearRegr.score(X_test, y_test)))

จะเห็นว่าผลที่ได้ ดูไม่จืดเลย ถูกต้องละค่ะ อย่างที่บอกไปแล้วว่าโมเดลนี้เป็นโมเดลที่ไม่ซับซ้อน การทำงานก็จะพยายามหาเส้นตรงที่สามารถอธิบาย หรือ ฟิต กับข้อมูลได้ออกมา ซึ่งปกติแล้วข้อมูลที่เราทำงานด้วย ก็มักจะไม่สามารถอธิบายด้วยเส้นตรงง่ายๆ ได้ ดังนั้น ขอให้จำไว้เสมอว่า โมเดลนี้ ต้องการการปรับแต่ง! มาดูกันเลยค่ะ ว่าเราลองปรับอะไรได้บ้าง
Optimization
1 Ridge, Lasso และ ElasticNet Regularization
ทั้ง 3 วิธีนี้เป็นวิธีการปรับแต่งค่า Cost function ของ Linear Regression โดยการเพิ่มนิพจน์บางอย่างเข้าไป ถ้าผู้อ่านท่านใด ได้ลงทะเบียนเรียนคอร์ส “AI for Investment” เราก็จะอธิบายการปรับค่าในส่วนนี้ไว้อย่างละเอียดแล้ว ขออนุญาติไม่ลงรายละเอียดในบทความนะคะ เกรงว่าจะยาวเกินไป มาดูการใช้งาน และ ผลลัพธ์กันเลยดีกว่าค่ะ
# ------ Ridge
ridge = Ridge().fit(X_train, y_train)
print("training set score : {:.2f}".format(ridge.score(X_train, y_train)))
print("test set score: {:.2f}".format(ridge.score(X_test, y_test))
# ------ Lasso
lasso = Lasso().fit(X_train, y_train)
print("training set score: {:.2f}".format(lasso.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lasso.score(X_test, y_test)))
print("Number of features used:", np.sum(lasso.coef_ != 0))
# ------ Elastic Net
elastic = ElasticNet().fit(X_train, y_train)
print("training set score: {:.2f}".format(elastic .score(X_train, y_train)))
print("Test set score: {:.2f}".format(elastic .score(X_test, y_test)))

ผลลัพธ์ที่ได้ก็ยังไม่น่ารักอยู่ดี! ควรหยุดแล้วไปใช้โมเดลอื่นดีมั้ย … ใจเย็นๆ ก่อน จะทำงานกับ Machine Learning อย่าด่วนสรุปค่ะ ไม่ว่าจะนานแค่ไหน ผู้เขียนก็จะพูดคำนี้ค่ะ อย่าด่วนสรุป Machine Learning ไม่ใช่อัลกอริทึ่มที่จะหยิบจับมา แล้วตะบี้ตะบันใช้ จับข้อมูลยัดใส่ แล้วทำงานได้เลย …. ไม่อย่างนั้น ใครๆ ก็ใช้ได้สิคะ
สิ่งที่ต้องทำคือ ค่อยๆ ปรับค่ะ ด้านบนเราลองปรับโมเดลโดยใช้ Regularization กันไปแล้ว แต่แหม มันก็ยังไม่ดี เราจะทำยังไงได้อีกล่ะ
จริงๆ เรายังสามารถทำได้อีกเยอะเลยค่ะ เช่น การทำ Regularization เราสามารถทำการ optimize พารามิเตอร์แต่ละอัลกอริทึ่มได้อีก หรือ ก่อนปรับตัวโมเดลอาจจะวกกลับไปดูก่อนว่า แทนที่จะโทษที่ตัวโมเดลไม่ได้เรื่อง เราได้จัดการปรับข้อมูลก่อนนำมาสอนโมเดลได้ดีหรือยัง
ในที่นี้ขอเลือกวกกลับไปดูตัวข้อมูลตั้งต้นก่อนดีกว่าค่ะ เพราะเป็นขั้นตอนนี้ควรคอนเฟิร์มก่อนจริงๆ ก่อนที่จะทำอย่างอื่น

Feature Engineer
การทำ Feature Engineer หรือ การปรับแต่งฟีเจอร์ก็มีมากมายหลากหลายมากๆ ในที่นี้จะขอเลือกวิธีการ Log Transformation ก่อนค่ะ เพื่อปรับข้อมูลให้มีการแจกแจงใกล้เคียงกับข้อมูลปกติมากขึ้น เนื่องจากอัลกอริทึ่มของ Machine Learning จะทำงานได้ดีกว่ามากๆ เมื่อข้อมูลมีการแจกแจงที่เหมาะสม หรือการแจกแจงปกติ (จริงๆ แล้วก็ไม่ใช่แค่ ML นะคะ Statistic models ส่วนใหญ่ก็ทำงานได้ดีกับข้อมูลที่มีการแจงแจงปกติค่ะ) มาเริ่มปรับข้อมูลกันก่อนค่ะ
features.iloc[:,0:7] = np.log(features.iloc[:,0:7] + 1)
หลังจากทำ Log Transformation เรียบร้อยแล้ว ข้อมูลที่อยู่ในตัวแปร features จะมีค่าเปลี่ยนแปลงไป ดังนั้น จะต้องทำการแบ่ง ข้อมูลออกเป็น Train-Test ใหม่อีกครั้ง ก่อนทดลองรัน Linear Regression แต่ละโมเดลใหม่อีกครั้ง
# ------ Linear Regression (Default)
X_train, X_test, y_train, y_test = train_test_split(features.values, target.values, random_state=0)
LinearRegr = LinearRegression().fit(X_train, y_train)
print('Linear Regression')
print("training set score : {:.2f}".format(LinearRegr.score(X_train, y_train)))
print("test set score: {:.2f}".format(LinearRegr.score(X_test, y_test)))
# ------ Ridge
print(' \nRidge')
ridge = Ridge().fit(X_train, y_train)
print("training set score : {:.2f}".format(ridge.score(X_train, y_train)))
print("test set score: {:.2f}".format(ridge.score(X_test, y_test)))
# ------ Lasso
print(' \nLasso')
lasso = Lasso().fit(X_train, y_train)
print("training set score: {:.2f}".format(lasso.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lasso.score(X_test, y_test)))
print("Number of features used:", np.sum(lasso.coef_ != 0))
# ------ Elastic Net
print(' \nElasticNet')
elastic = ElasticNet().fit(X_train, y_train)
print("training set score: {:.2f}".format(elastic.score(X_train, y_train)))
print("Test set score: {:.2f}".format(elastic.score(X_test, y_test)))

ผลลัพธ์ดีขึ้นมาก แต่ยังไม่พอค่ะ ถ้าลองคิดกลับไปแล้ว จะเห็นว่า ตอนแรกเริ่มเลยของการทำ Regression เราได้มีการนำข้อมูลที่ไม่ใช่ตัวเลข เช่น Region และ Type ออกไปจากชุดข้อมูล แต่จริงๆ แล้วข้อมูลสองฟีเจอร์นี้อาจจะมีความสำคัญ สามารถช่วยเพิ่มประสิทธิภาพในการทำนายข้อมูลก็ได้ ดังนั้น เราจะนำข้อมูล 2 ตัวนี้กลับมาใช้กันค่ะ
Dummy Variables
ก่อนอื่นเราจะทำการเลือกฟีเจอร์ในการทำงานใหม่ และจะไม่ตัด Region กับ Type เหมือนที่ผ่านๆ มา
avo_features = avo.drop(['Date', 'Total Bags_scaled', 'AveragePrice_scaled', 'year_month'], axis=1)
avo_features = pd.get_dummies(avo_features)
target = avo_features['AveragePrice']
features = avo_features.drop(['AveragePrice'], axis=1)
เมื่อทำการเลือกฟีเจอร์ใหม่ โดยไม่มีการ drop (ลบทิ้ง) ฟีเจอร์ Region และ Type แล้ว เราก็จะทำการสร้าง Dummy Variable หรือ พูดง่ายๆ ก็คือ การเปลี่ยนข้อมูลในคอลัมน์ที่เป็นตัวหนังสือ เป็นตัวเลข นั่นเอง โดยใช้คำสั่ง get_dummies() จากนั้นทำการเตรียม target เหมือนกับที่ผ่านมา
features.iloc[:,0:7] = np.log(features.iloc[:,0:7] +1)
X_train, X_test, y_train, y_test = train_test_split(features.values, target.values, random_state=0)
จากนั้นก็นำข้อมูล Features ที่ถูกเลือก และ ผ่านการทำ Dummy variable แล้ว มาทำ Log Transformation และ แบ่ง Train-Test ใหม่
เมื่อทำขั้นตอนด้านบนครบแล้ว เราก็พร้อมแล้วค่ะ ที่จะมาทดสอบประสิทธิภาพของโมเดลกันอีกครั้ง
# ------ Ridge
print(' \nRidge')
ridge = Ridge().fit(X_train, y_train)
print("training set score : {:.2f}".format(ridge.score(X_train, y_train)))
print("test set score: {:.2f}".format(ridge.score(X_test, y_test)))
# ------ Lasso
print(' \nLasso')
lasso = Lasso().fit(X_train, y_train)
print("training set score: {:.2f}".format(lasso.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lasso.score(X_test, y_test)))
print("Number of features used:", np.sum(lasso.coef_ != 0))
#------ Elastic Net
print(' \nElasticNet')
elastic = ElasticNet().fit(X_train, y_train)
print("training set score: {:.2f}".format(elastic.score(X_train, y_train)))
print("Test set score: {:.2f}".format(elastic.score(X_test, y_test)))

เรียบร้อยแล้วนะคะ จะเห็นว่าเมื่อทำการปรับข้อมูลต่างๆ นาๆ แล้ว การใช้ Regulation model แบบ Ridge สามารถทำให้ Linear Regression ทำงานได้ดีขึ้นมากๆ ถึงแม้ว่าค่าความถูกต้องของการสอน (Training) จะต่ำกว่าโมเดล Decision Tree แต่ความเสี่ยงของการ Overfitting ก็ดูจะน้อยกว่าเช่นกัน
ก็ถือว่าการทำงานของเราประสบความสำเร็จระดับนึง โดยที่สามารถปรับแต่งโมเดลพื้นฐานอย่าง Linear Regression ให้ทำงานได้ดีขึ้นค่อนข้างมาก แต่ถ้าถามว่านี่คือ การปรับแต่งที่ดีที่สุดหรือไม่ ก็ขอตอบแบบเดิมนะคะ ว่ายังสามารถทำได้อีกค่ะ เช่น Optimize ตัวแปรของโมเดล Regularization เป็นต้น ยังไงก็ไปลองทำกันได้นะคะ
ก่อนจะจบบทความ มาสรุปกันหน่อยดีกว่า บทความนี้ เราได้ทดลองทำการทำนายข้อมูลด้วย Machine Learning ทั้งในรูปแบบของการทำนายกลุ่มของข้อมูล (Classification) และ การทำนายค่าของข้อมูล (Regression) โดยมีการเลือกใช้อัลกอริทึ่มที่หลากหลาย ไม่ว่าจะเป็น Decision Tree, SVM, k-NN หรือ Linear Regression เป็นต้น ไม่เพียงเท่านั้น เรายังได้มีโอกาสทดลองทำการปรับแต่งข้อมูล Feature Engineering และ ใช้งาน Regularization ในการ Optimize โมเดลอีกด้วย ทางผู้เขียนหวังเป็นอย่างยิ่งว่า บทความนี้จะเป็นประโยชน์แก่ผู้ที่สนใจการวิเคราะห์ข้อมูลไม่มากก็น้อย
สำหรับผู้ที่สนใจบทความนี้ สามารถตามไปอ่านให้ครบชุดได้ ดังลิงก์ด้านล่าง
บทความชุดนี้ประกอบด้วย 3 บทความย่อย คือ
1. สอนวิเคราะห์ข้อมูล [ฉบับจับมือทำ] ตั้งแต่เบื้องต้น ถึงการสร้างโมเดล Machine Learning ผ่านข้อมูล การ ซื้อ-ขาย ผลไม้เพื่อสุขภาพยอดฮิตอย่าง Avocado {Part 1: Descriptive Analysis}
2. สอนวิเคราะห์ข้อมูล [ฉบับจับมือทำ] ตั้งแต่เบื้องต้น ถึงการสร้างโมเดล Machine Learning ผ่านข้อมูล การ ซื้อ-ขาย ผลไม้เพื่อสุขภาพยอดฮิตอย่าง Avocado {Part 2: EDA}
3. สอนวิเคราะห์ข้อมูล [ฉบับจับมือทำ] ตั้งแต่เบื้องต้น ถึงการสร้างโมเดล Machine Learning ผ่านข้อมูล การ ซื้อ-ขาย ผลไม้เพื่อสุขภาพยอดฮิตอย่าง Avocado {Part 3: Machine Learning}