如何在第一次天池比賽中進入Top 5%(一)

國慶節前後,公司小夥伴給我推薦了個傳說中的天池機器學習演算法大賽(賽題鏈接:機場客流量的時空分布預測),說要參加下試試。作為一名名聲在外的機器學習葉公好龍宗師,我內心一開始是拒絕的……媽蛋我也就上了幾個課會調用調用MLlib的水平啊,這怎麼突然就要來真槍實彈了,還以公司名義組了個隊,這有點虛……正好那陣子業務開發也比較忙,就一直擱置著……

轉眼到了10月中旬,正好有個下午無心幹活,又想起了這個比賽,反正新手嘛,誰不是在被虐中成長起來的,要不就來天池劃個水試試吧。說干就干,下載了數據,起了個Jupyter notebook,開始……學習pandas……

雖然之前沒參加過比賽,不過上過幾門課還是了解一些大致的流程。首先看下比賽提供的原始數據,主要就是5張表:

  • 機場中700多個WIFI AP點每分鐘的詳細連接數。這個也是最後需要預測的值。初賽比較簡單,只需要預測某一天下午15-18點之間每個AP點每10分鐘的平均連接數,總共大概是13590個預測值。
  • 機場每天的航班排班表,其中有航班號,預定起飛時間和實際起飛時間。裡面有不少缺失值。
  • 機場所有登記口和機場區域的關聯表。WIFI AP的Tag上有機場區域信息,如E1, W1, T1, EC之類,而航班表裡都是登機口比如B225,這個表就是用來做關聯的。
  • 機場每天的乘客checkin記錄表,包括航班號,起飛時間和checkin時間。根據我自己坐飛機的經驗,這個應該指的是值機?感覺值機時人不一定要在機場,而且數據本身也有很多缺失。
  • 機場每天的乘客安檢記錄表,同樣包含了航班號,安檢時間。這個感覺會靠譜一些,雖然同樣數據也有很多缺失。兩張乘客表裡的乘客ID也都有提供,不過兩個表用的ID體系不同,無法關聯。試著distinct了一下發現ID沒有重複的,也就是說無法通過ID來確定某個乘客,基本就是無用的信息。

有了原始數據,就開始搞探索性分析。嗯,看看咋導入數據,要讀取csv嘛……pandas.read_csv就可以了!果然簡單!(省略各種pandas庫方法的各種google搜索學習……)然後查看下數據質量啦(有沒有缺失值異常值),畫畫圖看看有啥分布規律啦,看看統計關聯度啦等等……這塊是我的弱項,可能因為本身數學功底比較差,對於畫圖也不是很有sense,所以也沒有什麼特別的發現。第一天基本就在pandas和matplotlib的基本使用學習中度過了。晚上試著跑了個平均值,作為第一次預測結果提交了。

10月15號,第一次結果揭曉,竟然排到了200名以內!(當時總參賽隊伍有2000多)簡直不敢相信……搞個平均值就能進10%,這比賽真是對新手太友好了……

10月16號,繼續學習pandas,做了些其它的分析比如乘客一般會在預定起飛時間前多久來機場過安檢,飛機的平均延誤情況如何,機場每天的航班排布是不是比較接近,然後略微改進了下簡單的平均,做了下花式加權平均,進行了第二次提交。然後就衝到了100名附近。感覺自己勢不可擋,已然站在了世界之巔木哈哈……

10月17號,既然平均效果都這麼好了,那要不試試ARIMA之類的經典時序預測?又學了下statsmodels庫,看了下ARIMA模型需要序列stationary……好吧那做下一階差分。然後模型擬合需要設置auto-regressive和moving-average的參數,這有點懵逼……隨便瞎猜了下可能的回歸周期,發現這個跑起來又慢效果也不怎麼好,可能更適合長期粗粒度時間序列的預測吧……所以也就暫時沒有採用。不過移動平滑這個好像是個不錯的主意,繼續加入到原先的加權平均模型中,做了第三次提交,貌似名次沒什麼變化……

