halo 的技术博客

返回

用机器学习做因子选股,最尴尬的时刻不是模型跑不出收益,而是模型跑出了收益、你却没法向任何人(包括自己)解释它为什么看多这只股票。风控问你”这仓位的逻辑是什么”,你只能说”模型说的”。树模型自带的特征重要性(gain / split count)看似能帮忙,其实有两个绕不过的死穴:它偏爱取值多样的高基数特征(换手率这种连续因子天然比二值因子更容易被选中分裂),而且它只给全局排名,回答不了单笔预测的归因——“整体上动量最重要”和”这一只股票为什么被看多”是两个完全不同的问题。

结论先放这:SHAP(SHapley Additive exPlanations)把预测当成一场合作博弈,用博弈论里的 Shapley 值给每个因子分配它对这次预测的公平贡献。 它的杀手锏是可加性:一个基准值(全体样本的平均预测)加上所有因子的 SHAP 值,恰好精确等于模型对这个样本的预测 f(x)——不多不少。于是”模型为什么给这只股票打这个分”就被拆成了一张清清楚楚的贡献账单。本文从 Shapley 值的公平性公理讲起,用合成因子数据做单样本瀑布、全局重要性、依赖图,并诚实指出三类真实陷阱(中阶)。

SHAP 瀑布图:从基准值可加地走到单笔预测 f(x)

一、把预测看成一场博弈:Shapley 值从哪来#

Shapley 值本是博弈论概念(Lloyd Shapley, 1953),解决的是”几个人合作赚了一笔钱,怎么公平分”。SHAP 的洞见是把这个框架搬到模型预测上:

  • 一场博弈 = 模型对一个样本的一次预测
  • 玩家 = 每个因子(特征)
  • 合作收益 = 预测值 f(x) 相对于基准值 E[f(x)] 的偏离
  • 每个玩家的分成 = 这个因子的 SHAP 值

那怎么算一个因子”公平”的贡献?Shapley 值的定义是:这个因子在所有可能的”加入顺序”下带来的边际贡献的平均值。具体说,考虑因子 j,看它加入某个已有因子子集 S 时,让预测变化了多少:

边际贡献 = f(S ∪ {j}) − f(S)
plaintext

对所有可能的子集 S 加权平均,就是因子 j 的 Shapley 值。“加权”是为了让每种子集大小被公平对待。这么算出来的分配,被证明唯一地满足四条公平公理

  1. 有效性(可加性):所有因子 SHAP 值之和 = f(x) − E[f(x)]。这是 SHAP 最值钱的性质。
  2. 对称性:两个作用完全相同的因子,SHAP 值相等。
  3. 虚拟性:对预测毫无影响的因子,SHAP 值为 0。
  4. 可加性(跨模型):集成模型的 SHAP 值等于各子模型 SHAP 值之和。

下面是精确 Shapley 值的暴力实现(因子少时可用,用于理解本质):

看那个 weight——它就是 Shapley 公式里给每种”子集大小”的权重,保证每个”加入位置”被平等对待。可加性不是巧合,是这套加权平均从数学上保证出来的:把所有因子的 phi 加起来,中间项会望远镜式地相互抵消,最后精确剩下 f(x) − E[f(x)]

二、瀑布图:单笔预测的贡献账单#

可加性最直接的用途,就是把一次预测画成瀑布图。看开头那张图:最左边是基准值 E[f(x)](全体样本平均预测收益 +0.002),然后每个因子像台阶一样把预测往上推(红)或往下拉(蓝),最后精确落到这个样本的预测 f(x) = +0.025。

这张图能回答风控最爱问的问题:“为什么模型看多这只股票?“答案一目了然:动量因子贡献 +0.018(推高最多)、盈利增速 +0.013、估值 PE +0.007,而波动率 −0.011、换手率 −0.004 在拖后腿。净效应是看多,主因是强动量和高盈利增速。 这不是拍脑袋的事后叙事,是从模型内部精确拆出来的贡献,加起来分毫不差地等于预测值。

def shap_waterfall_values(predict_fn, x, background):
    """返回 base value 和每个因子的 SHAP 值,验证可加性"""
    base = predict_fn(background)
    shap_vals = np.array([exact_shapley(predict_fn, x, background, j)
                          for j in range(len(x))])
    fx = predict_fn(x)
    assert abs(base + shap_vals.sum() - fx) < 1e-6, "可加性被破坏!"
    return base, shap_vals, fx
python

那行 assert 是 SHAP 区别于其它归因方法的分水岭:它保证解释是”完备”的——所有贡献加起来严丝合缝等于预测,没有一个”其它/残差”垃圾桶项。相比之下,简单的”扰动一个特征看预测变多少”这类方法,各特征的效应加起来不等于总预测,因为它忽略了特征间的交互分摊。

三、全局重要性:平均 |SHAP| vs gain 重要性#

把每个样本、每个因子的 SHAP 值取绝对值再平均,就得到全局重要性——一个因子”平均而言对预测的影响有多大”。它和树模型自带的 gain 重要性经常给出不同的排名。

SHAP 全局重要性 vs 树自带 gain 重要性

