XGB算法梳理

引言

XGBoost(eXtreme Gradient Boosting)是大規模並行boosted tree的工具,它是目前最快最好的開源boosted tree工具包,比常見的工具包快10倍以上。在數據科學方面,有大量kaggle選手選用它進行數據挖掘比賽,如果你的算法預測結果不好,趕緊試試XGBoost吧。在工業界規模方面,xgboost的分佈式版本有廣泛的可移植性,支持在YARN, MPI, Sungrid Engine等各個平臺上面運行,並且保留了單機並行版本的各種優化,使得它可以很好地解決於工業界規模的問題。

XGBoost其實是對前面所講的GBDT算法的一種改良實現,主要包括了正則化、損失函數、學習策略、並行實現等一些方面。

1、算法原理

算法思想就是不斷地添加樹,不斷地進行特徵分裂來生長一棵樹,每次添加一個樹,其實是學習一個新函數,去擬合上次預測的殘差。當我們訓練完成得到k棵樹,我們要預測一個樣本的分數,其實就是根據這個樣本的特徵,在每棵樹中會落到對應的一個葉子節點,每個葉子節點就對應一個分數,最後只需要將每棵樹對應的分數加起來就是該樣本的預測值。
在這裏插入圖片描述
 注:w_q(x)爲葉子節點q的分數,f(x)爲其中一棵迴歸樹

如下圖例子,訓練出了2棵決策樹,小孩的預測分數就是兩棵樹中小孩所落到的結點的分數相加。爺爺的預測分數同理。

在這裏插入圖片描述

2、損失函數

對於迴歸問題,我們常用的損失函數是MSE,即:
在這裏插入圖片描述
對於分類問題,我們常用的損失函數是對數損失函數:
在這裏插入圖片描述
XGBoost目標函數定義爲:
在這裏插入圖片描述
在這裏插入圖片描述
目標函數由兩部分構成,第一部分用來衡量預測分數和真實分數的差距,另一部分則是正則化項。正則化項同樣包含兩部分,T表示葉子結點的個數,w表示葉子節點的分數。γ可以控制葉子結點的個數,λ可以控制葉子節點的分數不會過大,防止過擬合。

正如上文所說,新生成的樹是要擬合上次預測的殘差的,即當生成t棵樹後,預測分數可以寫成:
在這裏插入圖片描述
同時,可以將目標函數改寫成:
在這裏插入圖片描述
很明顯,我們接下來就是要去找到一個f_t能夠最小化目標函數。XGBoost的想法是利用其在f_t=0處的泰勒二階展開近似它。所以,目標函數近似爲:
在這裏插入圖片描述
由於前t-1棵樹的預測分數與y的殘差對目標函數優化不影響,可以直接去掉。簡化目標函數爲:
加粗樣式
上式是將每個樣本的損失函數值加起來,我們知道,每個樣本都最終會落到一個葉子結點中,所以我們可以將所以同一個葉子結點的樣本重組起來,過程如下圖:
在這裏插入圖片描述
因此通過上式的改寫,我們可以將目標函數改寫成關於葉子結點分數w的一個一元二次函數,求解最優的w和目標函數值就變得很簡單了,直接使用頂點公式即可。因此,最優的w和目標函數公式爲:
在這裏插入圖片描述

3、分裂結點算法

在上面的推導中,我們知道了如果我們一棵樹的結構確定了,如何求得每個葉子結點的分數。但我們還沒介紹如何確定樹結構,即每次特徵分裂怎麼尋找最佳特徵,怎麼尋找最佳分裂點。

正如上文說到,基於空間切分去構造一顆決策樹是一個NP難問題,我們不可能去遍歷所有樹結構,因此,XGBoost使用了和CART迴歸樹一樣的想法,利用貪婪算法,遍歷所有特徵的所有特徵劃分點,不同的是使用上式目標函數值作爲評價函數。具體做法就是分裂後的目標函數值比單子葉子節點的目標函數的增益,同時爲了限制樹生長過深,還加了個閾值,只有當增益大於該閾值才進行分裂。

4、正則化

xgboost使用瞭如下的正則化項:
在這裏插入圖片描述

注意:這裏出現了γ和λ,這是xgboost自己定義的,在使用xgboost時,你可以設定它們的值,顯然,γ越大,表示越希望獲得結構簡單的樹,因爲此時對較多葉子節點的樹的懲罰越大。λ越大也是越希望獲得結構簡單的樹。

爲什麼xgboost要選擇這樣的正則化項?很簡單,好使!效果好纔是真的好。

5、對缺失值處理

xgboost模型卻能夠處理缺失值,模型允許缺失值存在。

