我的XGBoost學習經歷及動手實踐

↑↑↑關注後"星標"Datawhale

每日干貨 & 每月組隊學習,不錯過

 Datawhale乾貨 

作者:李祖賢  深圳大學,Datawhale高校羣成員

知乎地址:http://www.zhihu.com/people/meng-di-76-92

我今天主要介紹機器學習集成學習方法中三巨頭之一的XGBoost,這個算法在早些時候機器學習比賽內曾經大放異彩,是非常好用的一個機器學習集成算法。

XGBoost是一個優化的分佈式梯度增強庫,旨在實現高效,靈活和便攜。它在Gradient Boosting框架下實現機器學習算法。XGBoost提供了並行樹提升(也稱爲GBDT,GBM),可以快速準確地解決許多數據科學問題。

相同的代碼在主要的分佈式環境(Hadoop,SGE,MPI)上運行,並且可以解決超過數十億個樣例的問題。XGBoost利用了核外計算並且能夠使數據科學家在一個主機上處理數億的樣本數據。最終,將這些技術進行結合來做一個端到端的系統以最少的集羣系統來擴展到更大的數據集上。

XGBoost原理介紹

從0開始學習,經歷過推導公式的波瀾曲折,下面展示下我自己的推公式的手稿吧,希望能激勵到大家能夠對機器學習數據挖掘更加熱愛!

XGBoost公式1

XGBoost公式2

現在我們對手稿的內容進行詳細的講解:

1. 優化目標: 

我們的任務是找到一組樹使得OBj最小,很明顯這個優化目標OBj可以看成是樣本的損失和模型的複雜度懲罰相加組成。

2. 使用追加法訓練(Additive Training Boosting)

核心思想是:在已經訓練好了  棵樹後不再調整前  棵樹,那麼第t棵樹可以表示爲:

 

(1).  那此時如果我們對第t棵樹訓練,則目標函數爲:

對上式進行泰勒二階展開:

由於前t-1棵樹已知,那麼

 

(2). 我們已經對前半部分的損失函數做出了充分的討論,但是後半部分的  還只是個符號並未定義,那我們現在就來定義  :假設我們待訓練的第t棵樹有T個葉子結點:葉子結點的輸出向量表示如下:

假設  表示樣本到葉子結點的映射,那麼  。那麼我們定義 :

(3). 我們的目標函數最終化簡爲:

我們找到了目標函數就需要對目標函數進行優化:

3. 生成樹的策略

我們剛剛的假設前提是已知前t-1棵樹,因此我們現在來探討怎麼生成樹。根據決策樹的生成策略,再每次分裂節點的時候我們需要考慮能使得損失函數減小最快的節點,也就是分裂後損失函數減去分裂前損失函數我們稱之爲Gain:

Gain越大越能說明分裂後目標函數值減小越多。(因爲從式子來看:  越大,反而OBj越小)

4. 尋找最優節點

  • 精確貪心算法(Basic Exact Greedy Algorithm)

  • 近似算法(Approximate Algorithm)

在決策樹(CART)裏面,我們使用的是精確貪心算法(Basic Exact Greedy Algorithm),也就是將所有特徵的所有取值排序(耗時耗內存巨大),然後比較每一個點的Gini,找出變化最大的節點。當特徵是連續特徵時,我們對連續值離散化,取兩點的平均值爲分割節點。可以看到,這裏的排序算法需要花費大量的時間,因爲要遍歷整個樣本所有特徵,而且還要排序!!

論文的精確貪心算法的僞代碼

因此在XGBoost裏面我們使用的是近似算法(Approximate Algorithm):該算法首先根據特徵分佈的百分位數(percentiles)提出候選分裂點,將連續特徵映射到由這些候選點分割的桶中,彙總統計信息並根據彙總的信息在提案中找到最佳解決方案。對於某個特徵k,算法首先根據特徵分佈的分位數找到特徵切割點的候選集合  ,然後將特徵k的值根據集合  劃分到桶(bucket)中,接着對每個桶內的樣本統計值G、H進行累加,最後在這些累計的統計量上尋找最佳分裂點。

論文的近似算法的僞代碼

XGBoost動手實踐:

1. 引入基本工具庫:

# 引入基本工具庫
import numpy as np
import pandas as pd
import xgboost as xgb
import matplotlib.pyplot as plt
plt.style.use("ggplot")
%matplotlib inline

2. XGBoost原生工具庫的上手:

