Residual value model ทำโมเดลหาราคาขายรถมือสอง
ได้รับโจทย์ใหม่ที่ท้าทายมาอีกแล้ว ออกตัวไว้ตรงนี้ก่อนเลยว่าโมเดลนี้เป็นโมเดลที่ทำออกมาจากการ Research ขึ้นมาล้วน ๆ ดังนั้นอาจมีที่ผิดบ้างถูกบ้าง ขอให้ใช้พื้นที่ตรงนี้มา Discuss กันได้อย่างไม่มีอะไรมาปิดกั้น
Residual value
พูดถึงโจทย์ที่ว่าคือการหาราคา Residual value ของรถมือสองในอนาคต ก่อนอื่นควรมาทำความเข้าใจกับคำว่า Residual value กันก่อน ถ้าแปลคำนี้ตามพจนานุกรมจะได้ความหมายว่า “มูลค่าคงเหลือ” ซึ่งมันก็ถูกต้องตามนิยามจริง ๆ แต่ถ้าให้ขยายความก็คือ มูลค่าของรถเมื่อเวลาผ่านไป ซึ่งโดยปกติแล้วสินทรัพย์ประเภทรถ มูลค่าของมันจะลดลงไปเรื่อย ๆ ตามเวลาที่เพิ่มขึ้น ซึ่งทั้งหมดนี้คือโจทย์ที่ได้รับมา
A car’s residual value is an estimate of the dollar amount your car will be worth at the end of the lease term
Model assumption
เนื่องการ Forecast หรือการทำนายมูลค่าจากโมเดลในครั้งนี้มันต่างออกไป เพราะว่ามันไม่ใช่ทั้ง Time series model หรือ Predictive modelling ปกติ เพราะอย่างที่ได้บอกไปว่า Residual value ของรถจะลดลงตามระยะเวลาที่เพิ่มขึ้น แต่มูลค่าที่ลดลงต้อง Estimate จาก Condition ที่เกิดขึ้นล่าสุด ณ ปัจจุบัน
ดังนั้นจึงจำเป็นต้องมีฟังก์ชั่นหรือสมการเกิดขึ้น เพื่อหาราคาหรือมูลค่ารถในอนาคต โดยฟังก์ชั่นมีค่า Input ที่เปลี่ยนแปลงเพียง 1 ค่าคือระยะเวลาที่เพิ่มขึ้น
โดยที่
- p คือมูลค่าที่เกิดขึ้น ณ เวลาใด ๆ
- a คือมูลค่า ณ ปัจจุบัน
- k คือค่า Coefficient ที่เกิดขึ้นจากโมเดล
- t คือเวลา
เมื่อต้องการหาความสัมพันธ์ของตัวแปรอื่น ๆ ที่อาจมีผลต่อราคาที่เกิดขึ้น สมการสามารถรับค่า Coefficients หรือ Weights ที่เกิดจากโมเดล และรับค่าตัวแปร ซึ่งสามารถเขียนเป็นสมการใหม่ได้ว่า
โดยที่
- b คือค่า Coefficient ที่เกิดขึ้นจากโมเดล
- x คือค่าของตัวแปรนั้น ๆ
คำถามที่เกิดขึ้นต่อไปคือ ควรใช้โมเดลอะไรเพื่อให้ได้ค่าทั้งหมดออกมา และต้องสามารถใช้ในสมการที่มีอยู่แล้วได้ด้วย ซึ่งครั้งนี้จึงขอใช้ Neural network ในการสร้างโมเดลขึ้นมา ข้อดีของ Neural network คือการที่สามารถปรับ Weight เพื่อให้ Loss ที่เกิดขึ้นต่ำที่สุด ส่วนเรื่องความสัมพันธ์ สามารถปล่อยให้เป็นหน้าที่ของ Network จัดการให้ พอฟังดูแล้วก็มีข้อเสียปะปนอยู่เหมือนกัน ซึ่งก็คือการ Randomly estimation
Dataset
เนื่องจากว่ายังไม่ได้ข้อมูลจริง ๆ ลูกค้า เลยต้องหา Dataset มาเพื่อทดลอง Develop model ก่อน โดยทั่วไปแล้วข้อมูลเกี่ยวกับ Used car สามารถหาได้ตามเว็บที่เกี่ยวข้องกับ Data analytics ซึ่งก็มีให้เลือกใช้งานอยู่มากมาย ในครั้งนี้ได้ข้อมูลมาจาก Kaggle ที่มีราคารถที่เป็นตัวแปรที่ต้องการ Predict และตัวแปรอื่น ๆ (Features) ที่มีความจำเป็นสำหรับการพัฒนาโมเดล สามารถดูได้ที่ Link ด้านล่าง
เนื่องจากข้อมูลที่ได้มายังไม่ผ่านการ Cleansing มาก่อน ดังนั้นจึงยังไม่สามารถใช้ข้อมูลได้ทันที จำเป็นต้องทำการ Cleansing อาจมีการตั้ง Assumption เพิ่มเติม ดังนั้นจึงขออธิบายไปพร้อมกันกับ Coding เพื่อความไม่สับสน
Data assessment
Tensorflow version: 2.3.0
GPU is available
/content/drive/My Drive/Colab Notebooks/used_car
ทั้งหมดคือ Libraries ที่ใช้สำหรับการ Develop model ในครั้งนี้ ซึ่งอาจดูเหมือนเยอะ แต่ทุก Libraries คงเป็นที่คุ้นเคยกันเป็นอย่างดีอยู่แล้วสำหรับการทำงานสายนี้ เนื่องจาก Dataset ที่ใช้มีขนาดค่อนข้างใหญ่ (1.2GB) จึงขอใช้ Google drive เพื่อเก็บ Data และไม่ต้องทำการโหลดใหม่ในทุก ๆ รอบที่เปิด Colab ขึ้นมา
ขอใช้ Kaggle API ในการโหลดข้อมูลไปยัง Google drive ซึ่งเคยเขียนวิธีการทำเอาไว้แล้วทั้งภาษาไทย และภาษาอังกฤษ ถ้าต้องจัดการกับข้อมูลขนาดใหญ่ วิธีนี้เป็นทางเลือกที่ดีมาก
Total row: 435849, Total columns: 25
เริ่มอ่านข้อมูลเข้ามา โดย Raw data มีข้อมูลทั้งหมด (435849, 25)
ซึ่งแน่นอนว่าไม่ได้ใช่ทั้งหมด ดังนั้นจึงทำการเก็บแค่ Columns ที่ต้องการเอาไว้ ซึ่งสามารถเพิ่มหรือลดลงได้เช่นกัน แต่จะมีผลในเรื่องของการ Transform features โดยครั้งนี้ใช้ Features ทั้ง 3 ตัวคือ 1.ปีรถ 2.ระยะไมล์ และ 3.สภาพรถ และ Target คือราคารถที่เราต้องการ Predict
Total columns after exclusion: 4
ลอง .describe()
ทั้ง DataFrame ออกมา ซึ่งสามารถ Return เฉพาะค่าจาก Columns ที่เป็นตัวเลขออกมาได้เท่านั้น ดังนั้นหมายความว่า condition
ถูกเก็บไว้เป็น String ซึ่งต้องมีการแก้ไขก่อน Training model เพราะโมเดลรู้จักเฉพาะตัวเลขเท่านั้น
แต่ก่อนอื่นขอเริ่ม Cleansing ที่ price
ก่อน จะเห็นได้ว่าราคาต่ำสุดคือ 0 มีทั้งหมด 30701 Rows ซึ่งเป็นข้อมูลที่ไม่มีประโยชน์สำหรับการสร้างโมเดล ขอลบออกทั้งหมด ต่อมาขอ Remove outlier ออก โดยกำหนดช่วงขอราคาให้มีค่ามากกว่าหรือเท่ากับ 750 ไปจนถึงต่ำกว่า 1 แสน ซึ่งมีจำนวน 536 และ 9984 ตามลำดับ
ทำการเช็คค่า Missing value ขอทั้ง Dataset
price 0
year 1093
odometer 63849
condition 160208
จะเห็นได้ว่า Features columns ยังมี Missing อยู่เยอะพอสมควร ซึ่งต้องพยายามแก้ไขให้ได้มากที่สุด เพื่อเก็บข้อมูลที่ใช้ในการทำโมเดลให้ได้เยอะที่สุด
ไปที่ตัวแปร year
ขอเติมค่าที่หายไปด้วยฐานนิยม (Mode) แต่โมเดลไม่ได้ต้องการใช้ปีรถเป็นหนึ่งใน Features แต่ต้องการใช้อายุของรถนั้น ๆ เพื่อการทำนาย ดังนั้นจึงต้องหาอายุของรถขึ้นมาให้ ซึ่งสามารถทำได้ด้วยการใช้ปีปัจจุบันลบด้วยปีรถ ซึ่งสามารถใช้คำสั่ง datatime.datatime.now().year
เพื่อให้ Return ปี 2020 ออกมา
Column ระยะไมล์ยังมีค่าที่หายไปเยอะพอสมควร 63849 ดังนั้นขอเติมค่าเหล่านั้นด้วยค่าเฉลี่ยของระยะไมล์ทั้งหมด ตอนนี้ Dataset ยังเหลืออีก 1 Column คือ condition
ที่มีค่า Missing สูงที่สุดซึ่งคือ 160208 เนื่องจากไม่สามารถแทนค่าใด ๆ ลงไปใน Column นี้ได้ เพราะไม่รู้ว่าาสภาพของรถจริง ๆ เป็นอย่างไร ดังนั้นจึงจำเป็นต้องลบข้อมูลที่หายไปออกทั้งหมด
และเนื่องจาก condition
มีการเก็บค่าเอาไว้เป็น String ดังนั้นจึงต้องมีการ Transform ข้อมูลให้เป็นตัวเลขก่อนนำไปใช้ใน Neural network โดยขอให้คะแนนตามคุณภาพที่เกิดขึ้น excellent = 6
ลดลงไปเรื่อย ๆ จนถึง salvage = 1
Model development
จบเรื่องของ Dataset ทั้งหมดเอาไว้ที่ด้านบน ตอนนี้สามารถเริ่มทำโมเดลได้แล้ว
เริ่มต้นแบ่ง train, test
เพื่อที่จะใช้ test
ในการทดสอบโมเดล โดยแบ่งเป็น 70/30 จากนั้นทำการ Scale ข้อมูลในอยู่ในช่วง 0–1 ด้วย MinMaxScaler()
และทำการแยก Target (y)
และ Features (x)
ก่อนที่จะเข้าสู่ Neural network
…, which can increase the velocity of convergence during the training phrase.
ใช้เป็น Sequential()
เพื่อทำ Back propagation สร้างความสัมพันธ์ให้กับข้อมูล ของเลือก activation
เป็น selu
และ Force weight ในทุก ๆ ชั้น Layer ให้เป็นค่า Positive เท่านั้น ซึ่งสามารถใช้ non_neg()
ที่รับมาจาก keras.constraints
หากพิจารณาสมการดูแล้ว ค่า Model weight ที่เกิดขึ้นต้องเป็นบวกเท่านั้น จึงสามารถ Fit เข้ากับสมการเพื่อให้เกิด Curve ที่ลู่ลงตามเวลาที่เพิ่มขึ้นได้ สิ่งนี้คือเป็นข้อดีของการทำ Neural network ที่สามารถเล่นกับ Weight ภายในชั้น Layer ได้
Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_96 (Dense) (None, 500) 2000 _________________________________________________________________ dense_97 (Dense) (None, 250) 125250 _________________________________________________________________ dense_98 (Dense) (None, 3) 753 _________________________________________________________________ dense_99 (Dense) (None, 1) 4 ================================================================= Total params: 128,007 Trainable params: 128,007
Non-trainable params: 0 _________________________________________________________________
ใช้ Optimizer เป็น Adam
กำหนด Learning rate = 0.01
และ Compile model ที่วัดค่า Error ด้วย mse
จากนั้น model.fit()
ด้วยการ Training 5 epochs
Epoch 1/5
2198/2198 [===========================] - 4s 2ms/step - loss: 0.0224
Epoch 2/5
2198/2198 [===========================] - 4s 2ms/step - loss: 0.0105 Epoch 3/5
2198/2198 [===========================] - 4s 2ms/step - loss: 0.0105 Epoch 4/5
2198/2198 [===========================] - 4s 2ms/step - loss: 0.0105 Epoch 5/5
2198/2198 [===========================] - 4s 2ms/step - loss: 0.0104
ผ่านไปเพียงแค่ไม่กี่ Epochs ค่า Loss ที่เกิดขึ้นก็ต่ำมากแล้ว
[0.109580815, 0.08728964, 0.11287784, 0.034483597]
สิ่งสุดท้ายที่ต้องการคือ Weight สุดท้ายที่เกิดขึ้นใน Hidden layer (Dense) สุดท้ายก่อนที่จะออกเป็น Output layer ดังนั้นจึงได้มีการกำหนดให้ Hidden layer สุดท้ายมีค่าเท่ากับจำนวน Features ซึ่งเท่ากับ 3 เพื่อทำการ Extract weight ออกมาได้ โดยใช้คำสั่ง model.layers[3].weights[0]
สำหรับ Feature weights และ model.layers[3].weights[1]
สำหรับ Bias weight ซึ่งค่าที่ออกมาได้เป็น Positive ทั้งหมดตามที่ Force ไว้ตั้งแต่ที่ออกแบบ Model architecture
Curve fitting
ตอนนี้ได้โมเดลสำหรับการทำนายราคารถแล้ว สิ่งที่ยังขาดไปคือการ Fitting curve เพื่อให้ราคามีความเหมาะสมมากขึ้น ซึ่งตรงนี้สามารถใส่ Expert input ลงไปได้ เช่น Business มีการคาดการณ์ไว้ว่าราคารถจะลงเป็นกี่ % จากราคาปัจจุบัน ก็สามารถสะท้อนมุมมองของ Business ลงไปได้ และนี่คือสิ่งที่เรียกว่า Expert input
ก่อนอื่นของเขียนฟังก์ชั่นเพื่อการคำนวณต่าง ๆ ขึ้นมาไว้ก่อน แบ่งออกเป็น 2 ฟังก์ชั่นคือ 1.สมการที่ได้พูดถึงจากด้านบน เพื่อการทำราคาออกมาในแต่ละปี 2.Random dataset สำหรับการทดสอบโมเดล
เตรียมข้อมูล test
ให้เหมือนกับข้อมูล train
โดยการ Scale ข้อมูลเหมือนกัน แต่สิ่งที่ต่างออกไปคือราคารถ ซึ่งจะใช้เป็นราคาที่ไม่ได้ผ่านการ Scale
Sampling ข้อมูลขึ้นมา 1 คัน จากนั้นนำเข้าฟังก์ชั่น residual_price
ที่เขียนเอาไว้โดยใช้ตัวแปรทั้งหมด 3 ตัว และทำราคาไปอีก 10 ปีข้างหน้า np.arange(10)
ก่อนที่จะใช้ราคาที่เกิดจากโมเดลและสมการ (ซึ่งอาจมีความไม่ Make sense เกิดขึ้นอยู่บ้าง) เพื่อลดความไม่ Make sense ดังนั้นจึงควรทำการ Fit curve ก่อน ซึ่งในตัวอย่างนี้ ขอกำหนดให้ราคาลดลงปีละ 10% ไปจนถึงปีสุดท้ายที่มีราคาซากรถอยู่ 5% ไว้ก่อน
ซึ่งตรงนี้สามารถใส่มุมมองของ Business ลงไปได้เช่น ในปีที่ 3 ราคารถจะตกลงมาอยู่ที่ 50% ก็สามารถสร้าง
market_curve
ขึ้นมาใหม่ เพื่อ Fit ราคาจากโมเดลให้เข้ากับมุมมองจาก Business
การ Fit curve สามารถใช้คำสั่ง curve_fit()
จาก Scipy ได้ โดยคำสั่งนี้รับค่าที่จำเป็น 3 ค่าคือ
- Function ที่ใช้ในการคำนวณราคา
residual_price
- ราคาที่เกิดขึ้นจากการใช้ฟังก์ชั่นคำนวณราคา
model_values
- Curve ที่ต้องการ Fit เข้าไปหา
market_curve
Result
Car testing parameters: [[31500.0, 0.010087641602321855, 1.0000000000000002, 0.08264462809917356]]
ซึ่งจากตัวอย่างรถที่เอามาคำนวณมีราคาปัจจุบันอยู่ที่ 31500 เหรียญ และค่า Scaled ตามที่เห็นอยู่ด้านบน เมื่อนำไปผ่านโมเดลแล้วได้ราคาใน 10 ปีข้างหน้าดังนี้
Model estimate: [31500.0 3451.79566741 3071.86942659 2733.76024633 2432.86547915 2165.08907378 1926.78581597 1714.71170659 1525.97980136 1358.02091116 1208.54862791]
ซึ่งแน่นอนว่าราคาลดลงไปเร็วกว่าที่ควรจะเป็นมาก ดังนั้นจึงต้องทำการ Fitting curve ให้เข้ากับ Market
Residual fitted estimate: [31500.0 24822.28661213 22090.19034985 19658.80570625 17495.03447799 15569.42145721 13855.75346063 12330.70249203 10973.50818047 9765.69517145 8690.8216236 ]
เห็นได้ว่าราคาที่เกิดขึ้นมีความสมเหตุสมผลมากขึ้น ซึ่งหากทดลอง Plot จะสามารถดูความแตกต่างของราคาได้อย่างชัดเจน
Conclusion
วิธีการนี้และโมเดลนี้ยังมีจุดที่ต้อง Improve อยู่มาก แต่เป็นวิธีที่สามารถ Estimate ราคาที่ “อาจจะ” เกิดขึ้นได้ ซึ่งหากมีแนวคิดหรือ Improvement ใหม่ ๆ คงเอามาเล่าให้โอกาสต่อไป