日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【白话机器学习】算法理论+实战之Xgboost算法

發布時間:2025/3/8 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【白话机器学习】算法理论+实战之Xgboost算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 寫在前面

如果想從事數據挖掘或者機器學習的工作,掌握常用的機器學習算法是非常有必要的,在這簡單的先捋一捋, 常見的機器學習算法:

  • 監督學習算法:邏輯回歸,線性回歸,決策樹,樸素貝葉斯,K近鄰,支持向量機,集成算法Adaboost等

  • 無監督算法:聚類,降維,關聯規則, PageRank等

已發布:

【白話機器學習】算法理論+實戰之K近鄰算法

【白話機器學習】算法理論+實戰之決策樹

【白話機器學習】算法理論+實戰之樸素貝葉斯

【白話機器學習】算法理論+實戰之支持向量機(SVM)

【白話機器學習】算法理論+實戰之AdaBoost算法

【白話機器學習】算法理論+實戰之K-Means聚類算法

【白話機器學習】算法理論+實戰之PCA降維

【白話機器學習】算法理論+實戰之EM聚類

【白話機器學習】算法理論+實戰之關聯規則

【白話機器學習】算法理論+實戰之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)、Xgboost

  • Bagging流派,各分類器之間沒有依賴關系,可各自并行,比如隨機森林(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塊]

  • 第一棵決策樹用這個樣本訓練的預測為950

  • 那么第二棵決策樹訓練時的輸入,這個樣本就變成了:[(feature1,feature2,feature3)->50]

  • 第二棵決策樹用這個樣本訓練的預測為30

  • 那么第三棵決策樹訓練時的輸入,這個樣本就變成了:[(feature1,feature2,feature3)->20]

  • 第三棵決策樹用這個樣本訓練的預測為20

  • 搞定,也就是說,下一棵決策樹輸入樣本會與前面決策樹的訓練和預測相關。用個圖來表示類似這樣:這就是GBDT的工作原理了, GBDT是旨在不斷減少殘差(回歸),通過不斷加入新的樹旨在在殘差減少(負梯度)的方向上建立一個新的模型?!磽p失函數是旨在最快速度降低殘差。

    那么為啥要講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 個基模型組成的一個加法運算式:

    其中為第k個基模型, 為第i個樣本的預測值。那么損失函數可由預測值與真實值進行表示:

    其中n為樣本數量。

    XGBoost算法通過優化結構化損失函數(加入了正則項的損失函數,可以起到降低過擬合的風險)來實現弱學習器的生成,并且XGBoost算法沒有采用搜索方法,而是直接利用了損失函數的一階導數和二階導數值,并通過預排序、加權分位數等技術來大大提高了算法的性能。

    說到這里,估計你已經看不下去了吧,這說的啥跟啥啊, 聽不懂啦啊!但其實我上面只是用數學的語言來說了一下前面舉得xgboost的那個例子,對于某個樣本,有若干個弱分類器做預測,最后的預測結果就是弱分類器的答案累加(注意此時沒有權重了,如果你還記得AdaBoost模型的話,會發現那個地方每個分類器前面會有個權重,最終分類器是,不騙你喲), 這是上面的第一個公式,第二個公式就是說我怎么判斷對于整個數據集預測的準不準啊,就得有個損失函數啊, 對比一下與真實值的差距,n個樣本,我都對比一下子。這個表示的某種損失函數,你可以先理解成平方差損失。

    如此白話,應該能聽懂了吧, 但還沒真正講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與抑制模型復雜度的正則項Ω組成, 所以目標函數如下:

  • 這個公式應該不需要過多的解釋了吧, 其中是正則化項

    前面的為葉子節點數, 表示j葉子上的節點權重, ,是預先給定的超參數。引入了正則化之后,算法會選擇簡單而性能優良的模型, 正則化項只是用來在每次迭代中抑制弱分類器過擬合,不參與最終模型的集成。(這個正則化項可以先不用管它, ?有個印象即可,后面樹那個地方會統一解釋)

    我們下面看看這個目標函數還能不能化簡呢?
    我們知道, boosting模型是前向加法, 以第t步模型為例, 模型對第i個樣本的預測為:

    其中,是第t-1步的模型給出的預測值, 是已知常數,是我們這次需要加入的新模型,所以把這個代入上面,就可以進一步化簡:

    這個就是xgboost的目標函數了,最優化這個目標函數,其實就是相當于求解當前的。Xgboost系統的每次迭代都會構建一顆新的決策樹,決策樹通過與真實值之間殘差來構建。什么,沒有看到殘差的身影?別急,后面會看到這個殘差長什么樣子。
    我們回到我們的例子,假設已經根據天賦這個屬性建立了一棵決策樹A(關于如何建樹在這里不做解釋,可以看看[白話機器學習算法理論+實戰之決策樹]),只不過這里的樹分裂計算收益的方式換了一種,后面會具體說到。

    我們有了第一棵樹, 通過這個樹的預測結果:那么我們建立第二棵樹的時候,我們是考慮的殘差,也就是樣本其實變成了下面這樣:通過最小化殘差學習到一個通過學習時間屬性構建的決策樹得到了90+5,60+5,90-5的預測值,再繼續通過(100-95=5)(70-65)(86-85)的殘差構建下一個決策樹,以此類推,當迭代次數達到上限或是殘差不再減小是停止,就得到一個擁有多個(迭代次數)決策樹的強分類器。這個就是xgboost工作的宏觀過程了。光宏觀部分確實挺好理解,但具體細節呢?比如我每一次建樹是怎么建的呢?既然說計算收益的方式不同, ?那么我考慮分裂的時候是怎么計算收益的呢?目前你心中肯定會有這些疑問, 莫慌莫慌, 下面我把建樹的細節給你娓娓道來,不過道來的過程中得崎嶇一點,需要用到數學的語言。

    那么究竟是如何得到一棵新的樹的呢?下面就是Xgboost的精髓了。前方高能,一大波數學公式來襲, 請戴好安全帽!!!

  • 目標函數的Taylor化簡:這是Xgboost的精髓了。我們首先看一個關于目標函數的簡單等式變換, 把上面的目標函數拿過來:

  • 我們看看這里的,這個部分,如果結合偉大的Taylor的話,會發生什么情況,你還記得泰勒嗎?

    ★在這里插入圖片描述”

    根據Taylor公式, 我們把函數在點處二階展開,可得到:

    那么我們把中的視為, 視為, 那么目標函數就可以寫成:

    其中是損失函數的一階導數, 是損失函數的二階導, 注意這里的求導是對求導。

    這里我們以平方損失函數為例:

    ”★

    則對于每一個樣本:

    由于在第t步時是一個已知的值, 所以是一個常數,其對函數的優化不會產生影響, 因此目標函數可以進一步寫成:

    所以我們只需要求出每一步損失函數的一階導和二階導的值(由于前一步的是已知的,所以這兩個值就是常數),然后最優化目標函數,就可以得到每一步的 f(x) ,最后根據加法模型得到一個整體模型。
    但是還有個問題,就是我們如果是建立決策樹的話,根據上面的可是無法建立出一棵樹來。因為這里的是什么鬼??咱不知道啊!所以啊,還得進行一步映射, 將樣本x映射到一個相對應的葉子節點才可以,看看是怎么做的?

  • 基于決策樹的目標函數的終極化簡上面的目標函數先放下來:

  • 我們這里先解決一下這個的問題,這個究竟怎么在決策樹里面表示呢?那解決這個問題之前,我們看看這個表示的含義是什么, 就是我有一個決策樹模型, 是每一個訓練樣本,那么這個整體就是我某一個樣本經過決策樹模型得到的一個預測值,對吧?那么,我如果是在決策樹上,可以這么想, 我的決策樹就是這里的, 然后對于每一個樣本,我要在決策樹上遍歷獲得預測值,其實就是在遍歷決策樹的葉子節點,因為每個樣本最終通過決策樹都到了葉子上去, 不信?看下圖(樣本都在葉子上, 只不過這里要注意一個葉子上不一定只有一個樣本):所以,通過決策樹遍歷樣本,其實就是在遍歷葉子節點。這樣我們就可以把問題就行轉換,把決策樹模型定義成, 其中代表了該樣本在哪個葉子節點上, 表示該葉子節點上的權重(上面分數預測里面+90, +60就是葉子節點的權重)。所以就代表了每個樣本的取值(預測值)。?那么這個樣本的遍歷,就可以這樣化簡:

    這個再解釋一遍就是:遍歷所有的樣本后求每個樣本的損失函數,但樣本最終會落在葉子節點上,所以我們也可以遍歷葉子節點,然后獲取葉子節點上的樣本集合(注意第二個等式和第三個等式求和符號的上下標, T代表葉子總個數)。 由于一個葉子節點有多個樣本存在,所以后面有了和這兩項,這里的它代表一個集合,集合中每個值代表一個訓練樣本的序號,整個集合就是某棵樹第j個葉子節點上的訓練樣本 , 為第j個葉子節點的取值。只要明白了這一步,后面的公式就很容易理解了。
    我們再解決一下后面那部分,在決策樹中,決策樹的復雜度可由葉子數 T 組成,葉子節點越少模型越簡單,此外葉子節點也不應該含有過高的權重 w (類比 LR 的每個變量的權重),所以目標函數的正則項可以定義為:

    即決策樹模型的復雜度由生成的所有決策樹的葉子節點數量(權衡),和所有節點權重(權衡)所組成的向量的 ?范式共同決定。這張圖給出了基于決策樹的 XGBoost 的正則項的求解方式。

    這樣,目標函數的前后兩部分都進行了解決,那么目標函數就可以化成最后這個樣子,看看能懂嗎?

    這里的為第j個葉子節點的樣本集合。為了簡化表達式,我們再定義:, 那么決策樹版本xgboost的目標函數:

    這里要注意和是前t-1步得到的結果, 其值已知,只有最后一棵樹的葉子節點的值不確定, 那么將目標函數對求一階導, 并令其等于0, , 則可以求得葉子節點j對應的權值:

    那么這個目標函數又可以進行化簡:

    這個就是基于決策樹的xgboost模型的目標函數最終版本了,這里的G和H的求法,就需要明確的給出損失函數來, 然后求一階導和二階導,然后代入樣本值即得出。
    這個代表了當我們指定一個樹的結構的時候,我們在目標上最多能夠減少多少,我們之前不是說建立一個樹就是讓殘差盡可能的小嗎?到底小多少呢?這個就是衡量這個的, 可以叫做結構分數。就類似于基尼系數那樣對樹結構打分的一個函數。那么這個分數怎么算呢?看下面的例子:

    還是上面的那個預測玩游戲的意愿,我們假設建了右邊的那棵樹,那么每個樣本都對應到了葉子節點上去,每一個樣本都會對應一個g和h, 那么我們遍歷葉子節點,就會得到G和H,然后累加就可以得到這棵樹的結構分數obj(這里有個小細節就是假設有N個訓練樣本, 那么就會有N次計算各自的和, 但是由于每個樣本的和沒有啥關系,所以可以并行計算,這樣就可以加速訓練了,而且,和是不依賴于損失函數的形式的,只要這個損失函數二次可微就可以了, emmm...powerful)。

    有了這個,我們就知道這棵樹建的好不好了。

    上面是可以判斷出來一棵樹究竟好不好,那么建立樹的時候應該怎么建立呢?一棵樹的結構近乎無限多,總不能一個一個去測算它們的好壞程度,然后再取最好的吧(這是個NP問題)。所以,我們仍然需要采取一點策略,這就是逐步學習出最佳的樹結構。這與我們將K棵樹的模型分解成一棵一棵樹來學習是一個道理,只不過從一棵一棵樹變成了一層一層節點而已。這叫什么?emmm, 貪心(找到每一步最優的分裂結果)!xgboost采用二叉樹, 開始的時候, 全部樣本在一個葉子節點上, 然后葉子節點不斷通過二分裂,逐漸生成一棵樹。

    那么在葉子節點分裂成樹的過程中最關鍵的一個問題就是應該在哪個特征的哪個點上進行分裂,也就是尋找最優切分點的過程。

  • 最優切分點劃分算法及優化策略在決策樹的生長過程中,一個非常關鍵的問題是如何找到節點的最優切分點, 我們學過了決策樹的建樹過程,那么我們知道ID3也好,C4.5或者是CART,它們尋找最優切分點的時候都有一個計算收益的東西,分別是信息增益,信息增益比和基尼系數。而xgboost這里的切分, 其實也有一個類似于這三個的東西來計算每個特征點上分裂之后的收益。

    假設我們在某一節點完成特征分裂,則分列前的目標函數可以寫為:

    分裂后的目標函數:

    則對于目標函數來說,分裂后的收益為(Obj1-Obj2):

    注意該特征收益也可作為特征重要性輸出的重要依據。

    那么我們就可以來梳理一下最優切分點的劃分算法了:

    • 從深度為 0 的樹開始,對每個葉節點枚舉所有的可用特征;

    • 針對每個特征,把屬于該節點的訓練樣本根據該特征值進行升序排列,通過線性掃描的方式來決定該特征的最佳分裂點,并記錄該特征的分裂收益;(這個過程每個特征的收益計算是可以并行計算的,xgboost之所以快,其中一個原因就是因為它支持并行計算,而這里的并行正是指的特征之間的并行計算,千萬不要理解成各個模型之間的并行)

    • 選擇收益最大的特征作為分裂特征,用該特征的最佳分裂點作為分裂位置,在該節點上分裂出左右兩個新的葉節點,并為每個新節點關聯對應的樣本集(這里稍微提一下,xgboost是可以處理空值的,也就是假如某個樣本在這個最優分裂點上值為空的時候, 那么xgboost先把它放到左子樹上計算一下收益,再放到右子樹上計算收益,哪個大就把它放到哪棵樹上。)

    • 回到第 1 步,遞歸執行到滿足特定條件為止


    上面就是最優切分點劃分算法的過程, 看完之后,是不是依然懵逼, 這到底是怎么做的啊, 下面就看一個尋找最優切分點的栗子吧:
    還是上面玩游戲的那個例子,假設我有這一家子人樣本,每個人有性別,年齡,興趣等幾個特征,我想用xgboost建立一棵樹預測玩游戲的意愿值。首先,五個人都聚集在根節點上,現在就考慮根節點分叉,我們就遍歷每個特征,對于當前的特征,我們要去尋找最優切分點以及帶來的最大收益,比如當前特征是年齡,我們需要知道兩點:* 按照年齡分是否有效,也就是是否減少了obj的值 * 如果真的可以分,特征收益比較大, 那么我們從哪個年齡點分開呢?


    對于這兩個問題,我們可以這樣做,首先我們先把年齡進行一個排序, 如下圖:按照這個圖從左至右掃描,我們就可以找出所有的切分點a, 對于每一個切分點a,計算出分割的梯度和和 。然后用上面的公式計算出每個分割方案的分數。然后哪個最大,就是年齡特征的最優切分點,而最大值就是年齡這個特征的最大信息收益。

    遍歷完所有特征后,我們就可以確定應該在哪個特征的哪個點進行切分。對切分出來的兩個節點,遞歸地調用這個過程,我們就能獲得一個相對較好的樹結構, 有了樹結構就比較容易找最優的葉子節點,這樣就能對上面的樣本進行預測了。當然,特征與特征之間的收益計算是互不影響的,所以這個遍歷特征的過程其實可以并行運行。

    在這個過程中你是否注意到了一個問題, 就是xgboost的切分操作和普通的決策樹切分過程是不一樣的。普通的決策樹在切分的時候并不考慮樹的復雜度,所以才有了后續的剪枝操作。而xgboost在切分的時候就已經考慮了樹的復雜度(obj里面看到那個了嗎)。所以,它不需要進行單獨的剪枝操作。

    這就是xgboost貪心建樹的一個思路了,即遍歷所有特征以及所有分割點,每次選最好的那個。GBDT也是采用的這種方式, 這算法的確不錯,但是有個問題你發現了沒?就是計算代價太大了,尤其是數據量很大,分割點很多的時候,計算起來非常復雜并且也無法讀入內存進行計算。所以作者想到了一種近似分割的方式(可以理解為分割點分桶的思路),選出一些候選的分裂點,然后再遍歷這些較少的分裂點來找到最佳分裂點。那么怎么進行分桶選候選分裂點才比較合理呢?我們一般的思路可能是根據特征值的大小直接進行等寬或者等頻分桶, 像下面這樣(這個地方理解起來有點難,得畫畫了,圖可能不太好看,能說明問題就行,哈哈):

    在這里插入圖片描述

    上面就是等頻和等寬分桶的思路了(這個不用較真,我這里只是為了和作者的想法產生更清晰的對比才這樣舉得例子),這樣選擇出的候選點是不是比就少了好多了?但是這樣劃分其實是有問題的,因為這樣劃分沒有啥依據啊, 比如我上面畫的等頻分桶,我是5個訓練樣本放一個桶,但是你說你還想10個一組來,沒有個標準啥的啊。即上面那兩種常規的劃分方式缺乏可解釋性,所以重點來了, 作者這里采用了一種對loss的影響權重的等值percentiles(百分比分位數)劃分算法(Weight Quantile Sketch), 我上面的這些鋪墊也正是為了引出這個方式,下面就來看看作者是怎么做的,這個地方其實不太好理解,所以慢一些

    作者進行候選點選取的時候,考慮的是想讓loss在左右子樹上分布的均勻一些,而不是樣本數量的均勻,因為每個樣本對降低loss的貢獻可能不一樣,按樣本均分會導致分開之后左子樹和右子樹loss分布不均勻,取到的分位點會有偏差。這是啥意思呢?再來一個圖(這個圖得看明白了):這其實就是作者提出的那種找候選節點的方式(分桶的思路),明白了這個圖之后,下面就是解釋一下上面這個圖的細節:第一個就是是啥?它為啥就能代表樣本對降低loss的貢獻程度??第二個問題就是這個bin是怎么分的,為啥是0.6一個箱?

    下面從第一個問題開始,揭開的神秘面紗, 其實上面已經說過了,損失函數在樣本處的二階導數啊!還記得開始的損失函數嗎?

    就是這個, 那么你可能要問了, 為啥它就能代表第個樣本的權值啊?這里再拓展一下吧,我們在引出xgboost的時候說過,GBDT這個系列都是聚焦在殘差上面,但是我們單看這個目標函數的話并沒有看到什么殘差的東西對不對?其實這里這個損失函數還可以進一步化簡的(和上面的化簡不一樣,上面的化簡是把遍歷樣本轉到了遍歷葉子上得到基于決策樹的目標函數,這里是從目標函數本身出發進行化簡):

    這樣化簡夠簡潔明了了吧, 你看到殘差的身影了嗎?后面的每一個分類器都是在擬合每個樣本的一個殘差, 其實把上面化簡的平方損失函數拿過來就一目了然了。而前面的可以看做計算殘差時某個樣本的重要性,即每個樣本對降低loss的貢獻程度。第一個問題說的聽清楚了吧 ?;)

    PS:這里加點題外話,Xgboost引入了二階導之后,相當于在模型降低殘差的時候給各個樣本根據貢獻度不同加入了一個權重,這樣就能更好的加速擬合和收斂,GBDT只用到了一階導數,這樣只知道梯度大的樣本降低殘差效果好,梯度小的樣本降低殘差不好(這個原因我會放到Lightgbm的GOSS那里說到),但是好與不好的這個程度,在GBDT中無法展現。而xgboost這里就通過二階導可以展示出來,這樣模型訓練的時候就有數了。

    下面再解釋第二個問題,這個分箱是怎么分的?比如我們定義一個數據集代表每個訓練樣本的第個特征的取值和二階梯度值,那么我們可以有一個排名函數:

    這里的代表特征值小于的那些樣本。這個排名函數表示特征值小于z的樣本的貢獻度比例。假設上面圖中,z是第一個候選點,那么, 這個東西的目的就是去找相對準確的候選點, 這里的, 而相鄰兩個桶之間樣本貢獻度的差距應滿足下面這個函數:

    這個控制每個桶中樣本貢獻度比例的大小,其實就是貢獻度的分位點。我們自己設定。比如在上面圖中我們設置了, 這意味著每個桶樣本貢獻度的比例是1/3(貢獻度的1/3分位點), 而所有的樣本貢獻度總和是1.8, 那么每個箱貢獻度是0.6(),分為3()個箱, 上面這些公式看起來挺復雜,可以計算起來很簡單,就是計算一下總的貢獻度, 然后指定, 兩者相乘得到每個桶的貢獻度進行分桶即可。這樣我們就可以確定合理的候選切分點,然后進行分箱了。

    到這終于把這一塊描述完了, 有點多,稍微理一理邏輯,前面那一部分是圍繞著如何建立一棵樹進行的,即采用貪心的方式從根節點開始一層層的建立樹結構(每一層爭取最優),然后就是建樹過程中一個關鍵的問題:如何尋找最優切分點,給出了最優切分點算法,基于這個算法就可以建立樹了。后面這一部分是一個優化的過程,提出了一種Weight Quantile Sketch的算法,這個算法可以將原來的分割點進行分桶,然后找到合適的候選分裂點,這樣可以減少遍歷時嘗試的分裂點的數量,是xgboost相比于GBDT做出的切分點優化策略,現在知道為啥xgboost要快了吧,因為xgboost尋找切分點的時候不用遍歷所有的,而是只看候選點就可以了。而且在特征上,xgboost是可以并行處理的。這樣xgboost的建樹過程及優化策略基本上就是這些了,當然這里面還有很多的細節,由于篇幅的原因就不寫在這里了。

  • 利用新的決策樹預測樣本值,并累加到原來的值上若干個決策樹是通過加法訓練的, 所謂加法訓練,本質上是一個元算法,適用于所有的加法模型,它是一種啟發式算法。運用加法訓練,我們的目標不再是直接優化整個目標函數,而是分步驟優化目標函數,首先優化第一棵樹,完了之后再優化第二棵樹,直至優化完K棵樹。整個過程如下圖所示:

  • 在這里插入圖片描述

    上圖中會發現每一次迭代得到的新模型前面有個(這個是讓樹的葉子節點權重乘以這個系數), 這個叫做收縮率,這個東西加入的目的是削弱每棵樹的作用,讓后面有更大的學習空間,有助于防止過擬合。也就是,我不完全信任每一個殘差樹,每棵樹只學到了模型的一部分,希望通過更多棵樹的累加來來彌補,這樣讓這個讓學習過程更平滑,而不會出現陡變。這個和正則化防止過擬合的原理不一樣,這里是削弱模型的作用,而前面正則化是控制模型本身的復雜度。

    好了, 到這里為止,xgboost的數學原理部分就描述完了,希望我描述清楚了吧。簡單的回顧一下上面的過程吧: xgboost是好多弱分類器的集成,訓練弱分類器的策略就是盡量的減小殘差,使得答案越來越接近正確答案。xgboost的精髓部分是目標函數的Taylor化簡,這樣就引入了損失函數的一階和二階導數。然后又把樣本的遍歷轉成了對葉子節點的遍歷,得到了最終的目標函數。這個函數就是衡量一棵樹好壞的標準。在建樹過程中,xgboost采用了貪心策略,并且對尋找分割點也進行了優化。基于這個,才有了后面的最優點切分建立一棵樹的過程。xgboost訓練的時候,是通過加法進行訓練,也就是每一次只訓練一棵樹出來, 最后的預測結果是所有樹的加和表示。?

    關于xgboost,依然還有很多的細節沒有說到,具體的去看論文吧。下面,我們就進行xgboost的實戰部分, 這里我們簡單的做一個分類任務, 主要是看看xgboost主要怎么用, 尤其是在一個數據競賽中(這次重點總結了一些用法)。

    3. Xgboost實戰二分類

    安裝:默認可以通過pip安裝,若是安裝不上可以通過:

    https://www.lfd.uci.edu/~gohlke/pythonlibs/

    網站下載相關安裝包,將安裝包拷貝到Anacoda3的安裝目錄的Scrripts目錄下, 然后pip install 安裝包安裝

    3.1 xgboost的基本使用

    • Xgboost參數說明頁面:

    https://xgboost.readthedocs.io/en/latest/parameter.html

    • Xgboost調參官方指南

    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.

    • 'eta': 0.007, 如同學習率

    • 'seed':1000,

    • 'nthread':7, cpu 線程數

    當然不需要全記住,常用的幾個記住即可。可以結合著上面的數學原理,看看哪個參數到底對于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']?=?ratioreturn?(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?-?labelshess?=?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_importance

    from?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?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))

    好了,實戰部分就整理這么多吧, 重點在于怎么使用,xgboost使用起來和sklearn的模型也是非常像, 也是.fit(), .predict()方法,只不過xgboost的參數很多,這個調起來會比較復雜, 但是懂了原理之后,至少每個參數是干啥的就了解了,關于調參的技術, 得從經驗中多學習,多嘗試,多總結才能慢慢修煉出來。

    4. 總結

    哇,終于完了,到這里,終于把xgboost的一些知識說清楚了, 每一次不知不覺就會這么多字, 可能是因為這個算法太重要了吧,所以多寫了點, 趕緊回顧一下:首先, 我們從集成算法開始講起,回顧了一下AdaBoost,GBDT, 然后引出了xgboost, 我們知道同屬boosting流派,但集成策略又有不同, 即使集成策略類似,那么得到最后結果的方式又不同。但對比之中,我們能更加體會它們的原理。其次,我們從數學原理的角度剖析了一下xgboost, 看到了它的目標函數,看到了如何生成一棵樹,看到了如何Taylor化簡,知道了為什么需要損失函數的一二階導數,也明白了為啥這個算法這么快。最后,我們通過實戰一個二分類問題,見識到了xgboost的代碼實現,基本使用和一些高級策略。

    下面看看xgboost相比于GBDT有哪些優點(面試的時候可能會涉及):

    • 精度更高:GBDT只用到一階泰勒, 而xgboost對損失函數進行了二階泰勒展開, 一方面為了增加精度, 另一方面也為了能夠自定義損失函數,二階泰勒展開可以近似大量損失函數

    • 靈活性更強:GBDT以CART作為基分類器,而Xgboost不僅支持CART,還支持線性分類器,另外,Xgboost支持自定義損失函數,只要損失函數有一二階導數。

    • 正則化:xgboost在目標函數中加入了正則,用于控制模型的復雜度。有助于降低模型方差,防止過擬合。正則項里包含了樹的葉子節點個數,葉子節點權重的L2范式。

    • Shrinkage(縮減):相當于學習速率。這個主要是為了削弱每棵樹的影響,讓后面有更大的學習空間,學習過程更加的平緩

    • 列抽樣:這個就是在建樹的時候,不用遍歷所有的特征了,可以進行抽樣,一方面簡化了計算,另一方面也有助于降低過擬合

    • 缺失值處理:這個是xgboost的稀疏感知算法,加快了節點分裂的速度

    • 并行化操作:塊結構可以很好的支持并行計算

    上面的這些優點,我在描述的時候基本上都涉及到了,正是因為xgboost有了這些優點,才讓它變得非?;?#xff0c;堪稱神器了現在,但是xgboost真的perfect了嗎?正所謂金無足赤,人無完人, xgboost也同樣如此,比如雖然利用了預排序和近似算法可以降低尋找最優分裂點的計算量,但在節點分裂過程中仍需要遍歷整個數據集。預排序過程的空間復雜度過高,不僅需要存儲特征值,還需要存儲特征對應樣本梯度統計值的索引,相當于消耗了兩倍的內存。所以在內存和計算方面還是有很大的優化空間的。那么xgboost還可以在哪些角度進行優化呢?后面通過lightgbm的故事再說給你聽 ;)

    xgboost的故事就先講到這里了,希望對你有所幫助,當然還有很多的細節沒有提到,本文只是拋磚引玉,具體的建議去看看原文,畢竟這個算法還是超級重要的,面試的時候也會摳得很細, 不看原文的話有些精華get不到。

    參考

    • xgboost論文原文 - 權威經典 : https://arxiv.org/pdf/1603.02754.pdf

    • Adaboost、GBDT與XGBoost的區別: https://blog.csdn.net/hellozhxy/article/details/82143554

    • xgboost算法詳細介紹(通過簡單例子講述): 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算法的原理詳析[文獻閱讀筆記]: https://zhuanlan.zhihu.com/p/90520307

    • XGBoost原理介紹: https://blog.csdn.net/yinyu19950811/article/details/81079192

    • 靈魂拷問,你看過Xgboost原文嗎?: https://zhuanlan.zhihu.com/p/86816771

    • 一文讀懂機器學習大殺器XGBoost原理: https://zhuanlan.zhihu.com/p/40129825


    往期精彩回顧適合初學者入門人工智能的路線及資料下載機器學習及深度學習筆記等資料打印機器學習在線手冊深度學習在線手冊AI基礎下載(pdf更新到25集)本站qq群1003271085,加入微信群請回復“加群”獲取一折本站知識星球優惠券,復制鏈接直接打開:https://t.zsxq.com/yFQV7am喜歡文章,點個在看

    總結

    以上是生活随笔為你收集整理的【白话机器学习】算法理论+实战之Xgboost算法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。