值得看!!!---大白話5分鐘帶你走進人工智能-第32節集成學習之最通俗理解XGBoost原理和過程

大白話系列更新了~~

鏈接如下:

https://blog.csdn.net/LHWorldBlog/article/details/103504725

目錄如下:

本節講解XGBoost的原理~

目錄

1、回顧:

1.1 有監督學習中的相關概念

1.2 迴歸樹概念

1.3 樹的優點

2、怎麼訓練模型:

2.1 案例引入

2.2 XGBoost目標函數求解

3、XGBoost中正則項的顯式表達

4、如何生長一棵新的樹?

5、xgboost相比原始GBDT的優化:

6、代碼參數:



1、回顧:

我們先回顧下有監督學習中的一些核心概念:

1.1 有監督學習中的相關概念

我們模型關注的就是如何在給定xi的情況下獲得ŷi。在線性模型裏面,我們認爲

                                                                                 \hat{y}_{i}=\sum w_{ij} x_{i j}

i是x的橫座標,j是x的列座標,本質上linear/logistic regression中的yi^都等於這個,只不過在logistic regression裏面,分了兩步,第一步ŷi=∑j wj xij,此時ŷi含義是一個得分,第二步你把這得分再扔到Sigmoid函數裏面,纔會得到概率。所以預測分值的ŷi在不同的任務中會有不同的解釋。對於線性迴歸(linear regression)來說,ŷi就是最終預測的結果。對於邏輯迴歸(logistic regression)來講,把ŷi丟到Sigmoid函數裏面去,是預測的概率。在其它的一些模型裏面,ŷi還有其它的作用。而在線性模型裏面的參數就是一組w,叫做θ。即

                                                                              $\Theta=\left\{w_{j} | j=1, \cdots, d\right\}$

對於參數型模型,要優化的目標函數有兩項組成,一項是Loss,一項是Regularization。表達公式即

                                                                            $\operatorname{Obj}(\Theta)=L(\Theta)+\Omega(\Theta)$

常見的損失函數Loss如:平方誤差損失函數(square loss),表示爲:

                                                                                    $\left(y_{i}-\hat{y}_{i}\right)^{2}$

交叉熵損失函數(Logistic loss),表示爲

                                                                      y_{i} \ln \left(1+e^{-\hat{y}_{i}}\right)+\left(1-y_{i}\right) \ln \left(1+e^{\hat{y}_{i}}\right)

正則項Ωθ是用來衡量模型簡單程度的,有L1正則,即

                                                                               \Omega(w)=\lambda\|w\|

有L2正則即L2範數的平方,乘一個λ。即

                                                                               $\Omega(w)=\lambda\|w\|^{2}$

λ是我們要調節的超參數,用來衡量Obj函數到底是更在乎簡單程度,還是更在乎在訓練集上的經驗損失。所謂經驗損失就是你在訓練集上錯了多少就叫經驗損失對於不同的loss和不同的Regularization這兩項加合起來,讓這兩項的和最小,就達到了一個兼顧的目的,所以它們倆都要相對比較小,纔是最好的模型效果。如果說爲了達到使L(θ)讓它下降一點點,而Ωθ上升好多,就代表着過擬合。模型複雜度上升了好多才帶來了一點點提升,這種事情並不是我們想要的。我們希望一個簡單的模型,能給我一個最好的答案;如果做不到這倆的話,也希望一個相對簡單的模型給我一個相對比較好的結果就行了。加法在這裏面達到了兼顧的目的。

對於不同的loss和不同的Regularization組合,再對它進行最優化,就構成了我們所謂的不同的算法

比如對於mse損失函數組合一個L2正則就是嶺迴歸,表達爲:

                                                                              $\sum_{i=1}^{n}\left(y_{i}-w^{T} x_{i}\right)^{2}+\lambda\|w\|^{2}$

對於mse損失函數組合一個L1正則就是lasso迴歸,表達爲:

                                                                           $\sum_{i=1}^{n}\left(y_{i}-w^{T} x_{i}\right)^{2}+\lambda\|w\|_{1}$

對於Logistic loss交叉熵損失函數組合一個L2正則就叫做邏輯迴歸,表達爲:

                                                        $\sum_{i=1}^{n}\left[y_{i} \ln \left(1+e^{-w^{T} x_{i}}\right)+\left(1-y_{i}\right) \ln \left(1+e^{w^{T} x_{i}}\right)\right]+\lambda\|w\|^{2}$

