xgboost極限梯度提升算法 (boosting提升法)


xgboost

xgboost簡介

  • XGBoost全稱是eXtreme Gradient Boosting,可譯為極限梯度提升算法。它由陳天奇所設計,致力於讓提升樹突破自身的計算極限,以實現運算快速,性能優秀的工程目標。和傳統的梯度提升算法相比,XGBoost進行了許多改進,並且已經被認為是在分類和回歸上都擁有超高性能的先進評估器。 在各平台的比賽中、高科技行業和數據咨詢等行業也已經開始逐步使用XGBoost,了解這個算法,已經成為學習機器學習中必要的一環。
  • 性能超強的算法往往有着復雜的原理,XGBoost也不能免俗,因此它背后的數學深奧復雜。但是授課中,我們不會重點講解和關注其算法的底層實現和原理,只要大家能夠把XGBoost合理的運用在我們的機器學習項目中且能夠創造真實價值就足夠了。

xgboost庫與XGB的sklearn API

  • 在開始講解XGBoost的細節之前,我先來介紹我們可以調用XGB的一系列庫,模塊和類。陳天奇創造了XGBoost之后,很快和一群機器學習愛好者建立了專門調用XGBoost庫,名為xgboost。xgboost是一個獨立的,開源的,專門提供XGBoost算法應用的算法庫。它和sklearn類似,有一個詳細的官方網站可以供我們查看,並且可以與C,Python,R,Julia等語言連用,但需要我們單獨安裝和下載。
  • xgboost庫要求我們必須要提供適合的Scipy環境,如果你是使用anaconda安裝 的Python,你的Scipy環境應該是沒有什么問題。

    image.png

    image.png

我們有兩種方式可以來使用我們的xgboost庫

第一種方式:是直接使用xgboost庫自己的建模流程

  image.png

  • 其中最核心的,是DMtarix這個讀取數據的類,以及train()這個用於訓練的類。與sklearn把所有的參數都寫在類中的方式不同,xgboost庫中必須先使用字典設定參數集,再使用train來將參數及輸入,然后進行訓練。會這樣設計的原因,是因為XGB所涉及到的參數實在太多,全部寫在xgb.train()中太長也容易出錯。在這里,我為大家准備了 params可能的取值以及xgboost.train的列表,給大家一個印象。

    image.png

第二種方式:使用XGB庫中的sklearn的API

  • 這是說,我們可以調用如下的類,並用我們sklearn當中慣例的實例化,fit和predict的流程來運行XGB,並且也可以調用屬性比如coef_等等。當然,這是我們回歸的類,我們也有用於分類的類。他們與回歸的類非常相似,因此了解一個類即可。

    image.png

  • 看到這長長的參數列表,可能大家會感到頭暈眼花——沒錯XGB就是這么的復雜。但是眼尖的小伙伴可能已經發現了, 調用xgboost.train和調用sklearnAPI中的類XGBRegressor,需要輸入的參數是不同的,而且看起來相當的不同。但其實,這些參數只是寫法不同,功能是相同的。
    • 比如說,我們的params字典中的第一個參數eta,其實就是我們XGBRegressor里面的參數learning_rate,他們的含義和實現的功能是一模一樣的。只不過在sklearnAPI中,開發團隊友好地幫助我們將參數的名稱調節成了與sklearn中其他的算法類更相似的樣子。

兩種使用方式的區別:

  • 使用xgboost中設定的建模流程來建模,和使用sklearnAPI中的類來建模,模型效果是比較相似的,但是xgboost庫本身的運算速度(尤其是交叉驗證)以及調參手段比sklearn要簡單。
  • 但是,我們前期已經習慣使用sklearn的調用方式,我們就先使用sklearn的API形式來講解和使用XGB。