10月18號,這天好像是換了數據,初賽只剩下最後6次提交了……這天花了非常多的時間,看了好些Kaggle winners solution,開始上模型了!(後來才知道我之前用的那些平均之類的方法被大家稱之為「規則」,很多情況下規則方法就可以達到不錯的成績了)添加了航班數量的feature,加上之前的時間點平均,滑動窗口,一階差分等等,用sklearn搞了個GBRT模型!離線驗證下效果也不錯,信心滿滿進行提交!果然成績一出,衝到了第6名……當時的感覺就是,一個能打的都沒有啊!(搖手指

10月19號,這天繼續瘋狂地學習著各種比賽經驗,以一種王者的姿態哈哈哈,把玩我的模型們。唔,原來隨機森林這種bagging為基礎的ensemble方法是主要降低variance的,所以每棵樹的深度不能太小,否則bias會過大……而gradient boosting主要降低bias,所以每棵樹不能太深太細……唔,原來做模型融合不只有簡單的加權平均,還可以在模型基礎上做stacking……期間借鑒了很多Kaggle前輩們在github和Kaggle kernels上的代碼,不得不表示Kaggle的分享氛圍真的是非常好啊!像我這樣首次參賽的新手也瞬間變得一招一式有板有眼起來……不過當天快到提交了還沒跑完grid search,只能隨便填了個估計參數上去預測了,又是自信爆棚提交結果!沒想到評測成績反而大幅下降……媽蛋,我是不是拿錯劇本了……

10月20號,此時的我已經殺紅了眼,前一天肯定是under fit了!於是我開啟狂暴模式,除了自己本本還用了幾台公司的虛擬機進行並發模型超參數grid search。這裡犯了不少錯誤,比如很多時候特徵工程,數據清洗之類的要遠比複雜的模型重要,有了基本模型之後花太多時間做超參數搜索其實也是非常浪費時間,而且數據量(應該做一下down sampling)和模型複雜度上去之後,跑一個簡單的cross validation都需要幾個小時,而提升很可能並不大。最要命的是,真正比賽時的cross validation方法其實是很講究的,而我只是很傻很天真地使用了random split……結果就是凄慘的over fit,離線效果很好,上線效果傻逼。這還不是最悲催的,那天幾乎是通宵作戰,到起床收訓練結果的時候才發現,模型預測時需要依賴前幾個時間點的數據,而在預測集裡面這些值都是未知的,必須得邊跑預測邊把預測值填進去進行下一個點的預測,這些feature的權重還都相當高。早上6點多發現問題,7點多改完程序後一直跑到10點(每天系統評分的時間點)結果還是沒有出來。所以那天就相當於提交了個全0的結果,浪費了一次寶貴的驗證機會……

10月21號,倒數第三天了,我終於靜下心來研究總結了一下前面成績下降的原因,其實是模型over fit而不是under fit,而且CV的方法也與線上評測不一致導致預估結果過於樂觀。思考一下接下來的戰術選擇,一條戰線是完善之前的樹模型,用上xgboost(這貨比sklearn帶的GBRT快了不止一點點,果然是大殺器)跟random forest和extra trees做混合,用一個2-fold stacking,第二層採用線性回歸。然後超參數調優也學乖了點,用上了效率更高一些的bayersian optimization。Feature方面完善了下時間(數據中包含了中秋節,做了點特殊處理),AP點位置等信息,pandas的get_dummies做one hot encoding相當清爽,一行代碼搞定。最終模型的feature數大概是60來個,訓練提交完之後成績終於止血回升,不過名次還是在40名左右。(由於提交次數有限,還有很多想法沒法在比賽中驗證。賽後對這條戰線做了些進一步的優化,將第一次幾個模型的預測結果作為feature加入到原來的訓練數據中來做第二層的模型訓練和預測,最終成績可以排進前20。)

10月22號,受到幾個Kaggle分享的啟發,對幾個分類分別建立模型訓練很可能比對整體數據建一個大模型訓練效果要好,於是加了條新戰線,打算對AP點做下歸類然後分別建模。看了下貌似有Dynamic Time Wrapping做時間序列的距離函數效果不錯,結合KMeans做clustering,然後用xgboost里拿到的權重比較高的幾個feature,對幾個cluster分別建線性模型來做預測(也是受了一位KDD Cup大神的影響,她說她贏了3屆KDD,用的都是線性模型,給跪了),訓練預測速度比樹模型們快了不少。Validation方法也改成盡量跟線上一致的預測一段連續時間的值。嘗試了下L1,L2模型正則化,混合一下做了提交。這天的結果到了12名。(看我的語氣,簡直風淡雲輕!)

10月23號,最後一天了,進複賽應該很穩,心態也更加平和了……這天也是進入倒計時後難得的早早地在晚上9點就提交結果了,其它幾天基本都是晚上通宵跑模型,早上6,7點起來看結果,做提交。這天主要是看了下各天數據的相關度,果斷去掉了前面一大段的時間,只用最後一周的數據來做訓練。然後用elastic net來替代之前的手動L1,L2混合,略微調整了下超參數,就提交了。

最終截止日,我都沒有去看成績,還是微信上同事告訴我說最後衝到了第5名。前幾十名的差距都不大,能拿到這個名次還是有很大的運氣成分……

第一賽季就這麼結束了,雖然是後半程才加入,不過基本上每天都是如饑似渴地學習新知識,邊跑模型,邊看文章,跑完一輪再加點新調整,機器學習,我也學習……慢慢地也掌握了不少新技術,對數據競賽也算有了些感性認識。其中走了不少彎路,尤其是一個人蔘賽,很容易思路僵化,走到「局部最優」點上去,怎麼調都沒提高……這個時候就要果斷切換思路,好好做數據探索,可視化分析,特徵提取。放到真實應用場景中,對業務本身的理解絕對是最重要的……對於大多數的問題,模型再高大上再複雜可能也只是微量的提升,但付出的訓練,預測的時間代價都是相當大的,可能這也是為什麼Netflix當年並沒有採用大賽冠軍的演算法的原因之一吧。反觀數據質量,業務特徵這些往往是能拉開很大差距的地方。

當然,這些結論都是在不了解深度學習的我看來如此的……在比賽過程中我經常感覺我做的很多人肉工作比如特徵選取完全可以由auto-encoder來替代,而不斷提交結果不斷改進模型的行為,也像極了一個SGD+BP優化過程。我還在尋找自己作為一個人類的獨有價值所在……

總結歸納下整體的比賽「套路」:

  • 首先理解賽題,讀清楚需要提交答案的內容和格式。比賽中還是有不少選手因為沒提交符合要求的csv文件導致評測出問題的。

  • 然後要做好探索性分析,理解業務邏輯,為後面各種特徵構建和建模思路做準備。比如本次比賽中從探索分析時就能發現機場每日的航班安排都比較接近,且很多連接點的歷史數據都具有很相似的pattern,因此可以用一些簡單的規則達到不錯的預測效果。

  • 數據清洗也很重要,不過在比賽中原始數據有限定,很多數據質量低的數據集基本沒有很好的辦法去填充缺失值,所以這塊主要還是過濾掉缺失異常值的做法比較多。

  • 接下來構建特徵是影響最終結果佔比非常大的部分了。這裡一是要仔細,很多時候想法很好,但代碼實現錯了,導致成績沒有提升,既傷士氣,又會影響路線選擇的判斷。因此寫代碼實現特徵過程中一定要時刻注意驗證,寫一個小的處理函數就配上一個簡單的測試用例看下數據跑出來對不對,養成良好習慣絕對會為整體比賽進程節省不少時間。不要到很後面才發現特徵建錯了推倒重來。另外一點就是要有想像力,learning rate一開始要狂野一些,一個方法思路不奏效就果斷嘗試下其它實現,不要被困死在局部最優上。

  • 做feature本身耗時耗力,所以開始幾天完全可以邊做feature,邊用簡單的規則進行預測提交,不要浪費線上評測的機會嘛!順便也可以摸摸底,看看真正線上測試集的大致情況,後面很多所謂的「經驗值」調優都依賴這些「提交測試」。在整個比賽過程中,真實的評測成績都是很重要的輸入信息,盡量不要浪費。而且大多數時候想法都要比驗證機會多,好好規劃提交策略。

  • 做模型的部分應該是很多初學者,比如我,最喜歡花時間的地方了。不過越到後面越發現其實模型影響並不是決定性的,質量好的數據,構建合理的特徵,用線性回歸跑跟用GBM跑很可能差距並不太大。用GBM的默認參數跑跟用花了很大力氣搜索出來的「最優參數」跑可能也不會有特別大的提升,更別提後面加了新feature可能還要重新搜索……所以這部分的調優完全可以放在比賽中後期再搞。當然如果用深度學習之類……可能情況會不太一樣?需要花很多功夫在網路結構上……我也沒搞過哈哈。
  • 盡量建立完善的離線驗證體系,如果能做到離線驗證有效果提升,上線也能同步提升,那就再好不過了。當然很多時候是達不到的,只能盡量去接近這個目標。就拿本次比賽來說,如果只用簡單的隨機split做cross validation,效果要比線上那種連續預測未來3小時的未知值效果好很多。所以後面我還是採用了預測同樣3小時連續時間點的方式來做,盡量選取當天pattern接近的3小時,或者前幾天同時間段的3小時來做。做離線驗證時也要注意不要在訓練時不小心把要驗證的數據也扔進去訓練了……哈哈,新手易犯。
  • 最後嘛,多看看別人的比賽分享,從相似的賽題中尋找靈感,不要連著寫代碼太久,效率降低而且出錯率也會變高。注意勞逸結合,畢竟比賽基本上都有1個月的時間,搞不好哪天洗澡時想出一個好的特徵就逆襲了呢!

嗯,這樣看上去總算切題了一些!

附幾個當時看到的不錯的學習資源:

某Kaggle大神的參賽示範代碼:ChenglongChen/Kaggle_CrowdFlower

Kaggle某次類似時間序列預測比賽的賽後分享和討論:Walmart Recruiting - Store Sales Forecasting

Phunter大神分享的比賽腳本模板:Flavours of Physics: Finding τ → μμμ

來自北大的著名天池選手的github(初賽第二名):wepe (wepon)

跟上面的大牛一起組隊的另一隻大牛的博客:DataCastle微額借款用戶人品預測大賽冠軍思路

ensemble必讀:mlwave.com/kaggle-ensem

xgboost調參指南:analyticsvidhya.com/blo

復旦小鮮肉的Kaggle比賽指南:dnc1994.com/2016/04/ran

時間序列特徵提取庫:github.com/blue-yonder/

一篇關於訓練數據量對結果影響的論文:web.mit.edu/vondrick/bi

後面還有第二賽季,有空再寫吧。

推薦閱讀:

數據挖掘系列篇(18):走進Facebook公司內部,動態消息演算法揭秘
模型優化不得不思考的幾個問題
監督學習中各演算法優缺點及應用場景概覽
哪種隨機數生成演算法最適合遊戲使用?
Weighted linear matroid parity問題

TAG:大数据 | 算法 | 机器学习 |