XGBoost算法原理以及實現


 

XGBoost算法是由GBDT算法演變出來的,GBDT算法在求解最優化問題的時候應用了一階導技術,而XGBoost則使用損失函數的一階導和二階導,不但如此,
還可以自己定義損失函數,自己定義損失函數前提是損失函數可一階導和二階導。

XGBoost算法原理:(務必保證先學習決策樹算法)

其實算法的原理就是在一顆決策樹的基礎上不斷地加樹,比如在n-1顆樹地基礎上加一棵樹變成n顆樹的同時算法的精確率不斷提高、效果提升。

基礎理解:

  • 損失函數: l(yi,yi^) = (yi-yi^)**2 【這里損失函數先以方差損失作為示例、因為比較好算和符號表達畢竟這個也很不錯;當然損失函數可以更改】

  • 如何最優化的求解? : F*(x) = argmin E(x,y)(L(y,F(x)))

  • 最終,集成算法的表示:yi^ = sum(fk(xi)) ; 其中,k=1~K;fk屬於F

  • yi0^ = 0

  • yi1^ = f1(xi) = yi0^+f1(xi)

  • yi2^ = f1(xi)+f2(xi) = yi1^+f2(xi)

  • yin^ = sum(fk(xi)) = yi{n-1}^ + fn(xi) ;其中,k=1~n。

推導過程:

  • 在樣本中着手計算,樣本的真實值yi,預測值yi^。

  • 目標: Obj{t} = sum(l(yi,yi^{t-1}+ft(xi)))+U(ft)+c 其中、c為常數,i=1~n, U(ft)為L2正則化的懲罰函數
    (說白了就是L2正則項:qT+lambad* 1/2* sum(wj**2);其中j=1~T;qT是某個常數,T為葉子節點個數)

  • 知識補充:

    • 泰勒展開: f(x+▲x) ~ f(x)+▲x* f(x)的一階導 + ▲x*f(x)的二階導
    • 定義:gi = G{y{t-1}^}* l(yi,y{t-1}^); hi = H{y{t-1}^}**2 * l(yi,y{t-1}^) ; gi是一階導、hi是二階導。
  • 所以,目標函數變換得到:Obj{t} ~ sum(l(yi,yi{t-1}^)+gi* ft{xi}+1/2* hi* ft(xi)**2) ;其中,i=1~n。其實l(yi,yi{t-1}^)相當於一個常數值,因為它從未改變,收斂於某個具體的常數。

  • 再次變換:得到Obj{t} ~ sum(gi* ft(xi)+1/2* hi* ft(xi)**2)+U(ft) ;其中,i=1~n。

  • 將樣本上的遍歷計算轉換為在葉子節點上的遍歷計算: Obj{t} = sum((sum(gi,i屬於Ij)* wj+1/2*(sum(hi,i屬於Ij)+lambad)* wj**2))+qT,其中,j=1~T。

  • 最終目標函數簡化為: Obj{t} = sum(Gj* wj+1/2*(Hj+lambad)* wj**2) + qT;其中Gj=sum(gi,i屬於Ij),Hj=sum(hi,i屬於Ij)。

  • 求解最終的目標函數:一貫操作:求偏導、令偏導為0、代入原函數;

    • J(ft)對wj求偏導 = Gj+(Hj+lambad)* wj=0
    • wj = -(Gj/Hj+lambad)
    • 將wj帶回原Obj{t}最終目標函數得到:Obj = -1/2* sum(Gj**2/(Hj+lambad),j=1~T)+qT; 代入數據計算得出的分數越小代表這個樹的結構越好,也就是損失值越小越好。
  • 加入新節點的時候的模型復雜度代價:Gain = 1/2*(G{L}**2/(H{L}+lambad)+G{R}**2/(H{R}+lambad)-(G{L}+G{R})**2/(H{L}+H{R}+lambad))-q

XGBoost的思路總結:1.根據數據集初始化一棵樹;2.確定損失函數;3.拿出一棵樹來作為推導“樣本上的遍歷計算”這個過程,然后通過函數變換得到“葉子節點上的遍歷計算”這個過程,之后求解模型。4.還可以根據計算得到的模型復雜度設置復雜度閾值、畢竟計算資源的代價太大的話也不是很好。

