XGBoost
Author:Miracle8070
From:AI蝸牛車
1. 寫在前面
如果想從事數據挖掘或者機器學習的工作,掌握常用的機器學習算法是非常有必要的,在這簡單的先捋一捋, 常見的機器學習算法:
監督學習算法:邏輯回歸,線性回歸,決策樹,樸素貝葉斯,K近鄰,支持向量機,集成算法Adaboost等無監督算法:聚類,降維,關聯規則, PageRank等這個系列已經基本包含了上面這些算法的原理和基本使用。但是,如果僅僅是會用這些算法可是不夠的, 我們也得跟著時代的步伐前進,近幾年,有很多大佬又在上面的某些算法上加以改進,發明了更加厲害的算法,而這些算法才是當今時代解決問題的主流,所以我們學習的一個方式就是掌握傳統,而又得緊跟時代。
所以,後面考慮加上當前流行的一些主流機器學習算法,既當複習,又當提升。由於不想和傳統的機器學習算法混合起來,故稱之為番外,也是傳統機器學習算法的延伸, 同樣是儘量白話,同樣是豐富實戰,但會夾雜數學的身影,畢竟後面的很多算法如果沒有了數學就仿佛失去了靈魂,無法活靈活現。所以機器學習算法的故事還沒有完,我們還得繼續走著。
學習算法的過程,獲得的不應該只有算法理論,還應該有樂趣和解決實際問題的能力!
今天分享的這個算法堪稱數據科學競賽界的神器, 它似乎是用於贏得數據科學競賽的分類器/預測器必不可少的算法, 那就是Xgboost。聽這個名字,你可能一下就想到了傳統機器學習算法裡面的AdaBoost,哈哈,聯想和對比才能更加理解算法的精華。你還別說,這個算法和那個來自於同一個家族,都是集成學習算法,都屬於boosting流派,但是兩者的boosting採用了不同的策略,而就是這策略的不同,導致xgboost成了目前競賽者眼中的紅人,它是目前最快最好的開源 boosting tree 工具包,比常見的工具包快 10 倍以上, 那麼xgboost到底採用了什麼策略呢?它又是如何做到高準確率和高速度的呢?Xgboost和AdaBoost到底有什麼不同呢?Xgboost又如何來解決實際問題呢? 這些問題,在這篇文章中都會一一來解剖。
大綱如下:
Xgboost?這個故事還得先從AdaBoost和GBDT說起Xgboost的基本原理(基於例子我們來看看好玩的公式推導)Xgboost的實戰應用(這裡用xgboost做一個分類任務,然後說一下基本使用和高級功能)Ok, let's go!
2. Xgboost? 這個故事還得先從AdaBoost和GBDT說起我覺得,學習一個算法的時候,有時候不能直接單拿出一個算法來說,這樣感覺顯得突兀了些,不知道突然從哪冒出來一樣。所以,講Xgboost之前,我想先帶你回顧一下我們之前的集成學習。
所謂集成學習,就是指構建多個弱分類器對數據集進行預測,然後用某種策略將多個分類器預測的結果集成起來,作為最終預測結果。在這裡就不再講了(可以理解成集成學習是一種把大傢伙叫到一塊,集思廣益想辦法解決問題的方式吧),在這裡想說的是集成學習的那兩大流派:Boosting和Bagging。
怎麼還有兩個流派呢?集思廣益不就完事?哈哈, 集思廣益也有不同的方式嗎?比如針對同一個問題,把問題劃分成不相干的子問題,然後分派給不同的人各幹各的是一種, 或者同一個問題,劃分成串行的子問題, 先由一個人解決一部分,解決不了的,後面的人再來這又是一種。把上面這兩種方式用官方的語言描述就是:根據各個弱分類器之間有無依賴關係,分為Boosting和Bagging。
Boosting流派,各分類器之間有依賴關係,必須串行,比如Adaboost、GBDT(Gradient Boosting Decision Tree)、XgboostBagging流派,各分類器之間沒有依賴關係,可各自並行,比如隨機森林(Random Forest)關於Bagging流派的Random Forest(隨機森林)算法,也是比較常用的,簡單的說就是各個弱分類器是獨立的、每個分類器在樣本堆裡隨機選一批樣本,隨機選一批特徵進行獨立訓練,各個分類器之間沒有啥關係, 最後投票表決, 這個在這裡不做詳述,後面遇到的時候再統一總結,今天的主角是Xgboost,所以我們主要是了解一下Boosting流派, 這這裡面的最具代表性的算法之一就是AdaBoost, 這個我這裡不做過多的表述,詳細的可以看一下專欄之前的文章, 這裡只回顧一下它的算法原理,這樣好引出後面的GBDT和Xgboost,並且可以進行策略上的對比。
AdaBoost,是英文"Adaptive Boosting"(自適應增強),它的自適應在於:前一個基本分類器分錯的樣本會得到加強,加權後的全體樣本再次被用來訓練下一個基本分類器。同時,在每一輪中加入一個新的弱分類器,直到達到某個預定的足夠小的錯誤率或達到預先指定的最大迭代次數。白話的講,就是它在訓練弱分類器之前,會給每個樣本一個權重,訓練完了一個分類器,就會調整樣本的權重,前一個分類器分錯的樣本權重會加大,這樣後面再訓練分類器的時候,就會更加注重前面分錯的樣本, 然後一步一步的訓練出很多個弱分類器,最後,根據弱分類器的表現給它們加上權重,組合成一個強大的分類器,就足可以應付整個數據集了。 這就是AdaBoost, 它強調自適應,不斷修改樣本權重, 不斷加入弱分類器進行boosting。
那麼,boosting還有沒有別的方式呢?GBDT(Gradient Boost Decision Tree)就是另一種boosting的方式, 上面說到AdaBoost訓練弱分類器關注的是那些被分錯的樣本,AdaBoost每一次訓練都是為了減少錯誤分類的樣本。而GBDT訓練弱分類器關注的是殘差,也就是上一個弱分類器的表現與完美答案之間的差距,GBDT每一次訓練分類器,都是為了減少這個差距,GBDT每一次的計算是都為了減少上一次的殘差,進而在殘差減少(負梯度)的方向上建立一個新的模型。這是什麼意思呢? 我可以舉個例子,假設我們去銀行借錢,我們想讓一個決策樹系統來預測可以借給我們多少錢, 如果標準答案是1000的話,假設第一棵決策樹預測,可以借給我們950塊錢, 那麼離標準答案的1000還差50, 效果不算好,能不能提高一些呢?我們就再加一棵決策樹,這課決策樹過來之後,看到前面的那個已經預測到950了,只是差50, 那麼我可以聚焦在這個50上,把這個殘差變得再小一些,所以第二個決策樹預測結果是30, 那麼前兩棵決策樹預測結果結合起來是980, 離標準答案差20, 所以加了一棵樹之後,效果好了。那麼還能不能提升呢?我再來一棵樹,發現殘差只有20了,那我把殘差變得再小, 結果第三個決策樹預測20, 那麼這三棵樹就可以正確的預測最終的1000了。
所以GBDT就是這樣的一個學習方式了,GBDT是boosting集成學習,boosting集成學習由多個相關聯的決策樹聯合決策, 什麼是相關聯?就是我上面的例子:
有一個樣本[數據->標籤]是:[(feature1,feature2,feature3)-> 1000塊]那麼第二棵決策樹訓練時的輸入,這個樣本就變成了:[(feature1,feature2,feature3)->50]那麼第三棵決策樹訓練時的輸入,這個樣本就變成了:[(feature1,feature2,feature3)->20]搞定,也就是說,下一棵決策樹輸入樣本會與前面決策樹的訓練和預測相關。用個圖來表示類似這樣:這就是GBDT的工作原理了, GBDT是旨在不斷減少殘差(回歸),通過不斷加入新的樹旨在在殘差減少(負梯度)的方向上建立一個新的模型。——即損失函數是旨在最快速度降低殘差。
那麼為啥要講GBDT呢? 我先賣個關子,不妨先看一下xgboost是怎麼解決問題的。這裡用xgboost原作者陳天奇的講座PPT中的那個圖來看假設我想預測,這一家子人中每個人想玩遊戲的意願值。我們用xgboost解決這個問題,就是我先訓練出來第一棵決策樹, 預測了一下小男孩想玩遊戲的意願是2, 然後發現離標準答案差一些,又訓練出來了第二棵決策樹, 預測了一下小男孩想玩遊戲的意願是0.9, 那麼兩個相加就是最終的答案2.9。這個其實就接近了標準答案。所以xgboost是訓練出來的弱分類結果進行累加就是最終的結論。
恩,你可能要拍案而起了,驚呼,這不是跟上面介紹的GBDT乃異曲同工麼?事實上,如果不考慮工程實現、解決問題上的一些差異,xgboost與gbdt比較大的不同就是目標函數的定義,但這倆在策略上是類似的,都是聚焦殘差(更準確的說, xgboost其實是gbdt算法在工程上的一種實現方式),GBDT旨在通過不斷加入新的樹最快速度降低殘差,而XGBoost則可以人為定義損失函數(可以是最小平方差、logistic loss function、hinge loss function或者人為定義的loss function),只需要知道該loss function對參數的一階、二階導數便可以進行boosting,其進一步增大了模型的泛化能力,其貪婪法尋找添加樹的結構以及loss function中的損失函數與正則項等一系列策略也使得XGBoost預測更準確。
所以,這就是我講Xgboost的故事之前,要簡單說一下AdaBoost和GBDT的原因了,這樣腦海裡面是不是對xgboost不那麼陌生了啊,你要知道,這三個同是屬於集成學習的boosting流派,AdaBoost叫做自適應提升,和GBDT,Xgboost提升時採用的策略不同,前者聚焦錯誤樣本,後者聚焦與標準答案的殘差。而GBDT和Xgboost叫做boosting集成學習,提升時策略類似,都是聚焦殘差,但是降低殘差的方式又各有不同。
好了,鋪墊到此為止,下面真正進入主角部分 -- Xgboost的基本原理。
3. Xgboost的基本原理Xgboost 的全稱是eXtreme Gradient Boosting,由華盛頓大學的陳天奇博士提出,在Kaggle的希格斯子信號識別競賽中使用,因其出眾的效率與較高的預測準確度而引起了廣泛的關注。
如果boosting算法每一步的弱分類器生成都是依據損失函數的梯度方向,則稱之為梯度提升(Gradient boosting),XGBoost算法是採用分步前向加性模型,只不過在每次迭代中生成弱學習器後不再需要計算一個係數,XGBoost 是由 k 個基模型組成的一個加法運算式:
其中
其中n為樣本數量。
★XGBoost算法通過優化結構化損失函數(加入了正則項的損失函數,可以起到降低過擬合的風險)來實現弱學習器的生成,並且XGBoost算法沒有採用搜索方法,而是直接利用了損失函數的一階導數和二階導數值,並通過預排序、加權分位數等技術來大大提高了算法的性能。
」說到這裡,估計你已經看不下去了吧,這說的啥跟啥啊, 聽不懂啦啊!但其實我上面只是用數學的語言來說了一下前面舉得xgboost的那個例子,對於某個樣本,有若干個弱分類器做預測,最後的預測結果就是弱分類器的答案累加(注意此時沒有權重了,如果你還記得AdaBoost模型的話,會發現那個地方每個分類器前面會有個權重
如此白話,應該能聽懂了吧, 但還沒真正講xgboost的數學原理呢, 所以後面的數學原理我打算換一種方式,從一個例子展開,剖析數學公式,這裡面就全是數學的原理了,如果你感覺直接上數學壓力有點大,那麼可以先跟著我繼續往下,從一個例子中看看xgboost樹到底是如何生成的,然後再回頭看數學原理也不遲 ;)
下面就通過算法流程圖舉一個例子來詳解xgboost樹的生成。
先給出一個流程圖, 不懂不要緊,可以看一遍後面的步驟,然後再回來:為了讓xgboost數學原理的部分不那麼boring, 我們跟著一個例子走吧:
★假設我想預測學生考試分數, 給定若干個學生屬性(比如天賦,每天學習時間,是否談戀愛等),
通過一個決策樹A, 我們可以看到一個天賦屬性的預測結果:天賦高的人+90, 不高的人+60通過決策樹B, 可以看到每天學習時間高於10小時的+5,低於10小時的-5通過決策樹C, 可以看到談戀愛的-1, 單身狗的+1
後面依次類推,還可能有更多的決策樹通過學生的某些屬性來推斷分數。
XGboost就是這樣一個不斷生成新的決策樹A,B,C,D…的算法,最終生成的決策樹算法就是樹A+B+C+D+…的和的決策樹。
」我們針對這個問題看看詳細的建樹過程吧:
首先,我們有三個學生, 屬性和標籤如下:我們初始化三個樣本的考試成績預測值為0。定義目標函數:模型的預測精度由偏差和方差共同決定,損失函數代表了模型的偏差,想要方差小則需要更簡單的模型, 所以目標函數最終由損失函數L與抑制模型複雜度的正則項Ω組成, 所以目標函數如下:這個公式應該不需要過多的解釋了吧, 其中
前面的
其中,
這個就是xgboost的目標函數了,最優化這個目標函數,其實就是相當於求解當前的
我們有了第一棵樹, 通過這個樹的預測結果:那麼我們建立第二棵樹的時候,我們是考慮的殘差,也就是樣本其實變成了下面這樣:通過最小化殘差學習到一個通過學習時間屬性構建的決策樹得到了90+5,60+5,90-5的預測值,再繼續通過(100-95=5)(70-65)(86-85)的殘差構建下一個決策樹,以此類推,當迭代次數達到上限或是殘差不再減小是停止,就得到一個擁有多個(迭代次數)決策樹的強分類器。這個就是xgboost工作的宏觀過程了。光宏觀部分確實挺好理解,但具體細節呢?比如我每一次建樹是怎麼建的呢?既然說計算收益的方式不同, 那麼我考慮分裂的時候是怎麼計算收益的呢?目前你心中肯定會有這些疑問, 莫慌莫慌, 下面我把建樹的細節給你娓娓道來,不過道來的過程中得崎嶇一點,需要用到數學的語言。
那麼究竟是如何得到一棵新的樹的呢?下面就是Xgboost的精髓了。前方高能,一大波數學公式來襲, 請戴好安全帽!!!
我們看看這裡的
★在這裡插入圖片描述」根據Taylor公式, 我們把函數
那麼我們把
其中
★這裡我們以平方損失函數為例:
」★則對於每一個樣本:
」由於在第t步時
所以我們只需要求出每一步損失函數的一階導和二階導的值(由於前一步的
基於決策樹的目標函數的終極化簡上面的目標函數先放下來:我們這裡先解決一下這個
這個再解釋一遍就是:遍歷所有的樣本後求每個樣本的損失函數,但樣本最終會落在葉子節點上,所以我們也可以遍歷葉子節點,然後獲取葉子節點上的樣本集合(注意第二個等式和第三個等式求和符號的上下標, T代表葉子總個數)。 由於一個葉子節點有多個樣本存在,所以後面有了
即決策樹模型的複雜度由生成的所有決策樹的葉子節點數量(
這樣,目標函數的前後兩部分都進行了解決,那麼目標函數就可以化成最後這個樣子,看看能懂嗎?
這裡的
這裡要注意
那麼這個目標函數又可以進行化簡:
這個就是基於決策樹的xgboost模型的目標函數最終版本了,這裡的G和H的求法,就需要明確的給出損失函數來, 然後求一階導和二階導,然後代入樣本值即得出。
這個
還是上面的那個預測玩遊戲的意願,我們假設建了右邊的那棵樹,那麼每個樣本都對應到了葉子節點上去,每一個樣本都會對應一個g和h, 那麼我們遍歷葉子節點,就會得到G和H,然後累加就可以得到這棵樹的結構分數obj(這裡有個小細節就是假設有N個訓練樣本, 那麼就會有N次計算各自的
」上面是可以判斷出來一棵樹究竟好不好,那麼建立樹的時候應該怎麼建立呢?一棵樹的結構近乎無限多,總不能一個一個去測算它們的好壞程度,然後再取最好的吧(這是個NP問題)。所以,我們仍然需要採取一點策略,這就是逐步學習出最佳的樹結構。這與我們將K棵樹的模型分解成一棵一棵樹來學習是一個道理,只不過從一棵一棵樹變成了一層一層節點而已。這叫什麼?emmm, 貪心(找到每一步最優的分裂結果)!xgboost採用二叉樹, 開始的時候, 全部樣本在一個葉子節點上, 然後葉子節點不斷通過二分裂,逐漸生成一棵樹。
那麼在葉子節點分裂成樹的過程中最關鍵的一個問題就是應該在哪個特徵的哪個點上進行分裂,也就是尋找最優切分點的過程。
最優切分點劃分算法及優化策略在決策樹的生長過程中,一個非常關鍵的問題是如何找到節點的最優切分點, 我們學過了決策樹的建樹過程,那麼我們知道ID3也好,C4.5或者是CART,它們尋找最優切分點的時候都有一個計算收益的東西,分別是信息增益,信息增益比和基尼係數。而xgboost這裡的切分, 其實也有一個類似於這三個的東西來計算每個特徵點上分裂之後的收益。
★假設我們在某一節點完成特徵分裂,則分列前的目標函數可以寫為:
分裂後的目標函數:
則對於目標函數來說,分裂後的收益為(Obj1-Obj2):
」注意該特徵收益也可作為特徵重要性輸出的重要依據。
那麼我們就可以來梳理一下最優切分點的劃分算法了:
從深度為 0 的樹開始,對每個葉節點枚舉所有的可用特徵;針對每個特徵,把屬於該節點的訓練樣本根據該特徵值進行升序排列,通過線性掃描的方式來決定該特徵的最佳分裂點,並記錄該特徵的分裂收益;(這個過程每個特徵的收益計算是可以並行計算的,xgboost之所以快,其中一個原因就是因為它支持並行計算,而這裡的並行正是指的特徵之間的並行計算,千萬不要理解成各個模型之間的並行)選擇收益最大的特徵作為分裂特徵,用該特徵的最佳分裂點作為分裂位置,在該節點上分裂出左右兩個新的葉節點,並為每個新節點關聯對應的樣本集(這裡稍微提一下,xgboost是可以處理空值的,也就是假如某個樣本在這個最優分裂點上值為空的時候, 那麼xgboost先把它放到左子樹上計算一下收益,再放到右子樹上計算收益,哪個大就把它放到哪棵樹上。)
上面就是最優切分點劃分算法的過程, 看完之後,是不是依然懵逼, 這到底是怎麼做的啊, 下面就看一個尋找最優切分點的慄子吧:
還是上面玩遊戲的那個例子,假設我有這一家子人樣本,每個人有性別,年齡,興趣等幾個特徵,我想用xgboost建立一棵樹預測玩遊戲的意願值。首先,五個人都聚集在根節點上,現在就考慮根節點分叉,我們就遍歷每個特徵,對於當前的特徵,我們要去尋找最優切分點以及帶來的最大收益,比如當前特徵是年齡,我們需要知道兩點:* 按照年齡分是否有效,也就是是否減少了obj的值* 如果真的可以分,特徵收益比較大, 那麼我們從哪個年齡點分開呢?
對於這兩個問題,我們可以這樣做,首先我們先把年齡進行一個排序, 如下圖:按照這個圖從左至右掃描,我們就可以找出所有的切分點a, 對於每一個切分點a,計算出分割的梯度和
上面就是等頻和等寬分桶的思路了(這個不用較真,我這裡只是為了和作者的想法產生更清晰的對比才這樣舉得例子),這樣選擇出的候選點是不是比就少了好多了?但是這樣劃分其實是有問題的,因為這樣劃分沒有啥依據啊, 比如我上面畫的等頻分桶,我是5個訓練樣本放一個桶,但是你說你還想10個一組來,沒有個標準啥的啊。即上面那兩種常規的劃分方式缺乏可解釋性,所以重點來了, 作者這裡採用了一種對loss的影響權重的等值percentiles(百分比分位數)劃分算法(Weight Quantile Sketch), 我上面的這些鋪墊也正是為了引出這個方式,下面就來看看作者是怎麼做的,這個地方其實不太好理解,所以慢一些
作者進行候選點選取的時候,考慮的是想讓loss在左右子樹上分布的均勻一些,而不是樣本數量的均勻,因為每個樣本對降低loss的貢獻可能不一樣,按樣本均分會導致分開之後左子樹和右子樹loss分布不均勻,取到的分位點會有偏差。這是啥意思呢?再來一個圖(這個圖得看明白了):這其實就是作者提出的那種找候選節點的方式(分桶的思路),明白了這個圖之後,下面就是解釋一下上面這個圖的細節:第一個就是
就是這個
這樣化簡夠簡潔明了了吧, 你看到殘差的身影了嗎?後面的每一個分類器都是在擬合每個樣本的一個殘差
PS:這裡加點題外話,Xgboost引入了二階導之後,相當於在模型降低殘差的時候給各個樣本根據貢獻度不同加入了一個權重,這樣就能更好的加速擬合和收斂,GBDT只用到了一階導數,這樣只知道梯度大的樣本降低殘差效果好,梯度小的樣本降低殘差不好(這個原因我會放到Lightgbm的GOSS那裡說到),但是好與不好的這個程度,在GBDT中無法展現。而xgboost這裡就通過二階導可以展示出來,這樣模型訓練的時候就有數了。
下面再解釋第二個問題,這個分箱是怎麼分的?比如我們定義一個數據集代表每個訓練樣本的第
這裡的
這個
到這終於把這一塊描述完了, 有點多,稍微理一理邏輯,前面那一部分是圍繞著如何建立一棵樹進行的,即採用貪心的方式從根節點開始一層層的建立樹結構(每一層爭取最優),然後就是建樹過程中一個關鍵的問題:如何尋找最優切分點,給出了最優切分點算法,基於這個算法就可以建立樹了。後面這一部分是一個優化的過程,提出了一種Weight Quantile Sketch的算法,這個算法可以將原來的分割點進行分桶,然後找到合適的候選分裂點,這樣可以減少遍歷時嘗試的分裂點的數量,是xgboost相比於GBDT做出的切分點優化策略,現在知道為啥xgboost要快了吧,因為xgboost尋找切分點的時候不用遍歷所有的,而是只看候選點就可以了。而且在特徵上,xgboost是可以並行處理的。這樣xgboost的建樹過程及優化策略基本上就是這些了,當然這裡面還有很多的細節,由於篇幅的原因就不寫在這裡了。
利用新的決策樹預測樣本值,並累加到原來的值上若干個決策樹是通過加法訓練的, 所謂加法訓練,本質上是一個元算法,適用於所有的加法模型,它是一種啟發式算法。運用加法訓練,我們的目標不再是直接優化整個目標函數,而是分步驟優化目標函數,首先優化第一棵樹,完了之後再優化第二棵樹,直至優化完K棵樹。整個過程如下圖所示:在這裡插入圖片描述上圖中會發現每一次迭代得到的新模型前面有個
好了, 到這裡為止,xgboost的數學原理部分就描述完了,希望我描述清楚了吧。簡單的回顧一下上面的過程吧: xgboost是好多弱分類器的集成,訓練弱分類器的策略就是儘量的減小殘差,使得答案越來越接近正確答案。xgboost的精髓部分是目標函數的Taylor化簡,這樣就引入了損失函數的一階和二階導數。然後又把樣本的遍歷轉成了對葉子節點的遍歷,得到了最終的目標函數。這個函數就是衡量一棵樹好壞的標準。在建樹過程中,xgboost採用了貪心策略,並且對尋找分割點也進行了優化。基於這個,才有了後面的最優點切分建立一棵樹的過程。xgboost訓練的時候,是通過加法進行訓練,也就是每一次只訓練一棵樹出來, 最後的預測結果是所有樹的加和表示。
關於xgboost,依然還有很多的細節沒有說到,具體的去看論文吧。下面,我們就進行xgboost的實戰部分, 這裡我們簡單的做一個分類任務, 主要是看看xgboost主要怎麼用, 尤其是在一個數據競賽中(這次重點總結了一些用法)。
安裝:默認可以通過pip安裝,若是安裝不上可以通過:
https://www.lfd.uci.edu/~gohlke/pythonlibs/
網站下載相關安裝包,將安裝包拷貝到Anacoda3的安裝目錄的Scrripts目錄下, 然後pip install 安裝包安裝
3.1 xgboost的基本使用https://xgboost.readthedocs.io/en/latest/parameter.html
https://xgboost.readthedocs.io/en/latest/tutorials/param_tuning.html
我們使用xgboost做一個分類任務,可以直接使用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的數據存儲格式, 也是一種常用的格式,存儲的稀疏數據。
第一列是label. a:b a表示index, b表示在該index下的數值, 這就類似於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')下面我們進行參數設置:關於xgboost的參數,詳細的可以看上面的參數說明,這裡拿分類器來說,解釋一些參數:
★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', 多分類的問題, 這個是優化目標,必須得有,因為xgboost裡面有求一階導數和二階導數,其實就是這個。'num_class':10, 類別數,與 multisoftmax 並用'gamma':損失下降多少才進行分裂, 控制葉子節點的個數'max_depth':12, 構建樹的深度,越大越容易過擬合'lambda':2, 控制模型複雜度的權重值的L2正則化項參數,參數越大,模型越不容易過擬合。'subsample':0.7, 隨機採樣訓練樣本'colsample_bytree':0.7, 生成樹時進行的列採樣'min_child_weight':3, 孩子節點中最小的樣本權重和。如果一個葉子節點的樣本權重和小於min_child_weight則拆分過程結束'silent':0 ,設置成1則沒有運行信息輸出,最好是設置為0.」當然不需要全記住,常用的幾個記住即可。可以結合著上面的數學原理,看看哪個參數到底對於xgboost有什麼作用,這樣利於調參。設置好參數,訓練測試就行了,使用起來和sklearn的模型非常像
"""paramet setting"""
param = {
'max_depth': 2,
'eta': 1,
'silent': 1,
'objective': 'binary:logistic'
}
watch_list = [(dtest, 'eval'), (dtrain, 'train')] # 這個是觀測的時候在什麼上面的結果 觀測集
num_round = 5
model = xgb.train(params=param, dtrain=dtrain, num_boost_round=num_round, evals=watch_list)然後就是預測:
"""預測"""
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)
"""第二種模型的存儲與導入方式 - 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# 這是模型本身的參數
param = {'max_depth':2, 'eta':1, 'silent':1, 'objective':'binary:logistic'}
num_round = 5 # 這個是和訓練相關的參數
xgb.cv(param, dtrain, num_round, nfold=5, metrics={'error'}, seed=3)
3.3 調整樣本權重這個是針對樣本不平衡的情況,可以在訓練時設置樣本的權重, 訓練的時候設置fpreproc這個參數, 相當於在訓練之前先對樣本預處理。
# 這個函數是說在訓練之前,先做一個預處理,計算一下正負樣本的個數,然後加一個權重,解決樣本不平衡的問題
def preproc(dtrain, dtest, param):
labels = dtrain.get_label()
ratio = float(np.sum(labels==0)) / np.sum(labels==1)
param['scale_pos_ratio'] = ratio
return (dtrain, dtest, param)
# 下面我們在做交叉驗證, 指明fpreproc這個參數就可以調整樣本權重
xgb.cv(param, dtrain, num_round, nfold=5, metrics={'auc'}, seed=3, fpreproc=preproc)
3.4 自定義目標函數(損失函數)如果在一個比賽中,人家給了自己的評判標準,那麼這時候就需要用人家的這個評判標準,這時候需要修改xgboost的損失函數, 但是這時候請注意一定要提供一階和二階導數
# 自定義目標函數(log似然損失),這個是邏輯回歸的似然損失。 交叉驗證
# 注意: 需要提供一階和二階導數
def logregobj(pred, dtrain):
labels = dtrain.get_label()
pred = 1.0 / (1+np.exp(-pred)) # sigmoid函數
grad = pred - labels
hess = pred * (1-pred)
return grad, hess # 返回一階導數和二階導數
def evalerror(pred, dtrain):
labels = dtrain.get_label()
return 'error', float(sum(labels!=(pred>0.0)))/len(labels)訓練的時候,把損失函數指定就可以了:
param = {'max_depth':2, 'eta':1, 'silent':1}
# 自定義目標函數訓練
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棵樹做預測 ntree_limit太多的樹可能發生過擬合,這時候我們可以指定前n棵樹做預測, 預測的時候設置ntree_limit這個參數
# 前1棵
pred1 = model.predict(dtest, ntree_limit=1)
evalerror(pred2, dtest)
3.6 畫出特徵重要度 plot_importancefrom xgboost import plot_importance
plot_importance(model, max_num_features=10)
3.7 同樣,也可以用sklearn的GridSearchCV調參from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import StratifiedKFold
model = 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))好了,實戰部分就整理這麼多吧, 重點在於怎麼使用,xgboost使用起來和sklearn的模型也是非常像, 也是.fit(), .predict()方法,只不過xgboost的參數很多,這個調起來會比較複雜, 但是懂了原理之後,至少每個參數是幹啥的就了解了,關於調參的技術, 得從經驗中多學習,多嘗試,多總結才能慢慢修煉出來。
4. 總結哇,終於完了,到這裡,終於把xgboost的一些知識說清楚了, 每一次不知不覺就會這麼多字, 可能是因為這個算法太重要了吧,所以多寫了點, 趕緊回顧一下:首先, 我們從集成算法開始講起,回顧了一下AdaBoost,GBDT, 然後引出了xgboost, 我們知道同屬boosting流派,但集成策略又有不同, 即使集成策略類似,那麼得到最後結果的方式又不同。但對比之中,我們能更加體會它們的原理。其次,我們從數學原理的角度剖析了一下xgboost, 看到了它的目標函數,看到了如何生成一棵樹,看到了如何Taylor化簡,知道了為什麼需要損失函數的一二階導數,也明白了為啥這個算法這麼快。最後,我們通過實戰一個二分類問題,見識到了xgboost的代碼實現,基本使用和一些高級策略。
精度更高:GBDT只用到一階泰勒, 而xgboost對損失函數進行了二階泰勒展開, 一方面為了增加精度, 另一方面也為了能夠自定義損失函數,二階泰勒展開可以近似大量損失函數靈活性更強:GBDT以CART作為基分類器,而Xgboost不僅支持CART,還支持線性分類器,另外,Xgboost支持自定義損失函數,只要損失函數有一二階導數。正則化:xgboost在目標函數中加入了正則,用於控制模型的複雜度。有助於降低模型方差,防止過擬合。正則項裡包含了樹的葉子節點個數,葉子節點權重的L2範式。Shrinkage(縮減):相當於學習速率。這個主要是為了削弱每棵樹的影響,讓後面有更大的學習空間,學習過程更加的平緩列抽樣:這個就是在建樹的時候,不用遍歷所有的特徵了,可以進行抽樣,一方面簡化了計算,另一方面也有助於降低過擬合缺失值處理:這個是xgboost的稀疏感知算法,加快了節點分裂的速度
下面看看xgboost相比於GBDT有哪些優點(面試的時候可能會涉及):上面的這些優點,我在描述的時候基本上都涉及到了,正是因為xgboost有了這些優點,才讓它變得非常火,堪稱神器了現在,但是xgboost真的perfect了嗎?正所謂金無足赤,人無完人, xgboost也同樣如此,比如雖然利用了預排序和近似算法可以降低尋找最優分裂點的計算量,但在節點分裂過程中仍需要遍歷整個數據集。預排序過程的空間複雜度過高,不僅需要存儲特徵值,還需要存儲特徵對應樣本梯度統計值的索引,相當於消耗了兩倍的內存。所以在內存和計算方面還是有很大的優化空間的。那麼xgboost還可以在哪些角度進行優化呢?後面通過lightgbm的故事再說給你聽 ;)
xgboost的故事就先講到這裡了,希望對你有所幫助,當然還有很多的細節沒有提到,本文只是拋磚引玉,具體的建議去看看原文,畢竟這個算法還是超級重要的,面試的時候也會摳得很細, 不看原文的話有些精華get不到。
參考:
xgboost論文原文 - 權威經典 : https://arxiv.org/pdf/1603.02754.pdfAdaboost、GBDT與XGBoost的區別: https://blog.csdn.net/hellozhxy/article/details/82143554xgboost算法詳細介紹(通過簡單例子講述): https://blog.csdn.net/wufengqi7585/article/details/86078049Introduction to Boosted Trees: https://xgboost.readthedocs.io/en/latest/tutorials/model.html終於有人把XGBoost 和 LightGBM 講明白了,項目中最主流的集成算法!:https://mp.weixin.qq.com/s/q4R-TAG4PZAdWLb41oov8gxgboost的原理沒你想像的那麼難: https://www.jianshu.com/p/7467e616f227XGBoost算法的原理詳析[文獻閱讀筆記]: https://zhuanlan.zhihu.com/p/90520307XGBoost原理介紹: https://blog.csdn.net/yinyu19950811/article/details/81079192靈魂拷問,你看過Xgboost原文嗎?: https://zhuanlan.zhihu.com/p/86816771一文讀懂機器學習大殺器XGBoost原理: https://zhuanlan.zhihu.com/p/40129825