所以邏輯迴歸的損失函數裏必定會帶這λw^2這項,沒有這項就不能叫做邏輯迴歸。

1.2 迴歸樹概念

在來回顧下迴歸樹的相關概念,對於迴歸樹(CART樹Classification and Regression Trees)來講,它的決策的分裂條件和決策樹是一樣的,也是多次嘗試分裂,哪次結果最好就留下來。最後它會得到一個葉子節點,也能表達是一個連續的值,我們在此稱之爲score分數,對於迴歸樹我們得到的是一組分數。比如下面例子:

我們要判斷這個人是否喜歡電腦遊戲,通過一個迴歸樹來訓練,年齡小於15的點被分到左邊,是否是男的分到左邊。所以小男孩最終落到了左邊葉子節點,小女孩通過分裂落在了右邊葉子節點,而另外三位落到了右邊葉子節點。最終通過某種方法,先不用考慮是什麼方法,它通過y平均得到了小男孩得分是+2,小女孩+0.1,而另外三位對於計算機遊戲的喜好程度是-1,這是一個迴歸樹的形式。

1.3 樹的優點

樹有這麼一個優勢,就是inputs對於輸入數量的大小,不太在意。做分類,原來最強的霸主是SVM,但它有一個問題就時間複雜度是O(n3)。所以它在小數據集上表現得非常好,代碼也跑得起來,但一旦到海量數據集上,它就會變得不可接受。如果一千條數據用十分鐘,10萬條數據可能就以年來記了。三次方實在太可怕了,這是它最慢的情況,當然它內部也做了一些優化,不是所有情況都會達到O(n3)。但是樹的訓練基本就是O(n)或者O(n*m),它需要遍歷所有的維度,它比O(n3)要scaling了好多,scaling在機器學習領域特指的是小數據量上表現的好不好,在小數據量上能跑的,在大數據量上還跑得起來的這個方面的性能。 這是一個非常重要的特性。樹模型最不怕的就是這個東西,它表現的時間複雜度比較穩定。另外它對於數據集的是否歸一化,不需要太過在意,參數型模型裏y是通過x計算出來的,它們之間有一個等式關係,而對於樹一系列的模型來講,x和y實際上是被割裂開的。x值只用來分裂,跟y值最後的結果沒有直接的計算關係,它有間接的關係。y的計算結果是通過落在葉子節點上的其它的y算出來的,而不是通過x算出來。

這裏先給大家鋪墊下,在xgboost中,第一,它雖然用的是CART樹,但它葉子節點的表達,不再像以前使用y平均了,但是它仍然是一顆迴歸樹,不再是計算純度的分類樹。第二,它判斷分裂條件的標準也不再是方差了,而是看損失函數下降的高低。所以它雖然形式上是一顆迴歸樹,但無論分裂條件的判斷,還有葉子節點的表達,跟我們之前講的迴歸樹不一樣了。

 

2、怎麼訓練模型:

實際上永遠咱們講模型是這個套路,先模型假使已經從天上掉下來了,你要怎麼用它也就是預測部分,理解了怎麼預測之後,咱們再講它是怎麼得到這模型的,怎麼訓練出來的。

2.1 案例引入

那麼我們該如何學到一堆樹?首先要定義一個損失函數或者目標函數,包括損失項和正則項,然後去優化它。

先看個例子:我想要預測到底在t時刻有多麼的喜歡浪漫的音樂,以下圖兩個時間節點來學到了這麼一棵樹(實際上我們知道做迴歸樹就是在上面畫鋸齒),當所有小於左邊圖中時間節點的時候,用這幾個點的平均值代表葉子節點的預測;當大於右邊節點的時候,用這些點的平均值代表葉子節點的預測。中間也是一樣。假設左邊這個點是我遇到女朋友的時候,接下來你會越來越喜歡romantic music一直到後邊,對於浪漫音樂喜好程度又開始下降了。

對於這個模型來說分了兩支,因爲決策樹就怕過擬合,它如果沒做預剪枝的話,會一直細分下去。

這是一個實際的例子,通過這個例子,我們可以得到對於普通的一棵樹來說,我們要學習的是:第一,splitting positions,意思是分裂的位置,也就是分裂條件應該設在哪,第二我們要學得每一段的高度,也就是指這個區間內葉子節點表達應該等於多少,因爲它是取葉子結點的平均值作爲結果。

 