import xgboost as xgb  # 引入工具庫
# read in data
dtrain = xgb.DMatrix('demo/data/agaricus.txt.train')   # XGBoost的專屬數據格式,但是也可以用dataframe或者ndarray
dtest = xgb.DMatrix('demo/data/agaricus.txt.test')  # # XGBoost的專屬數據格式,但是也可以用dataframe或者ndarray
# specify parameters via map
param = {'max_depth':2, 'eta':1, 'objective':'binary:logistic' }    # 設置XGB的參數,使用字典形式傳入
num_round = 2     # 使用線程數
bst = xgb.train(param, dtrain, num_round)   # 訓練
# make prediction
preds = bst.predict(dtest)   # 預測

3. XGBoost的參數設置(括號內的名稱爲sklearn接口對應的參數名字)

XGBoost的參數分爲三種:

1. 通用參數 

  • booster:使用哪個弱學習器訓練,默認gbtree,可選gbtree,gblinear 或dart

  • nthread:用於運行XGBoost的並行線程數,默認爲最大可用線程數

  • verbosity:打印消息的詳細程度。有效值爲0(靜默),1(警告),2(信息),3(調試)。

  • Tree Booster的參數:

    • eta(learning_rate):learning_rate,在更新中使用步長收縮以防止過度擬合,默認= 0.3,範圍:[0,1];典型值一般設置爲:0.01-0.2

    • gamma(min_split_loss):默認= 0,分裂節點時,損失函數減小值只有大於等於gamma節點才分裂,gamma值越大,算法越保守,越不容易過擬合,但性能就不一定能保證,需要平衡。範圍:[0,∞]

    • max_depth:默認= 6,一棵樹的最大深度。增加此值將使模型更復雜,並且更可能過度擬合。範圍:[0,∞]

    • min_child_weight:默認值= 1,如果新分裂的節點的樣本權重和小於min_child_weight則停止分裂 。這個可以用來減少過擬合,但是也不能太高,會導致欠擬合。範圍:[0,∞]

    • max_delta_step:默認= 0,允許每個葉子輸出的最大增量步長。如果將該值設置爲0,則表示沒有約束。如果將其設置爲正值,則可以幫助使更新步驟更加保守。通常不需要此參數,但是當類極度不平衡時,它可能有助於邏輯迴歸。將其設置爲1-10的值可能有助於控制更新。範圍:[0,∞]

    • subsample:默認值= 1,構建每棵樹對樣本的採樣率,如果設置成0.5,XGBoost會隨機選擇一半的樣本作爲訓練集。範圍:(0,1]

    • sampling_method:默認= uniform,用於對訓練實例進行採樣的方法。

      • uniform:每個訓練實例的選擇概率均等。通常將subsample> = 0.5 設置 爲良好的效果。

      • gradient_based:每個訓練實例的選擇概率與規則化的梯度絕對值成正比,具體來說就是  ,subsample可以設置爲低至0.1,而不會損失模型精度。

    • colsample_bytree:默認= 1,列採樣率,也就是特徵採樣率。範圍爲(0,1]

    • lambda(reg_lambda):默認=1,L2正則化權重項。增加此值將使模型更加保守。

    • alpha(reg_alpha):默認= 0,權重的L1正則化項。增加此值將使模型更加保守。

    • tree_method:默認=auto,XGBoost中使用的樹構建算法。

      • auto:使用啓發式選擇最快的方法。

        • 對於小型數據集,exact將使用精確貪婪()。

        • 對於較大的數據集,approx將選擇近似算法()。它建議嘗試hist,gpu_hist,用大量的數據可能更高的性能。(gpu_hist)支持。external memory外部存儲器。

      • exact:精確的貪婪算法。枚舉所有拆分的候選點。

      • approx:使用分位數和梯度直方圖的近似貪婪算法。

      • hist:更快的直方圖優化的近似貪婪算法。(LightGBM也是使用直方圖算法)

      • gpu_hist:GPU hist算法的實現。

    • scale_pos_weight:控制正負權重的平衡,這對於不平衡的類別很有用。Kaggle競賽一般設置sum(negative instances) / sum(positive instances),在類別高度不平衡的情況下,將參數設置大於0,可以加快收斂。

    • num_parallel_tree:默認=1,每次迭代期間構造的並行樹的數量。此選項用於支持增強型隨機森林。

    • monotone_constraints:可變單調性的約束,在某些情況下,如果有非常強烈的先驗信念認爲真實的關係具有一定的質量,則可以使用約束條件來提高模型的預測性能。(例如params_constrained['monotone_constraints'] = "(1,-1)",(1,-1)我們告訴XGBoost對第一個預測變量施加增加的約束,對第二個預測變量施加減小的約束。)

  • Linear Booster的參數:

    • lambda(reg_lambda):默認= 0,L2正則化權重項。增加此值將使模型更加保守。歸一化爲訓練示例數。

    • alpha(reg_alpha):默認= 0,權重的L1正則化項。增加此值將使模型更加保守。歸一化爲訓練示例數。

    • updater:默認= shotgun。

      • shotgun:基於shotgun算法的平行座標下降算法。使用“ hogwild”並行性,因此每次運行都產生不確定的解決方案。

      • coord_descent:普通座標下降算法。同樣是多線程的,但仍會產生確定性的解決方案。

    • feature_selector:默認= cyclic。特徵選擇和排序方法

      • cyclic:通過每次循環一個特徵來實現的。

      • shuffle:類似於cyclic,但是在每次更新之前都有隨機的特徵變換。

      • random:一個隨機(有放回)特徵選擇器。

      • greedy:選擇梯度最大的特徵。(貪婪選擇)

      • thrifty:近似貪婪特徵選擇(近似於greedy)

    • top_k:要選擇的最重要特徵數(在greedy和thrifty內)

