XGBoost學習(六):輸出特徵重要性以及篩選特徵

安然煙火發表於2020-09-03

XGBoost學習(一):原理
XGBoost學習(二):安裝及介紹
XGBoost學習(三):模型詳解
XGBoost學習(四):實戰
XGBoost學習(五):引數調優
XGBoost學習(六):輸出特徵重要性以及篩選特徵
完整程式碼及其資料

XGBoost輸出特徵重要性以及篩選特徵

1,梯度提升演算法是如何計算特徵重要性的?

使用梯度提升演算法的好處是在提升樹被建立後,可以相對直接地得到每個屬性的重要性得分。一般來說,重要性分數,衡量了特徵在模型中的提升決策樹構建中的價值。一個屬性越多的被用來在模型中構建決策樹,它的重要性就相對越高。

屬性重要性是通過對資料集中的每個屬性進行計算,並進行排序得到。在單個決策樹中通過每個屬性分裂點改進效能度量的量來計算屬性重要性。由節點負責加權和記錄次數,也就是說一個屬性對分裂點改進效能度量越大(越靠近根節點),權值越大;被越多提升樹所選擇,屬性越重要。效能度量可以是選擇分裂節點的Gini純度,也可以是其他度量函式。

最終將一個屬性在所有提升樹中的結果進行加權求和後然後平均,得到重要性得分。

2,繪製特徵重要性

一個已訓練的Xgboost模型能夠自動計算特徵重要性,這些重要性得分可以通過成員變數feature_importances_得到。可以通過如下命令列印:

print(model.feature_importances_)

我們可以直接在條形圖上繪製這些分數,以便獲得資料集中每個特徵的相對重要性的直觀顯示,例如:

# plot
pyplot.bar(range(len(model.feature_importances_)), model.feature_importances_)
pyplot.show()

我們可以通過在the Pima Indians onset of diabetes 資料集上訓練XGBoost模型來演示,並從計算的特徵重要性中繪製條形圖。

# plot feature importance manually
from numpy import loadtxt
from xgboost import XGBClassifier
from matplotlib import pyplot
from sklearn.datasets import load_iris
# load data
dataset = load_iris()
# split data into X and y
X = dataset.data
y = dataset.target
# fit model no training data
model = XGBClassifier()
model.fit(X, y)
# feature importance
print(model.feature_importances_)
# plot
pyplot.bar(range(len(model.feature_importances_)), model.feature_importances_)
pyplot.show()

執行這個例項,首先輸出特徵重要性分數:
[0.17941953 0.11345647 0.41556728 0.29155672]
相對重要性條形圖:
在這裡插入圖片描述
  這種繪製的缺點在於,只顯示了特徵重要性而沒有排序,可以在繪製之前對特徵重要性得分進行排序。
  通過內建的繪製函式進行特徵重要性得分排序後的繪製,這個函式就是plot_importance(),示例如下:

# plot feature importance manually
from numpy import loadtxt
from xgboost import XGBClassifier
from matplotlib import pyplot
from sklearn.datasets import load_iris
from xgboost import plot_importance
 
# load data
dataset = load_iris()
# split data into X and y
X = dataset.data
y = dataset.target
# fit model no training data
model = XGBClassifier()
model.fit(X, y)
# feature importance
print(model.feature_importances_)
# plot feature importance
 
plot_importance(model)
pyplot.show()

示例得到條形圖:
在這裡插入圖片描述

根據其在輸入陣列的索引,特徵被自動命名為f0~f3,在問題描述中手動的將這些索引對映到名稱,我們可以看到,f2具有最高的重要性,f1具有最低的重要性。

3,根據Xgboost特徵重要性得分進行特徵選擇

特徵重要性得分,可以用於在scikit-learn中進行特徵選擇。通過SelectFromModel類實現,該類採用模型並將資料集轉換為具有選定特徵的子集。這個類可以採取預先訓練的模型,例如在整個資料集上訓練的模型。然後,它可以閾值來決定選擇哪些特徵。當在SelectFromModel例項上呼叫transform()方法時,該閾值被用於在訓練集和測試集上一致性選擇相同特徵。