基於這兩個因素,我們怎麼定義這個樹是複雜還是不復雜呢?首先有多少個分裂點可以看出它複雜還是不復雜,如果它切的更碎,正則項也就更多更復雜;另外一個類似L2正則的方式,就是每一段的高度的L2正則,也可以像邏輯迴歸,線性迴歸一樣,一定程度上衡量葉子節點有多麼的複雜。所以它提出了兩種思想,第一個是分裂的次數可以衡量這個樹複雜不復雜,第二分裂之後每個樹給你一個score結果,這個結果做一個平方再加和的總體值,也可以一定程度上衡量這個樹到底複雜不復雜。

比如對於如下的樹我們用以上的定義複雜的因素來評判下:

分裂幾種情況,第一種情況:

對於這麼一個要分類的結果,顯然它會造成Loss小,因爲它非常好的擬合了訓練集;如果簡用分裂次數和段高低的平方加和來講,正則項會很大。

第二種情況:

它在不對的splitting point上分割了,導致L大了,而Ω項還可以,因爲它只做了一次分裂。

第三種情況:

就是一個非常好的平衡,只做了一次分裂,並且損失項還比較小,所以我們的目標就是在訓練樹上也應該去衡量一下樹的複雜程度,能讓它做到即訓練損失小一些,並且正則項也要小一些。

2.2 XGBoost目標函數求解
 

對於集成學習,我們知道是把多棵樹的結果給累加起來。假設我們有K棵樹,最終的ŷ就是K棵樹的預測結果的相加。表達爲:

                                                                         \hat{y}_{i}=\sum_{k=1}^{K} f_{k}\left(x_{i}\right), f_{k} \in(\mathcal{F})

這裏面的F是我們所有的迴歸樹,就之前的線性模型來講,它的參數是一組w,而在這個裏面,它的參數是就一堆樹。所以原來參數型模型我們要學的目標是一組權重w,是我們要的結果,而樹的集成學習模型,我們要學到的是一堆樹,這是要從訓練集中學到的。基於此,我們建立一個目標函數的形式即:

                                                                            O b j=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}\right)+\sum_{k=1}^{K} \Omega\left(f_{k}\right)

其中第一項是損失函數,損失函數既然每顆小樹裏面沒有w,我們也就不費事地把它翻譯成w的語言了,直接把ŷ拿來用,一定是有了yi,y^這兩項就能算損失函數的,這是損失部分,跟GBDT裏面的損失是一個概念。而另一項Ω是新引入的。這個公式裏面從1到K,從一到n分別代表什麼含義? n是樣本的數量,K是樹的數量。你有n條樣本,統計損失的時候,n條樣本都要考慮進去,而有K棵樹在統計複雜度的時候,要以樹爲單位去統計。

如何去定義Ω這個東西?它提出了幾種思路:

第一每棵樹葉子節點數量,可以描述這個樹的複雜程度。

第二每棵樹葉子節點上的評分score的L2範數也可以評估這棵樹是否複雜。

第三把這上面兩項結合在一起,做了一個正則項。

現在既想要一個有一定的預測效果,而相對簡單的模型,要怎麼做呢?我們給集成學習將Ft(x)寫成如下的形式:

                                                                    \begin{aligned} \hat{y}_{i}^{(0)} &=0 \\ \hat{y}_{i}^{(1)} &=f_{1}\left(x_{i}\right)=\hat{y}_{i}^{(0)}+f_{1}\left(x_{i}\right) \\ \hat{y}_{i}^{(2)} &=f_{1}\left(x_{i}\right)+f_{2}\left(x_{i}\right)=\hat{y}_{i}^{(1)}+f_{2}\left(x_{i}\right) \\ \hat{y}_{i}^{(t)} &=\sum_{k=1}^{t} f_{k}\left(x_{i}\right)=\hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right) \end{aligned}

這裏的yi^代表之前寫的大F(x),y^0就是F0(x),y^1就是F1(x),一直到Ft(x)。通過boosting這種加法模式,最終的ŷ就等於之前所有小樹的加和,也等於上一代的ŷ加上最新的一棵小樹。此時Loss中的ŷ是什麼的問題就解決了。

我們把它分解一下,既然

                                                                       \hat{y}_{i}^{(t)}=\hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right)

此時Obj函數就變成了

                                                         \begin{aligned} O b j^{(t)} &=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(t)}\right)+\sum_{i=1}^{t} \Omega\left(f_{i}\right) \\ & = \sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right)\right)+\Omega\left(f_{t}\right)+\text { constant } \end{aligned}

我們現在的目標就是找到一個f(t),能夠使這個函數變小。只要這個函數小了,預測結果一定差不了。這個函數帶着求和號就代表着它已經考慮到所有樣本的情況了。