通用參數有兩種類型的booster,因爲tree的性能比線性迴歸好得多,因此我們很少用線性迴歸。

2. 任務參數

  • objective:默認=reg:squarederror,表示最小平方誤差。

    • reg:squarederror,最小平方誤差。

    • reg:squaredlogerror,對數平方損失。

    • reg:logistic,邏輯迴歸

    • reg:pseudohubererror,使用僞Huber損失進行迴歸,這是絕對損失的兩倍可微選擇。

    • binary:logistic,二元分類的邏輯迴歸,輸出概率。

    • binary:logitraw:用於二進制分類的邏輯迴歸,邏輯轉換之前的輸出得分。

    • binary:hinge:二進制分類的鉸鏈損失。這使預測爲0或1,而不是產生概率。(SVM就是鉸鏈損失函數)

    • count:poisson –計數數據的泊松迴歸,泊松分佈的輸出平均值。

    • survival:cox:針對正確的生存時間數據進行Cox迴歸(負值被視爲正確的生存時間)。

    • survival:aft:用於檢查生存時間數據的加速故障時間模型。

    • aft_loss_distribution:survival:aft和aft-nloglik度量標準使用的概率密度函數。

    • multi:softmax:設置XGBoost以使用softmax目標進行多類分類,還需要設置num_class(類數)

    • multi:softprob:與softmax相同,但輸出向量,可以進一步重整爲矩陣。結果包含屬於每個類別的每個數據點的預測概率。

    • rank:pairwise:使用LambdaMART進行成對排名,從而使成對損失最小化。

    • rank:ndcg:使用LambdaMART進行列表式排名,使標準化折讓累積收益(NDCG)最大化。

    • rank:map:使用LambdaMART進行列表平均排名,使平均平均精度(MAP)最大化。

    • reg:gamma:使用對數鏈接進行伽馬迴歸。輸出是伽馬分佈的平均值。

    • reg:tweedie:使用對數鏈接進行Tweedie迴歸。

    • 自定義損失函數和評價指標:

  • eval_metric:驗證數據的評估指標,將根據目標分配默認指標(迴歸均方根,分類誤差,排名的平均平均精度),用戶可以添加多個評估指標

    • rmse,均方根誤差;rmsle:均方根對數誤差;mae:平均絕對誤差;mphe:平均僞Huber錯誤;logloss:負對數似然;error:二進制分類錯誤率;

    • merror:多類分類錯誤率;mlogloss:多類logloss;auc:曲線下面積;aucpr:PR曲線下的面積;ndcg:歸一化累計折扣;map:平均精度;

  • seed :隨機數種子,[默認= 0]。

這個參數用來控制理想的優化目標和每一步結果的度量方法。

3. 命令行參數 

這裏不說了,因爲很少用命令行控制檯版本

4. XGBoost的調參說明:

參數調優的一般步驟:

  • 1.確定(較大)學習速率和提升參數調優的初始值

  • 2.max_depth 和 min_child_weight 參數調優

  • 3.gamma參數調優

  • 4.subsample 和 colsample_bytree 參數優

  • 5.正則化參數alpha調優

  • 6.降低學習速率和使用更多的決策樹

