import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['axes.unicode_minus'] = False
import warnings
warnings.filterwarnings("ignore")
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()

導入數據并預處理:讀取數據集,提取特征和目標變量、劃分訓練集和測試集:將數據集拆分為訓練集和測試集,便于后續模型的訓練和評估、該數據集中使用了14個特征變量,這些特征包括了患者的基本信息和多項醫療檢測指標,以下是每個特征的詳細說明:

age(年齡):患者的年齡,單位為年

sex(性別):患者性別,1代表男性,0代表女性

cp(胸痛類型):患者的胸痛類型,有4種可能取值:1:典型心絞痛、2:非典型心絞痛、3:非心絞痛、4:無癥狀

trestbps(靜息血壓):患者入院時的靜息血壓,單位為mm Hg

chol(膽固醇):血清膽固醇值,單位為mg/dl

fbs(空腹血糖):空腹血糖值是否大于120 mg/dl,1為真,0為假

restecg(靜息心電圖結果):靜息心電圖的結果,有3個可能取值:0:正常、1:存在ST-T波異常(T波反轉或ST段升高或降低超過0.05 mV)、2:顯示可能或確定的左心室肥大

thalach(最大心率):運動測試中達到的最大心率

exang(運動誘發型心絞痛):是否在運動時誘發心絞痛,1為是,0為否

oldpeak(運動引發的ST段抑制值):與靜息時相比的運動引發的ST段抑制

slope(ST段峰值的斜率):運動高峰時ST段的斜率,有3種取值:1:上升、2:平坦、3:下降

ca(主要血管的數量):使用熒光鏡檢查染色的主要血管數量,取值為0到3

thal(地中海貧血癥):血液中的地中海貧血類型,有3個可能取值:0:正常、1:固定缺陷、2:可逆缺陷

num(診斷結果):目標變量,表示是否診斷出心臟病,0為小于50%直徑縮?。o心臟?。?為大于50%直徑縮?。ㄓ行呐K?。?/p>

模型構建

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折交叉驗證(GridSearchCV)來優化模型參數,首先,定義了XGBoost模型的初始參數(如學習率、提升方法、樹的葉子節點數等)和參數搜索網格(如樹的數量、深度、學習率等),然后,通過網格搜索在不同的參數組合下訓練模型,目標是最小化負對數損失(logloss)作為評估指標,最后,輸出最優的參數組合,并使用該參數重新訓練模型

SHAP值計算

import shap
explainer = shap.TreeExplainer(best_model)
# 計算shap值為numpy.array數組
shap_values_numpy = explainer.shap_values(X)
# 計算shap值為Explanation格式
shap_values_Explanation = explainer(X)

使用shap.TreeExplainer創建一個解釋器對象explainer,它用于解釋基于樹模型(如XGBoost、決策樹等)的預測,分別計算SHAP值并返回為numpy數組格式和更復雜的Explanation格式,后者適合更高級的分析和圖形展示

XGBoost模型特征重要性可視化

# 獲取XGBoost模型的特征貢獻度(重要性)
feature_importances = best_model.feature_importances_
# 將特征和其重要性一起排序
sorted_indices = np.argsort(feature_importances)[::-1] # 逆序排列,重要性從高到低
sorted_features = X_train.columns[sorted_indices]
sorted_importances = feature_importances[sorted_indices]
# 繪制按重要性排序的特征貢獻性柱狀圖
plt.figure(figsize=(10, 6), dpi=1200)
plt.barh(sorted_features, sorted_importances, color='steelblue')
plt.xlabel('Importance', fontsize=14)
plt.ylabel('Features', fontsize=14)
plt.title('Sorted Feature Importance', fontsize=16)
plt.gca().invert_yaxis()
plt.savefig("Sorted Feature Importance.pdf", format='pdf',bbox_inches='tight')
# 顯示圖表
plt.show()

通過提取XGBoost模型的內置特征重要性,生成并展示了基于模型自身計算的特征排名,幫助理解每個特征對模型預測結果的貢獻;與后續基于SHAP值的特征排名不同,它僅反映模型的分裂結構

基于SHAP numpy數組格式的特征重要性總結圖