如果我們的損失函數是mse的話,將上式中的l替換成具體的損失函數就是:

                                                 \begin{aligned} O b j^{(t)} &=\sum_{i=1}^{n}\left(y_{i}-\left(\hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right)\right)\right)^{2}+\Omega\left(f_{t}\right)+\text {const } \\ &=\sum_{i=1}^{n}\left[2\left(\hat{y}_{i}^{(t-1)}-y_{i}\right) f_{t}\left(x_{i}\right)+f_{t}\left(x_{i}\right)^{2}\right]+\Omega\left(f_{t}\right)+\text { const } \end{aligned}

yi^2和yi^(t-1)^2這兩項都可以放到後面的常數項裏面去,它們不會影響ft(xi)這一項的變大或者變小,只有它的係數可以影響。所以整合完後就是如上所述的公式,其中ft(xi)是未知數。所以這就變成了一個簡單的二次函數求最小值的問題。

ax^2+bx+c當x等於什麼的時候有最小值?根據初中的理論,當x爲-b/a2的時候,函數有最小值。此時在上述公式裏面

a是1,b是

                                                                                   2$\left(\hat{y}_{i}^{(t-1)}-y_{i}\right)

所以當ft(x)爲yi-yi^(t-1)的時候,函數取最小值。這就是我們說的殘差,所以殘差你從這個角度也可以一樣推出來,我們希望損失函數可以最小,就是當ft(x)爲yi-yi^(t-1)的時候最小,這裏面爲什麼我們要寫成ft(xi)的形式,因爲我們目標是要找到ft,所以一定要把它暴露出來,這樣我們才知道ft要滿足什麼條件纔可以。

mse函數是一個非常幸運的函數,它展開之後就是一個二次函數,你可以求出它最小值的解析解,對於邏輯迴歸函數甚至更復雜的函數你是沒法寫出解析解的,你就需要把目標函數展開成一個能寫出解析解的東西來,再去求它的解析解,此時就用到我們的二階泰勒展開。二階泰勒展開的公式如下:

                                                           f(x+\Delta x) \simeq f(x)+f^{\prime}(x) \Delta x+\frac{1}{2} f^{\prime \prime}(x) \Delta x^{2}

我們再看下我們的目標函數:

                                                           \begin{aligned} O b j^{(t)} &=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(t)}\right)+\sum_{i=1}^{t} \Omega\left(f_{i}\right) \\ & = \sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right)\right)+\Omega\left(f_{t}\right)+\text { constant } \end{aligned}

結合二階泰勒展開的公式,這裏把f_{t}\left(x_{i}\right)看作△x,把y_{i}, \hat{y}_{i}^{(t-1)}看作x。進行二階泰勒展開,x已知。對f(x)求一階導是:

                                                                         g_{i}=\partial_{\hat{y}^{(t-1)}} l\left(y_{i}, \hat{y}^{(t-1)}\right)

上面的含義是對l求一次導,然後把y^(t-1)帶進去。

對f(x)求二階導是:

                                                                      h_{i}=\partial_{\hat{y}^{(t-1)}}^{2} l\left(y_{i}, \hat{y}^{(t-1)}\right)

所以最後的二階泰勒展開就是

                                            O b j^{(t)} \simeq \sum_{i=1}^{n}\left[l\left(y_{i}, \hat{y}_{i}^{(t-1)}\right)+g_{i} f_{t}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{t}\right)+$ constant

在這個函數裏面只有f_{t}\left(x_{i}\right)是自變量,同樣這也是一個二階函數,所以當x=-b/2a的時候,損失函數取得最小值,也就是ft(xi)=f''(x)/f'(x)的時候。如果看到這不理解的話,我們考慮下一個特殊的函數--平方損失函數。對於平方損失函數來講,一階導是二倍的殘差,二階導是一個常數2,即:

                                      g_{i}=\partial_{\hat{y}^{(t-1)}}\left(\hat{y}^{(t-1)}-y_{i}\right)^{2}=2\left(\hat{y}^{(t-1)}-y_{i}\right) \quad h_{i}=\partial_{\hat{y}^{(t-1)}}^{2}\left(y_{i}-\hat{y}^{(t-1)}\right)^{2}=2