使用wl包安裝xgboost之后,使用import引入即可使用

  • 其中、xgboost模塊的XGBClassifier類是解決分類問題、XGBRegressor類是解決回歸問題。

  • xgboost.XGBClassifier(max_depth=3,learning_rate=0.1,n_estimators=100,silent=True,objective=‘binary:logistic’,booster=‘gbtree’,n_jobs=1,
    nthread=None,gamma=0,min_child_weight=1,max_delta_step=0,subsample=1,colsample_bytree=1,colsample_bylevel=1,reg_alpha=0,
    reg_lambda=1,scale_pos_weight=1,base_score=0.5,random_state=0,seed=None,missing=None)

     

  • xgboost.XGBRegressor(max_depth=3,learning_rate=0.1,n_estimators=100,silent=True,objective=‘binary:linear’,booster=‘gbtree’,n_jobs=1, nthread=None,gamma=0,min_child_weight=1,max_delta_step=0,subsample=1,colsample_bytree=1,colsample_bylevel=1,reg_alpha=0,reg_lambda=1,
    scale_pos_weight=1,base_score=0.5,random_state=0,seed=None,missing=None)

     

參數解釋:

  • max_depth用於指定每個基礎模型所包含的最大深度、默認為3層。
    learning_rate 用於指定模型迭代的學習率(步長)、默認為0.1;【與前面的提升樹模型、梯度提升樹模型含義相同】
    n_estimators 用於指定基礎模型的數量、默認為100個。
    silent:bool類型參數,是否輸出算法運行過程中的日志信息,默認為True。
    objective用於指定目標函數中的損失函數類型,1.對於分類類型的XGBoost算法、默認損失函數為二分類的Logistic損失(模型返回概率值)函數,也可以是’multi:softmax’表示處理多分類的損失函數(模型返回類別值)、還可以是’multi:softprob’處理多分類(模型返回各類別對應的概率值)。 2.對於預測型的XGBoost算法,默認損失函數為線性回歸損失’binary:linear’。
    booster:用於指定基礎模型的類型,默認為’gbtree’,即CART模型,也可以是’gblinear’表示基礎模型為線性模型。
    nthread:用於指定XGBoost算法在運行時所使用的線程數、默認為None表示計算機最大可能的線程數。
    gamma:用於指定節點分割所需的最小損失函數下降值,即增益值Gain的閾值,默認為0.
    min_child_weight : 用於指定葉子節點中各樣本點二階導之和的最小值,即Hj的最小值,默認為1,該參數的值越小模型越容易過擬合,
    max_delta_step:用於指定模型在更新過程中的步長,如果為0表示沒有約束;如果取值為某個較小的正數就會導致模型更加保守。
    subsample:用於指定構建基礎模型所使用的抽樣比例,默認為1,表示使用原始數據構建每一個基礎模型;當抽樣比例小於1時,表示構建隨機梯度提升樹模型,通常會導致模型的方差降低、偏差提高。
    colsample_bytree:用於指定每個基礎模型所需要的采樣字段比例,默認為1表示使用原始數據的所有字段。
    colsample_bylevel:用於指定每個基礎模型在節點分割時所需的采樣字段比例,默認為1表示使用原始數據的所有字段。
    reg_alpha:用於指定L1正則項的系數、默認為0。
    reg_lambda:用於指定L2正則項的系數、默認為1。
    scale_pos_weight:當各類別樣本的比例十分不平衡時,通過設定該參數為一個正值可以使算法更快收斂。
    base_score:用於指定所有樣本的初始化預測得分,默認為0.5。
    random_state:默認為0表示使用默認的隨機數生成器。
    seed:與random_state相同。
    missing:用於指定缺失值的表示方法,默認為None,即NaN為默認值。

     

XGBoost實戰案例

  • 數據集:信用卡欺詐數據集、來源於kaggle網站。
  • 包含25個變量、284807條記錄,因變量為class表示用戶在交易中是否發生欺詐行為(0表示不欺詐、1表示欺詐)
  • 由於數據涉及敏感信息、文件中已經做好了主成分分析(PCA)處理。

讀取數據:

