精确 Shapley 要枚举 2^10 = 1024 个子集, 10 个特征还行,100 个特征就是 2^100。换成 60 次置换的近似解, Top3 排序只有 38/60 和精确解一致(63%),平均绝对偏差 0.0709。 再把一个特征原样复制一份,两边的功劳差 0.000000——劈得干干净净, 业务却问不出「到底哪个是主因」。
12.1 前两节解决了「数据对不对」「模型坏没坏」。 这一节解决最后一个问题:模型给出的理由,能不能被追问。 金融是强监管行业,一个不能解释的拒绝决定,等于没有决定。
一、精确解很美,但指数爆炸
Shapley 值的定义很干净:把「这次预测」的功劳, 按「每个特征在多少个组合里贡献了多少」分摊下去。 它是唯一满足四条公理的分摊方式——效率性、对称性、虚拟性、可加性。 代价是:要精确算,得把所有特征子集都跑一遍。 10 个特征就是 2^10 = 1024 次模型调用。 20 个特征是一百万次,100 个特征就是 2^100——比宇宙原子数还多。 所以真实系统里几乎都在用近似。 最常见的做法是 Monte Carlo 置换抽样:随机排特征顺序、逐个加进去,跑若干次取平均。 近似就带来一个新问题:结果带随机性。 同一份数据、同一个模型,跑两次的解释可能不一样。 这在合规场景里极其麻烦——客户问「为什么拒我」,你不能给一个每次重算都变的答案。 Demo 用一个带两两交互项的二次函数当模型(这样精确解可算), 对比 60 次置换的近似解与精确解的差距。
二、63% 的一致率意味着什么
| 指标 | 数值 |
|---|---|
| Top3 排序一致 | 38 / 60(63%) |
| 平均绝对偏差 | 0.0709 |
| 精确解平均绝对值 | 0.6676 |
| 精确解单次耗时 | 2^10 次模型调用 |
| 平均绝对偏差 0.0709,相对量级是 0.6676——大约 11%。 | |
| 这个精度看起来够用,但 Top3 排序只有 63% 一致。 | |
| 为什么偏差不大、排序却对不上? | |
| 因为排名靠前的几个特征功劳本来就接近。 | |
| 差 0.07 在绝对值上是小误差,在「谁第一谁第二」上就是翻盘。 | |
| 这条区别很重要:解释结果有两种用法,容错度完全不同。 | |
| 「这个特征贡献了多少」对误差宽容;「哪个特征最重要」对误差极敏感。 | |
| 所以对外解释时,报「贡献度区间」比报「排名第一」安全得多。 | |
| 「负债比贡献了 0.35 到 0.42」是诚实的,「第一重要因素是负债比」在 63% 的可靠度下就是冒险。 |
三、相关特征会分账,这是定义决定的
把第 10 个特征原样复制一份(完全相关),再算精确 Shapley: 它原本在 10 个样本里有 4 个排第一; 复制之后,它和副本分到的功劳最大相差 0.000000。 它自己那一份,和原来的一半最大也只差 0.000000。 两处都贴着 0,说明功劳是干干净净对半劈开的。 这不是算法错了——对称性公理要求「提供完全相同信息的两个特征,必须得到相同的功劳」。 但业务方想知道的是「到底哪个是主因」,而这个问题 Shapley 答不了。 它只能告诉你「这一组相关的特征一共贡献了多少」,然后在这组内部平均分。 这在风控里非常常见: 「月收入」和「年收入」相关,「逾期次数」和「最长逾期天数」相关, 「负债总额」和「负债比」相关。 它们两两之间几乎共线,归因结果就会被摊薄成一堆看起来都不重要的特征。 对策是把相关特征先合并成一个(比如「收入」和「可支配收入」合成「还款能力」), 再对合并后的组做归因。 归因的粒度应该由业务定义,不是由原始列决定。
四、落地:三层交付
① 全局层:特征重要度排序 + 区间。 给监管看「模型整体依赖哪些维度」,这一层用近似解就够,但必须报出波动范围。 ② 单样本层:这次拒绝的主要原因。 这一层要能复现——固定随机种子,或者对每个客户缓存结果。 「每次重算都变」的解释在客诉场景里站不住。 ③ 反事实层:改什么能通过。 「如果负债比从 0.62 降到 0.45,这次就能过」——这是客户真正想听的, 也是唯一能落到行动上的一句话。 最后一句:可解释性不是一个数字,是一份「能被追问」的说明。 精确解、近似解、相关特征分账——三个坑都要知道,才敢交给监管。
五、动手 Demo:可解释性:60 次抽样的近似归因,Top3 排序只有 38 次和精确解一致
造一个 10 维、含两两交互项的二次函数当模型,这样 2^10 = 1024 个子集可以全枚举, 精确 Shapley 算得出来。再用 Monte Carlo 置换抽样 60 次求近似,比对两者的 Top3 排序与平均绝对偏差; 最后把功劳最大的那个特征原样复制一份,重算精确解,看功劳怎么分。
# 精确解:枚举全部子集,v(S) 是「只用 S 里特征」时的预测
bits = ((np.arange(2 ** d)[:, None] >> np.arange(d)) & 1).astype(float)
for k in range(len(Xq)):
Z = bits * Xq[k] + (1 - bits) * mu
v = f(Z)
# 每个 i 的边际贡献 v(S+i) - v(S) 按 |S| 加权求和
# 近似:随机排特征顺序,逐个加进去,跑 60 次取平均
for _ in range(60):
order = rg.permutation(d)
# 按 order 依次加入,累加每次的边际贡献
实跑输出:
--------------------------------------------------------------------
可解释性:60 次抽样的近似归因,Top3 排序只有 38 次和精确解一致
--------------------------------------------------------------------
特征数 10 维,含两两交互项
精确解 枚举 2^10 = 1024 个子集
近似解 Monte Carlo 置换 60 次
平均绝对偏差 0.0709(精确解量级 0.67)
指标 数值
---------------------------------
Top3 排序一致 38 / 60(63%)
平均绝对偏差 0.0709
精确解平均绝对值 0.6676
精确解单次耗时 2^10 次模型调用
**先把两件事分开:算得对不对,和算得快不快。**
**精确 Shapley 要对 2^10 = 1024 个子集各调用一次模型。**
**10 个特征还行,100 个特征就是 2^100——比宇宙原子数还多。**
**所以真实系统里几乎都在用近似。**
**近似解在这个 Demo 里 Top3 排序只有 63% 一致,平均偏差 0.0709。**
**听起来还行,但要注意:这是 60 次置换的结果。**
**次数减到 10,排序会更抖;加到 1000,会收敛到精确解。**
**所以「归因结果」这件事本身带随机性——同一个客户跑两次,解释可能不一样。**
**这一点在合规场景里很麻烦:客户问「为什么拒我」,你不能给一个每次重算都变的答案。**
第二件事更隐蔽,看下面这组对比。
**把第 10 个特征原样复制一份(完全相关),再算一次精确 Shapley:**
**第 10 个特征原本在 10 个样本里有 4 个排第一。**
**复制之后,它和副本分到的功劳最大相差 0.000000。**
**它自己那一份,和原来的一半最大也只差 0.000000。**
**两处都贴着 0,说明功劳是干干净净对半劈开的。**
**这不是算法错了,是 Shapley 的定义决定的:两个特征提供完全相同的信息,就应该平分功劳。**
**但业务上想知道的是「到底哪个是主因」,而这个问题 Shapley 答不了。**
**对策是把相关特征先合并成一个(比如「收入」和「可支配收入」),再对合并后的组做归因。**
**归因的粒度应该由业务定义,不是由原始列决定。**
最后一句:**可解释性不是一个数字,是一份「能被追问」的说明。**
**精确解、近似解、相关特征分账——三个坑都要知道,才敢交给监管。**
要说明:模型是我**造的一个带交互项的二次函数**,不是真模型。
**真实模型里归因还会受特征尺度、非线性强度影响。**
**但「精确解指数爆炸」「近似解有随机性」「相关特征会分账」都是真的。**
done -> _out_39u.txt
这张表里最容易被忽略的是第三行:精确解的平均绝对值是 0.6676。 所以 0.0709 的偏差不是「差了 0.07」,是「差了量级的 11%」。 11% 的精度用在「贡献度区间」上够,用在「谁第一」上不够。 这就是为什么 63% 这个数比 0.0709 更值得记住——它直接对应「解释会不会变」。 而复制特征那一组实验,差的不是精度,是定义。 0.000000 的对称不是巧合,是公理逼出来的结果。 任何「符合公理」的归因方法,都会给出同样的分账。 这一条可以推广:凡是「两个输入提供相同信息」的场景,归因一定摊薄。 想得到「主要原因」,必须在建模前就把相关特征合并,而不是事后解释。 要说明:模型是我造的一个带交互项的二次函数,不是真模型。 真实模型里归因还会受特征尺度、非线性强度影响。 但「精确解指数爆炸」「近似解有随机性」「相关特征会分账」都是真的。
30 秒实验
- 把置换次数从 60 减到 10,再看 Top3 一致率。 会明显掉,而且每次运行结果都不一样——这就是「解释带随机性」的直观样子。
- 把置换次数加到 2000,看平均绝对偏差能不能降到接近 0。 会,但耗时也涨 30 倍左右;这就是真实系统里的取舍点。
- 把维度从 10 降到 6,看精确解耗时与近似偏差各怎么变。 耗时降到 1/16,近似偏差也会变小——维度越低,近似越容易碰到精确解。
常见误区
- 「Shapley 是唯一公平的分摊,所以结果是客观的」。它对称对待相关特征,恰恰让业务问不出主因。
- 「偏差只有 0.07,解释是稳的」。偏差 0.07 就足以让 Top3 排序 37% 的次数对不上。
- 「多跑几次取平均就能稳定」。 可以降低随机性,但对客诉场景要求的是「同一个客户每次得到同一个答案」。
自测三问(点开对答案)
1. 为什么近似解的偏差不大,Top3 排序却只有 63% 一致? —— 靠前的几个特征功劳本身接近,0.07 的绝对误差足以让它们互换名次; 数值误差和排序稳定性是两件事。 2. 复制一个特征之后功劳被对半劈开,是算法缺陷吗? —— 不是。对称性公理要求「提供相同信息的特征得到相同功劳」; 缺陷在于业务想追问「主因」,而 Shapley 的定义不回答这个问题。 3. 为什么归因的粒度要由业务定义? —— 共线特征会被摊薄,导致每个看起来都不重要; 先按业务含义把相关列合并成「还款能力」这类维度,归因才有意义。三节训练要点讲完。从 12.2 开始进入具体的商业场景: 第一个场景不是「模型准不准」,是「这个策略赚不赚钱」。
- ☐ (本节完,下一节 12.2.1 投资决策智能化)