5. XGBoost詳細攻略:

1). 安裝XGBoost

方式1:
pip3 install xgboost
方式2:
pip install xgboost

2). 數據接口XGBoost可處理的數據格式DMatrix)

# 1.LibSVM文本格式文件
dtrain = xgb.DMatrix('train.svm.txt')
dtest = xgb.DMatrix('test.svm.buffer')
# 2.CSV文件(不能含類別文本變量,如果存在文本變量請做特徵處理如one-hot)
dtrain = xgb.DMatrix('train.csv?format=csv&label_column=0')
dtest = xgb.DMatrix('test.csv?format=csv&label_column=0')
# 3.NumPy數組
data = np.random.rand(5, 10)  # 5 entities, each contains 10 features
label = np.random.randint(2, size=5)  # binary target
dtrain = xgb.DMatrix(data, label=label)
# 4.scipy.sparse數組
csr = scipy.sparse.csr_matrix((dat, (row, col)))
dtrain = xgb.DMatrix(csr)
# pandas數據框dataframe
data = pandas.DataFrame(np.arange(12).reshape((4,3)), columns=['a', 'b', 'c'])
label = pandas.DataFrame(np.random.randint(2, size=4))
dtrain = xgb.DMatrix(data, label=label)

筆者推薦:先保存到XGBoost二進制文件中將使加載速度更快,然後再加載進來

# 1.保存DMatrix到XGBoost二進制文件中
dtrain = xgb.DMatrix('train.svm.txt')
dtrain.save_binary('train.buffer')
# 2. 缺少的值可以用DMatrix構造函數中的默認值替換:
dtrain = xgb.DMatrix(data, label=label, missing=-999.0)
# 3.可以在需要時設置權重:
w = np.random.rand(5, 1)
dtrain = xgb.DMatrix(data, label=label, missing=-999.0, weight=w)

3). 參數的設置方式

# 加載並處理數據
df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data',header=None)
df_wine.columns = ['Class label', 'Alcohol','Malic acid', 'Ash','Alcalinity of ash','Magnesium', 'Total phenols',
                   'Flavanoids', 'Nonflavanoid phenols','Proanthocyanins','Color intensity', 'Hue','OD280/OD315 of diluted wines','Proline']
df_wine = df_wine[df_wine['Class label'] != 1]  # drop 1 class
y = df_wine['Class label'].values
X = df_wine[['Alcohol','OD280/OD315 of diluted wines']].values
from sklearn.model_selection import train_test_split  # 切分訓練集與測試集
from sklearn.preprocessing import LabelEncoder   # 標籤化分類變量
le = LabelEncoder()
y = le.fit_transform(y)
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=1,stratify=y)
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test)
# 1.Booster 參數
params = {
    'booster': 'gbtree',
    'objective': 'multi:softmax',  # 多分類的問題
    'num_class': 10,               # 類別數,與 multisoftmax 並用
    'gamma': 0.1,                  # 用於控制是否後剪枝的參數,越大越保守,一般0.1、0.2這樣子。
    'max_depth': 12,               # 構建樹的深度,越大越容易過擬合
    'lambda': 2,                   # 控制模型複雜度的權重值的L2正則化項參數,參數越大,模型越不容易過擬合。
    'subsample': 0.7,              # 隨機採樣訓練樣本
    'colsample_bytree': 0.7,       # 生成樹時進行的列採樣
    'min_child_weight': 3,
    'silent': 1,                   # 設置成1則沒有運行信息輸出,最好是設置爲0.
    'eta': 0.007,                  # 如同學習率
    'seed': 1000,
    'nthread': 4,                  # cpu 線程數
    'eval_metric':'auc'
}
plst = params.items()
# evallist = [(dtest, 'eval'), (dtrain, 'train')]   # 指定驗證集

4). 訓練

# 2.訓練
num_round = 10
bst = xgb.train( plst, dtrain, num_round)
#bst = xgb.train( plst, dtrain, num_round, evallist )

5). 保存模型

# 3.保存模型
bst.save_model('0001.model')
# dump model
bst.dump_model('dump.raw.txt')
# dump model with feature map
#bst.dump_model('dump.raw.txt', 'featmap.txt')

6) . 加載保存的模型

# 4.加載保存的模型:
bst = xgb.Booster({'nthread': 4})  # init model
bst.load_model('0001.model')  # load data

7). 設置早停機制