梯度提升算法:XGBoost的基礎是梯度提升算法,因此我們必須先從了解梯度提升算法開始。

  • 梯度提升(Gradient boosting):
    • 是構建預測模型的最強大技術之一,它是集成算法中提升法(Boosting)的代表算法。集成算法通過在數據上構建多個弱評估器,匯總所有弱評估器的建模結果,以獲取比單個模型更好的回歸或分類表現。
      • 集成不同弱評估器的方法有很多種。就像我們曾經在隨機森林的課中介紹的,一次性建立多個平行獨立的弱評估器的裝袋法。也有像我們今天要介紹的提升法這樣,逐一構建弱評估器,經過多次迭代逐漸累積多個弱評估器的方法。
    • 我們知道梯度提升法是集成算法中提升法(Boosting)的代表算法。回顧:在集成學習講解中我們說boosting算法是將其中參與訓練的基礎學習器按照順序生成。序列方法的原理是利用基礎學習器之間的依賴關系。通過對之前訓練中錯誤標記的樣本賦值較高的權重,可以提高整體的預測效果。
      • 基於梯度提升的回歸或分類模型來講,其建模過程大致如下:最開始先建立一棵樹,然后逐漸迭代,每次迭代過程中都增加一棵樹,逐漸形成眾多樹模型集成的強評估器。

    image.png

  • XGB算法原理:
    • XGB中構建的弱學習器為CART樹,這意味着XGBoost中所有的樹都是二叉的。
      • 在講解決策樹算法原理時,我們主要講解的是信息熵實現的樹模型,除此外,還有一種是基於基尼系數實現的CART樹,它既可以處理分類也可以處理回歸問題,並且構建出的只能是二叉樹。
    • XGBT中的預測值是所有弱分類器上的葉子節點權重直接求和得到,計算葉子權重是一個復雜的過程。
    • 那么什么是葉子的權重呢?
      • 先來舉個例子,我們要預測一家人對電子游戲的喜好程度,考慮到年輕和年老相比,年輕更可能喜歡電子游戲,以及男性和女性相比,男性更喜歡電子游戲,故先根據年齡大小區分小孩和大人,然后再通過性別區分開是男是女,逐一給各人在電子游戲喜好程度上打分,這個分值就是葉子節點的權重。假設,我們訓練出了2棵樹tree1和tree2,兩棵樹的結論的打分值累加起來便是最終的結論,所以小男孩的預測分數就是兩棵樹中小男孩所落到的結點的分數相加:2 + 0.9 = 2.9。爺爺的預測分數同理:-1 + (-0.9)= -1.9。具體如下圖所示::

      image.png

  • 因此,假設這個集成模型XGB中總共有k棵決策樹,則整個模型在這個樣本i上給出的預測結果為:

      image.png

    • yi(k)表示k課樹葉子節點權重的累和或者XGB模型返回的預測結果,K表示樹的總和,fk(xi)表示第k顆決策樹返回的葉子節點的權重(第k棵樹返回的結果)
  • 從上面的式子來看,在集成中我們需要的考慮的第一件事是我們的超參數K,究竟要建多少棵樹呢?
    • 試着回想一下我們在隨機森林中是如何理解n_estimators的:n_estimators越大,模型的學習能力就會越強,模型也 越容易過擬合。在隨機森林中,我們調整的第一個參數就是n_estimators,這個參數非常強大,常常能夠一次性將模 型調整到極限。在XGB中,我們也期待相似的表現,雖然XGB的集成方式與隨機森林不同,但使用更多的弱分類器來 增強模型整體的學習能力這件事是一致的。
from xgboost import XGBRegressor as XGBR
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error as MSE
from sklearn.model_selection import KFold, cross_val_score, train_test_split

data = load_boston()
X = data.data
y = data.target

Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,y,test_size=0.3,random_state=420)

xgboost

reg = XGBR(n_estimators=100).fit(Xtrain,Ytrain)
reg.score(Xtest,Ytest)   # 0.9050988954757183

MSE(Ytest,reg.predict(Xtest)) # 8.830916470718748

cross_val_score(reg,Xtrain,Ytrain,cv=5).mean()  # 0.7995062802699481

隨機森林

rfr = RFR(n_estimators=100).fit(Xtrain,Ytrain)
rfr.score(Xtest,Ytest)   # 0.8951649975839067

MSE(Ytest,rfr.predict(Xtest))  # 9.755304263157894

cross_val_score(rfr,Xtrain,Ytrain,cv=5).mean()  # 0.7940731408344127