在下面的示例中,我們首先在訓練集上訓練xgboost模型,然後在測試上評估。使用從訓練資料集計算的特徵重要性,然後,將模型封裝在一個SelectFromModel例項中。我們使用這個來選擇訓練集上的特徵,用所選擇的特徵子集訓練模型,然後在相同的特徵方案下對測試集進行評估。

# select features using threshold
selection = SelectFromModel(model, threshold=thresh, prefit=True)
select_X_train = selection.transform(X_train)
# train model
selection_model = XGBClassifier()
selection_model.fit(select_X_train, y_train)
# eval model
select_X_test = selection.transform(X_test)
y_pred = selection_model.predict(select_X_test)

我們可以通過測試多個閾值,來從特徵重要性中選擇特徵。具體而言,每個輸入變數的特徵重要性,本質上允許我們通過重要性來測試每個特徵子集。

完整程式碼如下:

# plot feature importance manually
import numpy as np
from xgboost import XGBClassifier
from matplotlib import pyplot
from sklearn.datasets import load_iris
from xgboost import plot_importance
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.feature_selection import SelectFromModel
 
# load data
dataset = load_iris()
# split data into X and y
X = dataset.data
y = dataset.target
 
# split data into train and test sets
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.33,random_state=7)
 
# fit model no training data
model = XGBClassifier()
model.fit(X_train, y_train)
# feature importance
print(model.feature_importances_)
 
# make predictions for test data and evaluate
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]
accuracy = accuracy_score(y_test,predictions)
print("Accuracy:%.2f%%"%(accuracy*100.0))
 
#fit model using each importance as a threshold
thresholds = np.sort(model.feature_importances_)
for thresh in thresholds:
    # select features using threshold
    selection = SelectFromModel(model,threshold=thresh,prefit=True )
    select_X_train = selection.transform(X_train)
    # train model
    selection_model = XGBClassifier()
    selection_model.fit(select_X_train, y_train)
    # eval model
    select_X_test = selection.transform(X_test)
    y_pred = selection_model.predict(select_X_test)
    predictions = [round(value) for value in y_pred]
    accuracy = accuracy_score(y_test,predictions)
    print("Thresh=%.3f, n=%d, Accuracy: %.2f%%" % (thresh, select_X_train.shape[1], accuracy * 100.0))

執行示例,得到輸出:
[0.20993228 0.09029345 0.54176074 0.15801354]
Accuracy:92.00%
Thresh=0.090, n=4, Accuracy: 92.00%
Thresh=0.158, n=3, Accuracy: 92.00%
Thresh=0.210, n=2, Accuracy: 86.00%
Thresh=0.542, n=1, Accuracy: 90.00%
我們可以看到,模型的效能通常隨著所選擇的特徵的數量減少,在這一問題上,可以對測試集準確率和模型複雜度做一個權衡,例如選擇三個特徵,接受準確率為92%,這可能是對這樣一個小資料集的清洗,但是對於更大的資料集和使用交叉驗證作為模型評估方案可能是更有用的策略。

4,網格搜尋

程式碼1:

from sklearn.model_selection import GridSearchCV
tuned_parameters= [{'n_estimators':[100,200,500],
                  'max_depth':[3,5,7], ##range(3,10,2)
                  'learning_rate':[0.5, 1.0],
                  'subsample':[0.75,0.8,0.85,0.9]
                  }]
tuned_parameters= [{'n_estimators':[100,200,500,1000]
                  }]
clf = GridSearchCV(XGBClassifier(silent=0,nthread=4,learning_rate= 0.5,min_child_weight=1, max_depth=3,gamma=0,subsample=1,colsample_bytree=1,reg_lambda=1,seed=1000), param_grid=tuned_parameters,scoring='roc_auc',n_jobs=4,iid=False,cv=5) 
clf.fit(X_train, y_train)
##clf.grid_scores_, clf.best_params_, clf.best_score_
print(clf.best_params_)
y_true, y_pred = y_test, clf.predict(X_test)
print"Accuracy : %.4g" % metrics.accuracy_score(y_true, y_pred)
y_proba=clf.predict_proba(X_test)[:,1]
print "AUC Score (Train): %f" % metrics.roc_auc_score(y_true, y_proba)   

