監督式回歸問題

機器學習預測北捷人流--隨機森林

使用隨機森林預測台北捷運每天、每時、每站的人流

Harry Cheng
Jul 9, 2021
來源:網路圖片

對住在台北的人來說,捷運是生活中不可或缺的一部分。但許多人可能沒想過,自己每天刷卡進站、出站,北捷其實都有把資料存下來。利用這些資料的預測性質,我們能夠預測未來任何一個時間點的捷運人流。

利用暑假的時間,我嘗試建了一個預測模型,結果還不錯!今天我會帶大家走過整個專案的過程,並且附上我所有的code

大綱

  1. 問題定義
  2. 資料處理
  3. 探索式資料分析
  4. 特徵工程
  5. 建模
  6. 下一步的討論

問題定義

我們把問題定義為:如何預測北捷在未來某一日/某一時/某一站的人流(進站+出站)

例如:多少人在2021年8/1,16:00 進出捷運市政府站?(預測時間以整點為單位)

這個模型如果成功建出,能夠產生很大的商業/社會價值。我們以不同Stakeholders的角度來探討:

  1. Uber、計程車公司:能夠定出更精準的動態定價模型
  2. 台北市政府:模型能夠結合公車班表、Ubike補缺系統,打造更完善的交通服務
  3. 捷運商圈商家:透過人流預測決定更好的營運、行銷策略

資料處理

資料下載

第一步要做的事就是抓資料。這個步驟很方便,因為市政府的台北市資料大平台都有開放資料可以使用。

在這邊我只使用2018、2019兩年的資料,為了避開肺炎的影響。當然我們不能保證未來不會再次發生足以影響北捷整體營運的大事件,而且捷運營運狀況也隨時在改變(例如2020年多了環狀線),這部分模型要怎麼適應,在文章最後我會提到。

下載完了資料之後,我把資料讀進Jupyter Notebook,顯示總共有一億七千萬筆,五個欄位,電腦差點一命嗚呼…

資料長相

資料處理

我做的第一件事,是把問題拆成“進站預測”以及“出站預測”,並且先為“進站”建模。這樣可以直接減少100倍的資料,而且“進站”與“出站”其實是一體兩面,可以使用相同的資料處理步驟以及模型。今天的專案我就先以“進站”為例,“出站”的程式可以在我的GitHub找到(模型結果與進站幾乎一模一樣)。

處理完之後,也順便換了欄位名稱,清爽多了!(還是有一百六十萬筆就是了…)

探索式資料分析

在這部分我根據自己對於北捷的既有認識,進行EDA。

日期與人次之間的關聯

根據我的假設,捷運的人流會依照星期、月份、假日而有所變化。假設也透過視覺化得到驗證。以2018年為例:

2018年資料,橫軸為時間,縱軸為人流
  1. 每一顆鋸齒都是一個禮拜。一到五為高峰,六日為低谷
  2. 不同月份也有人流高低起伏。11、12月偏高
  3. 特殊節日也有影響。圖中最低的離群值為除夕

→製造星期、月份、特殊節日的feature

車站與人次之間的關聯

這應該不用特別說了,直接看圖:

中文顯示的時候有點問題,但是可以另外印出車站名稱,不影響判斷
  1. 熱門車站與冷門車站之間差距極度懸殊(沒錯,最高的那個就是北車)

→需選擇不會被離群值影響太多的模型

→等模型決定之後再決定如何處理【Station】feature

模型選擇

經過EDA,我認為最適合這個資料的模型是隨機森林。

隨機森林是一個簡單粗暴的演算法。簡單是在於它的運作模式相對好懂,不像是神經網路一樣是一個黑盒子;粗暴是在於它是一個Ensemble model,也就是說它是由許多決策樹所組成的,所以計算部分會比較重一點。

如果想要瞭解隨機森林的原理,可以看這支關於決策樹的影片,以及這支隨機森林的影片。我覺得沒有人可以講的比Josh Starmer更清楚了!

那為什麼隨機森林會適合這組資料呢?原因有三:

  1. 隨機森林不容易被離群值影響,因為離群值常會被分到一片小樹葉中,不會對其他數值的預測造成影響。Model不會因為台北車站人特別多影響對於其他數值的判斷。
  2. 隨機森林可以處理非線性的關係。這組資料feature跟人潮的關係都不是線性的,像是時段跟人潮之間就不是線性關係。
  3. 這組資料不會有外推(Extrapolation)的問題。隨機森林比較怕的就是新資料跳出舊資料的範圍,但是這在北捷資料裡不會發生。唯一的可能就是新車站的產生以及新節日的出現。這我們在文末還會多加討論。