# 繪制SHAP值總結圖(Summary Plot)
plt.figure(figsize=(10, 5), dpi=1200)
shap.summary_plot(shap_values_numpy, X, plot_type="bar", show=False)
plt.title('SHAP_numpy Sorted Feature Importance')
plt.savefig("SHAP_numpy Sorted Feature Importance.pdf", format='pdf',bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_numpy(即基于SHAP值的numpy數組格式)繪制特征重要性總結圖,展示各個特征在整體模型預測中的貢獻,細心的讀者可發現兩種方法的排名不一致,這是因為XGBoost基于樹分裂計算特征重要性,而SHAP值根據每個特征對個體預測的影響進行評估,因此兩者方法不同導致排名差異

基于SHAP Explanation格式的特征重要性條形圖

plt.figure(figsize=(10, 5), dpi=1200)
shap.plots.bar(shap_values_Explanation, show=False)
plt.title('SHAP_Explanation Sorted Feature Importance')
plt.savefig("SHAP_Explanation Sorted Feature Importance.pdf", format='pdf',bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_Explanation(即SHAP值的Explanation格式)繪制特征重要性條形圖,展示每個特征對模型預測的貢獻大小,并保存為PDF文件,該格式提供了更豐富的特征信息,便于更深入的分析和可視化,

這張圖展示了部分特征的SHAP值重要性,但并未完全展示所有特征,較不重要的特征已被合并為“Sum of 4 other features”,如果希望展示更多特征,可以通過設置max_display參數,調整展示的特征數量,以便觀察更完整的特征貢獻排名

# 設置 max_display 值
max_display = 13
plt.figure(figsize=(10, 5), dpi=1200)

# 創建 SHAP 值條形圖,并使用 max_display 參數限制最多顯示的特征數量
shap.plots.bar(shap_values_Explanation, max_display=max_display, show=False)
plt.title(f'SHAP Explanation Sorted Feature Importance (Top {max_display})')
plt.savefig(f"SHAP_Explanation_Sorted_Feature_Importance_Top_{max_display}.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

基于SHAP Explanation的單個樣本特征重要性與實際數據可視化

plt.figure(figsize=(10, 5), dpi=1200)
# 創建 SHAP 值條形圖,展示數據
shap.plots.bar(shap_values_Explanation[1], show_data=True, show=False, max_display=13)
plt.title('SHAP Explanation for Instance (Feature Importance with Data)')
plt.savefig("SHAP_Explanation_Instance_Feature_Importance_with_Data.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_Explanation[1](即第一個樣本的SHAP值)繪制特征重要性條形圖,并通過show_data=True顯示每個特征的具體數值,最后將圖表保存為PDF文件,該圖展示了某個具體樣本的各個特征如何正負向影響模型的預測結果,紅色表示正貢獻,藍色表示負貢獻

基于SHAP Explanation的單個樣本瀑布圖可視化

plt.figure(figsize=(10, 5), dpi=1200)
# 繪制第1個樣本的 SHAP 瀑布圖,并設置 show=False 以避免直接顯示
shap.plots.waterfall(shap_values_Explanation[1], show=False, max_display=13)
# 保存圖像為 PDF 文件
plt.savefig("SHAP_Waterfall_Plot_Sample_1.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_Explanation[1](即第一個樣本的SHAP值)繪制了該樣本的瀑布圖,通過waterfall函數展示各個特征如何逐步影響最終的模型預測值,設置max_display=13限制最多展示13個特征,該瀑布圖其中紅色表示正向貢獻,藍色表示負向影響,最終累積影響得出模型的預測值為1.543,其中E[f(X)] = -0.172是模型的基準值,表示在沒有特征信息的情況下,模型的平均預測輸出,它代表了模型對所有樣本的整體預測傾向

基于SHAP Explanation值的按性別分組特征重要性可視化

#  'sex' 列包含性別信息,0 代表女性,1 代表男性
sex = ["Women" if df.loc[i, "sex"] == 0 else "Men" for i in range(df.shape[0])]

plt.figure(figsize=(10, 5), dpi=1200)
# 使用 SHAP 的 cohorts 方法根據 sex 進行分組并繪制條形圖,限制顯示最多13個特征
shap.plots.bar(shap_values_Explanation.cohorts(sex).abs.mean(0), max_display=13, show=False)
plt.title('SHAP Explanation Sorted Feature Importance by Sex')
plt.savefig("SHAP_Explanation_Sorted_Feature_Importance_by_Sex.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_Explanation按sex(性別,原始特征為分類數據)特征分組,通過SHAP的cohorts方法計算并繪制各個特征對不同性別群體模型預測的平均貢獻度,并限制顯示13個最重要的特征,結果展示了按性別(男性和女性)分組后,特征對模型預測的重要性。無論是男性還是女性,cp(胸痛類型)、thal(地中海貧血類型)和ca(主要血管數量)都是最重要的特征,且貢獻度相近。然而,某些特征的貢獻在性別之間存在差異,如oldpeak在男性中的貢獻更顯著,這表明模型在預測不同性別群體時,部分特征的影響力存在明顯差異,右下角數值分別代表各類別的樣本量

基于SHAP Explanation值的按自動分組年齡的特征重要性可視化

# 例如,將年齡劃分為三類:青年(<30歲),中年(30-60歲),老年(>60歲)
age_groups = ["Young" if df.loc[i, "age"] < 30 else "Middle-aged" if df.loc[i, "age"] <= 60 else "Senior" for i in range(df.shape[0])]
# 使用 SHAP 的 cohorts 方法根據 age_groups 進行分組
v = shap_values_Explanation.cohorts(age_groups).abs.mean(0)
plt.figure(figsize=(10, 5), dpi=1200)
# 繪制 SHAP 條形圖
shap.plots.bar(v, show=False, max_display=13)
plt.title('SHAP Explanation Sorted Feature Importance by Automatically Grouped Age')
plt.savefig("SHAP_Explanation_Sorted_Feature_Importance_by_Grouped_Age.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

使用了shap_values_Explanation,根據年齡(age)這一連續性特征將樣本分為青年、中年和老年三類,利用SHAP的cohorts方法計算各組的平均特征貢獻度,并繪制條形圖。該圖展示了在不同年齡段中,特征對模型預測的平均重要性,顯示了特征影響隨年齡變化的差異,結果顯示特征thal和cp在各年齡段的貢獻度較為接近,但chol(膽固醇)在老年群體中的影響顯著高于中年和青年,說明不同年齡段特征對模型預測的影響存在差異

基于SHAP Explanation值的特征聚類可視化(cutoff=0.5)

# 計算 clustering 結果和 shap_values_Explanation
clustering = shap.utils.hclust(X, y)
plt.figure(figsize=(10, 5), dpi=1200)
shap.plots.bar(shap_values_Explanation,
clustering=clustering,
clustering_cutoff=0.5,
show=False, max_display=13)
plt.title('SHAP Explanation with Clustering (cutoff=0.5)')
plt.savefig("SHAP_Explanation_with_Clustering.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_Explanation,結合層次聚類方法(hclust),根據特征對模型的貢獻進行聚類,設置了聚類截斷值(clustering_cutoff=0.5),以展示相關特征的分組情況,通過條形圖展示了前13個特征的平均SHAP值貢獻,顯示了模型中每個特征的相對重要性,聚類可視化的作用是通過層次聚類分析特征之間的相關性,幫助識別哪些特征在對模型預測產生類似影響,通過設置聚類截斷值(cutoff=0.5),我們可以將影響力相似的特征分組,從而更好地理解模型的決策結構以及特征之間的相互作用,從當前的可視化結果來看,沒有明確顯示出類似的特征被分組在一起

該圖為生成模擬數據基于SHAP值的特征重要性,結合層次聚類(clustering_cutoff=0.5)對相似特征進行了分組,圖中的feature_0和feature_3被劃分在同一組(有一個灰色的聚類括號),這表明這兩個特征在模型中對預測的影響非常相似,屬于相關性較強的特征,相比之前未能顯示明顯相關特征的圖,這個圖表通過聚類更好地揭示了特征之間的相似性,有助于更清晰地理解哪些特征在模型決策中發揮了類似的作用,當特征被聚類在同一組時,說明它們對模型的預測有類似的貢獻,可能在數據上表現出較高的相關性或冗余性,這意味著這些特征可能描述了相似的現象,或者它們在模型中提供的獨立信息量較低。在模型優化中,發現這些相關特征可以幫助我們減少冗余特征,簡化模型,或者進一步探索這些特征之間的關系

基于SHAP Explanation值的特征散點圖可視化

# 指定特征的名稱為 'cp'(胸痛類型)
feature_name = 'cp'
# 找到指定特征的索引
feature_index = shap_values_Explanation.feature_names.index(feature_name)
plt.figure(figsize=(10, 5), dpi=1200)
# 使用 SHAP 的 scatter 方法繪制指定特征的散點圖
shap.plots.scatter(shap_values_Explanation[:, feature_index], show=False)
plt.title(f'SHAP Scatter Plot for Feature: {feature_name}')
plt.savefig(f"SHAP_Scatter_Plot_{feature_name}.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_Explanation,繪制特定特征cp(胸痛類型)的SHAP散點圖,通過shap.plots.scatter展示該特征對模型預測的貢獻值,并保存為PDF文件,圖表橫軸顯示特征cp的不同取值,縱軸顯示該特征的SHAP值(即它對模型輸出的影響),可以發現cp的取值越高,其對應的SHAP值越大,表明較高的cp值(如4)對模型的正向預測影響更大,而較低的cp值(如1)則對模型的負向預測影響更顯著

基于SHAP Explanation值的力圖匯總可視化

# 初始化 JS 庫
shap.initjs()
# 使用 force_plot 方法可視化所有樣本的解釋
shap.force_plot(explainer.expected_value, shap_values_Explanation.values, X)

首先初始化SHAP的JavaScript庫 (shap.initjs()),然后使用force_plot方法可視化所有樣本的解釋結果,通過傳入模型的期望輸出值(explainer.expected_value)以及所有樣本的shap_values_Explanation.values,展示整體模型的預測解釋力圖(force plot),并為每個樣本的特征貢獻進行匯總展示,生成的力圖是交互式的,用戶可以通過點擊和懸停在圖中的元素上,查看每個特征對模型預測的正向或負向影響,從而更直觀地理解模型的決策過程

基于SHAP Explanation值的單個樣本決策圖可視化

# 獲取模型的期望輸出值(平均預測值)
expected_value = explainer.expected_value
# 選擇第1個樣本的 SHAP 值
shap_values = shap_values_numpy[1]
# 決策圖的特征名展示
features_display = X
# 繪制 SHAP 決策圖
plt.figure(figsize=(10, 5), dpi=1200)
shap.decision_plot(expected_value, shap_values, features_display, show=False)
# 保存圖像為 PDF
plt.savefig("shap_decision_plot_samples.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_numpy[1](即第1個樣本的SHAP值),并結合模型的期望輸出值(explainer.expected_value)生成了一個SHAP決策圖,通過shap.decision_plot展示各個特征對模型預測結果的貢獻,并保存為PDF文件,決策圖顯示了各個特征如何一步步影響模型的最終輸出值,紅線向右偏表示正向影響,向左偏表示負向影響,幫助理解模型如何基于各特征做出決策

基于SHAP Explanation值的錯分類樣本決策圖可視化

# 獲取模型的期望輸出值(平均預測值)
expected_value = explainer.expected_value
# 計算預測值:根據 SHAP 值求和加上期望值,再將結果通過閾值判斷為分類輸出
y_pred = (shap_values_numpy.sum(1) + expected_value) > 0
# 計算錯分類的樣本
misclassified = y_pred != y
# 決策圖展示的特征
features_display = X
# 繪制 SHAP 決策圖,帶有錯分類的樣本高亮
plt.figure(figsize=(10, 5), dpi=1200) # 設置畫布大小和分辨率
shap.decision_plot(expected_value, shap_values_numpy, features_display,
link='logit', highlight=misclassified, show=False)
# 保存圖像為 PDF 文件
plt.savefig("shap_decision_plot_with_misclassified.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_numpy計算模型預測的SHAP值,并結合模型的期望輸出值(expected_value)生成了一個決策圖。通過shap.decision_plot展示所有樣本的特征貢獻,并高亮顯示了錯分類的樣本,最終生成的圖表展示了分類模型中哪些樣本被錯誤預測,并保存為PDF文件,決策圖展示了各特征對模型預測的影響軌跡,藍色和紅色線條分別代表對模型輸出的負向和正向貢獻,錯分類的樣本被高亮顯示,幫助分析哪些特征對這些錯誤預測影響最大,從而可以進一步優化模型或調整特征

基于SHAP Explanation值的僅錯分類樣本決策圖可視化

# 獲取錯分類樣本的索引
misclassified_indices = misclassified[misclassified].index

# 過濾出錯分類樣本的 SHAP 值
shap_values_misclassified = shap_values_numpy[misclassified_indices]

# 保留原始特征名稱,過濾出錯分類樣本的特征
features_display_misclassified = X.loc[misclassified_indices]

# 繪制 SHAP 決策圖,顯示錯分類的樣本
plt.figure(figsize=(10, 5), dpi=1200) # 設置畫布大小和分辨率
shap.decision_plot(expected_value,
shap_values_misclassified,
link='logit',
highlight=True, # 高亮顯示這些樣本
feature_names=list(X.columns), # 轉換為列表以避免 TypeError
show=False)
plt.savefig("shap_decision_plot_misclassified_only.pdf", format='pdf', bbox_inches='tight')
plt.tight_layout()
plt.show()

使用shap_values_numpy,通過提取錯分類樣本的SHAP值,并結合模型的期望輸出值(expected_value),生成了錯分類樣本的決策圖。通過shap.decision_plot高亮顯示僅錯分類的樣本,并展示了每個特征對這些樣本模型預測的影響,幫助我們深入分析哪些特征導致了錯誤預測,并為后續模型調整或特征工程提供參考

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

上一篇:

?使用Keras函數式API進行深度學習

下一篇:

如何用SHAP解讀集成學習Stacking中的基學習器和元學習器以及整體模型貢獻
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

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

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

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

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

#AI深度推理大模型API

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

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