model_count = []
scores = []
for i in range(50,170):
    xgb = XGBR(n_estimators=i).fit(Xtrain,Ytrain)
    score = xgb.score(Xtest,Ytest) 
    scores.append(score)
    model_count.append(i)

%matplotlib inline
import matplotlib.pyplot as plt
plt.plot(model_count,scores)

重要參數

有放回隨機抽樣:subsample(0-1,默認為1)

  • 確認了有多少棵樹之后,我們來思考一個問題:建立了眾多的樹,怎么就能夠保證模型整體的效果變強呢?集成的目的是為了模型在樣本上能表現出更好的效果,所以對於所有的提升集成算法,每構建一個評估器,集成模型的效果都會比之前更好。也就是隨着迭代的進行,模型整體的效果必須要逐漸提升,最后要實現集成模型的效果最優。要實現這個目標,我們可以首先從訓練數據上着手。
  • 我們訓練模型之前,必然會有一個巨大的數據集。我們都知道樹模型是天生容易發生過擬合,並且如果數據量太過巨 大,樹模型的計算會非常緩慢,因此,我們要對我們的原始數據集進行有放回抽樣(bootstrap)。有放回的抽樣每 次只能抽取一個樣本,若我們需要總共N個樣本,就需要抽取N次。每次抽取一個樣本的過程是獨立的,這一次被抽到的樣本會被放回數據集中,下一次還可能被抽到,因此抽出的數據集中,可能有一些重復的數據。
  • 在無論是裝袋還是提升的集成算法中,有放回抽樣都是我們防止過擬合,讓單一弱分類器變得更輕量的必要操作。實際應用中,每次抽取50%左右的數據就能夠有不錯的效果了。sklearn的隨機森林類中也有名為boostrap的參數來幫 助我們控制這種隨機有放回抽樣。
  • 在梯度提升樹中,我們每一次迭代都要建立一棵新的樹,因此我們每次迭代中,都要有放回抽取一個新的訓練樣本。不過,這並不能保證每次建新樹后,集成的效果都比之前要好。因此我們規定,在梯度提升樹中,每構建一個評估器,都讓模型更加集中於數據集中容易被判錯的那些樣本。
  • 首先我們有一個巨大的數據集,在建第一棵樹時,我們對數據進行初次又放回抽樣,然后建模。建模完畢后,我們對模型進行一個評估,然后將模型預測錯誤的樣本反饋給我們的數據集,一次迭代就算完成。緊接着,我們要建立第二棵決策樹,於是開始進行第二次又放回抽樣。但這次有放回抽樣,和初次的隨機有放回抽樣就不同了,在這次的抽樣中,我們加大了被第一棵樹判斷錯誤的樣本的權重。也就是說,被第一棵樹判斷錯誤的樣本,更有可能被我們抽中。
  • 基於這個有權重的訓練集來建模,我們新建的決策樹就會更加傾向於這些權重更大的,很容易被判錯的樣本。建模完畢之后,我們又將判錯的樣本反饋給原始數據集。下一次迭代的時候,被判錯的樣本的權重會更大,新的模型會更加傾向於很難被判斷的這些樣本。如此反復迭代,越后面建的樹,越是之前的樹們判錯樣本上的專家,越專注於攻克那些之前的樹們不擅長的數據。對於一個樣本而言,它被預測錯誤的次數越多,被加大權重的次數也就越多。我們相信,只要弱分類器足夠強大,隨着模型整體不斷在被判錯的樣本上發力,這些樣本會漸漸被判斷正確。如此就一定程度上實現了我們每新建一棵樹模型的效果都會提升的目標。
  • 在sklearn中,我們使用參數subsample來控制我們的隨機抽樣。在xgb和sklearn中,這個參數都默認為1且不能取到 0,這說明我們無法控制模型是否進行隨機有放回抽樣,只能控制抽樣抽出來的樣本量大概是多少。
  • 注意:那除了讓模型更加集中於那些困難錯誤樣本,采樣還對模型造成了什么樣的影響呢?采樣會減少樣本數量,而從學習曲線來看樣本數量越少模型的過擬合會越嚴重,因為對模型來說,數據量越少模型學習越容易,學到的規則也會越具體越不適用於測試樣本。所以subsample參數通常是在樣本量本身很大的時候來調整和使用。