特徵工程

新增【DayofWeek】、【Month】以及【Special】(特殊節日)

星期跟月份很簡單,直接從【Date】裡面取出就行了。特殊節日則是直接hardcode。以2018為例,0代表放假,1代表普通日,2代表補班。

用【平均車站單日人流】取代【Station】

這是我嘗試過不同處理方法(例如One Hot Encoding)之後選出來最好的方法。

平均車站單日人流就是在所有訓練資料當中,該車站的人流加總÷所有天數。用這個數來代表車站最能夠體現各車站之間的人流差距。

在做這件事之前,我們必須要先用時間分出訓練資料(Train data)、驗證資料(Validation data)跟測驗資料(Test data),並且只用Train data做【Station】的轉換。這邊不使用一般的Train Test Split是因為使用時間先後分資料的話,我們才是真正在使用過去的資料預測未來的資料,【Station】的轉換也不會誤用到未來的資料。

6成的資料會當做訓練資料。(整個2018年以及2019的一、二月左右)

2成的資料會當做驗證資料。(2019的三月到七月左右)

2成的資料會當做測驗資料。(2019的八月到年底左右)

接著我們把Station整年的累計人次取出,轉換成字典之後,再除上訓練資料的日數(440)。

非完整的list

最後,把字典的資料轉換到【Station】裡面,所有的特徵工程就大功告成嘍!

完成之後的資料長相

建模

模型訓練

資料都洗乾淨了,所以就直接丟到模型裡面!圖中Hyperparameter的選擇是我經過測試後的最佳選項。但其實根據隨機森林的特性,通常使用預設的Hyperparameter表現也能夠很好。

訓練結果

結果整體來說非常好!

R Square

解釋方法:人流變異能夠被 Feature解釋的程度。滿分為 1,如果 Model完全不管 input feature隨便預測的話是 0分,如果 Model表現比隨機還差的話可以是負值。

我的Model結果是0.986!我選擇的Feature跟人流之間確實有很強的的關聯性。

最下方的數字為R Square

RMSE(Root Mean Squared Error)

解釋方法:平均每次預測的誤差。注意!這個數值很容易被離群值影響,尤其北捷資料離群值很嚴重,這個數值應該被遠遠高估。

我的Model的RMSE是162!乍看之下有點大,但我們先看看它跟Baseline的比較。

Baseline:假設我們直接使用訓練資料裡面每一筆資料的人流平均進行預測,得到的RMSE會是1306。

隨機抓20筆資料來看

從這邊就可以看出,其實大部分誤差根本不到162,而且母數越大誤差越大。RMSE確實是被離群值拉大了。

大部分預測都很到位

視覺化

最後,我們直接隨機選擇一段時間的資料畫圖看看。從這裡就可以看出,模型的表現是很好的!

藍線是實際值,紅點是預測值。紅點在線上基本上就代表預測準確。

下一步的討論

模型的實用性

雖然看起來模型表現不錯,但是這是因為2018年到2019年並沒有發生任何會嚴重擾亂大家搭捷運的事件。2021年的疫情爆發,這個模型就會不準。2020年環狀線開始營運,模型也會不準。這時候該怎麼辦?

最好的方式還是建一個Dynamic Model。模型必須能夠每天更新最新資料並且重新訓練,跟著大趨勢的變化一起調整,才能夠隨時準確的預測。當然,還是會有時間剛開始發生的一小段陣痛期!不過長遠來看這個方法是可行的。(北捷願意付錢的話我就做hehe)

模型優化

這個模型還能更好嗎?或許可以從Hyperparameter的調整來試試看。因為硬體設備限制,我沒辦法進行大動作的Tuning,但是我覺得其實效果還是有限,畢竟隨機森林通常預設Hyperparameter就會表現得很好了。

模型部署

最後就是選擇一個適合的應用,進行客製化,並且部署模型!(Uber請贊助我)

結語

模型雖然表現得不錯,但是離實際部署還是有一段距離,就算部署了,要產生商業價值還是要靠策略的輔助。從一整個專案的角度來看,這模型的建立不過是冰山一角。

如果你對於這個專案有任何想法,歡迎在下方留言!

也可以透過LinkedInFB跟我connect!

--

--

Harry Cheng

Research Intern @ MBB Consulting. Data Science enthusiast.