這是二分類XGBoost部署的APP輸出的shap力圖,可以發現f(X)=-2.50很明顯這不是模型屬于某個類別的概率,具體解讀會在接下來根據代碼進行解釋。

二分類隨機森林RF

這里是一篇醫學柳葉刀頂刊部署的APP,使用的模型為隨機森林RF,它這里的解釋為f(X)為該預測類別的概率,和XGBoost的力圖輸出f(X)存在很大差異,于是我們利用數據分別實現這兩個模型來探討一下到底是因為為什么,出現了這種情況。

代碼實現

數據讀取處理

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
df = pd.read_csv('Dataset.csv')
# 劃分特征和目標變量
X = df.drop(['target'], axis=1)
y = df['target']
# 劃分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
random_state=42, stratify=df['target'])
df.head()

讀取數據,劃分出特征和目標變量,然后將數據集按照80%訓練集和20%測試集的比例進行分割,同時確保目標變量的類別分布在訓練集和測試集中保持一致。

XGBoost模型構建

import xgboost as xgb
from sklearn.model_selection import GridSearchCV

# XGBoost模型參數
params_xgb = {
'learning_rate': 0.02, # 學習率,控制每一步的步長,用于防止過擬合。典型值范圍:0.01 - 0.1
'booster': 'gbtree', # 提升方法,這里使用梯度提升樹(Gradient Boosting Tree)
'objective': 'binary:logistic', # 損失函數,這里使用邏輯回歸,用于二分類任務
'max_leaves': 127, # 每棵樹的葉子節點數量,控制模型復雜度。較大值可以提高模型復雜度但可能導致過擬合
'verbosity': 1, # 控制 XGBoost 輸出信息的詳細程度,0表示無輸出,1表示輸出進度信息
'seed': 42, # 隨機種子,用于重現模型的結果
'nthread': -1, # 并行運算的線程數量,-1表示使用所有可用的CPU核心
'colsample_bytree': 0.6, # 每棵樹隨機選擇的特征比例,用于增加模型的泛化能力
'subsample': 0.7, # 每次迭代時隨機選擇的樣本比例,用于增加模型的泛化能力
'eval_metric': 'logloss' # 評價指標,這里使用對數損失(logloss)
}

# 初始化XGBoost分類模型
model_xgb = xgb.XGBClassifier(**params_xgb)

# 定義參數網格,用于網格搜索
param_grid = {
'n_estimators': [100, 200, 300, 400, 500], # 樹的數量
'max_depth': [3, 4, 5, 6, 7], # 樹的深度
'learning_rate': [0.01, 0.02, 0.05, 0.1], # 學習率
}

# 使用GridSearchCV進行網格搜索和k折交叉驗證
grid_search = GridSearchCV(
estimator=model_xgb,
param_grid=param_grid,
scoring='neg_log_loss', # 評價指標為負對數損失
cv=5, # 5折交叉驗證
n_jobs=-1, # 并行計算
verbose=1 # 輸出詳細進度信息
)

# 訓練模型
grid_search.fit(X_train, y_train)

# 輸出最優參數
print("Best parameters found: ", grid_search.best_params_)
print("Best Log Loss score: ", -grid_search.best_score_)

# 使用最優參數訓練模型
best_model = grid_search.best_estimator_

使用XGBoost分類器通過網格搜索和5折交叉驗證來尋找最佳模型參數,并在訓練集上進行訓練,同時輸出最佳參數和對應的最優對數損失分數。

隨機森林RF模型構建