左图 SHAP 全局重要性:动量 > 盈利增速 > 波动率 > 估值 > 换手率,而且带单位(平均影响约 0.014 的预测收益),你能说出”动量平均把预测挪动 1.4 个百分点”这种有意义的话。右图 gain 重要性:排名不同(估值和换手率被抬高),而且无单位、无法横向解读。

差异的根源:gain 偏爱高基数特征。 换手率这种连续因子取值多样,树在生长时有更多机会用它分裂,累积的 gain 就虚高——哪怕它对预测的真实边际影响不大。SHAP 因为直接度量”对预测输出的贡献”,不受特征基数干扰,给出的是更诚实的排名。如果你的因子筛选是靠 gain 排名做的,很可能一直在高估那些取值花哨的因子。

四、依赖图:SHAP 顺手把交互效应也暴露了#

SHAP 还有个副产品:把单个因子的 SHAP 值 vs 它的取值画成散点,就是依赖图,能看出这个因子是怎么影响预测的——线性?非线性?有没有拐点?

SHAP 依赖图:动量贡献随取值上升,低波动时斜率更陡

横轴是动量因子的取值,纵轴是它的 SHAP 值:整体单调上升,动量越强、对预测的正贡献越大——符合直觉。但真正值钱的是颜色(用波动率着色):在同样的动量水平上,低波动(蓝点)时动量的 SHAP 斜率明显更陡。这暴露了一个交互效应——动量策略在低波动环境里更有效。这种”因子 A 的效果依赖因子 B 的取值”的规律,用线性因子模型或单变量分析根本看不出来,SHAP 依赖图顺手就画出来了。

再看蜂群图,把所有因子的 SHAP 分布叠在一起:

蜂群图:每点一个样本,横轴=贡献,颜色=因子高低

每一行是一个因子,每个点是一个样本,横轴是 SHAP 值,颜色是因子取值(红高蓝低)。动量那行:红点集中在右侧(高动量→正贡献),蓝点在左(低动量→负贡献),说明是干净的正向单调因子。波动率那行不一样:红点(高波动)几乎都在左侧,说明高波动一致地拉低预测——这是一个反向的风险因子。一张图同时告诉你每个因子的方向、强度、和取值-贡献的关系,信息密度远超一根重要性柱状图。

五、三类真实陷阱#

  1. 相关特征会”分摊”贡献,别把 SHAP 值读成因果。 SHAP 满足可加性,但当两个因子高度相关(比如”20 日动量”和”60 日动量”),它们对预测的共同贡献会被分摊到两个因子头上,每个的 SHAP 值都变小。这不代表任何一个不重要,只是贡献被摊薄了。更危险的是因果幻觉:SHAP 值大只说明”这个因子对模型预测影响大”,不等于”这个因子在真实世界里导致了收益”。模型可能学到了一个伪相关,SHAP 会忠实地把这个伪相关解释得头头是道。SHAP 解释的是模型,不是市场。

  2. 精确 Shapley 值是指数级计算量,实战必须用近似。 本文的暴力实现要遍历所有 2^n 个因子子集,因子一多就爆炸。实战里对树模型用 TreeSHAP(利用树结构把复杂度降到多项式级)、对任意模型用 KernelSHAP(用加权线性回归采样近似)。近似有代价:KernelSHAP 的采样数不够时,SHAP 值会有估计噪声,可加性只近似成立。别以为调个库就万事大吉,采样数、背景数据集的选择都会显著影响结果。

  3. 背景数据集(base value)的选择会改变整张解释。 SHAP 值是相对于”基准”定义的,而基准 E[f(x)] 取决于你喂的背景数据集。用全市场做背景、用行业内做背景、用某个时间段做背景,算出的 SHAP 值都不同——因为”缺失一个因子时用什么填充”变了。这不是 bug,是 SHAP 的定义使然:它回答的永远是”相对于这个基准,这个因子贡献了多少”。换基准,问题就变了。汇报 SHAP 结果时,必须说清背景数据集是什么,否则数字无法解读。

结语#

SHAP 把”模型为什么这么预测”从一句无法交代的”模型说的”,变成了一张可加、完备、精确等于预测值的贡献账单。它借博弈论的 Shapley 值实现了三件树模型自带重要性做不到的事:单笔预测的归因(瀑布图回答”这只股票为什么被看多”)、不受特征基数干扰的全局重要性(比 gain 更诚实的因子排名)、顺手暴露的交互效应(依赖图看出动量在低波动时更有效)。

但要守住底线:SHAP 解释的是模型,不是市场。相关因子会分摊贡献、精确计算贵到必须近似、基准选择会改变整张图——最致命的是它能把模型学到的任何伪相关都解释得天衣无缝。把它当成审视模型内部逻辑的显微镜,而不是发现因果的水晶球——用在这个位置,它是把机器学习因子从”黑箱赌博”拉回”可交代研究”的关键工具。

SHAP 因子解释:用博弈论份额拆开每个特征对预测的贡献
https://blog.halo26812.eu.org/blog/shap-factor-explanation
Author halo
Published at 2026年7月25日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