# 5.也可以設置早停機制(需要設置驗證集)
train(..., evals=evals, early_stopping_rounds=10)

8). 預測

# 6.預測
ypred = bst.predict(dtest)

9). 繪圖

# 1.繪製重要性
xgb.plot_importance(bst)
# 2.繪製輸出樹
#xgb.plot_tree(bst, num_trees=2)
# 3.使用xgboost.to_graphviz()將目標樹轉換爲graphviz
#xgb.to_graphviz(bst, num_trees=2)

6. 實戰案例:

1). 分類案例

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
from sklearn.metrics import accuracy_score   # 準確率
# 加載樣本數據集
iris = load_iris()
X,y = iris.data,iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234565) # 數據集分割


# 算法參數
params = {
    'booster': 'gbtree',
    'objective': 'multi:softmax',
    'num_class': 3,
    'gamma': 0.1,
    'max_depth': 6,
    'lambda': 2,
    'subsample': 0.7,
    'colsample_bytree': 0.75,
    'min_child_weight': 3,
    'silent': 0,
    'eta': 0.1,
    'seed': 1,
    'nthread': 4,
}


plst = params.items()


dtrain = xgb.DMatrix(X_train, y_train) # 生成數據集格式
num_rounds = 500
model = xgb.train(plst, dtrain, num_rounds) # xgboost模型訓練


# 對測試集進行預測
dtest = xgb.DMatrix(X_test)
y_pred = model.predict(dtest)


# 計算準確率
accuracy = accuracy_score(y_test,y_pred)
print("accuarcy: %.2f%%" % (accuracy*100.0))


# 顯示重要特徵
plot_importance(model)
plt.show()

2). 迴歸案例

import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error


# 加載數據集
boston = load_boston()
X,y = boston.data,boston.target


# XGBoost訓練過程
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)


params = {
    'booster': 'gbtree',
    'objective': 'reg:squarederror',
    'gamma': 0.1,
    'max_depth': 5,
    'lambda': 3,
    'subsample': 0.7,
    'colsample_bytree': 0.7,
    'min_child_weight': 3,
    'silent': 1,
    'eta': 0.1,
    'seed': 1000,
    'nthread': 4,
}


dtrain = xgb.DMatrix(X_train, y_train)
num_rounds = 300
plst = params.items()
model = xgb.train(plst, dtrain, num_rounds)


# 對測試集進行預測
dtest = xgb.DMatrix(X_test)
ans = model.predict(dtest)


# 顯示重要特徵
plot_importance(model)
plt.show()

7. XGBoost調參(結合sklearn網格搜索)

import xgboost as xgb
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import roc_auc_score


iris = load_iris()
X,y = iris.data,iris.target
col = iris.target_names
train_x, valid_x, train_y, valid_y = train_test_split(X, y, test_size=0.3, random_state=1)   # 分訓練集和驗證集
parameters = {
              'max_depth': [5, 10, 15, 20, 25],
              'learning_rate': [0.01, 0.02, 0.05, 0.1, 0.15],
              'n_estimators': [500, 1000, 2000, 3000, 5000],
              'min_child_weight': [0, 2, 5, 10, 20],
              'max_delta_step': [0, 0.2, 0.6, 1, 2],
              'subsample': [0.6, 0.7, 0.8, 0.85, 0.95],
              'colsample_bytree': [0.5, 0.6, 0.7, 0.8, 0.9],
              'reg_alpha': [0, 0.25, 0.5, 0.75, 1],
              'reg_lambda': [0.2, 0.4, 0.6, 0.8, 1],
              'scale_pos_weight': [0.2, 0.4, 0.6, 0.8, 1]


}


xlf = xgb.XGBClassifier(max_depth=10,
            learning_rate=0.01,
            n_estimators=2000,
            silent=True,
            objective='multi:softmax',
            num_class=3 ,
            nthread=-1,
            gamma=0,
            min_child_weight=1,
            max_delta_step=0,
            subsample=0.85,
            colsample_bytree=0.7,
            colsample_bylevel=1,
            reg_alpha=0,
            reg_lambda=1,
            scale_pos_weight=1,
            seed=0,
            missing=None)


gs = GridSearchCV(xlf, param_grid=parameters, scoring='accuracy', cv=3)
gs.fit(train_x, train_y)


print("Best score: %0.3f" % gs.best_score_)
print("Best parameters set: %s" % gs.best_params_ )
本文電子版 後臺回覆 XGBoost 獲取


????點擊閱讀原文,關注作者
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章