把它帶進去二階泰勒展開的式子裏,會得到跟之前直接把它展開同樣的結果。因爲它本身是個二次函數,對於二次函數進行二階泰勒展開沒有損失,實際上是用一個拋物線去擬合一個拋物線,所以不會有約等於號。但是對於複雜一點的函數,比如說交叉熵損失函數,它就有損失了,但是有損失它也不在乎了,我就保留它的這些近似項。

再來對這個公式進行簡化,

                                             O b j^{(t)} \simeq \sum_{i=1}^{n}\left[l\left(y_{i}, \hat{y}_{i}^{(t-1)}\right)+g_{i} f_{t}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{t}\right)+$ constant

y_{i}, \hat{y}_{i}^{(t-1)}這一項可以扔掉,因爲它是常數項,yi是真實值,而y^(t-1),是前面t-1輪預測已經得到的結果,所以這裏面的關於它兩個的損失函數可以計算出來。現在只有f_{t}\left(x_{i}\right)未知,剩餘的都是可以算出來的。 我們把常數項放在一起,整理之後,新目標函數:

                                                                   \sum_{i=1}^{n}\left[g_{i} f_{t}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{t}\right)

其中:

                                                  g_{i}=\partial_{\hat{y}^{(t-1)}} l\left(y_{i}, \hat{y}^{(t-1)}\right), \quad h_{i}=\partial_{\hat{y}^{(t-1)}}^{2} l\left(y_{i}, \hat{y}^{(t-1)}\right)

這裏面簡化之後雖然l丟掉了,但l實際上存活在gi和hi裏,因爲在計算gi和hi的時候,需要對l進行ft-1的求偏導,再把ft-1帶進去,gi就是函數空間中梯度下降對應着如下結果:     

                                                      g_{m}(X)=\left[\frac{\partial L(y, F(X))}{\partial F(X)}\right]_{F(X)=F_{m-1}(X)}

gi和hi是什麼東西?它已經是個能求得出來的真真切切的一個數。y^(t-1)是知道的,它是某一個數據點在上一輪預測出來的具體的結果值,對它求偏導的解析式也能知道,只要損失函數定義出來就知道了。求完偏導得到了一個新的函數,把具體的值代進去就得到了一個新的值。有多少條樣本就有多少個gi和hi。

 

 

3、XGBoost中正則項的顯式表達

我們目前把loss項已經整理好了,而Ω這一項還沒顯式地表示出來。我們最終想找到一個ft去讓整個目標函數最小,gi和hi已經是具體的數了,而Ω這一項還沒有定義。接下來我們來討論下Ω的表達。

引入分配機的概念,它定義了ft(x)最終的預測結果就等於wq(x)。其中q(x)代表它落到了哪一個葉子節點上。假如說x第一條數據落到了第一個葉子節點上,這會的第一條數據就等於1。對於小男孩來說,它落到了一號節點上,q(小男孩)=1,也就是ft(小男孩)=wq(x)=w1,某一條數據最終會落在幾號葉子節點上,q這條數據就等於幾。也就是它定義了什麼叫w,在這裏面w1就是第一個葉子節點的表達,w2就是第二個葉子節點的表達,w3就是第三個葉子節點表達。

有了w的定義,我們定義Ω項,定義了損失項。損失項包含兩部分,Ω是跟着每棵樹來的,應該一棵樹有一個Ω。也就是對於任何一個小樹我扔到Ω裏面,你就要給我評估出我樹有多複雜。先看定義公式:  

                                                                    \Omega\left(f_{t}\right)=\gamma T+\frac{1}{2} \lambda \sum_{j=1}^{T} w_{j}^{2}

對於某一棵樹的正則項來講,它就等於葉子節點的數量乘以第一個超參數γ,這個是設置的。然後就是這棵樹上的每一個葉子節點的表達的平方加和,再乘1/2λ。比如下面這個樹它的Ω等於多少?

它有三個葉子節點,就是3乘以γ,w1等於+2,平方是4,w2平方等於0.01,w3平方等於1。所以最終的正則項是上述表達,也就是說對於這棵樹的Ω,在確定了這兩個超參數之後,也是一個可以具體計算的數。