原始論文中關於缺失值的處理將其看與稀疏矩陣的處理看作一樣。在尋找split point的時候,不會對該特徵爲missing的樣本進行遍歷統計,只對該列特徵值爲non-missing的樣本上對應的特徵值進行遍歷,通過這個技巧來減少了爲稀疏離散特徵尋找split point的時間開銷。在邏輯實現上,爲了保證完備性,會分別處理將missing該特徵值的樣本分配到左葉子結點和右葉子結點的兩種情形,計算增益後選擇增益大的方向進行分裂即可。可以爲缺失值或者指定的值指定分支的默認方向,這能大大提升算法的效率。如果在訓練中沒有缺失值而在預測中出現缺失,那麼會自動將缺失值的劃分方向放到右子樹。

6、優缺點

優點:

1) xgBoosting支持線性分類器,相當於引入L1和L2正則化項的邏輯迴歸(分類問題)和線性迴歸(迴歸問題);

2) xgBoosting對代價函數做了二階Talor展開,引入了一階導數和二階導數;

3)當樣本存在缺失值是,xgBoosting能自動學習分裂方向;

4)xgBoosting借鑑RF的做法,支持列抽樣,這樣不僅能防止過擬合,還能降低計算;

5)xgBoosting的代價函數引入正則化項,控制了模型的複雜度,正則化項包含全部葉子節點的個數,每個葉子節點輸出的score的L2模的平方和。從貝葉斯方差角度考慮,正則項降低了模型的方差,防止模型過擬合;

6)xgBoosting在每次迭代之後,爲葉子結點分配學習速率,降低每棵樹的權重,減少每棵樹的影響,爲後面提供更好的學習空間;

7)xgBoosting工具支持並行,但並不是tree粒度上的,而是特徵粒度,決策樹最耗時的步驟是對特徵的值排序,xgBoosting在迭代之前,先進行預排序,存爲block結構,每次迭代,重複使用該結構,降低了模型的計算;block結構也爲模型提供了並行可能,在進行結點的分裂時,計算每個特徵的增益,選增益最大的特徵進行下一步分裂,那麼各個特徵的增益可以開多線程進行;

8)可並行的近似直方圖算法,樹結點在進行分裂時,需要計算每個節點的增益,若數據量較大,對所有節點的特徵進行排序,遍歷的得到最優分割點,這種貪心法異常耗時,這時引進近似直方圖算法,用於生成高效的分割點,即用分裂後的某種值減去分裂前的某種值,獲得增益,爲了限制樹的增長,引入閾值,當增益大於閾值時,進行分裂;

缺點:

1)xgBoosting採用預排序,在迭代之前,對結點的特徵做預排序,遍歷選擇最優分割點,數據量大時,貪心法耗時,LightGBM方法採用histogram算法,佔用的內存低,數據分割的複雜度更低;

2)xgBoosting採用level-wise生成決策樹,同時分裂同一層的葉子,從而進行多線程優化,不容易過擬合,但很多葉子節點的分裂增益較低,沒必要進行跟進一步的分裂,這就帶來了不必要的開銷;LightGBM採用深度優化,leaf-wise生長策略,每次從當前葉子中選擇增益最大的結點進行分裂,循環迭代,但會生長出更深的決策樹,產生過擬合,因此引入了一個閾值進行限制,防止過擬合.

7、sklearn參數

分類:

 
from sklearn.datasets import load_iris
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
 
# read in the iris data
iris = load_iris()
 
X = iris.data
y = iris.target
 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
 
# 訓練模型
model = xgb.XGBClassifier(max_depth=5, learning_rate=0.1, n_estimators=160, silent=True, objective='multi:softmax')
model.fit(X_train, y_train)
 
# 對測試集進行預測
ans = model.predict(X_test)
 
# 計算準確率
cnt1 = 0
cnt2 = 0
for i in range(len(y_test)):
    if ans[i] == y_test[i]:
        cnt1 += 1
    else:
        cnt2 += 1
 
print("Accuracy: %.2f %% " % (100 * cnt1 / (cnt1 + cnt2)))
 
# 顯示重要特徵
plot_importance(model)
plt.show()

迴歸:

 
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
 
# 讀取文件原始數據
data = []
labels = []
labels2 = []
with open("lppz5.csv", encoding='UTF-8') as fileObject:
    for line in fileObject:
        line_split = line.split(',')
        data.append(line_split[10:])
        labels.append(line_split[8])
 
X = []
for row in data:
    row = [float(x) for x in row]
    X.append(row)
 
y = [float(x) for x in labels]
 
# XGBoost訓練過程
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
 
model = xgb.XGBRegressor(max_depth=5, learning_rate=0.1, n_estimators=160, silent=True, objective='reg:gamma')
model.fit(X_train, y_train)
 
# 對測試集進行預測
ans = model.predict(X_test)
 
# 顯示重要特徵
plot_importance(model)
plt.show()

參考鏈接:機器學習算法總結之XGBoost(上)理論基礎

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