最近這個月參加了DataCastle上的科大訊飛AI營銷算法大賽,最后的名次是97 / 1086萧求,沒能進入復賽(要求前50名)其兴。其實也沒什么好失落的,已經(jīng)盡力了夸政,這就是我現(xiàn)階段的真實水平元旬。最大的遺憾,應該是比賽結束前三天才知道這個比賽竟然有交流用的QQ群!感覺錯過了一個億仿便!這段時間里總是在想,DataCastle這個平臺怎么做得這么差卷玉,競賽圈里沒有人發(fā)帖,沒有交流镀岛,只能靠自己和隊友埋頭苦干耻瑟。后來才知道原來大家天天都在群里交流心得姓言。。唉需纳,生活就是遺憾的藝術吧。
Anyway狞谱,還是有一些收獲的叭莫,寫篇博客復盤一下刊橘。等復賽的最后結果開源之后颖医,應該會有更多的收獲吧贮缕,到時候再更新一下。
一些自問自答
- 似乎這一個月里其實也沒做多少內(nèi)容矛市?
是的。我原本以為自己投入了很多的精力,做了很多事情,今天寫完博客之后才發(fā)現(xiàn)档押,其實也沒做什么革娄。可能是在給將來積累知識和經(jīng)驗吧露戒,終究是能夠用得上的椒功,避免今后掉進相同的坑里。
- 有沒有學到什么關于廣告業(yè)務的知識玫锋?
并沒有蛾茉。我到今天都不知道數(shù)據(jù)特征里的創(chuàng)意(creative)和一級頻道(f_channel)到底指的是什么,而且這個比賽的所有變量都是脫敏過后的撩鹿,能看到的都是一堆亂碼谦炬,都是諸如ag_2100040,724495373286, B4734117F3 這樣的數(shù)據(jù)键思,場景的代入感很差础爬。我也不必要知道某個變量是什么意思,知道它是categorical feature還是numeric feature就夠了吼鳞。
我并不知道該怎么樣把廣告的點擊率給提升上去看蚜,我所做的事情,更多是找到一種判斷方法赔桌,判斷什么樣的廣告點擊率高供炎,什么樣的廣告點擊率低,僅此而已疾党。
- 寫代碼的水平有沒有提升音诫?
有提升,但提升不大雪位。雖然我寫的代碼有一千多行竭钝,但都很簡單,就是些常見的pandas的操作罷了雹洗。要說有什么新的香罐,應該是學了一下python中的try..except...
以及continue
、next
等語句的寫法时肿。之前參加kaggle的home credit default risk競賽的時候庇茫,接觸到了一些大型的工程,十個py文件嗜侮,四千多行代碼港令,層層疊疊的復雜依賴關系,這次比賽里沒有用到锈颗,因為數(shù)據(jù)量小顷霹,我覺得沒啥必要,徒增麻煩击吱。
- 對特征工程有什么新的理解淋淀?
似乎生成新的特征并不需要對實際業(yè)務有多么深刻的理解,常見的套路就是求各種各樣的統(tǒng)計量覆醇,count/ unique/ sum/ mean/ var等等朵纷,如此種種,感覺就是暴力一把梭永脓,把所有的可能都給枚舉出來袍辞,窮盡各種可能,然后用高性能的機器去遍歷常摧,尋找出好的特征搅吁。當然威创,我的這個理解可能是錯的,因為我并不擅長創(chuàng)造新特征谎懦,我的想象力太差了肚豺,希望這個比賽最終結束之后能有新的觀點吧。
-
為什么要參加比賽界拦?
- 可以把平時學習的東西綜合應用到實際中吸申,能強化理解,能鍛煉自己享甸;
- 有利于自己實現(xiàn)轉行截碴,打比賽的時候別人才不管你是什么學科背景的呢,提交分數(shù)高才是王道枪萄;
- 比賽的數(shù)據(jù)都是來自于真實世界隐岛、真實業(yè)務場景下的寶貴的真實數(shù)據(jù),我對這樣的數(shù)據(jù)沒有抵抗力瓷翻,我想觸碰真實世界,不想在模擬的數(shù)據(jù)集里鬧騰割坠;
- 和選手們進行交流和討論齐帚,結交數(shù)據(jù)科學圈內(nèi)的新朋友,積累人脈彼哼,逐步實現(xiàn)轉型;
- 提交成績和排名榜的更新都是實時的,就像是《頭號玩家》的排名榜塞淹,我覺得很刺激耙饰,很有趣,像是大家在一塊兒打游戲拴签;
- 覺得自己在數(shù)據(jù)科學這塊領域中的核心競爭力是什么孝常?
暫時還沒有。硬要說有的話蚓哩,大概就是自己對于數(shù)據(jù)科學的熱情和對未知事物的好奇心吧构灸。
- 參加比賽有沒有覺得沮喪的時候?
有岸梨,很多很多喜颁。做的實驗有很多都是失敗的,經(jīng)常實現(xiàn)不了預期的效果曹阔,成績長期沒有提升半开。最沮喪的時候是發(fā)現(xiàn)自以為學到了很多新知識,把成果拿出來和別人PK赃份,這才發(fā)現(xiàn)原來自己是在自娛自樂寂拆。
- 下一步有什么打算?
還沒想好。不過不太想做數(shù)據(jù)挖掘類(回歸和分類)的比賽了漓库,想嘗試一下深度學習(計算機視覺)方向的比賽恃慧。數(shù)據(jù)挖掘比賽做久了,感覺什么都是玄學渺蒿,感覺做什么都有data leakage痢士,很多東西都難以解釋難以理解,有種flying blind的感覺茂装。
比賽內(nèi)容
來自官方的背景介紹:科大訊飛AI營銷云在高速發(fā)展的同時怠蹂,積累了海量的廣告數(shù)據(jù)和用戶數(shù)據(jù),如何有效利用這些數(shù)據(jù)去預測用戶的廣告點擊概率少态,是大數(shù)據(jù)應用在精準營銷中的關鍵問題城侧,也是所有智能營銷平臺必須具備的核心技術。本次大賽提供了訊飛AI營銷云的海量廣告投放數(shù)據(jù)彼妻,參賽選手通過人工智能技術構建預測模型預估用戶的廣告點擊概率嫌佑,即給定廣告點擊相關的廣告、媒體侨歉、用戶屋摇、上下文內(nèi)容等信息的條件下預測廣告點擊概率。希望通過本次大賽挖掘AI營銷算法領域的頂尖人才幽邓,共同推動AI營銷的技術革新炮温。
簡單來說就是道兩分類的機器學習題,target變量為用戶是否點擊該廣告(1為點擊牵舵,0為未點擊)柒啤。初賽的數(shù)據(jù)量級是,訓練集100萬樣本畸颅,測試集4萬樣本担巩,特征一共有34個。這份比賽數(shù)據(jù)比較特別的一點是重斑,幾乎所有的特征都是categorical feature兵睛,所有特征大致可以分為下面四類:
可以看到,除了province/ creative_width/ creative_height/ time這四個特征之外窥浪,所有的特征都是categorical的祖很,說白了就是,幾乎所有的特征都是id漾脂,各種各樣的id假颇。
比賽的評估標準是logloss,越低越好骨稿。
比賽進程
初期探索
參賽的第一天笨鸡,主要是熟悉了一下數(shù)據(jù)的構成姜钳,做了一些簡單的EDA,然后就是清洗一下make(手機品牌)和model(手機型號)這兩個特征形耗。說實在的哥桥,科大訊飛給的這套數(shù)據(jù),不太干凈激涤,感覺他們收集用戶數(shù)據(jù)的時候沒有統(tǒng)一的標準和規(guī)則拟糕,很雜很亂(其他公司的數(shù)據(jù)也未必好到哪里去...)。
以特征make(手機品牌)為例倦踢,顯然APPLE/ Apple/ apple/ iPhone這四個都是蘋果手機送滞,于是我把這四類樣本給合并為Apple,而且辱挥,make特征本應該是各種手機廠商犁嗅,但是卻混進來很多的手機型號,比方說這個特征里有MI 8/ MI 6/ MI MAX 2/ MI Note Pro等晤碘,身為小米腦殘粉的我當然知道這些都是小米手機褂微,于是我把這些取值全部修改為了Xiaomi,但是哼蛆,我顯然不可能知道世界上所有的手機型號蕊梧,而這個make特征里又恰恰混進了很多的手機型號,于是我花了兩三個小時腮介,通過Google來搜索某個型號屬于什么品牌,然后修改其取值端衰。
make特征的缺失值大概是10萬叠洗,占總樣本量的10%左右,查看了一些缺失的樣本過后旅东,我發(fā)現(xiàn)其中一部分樣本的缺失值可以很科學地進行填補灭抑。比方說,有一個樣本抵代,它的make特征是缺失的腾节,但是model卻顯示為OPPO R7Plus,所以這款手機的品牌顯然是OPPO荤牍。這樣的樣本有很多案腺,我按照這種通過手機型號來推斷手機品牌的方法,進行缺失值填補康吵,最后make特征的缺失量從10萬減少到了不到3萬劈榨,而剩下的這些缺失值就沒法填補了。
model特征(手機型號)的問題在于數(shù)據(jù)錄入錯誤晦嵌,比方說同辣,里面同時存在著OPPO+R11/ OPPO-R11/ OPPO R11這三類手機型號拷姿,但是顯然這是同一類型號,只不過系統(tǒng)把空格錯誤地處理為了+號和-號旱函,這樣的錯誤幾乎無處不在响巢,所以我又花了幾個小時的時間來進行修正。沒有什么技術難度(就是一些簡單的pandas操作)棒妨,就只是繁瑣罷了踪古。說起來,看著數(shù)據(jù)不斷地變得干凈靶衍、統(tǒng)一灾炭,心里還挺滿足的。
osv(手機操作系統(tǒng)版本)這個特征也花了一些時間來清洗颅眶。我們知道蜈出,現(xiàn)在市面上就兩款操作系統(tǒng),谷歌的android和蘋果的ios涛酗,(塞班已經(jīng)停止發(fā)展了铡原,這套數(shù)據(jù)里也沒有塞班的手機)。安卓的最新版本是9商叹,但是絕大多數(shù)用戶的版本都是8燕刻;ios現(xiàn)在最新版是12,但這套數(shù)據(jù)里絕大多數(shù)蘋果用戶的版本都是ios 11剖笙。理論上來說卵洗,osv這個特征的最大取值應該是12,因為最新版本號也就只有12弥咪,但是數(shù)據(jù)里卻出現(xiàn)了很多的21/ 22/ 23這樣的取值过蹂。查了一下資料,原來這是安卓手機的API level聚至,它和安卓版本號大致是一種一一對應的關系:
于是我按照這張表統(tǒng)一了安卓手機的版本號酷勺,合并了API level。蘋果手機倒是沒有這樣的數(shù)據(jù)問題扳躬,它主要的錯誤在于寫法不統(tǒng)一脆诉,比如,數(shù)據(jù)里有11.4.1/ 11_4_1/ iOS 11.4.1 / iOS11.4.1/ iPhone OS 11.4.1這五類樣本贷币,顯然都是同一個系統(tǒng)版本(所以我說科大訊飛的數(shù)據(jù)不太干凈)击胜。我清洗了這類錯誤。
上面說的make/ model/ osv這三個特征是出問題比較多片择,比較不干凈的特征潜的,所以花了一些時間來認真地清洗,其他特征的問題不大字管。
在做了一些基本的清洗之后啰挪,我開始使用LightGBM模型來跑算法信不。LightGBM除了運算速度非常快以外亡呵,還有一個優(yōu)點抽活,就是它能夠直接處理categorical feature,不用把它們進行one hot encoding轉化為維度很高的稀疏矩陣锰什,這也是我非常喜歡LightGBM的原因之一下硕。
我在比賽第一天就提交了結果,當時線下的交叉驗證分數(shù)是0.4236汁胆,但是提交之后的線上分數(shù)只有0.43419梭姓,遠遠不如線下的成績。事實證明嫩码,線上分數(shù)比線下分數(shù)要差誉尖,從第一天第一次提交結果開始,就一直困擾著我铸题,直到現(xiàn)在铡恕,直到此時此刻都不明白是為什么。因為我提交得很快丢间,所以當時的排名是第11名探熔,這是我參賽以來的最好成績了,從那以后就一直在退步烘挫。事實證明诀艰,提交得快沒啥用,暫時排名是靠前是因為大佬們都還沒提交呢饮六,真正重要的是提交性能好的結果涡驮。
線上線下的分數(shù)差距
接下來的時間里,除了進一步清洗數(shù)據(jù)之外喜滨,就是在思考,為什么線上的分數(shù)比線下的分數(shù)差這么多撤防。線下的交叉驗證分數(shù)顯然是一個過分樂觀的分數(shù)虽风,線上的分數(shù)則是冷冰冰的現(xiàn)實。我使用交叉驗證的模式寄月,得到的預測結果的線上線下分數(shù)差距是100個萬分點辜膝,我想要縮小這個差距。
我的一個猜想是漾肮,驗證集和測試集的數(shù)據(jù)分布不一致(查了不少資料厂抖,大家都是這么歸因的),驗證集的分數(shù)過于樂觀克懊,沒能generalize到測試集上面忱辅。
我在競賽圈里也問了這個問題七蜘,當時一位同學告訴我說,這個比賽實際上是具有時間序列屬性的墙懂,訓練集是第1~7天橡卤,測試集是第8天,所以不能用交叉驗證损搬,更理想的做法應該是用前6天的數(shù)據(jù)做訓練碧库,第7天做驗證,第8天做測試巧勤。
How (and why) to create a good validation set 里也提到了這一點:
我能理解為什么不應該做交叉驗證嵌灰,因為你不能夠用未來的數(shù)據(jù)做訓練,而把過去的數(shù)據(jù)做驗證颅悉,這不是現(xiàn)實場景下的應用方案沽瞭。我理解,在道義上签舞,是不應該做交叉驗證的秕脓,可是,同樣一套數(shù)據(jù)儒搭,我用交叉驗證的分數(shù)總是優(yōu)于規(guī)范的做法吠架,不管是線上分數(shù)還是線下分數(shù)。于是我想搂鲫,這套數(shù)據(jù)是時間序列的傍药,又如何?短短幾天時間內(nèi)魂仍,用戶的行為差異有這么大嗎拐辽?把八天時間看做同一天,當做是橫截面數(shù)據(jù)來做擦酌,又有什么不行的呢俱诸?
我其實不懂統(tǒng)計,對我而言赊舶,兩個數(shù)據(jù)集的數(shù)據(jù)分布不一致是一件非常抽象的事情睁搭,所謂數(shù)據(jù)分布,指的是target變量y的分布笼平?還是指所有特征X的聯(lián)合分布园骆?還是指(X, y)的聯(lián)合分布?如果是指y或者(X, y)的話寓调,這里就沒法檢驗訓練集和測試集是否分布相同锌唾,因為測試集是沒有y的。
粗淺地了解了一下夺英,似乎是有Kolomogorov-Smirnov檢驗等方法來對數(shù)據(jù)分布是否一致進行判斷晌涕,對離散變量和對連續(xù)變量的檢驗方法也各有不同滋捶,我因為在這方面沒有太多的基礎,隨后也就放棄了渐排。并且炬太,做檢驗最后得到的結果無非就是告訴你,這兩個數(shù)據(jù)集是否分布一致驯耻,并給出一個置信度亲族。這其實并不是我想要的,我想要的可缚,不是判斷這兩份數(shù)據(jù)的分布究竟是否一致(況且我做了幾天比賽之后已經(jīng)能夠比較有信心地說這兩個分布就是不一致的)霎迫,而是如何解決數(shù)據(jù)分布不一致這個問題。
根據(jù)我對現(xiàn)實世界的觀察帘靡,我認為安卓用戶和蘋果用戶在用戶行為上還是有很大的差異的知给,我查看了數(shù)據(jù),發(fā)現(xiàn)在訓練集里描姚,安卓蘋果用戶比例是91.1% / 8.9%涩赢,而在測試集里,安卓蘋果用戶比例是87.8% / 12.2%轩勘,這其實是非常大的差異筒扒。我當時心中一喜,以為找到了分布不一致的解決方案绊寻,心里想著花墩,訓練集和測試集的分布差異,肯定不止體現(xiàn)于用戶的手機系統(tǒng)這個維度上澄步,但是冰蘑,安卓手機和蘋果手機的分布比例,可以看做是整套數(shù)據(jù)分布差異的一個近似村缸,因此祠肥,通過調整用戶手機系統(tǒng)的分布,應該有助于訓練集和測試集趨向于一致梯皿。
于是我嘗試了重抽樣的方法搪柑,從整個訓練集中隨機抽取38000個蘋果手機用戶的樣本,使得訓練集的樣本量從100萬增加到了104萬索烹,這樣一來,就確保了驗證集和測試集的安卓蘋果用戶比例一致弱睦,都為87.8% / 12.2%百姓。理論上來說,這種做法應該能夠使得驗證集(線下)分數(shù)和測試集(線上)分數(shù)之間的差距縮小况木±萋#可是我在實際操作之后旬迹,我發(fā)現(xiàn)這個差距不但沒有縮小,反而是100個萬分點擴大到了120個萬分點求类。這個比賽中奔垦,選手之間的差距都非常小,前后兩人之間分數(shù)差距不到1個萬分點尸疆,而我卻把線上線下差距擴大了20個萬分點椿猎,這意味著什么?意味著我的實驗失敗了寿弱。
既然手機操作系統(tǒng)不是數(shù)據(jù)分布差異的本源犯眠,我便繼續(xù)嘗試其他的變量。我想到症革,一天24個小時筐咧,用戶在不同時間段的點擊廣告的心境、行為和狀態(tài)都是不同的噪矛,于是我檢查了一下訓練集和測試集在時間段這個特征上的分布量蕊,發(fā)現(xiàn)它們的差別還是挺大的。
于是沿用之前的做法艇挨,按照測試集的小時分布残炮,從訓練集中抽取樣本作為驗證集,確保了驗證集和測試集在時間段這個維度是一致的雷袋。運行模型吉殃,線下得到的分數(shù)非常好,0.408991楷怒,遠遠地超過了我目前所有的模型分數(shù)蛋勺,興奮地點擊了提交,卻失落地發(fā)現(xiàn)線上分數(shù)只有0.4270鸠删,線上線下的差距進一步擴大到了180個萬分點抱完。
好吧,這一次實驗又失敗了刃泡。但是想不明白為什么巧娱。也許是因為我抽樣的方法不對?也許不應該從整個訓練集中抽樣來生成驗證集烘贴,這和交叉驗證本質上沒什么區(qū)別禁添,他們都使得訓練集和驗證集都有著全部7天的數(shù)據(jù),這就陷進了使用未來的信息去預測過去的信息的錯誤中了桨踪。也許這是問題所在老翘。于是我將訓練集分為第1~6天和第7天兩部分,前一部分做訓練集,后一部分按照測試集的小時分布铺峭,抽取出一個子樣本來作為驗證集墓怀。這樣一來,我既沒有犯使用未來的信息去預測過去的信息的錯誤卫键,也確保了驗證集和測試集數(shù)據(jù)分布的一致傀履。
心想,這一次應該有所好轉了吧莉炉。然鵝現(xiàn)實是钓账,差距進一步惡化了,并且線上線下的分數(shù)都變差了呢袱。好吧官扣,再一次實驗失敗了。
這幾個實驗可以看做是我的這次比賽經(jīng)歷的一個縮影吧羞福,每次以為我就要抓住線上線下差距的本源了惕蹄,興奮地張開了手,卻發(fā)現(xiàn)我什么都沒有抓住治专,真是失落極了卖陵。
關于Data Leakage
關于廣告點擊率預測的數(shù)據(jù)挖掘比賽其實有很多很多,比如kaggle的TalkingData AdTracking Fraud Detection Challenge张峰,騰訊TSA的社交廣告算法大賽等泪蔫,相關的論文也發(fā)表了不少,當然我是沒有時間和精力把所有的資料都看一遍啦喘批,只能學一點是一點撩荣。
我在比賽初期的做法很不規(guī)范,我把整個訓練集做了特征工程之后饶深,得到了一個新的訓練集餐曹,然后才在它的基礎上做交叉驗證跑模型,下面會提到為什么這種做法不規(guī)范敌厘。
我從類似的比賽中發(fā)現(xiàn)台猴,大家都提取了一類很重要的變量:分組點擊率,比方說俱两,按照app來分組饱狂,求出每個app的平均的廣告點擊率。嗯宪彩,這樣的特征的含義還挺直觀的休讳。后來查資料的時候發(fā)現(xiàn)這種做法其實有個官方名字,叫Target Encoding:
于是開始往我的模型里加上這類變量尿孔。在比賽的第七天晚上衍腥,加入了這類變量跑算法的時候磺樱,驚訝地發(fā)現(xiàn)線下的成績竟然是0.22!和之前所有模型的分數(shù)完全不在一個檔次上婆咸!當時興奮地以為自己找到了性能非常棒的magic features,屁顛屁顛地跑去提交結果芜辕,沒想到尚骄,分數(shù)竟然是我所有模型中最差的,墊底的那一個侵续。
感覺自己在幾分鐘內(nèi)體驗了極度的興奮和極度的失落倔丈。。状蜗。
冷靜下來之后找了找原因需五,查了查Google的資料,發(fā)現(xiàn)原來是我的不規(guī)范的交叉驗證導致了data leakage轧坎。
所謂的data leakage宏邮,簡單來說就是,訓練模型的時候缸血,模型學到了不該學的東西蜜氨。舉一個例子,你是一位數(shù)學課任老師捎泻,期末考試要到了飒炎,你決定給同學們做一套模擬試題,讓他們提前熱熱身笆豁,進入狀態(tài)郎汪,但是,在出期末考試題目的時候闯狱,你不小心把幾道已經(jīng)出過的模擬試題原封不動地放進了期末考卷里煞赢,后來你發(fā)現(xiàn),大家的考試成績都出奇地好扩氢。你以為是同學們的整體實力都變強了耕驰,但事實是,你無意之間把考題給泄露出去了录豺,同學們在參與測試之前朦肘,就已經(jīng)見過考試題目了。
機器學習中的泄露也是同樣的道理双饥,模型的性能表現(xiàn)如何是用驗證集來評估的媒抠。如果,模型在進行訓練的時候咏花,就已經(jīng)見過了驗證集的數(shù)據(jù)的模樣趴生,那么它的分數(shù)會出奇地好阀趴,就像考試泄題一般好。
Data leak occurs when you aren't careful distinguishing training data from validation data. For example, this happens if you run preprocessing (like fitting the Imputer for missing values) before calling train_test_split. Validation is meant to be a measure of how the model does on data it hasn't considered before. You can corrupt this process in subtle ways if the validation data affects the preprocessing behavoir.. The end result? Your model will get very good validation scores, giving you great confidence in it, but perform poorly when you deploy it to make decisions.
評估一個模型的時候苍匆,必須選用一套該模型迄今為止從來沒有見過的數(shù)據(jù)做驗證刘急,這才是科學合理的驗證方式,只有在這種驗證方法下取得好成績的模型浸踩,才能夠真正地部署應用到實際生活之中叔汁。否則,你將會看到一個模型的分數(shù)令你十分滿意检碗,可應用到現(xiàn)實生活之中時卻表現(xiàn)得一塌糊涂据块。
我之前的做法之所以會導致data leak,是因為我在求分組點擊率的時候,是在整個訓練集(100萬樣本)的基礎上求的,這樣一來照宝,在做交叉驗證的時候咽筋,我得到的點擊率特征,既包含了訓練集的信息,也包含了驗證集的信息。模型都已經(jīng)見過了驗證集,這個驗證集苟耻,已經(jīng)沒有驗證的意義了。
在Youtube上看了一個視頻Leakage in Meta Modeling And Its Connection to HCC Target-Encoding扶檐,里面多次說到在做Target Encoding的時候要小心data leakage:
所以凶杖,規(guī)范的交叉驗證的做法應該是,將數(shù)據(jù)隨機分成五份款筑,取其中的4份(80萬樣本)智蝠,做特征工程,做target encoding奈梳,然后把處理的過程原封不動地apply到驗證集(20萬樣本)上面杈湾。這樣一來,就能確保在處理特征的時候攘须,只涉及到訓練集漆撞,不管驗證集長啥樣,都是把訓練集上的處理過程原封不動地照搬到驗證集上面于宙,這樣浮驳,對模型來說,驗證集就是一套從來沒有見過的數(shù)據(jù)捞魁,這樣的驗證是可靠的至会。
這個時候,我才知道了sklearn中的pipeline的意義所在(我之前一直不喜歡pipeline谱俭,感覺像是八股文一樣套路)奉件,pipeline能夠確保你在操作訓練集的時候宵蛀,用的是fit_transform()
,操作驗證集時用的是transform()
县貌。原來术陶,規(guī)范的操作能夠避免很多的問題,學到了煤痕。
后來的這幾天時間里瞳别,我都在進一步查閱關于data leakage的資料,然后把我的所有代碼改成規(guī)范的驗證范式來避免data leakage杭攻。其實學習的時候還挺開心的,因為不只是我這樣的初學者疤坝,即便是有著多年機器學習經(jīng)驗的數(shù)據(jù)科學家兆解,有的時候也會犯下這樣的錯誤,造成的商業(yè)價值的損失高達幾百幾千萬跑揉,我在年輕的時候認識到了這個問題的重要性锅睛,也挺好的。其實历谍,初學者也好现拒,高端大佬也好,我們所面臨的最高檢驗望侈,都是現(xiàn)實世界印蔬,只有在真實世界中表現(xiàn)良好的模型,才是真正有價值的模型的脱衙,其他的侥猬,都不重要。
比賽后期
上面說了捐韩,從第一天起退唠,我們的排名就一直在掉,到了比賽結束前三天的時候荤胁,已經(jīng)掉到270名了瞧预,最好的一次提交是在半個月前,也就是說仅政,長達半個月的時間沒有進步了垢油。這天晚上,偶然知道這個比賽原來有交流用的QQ群已旧。感覺自己錯過了全世界秸苗。。运褪。
加入了群聊之后惊楼,發(fā)現(xiàn)比賽的第一名選手把自己的baseline代碼開源了玖瘸。下載來運行了一下,好家伙檀咙,分數(shù)是0.424759雅倒,碾壓了我提交過的所有模型,直接把我的排名提高了110名弧可。
好吧蔑匣,原來自己埋頭苦干了這么久,還遠遠不及別人的baseline呢棕诵。實力的差距吧裁良。
研究這份baseline的時候,發(fā)現(xiàn)代碼不過也就100多行校套,沒有任何的數(shù)據(jù)清洗价脾,就是把所有的categorical feature進行one hot處理,得到的稀疏矩陣CSR的維度高達24000維笛匙,我原本以為我的筆記本電腦處理不了這么高維的數(shù)據(jù)侨把,但是發(fā)現(xiàn)LightGBM處理CSR矩陣非常快妹孙,和稠密矩陣的速度差不多秋柄。
玄學的地方來了,同樣的一套數(shù)據(jù)蠢正,同樣是LightGBM算法骇笔,我一直使用的是label encoding,而第一名選手用的baseline机隙,做的是one hot encoding蜘拉。從label encoding改為one hot encoding后,排名一下提升100多名有鹿。實在難以理解旭旭。
我原本以為,用one hot來處理類別變量葱跋,不是一種好的處理方法持寄,事實上這也是LightGBM放棄one hot的原因,微軟的開發(fā)團隊轉而采用了many to many的方法來直接處理類別變量∮榘常現(xiàn)在看來稍味,我還是缺乏經(jīng)驗,太想當然了荠卷,應該把one hot encoding和label encoding都嘗試一遍才對模庐。不過,我之前沒有加入到群聊里油宜,思路完全受阻掂碱,根本沒有往這一塊兒想怜姿,我根本不知道scipy庫的里sparse matrix運算性能這么好,以前用one hot生成的是稠密矩陣疼燥,總是容易內(nèi)存溢出沧卢。唉,早日加入到群聊里就好了醉者,可以少走很多彎路但狭,可以多學一些知識,真遺憾撬即。
另外立磁,baseline對于用戶標簽user tags這個特征的處理是把它當成文本,統(tǒng)計詞頻剥槐,用的是sklearn里的CountVectorizer和TfidfVectorizer息罗,這也是我之前所不知道的,想了很長時間也不知道該怎么處理這個特征才沧。
所以最后三天的時間里,我所做的事情就是在這份baseline的基礎上做提升绍刮。
上面說了温圆,我花了不少時間去清洗手機型號/ 手機品牌/ 系統(tǒng)版本號這三個特征,于是我在baseline的前面加入了這一部分清洗的代碼孩革,結果岁歉,玄學的地方又出現(xiàn)了,成績竟然變差了膝蜈?锅移??也就是說饱搏,我把一些錯誤的數(shù)據(jù)給糾正過來之后非剃,反而導致模型的性能變差了?推沸?臟的备绽、亂的數(shù)據(jù)通過清洗變得干凈、統(tǒng)一之后鬓催,分數(shù)反而變差了肺素??實在是難以理解宇驾,這就意味著倍靡,根本不需要做任何data cleaning,直接把最初的原始數(shù)據(jù)送到模型里跑就行了课舍?這簡直是顛覆了我的認知塌西,太玄學了他挎。
接著,我造了不少groupby之后的分組統(tǒng)計特征雨让,然后逐個加入到模型中雇盖,如果這個特征使得分數(shù)提高了,那么我就認為它是一個好的特征栖忠,應當保留下來崔挖。我用這種方法大概篩選十個左右的新特征,把它們一起放進模型里的時候庵寞,玄學的地方又雙叒叕出現(xiàn)了狸相,成績特么地竟然下降了,也就是說捐川,這十個特征脓鹃,每個特征單獨加到模型里之后,都能改進模型古沥,可一旦它們一塊兒進入到模型里之后瘸右,模型反而變差了。真奇怪岩齿,我所做的太颤,就是標準的feature selection的流程啊,kaggle的廣告點擊率預測比賽里盹沈,獲得第六名的選手就是這么做的啊龄章。搞不明白,真玄學乞封。
隊友給我打了一個比方做裙,好吧,玄學就是不能理解的肃晚。
比賽的最后一天里锚贱,嘗試通過stakcing來沖一下排名,用baseline跑了幾個LightGBM模型关串,搭配上之前跑的神經(jīng)網(wǎng)絡和CatBoost惋鸥,可是不管怎么搭配模型進行stacking,分數(shù)都只會下降悍缠,沒有提高卦绣,和預期完全不符(說起來,整個比賽過程中飞蚓,就沒有什么操作是能夠實現(xiàn)預期效果的)滤港。
在比賽結束前十分鐘,我們還剩下兩次提交機會,我的最好成績是0.424759溅漾,隊友的最好成績是0.424893山叮,原本打算選擇這兩次提交作為最終結果了,后來我轉念一想添履,干脆別stacking了屁倔,干脆就把這兩份文件求個加權平均吧。
驚喜暮胧!加權平均之后成績上到了0.424691锐借,排名提高了一些,然后隊友告訴我往衷,評價指標logloss是越低越好钞翔,不是越高越好,應該用調和平均席舍,而不是加權平均布轿,于是我用了最后一次提交機會。成績又提高了来颤!0.424689汰扭,比上一次又提升了。正好這兩個文件可以用來做最后的提交文件福铅。怎么說呢东且,直到比賽的最后一刻,我們都還在進步本讥,算是雖敗猶榮了吧。鲁冯。拷沸。
我們的團隊最好成績,是在比賽結束前一分鐘提交的薯演,可以說是很迷了撞芍。如果能早點加入到群聊里和其他選手交流,說不定能夠取得更好的成績跨扮。
初賽結束序无,第二天換榜之后,我們的排名上到了第97名衡创,也就是我們的最終排名了帝嗡。算是很幸運了吧,看來有不少的選手過擬合了璃氢,換榜之后分數(shù)就掉了下來哟玷,主要還是數(shù)據(jù)量級太小了,分數(shù)不夠穩(wěn)定一也。
關于LightGBM
機器學習里有一個著名的NFL定理(No free lunch theorem巢寡,天下沒有免費的午餐):沒有任何一種算法喉脖,在任何場合下都能碾壓其他的算法,現(xiàn)實情況一定是抑月,在特定的某種場合下树叽,這種算法效果最好,在另一種場景下谦絮,另一種算法效果更好题诵。這很好理解,合適的才是最好的嘛挨稿。我認可這個定理仇轻,但我不能理解的是,為什么在數(shù)據(jù)挖掘競賽中獲得冠軍的算法奶甘,通常都是以LightGBM/ XGBoost為代表的GBDT類算法篷店,在比賽中,你很難遇到性能比它們更優(yōu)秀的其他算法臭家。
我第一次接觸LightGBM是在我第一次參加數(shù)據(jù)挖掘競賽(kaggle的Home Credit Default Risk)的時候疲陕,那時候發(fā)現(xiàn)大家用的都是這個模型,我也就跟風用了(另外一個原因是钉赁,我當時才接觸數(shù)據(jù)挖掘不久蹄殃,懂的模型很少,我不知道在處理真正的大數(shù)據(jù)時你踩,工業(yè)上用的都是什么模型)诅岩,后來也稍微嘗試用了一下其他的模型,這才發(fā)現(xiàn)LightGBM模型是如此優(yōu)越带膜,它運行得很快吩谦,能夠自動填補缺失值,跑出來的結果都很好膝藕,(又好又快可以說是機器學習模型的終極目標了吧式廷,LightGBM做到了),所以它非常適合用于做實驗芭挽,如果你有什么新的idea滑废,使用LightGBM來檢驗一下,再合適不過了袜爪。參加過數(shù)據(jù)挖掘比賽的人都知道蠕趁,快速迭代和快速實踐是成功的關鍵之一。
我似乎是陷入了一個舒適圈里辛馆,覺得LightGBM就是最好的,我喜歡用它,我依賴用它倔韭。我似乎越來越不愿意嘗試去用別的算法和模型了术浪,并且思考問題的視角也越來越往LightGBM上面靠。這樣不好寿酌,得改胰苏,否則視野會越來越狹隘,思維會逐漸枯死醇疼。還有很多的新事物值得探索硕并。