【白话机器学习】算法理论+实战之Xgboost算法
1. 寫在前面
如果想從事數(shù)據(jù)挖掘或者機(jī)器學(xué)習(xí)的工作,掌握常用的機(jī)器學(xué)習(xí)算法是非常有必要的,在這簡單的先捋一捋, 常見的機(jī)器學(xué)習(xí)算法:
監(jiān)督學(xué)習(xí)算法:邏輯回歸,線性回歸,決策樹,樸素貝葉斯,K近鄰,支持向量機(jī),集成算法Adaboost等
無監(jiān)督算法:聚類,降維,關(guān)聯(lián)規(guī)則, PageRank等
已發(fā)布:
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之K近鄰算法
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之決策樹
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之樸素貝葉斯
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之支持向量機(jī)(SVM)
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之AdaBoost算法
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之K-Means聚類算法
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之PCA降維
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之EM聚類
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之關(guān)聯(lián)規(guī)則
【白話機(jī)器學(xué)習(xí)】算法理論+實(shí)戰(zhàn)之PageRank算法
這個系列已經(jīng)基本包含了上面這些算法的原理和基本使用。但是,如果僅僅是會用這些算法可是不夠的, 我們也得跟著時代的步伐前進(jìn),近幾年,有很多大佬又在上面的某些算法上加以改進(jìn),發(fā)明了更加厲害的算法,而這些算法才是當(dāng)今時代解決問題的主流,所以我們學(xué)習(xí)的一個方式就是掌握傳統(tǒng),而又得緊跟時代。
所以,后面考慮加上當(dāng)前流行的一些主流機(jī)器學(xué)習(xí)算法,既當(dāng)復(fù)習(xí),又當(dāng)提升。由于不想和傳統(tǒng)的機(jī)器學(xué)習(xí)算法混合起來,故稱之為番外,也是傳統(tǒng)機(jī)器學(xué)習(xí)算法的延伸, 同樣是盡量白話,同樣是豐富實(shí)戰(zhàn),但會夾雜數(shù)學(xué)的身影,畢竟后面的很多算法如果沒有了數(shù)學(xué)就仿佛失去了靈魂,無法活靈活現(xiàn)。所以機(jī)器學(xué)習(xí)算法的故事還沒有完,我們還得繼續(xù)走著。
學(xué)習(xí)算法的過程,獲得的不應(yīng)該只有算法理論,還應(yīng)該有樂趣和解決實(shí)際問題的能力!
今天分享的這個算法堪稱數(shù)據(jù)科學(xué)競賽界的神器, 它似乎是用于贏得數(shù)據(jù)科學(xué)競賽的分類器/預(yù)測器必不可少的算法, 那就是Xgboost。聽這個名字,你可能一下就想到了傳統(tǒng)機(jī)器學(xué)習(xí)算法里面的AdaBoost,哈哈,聯(lián)想和對比才能更加理解算法的精華。你還別說,這個算法和那個來自于同一個家族,都是集成學(xué)習(xí)算法,都屬于boosting流派,但是兩者的boosting采用了不同的策略,而就是這策略的不同,導(dǎo)致xgboost成了目前競賽者眼中的紅人,它是目前最快最好的開源 boosting tree 工具包,比常見的工具包快 10 倍以上, 那么xgboost到底采用了什么策略呢?它又是如何做到高準(zhǔn)確率和高速度的呢?Xgboost和AdaBoost到底有什么不同呢?Xgboost又如何來解決實(shí)際問題呢??這些問題,在這篇文章中都會一一來解剖。
大綱如下:
Xgboost?這個故事還得先從AdaBoost和GBDT說起
Xgboost的基本原理(基于例子我們來看看好玩的公式推導(dǎo))
Xgboost的實(shí)戰(zhàn)應(yīng)用(這里用xgboost做一個分類任務(wù),然后說一下基本使用和高級功能)
Ok, let's go!
2. Xgboost? 這個故事還得先從AdaBoost和GBDT說起
我覺得,學(xué)習(xí)一個算法的時候,有時候不能直接單拿出一個算法來說,這樣感覺顯得突兀了些,不知道突然從哪冒出來一樣。所以,講Xgboost之前,我想先帶你回顧一下我們之前的集成學(xué)習(xí)。
所謂集成學(xué)習(xí),就是指構(gòu)建多個弱分類器對數(shù)據(jù)集進(jìn)行預(yù)測,然后用某種策略將多個分類器預(yù)測的結(jié)果集成起來,作為最終預(yù)測結(jié)果。在這里就不再講了(可以理解成集成學(xué)習(xí)是一種把大家伙叫到一塊,集思廣益想辦法解決問題的方式吧),在這里想說的是集成學(xué)習(xí)的那兩大流派:Boosting和Bagging。
怎么還有兩個流派呢?集思廣益不就完事?哈哈, 集思廣益也有不同的方式嗎?比如針對同一個問題,把問題劃分成不相干的子問題,然后分派給不同的人各干各的是一種, 或者同一個問題,劃分成串行的子問題, 先由一個人解決一部分,解決不了的,后面的人再來這又是一種。把上面這兩種方式用官方的語言描述就是:根據(jù)各個弱分類器之間有無依賴關(guān)系,分為Boosting和Bagging。
Boosting流派,各分類器之間有依賴關(guān)系,必須串行,比如Adaboost、GBDT(Gradient Boosting Decision Tree)、Xgboost
Bagging流派,各分類器之間沒有依賴關(guān)系,可各自并行,比如隨機(jī)森林(Random Forest)
關(guān)于Bagging流派的Random Forest(隨機(jī)森林)算法,也是比較常用的,簡單的說就是各個弱分類器是獨(dú)立的、每個分類器在樣本堆里隨機(jī)選一批樣本,隨機(jī)選一批特征進(jìn)行獨(dú)立訓(xùn)練,各個分類器之間沒有啥關(guān)系, 最后投票表決, 這個在這里不做詳述,后面遇到的時候再統(tǒng)一總結(jié),今天的主角是Xgboost,所以我們主要是了解一下Boosting流派, 這這里面的最具代表性的算法之一就是AdaBoost, 這個我這里不做過多的表述,詳細(xì)的可以看一下專欄之前的文章, 這里只回顧一下它的算法原理,這樣好引出后面的GBDT和Xgboost,并且可以進(jìn)行策略上的對比。
AdaBoost,是英文"Adaptive Boosting"(自適應(yīng)增強(qiáng)),它的自適應(yīng)在于:前一個基本分類器分錯的樣本會得到加強(qiáng),加權(quán)后的全體樣本再次被用來訓(xùn)練下一個基本分類器。同時,在每一輪中加入一個新的弱分類器,直到達(dá)到某個預(yù)定的足夠小的錯誤率或達(dá)到預(yù)先指定的最大迭代次數(shù)。白話的講,就是它在訓(xùn)練弱分類器之前,會給每個樣本一個權(quán)重,訓(xùn)練完了一個分類器,就會調(diào)整樣本的權(quán)重,前一個分類器分錯的樣本權(quán)重會加大,這樣后面再訓(xùn)練分類器的時候,就會更加注重前面分錯的樣本, 然后一步一步的訓(xùn)練出很多個弱分類器,最后,根據(jù)弱分類器的表現(xiàn)給它們加上權(quán)重,組合成一個強(qiáng)大的分類器,就足可以應(yīng)付整個數(shù)據(jù)集了。?這就是AdaBoost, 它強(qiáng)調(diào)自適應(yīng),不斷修改樣本權(quán)重, 不斷加入弱分類器進(jìn)行boosting。
那么,boosting還有沒有別的方式呢?GBDT(Gradient Boost Decision Tree)就是另一種boosting的方式, 上面說到AdaBoost訓(xùn)練弱分類器關(guān)注的是那些被分錯的樣本,AdaBoost每一次訓(xùn)練都是為了減少錯誤分類的樣本。而GBDT訓(xùn)練弱分類器關(guān)注的是殘差,也就是上一個弱分類器的表現(xiàn)與完美答案之間的差距,GBDT每一次訓(xùn)練分類器,都是為了減少這個差距,GBDT每一次的計算是都為了減少上一次的殘差,進(jìn)而在殘差減少(負(fù)梯度)的方向上建立一個新的模型。這是什么意思呢??我可以舉個例子,假設(shè)我們?nèi)ャy行借錢,我們想讓一個決策樹系統(tǒng)來預(yù)測可以借給我們多少錢, 如果標(biāo)準(zhǔn)答案是1000的話,假設(shè)第一棵決策樹預(yù)測,可以借給我們950塊錢, 那么離標(biāo)準(zhǔn)答案的1000還差50, 效果不算好,能不能提高一些呢?我們就再加一棵決策樹,這課決策樹過來之后,看到前面的那個已經(jīng)預(yù)測到950了,只是差50, 那么我可以聚焦在這個50上,把這個殘差變得再小一些,所以第二個決策樹預(yù)測結(jié)果是30, 那么前兩棵決策樹預(yù)測結(jié)果結(jié)合起來是980, 離標(biāo)準(zhǔn)答案差20, 所以加了一棵樹之后,效果好了。那么還能不能提升呢?我再來一棵樹,發(fā)現(xiàn)殘差只有20了,那我把殘差變得再小, 結(jié)果第三個決策樹預(yù)測20, 那么這三棵樹就可以正確的預(yù)測最終的1000了。
所以GBDT就是這樣的一個學(xué)習(xí)方式了,GBDT是boosting集成學(xué)習(xí),boosting集成學(xué)習(xí)由多個相關(guān)聯(lián)的決策樹聯(lián)合決策, 什么是相關(guān)聯(lián)?就是我上面的例子:
有一個樣本[數(shù)據(jù)->標(biāo)簽]是:[(feature1,feature2,feature3)-> 1000塊]
第一棵決策樹用這個樣本訓(xùn)練的預(yù)測為950
那么第二棵決策樹訓(xùn)練時的輸入,這個樣本就變成了:[(feature1,feature2,feature3)->50]
第二棵決策樹用這個樣本訓(xùn)練的預(yù)測為30
那么第三棵決策樹訓(xùn)練時的輸入,這個樣本就變成了:[(feature1,feature2,feature3)->20]
第三棵決策樹用這個樣本訓(xùn)練的預(yù)測為20
搞定,也就是說,下一棵決策樹輸入樣本會與前面決策樹的訓(xùn)練和預(yù)測相關(guān)。用個圖來表示類似這樣:這就是GBDT的工作原理了, GBDT是旨在不斷減少殘差(回歸),通過不斷加入新的樹旨在在殘差減少(負(fù)梯度)的方向上建立一個新的模型?!磽p失函數(shù)是旨在最快速度降低殘差。
那么為啥要講GBDT呢??我先賣個關(guān)子,不妨先看一下xgboost是怎么解決問題的。這里用xgboost原作者陳天奇的講座PPT中的那個圖來看假設(shè)我想預(yù)測,這一家子人中每個人想玩游戲的意愿值。我們用xgboost解決這個問題,就是我先訓(xùn)練出來第一棵決策樹, 預(yù)測了一下小男孩想玩游戲的意愿是2, 然后發(fā)現(xiàn)離標(biāo)準(zhǔn)答案差一些,又訓(xùn)練出來了第二棵決策樹, 預(yù)測了一下小男孩想玩游戲的意愿是0.9, 那么兩個相加就是最終的答案2.9。這個其實(shí)就接近了標(biāo)準(zhǔn)答案。所以xgboost是訓(xùn)練出來的弱分類結(jié)果進(jìn)行累加就是最終的結(jié)論。
恩,你可能要拍案而起了,驚呼,這不是跟上面介紹的GBDT乃異曲同工么?事實(shí)上,如果不考慮工程實(shí)現(xiàn)、解決問題上的一些差異,xgboost與gbdt比較大的不同就是目標(biāo)函數(shù)的定義,但這倆在策略上是類似的,都是聚焦殘差(更準(zhǔn)確的說, xgboost其實(shí)是gbdt算法在工程上的一種實(shí)現(xiàn)方式),GBDT旨在通過不斷加入新的樹最快速度降低殘差,而XGBoost則可以人為定義損失函數(shù)(可以是最小平方差、logistic loss function、hinge loss function或者人為定義的loss function),只需要知道該loss function對參數(shù)的一階、二階導(dǎo)數(shù)便可以進(jìn)行boosting,其進(jìn)一步增大了模型的泛化能力,其貪婪法尋找添加樹的結(jié)構(gòu)以及l(fā)oss function中的損失函數(shù)與正則項等一系列策略也使得XGBoost預(yù)測更準(zhǔn)確。
所以,這就是我講Xgboost的故事之前,要簡單說一下AdaBoost和GBDT的原因了,這樣腦海里面是不是對xgboost不那么陌生了啊,你要知道,這三個同是屬于集成學(xué)習(xí)的boosting流派,AdaBoost叫做自適應(yīng)提升,和GBDT,Xgboost提升時采用的策略不同,前者聚焦錯誤樣本,后者聚焦與標(biāo)準(zhǔn)答案的殘差。而GBDT和Xgboost叫做boosting集成學(xué)習(xí),提升時策略類似,都是聚焦殘差,但是降低殘差的方式又各有不同。
好了,鋪墊到此為止,下面真正進(jìn)入主角部分 -- Xgboost的基本原理。
3. Xgboost的基本原理
Xgboost 的全稱是eXtreme Gradient Boosting,由華盛頓大學(xué)的陳天奇博士提出,在Kaggle的希格斯子信號識別競賽中使用,因其出眾的效率與較高的預(yù)測準(zhǔn)確度而引起了廣泛的關(guān)注。
如果boosting算法每一步的弱分類器生成都是依據(jù)損失函數(shù)的梯度方向,則稱之為梯度提升(Gradient boosting),XGBoost算法是采用分步前向加性模型,只不過在每次迭代中生成弱學(xué)習(xí)器后不再需要計算一個系數(shù),XGBoost 是由 k 個基模型組成的一個加法運(yùn)算式:
其中為第k個基模型, 為第i個樣本的預(yù)測值。那么損失函數(shù)可由預(yù)測值與真實(shí)值進(jìn)行表示:
其中n為樣本數(shù)量。
★XGBoost算法通過優(yōu)化結(jié)構(gòu)化損失函數(shù)(加入了正則項的損失函數(shù),可以起到降低過擬合的風(fēng)險)來實(shí)現(xiàn)弱學(xué)習(xí)器的生成,并且XGBoost算法沒有采用搜索方法,而是直接利用了損失函數(shù)的一階導(dǎo)數(shù)和二階導(dǎo)數(shù)值,并通過預(yù)排序、加權(quán)分位數(shù)等技術(shù)來大大提高了算法的性能。
”說到這里,估計你已經(jīng)看不下去了吧,這說的啥跟啥啊, 聽不懂啦啊!但其實(shí)我上面只是用數(shù)學(xué)的語言來說了一下前面舉得xgboost的那個例子,對于某個樣本,有若干個弱分類器做預(yù)測,最后的預(yù)測結(jié)果就是弱分類器的答案累加(注意此時沒有權(quán)重了,如果你還記得AdaBoost模型的話,會發(fā)現(xiàn)那個地方每個分類器前面會有個權(quán)重,最終分類器是,不騙你喲), 這是上面的第一個公式,第二個公式就是說我怎么判斷對于整個數(shù)據(jù)集預(yù)測的準(zhǔn)不準(zhǔn)啊,就得有個損失函數(shù)啊, 對比一下與真實(shí)值的差距,n個樣本,我都對比一下子。這個表示的某種損失函數(shù),你可以先理解成平方差損失。
如此白話,應(yīng)該能聽懂了吧, 但還沒真正講xgboost的數(shù)學(xué)原理呢, 所以后面的數(shù)學(xué)原理我打算換一種方式,從一個例子展開,剖析數(shù)學(xué)公式,這里面就全是數(shù)學(xué)的原理了,如果你感覺直接上數(shù)學(xué)壓力有點(diǎn)大,那么可以先跟著我繼續(xù)往下,從一個例子中看看xgboost樹到底是如何生成的,然后再回頭看數(shù)學(xué)原理也不遲 ;)
下面就通過算法流程圖舉一個例子來詳解xgboost樹的生成。
先給出一個流程圖, 不懂不要緊,可以看一遍后面的步驟,然后再回來:為了讓xgboost數(shù)學(xué)原理的部分不那么boring, 我們跟著一個例子走吧:
★假設(shè)我想預(yù)測學(xué)生考試分?jǐn)?shù), 給定若干個學(xué)生屬性(比如天賦,每天學(xué)習(xí)時間,是否談戀愛等),
通過一個決策樹A, 我們可以看到一個天賦屬性的預(yù)測結(jié)果:天賦高的人+90, 不高的人+60
通過決策樹B, 可以看到每天學(xué)習(xí)時間高于10小時的+5,低于10小時的-5
通過決策樹C, 可以看到談戀愛的-1, 單身狗的+1
后面依次類推,還可能有更多的決策樹通過學(xué)生的某些屬性來推斷分?jǐn)?shù)。
XGboost就是這樣一個不斷生成新的決策樹A,B,C,D…的算法,最終生成的決策樹算法就是樹A+B+C+D+…的和的決策樹。
”我們針對這個問題看看詳細(xì)的建樹過程吧:
首先,我們有三個學(xué)生, 屬性和標(biāo)簽如下:我們初始化三個樣本的考試成績預(yù)測值為0。
定義目標(biāo)函數(shù):模型的預(yù)測精度由偏差和方差共同決定,損失函數(shù)代表了模型的偏差,想要方差小則需要更簡單的模型, 所以目標(biāo)函數(shù)最終由損失函數(shù)L與抑制模型復(fù)雜度的正則項Ω組成, 所以目標(biāo)函數(shù)如下:
這個公式應(yīng)該不需要過多的解釋了吧, 其中是正則化項
前面的為葉子節(jié)點(diǎn)數(shù), 表示j葉子上的節(jié)點(diǎn)權(quán)重, ,是預(yù)先給定的超參數(shù)。引入了正則化之后,算法會選擇簡單而性能優(yōu)良的模型, 正則化項只是用來在每次迭代中抑制弱分類器過擬合,不參與最終模型的集成。(這個正則化項可以先不用管它, ?有個印象即可,后面樹那個地方會統(tǒng)一解釋)
我們下面看看這個目標(biāo)函數(shù)還能不能化簡呢?
我們知道, boosting模型是前向加法, 以第t步模型為例, 模型對第i個樣本的預(yù)測為:
其中,是第t-1步的模型給出的預(yù)測值, 是已知常數(shù),是我們這次需要加入的新模型,所以把這個代入上面,就可以進(jìn)一步化簡:
這個就是xgboost的目標(biāo)函數(shù)了,最優(yōu)化這個目標(biāo)函數(shù),其實(shí)就是相當(dāng)于求解當(dāng)前的。Xgboost系統(tǒng)的每次迭代都會構(gòu)建一顆新的決策樹,決策樹通過與真實(shí)值之間殘差來構(gòu)建。什么,沒有看到殘差的身影?別急,后面會看到這個殘差長什么樣子。
我們回到我們的例子,假設(shè)已經(jīng)根據(jù)天賦這個屬性建立了一棵決策樹A(關(guān)于如何建樹在這里不做解釋,可以看看[白話機(jī)器學(xué)習(xí)算法理論+實(shí)戰(zhàn)之決策樹]),只不過這里的樹分裂計算收益的方式換了一種,后面會具體說到。
我們有了第一棵樹, 通過這個樹的預(yù)測結(jié)果:那么我們建立第二棵樹的時候,我們是考慮的殘差,也就是樣本其實(shí)變成了下面這樣:通過最小化殘差學(xué)習(xí)到一個通過學(xué)習(xí)時間屬性構(gòu)建的決策樹得到了90+5,60+5,90-5的預(yù)測值,再繼續(xù)通過(100-95=5)(70-65)(86-85)的殘差構(gòu)建下一個決策樹,以此類推,當(dāng)?shù)螖?shù)達(dá)到上限或是殘差不再減小是停止,就得到一個擁有多個(迭代次數(shù))決策樹的強(qiáng)分類器。這個就是xgboost工作的宏觀過程了。光宏觀部分確實(shí)挺好理解,但具體細(xì)節(jié)呢?比如我每一次建樹是怎么建的呢?既然說計算收益的方式不同, ?那么我考慮分裂的時候是怎么計算收益的呢?目前你心中肯定會有這些疑問, 莫慌莫慌, 下面我把建樹的細(xì)節(jié)給你娓娓道來,不過道來的過程中得崎嶇一點(diǎn),需要用到數(shù)學(xué)的語言。
那么究竟是如何得到一棵新的樹的呢?下面就是Xgboost的精髓了。前方高能,一大波數(shù)學(xué)公式來襲, 請戴好安全帽!!!
目標(biāo)函數(shù)的Taylor化簡:這是Xgboost的精髓了。我們首先看一個關(guān)于目標(biāo)函數(shù)的簡單等式變換, 把上面的目標(biāo)函數(shù)拿過來:
我們看看這里的,這個部分,如果結(jié)合偉大的Taylor的話,會發(fā)生什么情況,你還記得泰勒嗎?
★在這里插入圖片描述”根據(jù)Taylor公式, 我們把函數(shù)在點(diǎn)處二階展開,可得到:
那么我們把中的視為, 視為, 那么目標(biāo)函數(shù)就可以寫成:
其中是損失函數(shù)的一階導(dǎo)數(shù), 是損失函數(shù)的二階導(dǎo), 注意這里的求導(dǎo)是對求導(dǎo)。
★這里我們以平方損失函數(shù)為例:
”★則對于每一個樣本:
”由于在第t步時是一個已知的值, 所以是一個常數(shù),其對函數(shù)的優(yōu)化不會產(chǎn)生影響, 因此目標(biāo)函數(shù)可以進(jìn)一步寫成:
所以我們只需要求出每一步損失函數(shù)的一階導(dǎo)和二階導(dǎo)的值(由于前一步的是已知的,所以這兩個值就是常數(shù)),然后最優(yōu)化目標(biāo)函數(shù),就可以得到每一步的 f(x) ,最后根據(jù)加法模型得到一個整體模型。
但是還有個問題,就是我們?nèi)绻墙Q策樹的話,根據(jù)上面的可是無法建立出一棵樹來。因為這里的是什么鬼??咱不知道啊!所以啊,還得進(jìn)行一步映射, 將樣本x映射到一個相對應(yīng)的葉子節(jié)點(diǎn)才可以,看看是怎么做的?
基于決策樹的目標(biāo)函數(shù)的終極化簡上面的目標(biāo)函數(shù)先放下來:
我們這里先解決一下這個的問題,這個究竟怎么在決策樹里面表示呢?那解決這個問題之前,我們看看這個表示的含義是什么, 就是我有一個決策樹模型, 是每一個訓(xùn)練樣本,那么這個整體就是我某一個樣本經(jīng)過決策樹模型得到的一個預(yù)測值,對吧?那么,我如果是在決策樹上,可以這么想, 我的決策樹就是這里的, 然后對于每一個樣本,我要在決策樹上遍歷獲得預(yù)測值,其實(shí)就是在遍歷決策樹的葉子節(jié)點(diǎn),因為每個樣本最終通過決策樹都到了葉子上去, 不信?看下圖(樣本都在葉子上, 只不過這里要注意一個葉子上不一定只有一個樣本):所以,通過決策樹遍歷樣本,其實(shí)就是在遍歷葉子節(jié)點(diǎn)。這樣我們就可以把問題就行轉(zhuǎn)換,把決策樹模型定義成, 其中代表了該樣本在哪個葉子節(jié)點(diǎn)上, 表示該葉子節(jié)點(diǎn)上的權(quán)重(上面分?jǐn)?shù)預(yù)測里面+90, +60就是葉子節(jié)點(diǎn)的權(quán)重)。所以就代表了每個樣本的取值(預(yù)測值)。?那么這個樣本的遍歷,就可以這樣化簡:
這個再解釋一遍就是:遍歷所有的樣本后求每個樣本的損失函數(shù),但樣本最終會落在葉子節(jié)點(diǎn)上,所以我們也可以遍歷葉子節(jié)點(diǎn),然后獲取葉子節(jié)點(diǎn)上的樣本集合(注意第二個等式和第三個等式求和符號的上下標(biāo), T代表葉子總個數(shù))。 由于一個葉子節(jié)點(diǎn)有多個樣本存在,所以后面有了和這兩項,這里的它代表一個集合,集合中每個值代表一個訓(xùn)練樣本的序號,整個集合就是某棵樹第j個葉子節(jié)點(diǎn)上的訓(xùn)練樣本 , 為第j個葉子節(jié)點(diǎn)的取值。只要明白了這一步,后面的公式就很容易理解了。
我們再解決一下后面那部分,在決策樹中,決策樹的復(fù)雜度可由葉子數(shù) T 組成,葉子節(jié)點(diǎn)越少模型越簡單,此外葉子節(jié)點(diǎn)也不應(yīng)該含有過高的權(quán)重 w (類比 LR 的每個變量的權(quán)重),所以目標(biāo)函數(shù)的正則項可以定義為:
即決策樹模型的復(fù)雜度由生成的所有決策樹的葉子節(jié)點(diǎn)數(shù)量(權(quán)衡),和所有節(jié)點(diǎn)權(quán)重(權(quán)衡)所組成的向量的 ?范式共同決定。這張圖給出了基于決策樹的 XGBoost 的正則項的求解方式。
這樣,目標(biāo)函數(shù)的前后兩部分都進(jìn)行了解決,那么目標(biāo)函數(shù)就可以化成最后這個樣子,看看能懂嗎?
這里的為第j個葉子節(jié)點(diǎn)的樣本集合。為了簡化表達(dá)式,我們再定義:, 那么決策樹版本xgboost的目標(biāo)函數(shù):
這里要注意和是前t-1步得到的結(jié)果, 其值已知,只有最后一棵樹的葉子節(jié)點(diǎn)的值不確定, 那么將目標(biāo)函數(shù)對求一階導(dǎo), 并令其等于0, , 則可以求得葉子節(jié)點(diǎn)j對應(yīng)的權(quán)值:
那么這個目標(biāo)函數(shù)又可以進(jìn)行化簡:
這個就是基于決策樹的xgboost模型的目標(biāo)函數(shù)最終版本了,這里的G和H的求法,就需要明確的給出損失函數(shù)來, 然后求一階導(dǎo)和二階導(dǎo),然后代入樣本值即得出。
這個代表了當(dāng)我們指定一個樹的結(jié)構(gòu)的時候,我們在目標(biāo)上最多能夠減少多少,我們之前不是說建立一個樹就是讓殘差盡可能的小嗎?到底小多少呢?這個就是衡量這個的, 可以叫做結(jié)構(gòu)分?jǐn)?shù)。就類似于基尼系數(shù)那樣對樹結(jié)構(gòu)打分的一個函數(shù)。那么這個分?jǐn)?shù)怎么算呢?看下面的例子:
還是上面的那個預(yù)測玩游戲的意愿,我們假設(shè)建了右邊的那棵樹,那么每個樣本都對應(yīng)到了葉子節(jié)點(diǎn)上去,每一個樣本都會對應(yīng)一個g和h, 那么我們遍歷葉子節(jié)點(diǎn),就會得到G和H,然后累加就可以得到這棵樹的結(jié)構(gòu)分?jǐn)?shù)obj(這里有個小細(xì)節(jié)就是假設(shè)有N個訓(xùn)練樣本, 那么就會有N次計算各自的和, 但是由于每個樣本的和沒有啥關(guān)系,所以可以并行計算,這樣就可以加速訓(xùn)練了,而且,和是不依賴于損失函數(shù)的形式的,只要這個損失函數(shù)二次可微就可以了, emmm...powerful)。
有了這個,我們就知道這棵樹建的好不好了。
上面是可以判斷出來一棵樹究竟好不好,那么建立樹的時候應(yīng)該怎么建立呢?一棵樹的結(jié)構(gòu)近乎無限多,總不能一個一個去測算它們的好壞程度,然后再取最好的吧(這是個NP問題)。所以,我們?nèi)匀恍枰扇∫稽c(diǎn)策略,這就是逐步學(xué)習(xí)出最佳的樹結(jié)構(gòu)。這與我們將K棵樹的模型分解成一棵一棵樹來學(xué)習(xí)是一個道理,只不過從一棵一棵樹變成了一層一層節(jié)點(diǎn)而已。這叫什么?emmm, 貪心(找到每一步最優(yōu)的分裂結(jié)果)!xgboost采用二叉樹, 開始的時候, 全部樣本在一個葉子節(jié)點(diǎn)上, 然后葉子節(jié)點(diǎn)不斷通過二分裂,逐漸生成一棵樹。
那么在葉子節(jié)點(diǎn)分裂成樹的過程中最關(guān)鍵的一個問題就是應(yīng)該在哪個特征的哪個點(diǎn)上進(jìn)行分裂,也就是尋找最優(yōu)切分點(diǎn)的過程。
最優(yōu)切分點(diǎn)劃分算法及優(yōu)化策略在決策樹的生長過程中,一個非常關(guān)鍵的問題是如何找到節(jié)點(diǎn)的最優(yōu)切分點(diǎn), 我們學(xué)過了決策樹的建樹過程,那么我們知道ID3也好,C4.5或者是CART,它們尋找最優(yōu)切分點(diǎn)的時候都有一個計算收益的東西,分別是信息增益,信息增益比和基尼系數(shù)。而xgboost這里的切分, 其實(shí)也有一個類似于這三個的東西來計算每個特征點(diǎn)上分裂之后的收益。
★假設(shè)我們在某一節(jié)點(diǎn)完成特征分裂,則分列前的目標(biāo)函數(shù)可以寫為:
分裂后的目標(biāo)函數(shù):
則對于目標(biāo)函數(shù)來說,分裂后的收益為(Obj1-Obj2):
”注意該特征收益也可作為特征重要性輸出的重要依據(jù)。
那么我們就可以來梳理一下最優(yōu)切分點(diǎn)的劃分算法了:
從深度為 0 的樹開始,對每個葉節(jié)點(diǎn)枚舉所有的可用特征;
針對每個特征,把屬于該節(jié)點(diǎn)的訓(xùn)練樣本根據(jù)該特征值進(jìn)行升序排列,通過線性掃描的方式來決定該特征的最佳分裂點(diǎn),并記錄該特征的分裂收益;(這個過程每個特征的收益計算是可以并行計算的,xgboost之所以快,其中一個原因就是因為它支持并行計算,而這里的并行正是指的特征之間的并行計算,千萬不要理解成各個模型之間的并行)
選擇收益最大的特征作為分裂特征,用該特征的最佳分裂點(diǎn)作為分裂位置,在該節(jié)點(diǎn)上分裂出左右兩個新的葉節(jié)點(diǎn),并為每個新節(jié)點(diǎn)關(guān)聯(lián)對應(yīng)的樣本集(這里稍微提一下,xgboost是可以處理空值的,也就是假如某個樣本在這個最優(yōu)分裂點(diǎn)上值為空的時候, 那么xgboost先把它放到左子樹上計算一下收益,再放到右子樹上計算收益,哪個大就把它放到哪棵樹上。)
回到第 1 步,遞歸執(zhí)行到滿足特定條件為止
上面就是最優(yōu)切分點(diǎn)劃分算法的過程, 看完之后,是不是依然懵逼, 這到底是怎么做的啊, 下面就看一個尋找最優(yōu)切分點(diǎn)的栗子吧:
還是上面玩游戲的那個例子,假設(shè)我有這一家子人樣本,每個人有性別,年齡,興趣等幾個特征,我想用xgboost建立一棵樹預(yù)測玩游戲的意愿值。首先,五個人都聚集在根節(jié)點(diǎn)上,現(xiàn)在就考慮根節(jié)點(diǎn)分叉,我們就遍歷每個特征,對于當(dāng)前的特征,我們要去尋找最優(yōu)切分點(diǎn)以及帶來的最大收益,比如當(dāng)前特征是年齡,我們需要知道兩點(diǎn):* 按照年齡分是否有效,也就是是否減少了obj的值 * 如果真的可以分,特征收益比較大, 那么我們從哪個年齡點(diǎn)分開呢?
對于這兩個問題,我們可以這樣做,首先我們先把年齡進(jìn)行一個排序, 如下圖:按照這個圖從左至右掃描,我們就可以找出所有的切分點(diǎn)a, 對于每一個切分點(diǎn)a,計算出分割的梯度和和 。然后用上面的公式計算出每個分割方案的分?jǐn)?shù)。然后哪個最大,就是年齡特征的最優(yōu)切分點(diǎn),而最大值就是年齡這個特征的最大信息收益。
遍歷完所有特征后,我們就可以確定應(yīng)該在哪個特征的哪個點(diǎn)進(jìn)行切分。對切分出來的兩個節(jié)點(diǎn),遞歸地調(diào)用這個過程,我們就能獲得一個相對較好的樹結(jié)構(gòu), 有了樹結(jié)構(gòu)就比較容易找最優(yōu)的葉子節(jié)點(diǎn),這樣就能對上面的樣本進(jìn)行預(yù)測了。當(dāng)然,特征與特征之間的收益計算是互不影響的,所以這個遍歷特征的過程其實(shí)可以并行運(yùn)行。
在這個過程中你是否注意到了一個問題, 就是xgboost的切分操作和普通的決策樹切分過程是不一樣的。普通的決策樹在切分的時候并不考慮樹的復(fù)雜度,所以才有了后續(xù)的剪枝操作。而xgboost在切分的時候就已經(jīng)考慮了樹的復(fù)雜度(obj里面看到那個了嗎)。所以,它不需要進(jìn)行單獨(dú)的剪枝操作。
這就是xgboost貪心建樹的一個思路了,即遍歷所有特征以及所有分割點(diǎn),每次選最好的那個。GBDT也是采用的這種方式, 這算法的確不錯,但是有個問題你發(fā)現(xiàn)了沒?就是計算代價太大了,尤其是數(shù)據(jù)量很大,分割點(diǎn)很多的時候,計算起來非常復(fù)雜并且也無法讀入內(nèi)存進(jìn)行計算。所以作者想到了一種近似分割的方式(可以理解為分割點(diǎn)分桶的思路),選出一些候選的分裂點(diǎn),然后再遍歷這些較少的分裂點(diǎn)來找到最佳分裂點(diǎn)。那么怎么進(jìn)行分桶選候選分裂點(diǎn)才比較合理呢?我們一般的思路可能是根據(jù)特征值的大小直接進(jìn)行等寬或者等頻分桶, 像下面這樣(這個地方理解起來有點(diǎn)難,得畫畫了,圖可能不太好看,能說明問題就行,哈哈):
上面就是等頻和等寬分桶的思路了(這個不用較真,我這里只是為了和作者的想法產(chǎn)生更清晰的對比才這樣舉得例子),這樣選擇出的候選點(diǎn)是不是比就少了好多了?但是這樣劃分其實(shí)是有問題的,因為這樣劃分沒有啥依據(jù)啊, 比如我上面畫的等頻分桶,我是5個訓(xùn)練樣本放一個桶,但是你說你還想10個一組來,沒有個標(biāo)準(zhǔn)啥的啊。即上面那兩種常規(guī)的劃分方式缺乏可解釋性,所以重點(diǎn)來了, 作者這里采用了一種對loss的影響權(quán)重的等值percentiles(百分比分位數(shù))劃分算法(Weight Quantile Sketch), 我上面的這些鋪墊也正是為了引出這個方式,下面就來看看作者是怎么做的,這個地方其實(shí)不太好理解,所以慢一些
作者進(jìn)行候選點(diǎn)選取的時候,考慮的是想讓loss在左右子樹上分布的均勻一些,而不是樣本數(shù)量的均勻,因為每個樣本對降低loss的貢獻(xiàn)可能不一樣,按樣本均分會導(dǎo)致分開之后左子樹和右子樹loss分布不均勻,取到的分位點(diǎn)會有偏差。這是啥意思呢?再來一個圖(這個圖得看明白了):這其實(shí)就是作者提出的那種找候選節(jié)點(diǎn)的方式(分桶的思路),明白了這個圖之后,下面就是解釋一下上面這個圖的細(xì)節(jié):第一個就是是啥?它為啥就能代表樣本對降低loss的貢獻(xiàn)程度??第二個問題就是這個bin是怎么分的,為啥是0.6一個箱?
下面從第一個問題開始,揭開的神秘面紗, 其實(shí)上面已經(jīng)說過了,損失函數(shù)在樣本處的二階導(dǎo)數(shù)啊!還記得開始的損失函數(shù)嗎?
就是這個, 那么你可能要問了, 為啥它就能代表第個樣本的權(quán)值啊?這里再拓展一下吧,我們在引出xgboost的時候說過,GBDT這個系列都是聚焦在殘差上面,但是我們單看這個目標(biāo)函數(shù)的話并沒有看到什么殘差的東西對不對?其實(shí)這里這個損失函數(shù)還可以進(jìn)一步化簡的(和上面的化簡不一樣,上面的化簡是把遍歷樣本轉(zhuǎn)到了遍歷葉子上得到基于決策樹的目標(biāo)函數(shù),這里是從目標(biāo)函數(shù)本身出發(fā)進(jìn)行化簡):
這樣化簡夠簡潔明了了吧, 你看到殘差的身影了嗎?后面的每一個分類器都是在擬合每個樣本的一個殘差, 其實(shí)把上面化簡的平方損失函數(shù)拿過來就一目了然了。而前面的可以看做計算殘差時某個樣本的重要性,即每個樣本對降低loss的貢獻(xiàn)程度。第一個問題說的聽清楚了吧 ?;)
PS:這里加點(diǎn)題外話,Xgboost引入了二階導(dǎo)之后,相當(dāng)于在模型降低殘差的時候給各個樣本根據(jù)貢獻(xiàn)度不同加入了一個權(quán)重,這樣就能更好的加速擬合和收斂,GBDT只用到了一階導(dǎo)數(shù),這樣只知道梯度大的樣本降低殘差效果好,梯度小的樣本降低殘差不好(這個原因我會放到Lightgbm的GOSS那里說到),但是好與不好的這個程度,在GBDT中無法展現(xiàn)。而xgboost這里就通過二階導(dǎo)可以展示出來,這樣模型訓(xùn)練的時候就有數(shù)了。
下面再解釋第二個問題,這個分箱是怎么分的?比如我們定義一個數(shù)據(jù)集代表每個訓(xùn)練樣本的第個特征的取值和二階梯度值,那么我們可以有一個排名函數(shù):
這里的代表特征值小于的那些樣本。這個排名函數(shù)表示特征值小于z的樣本的貢獻(xiàn)度比例。假設(shè)上面圖中,z是第一個候選點(diǎn),那么, 這個東西的目的就是去找相對準(zhǔn)確的候選點(diǎn), 這里的, 而相鄰兩個桶之間樣本貢獻(xiàn)度的差距應(yīng)滿足下面這個函數(shù):
這個控制每個桶中樣本貢獻(xiàn)度比例的大小,其實(shí)就是貢獻(xiàn)度的分位點(diǎn)。我們自己設(shè)定。比如在上面圖中我們設(shè)置了, 這意味著每個桶樣本貢獻(xiàn)度的比例是1/3(貢獻(xiàn)度的1/3分位點(diǎn)), 而所有的樣本貢獻(xiàn)度總和是1.8, 那么每個箱貢獻(xiàn)度是0.6(),分為3()個箱, 上面這些公式看起來挺復(fù)雜,可以計算起來很簡單,就是計算一下總的貢獻(xiàn)度, 然后指定, 兩者相乘得到每個桶的貢獻(xiàn)度進(jìn)行分桶即可。這樣我們就可以確定合理的候選切分點(diǎn),然后進(jìn)行分箱了。
到這終于把這一塊描述完了, 有點(diǎn)多,稍微理一理邏輯,前面那一部分是圍繞著如何建立一棵樹進(jìn)行的,即采用貪心的方式從根節(jié)點(diǎn)開始一層層的建立樹結(jié)構(gòu)(每一層爭取最優(yōu)),然后就是建樹過程中一個關(guān)鍵的問題:如何尋找最優(yōu)切分點(diǎn),給出了最優(yōu)切分點(diǎn)算法,基于這個算法就可以建立樹了。后面這一部分是一個優(yōu)化的過程,提出了一種Weight Quantile Sketch的算法,這個算法可以將原來的分割點(diǎn)進(jìn)行分桶,然后找到合適的候選分裂點(diǎn),這樣可以減少遍歷時嘗試的分裂點(diǎn)的數(shù)量,是xgboost相比于GBDT做出的切分點(diǎn)優(yōu)化策略,現(xiàn)在知道為啥xgboost要快了吧,因為xgboost尋找切分點(diǎn)的時候不用遍歷所有的,而是只看候選點(diǎn)就可以了。而且在特征上,xgboost是可以并行處理的。這樣xgboost的建樹過程及優(yōu)化策略基本上就是這些了,當(dāng)然這里面還有很多的細(xì)節(jié),由于篇幅的原因就不寫在這里了。
利用新的決策樹預(yù)測樣本值,并累加到原來的值上若干個決策樹是通過加法訓(xùn)練的, 所謂加法訓(xùn)練,本質(zhì)上是一個元算法,適用于所有的加法模型,它是一種啟發(fā)式算法。運(yùn)用加法訓(xùn)練,我們的目標(biāo)不再是直接優(yōu)化整個目標(biāo)函數(shù),而是分步驟優(yōu)化目標(biāo)函數(shù),首先優(yōu)化第一棵樹,完了之后再優(yōu)化第二棵樹,直至優(yōu)化完K棵樹。整個過程如下圖所示:
上圖中會發(fā)現(xiàn)每一次迭代得到的新模型前面有個(這個是讓樹的葉子節(jié)點(diǎn)權(quán)重乘以這個系數(shù)), 這個叫做收縮率,這個東西加入的目的是削弱每棵樹的作用,讓后面有更大的學(xué)習(xí)空間,有助于防止過擬合。也就是,我不完全信任每一個殘差樹,每棵樹只學(xué)到了模型的一部分,希望通過更多棵樹的累加來來彌補(bǔ),這樣讓這個讓學(xué)習(xí)過程更平滑,而不會出現(xiàn)陡變。這個和正則化防止過擬合的原理不一樣,這里是削弱模型的作用,而前面正則化是控制模型本身的復(fù)雜度。
好了, 到這里為止,xgboost的數(shù)學(xué)原理部分就描述完了,希望我描述清楚了吧。簡單的回顧一下上面的過程吧: xgboost是好多弱分類器的集成,訓(xùn)練弱分類器的策略就是盡量的減小殘差,使得答案越來越接近正確答案。xgboost的精髓部分是目標(biāo)函數(shù)的Taylor化簡,這樣就引入了損失函數(shù)的一階和二階導(dǎo)數(shù)。然后又把樣本的遍歷轉(zhuǎn)成了對葉子節(jié)點(diǎn)的遍歷,得到了最終的目標(biāo)函數(shù)。這個函數(shù)就是衡量一棵樹好壞的標(biāo)準(zhǔn)。在建樹過程中,xgboost采用了貪心策略,并且對尋找分割點(diǎn)也進(jìn)行了優(yōu)化?;谶@個,才有了后面的最優(yōu)點(diǎn)切分建立一棵樹的過程。xgboost訓(xùn)練的時候,是通過加法進(jìn)行訓(xùn)練,也就是每一次只訓(xùn)練一棵樹出來, 最后的預(yù)測結(jié)果是所有樹的加和表示。?
關(guān)于xgboost,依然還有很多的細(xì)節(jié)沒有說到,具體的去看論文吧。下面,我們就進(jìn)行xgboost的實(shí)戰(zhàn)部分, 這里我們簡單的做一個分類任務(wù), 主要是看看xgboost主要怎么用, 尤其是在一個數(shù)據(jù)競賽中(這次重點(diǎn)總結(jié)了一些用法)。
3. Xgboost實(shí)戰(zhàn)二分類
安裝:默認(rèn)可以通過pip安裝,若是安裝不上可以通過:
https://www.lfd.uci.edu/~gohlke/pythonlibs/
網(wǎng)站下載相關(guān)安裝包,將安裝包拷貝到Anacoda3的安裝目錄的Scrripts目錄下, 然后pip install 安裝包安裝
3.1 xgboost的基本使用
Xgboost參數(shù)說明頁面:
https://xgboost.readthedocs.io/en/latest/parameter.html
Xgboost調(diào)參官方指南
https://xgboost.readthedocs.io/en/latest/tutorials/param_tuning.html
我們使用xgboost做一個分類任務(wù),可以直接使用xgboost。
#?0?1:1?9:1?19:1?21:1?24:1?34:1?36:1?39:1?42:1?53:1?56:1?65:1?69:1?77:1?86:1?88:1?92:1?95:1?102:1?106:1?117:1?122:1 #?1?3:1?9:1?19:1?21:1?30:1?34:1?36:1?40:1?41:1?53:1?58:1?65:1?69:1?77:1?86:1?88:1?92:1?95:1?102:1?106:1?118:1?124:1 #?0?1:1?9:1?20:1?21:1?24:1?34:1?36:1?39:1?41:1?53:1?56:1?65:1?69:1?77:1?86:1?88:1?92:1?95:1?102:1?106:1?117:1?122:1 #?0?3:1?9:1?19:1?21:1?24:1?34:1?36:1?39:1?51:1?53:1?56:1?65:1?69:1?77:1?86:1?88:1?92:1?95:1?102:1?106:1?116:1?122:1 #?0?4:1?7:1?11:1?22:1?29:1?34:1?36:1?40:1?41:1?53:1?58:1?65:1?69:1?77:1?86:1?88:1?92:1?95:1?102:1?105:1?119:1?124:1 #?0?3:1?10:1?20:1?21:1?23:1?34:1?37:1?40:1?42:1?54:1?55:1?65:1?69:1?77:1?86:1?88:1?92:1?95:1?102:1?106:1?118:1?126:1 #?1?3:1?9:1?11:1?21:1?30:1?34:1?36:1?40:1?51:1?53:1?58:1?65:1?69:1?77:1?86:1?88:1?92:1?95:1?102:1?106:1?117:1?124:1"""上面是libsvm的數(shù)據(jù)存儲格式,?也是一種常用的格式,存儲的稀疏數(shù)據(jù)。? 第一列是label.?a:b?a表示index,?b表示在該index下的數(shù)值,?這就類似于one-hot"""import?numpy?as?np import?scipy.sparse????#?稀疏矩陣的處理 import?pickle import?xgboost?as?xgb#?libsvm?format?data?的讀入方式,?直接用xgb的DMatrix dtrain?=?xgb.DMatrix('./xgbdata/agaricus.txt.train') dtest?=?xgb.DMatrix('./xgbdata/agaricus.txt.test')下面我們進(jìn)行參數(shù)設(shè)置:關(guān)于xgboost的參數(shù),詳細(xì)的可以看上面的參數(shù)說明,這里拿分類器來說,解釋一些參數(shù):
★xgb1 = XGBClassifier( learning_rate =0.1, n_estimators=1000, max_depth=5, min_child_weight=1, gamma=0, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1, seed=27)
'booster':'gbtree', ?這個指定基分類器
'objective': 'multi:softmax', 多分類的問題, 這個是優(yōu)化目標(biāo),必須得有,因為xgboost里面有求一階導(dǎo)數(shù)和二階導(dǎo)數(shù),其實(shí)就是這個。
'num_class':10, 類別數(shù),與 multisoftmax 并用
'gamma':損失下降多少才進(jìn)行分裂, 控制葉子節(jié)點(diǎn)的個數(shù)
'max_depth':12, 構(gòu)建樹的深度,越大越容易過擬合
'lambda':2, 控制模型復(fù)雜度的權(quán)重值的L2正則化項參數(shù),參數(shù)越大,模型越不容易過擬合。
'subsample':0.7, 隨機(jī)采樣訓(xùn)練樣本
'colsample_bytree':0.7, 生成樹時進(jìn)行的列采樣
'min_child_weight':3, 孩子節(jié)點(diǎn)中最小的樣本權(quán)重和。如果一個葉子節(jié)點(diǎn)的樣本權(quán)重和小于min_child_weight則拆分過程結(jié)束
'silent':0 ,設(shè)置成1則沒有運(yùn)行信息輸出,最好是設(shè)置為0.
'eta': 0.007, 如同學(xué)習(xí)率
'seed':1000,
'nthread':7, cpu 線程數(shù)
當(dāng)然不需要全記住,常用的幾個記住即可。可以結(jié)合著上面的數(shù)學(xué)原理,看看哪個參數(shù)到底對于xgboost有什么作用,這樣利于調(diào)參。設(shè)置好參數(shù),訓(xùn)練測試就行了,使用起來和sklearn的模型非常像
"""paramet?setting""" param?=?{'max_depth':?2,'eta':?1,?'silent':?1,'objective':?'binary:logistic' } watch_list?=?[(dtest,?'eval'),?(dtrain,?'train')]??#?這個是觀測的時候在什么上面的結(jié)果??觀測集 num_round?=?5 model?=?xgb.train(params=param,?dtrain=dtrain,?num_boost_round=num_round,?evals=watch_list)然后就是預(yù)測:
"""預(yù)測""" pred?=?model.predict(dtest)????#?這里面表示的是正樣本的概率是多少from?sklearn.metrics?import?accuracy_score predict_label?=?[round(values)?for?values?in?pred] accuracy_score(labels,?predict_label)???#?0.993模型的保存了解一下:
"""兩種方式:?第一種, pickle的序列化和反序列化""" pickle.dump(model,?open('./model/xgb1.pkl',?'wb')) model1?=?pickle.load(open('./model/xgb1.pkl',?'rb')) model1.predict(dtest)"""第二種模型的存儲與導(dǎo)入方式?-?sklearn的joblib""" from?sklearn.externals?import?joblib joblib.dump(model,?'./model/xgb.pkl') model2?=?joblib.load('./model/xgb.pkl') model2.predict(dtest)3.2 交叉驗證 xgb.cv
#?這是模型本身的參數(shù) param?=?{'max_depth':2,?'eta':1,?'silent':1,?'objective':'binary:logistic'} num_round?=?5???#?這個是和訓(xùn)練相關(guān)的參數(shù)xgb.cv(param,?dtrain,?num_round,?nfold=5,?metrics={'error'},?seed=3)3.3 調(diào)整樣本權(quán)重
這個是針對樣本不平衡的情況,可以在訓(xùn)練時設(shè)置樣本的權(quán)重, 訓(xùn)練的時候設(shè)置fpreproc這個參數(shù), 相當(dāng)于在訓(xùn)練之前先對樣本預(yù)處理。
#?這個函數(shù)是說在訓(xùn)練之前,先做一個預(yù)處理,計算一下正負(fù)樣本的個數(shù),然后加一個權(quán)重,解決樣本不平衡的問題 def?preproc(dtrain,?dtest,?param):?labels?=?dtrain.get_label()ratio?=?float(np.sum(labels==0))?/?np.sum(labels==1)param['scale_pos_ratio']?=?ratioreturn?(dtrain,?dtest,?param)#?下面我們在做交叉驗證,?指明fpreproc這個參數(shù)就可以調(diào)整樣本權(quán)重 xgb.cv(param,?dtrain,?num_round,?nfold=5,?metrics={'auc'},?seed=3,?fpreproc=preproc)3.4 自定義目標(biāo)函數(shù)(損失函數(shù))
如果在一個比賽中,人家給了自己的評判標(biāo)準(zhǔn),那么這時候就需要用人家的這個評判標(biāo)準(zhǔn),這時候需要修改xgboost的損失函數(shù), 但是這時候請注意一定要提供一階和二階導(dǎo)數(shù)
#?自定義目標(biāo)函數(shù)(log似然損失),這個是邏輯回歸的似然損失。?交叉驗證 #?注意:?需要提供一階和二階導(dǎo)數(shù)def?logregobj(pred,?dtrain):labels?=?dtrain.get_label()pred?=?1.0?/?(1+np.exp(-pred))????#?sigmoid函數(shù)grad?=?pred?-?labelshess?=?pred?*?(1-pred)return?grad,?hess?????#?返回一階導(dǎo)數(shù)和二階導(dǎo)數(shù)def?evalerror(pred,?dtrain):labels?=?dtrain.get_label()return?'error',?float(sum(labels!=(pred>0.0)))/len(labels)訓(xùn)練的時候,把損失函數(shù)指定就可以了:
param?=?{'max_depth':2,?'eta':1,?'silent':1}#?自定義目標(biāo)函數(shù)訓(xùn)練 model?=?xgb.train(param,?dtrain,?num_round,?watch_list,?logregobj,?evalerror)#?交叉驗證 xgb.cv(param,?dtrain,?num_round,?nfold=5,?seed=3,?obj=logregobj,?feval=evalerror)3.5?用前n棵樹做預(yù)測 ntree_limit
太多的樹可能發(fā)生過擬合,這時候我們可以指定前n棵樹做預(yù)測, 預(yù)測的時候設(shè)置ntree_limit這個參數(shù)
#?前1棵 pred1?=?model.predict(dtest,?ntree_limit=1) evalerror(pred2,?dtest)3.6?畫出特征重要度 plot_importance
from?xgboost?import?plot_importance plot_importance(model,?max_num_features=10)3.7?同樣,也可以用sklearn的GridSearchCV調(diào)參
from?sklearn.model_selection?import?GridSearchCV from?sklearn.model_selection?import?StratifiedKFoldmodel?=?XGBClassifier() learning_rate?=?[0.0001,?0.001,?0.1,?0.2,?0.3] param_grid?=?dict(learning_rate=learning_rate) kfold?=?StratifiedKFold(n_splits=10,?shuffle=True,?random_state=7) grid_search?=?GridSearchCV(model,?param_grid,?scoring="neg_log_loss",?n_jobs=-1,?cv=kfold) grid_result?=?grid_search.fit(x_train,?y_train)print("best:?%f?using?%s"?%(grid_result.best_score_,?grid_result.best_params_))means?=?grid_result.cv_results_['mean_test_score'] params?=?grid_result.cv_results_['params']for?mean,?param?in?zip(means,?params):print("%f with:?%r"?%?(mean,?param))好了,實(shí)戰(zhàn)部分就整理這么多吧, 重點(diǎn)在于怎么使用,xgboost使用起來和sklearn的模型也是非常像, 也是.fit(), .predict()方法,只不過xgboost的參數(shù)很多,這個調(diào)起來會比較復(fù)雜, 但是懂了原理之后,至少每個參數(shù)是干啥的就了解了,關(guān)于調(diào)參的技術(shù), 得從經(jīng)驗中多學(xué)習(xí),多嘗試,多總結(jié)才能慢慢修煉出來。
4. 總結(jié)
哇,終于完了,到這里,終于把xgboost的一些知識說清楚了, 每一次不知不覺就會這么多字, 可能是因為這個算法太重要了吧,所以多寫了點(diǎn), 趕緊回顧一下:首先, 我們從集成算法開始講起,回顧了一下AdaBoost,GBDT, 然后引出了xgboost, 我們知道同屬boosting流派,但集成策略又有不同, 即使集成策略類似,那么得到最后結(jié)果的方式又不同。但對比之中,我們能更加體會它們的原理。其次,我們從數(shù)學(xué)原理的角度剖析了一下xgboost, 看到了它的目標(biāo)函數(shù),看到了如何生成一棵樹,看到了如何Taylor化簡,知道了為什么需要損失函數(shù)的一二階導(dǎo)數(shù),也明白了為啥這個算法這么快。最后,我們通過實(shí)戰(zhàn)一個二分類問題,見識到了xgboost的代碼實(shí)現(xiàn),基本使用和一些高級策略。
下面看看xgboost相比于GBDT有哪些優(yōu)點(diǎn)(面試的時候可能會涉及):
精度更高:GBDT只用到一階泰勒, 而xgboost對損失函數(shù)進(jìn)行了二階泰勒展開, 一方面為了增加精度, 另一方面也為了能夠自定義損失函數(shù),二階泰勒展開可以近似大量損失函數(shù)
靈活性更強(qiáng):GBDT以CART作為基分類器,而Xgboost不僅支持CART,還支持線性分類器,另外,Xgboost支持自定義損失函數(shù),只要損失函數(shù)有一二階導(dǎo)數(shù)。
正則化:xgboost在目標(biāo)函數(shù)中加入了正則,用于控制模型的復(fù)雜度。有助于降低模型方差,防止過擬合。正則項里包含了樹的葉子節(jié)點(diǎn)個數(shù),葉子節(jié)點(diǎn)權(quán)重的L2范式。
Shrinkage(縮減):相當(dāng)于學(xué)習(xí)速率。這個主要是為了削弱每棵樹的影響,讓后面有更大的學(xué)習(xí)空間,學(xué)習(xí)過程更加的平緩
列抽樣:這個就是在建樹的時候,不用遍歷所有的特征了,可以進(jìn)行抽樣,一方面簡化了計算,另一方面也有助于降低過擬合
缺失值處理:這個是xgboost的稀疏感知算法,加快了節(jié)點(diǎn)分裂的速度
并行化操作:塊結(jié)構(gòu)可以很好的支持并行計算
上面的這些優(yōu)點(diǎn),我在描述的時候基本上都涉及到了,正是因為xgboost有了這些優(yōu)點(diǎn),才讓它變得非?;?#xff0c;堪稱神器了現(xiàn)在,但是xgboost真的perfect了嗎?正所謂金無足赤,人無完人, xgboost也同樣如此,比如雖然利用了預(yù)排序和近似算法可以降低尋找最優(yōu)分裂點(diǎn)的計算量,但在節(jié)點(diǎn)分裂過程中仍需要遍歷整個數(shù)據(jù)集。預(yù)排序過程的空間復(fù)雜度過高,不僅需要存儲特征值,還需要存儲特征對應(yīng)樣本梯度統(tǒng)計值的索引,相當(dāng)于消耗了兩倍的內(nèi)存。所以在內(nèi)存和計算方面還是有很大的優(yōu)化空間的。那么xgboost還可以在哪些角度進(jìn)行優(yōu)化呢?后面通過lightgbm的故事再說給你聽 ;)
xgboost的故事就先講到這里了,希望對你有所幫助,當(dāng)然還有很多的細(xì)節(jié)沒有提到,本文只是拋磚引玉,具體的建議去看看原文,畢竟這個算法還是超級重要的,面試的時候也會摳得很細(xì), 不看原文的話有些精華get不到。
參考:
xgboost論文原文 - 權(quán)威經(jīng)典 : https://arxiv.org/pdf/1603.02754.pdf
Adaboost、GBDT與XGBoost的區(qū)別: https://blog.csdn.net/hellozhxy/article/details/82143554
xgboost算法詳細(xì)介紹(通過簡單例子講述): https://blog.csdn.net/wufengqi7585/article/details/86078049
Introduction to Boosted Trees: https://xgboost.readthedocs.io/en/latest/tutorials/model.html
終于有人把XGBoost 和 LightGBM 講明白了,項目中最主流的集成算法!:https://mp.weixin.qq.com/s/q4R-TAG4PZAdWLb41oov8g
xgboost的原理沒你想像的那么難: https://www.jianshu.com/p/7467e616f227
XGBoost算法的原理詳析[文獻(xiàn)閱讀筆記]: https://zhuanlan.zhihu.com/p/90520307
XGBoost原理介紹: https://blog.csdn.net/yinyu19950811/article/details/81079192
靈魂拷問,你看過Xgboost原文嗎?: https://zhuanlan.zhihu.com/p/86816771
一文讀懂機(jī)器學(xué)習(xí)大殺器XGBoost原理: https://zhuanlan.zhihu.com/p/40129825
總結(jié)
以上是生活随笔為你收集整理的【白话机器学习】算法理论+实战之Xgboost算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法工程师的效率神器——vim篇
- 下一篇: 看不懂花书?博士教你如何深入深度学习,从