Multiple linear regression ด้วย TensorFlow 2.0 และ Keras
Linear regression หรือสมการเส้นตรงที่รู้จักกับมาตั้งแต่สมัยเรียนมัธยม ซึ่งตอนนั้นยังไม่เห็นความสามารถของสมการนี้ นอกจากการการทำนายน้ำหนักจากส่วนสูง หรือส่วนสูงจากน้ำหนัก (โจทย์สมัยตอนที่เรียน) แต่พอเข้าสู่โลกการทำงาน ไม่น่าเชื่อว่าสมการเส้นตรงง่าย ๆ จะถูกใช้อย่างกว้างขวางและแพร่หลาย เชื่อว่าส่วนหนึ่งมาจากความเข้าใจง่ายของมัน
เนื่องจากพื้นฐานความนิยมและความสามารถในการใช้งาน ไม่แปลกใจเลยว่า Machine learning ต่าง ๆ จะมี Library หรือ Package สำเร็จรูปเพื่อทำ Linear regression หรือ Multiple linear regression (ต่างกันที่จำนวนตัวแปร X) ที่พิมพ์โค้ดเพียง 1 บรรทัด ก็ได้ค่า Estimates หรือ Coefficient ของโมเดลไปใช้งาน แต่วันนี้ลองมาแก้สมการเส้นตรงโดยไม่ใช้เครื่องมือสำเร็จรูปเหล่านั้น วันนี้มาแก้สมการเส้นตรงแบบ “กึ่งสำเร็จรูป” ด้วย TersorFlow และ Keras
Linear regression
ขอ Recap เกี่ยวกับ Linear regression สั้น ๆ ว่ามันคือเครื่องมือทางสถิติอย่างหนึ่ง ที่ใช้หาความสัมพันธ์ระหว่างตัวแปรต้น หรือที่เรียกว่าตัวแปรอิสระ (Independent variable: X) และตัวแปรตาม (Dependent variable: Y) โดยเมื่อตัวแปรต้นมีค่าเท่ากับใด ๆ และส่งผลต่อตัวแปรตามเป็นเช่นไร ซึ่งถ้ามีตัวแปรต้นหลายตัวแปร Linear regression จะถูกเปลี่ยนชื่อเรียกว่า Multiple linear regression หมายความว่าตัวแปรต้นหลายตัว ส่งผลต่อตัวแปรตามตัวเดียว
โดยการทำงานของสมการคือ พยายามลากเส้นตรงให้ผ่านจุดที่เป็นค่าจริงมากที่สุด โดยการ “สุ่มค่า” ทั้ง Slope (b0) และ Bias (bn) เพื่อให้ค่าที่ Predict ออกจากสมการมีค่าเข้าใกล้ความจริงมากที่สุด กล่าวอีกมุมคือ เพื่อให้เกิด Error ที่ต่ำที่สุด (Cost function)
Gradient Descent
จากที่บอกไปว่าการหาคำตอบของสมการเส้นตรงเป็นการสุ่มค่า โดย Machine จะค่อย ๆ ปรับ Slope และ Bias ขึ้นหรือลง และดูว่า Cost ที่เกิดขึ้นจากการปรับค่าแต่ละรอบเพิ่มหรือลดอย่างไร ซึ่งกระบวนนี้เรียกว่า Gradient Descent
โดยที่เป้าหมายของ Gradient Descent คือการลด Cost ให้ไปอยู่จุดที่ต่ำที่สุดที่เรียกว่า Global Minumum เพื่อให้ไปถึงเป้าหมาย สิ่งแรกที่ต้องรู้คือการ “เพิ่มหรือลด” ค่า Slope และ Bias ซึ่ง Machine จำเป็นต้องหา Derivative ของ Error หรือ Cost function ตัวอย่างเช่นถ้า Initial weight อยู่ที่ฝั่งขวาของกราฟ ดังนั้นค่าความชันจึงมีค่าเป็นบวก เพื่อให้ไปถึง Global Minimum ค่า Weight ต่อไปต้องขยับมาทางซ้าย เพื่อให้เข้าใกล้ Global Minumum มากขึ้น เช่นเดียวกันถ้า Initial weight อยู่ที่ฝั่งซ้าย ค่า Weight ต้องปรับไปทางขวา เพื่อความชันมีค่าเป็นลบ
Learning rate
Process ที่กล่าวไปเป็นกระบวนการทำซ้ำจนกว่าไปถึงเป้าหมาย ดังนั้นมันจึงเป็นการเรียนรู้ของ Machine หรือที่เรียกว่า Machine learning จึงมี Parameter ตัวใหม่เกิดขึ้นมาคือ Learning rate
Learning rate เปรียบเหมือนการเรียนรู้จากการสุ่มค่า ถ้าปรับ Rate ให้มากแน่นอนว่าความเร็วในการ Training model คือความได้เปรียบ แต่ความแม่นยำย่อมมีค่าลดลง เพราะทั้งหมดนี้มันคือการสุ่มค่าตัวเลข เช่นเดียวกับการที่ปรับ Rate ที่น้อย ความแม่นของโมเดลจะมีมากขึ้น แต่ต้องแลกกับ Training time ที่ยาวนาน
TensorFlow 2.0
การทำ Linear regression หรือ Multiple linear regression ด้วย TensorFlow สามารถทำได้หลายวิธี ซึ่งแตกต่างจากการใช้ Library สำเร็จรูปอย่าง Scikit-learn หรือ Statsmodels เพราะ TensorFlow ต้องมีการเขียน Model เองตั้งแต่เริ่ม แต่อาจมีเครื่องมือบางอย่างมาช่วยทำให้การเขียน Model ง่ายขึ้นจากเมื่อก่อน
ตัวอย่าง Dataset ขอใช้เป็นตัวอย่างเดียวกับตอนทำ Linear Regression ด้วย Bayesian ซึ่งมี Link เอาไว้ให้อ่านเพิ่มเติมด้านล่าง
**TensorFlow เวอร์ชั่น 1.x อาจมีการเขียน Code ที่แตกต่างกันออกไปจากนี้**
เนื่องจากข้อมูลมีตัวแปร Independence variables ทั้งหมด 3 ตัว (x1
, x2
และ x3
) จึงต้องสุ่มตัวเลขขึ้นมาทั้งหมด 3 ค่า รวมกับ Bias ที่ควรเกิดขึ้นจากการ Training model อีก 1 ตัว ดังนั้นการสุ่มตัวเลขจึงต้องทำทั้งหมด 4 ค่า ซึ่งการเซ็ตตัวแปรสามารถทำได้ด้วยคำสั่ง tensorflow.Variable()
การสุ่มตัวเลขขอเป็นจาก Standard normal distribution ซึ่งสามารถใช้ numpy.random.randn()
ตรงนี้สามารถเปลี่ยนวิธีการสุ่มค่าได้ตามต้องการ นอกจากนี้ขอกำหนด Learning rate ไว้ที่ 0.1
ซึ่งแน่นอนว่าต้องการปรับขึ้นหรือลงให้เหมาะสมกับชุดข้อมูลที่ใส่เข้าไปใน Model
จากนั้นเขียน for… loop… ขึ้นด้วยตัวเลขจำนวนหนึ่ง ซึ่งขอตั้งไว้เป็น 1 แสนรอบในตอนแรก จากนั้นต้องมีการบันทึกค่า Gradient Descent ที่เกิดขึ้นในแต่ละรอบการเทรนด้วย tensorflow.GradientTape()
ซึ่งทำงานภายใต้ with
Statement เริ่มเขียนสมการเส้นตรงที่ต้องการแก้ ซึ่งเหมือนมีมากกว่า 1 ตัวแปร สมการเส้นตรงจึงควรเป็นแบบ Multiple linear regression
Cost function ของ Linear regression คือค่าความผิดพลาดของค่าจริง และค่าที่เกิดขึ้นจากการทำนายของโมเดล ซึ่งสามารถหาได้จากความแตกต่างที่เกิดขึ้น (diff)
ซึ่งเมื่อมองจากสมการแล้ว ค่า Error สามารถเกิดขึ้นได้ทั้งทางบวกและทางลบ ดังนั้นเพื่อการคำนวณผลรวมของ Error สุดท้ายจึงใช้ค่าผลรวมของเลขยกกำลัง หรือที่เรียกว่า Residual Sum of Squares (RSS)
เนื่องจากค่า RSS คือค่าผลรวม และมีแนวโน้มที่จะสูงขึ้นเรื่อย ๆ ดังนั้นเพื่อการวัดค่า Error ที่เกิดขึ้นของสมการเส้นตรง จึงมีการคำนวณ Cost function หลายแบบ เช่น MSE, RMSE หรือ MAE ซึ่งขอใช้ MSE การวัดค่า Error ในครั้งนี้ ซึ่งสามารถใช้ TensorFlow เขียนได้โดย tensorflow.reduce_mean((predict-actual)**2)
จากนั้นทำการหาค่า Derivative ที่เกิดขึ้นในแต่ละ Epoch ด้วย tensorflow.GradientTape().gradient()
โดยตัวแปรที่ผ่านเข้าไปมีด้วยกันทั้งหมด 2 ค่าคือ 1.Cost function ในที่นี้คือ MSE และ 2.Matrix ของการสุ่มตัวเลข โมเดลต้องมีการอัพเดตค่าในทุกรอบ Epoch เพื่อให้ไปถึง Global Minumum ซึ่งสามารถทำได้ด้วยคำสั่ง .assign_sub()
ด้วยค่า Learning rate เพียงเท่านี้ก็สามารถเริ่มเทรนโมเดลได้
0 0.036594782 1.3330247 0.38275766 -0.2781242 18.877241
5000 0.19926678 -0.079745315 3.6152542 -3.6022751 0.019064685
10000 0.29830837 -0.078959376 3.6267447 -3.6017861 0.019044155
15000 0.39664203 -0.07921105 3.6246412 -3.6018493 0.01902482
20000 0.49454826 -0.07946162 3.6225464 -3.6019123 0.01900563
25000 0.592051 -0.07971115 3.6204607 -3.601975 0.018986616
30000 0.6891031 -0.07995957 3.6183846 -3.6020374 0.018967763
35000 0.78577256 -0.08020697 3.616316 -3.6020994 0.01894907
40000 0.8820024 -0.080453224 3.6142578 -3.6021614 0.018930543
45000 0.9778258 -0.08069849 3.6122077 -3.602223 0.018912166
50000 1.0732623 -0.08094279 3.6101658 -3.6022842 0.018893946
55000 1.1682396 -0.08118583 3.6081338 -3.6023452 0.01887589
60000 1.2628968 -0.08142807 3.6061091 -3.602406 0.018857973
65000 1.3570721 -0.08166912 3.6040945 -3.6024668 0.018840233
70000 1.450819 -0.08190903 3.6020885 -3.602527 0.018822636
75000 1.5442572 -0.08214822 3.6000893 -3.602587 0.018805174
80000 1.6372404 -0.08238614 3.5981002 -3.6026468 0.018787876
85000 1.7297792 -0.082622975 3.5961206 -3.6027062 0.018770736
90000 1.8220206 -0.08285903 3.594147 -3.6027656 0.018753717
95000 1.9138118 -0.083094 3.5921836 -3.6028247 0.018736856
เนื่องจากโมเดลมีการเทรนทั้งหมด 1 แสนรอบ จึงกำหนดให้แสดงผลการทำงานทุก ๆ 5 พันรอบ เพื่อไม่ให้เยอะจนเกินไป โดยตัวเลขที่เกิดขึ้นคือ Model coefficients โดยเรียงจาก b0, b1, b2 และ b3 ค่าสุดท้ายคือค่า Error ล่าสุดที่เกิดขึ้นเมื่อใช้เลขสุ่มชุดนั้น ๆ ซึ่งอยู่ที่ประมาณ 1.87%
Model loss: 0.018720148131251335
Intercept: -3.6028833389282227
Coefficient x1: 2.005181074142456
Coefficient x2: -0.08332784473896027
Coefficient x3: 3.590228319168091
ค่า Model coefficients ที่ Epoch สุดท้าย (1 แสน) และค่า Predict ที่เกิดขึ้น
Keras
ด้านบนเป็นการใช้ TensorFlow เขียนโมเดลขึ้นมา ลองเปลี่ยนมาใช้ Integrated library อย่าง Keras ที่นิยมใช้ทำ Neural network บ้างว่ามีความยากง่ายต่างกันมากน้อยแค่ไหน
ขอเขียนโมเดลด้วยฟังก์ชั่น Sequential()
เพราะส่วนตัวแล้วคิดว่าง่ายและสะดวกกว่าฟังก์ชั่น Model() โดยที่ออกแบบ Network แบบพื้นฐานให้เป็น Fully connected ทั้งหมด 500 Nodes โดยที่ซ้อน Layer ทั้งหมด 3 ชั้น และชั้น Output อีก 1 ชั้น ดังนั้น Network จึงมีหน้าตาแบบ 500–500–500–1 Activation function ขอใช้เป็น Rectified Linear Unit (ReLU) ซึ่งสามารถเปลี่ยนได้เช่นกัน input_shape
ต้องกำหนดให้เท่ากับตัวแปร Independence variables ซึ่งมีทั้งหมด 3 ตัว แต่ข้อมูลอยู่ใน DataFrame เดียวกัน ดังนั้นจึงใช้คำสั่ง df.shape[1]
เป็นนับจำนวนแถว Columns ทั้งหมด และ -1
ออกเพื่อกำจัดตัวแปร Dependence variable
Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_32 (Dense) (None, 64) 256 _________________________________________________________________ dense_33 (Dense) (None, 128) 8320 _________________________________________________________________ dense_34 (Dense) (None, 64) 8256 _________________________________________________________________ dense_35 (Dense) (None, 1) 65 ================================================================= Total params: 16,897
Trainable params: 16,897
Non-trainable params: 0 _________________________________________________________________
กำหนดตัว Optimizer ของโมเดล ขอใช้ตัวพื้นฐานที่สุดคือ RMSprop()
ซึ่งมีจุดเด่นคือการทำงานที่รวดเร็ว ตรงนี้อาจเปลี่ยนไปใช้ Optimizer ตัวอื่น เช่น Adam ก็ทำได้เช่นกัน โดยขอกำหนด Learning rate ที่ 0.001
เป็นค่าเริ่มต้น จากนั้นทำการ Compile model และวัดค่า Cost function ด้วย MSE เมื่อทุกอย่างถูกกำหนดแล้ว สามารถทำการ .fit()
โมเดลได้ โดยที่กำหนดรอบการเทรนไว้ที่ 500 epochs
ด้านล่างเป็นกราฟ Back testing ที่เกิดจากการ Training model ด้วย Keras ซึ่งสามารถปรับ Parameters ต่าง ๆ เพื่อผลลัพธ์ที่ใกล้เคียงค่าจริงให้มากที่สุดได้
Conclusion
เห็นได้ว่าแนวคิดในการสร้างโมเดลด้วยสมการเส้นตรงด้วย TensorFlow มีแนวคิดคล้ายกัน ไม่ว่าจะเขียนด้วย TensorFlow หรือ Keras แต่ Keras มีฟังก์ชั่น Bulit-in ที่ให้ความสะดวกสบายในการใช้งานมากกว่า แต่แน่นอนว่า TensorFlow ถ้าเขียนคล่องแล้วจะสามารถปรับแต่งได้เยอะกว่า