我們再看下一種表達,假設I_{j}=\left\{i | q\left(x_{i}\right)=j\right\},它是一個樣本的集合,所有被分到一號葉子節點裏邊的樣本的集合就叫I1,所有分到二號葉子節點裏邊的集合就叫I2。 然後ft(x)寫成wq(x),最後我們的損失函數就表示成:

                                                    \begin{aligned} O b j^{(t)} & \simeq \sum_{i=1}^{n}\left[g_{i} f_{t}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{t}\right) \\ &=\sum_{i=1}^{n}\left[g_{i} w_{q\left(x_{i}\right)}+\frac{1}{2} h_{i} w_{q\left(x_{i}\right)}^{2}\right]+\gamma T+\lambda \frac{1}{2} \sum_{j=1}^{T} w_{j}^{2} \\ &=\sum_{j=1}^{T}\left[\left(\sum_{i \in I_{j}} g_{i}\right) w_{j}+\frac{1}{2}\left(\sum_{i \in I_{j}} h_{i}+\lambda\right) w_{j}^{2}\right]+\gamma T \end{aligned}

解釋下:
從第一步到第二步應該沒有問題就是把正則的具體表達帶入進去,而第二步到第三步實際上就是我們換一種遍歷的維度,原來是樣本序號,從樣本i=1遍歷到n,現在改成遍歷葉子節點j=1到T,從1號葉子節點裏數,所有屬於1號葉子節點的gi做一個加和,統一乘wj。比如有三個葉子節點,其中1,2號樣本落在第一個,所以表達爲w1。3,5落在第二個葉子結點,表達爲w2。4,6落在第三個葉子結點,表達爲w3。根據第二個公式的加法g_{i} w_{q\left(x_{i}\right)}就是g1w1+g2w1+g3w2+g4w3+g5w2+g6w3,而現在根據第三個公式表達的就是(g1+g2)w1+(g3+g5)w2+(g4+g6)w3,實際上這一項的轉換就是把相同的w給提到一起去,也就相是同葉子節點裏邊的g都加和到一起。

另一項

                                                                                    \frac{1}{2} h_{i} w_{q\left(x_{i}\right)}^{2}

按葉子節點遍歷,就是

                                                                               \frac{1}{2}\left(\sum_{i \in I_{j}} h_{i}\right) w_{j}^{2}

而結合最外面的正則項,剛好也是按葉子節點遍歷,所以乾脆合到一起,就變稱第三個公式的表達。

原來是按樣本遍歷,但這樣本里面肯定有很多點落在同一個葉子節點裏了,也就是這些點,未來的wq(xi)都是相同的。我們就把它以葉子節點來遍歷,把所有屬於一號葉子節點的gi,雖然它們落在同一個葉子節點裏了,但是它們的gi不一定相等,取決於這條樣本在上一輪預測中它落在哪一個葉子節點。

通過顯示的定義的Ω,我們就把目標函數整理成了如下形式:

                                                O b j^{(t)}=\sum_{j=1}^{T}\left[\left(\sum_{i \in I_{j}} g_{i}\right) w_{j}+\frac{1}{2}\left(\sum_{i \in I_{j}} h_{i}+\lambda\right) w_{j}^{2}\right]+\gamma T

這就是我們的目標函數。 現在這個目標函數只看wj的變化而變化了。我們定義Gj等於落在某一個葉子節點上的所有gi的加和, Hj定義爲落在某一個j葉子節點上所有hi的加和。即:

                                                                  G_{j}=\sum_{i \in I_{j}} g_{i} \quad H_{j}=\sum_{i \in I_{j}} h_{i}

最後損失函數表示爲:

                                              \begin{aligned} O b j^{(t)} &=\sum_{j=1}^{T}\left[\left(\sum_{i \in I_{j}} g_{i}\right) w_{j}+\frac{1}{2}\left(\sum_{i \in I_{j}} h_{i}+\lambda\right) w_{j}^{2}\right]+\gamma T \\ &=\sum_{j=1}^{T}\left[G_{j} w_{j}+\frac{1}{2}\left(H_{j}+\lambda\right) w_{j}^{2}\right]+\gamma T \end{aligned}

現在這個損失函數是大還是小,取決於誰呢?GjWj是已經知道的,Hj這一項也是知道的,λ是自己設的超參數,只有wj是未知的。當它等於什麼的時候能讓Obj最小呢?這又是一個二次函數,當x=-b/a2的時候最小,在這裏wj是未知數,b是Gj,a是1/2(Hj+λ),所以根據表達式,即wj如下的時候:

                                                                          w_{j}^{*}=-\frac{G_{j}}{H_{j}+\lambda}

能讓損失函數等於最小值。此時損失函數根據一般的二次方程的最小值表達形式-b^2/4+c,此時損失函數的最小值可以表達出來如下:

                                                                    O b j=-\frac{1}{2} \sum_{j=1}^{T} \frac{G_{j}^{2}}{H_{j}+\lambda}+\gamma T

