正在加载中...

展开本页目录
算法教程SHAP-可解释性

SHAP-可解释性

No.146 · 在线教程

SHAP-可解释性 的真实核心主要位于:

SHAP-可解释性

1. 方法概述

SHAP-可解释性 的真实核心主要位于:

  • core/shap_calculator.py
  • ui/upload_widget.py
  • ui/results_widget.py
  • ui/main_window.py

从当前真实代码看,这个模块不是“只要叫 SHAP 就一定完全依赖官方 shap 包”的单一路径实现,而是:

  1. 先训练树模型;
  2. 优先尝试 shap.TreeExplainer
  3. 若本机未安装 shap,则回退到自定义近似解释;
  4. 汇总 mean_abs_shap
  5. 导出 Excel、图表和复现脚本。

设清洗后的样本集为

$$ \mathcal{D}=\{(x_i,y_i)\}_{i=1}^{N} \tag{1} $$

代码最终输出的是每个样本、每个特征的贡献矩阵,以及按平均绝对贡献排序后的全局重要性表。

在标准 SHAP 口径下,局部解释满足加性分解

$$ f(x)=\phi_0+\sum_{j=1}^{p}\phi_j \tag{2} $$

但这套程序只有在 shap.TreeExplainer 成功导入并计算时,才真正接近这一标准形式;若进入回退分支,则得到的是“近似贡献值”,论文说明里必须写清楚。

2. 数据约束、编码与训练前处理

2.1 输入约束

代码要求:

  1. 数据非空;
  2. 目标列存在;
  3. 至少选择 1 个特征列;
  4. 特征列不能包含目标列;
  5. 清洗后至少保留 10 行有效样本;
  6. 清洗后至少保留 1 个有效特征。

当前 UI 只允许显式选择两类任务:

  • classification
  • regression

并不包含 auto 任务识别。

2.2 类别特征编码与仅数值模式

encode_categorical=True,代码对特征执行:

$$ X^{(\text{enc})}=\mathrm{OneHot}(X) \tag{3} $$

即直接使用 pd.get_dummies(drop_first=False)

encode_categorical=False,则只保留数值列:

$$ X^{(\text{num})}=X[:,\text{numeric columns}] \tag{4} $$

被丢弃的非数值特征数量会记录到 dropped_info["non_numeric"] 中。

2.3 inf / 缺失值处理

代码会先把 inf-inf 统一替换成 NaN,然后把特征与目标列拼接后整行删除缺失:

$$ (\tilde X,\tilde y)=\mathrm{DropNaRows}\big(X,y\big) \tag{5} $$

因此这里不是“插补后再解释”,而是“直接删掉含缺失 / 无穷值的样本行”。删除的行数会记录在 dropped_info["nan_rows"] 中。

2.4 目标列处理

分类任务中,只有当目标列不是数值型时,代码才会把它转成 category codes

$$ y^{(\text{cls})}=\mathrm{CatCode}(y) \tag{6} $$

这不是 LabelEncoder,而是 pandas 的类别编码。

回归任务中,若目标列不是数值型,则尝试:

$$ y^{(\text{reg})}=\mathrm{to\_numeric}(y) \tag{7} $$

若转换后仍有非法值,则直接报错终止。

2.5 训练 / 测试划分

模型训练前执行:

$$ \mathcal{D}_{\text{train}}\cup\mathcal{D}_{\text{test}}=\mathcal{D} \tag{8} $$

其中:

  • test_size 由界面给定;
  • 分类任务优先使用 stratify=y
  • 若分层切分失败,则自动回退为不分层切分;
  • 若回退后训练集只剩 1 个类别,则直接报错。

3. 模型族与 SHAP 计算口径

3.1 支持的模型族

这套实现只支持两类树模型:

  1. random_forest
  2. gradient_boosting

对应到 sklearn 分别是:

  • RandomForestClassifier / RandomForestRegressor
  • GradientBoostingClassifier / GradientBoostingRegressor

