参考帖子:SHAP 可视化解释机器学习模型简介_shap图-CSDN博客
今日作业偏思考类型,有一定难度(暂未完成,等第二遍回来学再二编)
- 参考上述文档补全剩余的几个图
- 尝试确定一下shap各个绘图函数对于每一个参数的尺寸要求,如shap.force_plot力图中的数据需要满足什么形状?
- 确定分类问题和回归问题的数据如何才能满足尺寸,分类采取信贷数据集,回归采取单车数据集。
目录
3.2.1 SHAP 特征重要性蜂巢图 (Summary Plot - Beeswarm)
3.2.2 SHAP 特征重要性条形图 (Summary Plot - Bar)
3.2.3 SHAP 特征重要性小提琴图 (Summary Plot - Violin)
一编(小白学习)
SHAP是什么?
目标:理解复杂机器学习模型(尤其是“黑箱”模型,如随机森林、梯度提升树、神经网络等)为什么会对特定输入做出特定预测。 SHAP 提供了一种统一的方法来解释模型的输出。
PS:黑箱模型就像一个封闭的黑箱子,你往里面输入数据,它会输出结果,但是你很难知道箱子内部是怎么把输入转换成输出的。
核心思想:合作博弈论中的 Shapley 值(量化每个特征对模型预测结果的正向或负向影响。)
比喻:SHAP就像一份“披萨账单”,告诉你每种配料(特征)对总价格(预测结果)的贡献。
-
比如总价20元,芝士贡献+8元,辣椒贡献+2元,菠萝扣了-5元(因为有人讨厌菠萝)。
3个核心概念
1. 基线值(Base Value)
比喻:披萨的“基础价格”——不放任何配料的空白披萨价格。
-
比如所有披萨的平均价格是15元,这就是基线值。
2. SHAP值(Shapley Value)
比喻:每种配料带来的价格变化。
-
芝士:+8元 → SHAP值=+8
-
菠萝:-5元 → SHAP值=-5
3. 模型预测值
公式:预测值 = 基线值 + 所有SHAP值的和
-
例如:15(基线) +8(芝士) +2(辣椒) -5(菠萝) =20元
跑一遍简单代码
# 1. 安装库(就像安装手机APP)
# !pip install shap
# 我在anaconda环境下安装的
# pip install shap -i https://pypi.tuna.tsinghua.edu.cn/simple
# 2. 导入库(就像打开APP)
import shap
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier # 随便选一个模型
任务:预测一个人是否患糖尿病(0=健康,1=患病)
1. 准备数据(超级简单版)
# 假设有3个特征(年龄、血糖、BMI),4个样本
data = {
'年龄': [25, 60, 45, 30],
'血糖': [80, 160, 120, 90],
'BMI': [22, 30, 28, 24]
}
X = pd.DataFrame(data)
y = np.array([0, 1, 1, 0]) # 标签:是否患病
# 训练一个模型(就像教小孩认图)
model = RandomForestClassifier()
model.fit(X, y)
2. 计算SHAP值(自动生成“配料账单”)
# 创建解释器(就像收银机)
explainer = shap.Explainer(model)
# 计算SHAP值(计算每个特征的贡献)
shap_values = explainer(X)
# 查看基线值(空白披萨价格)
print("基线值:", explainer.expected_value)
输出为:
基线值: [0.5375 0.4625]
3. 画图解读(看图说话)
检查一下
print(type(shap_values))
输出为:
<class 'numpy.ndarray'>
shap_values
# 每一行代表一个样本,每一列代表一个特征,值表示该特征对该样本的预测结果的影响程度。
# 正值表示该特征对预测结果有正向影响,负值表示负向影响。
输出为:
array([[[ 0.1475, -0.1475],
[ 0.12 , -0.12 ],
[ 0.155 , -0.155 ]],
[[-0.1425, 0.1425],
[-0.16 , 0.16 ],
[-0.195 , 0.195 ]],
[[-0.0925, 0.0925],
[-0.08 , 0.08 ],
[-0.195 , 0.195 ]],
[[ 0.1475, -0.1475],
[ 0.12 , -0.12 ],
[ 0.155 , -0.155 ]]])
shap_values.shape # 第一维是样本数,第二维是特征数,第三维是类别数
输出为:
(4, 3, 2)
# 打印 shap_values 数组的整体形状
# shap_values 通常是通过 SHAP 解释器计算得到的用于表示特征对模型预测结果贡献的数组
# 这里输出的形状信息可以帮助我们了解数据的维度结构,例如对于分类问题可能涉及样本数、特征数和类别数等维度
print("shap_values shape:", shap_values.shape)
# 打印 shap_values 数组中第一个元素(索引为 0)的形状
# 在某些情况下,shap_values 可能是一个包含多个数组的列表或多维数组
# 这里查看第一个元素的形状可以进一步分析其内部结构,比如在分类问题中可能对应某个类别的特征贡献数组
print("shap_values[0] shape:", shap_values[0].shape)
# 打印 shap_values 数组中所有样本、所有特征在第一个类别(索引为 0)下的子数组形状
# 通过这种切片操作,我们可以聚焦于特定类别的特征贡献情况
# 输出的形状信息有助于我们理解在这个特定类别下,样本和特征的维度关系
print("shap_values[:, :, 0] shape:", shap_values[:, :, 0].shape)
输出为:
shap_values shape: (4, 3, 2)
shap_values[0] shape: (3, 2)
shap_values[:, :, 0] shape: (4, 3)
调整一下图(第一次忘记搞,输出的图中显示不出中文)
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
3.1 单个样本分析(force_plot)
错误代码!
# 分析第2个人为什么被预测为患病(索引=1)
shap.force_plot(
base_value=explainer.expected_value[1], # 注意分类问题要选类别索引
shap_values=shap_values[1][1,:], # [样本索引][类别, 特征]
features=X.iloc[1,:], # 第2个人的特征值
matplotlib=True # 用Matplotlib显示(适合小白)
)
以后要格外注意!这步卡的我想鼠!!
分类问题和回归问题输出的shap_values的形状不同:
- 分类问题:shap_values.shape =(n_samples, n_features, n_classes)
- 回归问题:shap_values.shape = (n_samples, n_features)
数据维度的要求将是未来学习神经网络最重要的东西之一。
# 确保 shap_values 是 (n_samples, n_features, n_classes) 形状
if isinstance(shap_values, list):
shap_values = np.stack(shap_values, axis=-1)
# 分析第2个人为什么被预测为患病(索引=1),假设关注类别 1
sample_index = 1
class_index = 1
shap.force_plot(
base_value=explainer.expected_value[class_index],
shap_values=shap_values[sample_index, :, class_index],
features=X.iloc[sample_index, :],
matplotlib=True
)
plt.show()
解读该图:
- 基线值:图中未明确给出具体数值,仅标记 “base value”,是模型预测的起始参考值
- 年龄 = 60.0 :对最终预测值有正向贡献,使预测值高于基线值,增加患病可能性
- 血糖 = 160.0 :对最终预测值有正向贡献,使预测值高于基线值,增加患病可能性
- BMI = 30.0 :对最终预测值有正向贡献,使预测值高于基线值,增加患病可能性
- 最终预测值:f (x) = 0.96 ,高于常见二分类阈值 0.5(模型实际阈值需依建模设定),倾向于预测为患病
3.2 全局特征重要性(summary_plot)
3.2.1 SHAP 特征重要性蜂巢图 (Summary Plot - Beeswarm)
# 确保 shap_values 是 (n_samples, n_features, n_classes) 形状
if isinstance(shap_values, list):
shap_values = np.stack(shap_values, axis=-1)
# --- 1. SHAP 特征重要性蜂巢图 (Summary Plot - Beeswarm) ---
print("--- 1. SHAP 特征重要性蜂巢图 ---")
shap.summary_plot(shap_values[:, :, 0], features=X, feature_names=X.columns, show=False, max_display=10)
plt.title("SHAP Feature Importance (Beeswarm Plot)")
plt.show()
# 我第一次代码 打印出来的图一模一样
# shap.summary_plot(shap_values[:, :, 0], X) # 只看类别1(患病)的贡献
--- 1. SHAP 特征重要性蜂巢图 (Summary Plot - Beeswarm) ---
-
每个点代表一个样本的特征贡献
-
BMI的SHAP值分布最广 → 对预测影响最大
-
红色表示特征值高(如血糖=160),蓝色表示低(血糖=80)
3.2.2 SHAP 特征重要性条形图 (Summary Plot - Bar)
# --- 2. SHAP 特征重要性条形图 (Summary Plot - Bar) ---
print("--- 2. SHAP 特征重要性条形图 ---")
shap.summary_plot(shap_values[:, :, 0], features=X, feature_names=X.columns, plot_type="bar",show=False) # 这里的show=False表示不直接显示图形,这样可以继续用plt来修改元素,不然就直接输出了
plt.title("SHAP Feature Importance (Bar Plot)")
plt.show()
--- 2. SHAP 特征重要性条形图 ---
3.2.3 SHAP 特征重要性小提琴图 (Summary Plot - Violin)
# --- 3. SHAP 特征重要性小提琴图 (Summary Plot - Violin) ---
print("--- 3. SHAP 特征重要性小提琴图 ---")
shap.summary_plot(shap_values[:, :, 0],features=X, feature_names=X.columns, plot_type="violin",show=False,max_display=10) # 这里的show=False表示不直接显示图形,这样可以继续用plt来修改元素,不然就直接输出了
plt.title("SHAP Feature Importance (Violin Plot)")
plt.show()
--- 3. SHAP 特征重要性小提琴图 ---
内容来自@浙大疏锦行python打卡训练营