這是一個簡單的二次函數,函數的最小值解能寫出來。

所以我們可以看到葉子節點的表達不再等於y平均了,而等於上面wj的表達。

舉個例子,假設有這麼一棵樹:

分到一號葉子節點有的第一號樣本,分到二號葉子節點有第四號樣本,分到第三個葉子節點有235,三個樣本。此時的G1就是小男孩的g1,H1就是小男孩的h1。G3=g2+g3+g5,H2=h2+h3+h5。最終的得分,左邊爲什麼是+2?g1就應該等於前面所有的樹對y的偏導,算出了具體的值。對它再求一次偏導,算出h。g1,h1是具體數,每一條樣本都可以帶到一階導和二階導的函數裏面,算出它的g和h。在分裂的過程中,如果分到了同一個葉子節點,要把它們所有的g,h相加,再帶到剛纔obj公式裏面去,算得一個葉子節點表達即2。所以葉子結點的表達就不再是平均了,而變成了跟以前的預測結果相關的。

 

4、如何生長一棵新的樹?

假如我想訓練第T+1棵樹,也是就f(t+1),那你在手裏有什麼,你其實有個FT,你手裏還有一個訓練集,根據這些我們能得到從g1一直到gn,從h1一直到hn。它們每一個都是訓練集背後具體的一個數。

我們看下從最開始的首先一個根節點,這個根節點裏包含了n條數據。你每次分裂,就像樹一樣,變例所有可能的分裂的維度,每次分裂,都能算一下此時的obj等於多少,Obj是關於G和H的,然後再求和,在加上一個t倍的γ,你每嘗試一次就算一次obj,是不是總能找到一個使得obj最小的那個標準,固化在這,沒問題吧?

上面是兩個葉子結點。

接下來我該試着下一次分裂是不是左邊的節點又能一份爲二,如下:

分完之後這棵樹變爲3個葉子結點,然後1裏邊落哪些,你每次分裂的時候。假如這棵樹就是現在這個額結構,那麼我obj應該能等於多少也可以求出來。所以肯定能夠找到使得obj最小的的分裂去分他,沒問題吧。然後再一次的對1號節點分裂,然後發現,無論選擇哪一個分裂,obj都沒有下降,此時怎麼辦,不分裂了,它就是葉子結點了,那你知道這是葉子結點了要去計算這個葉子結點未來怎麼表達啊,怎麼表達呢,用剛纔那個w_{j}^{*}=-\frac{G_{j}}{H_{j}+\lambda}來表達,這個節點固定死在這了,這個節點裏有哪些樣本,也都知道了,G知道了,H也知道了,λ是你自己寫的,這個節點就這樣了。接下來再去找2號節點分裂看下能不能使得損失函數再下降一下,能下降就分,不能下降就不分了,接着去算這個葉子節點的表達,這個實際上就是樹分裂學習的方法。

當然也會有預剪枝的條件,xgboost裏邊也有最大深度來讓你設置,那麼它到底是聽最大深度的呢,還是聽損失函數的呢,哪個先達到底線,就聽那個,假如說損失函數還沒達到最大深度已經不下降了,那他它也不會在訓練了,假如損失函數還想着自己還有潛力再下降,但是已經達到最大深度,此時也不在下降了。

以上就是如何長成新樹的流程。總結下,初始所有樣本都在同一個根節點裏,當它一分爲二了,選定所有分裂條件都試一試,註定有一部分節點落在左邊,一部分節點落在右邊。只要這次分裂分出來了,G和H就可以算了,T也就知道了。此時就可以看Obj的函數等於多少?原始的損失函數能算,分出來之後的損失函數函數,看哪次下降的最多,就把那次分裂作爲這一次的分裂條件保存下來,隨着每次分裂,T一定會+1,每分裂一次葉子節點就會多一個。

Loss這一項會下降,下降了的這個值一定要勝過+1這個值,它纔會認爲目標函數下降了,否則就是沒帶來多大提升,還白白的多弄出來一個葉子,這樣效果不好。

所以xgboost不需要人工剪枝,它通過正則項就把剪枝的能力賦予它了,當它生長髮現註定會+1的情況下,而遍歷了所有維度,發現沒有一個能打敗這個東西+1的損失的,它就停止生長了。 相當於自動的剪枝。