它不是通用 explainability 平台,也不支持线性模型、SVM、KNN 或神经网络的 SHAP。

其中一个实现细节是:

$$ \text{max\_depth}= \begin{cases} \text{None}, & \text{随机森林且界面勾选“不限深度”}\\ 3, & \text{梯度提升树且传入深度}<1\\ \text{用户输入值}, & \text{其他情况} \end{cases} \tag{9} $$

3.2 解释样本与背景样本

代码不会只在测试集上解释,而是先从清洗后的全体样本中抽取待解释样本:

$$ X_{\text{shap}}=\mathrm{Sample}(X,\texttt{shap\_sample\_size}) \tag{10} $$

同时从训练集抽取背景样本:

$$ X_{\text{bg}}=\mathrm{Sample}(X_{\text{train}},\texttt{background\_sample\_size}) \tag{11} $$

因此:

  1. 模型指标来自测试集;
  2. SHAP 值样本来自全体清洗后数据的一个随机子集;
  3. 背景集只来自训练集。

3.3 标准分支:TreeExplainer

import shap 成功,代码执行:

  • shap.TreeExplainer(model, data=background)
  • explainer.shap_values(X_shap, check_additivity=False)

这里特别要注意 check_additivity=False 已被显式关闭。

3.4 二分类与多分类的输出处理

官方 TreeExplainer 的输出在不同任务下形态不同,代码做了额外整理:

  1. 若返回的是二分类 list,则取第 2 类(索引 1)的 SHAP 值;
  2. 若返回三维数组且最后一维为 2,也取第 2 类;
  3. 若是多分类多于 2 类,则对类别维做绝对值平均。

多分类聚合后的特征贡献可写成

$$ \phi_{ij}^{(\text{mc})}=\frac{1}{K}\sum_{k=1}^{K}\left|\phi_{ijk}\right| \tag{12} $$

这意味着多分类场景下,程序会丢失“正负方向”,保留的是跨类别平均后的绝对贡献强度。

3.5 回退分支 1:树路径贡献近似

若本机没有 shap 包,代码会先尝试 _tree_path_fallback_shap_values()。但这条分支只适用于:

  • RandomForestRegressor
  • GradientBoostingRegressor

它会沿着每棵树中样本实际经过的分裂路径,累计父节点到子节点的预测值变化,并记到当前分裂特征上。可近似写成

$$ \tilde\phi_{ij}=\sum_{t\in\mathrm{Path}_i(j)} s_t\big(v_{\mathrm{child}(t)}-v_t\big) \tag{13} $$

其中:

  • \(\mathrm{Path}_i(j)\) 表示样本 \(x_i\) 在树路径上由特征 \(j\) 触发的分裂节点集合;
  • \(v_t\) 表示节点预测值;
  • \(s_t\) 是模型类型相关的缩放因子。

当前代码中:

  • 随机森林的缩放因子约为 1 / n_estimators
  • 梯度提升回归树的缩放因子约为 learning_rate

3.6 回退分支 2:线性化近似

若树路径回退也不可用,则进入 _fallback_shap_values() 的第二层近似。

先取训练集特征均值作为基线:

$$ \bar x_j=\frac{1}{n_{\text{train}}}\sum_{i=1}^{n_{\text{train}}}x_{ij} \tag{14} $$

再从模型中抽取一组特征权重:

$$ \omega_j= \begin{cases} \text{feature\_importances\_}_j, & \text{若模型具有该属性}\\ |\text{coef}_j|, & \text{若模型具有系数}\\ 1, & \text{否则} \end{cases} \tag{15} $$

并归一化为

$$ w_j=\frac{\omega_j}{\sum_{\ell=1}^{p}|\omega_\ell|} \tag{16} $$

最终近似贡献写成

$$ \tilde\phi_{ij}=(x_{ij}-\bar x_j)w_j \tag{17} $$

这一步并不是严格的 SHAP 定义,只是一种“以特征中心化差值乘归一化权重”的线性化替代。

另外,回退分支里的基线输出使用:

$$ \phi_0^{(\text{fallback})}=\frac{1}{n_{\text{train}}}\sum_{i=1}^{n_{\text{train}}}\hat y_i \tag{18} $$

这里调用的是 model.predict(X_train)。因此在分类任务的回退模式下,这个 expected_value 更接近“预测类别编码的均值”,而不是官方 SHAP 中常见的类别概率基线。

3.7 全局特征重要性

无论走标准分支还是回退分支,最终全局特征重要性都统一计算为:

$$ I_j=\frac{1}{m}\sum_{i=1}^{m}|\phi_{ij}| \tag{19} $$

其中 \(m\) 是 X_shap 的样本数。导出表 mean_abs_shap 就是按这个值降序排序。

4. 模型指标、图表与可视化口径

4.1 分类指标

分类任务只输出:

$$ \mathrm{Accuracy}=\frac{1}{n}\sum_{i=1}^{n}\mathbf{1}(\hat y_i=y_i) \tag{20} $$

并额外输出:

  • f1_macro
  • f1_weighted

这里不会额外导出 precisionrecallroc_auc

4.2 回归指标

回归任务输出:

$$ \mathrm{RMSE}=\sqrt{\frac{1}{n}\sum_{i=1}^{n}(y_i-\hat y_i)^2} \tag{21} $$

$$ \mathrm{MAE}=\frac{1}{n}\sum_{i=1}^{n}|y_i-\hat y_i| \tag{22} $$

$$ R^2=1-\frac{\sum_i(y_i-\hat y_i)^2}{\sum_i(y_i-\bar y)^2} \tag{23} $$

4.3 summary.png 的真实含义

一个很重要的细节是:代码没有调用 shap.summary_plot() 来生成经典 beeswarm 图,而是自己用 matplotlib 画了“横向箱线图”:

  1. 先取 mean_abs_shap 排名前 max_display 的特征;
  2. 收集这些特征在 X_shap 上的贡献值分布;
  3. 对每列画水平 boxplot;
  4. 保存为 summary.png

所以这张图更准确地说是:

  • SHAP Value Distribution 箱线图

而不是标准 SHAP 论文里常见的点状 summary beeswarm。

4.4 bar.png

另一张图是:

  • Mean |SHAP| 水平条形图

其数值直接来自公式 \((19)\) 的 mean_abs_shap

5. 输出结果、Excel 与输出文件

5.1 结果字典

run_analysis() 最终保存的关键字段包括:

  • raw_data
  • processed_data
  • step_results
  • final_results
  • charts
  • parameters

其中:

  1. raw_data 是完整原始数据表;
  2. processed_data 是清洗并编码后的特征矩阵,再附上目标列;
  3. step_results["shap_values"] 会在首列插入 sample_index
  4. step_results["dropped_info"] 记录了非数值列丢弃数与缺失行删除数;
  5. parameters["shap_backend"] 会记录实际使用的后端。

当前 shap_backend 可能出现的取值有:

  • tree_shap
  • fallback_tree_path
  • fallback_linearized

5.2 Excel 工作表

save_to_excel() 实际导出:

  • 原始数据
  • 处理后数据
  • 特征重要性
  • SHAP值样本
  • 模型指标
  • 参数
  • 图表索引

其中 图表索引 会同时保存:

  1. 图表名称;
  2. 相对路径;
  3. 绝对路径;
  4. 中文列名形式的路径字段。

5.3 图表目录

若输出文件名为:

  • SHAP_results_时间戳.xlsx

则图表会写入同目录下的:

  • SHAP_results_时间戳_plots/summary.png
  • SHAP_results_时间戳_plots/bar.png

5.4 默认输出路径与自动导出

上传页默认输出路径形如:

  • results/SHAP-可解释性分析结果_时间戳/SHAP_results_时间戳.xlsx

结果页线程在计算完成后会自动调用 save_to_excel(),因此当前主流程不是“先算完再手动另存”,而是“算完立即自动落盘”。

5.5 复现脚本

结果页还会自动生成:

  • repro_shap.py

并尽量把原输入文件复制到:

  • repro_inputs/

若原始输入并不是外部文件,而是内存中的 DataFrame,则会额外写出:

  • shap_repro_data.csv

用于后续复跑。

6. 实现说明与注意事项

根据当前真实代码,论文说明中建议明确以下边界:

  1. 这不是“始终依赖官方 SHAP 包”的单一路径实现,而是 TreeExplainer 优先、缺包时回退到自定义近似解释。
  2. 当前只支持 random_forestgradient_boosting 两类树模型,不支持更广泛的 explainability 后端。
  3. shap_sample_size 的采样对象是全体清洗后样本,不是测试集,所以 SHAP 值样本与模型评估样本并不完全相同。
  4. 多分类场景下,代码会把类别维做绝对值平均,方向信息会丢失。
  5. 在没有 shap 包且任务为分类时,通常会落到 fallback_linearized,此时结果更适合作为近似特征贡献参考,而不应写成标准 SHAP 值。
  6. summary.png 是自绘箱线图,不是官方 shap.summary_plot() 的 beeswarm 图。
  7. 目录里虽然有 feature_engineering_panel.pyresults_preview_widget.py,但 main_window.py 当前真正接入的是 UploadWidget + ResultsWidget 主链路,这两个文件并未进入现行主分析流程。

7. 论文写作建议

7.1 方法描述模板

论文正文可写为:本文采用 SHAP 思想对树模型预测结果进行解释。当运行环境具备官方 shap 依赖时,使用 TreeExplainer 计算特征贡献;当环境缺失 shap 时,项目自动退化为内置近似解释分支。因此,文中应同时说明实际运行所采用的解释路径,以保证结论与实现一致。

7.2 结果解释模板

结果部分可写为:全局层面可利用 mean_abs_shap 排序识别对模型输出影响最大的特征,局部层面则可通过单样本贡献值解释具体预测是如何被各变量共同推动形成的。若进入近似回退分支,论文中宜将其表述为“近似特征贡献值”或“SHAP 风格解释结果”,而不宜直接等同于严格的 Shapley 值分解。对于多分类任务,还需额外说明当前实现对类别维做绝对值平均,因此结果更适合解释总体贡献强度。

7.3 图表题注模板

  • 表题可写为:全局特征贡献 mean_abs_shap 排序结果。
  • 表题可写为:典型样本的局部 SHAP 贡献明细。
  • 图注可写为:模型全局特征重要性图与样本分布型解释图。

8. 单篇终审补充

8.1 图题与表题对齐建议

  • 原始数据 表可写为:表X SHAP 分析原始数据预览。
  • 处理后数据 表可写为:表X SHAP 分析处理后特征数据。
  • 特征重要性 表可写为:表X 全局 mean_abs_shap 排序结果。
  • SHAP值样本 表可写为:表X 样本级 SHAP 风格贡献值明细。
  • 模型指标 表可写为:表X 基础树模型预测性能指标。
  • 参数 表可写为:表X SHAP 分析参数及解释后端记录。
  • 图表索引 表可写为:表X SHAP 图表索引与路径清单。
  • summary.png 建议写为:图X SHAP 值分布箱线图。
  • bar.png 建议写为:图X 基于 mean_abs_shap 的全局特征重要性条形图。

8.2 终审说明

  • 当前真实工作簿名称一般是 SHAP_results_时间戳.xlsx,同目录下会生成 SHAP_results_时间戳_plots/summary.pngSHAP_results_时间戳_plots/bar.png;若是复现实验,则对应 shap_repro.xlsxshap_repro_plots/。论文中引用结果文件时应区分“主运行输出”和“repro 输出”。
  • summary.png 在当前实现里是自绘箱线图,不是官方 SHAP beeswarm;正文若写“SHAP 总结图”,建议补一句“采用箱线图形式展示样本贡献分布”,避免图义与读者常见认知错位。
  • 参数 表中的 shap_backend 是结果解释的关键口径。若该值为 fallback_tree_pathfallback_linearized,则正文应用“近似 SHAP 风格解释”或“近似特征贡献分析”表述,而不应直接宣称获得严格 Shapley 值。
  • SHAP值样本 的样本来源受 shap_sample_size 控制,且默认从清洗后样本中抽取,不是严格意义上的测试集子样本。论文若把该表用作案例解释,应说明其为“解释样本”而不是“测试集全部样本”。
  • 真实 repro 脚本为 repro_shap.py,并通过 repro_inputs/sample_data.csv 读取输入副本;若原输入不来自外部文件,程序还可能额外落 shap_repro_data.csv。附录中给复现实验路径时,应优先引用结果目录下的相对路径结构。

8.3 全量强化补充

本篇终审补充绑定的真实算法目录为 具体的算法3/可解释与AutoML/SHAP-可解释性,本次采用的代表性结果目录为 具体的算法3/可解释与AutoML/SHAP-可解释性/results/manual_sample_data_regression_20260322

当前目录中真实存在两份工作簿:

  • shap_sample_data_regression.xlsx
  • shap_repro.xlsx

两者实测工作表一致,均包含:

  • 原始数据
  • 处理后数据
  • 特征重要性
  • SHAP值样本
  • 模型指标
  • 参数
  • 图表索引

因此这一轮结果目录同时保留了主结果工作簿与 repro 再生产物。若正文引用该轮结果,应优先使用 shap_sample_data_regression.xlsx 作为主结果,把 shap_repro.xlsx 明确写成复现输出。

当前目录中的真实图文件同样分成两套:

  • 具体的算法3/可解释与AutoML/SHAP-可解释性/results/manual_sample_data_regression_20260322/shap_sample_data_regression_plots/summary.png
  • 具体的算法3/可解释与AutoML/SHAP-可解释性/results/manual_sample_data_regression_20260322/shap_sample_data_regression_plots/bar.png
  • 具体的算法3/可解释与AutoML/SHAP-可解释性/results/manual_sample_data_regression_20260322/shap_repro_plots/summary.png
  • 具体的算法3/可解释与AutoML/SHAP-可解释性/results/manual_sample_data_regression_20260322/shap_repro_plots/bar.png

因此这一轮真实图证据只有两类:summary.pngbar.png。其中 summary.png 仍应按当前实现解释为自绘贡献分布箱线图,而不是官方 beeswarm。

复现实物方面,该目录实际包含:

  • 具体的算法3/可解释与AutoML/SHAP-可解释性/results/manual_sample_data_regression_20260322/repro_shap.py
  • 具体的算法3/可解释与AutoML/SHAP-可解释性/results/manual_sample_data_regression_20260322/repro_inputs/sample_data.csv

这里需要如实说明一个真实现象:脚本虽然把 INPUT_FILE 相对化为 repro_inputs/sample_data.csv,但 PARAMS 中仍保留了旧的绝对路径 file_path 与绝对 output_file 字符串,同时又会把 output_file 覆盖为本地 shap_repro.xlsx。因此这一篇不能美化成“目录已完全纯相对路径化”,更准确的口径是“输入副本已相对化,但参数字典里仍残留旧绝对路径兼容信息”。

11. 软件实现核查补充(2026-07)

  • 当前主结果目录应写作 具体的算法3/可解释与AutoML/SHAP-可解释性/results/manual_sample_data_regression_20260322
  • 正文应围绕 RawDataProcessedShapValuesParams参数图表索引Charts 来写。
  • 图证应对应 summary.pngbar.png,并把主结果和 repro 结果区分开。
  • 复现脚本应按 repro_shap.py + repro_inputs/sample_data.csv 的口径说明,同时注意参数字典里仍残留旧绝对路径兼容信息。