基於模型刷選特征方法有:排列重要性、shap value、null importance
下面來說一下 shap value
一、shap value的原理
在SHAP被廣泛使用之前,我們通常用feature importance或者partial dependence plot來解釋xgboost。Feature importance可以直觀地反映出特征的重要性,看出哪些特征對最終的模型影響較大。但是無法判斷特征與最終預測結果的關系是如何的,是正相關、負相關還是其他更復雜的相關性?因此就引起來SHAP。
SHAP的名稱來源於SHapley Additive exPlanation。Shapley value起源於合作博弈論。比如說甲乙丙丁四個工人一起打工,甲和乙完成了價值100元的工件,甲、乙、丙完成了價值120元的工件,乙、丙、丁完成了價值150元的工件,甲、丁完成了價值90元的工件,那么該如何公平、合理地分配這四個人的工錢呢?Shapley提出了一個合理的計算方法(有興趣地可以查看原論文),我們稱每個參與者分配到的數額為Shapley value。
SHAP是由Shapley value啟發的可加性解釋模型。對於每個預測樣本,模型都產生一個預測值,SHAP value就是該樣本中每個特征所分配到的數值。 假設第i個樣本為xi,第i個樣本的第j個特征為xi,j,模型對第i個樣本的預測值為yi,整個模型的基線(通常是所有樣本的目標變量的均值)為ybase,那么SHAP value服從以下等式。
其中f(xi,1)為xi,j的SHAP值。直觀上看,f(xi,1)就是第i個樣本中第1個特征對最終預測值yi的貢獻值,當f(xi,1)>0,說明該特征提升了預測值,也正向作用;反之,說明該特征使得預測值降低,有反作用。SHAP value最大的優勢是SHAP能對於反映出每一個樣本中的特征的影響力,而且還表現出影響的正負性
二、shap的實現
前期簡單建模,使用波士頓房價數據
# -*- coding: utf-8 -*- """ Created on Sun Sep 26 15:51:26 2021 @author: chenguimei """ # 加載模塊 import xgboost as xgb import pandas as pd import numpy as np import matplotlib.pyplot as plt; plt.style.use('seaborn') #波士頓房價數據集 from sklearn.datasets import load_boston boston=load_boston() boston.data boston.target boston.feature_names boston_df=pd.DataFrame(boston.data,columns=boston.feature_names) boston_df['target'] = boston.target cols = [i for i in boston_df.columns[:-1]] # 訓練xgboost回歸模型 model = xgb.XGBRegressor(max_depth=4, learning_rate=0.05, n_estimators=150) model.fit(boston_df[cols], boston_df['target'].values) # 獲取feature importance plt.figure(figsize=(15, 5)) plt.bar(range(len(cols)), model.feature_importances_) plt.xticks(range(len(cols)), cols, rotation=-45, fontsize=14) plt.title('Feature importance', fontsize=14) plt.show()
將每一行每個特征的shap value值放在一個df中,也就是將上面的公式實例出來
import shap # model是在第1節中訓練的模型 explainer = shap.TreeExplainer(model) shap_values = explainer.shap_values(boston_df[cols])
(1)單個樣本(一行數據)的SHAP值
隨機檢查其中一個樣本的每個特征對預測值的影響
#第一列是特征名稱,第二列是特征的數值,第三列是各個特征在該樣本中對應的SHAP值。 # 比如我們挑選數據集中的第30位 j = 30 player_explainer = pd.DataFrame() player_explainer['feature'] = cols player_explainer['feature_value'] = boston_df[cols].iloc[j].values player_explainer['shap_value'] = shap_values[j] player_explainer['base'] = model.predict(boston_df[cols]).mean() #就是預測的分數的均值 player_explainer['sum'] = player_explainer['shap_value'].sum() #特征的shap和 player_explainer['base+sum'] = player_explainer['base']+player_explainer['sum'] player_explainer
shap
還提供極其強大的數據可視化功能
shap.initjs()
shap.force_plot(explainer.expected_value, shap_values[j], boston_df[cols].iloc[j])
藍色表示該特征的貢獻是負數,紅色則表示該特征的貢獻是正數,黑色加粗的是該樣本最后的預測值。最長的藍色條是LSTAT,該特征的含義是人口密度,密度低,房價自然也低。
(2)全部特征的分析
除了能對單個樣本的SHAP值進行可視化之外,還能對特征進行整體的可視化
shap.summary_plot(shap_values, boston_df[cols])
圖中每一行代表一個特征,橫坐標為SHAP值。一個點代表一個樣本,顏色越紅說明特征本身數值越大,顏色越藍說明特征本身數值越小,可以看出LSTAT越大,房價越小,和房價成反比關系
也可以把一個特征對目標變量影響程度的絕對值的均值作為這個特征的重要性
shap.summary_plot(shap_values, boston_df[cols], plot_type="bar")
上面這個圖怎么得來的,我就演示一下LSTAT變量的值
#LSTAT位於最后一個,因此我們只需要提取最后一列 pd.DataFrame(shap_values).iloc[:,-1].apply(lambda x:abs(x)).mean() #輸出 3.7574333926117998
(3)部分依賴圖Partial Dependence Plot
就是shap值和原值的散點圖,可以看出趨勢,是單調還是U型,等等
shap.dependence_plot('LSTAT', shap_values, boston_df[cols], interaction_index=None, show=False)
(4)對多個變量的交互進行分析
我們也可以多個變量的交互作用進行分析。一種方式是采用summary_plot
描繪出散點圖
shap interaction values則是特征倆倆之間的交互歸因值,用於捕捉成對的相互作用效果,由於shap interaction values得到的是相互作用的交互歸因值,假設有N個樣本M個特征時,shap values的維度是N×M,而shap interaction values的維度是N×M×M,也就是說一個樣本的一個特征shap valus由一個歸因值對應,而shap interaction values由一系列交互歸因值對應,並且交互歸因值的和等於歸因值。
我是這樣理解:
shap valus = base+x1_shap+x2_shap+...+xn_shap
shap interaction values = base + x1_shap+x2_shap+...+xn_shap+x2*x3*x4...xn_shap_interaction_values+....
shap_interaction_values = shap.TreeExplainer(model).shap_interaction_values(boston_df[cols])
shap.summary_plot(shap_interaction_values, boston_df[cols], max_display=4)
看這個圖不是很明白,我們看具體數據,就很清晰明了
print('第一個特征的shap values:', shap_values[0][0]) print('第一個特征的shap interaction values:', shap_interaction_values[0][0]) print('第一個特征的shap interaction values和:', shap_interaction_values[0][0].sum()) ''' 第一個特征的shap values: -0.38626033 第一個特征的shap interaction values: [ 8.65335166e-02 5.80601394e-04 5.75962476e-03 -1.57106481e-03 -7.26313889e-02 -6.15637302e-02 -7.20938668e-02 -6.41713059e-03 -4.72193956e-03 -3.00363600e-02 -1.32852495e-02 1.28373504e-05 -2.16826200e-01] 第一個特征的shap interaction values和: -0.38626036 '''
我們也可以用dependence_plot
描繪兩個變量交互下變量對目標值的影響,這樣看更加明白
for i in ['LSTAT','RM','DIS','PTRATIO']: shap.dependence_plot('LSTAT', shap_values, boston_df[cols], interaction_index=i, show=False)
從上面的圖片我們可以看出,'RM','DIS','PTRATIO',這個顏色代表着值得大小,越紅代表越大,越藍代表越小,比如說LSTAT和RM,可以看出,在LSTAT越小的時候,RM的顏色基本是紅色的,說明此時RM的值是比較大的,而當LSTAT越大是,RM的顏色基本是藍色,說明RM的值是越來越小,因此可以推斷出LSTAT和RM的交互系數應該是一份負值。
for i in ['RM','LSTAT']: shap.dependence_plot('RM', shap_values, boston_df[cols], interaction_index=i, show=False)
我們看具體的LSTAT和RM的交互系數分布