如何去搜索條件分裂?就是一個線性搜索挨個嘗試在不同位置進行分裂。哪種分裂能帶來最大的增益,就保留哪一個條件作爲分裂條件。實際上它定義了增益的形式,增益就等於

                                             Gain =\frac{G_{L}^{2}}{H_{L}+\lambda}+\frac{G_{R}^{2}}{H_{R}+\lambda}-\frac{\left(G_{L}+G_{R}\right)^{2}}{H_{L}+H_{R}+\lambda}-\gamma

這是分裂後的目標函數的值減去舊目標函數的值,也就是左子節點的得分\frac{G_{L}^{2}}{H_{L}+\lambda},加上右子節點的得分\frac{G_{R}^{2}}{H_{R}+\lambda}。減去不分裂的得分\frac{\left(G_{L}+G_{R}\right)^{2}}{H_{L}+H_{R}+\lambda}。因爲每一次新的分裂葉子結點永遠只增加一個(可參考上面圖例),而每一次分裂的得分都帶一部分λT,所以λ(T+1)-λT=λ。也就是上式爲什麼帶個λ的原因。

 

5、xgboost相比原始GBDT的優化:

1、原始GBDT是一個原始空間的梯度下降,它實際上是對損失函數進行一個一階的展開,去求這個一階泰勒展開的近似,而xgboost採用二階泰勒展開擬合目標函數的L部分。也就是類似牛頓法來使函數收斂的次數更快,所以它需要更少棵樹,就可以達到一個比較不錯的預測結果,這是它的理論依據。

2、xgboost還增加了一個正則項,

                                                                           \Omega\left(f_{t}\right)=\gamma T+\frac{1}{2} \lambda \sum_{j=1}^{T} w_{j}^{2}

來防止過擬合。

3、它引入了縮水系數,大F(x)是若干f(x)的加和,我們每次新訓練一棵樹,也要給它加一個學習率進去,也就是f(x)前面也加了一個學習率,叫η。即ηf(x)。所以新的大F(x)表達即爲{F}_{T}(x)={F}_{T-1}(x)+\eta {f}_{t}(x)。如果η爲1,就沒有縮水,每次加的就是得到損失函數最小的那個小樹。

4、xgboost 有自己的剪枝策略。對於剪枝它不需要特意的去確定,只要把超參數確定了,γ和λ確定了之後,它會自動的根據損失函數到底是上升了還是下降了,決定是否還要繼續分裂下去。

5、它天生可以應對缺失值,在它的實現裏邊對缺失值做了一些處理。

6、它可以自定義損失函數傳進去,損失函數需要傳一個g和h的返回,你定義兩個方式就可以。

7、它支持集羣運算。通常我們說Boosting是不支持集羣運算的,根據這棵樹的預測結果,甚至所有樹的結果來生成下一個樹。工作量在訓練樹中的排序部分,我們說每次分裂都要去對可能的條件做一個線性搜索,然後排序,在排序的過程中,它會可以通過集羣,多任務來跑排序。雖然我們以樹爲粒度,我們沒有辦法把它分配到多個機器上跑,但是以樹內部的分類歸併排序,在集羣上來跑,所以這樣它一定程度上支持了集羣。

 

6、代碼參數:

from xgboost import XGBClassifier
XGBClassifier()

它有哪些超參數?

max_depth:雖然它不需要設置預剪枝,但同樣可以強制的設一下樹的深度,強制不要讓它走太深。

learning_rate:就是剛纔說的縮水系數,學習率。

n-estimators:總共樹的棵數,對應着牛頓法裏面的迭代次數。

silent:true和false取決於它會不會在你的控制檯打印它訓練的一些損失,你能看到損失在第多少棵樹的時候就不再變化了,達到低谷收斂了。

objective:目標函數,它默認提供了mse , Logistic,也就是用邏輯迴歸做的二分類、multiclass,還有一些其它的。另外一個非常厲害的地方,是它支持你自己定義損失函數,在objective可以傳入一個函數進來,它要求g和h分別怎麼算,告訴它求完一階導之後等於什麼,二階導之後等於什麼就行了。它要g和h是怎麼算,輸入一個yi一組序列,輸入一個ŷi,然後算出所有g和h。

subsample:每次訓練小樹的時候,要從訓練集中行抽樣了多少,如果設置這個參數,便不再是有放回的隨機抽樣了,默認是1。隨機森林它一定是有100萬條數據,它生成的每一個子數據集都是100萬條數據,因爲它沒有subsample,你不可以設置,它就是1。

Colsample-bytree:代表每次訓練一棵樹的時候,要不要少給它一些維度讓它看, 1是100%的意思。

 

發佈了204 篇原創文章 · 獲贊 148 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章