import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
df = pd.read_excel('california.xlsx')

from sklearn.model_selection import train_test_split

X = df.drop(['price'],axis=1)
y = df['price']

X_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 然后將訓練集進一步劃分為訓練集和驗證集
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.125, random_state=42) # 0.125 x 0.8 = 0.1

import lightgbm as lgb

# LightGBM模型參數
params_lgb = {
'learning_rate': 0.02, # 學習率,控制每一步的步長,用于防止過擬合。典型值范圍:0.01 - 0.1
'boosting_type': 'gbdt', # 提升方法,這里使用梯度提升樹(Gradient Boosting Decision Tree,簡稱GBDT)
'objective': 'mse', # 損失函數
'metric': 'rmse', # 評估指標
'num_leaves': 127, # 每棵樹的葉子節點數量,控制模型復雜度。較大值可以提高模型復雜度但可能導致過擬合
'verbose': -1, # 控制 LightGBM 輸出信息的詳細程度,-1表示無輸出,0表示最少輸出,正數表示輸出更多信息
'seed': 42, # 隨機種子,用于重現模型的結果
'n_jobs': -1, # 并行運算的線程數量,-1表示使用所有可用的CPU核心
'feature_fraction': 0.8, # 每棵樹隨機選擇的特征比例,用于增加模型的泛化能力
'bagging_fraction': 0.9, # 每次迭代時隨機選擇的樣本比例,用于增加模型的泛化能力
'bagging_freq': 4 # 每隔多少次迭代進行一次bagging操作,用于增加模型的泛化能力
}

model_lgb = lgb.LGBMRegressor(**params_lgb)
model_lgb.fit(X_train, y_train, eval_set=[(X_val, y_val)],

使用LightGBM模型對房價進行預測,并通過劃分訓練集、驗證集和測試集的方式來訓練模型、驗證其性能,以確保模型的泛化能力,接下來也將對這個模型進行力圖的繪制,數據集是一個包含了房價預測相關的特征信息數據,如中位收入、房齡、平均房間數、平均臥室數、人口數、平均家庭人口數、緯度和經度,以及對應的房價。

基礎力圖繪制

import shap
# 構建 shap解釋器
explainer = shap.TreeExplainer(model_lgb)
# 計算測試集的shap值
shap_values = explainer.shap_values(X_test)

# 繪制單個樣本的SHAP解釋(Force Plot)
sample_index = 7 # 選擇一個樣本索引進行解釋
shap.force_plot(explainer.expected_value, shap_values[sample_index], X_test.iloc[sample_index], matplotlib=True)
plt.savefig("SHAP力圖_1.pdf", format='pdf', bbox_inches='tight')

這是一個基礎的SHAP力圖,目前在許多文章中采用了類似的展示形式,然而,這種樣式仍有許多值得我們深入探討的地方,例如,當特征數量過多時,如果我們只想展示部分關鍵特征,該如何實現?又如,特征帶有度量單位時,如何在力圖上清晰地展示?這些都是可以改進的方向,作者在一篇文獻中發現了類似的SHAP力圖如下:

接下來我們通過一步步解讀參數,根據原理去繪制類似的力圖。

explainer.expected_value

explainer.expected_value 是SHAP解釋器中的一個關鍵參數,它代表了模型在沒有任何特征輸入時的預測值,即模型輸出的基準值或平均值,在回歸問題中,這通常是模型在整個訓練數據集上的平均預測值,這個值作為SHAP力圖中的基礎線,所有特征的SHAP值都相對于這個基準值進行調整,從而顯示出每個特征對最終預測的貢獻。

shap_values[sample_index]

shap_values[sample_index] 表示SHAP解釋器對第 sample_index 個樣本計算出的各個特征的SHAP值。

X_test.iloc[sample_index]

X_test.iloc[sample_index] 返回的是測試集 X_test 中第 sample_index 個樣本的特征值也就是前文shap對應的樣本,可以發現它本身是不帶度量單位的,接下來根據這個原理去繪制帶度量單位的shap力圖。

帶度量單位的shap力圖

base_value = explainer.expected_value  # 基礎值,一般是模型在訓練集上的平均輸出
shap_values = shap_values[sample_index] # 每個特征的SHAP值
features = np.array([
'MedInc=-0.096140 ($1000)', # 中位收入,單位為千美元
'HouseAge=0.749907 (years)', # 房齡,單位為年
'AveRooms=-0.195315 (rooms/house)', # 每個家庭的平均房間數
'AveBedrms=-0.111900 (bedrooms/house)', # 每個家庭的平均臥室數
'Population=-0.367422 (people)', # 人口數
'AveOccup=0.053259 (people/house)', # 每個家庭的平均人口數
'Latitude=-0.807184 (degrees)', # 緯度,單位為度
'Longitude=0.749004 (degrees)'])# 經度,單位為度 特征名稱和數值
# 創建力圖
shap.force_plot(base_value, shap_values, features,
matplotlib=True, # 使用Matplotlib來顯示圖像
show=False)

plt.savefig("SHAP力圖_2.pdf", format='pdf', bbox_inches='tight')

根據特定樣本的特征值和SHAP值生成并保存一個解釋模型預測的力圖,可以發現該力圖對于每個特征都存在一個其對應的單位,但是存在折疊,或許在實際應用中我們只希望展示部分特征就可以了,而不是每個特征都進行展示,只需要添加參數調整閾值就好了。

特征展示控制

# 創建力圖
shap.force_plot(base_value, shap_values, features,
matplotlib=True, # 使用Matplotlib來顯示圖像
show=False,
contribution_threshold=0.1) # 調整閾值以控制顯示哪些特征

plt.savefig("SHAP力圖_3.pdf", format='pdf', bbox_inches='tight')

通過添加 contribution_threshold=0.1 參數,我們可以調整SHAP力圖中的顯示閾值,對比兩幅力圖后可以發現,特征 HouseAge 已不在當前的力圖中顯示,因為它對預測結果的貢獻較小。這種調整并未改變圖表的整體信息表達,同時也使圖表更加簡潔,讀者可以根據具體需求自行調整閾值,以更好地控制特征的展示效果。

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

上一篇:

不止 SHAP 力圖:LIME 實現任意黑盒模型的單樣本解釋

下一篇:

用圖表說話:如何有效呈現回歸預測模型結果

我們有何不同?

API服務商零注冊

多API并行試用

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

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

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

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

#AI深度推理大模型API

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

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