import pandas as pd

creditcard = pd.read_csv(r'creditcard.csv')
creditcard.head(5)

 

查看各類別的標簽比例:

#探索查看各類別的比例差異
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 為確保繪制的餅圖為圓形,需執行如下代碼
plt.axes(aspect = 'equal')
# 統計交易是否為欺詐的頻數
counts = creditcard.Class.value_counts()
# 繪制餅圖
plt.pie(x = counts, # 繪圖數據
        labels=pd.Series(counts.index).map({0:'正常',1:'欺詐'}), # 添加文字標簽
        autopct='%.2f%%' # 設置百分比的格式,這里保留一位小數
       )
# 顯示圖形
plt.show()

 

在28w條交易數據中、欺詐交易僅占0.17%,兩個類別的比例存在嚴重的不平衡。
如果直接建模則模型的准確率會偏向多數類別的樣本,而正確預測交易為欺詐的概率幾乎為0.所以,需要使用SMOTE算法轉換為相對平衡的數據:

from sklearn import model_selection

# 將數據拆分為訓練集和測試集
# 刪除自變量中的Time變量
X = creditcard.drop(['Time','Class'], axis = 1)
y = creditcard.Class
# 數據拆分
X_train,X_test,y_train,y_test = model_selection.train_test_split(X,y,test_size = 0.3, random_state = 1234)

from imblearn.over_sampling import SMOTE

# 運用SMOTE算法實現訓練數據集的平衡
over_samples = SMOTE(random_state=1234) 
over_samples_X,over_samples_y = over_samples.fit_sample(X_train, y_train)
#over_samples_X, over_samples_y = over_samples.fit_sample(X_train.values,y_train.values.ravel())

# 重抽樣前的類別比例
print(y_train.value_counts()/len(y_train))
# 重抽樣后的類別比例
print('')
print(pd.Series(over_samples_y).value_counts()/len(over_samples_y))

 

使用默認參數直接建模:(也可以進行交叉驗證以及其它方式找出最優參數,但我的目的是想看一下處理非平衡數據以及不處理非平衡數據會是怎么樣的一種對比)

from sklearn import metrics
import xgboost
import numpy as np

# 構建XGBoost分類器
xgboost = xgboost.XGBClassifier()
# 使用重抽樣后的數據,對其建模
xgboost.fit(over_samples_X,over_samples_y)
# 將模型運用到測試數據集中
resample_pred = xgboost.predict(np.array(X_test)) #傳入的是array

# 返回模型的預測效果
print('模型的准確率為:\n',metrics.accuracy_score(y_test, resample_pred))
print('模型的評估報告:\n',metrics.classification_report(y_test, resample_pred))

 

計算欺詐交易的概率值,用於生成ROC曲線的數據:

y_score = xgboost.predict_proba(np.array(X_test))[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
# 計算AUC的值
roc_auc = metrics.auc(fpr,tpr)

# 繪制面積圖
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加邊際線
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加對角線
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x軸與y軸標簽
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 顯示圖形
plt.show()

 

利用不平衡數據建模進行對比一下

# 構建XGBoost分類器
import xgboost
xgboost2 = xgboost.XGBClassifier()
# 使用非平衡的訓練數據集擬合模型
xgboost2.fit(X_train,y_train)
# 基於擬合的模型對測試數據集進行預測
pred2 = xgboost2.predict(X_test)
# 混淆矩陣
pd.crosstab(pred2,y_test)
# 返回模型的預測效果
print('模型的准確率為:\n',metrics.accuracy_score(y_test, pred2))
print('模型的評估報告:\n',metrics.classification_report(y_test, pred2))

 

計算欺詐交易的概率值,用於生成ROC曲線的數據:

y_score = xgboost2.predict_proba(X_test)[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
# 計算AUC的值
roc_auc = metrics.auc(fpr,tpr)

# 繪制面積圖
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加邊際線
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加對角線
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x軸與y軸標簽
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 顯示圖形
plt.show()

 

AUC值一個是0.98,一個是0.97,雖然處理非平衡數據過后只是提升了0.01,但是也算是得到了優化。
希望大家能多多給予意見和建議。謝謝。

 


免責聲明!

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



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