from sklearn.ensemble import RandomForestClassifier
# 使用隨機森林建模
model_rf = RandomForestClassifier(n_estimators=100, criterion='gini', bootstrap=True, max_depth=3, random_state=8)
model_rf.fit(X_train, y_train

使用隨機森林分類器(指定了100棵樹、基尼系數作為分裂標準、引導抽樣、最大深度為3,以及隨機種子8)在訓練集上進行模型訓練。。

shap力圖

XGBoost力圖

import shap
explainer = shap.TreeExplainer(best_model)
shap_values = explainer.shap_values(X_test)
print("基準值:",explainer.expected_value)
print("shap值維度:",shap_values.shape)

基準值:-0.17231837是模型在沒有任何特征輸入時的預測輸出。

SHAP值維度:(60, 13)表明測試集中有60個樣本,每個樣本有13個特征。

sample_index = 0
shap.force_plot(explainer.expected_value, shap_values[sample_index], X_test.iloc[sample_index], matplotlib=True)

XGBoost的力圖會顯示基準值(通常是模型的平均輸出)以及繪制樣本各特征的具體數值,還有就是f(X)它是XGBoost模型根據這些特征實際的預測值,但是它并不是模型預測類別的概率值,而是輸出一個經過Sigmoid函數處理前的對數幾率(log-odds)值。

Log-Odds: 在二分類問題中,XGBoost的輸出 f(X) 實際上是一個對數幾率(log-odds)值,它表示類別為1的對數幾率:

這個值可以是任何實數,可能會大于1,也可能小于0。

隨機森林RF力圖

explainer = shap.TreeExplainer(model_rf)
shap_values = explainer.shap_values(X_test)
print("基準值:",explainer.expected_value)
print("shap值維度:",shap_values.shape)

基準值 [0.54063291, 0.45936709] 對應于每個類別的基準概率。如果不知道任何特征信息,模型會預測類別 0 的概率為 0.5406,類別 1 的概率為 0.4594。

SHAP 值數組的維度是 (60, 13, 2),60: 測試集 X_test 中的樣本數量,13: 數據集中的特征數量,2: 模型中的類別數量(這里是二分類問題)。

可以發現這里隨機森林RF模型和XGBoost模型的shap結果輸出已經出現不一樣了,雖然使用的是同一個shap解釋器TreeExplainer。

sample_index = 0
shap.force_plot(explainer.expected_value[0], shap_values[sample_index,:,0], X_test.iloc[sample_index], matplotlib=True)

可視化 X_test 中第 0 個樣本的第 0 類別的 SHAP 值,展示各個特征對該樣本在第 0 類別上的預測的貢獻情況,explainer.expected_value[0]:類別 0 的基準值(即模型在不知道任何特征時對類別 0 的平均預測值),shap_values[sample_index,:,0]:第 0 個樣本的所有特征對類別 0 預測的 SHAP 值,X_test.iloc[sample_index]:第 0 個樣本的特征值,這里的f(X)=0.91實際上就是隨機森林RF模型預測這一個樣本為0這一類的概率。

sample_index = 0
shap.force_plot(explainer.expected_value[1], shap_values[sample_index,:,1], X_test.iloc[sample_index], matplotlib=True)

可視化 X_test 中第 0 個樣本的第 1 類別的 SHAP 值,展示各個特征對該樣本在第 1 類別上的預測貢獻情況,explainer.expected_value[1]:類別 1 的基準值(即模型在不知道任何特征時對類別 1 的平均預測值),shap_values[sample_index,:,1]:第 0 個樣本的所有特征對類別 1 預測的 SHAP 值,X_test.iloc[sample_index]:第 0 個樣本的特征值,這里的f(X)=0.09實際上就是隨機森林RF模型預測這一個樣本為1這一類的概率。

最后可以發現對這兩個在隨機森林RF下繪制的力圖f(X)相加為1(如果你進一步研究還會發現同樣本不同類別下的shap值和為0),根據這個概率也可以確定這個樣本在模型中是91%的概率預測為0類,也就符合頂刊中的解釋,但是如果采用XGBoost模型就不會存在這種解釋。

總結

差異原因:

模型輸出的本質:

SHAP 值的計算方式:

這個現象說明不同類型的模型在 SHAP 值解釋方面的不同之處,對于像隨機森林這樣直接輸出概率的模型,SHAP 值可以直接反映最終的預測概率,因此 f(X) 相加為 1 并且可以直觀地解釋為概率。

而對于 XGBoost 這種輸出 log-odds 值的模型,SHAP 值解釋的是對 log-odds 的貢獻,而不是直接的概率,因此在這種情況下,你無法通過簡單相加 SHAP 值來得到概率,這種解釋方式與頂刊中的標準解釋方法有所不同。

這提醒我們,在使用 SHAP 解釋模型時,需要根據具體模型的性質正確理解和解釋 SHAP 值的含義。

文章轉自微信公眾號@Python機器學習AI

上一篇:

Python實現數據預處理:常見異常值檢驗方法解析

下一篇:

多分類模型的 SHAP 特征貢獻圖及其衍生可視化繪制

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費