สร้าง Machine learning model ด้วย Pipeline
Don’t build the model but build the Pipeline
ตาม Process ปกติของการสร้างโมเดล (อ้างอิงจากงานที่ทำอยู่) การสร้างโมเดลในแต่ละรอบ มีความแตกต่างกันทั้งหมด ด้วยเรื่องของข้อมูล เรื่องการตั้ง Assumption ดังนั้น Code จากโปรเจคก่อนหน้านี้ จึงนำมาใช้ต่อไม่ได้ในทันที
แต่ถ้ามี Pipeline ที่ชัดแล้ว อาจทำให้ลดขั้นตอนในการทำงานลงได้มาก Blog ตอนนี้จะมาลองสร้าง ML Model ด้วย Pipeline ว่าจะแตกต่างจากงานที่อยู่อย่างไร
Pipeline
Pipeline เป็น Package ใน Scikit-learn ที่ช่วยการทำ ML Model ได้สะดวกมากขึ้น กล่าวคือโดยปกติขั้นตอนปกติในการทำโมเดล ไม่ว่าเป็นประเภทไหนต้องมีขั้นตอนที่เป็นพื้นฐานในทุกโมเดล ตามรูปด้านล่าง
ในตอนนี้ขอโฟกัสเฉพาะส่วนที่เกี่ยวข้องกับขั้นตอนการ Development เพียงอย่างเดียว ส่วนที่เหลือให้เป็นหน้าที่ของ Parties อื่น ๆ
Pipeline เข้ามามีบทบาทในส่วนที่เป็น Data preparation และ Data understanding อาจรวมถึง Model assembly บ้างเล็กน้อย ตัวอย่างเช่น ถ้าข้อมูลที่ได้รับมามี Missing value อยู่ ทำให้ไม่สามารนำข้อมูลนี้มาใช้งานได้ทันที ต้องมีการจัดการค่า Missing เหล่านั้นก่อน ขั้นตอนนี้เรียกว่า Imputer คือการแทนค่า Missing ด้วย Assumption ต่าง ๆ
ถ้าเราต้องการแทนค่า Missing ด้วยค่า Mean โดยปกติอาจต้องใส่ Column ที่ต้องการทำงาน และเขียน Code เพื่อแทนค่าเหล่านั้น เช่น
df['columnName'] = df['columnName'].fillna(df['columnName'].mean())
ซึ่งการทำตามที่เขียน Code ด้านบน ทำให้ข้อมูลใน df
เปลี่ยนแปลงไปคือ ค่า Missing ใน columnName
ถูกแทนด้วยค่าเฉลี่ยของ columnName
นั่นเอง
หรือหากต้องมีการ Transform ข้อมูลก่อนการ Training model เช่นต้องการเปลี่ยน Categorical feature ด้วย One-Hot Encoding โดยปกติอาจต้องเขียน เช่น
# Using Pandas
dummies = pd.get_dummies(df['columnName'])# Using Scikit-learn
encoder = OneHotEncoder()
encoder.fit(np.array(df['columnName']).reshape(-1, 1))
dummies = encoder.transform(np.array(df['columnName']).reshape(-1, 1))).toarray()
แน่นอนว่าการเขียนทั้งสองแบบ ทำให้เกิด DataFrame ใหม่ชื่อว่า dummies
ซึ่งอาจต้องมีการสร้าง Table สำหรับ Model training แยกต่างหาก
สำหรับ Pipeline มีการทำงานที่ต่างกันออกไป คือ Pipeline ไม่มีการเปลี่ยนข้อมูลใน Table ตั้งต้น หมายความว่าค่า Missing ที่อยู่ใน df
ยังคงมีอยู่ต่อไป หรือ Catergorical feature ก็ไม่ได้โดนเปลี่ยนเป็นเลข (0, 1)
ตาม One-Hot Encoding process
แต่การทำงานของ Pipeline คือการบอก Steps ต่าง ๆ ที่จำเป็นในการสร้างโมเดล เรียงเป็นขั้นตอน 1 → 2 → 3 แล้ว Pipeline จะทำการ “ต่อท่อ” ให้กับขั้นตอนเหล่านั้น จนได้เป็นโมเดลสุดท้ายออกมา
รายละเอียดเพิ่มเติมของ Pipeline สามารถอ่านได้จาก Link ด้านล่าง สำหรับ Code ตัวอย่างการทำงานของ Pipeline ดูได้ที่ Section ถัดไป
Code
สมมติว่ามี Data table พร้อมแล้ว และรู้ว่า Model development process มีอะไรบ้าง ไม่ขอลงรายละเอียดเกี่ยวกับการสร้างตัวแปรที่จำเป็น หรือการ Define target variable
Libraries ที่ใช้อาจดูเยอะเสียหน่อย แต่เชื่อว่าคงคุ้นเคยกันดีอยู่แล้ว สำหรับการสร้างโมเดล ดังนั้นจึงขอพูดถึงเฉพาะตัวที่เกี่ยวกับการใช้งาน Pipeline
แบ่งขั้นตอนการทำ Data preparation ออกเป็น 2 ขั้นตอนคือ
- Numeric features เนื่องจาก Columns ที่เป็นตัวเลขมีค่า Missing values อยู่ จึงจำเป็นต้องแทนค่า (Imputer) ด้วย Mean จากนั้นทำการ Normalization ให้อยู่ในช่วง 0–1 และ Transform เพิ่มด้วย Polynomial (ยกกำลัง 2 และคูณไขว้)
- Categorical features มีการแปลงค่าด้วย One-Hot Encoding ให้ตัวแปลทั้งหมดถูกเก็บไว้เป็นเลข 0 หรือ 1 และลด Effect การเกิด Multicollinearity ด้วยการ Drop ไป 1 Column
ทั้งหมดที่อธิบายคือ Steps ที่เกี่ยวข้องกับ Features ทั้งหมด ดังนั้นการสร้าง Pipeline สามารถใช้ Pipeline(steps = [(step1), (step2)])
และเขียนเรียงตามขั้นตอนเป็นลำดับ 1 → 2 → 3 ได้เลย
stepName = Pipeline(stpes = [('step1Name', method1Object()),
('step2Name', method2Object())])
จากตัวอย่างในตัวแปร numericTransformer
Step แรกคือการแทนค่า Missing ด้วย Mean จึงสามารถใช้ SimpleImputer(strategy = ‘mean’)
ต่อมา Normalization ด้วย MinMaxScaler()
และสุดท้าย Polynomial transform ด้วย PolynomialFeatures(degree = 2, include_bias = False)
สำหรับ catsTransformer
มีการจัดการข้อมูลเพียงแค่ทำ One-Hot Encoding สามารถทำตามโค้ดตัวอย่างได้เลย
เมื่อเรากำหนดขั้นตอนต่าง ๆ เสร็จทั้งหมดแล้ว ต่อไปคือการนำขั้นตอนเหล่านั้นไป Apply กับข้อมูลให้ถูกประเภท เช่น Numeric หรือ Categorical สามารถใช้ ColumnTransformer(transformers = [(transform1), (transform2)])
preprocessor = ColumnTransformer(transformers = [('transform1Name', step1Pipeline, columnsSelected1), ('transform2Name', step2Pipeline, columnsSelected2)])
ColumnTransformer()
ใช้ Define column ที่ต้อง Apply ด้วย Pipeline นั้น ๆ สิ่งที่ยังขาดสำหรับขั้นตอนนี้คือ การแยกประเภท Column ซึ่งสามารถใช้ make_column_selector()
สำหรับการกำหนดประเภทข้อมูลใน Columns สามารถใช้กำหนดด้วย dtype_include
เช่น dtype_include = np.number
หมายถึงเอา Column ที่มีข้อมูลเป็นตัวเลข หรือ dtype_include = object
หมายถึงเอา Column ที่เก็บเป็น String (Categorical features จากตัวอย่างถูกเก็บเป็น String ทั้งหมด อาจต้องคิดมาก่อนตั้งแต่การ Import ข้อมูล)
การใช้ Callable object จำเป็นต้อง Import library ในการทำงาน
เมื่อได้ทุกอย่างพร้อมแล้ว สามารถตัวแปร preprocessor
เพื่อดูขั้นตอนทั้งหมด สามารถสรุปได้ตามรูปด้านล่าง
Pipeline ยังสามารถต่อท่อออกไปได้อีก เช่น เมื่อ Preprocessing data เสร็จแล้ว ต้องการ Training model ต่อด้วย Linear regression สามารถ Define object ขึ้นมาเป็น LinearRegression()
จากนั้นใช้ Pipeline()
เพื่อบอกขั้นตอนต่อไป โดยสามารถใช้ Code structure ได้ตามเดิม เมื่อต่อท่อเสร็จแล้ว Pipeline สำหรับการสร้างโมเดลจะได้ตามรูปด้านล่าง
เมื่อได้ตัวแปร modelPipeline
มาแล้ว สามารถสร้างโมเดลจาก Pipeline ได้เลย เช่น หากต้องการเริ่ม Training model สามารถใช้ .fit()
แล้วผ่านตัวแปร Features และ Target เข้าไป หรือหากต้องการทำ Cross validation เหมือนตัวอย่าง สามารถใช้ cross_val_score()
แล้วผ่าน Parameters ได้ตามปกติ
Accuracy: [0.71165213 0.70554862 0.78076244 0.76061476 0.79887437 0.78799148 0.79665663 0.61820085 0.70419667 0.6462264 ]
Accuracy average: 0.731072434817978
Conclusion
Pipeline ถือเป็นตัวช่วยที่ดีให้การทำงานด้าน ML Model เพราะช่วยลดเวลาในการทำ Data (ไม่รวม Cleansing) ลงไปได้เยอะพอสมควร รวมถึงการเขียนโค้ดที่สั้นลงมาก แต่โดยส่วนตัวแล้ว Pipeline ต้องอาศัย Pipeline จริง ๆ หมายความว่าควรมี Pipeline ที่ชัดเจนตั้งแต่แรก ไม่เหมาะกับการลองผิดลองถูกมากนัก
ดังนั้นถ้านับเรื่องความถนัด หรือสิ่งที่ทำงานโดยปกติแล้ว Pipeline อาจยังไม่ถูกนำมาใช้ในการทำงานเท่าไหร่ แต่ถ้าพูดถึงเรื่องความสะดวกสบายในการเขียน Code การ Training model หลาย ๆ แบบ Pipeline ถือว่าเป็นสิ่งที่ตอบโจทย์มากทีเดียว
สำหรับ Colab notebook สามารถดูได้ที่ GitHub ที่ให้ไว้ด้านบน