背景
在机器学习模型的解释性分析中,SHAP被广泛应用于衡量特征对预测结果的贡献,R包ExplaineR提供了一种创新的方案——基于聚类的SHAP值可视化,它通过分簇揭示样本的特征贡献模式,并以简洁直观的方式呈现各簇的特征差异,SHAP聚类通过对样本的SHAP值进行分组,揭示不同群体在特征贡献上的模式差异,从而帮助更直观地理解模型的决策逻辑和群体行为特征,这里参考ExplaineR的聚类可视化逻辑,在Python环境下复现这一功可视化,最后切记是利用K-means来辅助进行shap解释,而不是对K-means做shap解释, 无监督模型本身无法直接进行SHAP解释 ,因为SHAP是专为解释模型的预测输出而设计的,而无监督模型通常不生成明确的目标预测(例如分类或回归的具体数值)
代码实现
模型构建
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['axes.unicode_minus'] = False
from sklearn.model_selection import train_test_split
df = pd.read_excel('2025-1-7公众号Python机器学习AI.xlsx')
# 划分特征和目标变量
X = df.drop(['y'], axis=1)
y = df['y']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3,
random_state=42, stratify=df['y'])
from xgboost import XGBClassifier
from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.metrics import accuracy_score
# 定义 XGBoost 二分类模型
model_xgb = XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=8)
# 定义参数网格
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [3, 5, 7],
'learning_rate': [0.01, 0.1, 0.2],
'subsample': [0.8, 1.0],
'colsample_bytree': [0.8, 1.0]
}
# 定义 K 折交叉验证 (Stratified K-Fold)
kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=8)
# 使用网格搜索寻找最佳参数
grid_search = GridSearchCV(estimator=model_xgb, param_grid=param_grid, scoring='accuracy',
cv=kfold, verbose=1, n_jobs=-1)
# 拟合模型
grid_search.fit(X_train, y_train)
# 使用最优参数训练模型
xgboost = grid_search.best_estimator_
通过对XGBoost模型进行超参数调优,训练出性能最佳的模型,为后续基于SHAP值的模型解释打下基础,确保生成的SHAP值能够更准确地反映模型对特征的贡献和决策逻辑
shap值计算
import shap
explainer = shap.TreeExplainer(xgboost)
shap_values = explainer.shap_values(X_test)
shap_values_df = pd.DataFrame(shap_values, columns=X_test.columns)
shap_values_df.head()
使用SHAP的TreeExplainer基于训练好的XGBoost模型计算测试集X_test每个特征的SHAP值,并将这些值存储为一个数据框shap_values_df,以便分析特征对模型预测的贡献
基于SHAP值的样本聚类
from sklearn.cluster import KMeans
# 使用KMeans对shap_values_df进行聚类
kmeans = KMeans(n_clusters=3, random_state=42)
clusters = kmeans.fit_predict(shap_values_df)
# 在shap_values_df中添加新的列,表示每个样本的聚类结果
shap_values_df['Cluster'] = clusters
shap_values_df
使用KMeans聚类算法(设置聚类数为3和随机种子为42),对shap_values_df中的SHAP值进行聚类分析,并将每个样本的聚类结果添加为新列Cluster,以便发现具有相似特征贡献模式的样本群体
添加真实标签和预测结果,标记预测准确性
# 添加真实标签和预测标签到 shap_values_df
shap_values_df['True_Label'] = y_test.values # 真实标签
shap_values_df['Predicted_Label'] = xgboost.predict(X_test) # XGBoost模型的预测标签
# 在 shap_values_df 中添加新的列,表示模型是否预测正确
shap_values_df['Prediction_Correct'] = (shap_values_df['True_Label'] == shap_values_df['Predicted_Label']).astype(int)
shap_values_df
将测试集y_test的真实标签和XGBoost模型对X_test的预测结果分别添加到shap_values_df中,并通过对比两者生成新的列Prediction_Correct,标记每个样本的预测是否正确(1为正确,0为错误)
基于SHAP值的特征重要性可视化带动态抖动
from matplotlib.colors import LinearSegmentedColormap
from matplotlib import gridspec
from scipy.spatial import KDTree
# 修改代码,将 shap_values_df 限制为前 13 列(SHAP 值部分)
shap_values_df_filtered = shap_values_df.iloc[:, 0:13]
# 重新计算 mean(|SHAP value|) 并排序特征
mean_abs_shap_values = shap_values_df_filtered.abs().mean().sort_values(ascending=False)
sorted_features = mean_abs_shap_values.index
# 创建绘图数据
plot_data = []
for feature in sorted_features:
for shap_val, feature_val in zip(
shap_values_df_filtered[feature], X_test[feature]
):
normalized_value = (feature_val - X_test[feature].min()) / (
X_test[feature].max() - X_test[feature].min()
) # 动态标准化
plot_data.append(
{"Feature": feature, "SHAP Value": shap_val, "Normalized Value": normalized_value}
)
plot_df = pd.DataFrame(plot_data)
# 创建深蓝-淡灰-深红渐变色
custom_cmap = LinearSegmentedColormap.from_list("custom", ["#3465A4", "#E6E6E6", "#CC3333"])
# 创建 KDTree 用于计算邻近点
all_points = plot_df["SHAP Value"].values.reshape(-1, 1) # SHAP Value 作为坐标
tree = KDTree(all_points)
# 定义抖动范围和距离阈值
jitter_scale = 0.15 # 抖动强度
distance_threshold = 0.05 # 距离阈值,决定是否抖动
# 创建图形和网格布局
fig = plt.figure(figsize=(12, 8)) # 调整图形大小
gs = gridspec.GridSpec(2, 1, height_ratios=[0.05, 0.85]) # 上方0.05部分用于颜色条
# 创建主绘图区域
ax = plt.subplot(gs[1])
for i, feature in enumerate(sorted_features):
# 筛选每个特征的数据
subset = plot_df[plot_df["Feature"] == feature]
shap_values = subset["SHAP Value"].values
# 初始化抖动数组
jitter = np.zeros(len(shap_values))
# 遍历每个点,判断是否需要抖动
for idx, shap_value in enumerate(shap_values):
# 查询邻近点数量
neighbors = tree.query_ball_point([shap_value], r=distance_threshold)
if len(neighbors) > 1: # 如果邻近点数量大于1,则参与抖动
jitter[idx] = np.random.normal(loc=0, scale=jitter_scale)
# 绘制散点
ax.scatter(
subset["SHAP Value"],
jitter + i, # y 轴位置加上抖动
c=subset["Normalized Value"], # 使用标准化后的值进行颜色映射
cmap=custom_cmap, # 使用自定义配色
s=15,
alpha=0.7,
)
# 添加 SHAP Value = 0 的灰色虚线
ax.axvline(x=0, color="gray", linestyle="--", linewidth=1.5, alpha=0.5)
# 设置图形标签
ax.set_xlabel("SHAP Value (Impact on Model Output)", fontsize=14) # 调整字体大小
ax.set_ylabel("Features", fontsize=14)
ax.set_yticks(range(len(sorted_features)))
ax.set_yticklabels(sorted_features, fontsize=12)
ax.invert_yaxis() # 反转 y 轴,使最重要的特征在顶部
# 移除网格线
ax.grid(False)
# 创建颜色条区域
cax = plt.subplot(gs[0]) # 独立的颜色条轴
sm = plt.cm.ScalarMappable(cmap=custom_cmap)
sm.set_array([]) # 颜色条无固定刻度值
cbar = fig.colorbar(sm, cax=cax, orientation="horizontal")
cbar.outline.set_visible(False) # 去掉颜色条的黑边
cbar.ax.tick_params(labelsize=10) # 调整刻度字体大小
cbar.ax.xaxis.set_ticks([]) # 隐藏刻度
cbar.ax.set_title("Feature Value", fontsize=14, pad=10) # 显示统一的标题
cbar.ax.text(-0.02, 0.5, "Low", ha="center", va="center", transform=cbar.ax.transAxes, fontsize=12)
cbar.ax.text(1.02, 0.5, "High", ha="center", va="center", transform=cbar.ax.transAxes, fontsize=12)
# 调整布局
plt.subplots_adjust(top=0.92) # 为颜色条和标题腾出空间
plt.savefig("1.png", bbox_inches='tight', dpi=1200)
plt.show()
通过计算测试集前13列的SHAP值均值(mean_abs_shap_values),生成特征排序,利用动态标准化的特征值创建颜色映射,并通过抖动(jitter)和散点图展示特征的SHAP值分布,突出每个特征对模型预测的贡献模式,同时用深蓝到深红的渐变色(custom_cmap)表示特征值大小的影响范围
结合预测结果的SHAP值分布可视化
在前面代码的基础上,根据Prediction_Correct将样本分为预测正确和预测错误两类,分别用圆点和叉号标记,进一步突出特征对正确和错误预测样本的贡献差异
加入样本轨迹连线的SHAP值动态可视化
在前面代码的基础上为每个样本添加了特征之间的轨迹连线,以颜色映射特征值大小,并结合正确与错误预测的标记,进一步展示特征对模型预测的逐步贡献路径及其在样本中的动态变化
到这里为止,这种可视化方法仅限于分析单个样本或全体样本的特征贡献路径,但尚未引入基于聚类的分组信息。聚类的引入可以帮助识别具有相似特征贡献模式的样本簇,为模型的全局解释和群体行为模式的发现提供更高层次的洞察
基于聚类的SHAP值分组可视化
相较于前面的代码引入了聚类信息,通过对每个簇(Cluster)分别绘制,展示具有相似特征贡献模式的样本组的SHAP值分布及轨迹,便于分析不同簇的特征模式差异
展示SHAP聚类中心,揭示每个簇的特征贡献趋势
最后将SHAP值的聚类中心可视化,表示每个簇在不同特征上的SHAP值中心贡献(特征重要性),反映了每个簇对特定特征的影响程度
R库利用k-means辅助SHAP解释,主要是利用k-means聚类后聚类中心代表数据分布的典型模式及分成不同的簇,流程是先计算SHAP 值,然后再对SHAP值进行聚类分析,目的是在高维数据中提取代表性模式,简化解释的复杂度,这里利用python实现是同样的道理,
完整 代码与数据集获取:如需获取本文的源代码和数据集,请添加作者微信联系
往期推荐
期刊配图:SHAP可视化改进依赖图+拟合线+边缘密度+分组对比
期刊配图:多种机器学习算法结合SHAP特征贡献在递归特征选择中的运用
复现SCI文章 SHAP 依赖图可视化以增强机器学习模型的可解释性
复现 Nature 图表——基于PCA的高维数据降维与可视化实践及其扩展
复现Nature图表——基于PCA降维与模型预测概率的分类效果可视化
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~
个人观点,仅供参考