3、特征稳定性监控:特征分布漂移检测、PSI计算与阈值设定、特征重要性变化追踪

各位同学,咱们今天聊一个特别实在的话题——特征稳定性监控。

说实话,我在风控这行干了快十年,见过太多模型上线时表现亮眼,三个月后就开始拉胯的案例。原因呢?十有八九是特征分布变了。你想想看,用户的行为习惯、经济环境、甚至政策法规都在变,特征怎么可能一成不变?

所以,监控特征稳定性,说白了就是给模型装个「健康监测仪」。今天我把这块掰开揉碎了讲,重点讲三个东西:分布漂移检测、PSI计算与阈值、特征重要性变化追踪。

3.1 特征分布漂移检测:先看「长相」变没变

什么叫分布漂移?举个例子:

模型训练时,用户的「近3个月平均消费金额」集中在500-2000元。结果上线后,这个特征的值突然集中在3000-8000元了。嗯,这就是漂移。

我个人习惯,先做可视化对比。把训练集和当前样本的特征分布画在一起,一眼就能看出问题。

核心思路:分布漂移检测不是「有没有漂移」,而是「漂了多少」。我们要量化这个差异。

常用的检测方法有几种:

  • KS检验(Kolmogorov-Smirnov):比较两个分布的最大垂直距离。适合连续型特征。
  • 卡方检验:适合分类型特征,看频数分布是否一致。
  • JS散度(Jensen-Shannon Divergence):对称版本的KL散度,更稳定。
  • PSI(Population Stability Index):这个最常用,咱们重点讲。

我在项目中遇到过一件事:有个特征KS检验p值一直小于0.05,显示显著漂移。但业务方说「这个特征就是季节性波动,每年这时候都这样」。所以啊,统计显著不等于业务显著,一定要结合业务理解。

我的建议:别只看一个指标。我通常把PSI和KS检验一起看,PSI大于0.1且KS显著时,才标记为「需关注」。

3.2 PSI计算与阈值设定:量化漂移的「尺子」

PSI,全称Population Stability Index。说白了,就是衡量两个分布有多像。

计算公式其实不复杂:

PSI = Σ (实际占比 - 预期占比) * ln(实际占比 / 预期占比)

其中:

  • 预期占比:模型训练集或基准期的分箱占比
  • 实际占比:当前监控期的分箱占比

分箱怎么分?我一般用等频分箱,分成10箱。为什么?因为等频分箱对极端值不敏感,而且每箱样本量差不多,计算更稳定。

来看一个实际计算的例子:

分箱区间 预期占比(%) 实际占比(%) (实际-预期) ln(实际/预期) 单项PSI
[0, 500) 10.0 8.5 -1.5 -0.163 0.0024
[500, 1000) 10.0 12.3 2.3 0.207 0.0048
[1000, 2000) 10.0 9.1 -0.9 -0.094 0.0008
... ... ... ... ... ...
合计 100.0 100.0 0.0 0.035

这个例子里PSI=0.035,属于什么水平?

行业里有个通用阈值:

  • PSI < 0.1:分布稳定,无需处理
  • 0.1 ≤ PSI < 0.25:轻度漂移,需关注
  • PSI ≥ 0.25:严重漂移,必须干预

但我要说一句:这个阈值不是金科玉律。我曾经在一个消费金融项目里,把阈值调到了0.15。为什么?因为那个场景下特征波动本来就大,0.1太敏感了,天天报警没人理。你想想看,狼来了喊多了,真出问题反而没人信。

避坑指南:我曾经犯过一个错——把所有特征用同一个PSI阈值。后来发现,有些特征天生波动大(比如消费金额),有些很稳定(比如年龄)。正确的做法是:每个特征单独设定阈值,或者用百分位法动态调整。

3.3 特征重要性变化追踪:谁在「篡权」?

特征分布没变,模型效果却下降了?这时候要看特征重要性。

特征重要性变化,反映的是模型内部「权力结构」的变动。原来最重要的特征,现在可能退居二线;原来不起眼的特征,突然成了主角。这往往意味着数据背后的业务逻辑变了。

我常用的追踪方法:

  1. 定期重训练模型:用当前数据重新训练,对比新旧模型的特征重要性排序
  2. 计算重要性排名变化:用Spearman相关系数衡量排名一致性
  3. 监控SHAP值变化:SHAP值能告诉你每个特征对预测结果的贡献方向

举个例子:

# 伪代码示例:计算特征重要性排名变化
import numpy as np
from scipy.stats import spearmanr

# 旧模型特征重要性排名
old_rank = {'feature_a': 1, 'feature_b': 2, 'feature_c': 3, ...}
# 新模型特征重要性排名
new_rank = {'feature_a': 3, 'feature_b': 1, 'feature_c': 2, ...}

# 计算Spearman相关系数
old_list = [old_rank[f] for f in sorted(old_rank.keys())]
new_list = [new_rank[f] for f in sorted(old_rank.keys())]
corr, p_value = spearmanr(old_list, new_list)

print(f"特征重要性排名一致性: {corr:.3f}")
# 如果corr < 0.7,说明特征重要性发生了较大变化

我个人习惯,每个月跑一次这个分析。如果发现某个特征排名掉了超过5位,我会去查:是这个特征本身失效了?还是被其他特征替代了?还是数据采集出了问题?

关键点:特征重要性变化不一定是坏事。有时候是因为业务调整,导致某些特征变得更重要。但如果是「莫名其妙」的变化,就要警惕了。

3.4 知识体系总览

下面这张图,把特征稳定性监控的整个逻辑串起来了:

特征稳定性监控知识体系 分布漂移检测 • KS检验 • 卡方检验 • JS散度 • 可视化对比 PSI计算与阈值 • 等频分箱(10箱) • PSI < 0.1: 稳定 • 0.1-0.25: 关注 • ≥ 0.25: 干预 重要性变化追踪 • 重训练对比 • Spearman相关系数 • SHAP值监控 • 排名变化分析 输出:特征稳定性报告 + 预警信号 + 迭代建议 反馈闭环:监控结果驱动特征迭代 图:特征稳定性监控三大模块及输出流程

从图上你能看到,三个模块不是孤立的。分布漂移检测发现问题后,用PSI量化严重程度,再结合重要性变化判断影响范围。最后输出一份报告,给出预警和迭代建议。

这个闭环,我建议每个月跑一次。频率太低,问题发现不及时;频率太高,噪声太多。月频是个不错的平衡点。

3.5 实操中的几个坑

最后,分享几个我踩过的坑:

  • 坑一:分箱方式影响PSI结果。等频分箱和等距分箱算出来的PSI可能差一倍。我建议统一用等频10箱,保持一致性。
  • 坑二:忽略样本量变化。如果当前监控期的样本量只有训练集的十分之一,PSI会虚高。这时候要结合置信区间看。
  • 坑三:只看PSI不看方向。PSI只告诉你「变了」,不告诉你「往哪变了」。我习惯同时看均值、方差的变化方向。
  • 坑四:特征重要性变化被「平均」掩盖。整体排名变化不大,但某个子群体里特征重要性完全变了。所以,分层监控很重要。

一个小技巧:我每次做特征稳定性监控,都会把结果做成一个dashboard。PSI用热力图展示,特征重要性用条形图对比。这样一眼就能看出哪些特征「出问题了」。

好了,特征稳定性监控这块就讲到这里。记住一句话:特征稳定,模型才稳定。别等到模型效果崩了才去查原因,那时候已经晚了。


公众号:蓝海资料掘金营,微信deep3321