背景
在机器学习中,SHAP是一种用于解释模型输出的重要工具,它为每个特征分配一个贡献值,表示该特征对模型输出的影响程度,然而,对于多分类模型与二分类模型的SHAP分析,存在一些重要区别——多分类模型的 SHAP 分析为每个类别单独计算一组特征贡献值,而二分类模型通常只计算正类或目标类的贡献(并不是绝对如RF模型也会计算二分类各类别的shap值),由于多分类模型需要为每个类别单独计算一组 SHAP 值,而二分类模型通常只关注正类(或目标类)的特征贡献,因此两者的可视化方式有所不同。以下展示将展示多分类模型下的shap特征贡献图绘制
代码实现
数据处理
import pandas as pd
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_excel('2024-10-30-公众号Python机器学习AI.xlsx')
from sklearn.model_selection import train_test_split
# 分割数据集
X = df.drop(['Type'], axis = 1)
y = df['Type']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3,
stratify=df['Type']) #分离训练集和测试集
加载数据,并将数据集中特征与标签分离后,将其按7:3比例分为训练集和测试集,确保测试集按标签类别进行分层抽样
模型构建
from sklearn.ensemble import RandomForestClassifier
# 创建随机森林分类器实例,并设置参数
rf = RandomForestClassifier(
n_estimators=100, # 森林中树的数量。默认是100。
criterion='gini', # 指定用于拆分的质量指标。可选'gini'或'entropy'。
max_depth=None, # 每棵树的最大深度。'None'表示不限制。
min_samples_split=2, # 节点分裂所需的最小样本数。默认是2。
min_samples_leaf=1, # 叶子节点所需的最小样本数。默认是1。
min_weight_fraction_leaf=0.0, # 类似'min_samples_leaf',但基于样本权重。默认0.0。
random_state=42, # 控制随机数生成,以便结果可复现。
max_leaf_nodes=None, # 限制每棵树的最大叶子节点数。'None'表示不限制。
min_impurity_decrease=0.0 # 节点分裂时要求的最小不纯度减少量。默认0.0。
)
# 训练分类器
rf.fit(X_train, y_train)
创建并配置了一个多分类随机森林分类器,并使用训练数据对其进行模型训练
shap值计算整理
import shap
explainer = shap.TreeExplainer(rf)
# 计算shap值为numpy.array数组
shap_values = explainer.shap_values(X_test)
# 提取每个类别的 SHAP 值
shap_values_class_0 = shap_values[:, :, 0]
shap_values_class_1 = shap_values[:, :, 1]
shap_values_class_2 = shap_values[:, :, 2]
shap_values_class_3 = shap_values[:, :, 3]
shap_values_class_4 = shap_values[:, :, 4]
# 计算每个类别的特征贡献度
importance_class_0 = np.abs(shap_values_class_0).mean(axis=0)
importance_class_1 = np.abs(shap_values_class_1).mean(axis=0)
importance_class_2 = np.abs(shap_values_class_2).mean(axis=0)
importance_class_3 = np.abs(shap_values_class_3).mean(axis=0)
importance_class_4 = np.abs(shap_values_class_4).mean(axis=0)
importance_df = pd.DataFrame({
'Class_0': importance_class_0,
'Class_1': importance_class_1,
'Class_2': importance_class_2,
'Class_3': importance_class_3,
'Class_4': importance_class_4
}, index=X_train.columns)
type_mapping = {
0: 'Type_A',
1: 'Type_B',
2: 'Type_C',
3: 'Type_D',
4: 'Type_E'
}
importance_df.columns = [type_mapping[int(col.split('_')[1])] for col in importance_df.columns]
importance_df
使用SHAP计算训练好的多分类随机森林模型对测试数据的特征贡献度,首先,TreeExplainer 用于解释模型,并生成每个类别的 SHAP 值,接着,提取每个类别(Class_0 到 Class_4)的 SHAP值,并计算了各类别下每个特征的平均贡献度(取 SHAP 值绝对值的均值),最终,这些贡献度被存储在一个 DataFrame 中,并将类别索引映射为具体的类别名称(Type_A 到 Type_E),为后续分析提供可解释的特征重要性表
不同类别下特征重要性的堆叠柱状图展示
import seaborn as sns
importance_df['row_sum'] = importance_df.sum(axis=1)
sorted_importance_df = importance_df.sort_values(by='row_sum', ascending=True)
sorted_importance_df = sorted_importance_df.drop(columns=['row_sum'])
elements = sorted_importance_df.index
colors = sns.color_palette("Set2", n_colors=len(sorted_importance_df.columns))
fig, ax = plt.subplots(figsize=(12, 6), dpi=1200)
bottom = np.zeros(len(elements))
for i, column in enumerate(sorted_importance_df.columns):
ax.barh(
sorted_importance_df.index,
sorted_importance_df[column],
left=bottom,
color=colors[i],
label=column
)
bottom += sorted_importance_df[column]
ax.set_xlabel('mean(|SHAP value|) (average impact on model output magnitude)', fontsize=12)
ax.set_ylabel('Features', fontsize=12)
ax.set_title('Feature Importance by Class', fontsize=15)
ax.set_yticks(np.arange(len(elements)))
ax.set_yticklabels(elements, fontsize=10)
for i, el in enumerate(elements):
ax.text(bottom[i], i, ' ' + str(el), va='center', fontsize=9)
ax.legend(title='Class', fontsize=10, title_fontsize=12)
ax.set_yticks([])
ax.set_yticklabels([])
ax.set_ylabel('')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.savefig('1.pdf', format='pdf', bbox_inches='tight')
plt.show()
使用 Seaborn 和 Matplotlib 绘制一个 堆叠水平柱状图,展示多分类模型中各特征在不同类别下的平均 SHAP 值的重要性,首先,将每个特征在所有类别中的 SHAP 值求和排序,以确定特征的重要性顺序,接着,依次为每个类别绘制水平条形,并通过堆叠方式展示每个类别对特征的重要性贡献,图例用于标明不同类别的颜色对应关系,最终图形美化去除了多余的坐标轴刻度,下面为一个二分类模型的shap特征贡献图
通过这两张图的直观对比,我们可以发现:二分类模型的 SHAP 特征贡献图(第一张图)展示的是特征对整体目标类预测的影响,而多分类模型的 SHAP 特征贡献图(第二张图)将每个特征在不同类别中的贡献分开显示,更详细地揭示了特征在各类别间的差异性,也就是前面所说的由于任务不同导致其可视化也存在差异
多分类模型特征贡献图其它绘制方法
与之前的堆叠柱状图相比,采用3D 柱状图来展示特征在不同类别中的贡献度,提供更立体的视觉效果,使我们能够从不同视角分析特征在各类别中的影响,相比之下,3D 图更直观,但在特征和类别较多时可能增加视觉复杂度,而堆叠柱状图更适合快速比较各类别的相对贡献,代码获取:如果您希望获取本文展示的改进版代码,请添加作者微信联系获取
🎁 赠书活动来啦! 🎁
支持知识分享,畅享学习乐趣!即日起,只需 点赞 、 在看 、 转发 此文章,作者将从后台随机抽取一位幸运儿,免费包邮赠送《细说机器学习:从理论到实践》这本精彩书籍📚!
💡 赶快参与,一键三连,说不定你就是那位幸运读者哦!
往期推荐
SCI图表复现:利用小提琴图折线图综合展示训练集、验证集、测试集模型性能对比
复现SCI文章 SHAP 依赖图可视化以增强机器学习模型的可解释性
复现 Nature 图表——基于PCA的高维数据降维与可视化实践及其扩展
复现 Nature 图表可视化——基于模型残差分析与显著性检验的模型解释
微信号|deep_ML
欢迎添加作者微信进入Python、ChatGPT群
进群请备注Python或AI进入相关群
无需科学上网、同步官网所有功能、使用无限制
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~
个人观点,仅供参考