import numpy as np

subs = []
scores = []
for i in np.linspace(0.05,1,20):
    xgb = XGBR(n_estimators=182,subsample=i).fit(Xtrain,Ytrain)
    score = xgb.score(Xtest,Ytest) 
    subs.append(i)
    scores.append(score)
plt.plot(subs,scores)

迭代的速率:learning_rate

  • 從數據的角度而言,我們讓模型更加傾向於努力攻克那些難以判斷的樣本。但是,並不是說只要我新建了一棵傾向於困難樣本的決策樹,它就能夠幫我把困難樣本判斷正確了。困難樣本被加重權重是因為前面的樹沒能把它判斷正確,所以對於下一棵樹來說,它要判斷的測試集的難度,是比之前的樹所遇到的數據的難度都要高的,那要把這些樣本都判斷正確,會越來越難。如果新建的樹在判斷困難樣本這件事上還沒有前面的樹做得好呢?如果我新建的樹剛好是一棵特別糟糕的樹呢?所以,除了保證模型逐漸傾向於困難樣本的方向,我們還必須控制新弱分類器的生成,我們必須保證,每次新添加的樹一定得是對這個新數據集預測效果最優的那一棵樹。
  • 思考:如何保證每次新添加的樹一定讓集成學習的效果提升呢?
    • 現在我們希望求解集成算法的最優結果,那我們可以:我們首先找到一個損失函數obj,這個損失函數應該可以通過帶入我們的預測結果y來衡量我們的梯度提升樹在樣本的預測效果。然后,我們利用梯度下降來迭代我們的集成算法。
    • 在模型中,使用參數learning_rate來表示迭代的速率。learning_rate值越大表示迭代速度越快,算法的極限會很快被達到,有可能無法收斂到真正最佳的損失值。learning_rate越小就越有可能找到更加精確的最佳值,但是迭代速度會變慢,耗費更多的運算空間和成本。
rates = []
scores = []
for i in np.linspace(0.05,1,20):
    xgb = XGBR(n_estimators=182,subsample=0.9,learning_rate=i).fit(Xtrain,Ytrain)
    score = xgb.score(Xtest,Ytest) 
    rates.append(i)
    scores.append(score)
plt.plot(rates,scores)

選擇弱評估器:booster

  • 梯度提升算法中不只有梯度提升樹,XGB作為梯度提升算法的進化,自然也不只有樹模型一種弱評估器。在XGB中, 除了樹模型,我們還可以選用線性模型,比如線性回歸,來進行集成。雖然主流的XGB依然是樹模型,但我們也可以 使用其他的模型。基於XGB的這種性質,我們有參數“booster"來控制我們究竟使用怎樣的弱評估器。

    image.png

 

for booster in ["gbtree","gblinear","dart"]:
    reg = XGBR(n_estimators=180
               ,learning_rate=0.1
               ,random_state=420
               ,booster=booster).fit(Xtrain,Ytrain)
    print(booster)
    print(reg.score(Xtest,Ytest)) #自己找線性數據試試看"gblinear"的效果吧~

[16:48:34] WARNING: src/objective/regression_obj.cu:152: reg:linear is now deprecated in favor of reg:squarederror.
gbtree
0.9231068620728082
[16:48:34] WARNING: src/objective/regression_obj.cu:152: reg:linear is now deprecated in favor of reg:squarederror.
gblinear
0.6286510307485139
[16:48:34] WARNING: src/objective/regression_obj.cu:152: reg:linear is now deprecated in favor of reg:squarederror.
dart
0.923106843149575

總結

xgboost(極限梯度提升算法):在分類和回歸上都擁有超高性能的先進評估器

梯度提升樹原理:通過不停的迭代,得到很多的弱評估器,當迭代結束后得到 k 個弱評估模型就是一棵樹,每棵樹都會有葉子節點,給每個葉子節點賦一個權重值,權重值累加得結果就是我們最終得梯度提升樹返回得預測結果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM