SHAP 值归因分析
一、直观理解:把一次预测相对“平均预测”的差额,公平分给各特征
复杂模型(随机森林、提升树、SVR+核)给出 后,评委与使用者会问:这次为什么预测成这样?SHAP(SHapley Additive exPlanations)借用博弈论 Shapley 值,把
分配到每个特征上,满足加性:
表示特征 把预测往上推,反之往下。它提供的是对该模型、该样本的解释,不是数据生成过程的因果证明。
对树模型可用 TreeSHAP 多项式时间精确计算;对一般模型可用 KernelSHAP 等近似。建模赛中,SHAP 常接在随机森林/提升树之后,用来写清“哪些变量驱动了预测”。
二、适用场景与优缺点
| 类型 | 说明 |
|---|---|
| 适用场景 | 黑箱/灰箱模型需要局部与全局解释;要做特征筛选辅助;需要案例级解释(某个高风险样本);对比不同模型是否学到相似结构。 |
| 核心优点 | 有公理基础(有效性、对称性、哑元、可加);局部+全局统一;可视化丰富(瀑布图、蜂群图);比简单重要性更细。 |
| 局限与缺点 | 计算可能昂贵;相关特征间分摊难直觉;解释的是模型不是世界;依赖背景数据集选择;MATLAB 生态不如 Python 成熟,需自写或调用。 |
三、核心原理
1. Shapley 值
特征集合 ,对联盟 ,边际贡献为
Shapley 值对所有联盟按组合权重平均。模型解释里 常取“只利用 中特征时的期望模型输出”(实现上有干预/条件两种哲学,结果可能不同)。
2. 加性解释模型
SHAP 把解释写成线性代理:
为特征是否出现的简化输入。对树模型,TreeSHAP 利用树结构高效计算。
3. 全局解释
对样本平均 得到全局重要性;绘制 对特征值的散点可看方向与非线性。
四、使用流程
- 先固定训练好的模型与严格验证后的预测性能;
- 选择背景集(训练数据的子样本)用于期望基线;
- 计算测试集或关键案例的 SHAP;
- 全局:平均绝对 SHAP 排序;
- 局部:对典型样本做瀑布图;
- 与机理/常识对照,发现数据问题或泄漏特征;
- 不把 SHAP 当因果,不在未验证模型上“解释”。
五、MATLAB 示例
MATLAB 近年在 interp/shapley 相关功能上有增强(版本差异大)。下面给两种路径:(A) 若有 shapley 对象则优先;(B) 排列型/核型简化实现思路,保证可写进论文。
%% 先训练一个随机森林
rng(29);
n = 400; p = 6;
X = randn(n,p);
y = 2*X(:,1) + 0.8*X(:,2).^2 - 1.2*X(:,3) + 0.3*randn(n,1);
predNames = compose("x%d",1:p);
tbl = array2table([X y], "VariableNames", [predNames, "y"]);
tr = 1:300; te = 301:n;
Mdl = fitrensemble(tbl(tr,:), "y", "Method","Bag", ...
"NumLearningCycles",150, "Learners",templateTree("MinLeafSize",5));
yhat = predict(Mdl, tbl(te,:));
fprintf("测试 RMSE=%.4f\n", sqrt(mean((tbl.y(te)-yhat).^2)));
%% 路径 A:若工具箱提供 shapley(R2021a+ 等,以本机文档为准)
try
queryPoint = tbl(te(1), predNames);
explainer = shapley(Mdl, tbl(tr,predNames));
explainer = fit(explainer, queryPoint);
plot(explainer);
title("单样本 SHAP 瀑布图(若支持)");
disp(explainer.ShapleyValues);
catch ME
warning("shapley 不可用,改用置换重要性 + 局部近似:%s", ME.message);
end
%% 路径 B:全局置换重要性(不是 SHAP,但常作可解释性补充)
imp = oobPermutedPredictorImportance(Mdl);
figure; bar(imp); set(gca,"XTickLabel",predNames);
ylabel("OOB 置换重要性"); title("全局重要性(置换)");
%% 路径 C:Kernel-SHAP 风格的简化蒙特卡洛(教学用)
% 对单个样本 x0,随机联盟估计 phi
x0 = X(te(1),:);
Xbg = X(tr,:); % 背景
f = @(XX) predict(Mdl, array2table(XX,"VariableNames",predNames));
phi0 = mean(f(Xbg));
phi = zeros(1,p);
M = 300; % 采样次数
for m = 1:M
Smask = rand(1,p) > 0.5; % 随机联盟
% 有 S / 无 j 的边际:对每个 j 轮换
for j = 1:p
S0 = Smask; S0(j)=false;
S1 = S0; S1(j)=true;
z0 = Xbg(randi(size(Xbg,1)),:);
z1 = z0; z0(S0) = x0(S0); % 联盟特征取 x0,其余取背景
z1 = z0; z1(j) = x0(j);
% 更标准的做法是分别构造 with/without j
xa = z0; xb = z0; xb(j)=x0(j);
% 若 j 已在 S0 中已处理;此处简化:
if ~S0(j)
phi(j) = phi(j) + (f(xb) - f(xa));
end
end
end
phi = phi / M;
fprintf("基线 E[f]=%.3f, 预测f(x0)=%.3f, sum(phi)+baseline≈%.3f\n", ...
phi0, f(x0), phi0+sum(phi));
figure; bar(phi); set(gca,"XTickLabel",predNames);
ylabel("近似 SHAP"); title("单样本特征贡献(简化蒙特卡洛)");
Python 生态(环境允许)更成熟:
import shap
explainer = shap.TreeExplainer(model)
sv = explainer.shap_values(X_te)
shap.summary_plot(sv, X_te)
shap.force_plot(explainer.expected_value, sv[0], X_te.iloc[0])
论文中应写明算法(TreeSHAP/KernelSHAP)、背景集与软件版本。
六、如何解释结果
完整写法:
- 模型性能先达标,再解释;
- 基线值 的含义;
- 全局:平均 排序,与业务常识是否一致;
- 局部:选取高/低预测案例,说明主推特征;
- 依赖图:特征值增大时 如何变;
- 相关特征:若 强相关,贡献可能被拆分,需联合讨论;
- 明确“关联解释 ≠ 因果干预效应”。
七、常见误区
1. 模型很差还大谈 SHAP
解释错误模型只会系统化错误故事。
2. 把 SHAP 当因果
政策建议需要识别假设,不是一张蜂群图。
3. 背景集乱选
背景决定 与反事实,须来自合理参照总体。
4. 忽略特征相关
独立介入与条件期望两种定义会冲突,需诚实讨论。
5. 只解释训练集“漂亮样本”
应覆盖测试集与边界案例。
八、小结
SHAP 的核心是:
用 Shapley 值把模型预测相对于平均水平的差额公平分配到各特征,提供局部与全局一致的可加解释。
它是机器学习预测落地到论文“可说明”的关键桥梁,尤其配合随机森林/提升树。MATLAB 可根据版本使用 shapley 或报告置换重要性 + 简化估计,并在必要时对照 Python SHAP。记住顺序:先验证预测,再解释;解释模型,而非宣称因果;始终结合训练期外表现,而不是只看 。