程式碼2:

from sklearn.model_selection import GridSearchCV
parameters= [{'learning_rate':[0.01,0.1,0.3],'n_estimators':[1000,1200,1500,2000,2500]}]
clf = GridSearchCV(XGBClassifier(
             max_depth=3,
             min_child_weight=1,
             gamma=0.5,
             subsample=0.6,
             colsample_bytree=0.6,
             objective= 'binary:logistic', #邏輯迴歸損失函式
             scale_pos_weight=1,
             reg_alpha=0,
             reg_lambda=1,
             seed=27
            ),
            param_grid=parameters,scoring='roc_auc') 
clf.fit(X_train, y_train)
print(clf.best_params_) 
y_pre= clf.predict(X_test)
y_pro= clf.predict_proba(X_test)[:,1]
print "AUC Score : %f" % metrics.roc_auc_score(y_test, y_pro)
print"Accuracy : %.4g" % metrics.accuracy_score(y_test, y_pre)

輸出特徵重要性:

import pandas as pd
import matplotlib.pylab as plt
feat_imp = pd.Series(clf.booster().get_fscore()).sort_values(ascending=False)
feat_imp.plot(kind='bar', title='Feature Importances')
plt.ylabel('Feature Importance Score')
plt.show()

補充:關於隨機種子——random_state

random_state是一個隨機種子,是在任意帶有隨機性的類或者函式裡作為引數來控制隨機模式。random_state取某一個值的時候,也就確定了一種規則。
random_state可以用於很多函式,比如訓練集測試集的劃分;構建決策樹;構建隨機森林

1,劃分訓練集和測試集的類train_test_split

隨機數種子控制每次劃分訓練集和測試集的模式,其取值不變時劃分得到的結果一模一樣,其值改變時,劃分得到的結果不同。若不設定此引數,則函式會自動選擇一種隨機模式,得到的結果也就不同。

2,構建決策樹的函式

clf = tree.DecisionTreeClassifier(criterion="entropy",random_state=30,splitter="random")

其取值不變時,用相同的訓練集建樹得到的結果一模一樣,對測試集的預測結果也是一樣的
其取值改變時,得到的結果不同;
若不設定此引數(即設定為None),則函式會自動選擇一種隨機模式,每次得到的結果也就不同,可能稍微有所波動。

3,構建隨機森林

clf = RandomForestClassifier(random_state=0)

其取值不變時,用相同的訓練集建樹得到的結果一模一樣,對測試集的預測結果也是一樣的
其取值改變時,得到的結果不同;
若不設定此引數(即設定為None),則函式會自動選擇一種隨機模式,每次得到的結果也就不同,可能稍微有所波動。

4,總結

在需要設定random_state的地方給其賦值,當多次執行此段程式碼得到完全一樣的結果,別人執行程式碼也可以復現你的過程。若不設定此引數則會隨機選擇一個種子,執行結果也會因此不同。雖然可以對random_state進行調參,但是調參後再訓練集上表現好的模型未必在陌生訓練集上表現好,所以一般會隨便選擇一個random_state的值作為引數。
對於那些本質上是隨機的過程,我們有必要控制隨機的狀態,這樣才能重複的展現相同的結果。如果對隨機狀態不加控制,那麼實驗的結果就無法固定,而是隨機的顯示。
其實random_state 與 random seed作用是相同的,下面我們通過 random seed來學習一下 random_state:
在這裡插入圖片描述
第一段程式碼和第二段程式碼完全相同,在1~100中取10個隨機數,都沒有設定 random seed,它每次取的結果就不太,它的隨機數種子與當前系統的時間有關。
第三段程式碼和第四段程式碼設定了相同的 random seed(123),他們取的隨機數就完全相同,你多執行幾次也是這樣。
第五段程式碼設定了 random seed(456),但是與之前設定的不同,於是執行取隨機數的結果也不同。

相關文章