4. PSI指标深度解析
群体稳定性指标(Population Stability Index,PSI)是授信决策引擎批次偏移诊断中最核心的量化工具。它衡量的是模型评分或关键特征在时间维度上的分布偏移程度。本章将从计算公式、分箱策略、阈值设定到拒绝样本的特殊陷阱,进行完整的技术拆解。
4.1 PSI计算公式与数学原理
PSI 基于信息论中的 KL 散度(Kullback-Leibler divergence)对称化改造,其标准计算公式如下:
PSI = Σ (P_actual_i - P_expected_i) * ln(P_actual_i / P_expected_i)
其中:
- P_expected_i:第 i 个分箱中,基准样本(如建模样本)的占比
- P_actual_i:第 i 个分箱中,实际样本(如当前申请批次)的占比
- i = 1, 2, ..., n(n 为分箱总数)
当某个分箱中 P_expected_i 或 P_actual_i 为 0 时,通常采用以下平滑处理:
if P == 0:
P = 0.0001 # 极小正数,避免 ln(0) 或除零错误
关键理解: PSI 本质是两阶段分布的加权差异度量。每个分箱的贡献 = (实际占比 - 基准占比) × 对数比率。当两个分布完全一致时,PSI = 0;差异越大,PSI 值越高。
4.2 分箱策略:等频 vs 等距 vs 自定义
分箱方式直接影响 PSI 的敏感度和稳定性。以下是三种主流策略的对比:
| 分箱策略 | 方法描述 | 适用场景 | 注意事项 |
|---|---|---|---|
| 等频分箱 | 每个分箱包含相同数量的样本(如 10 箱,每箱 10%) | 模型评分、概率输出等连续变量 | 基准分布和实际分布的分箱边界需保持一致 |
| 等距分箱 | 按数值区间等宽划分(如 0-100, 100-200) | 特征值分布均匀的场景 | 容易产生空箱,需做合并处理 |
| 自定义分箱 | 基于业务规则或风险等级划分 | 拒绝推断、特定阈值监控 | 需保证分箱在时间维度上语义一致 |
推荐实践: 在授信决策引擎中,对模型评分(如 0-1000 分)建议使用 等频分箱(10 箱)。这能保证每个分箱都有足够的样本量,避免因样本稀疏导致的 PSI 波动。
4.3 阈值设定:0.1 / 0.2 / 0.25 的行业标准
PSI 阈值是触发偏移告警的关键参数。以下是行业通用的三层阈值体系:
| PSI 范围 | 偏移等级 | 建议动作 |
|---|---|---|
| PSI < 0.1 | 无显著偏移 | 无需干预,持续监控 |
| 0.1 ≤ PSI < 0.2 | 轻度偏移 | 触发预警,排查特征或客群变化 |
| 0.2 ≤ PSI < 0.25 | 中度偏移 | 启动详细诊断,评估模型是否需要调整 |
| PSI ≥ 0.25 | 严重偏移 | 立即暂停模型使用,启动重训练或回退 |
阈值调整原则: 阈值并非一成不变。对于高价值客群或监管敏感场景,可适当收紧至 0.1 即触发深度排查;对于样本量较小的场景,可放宽至 0.25 以避免误报。关键是要建立阈值与业务影响之间的映射关系。
4.4 授信决策场景中的特殊注意事项
4.4.1 拒绝样本的 PSI 计算陷阱
这是授信场景中最容易被忽视的问题。拒绝样本(Reject Samples)的 PSI 计算存在以下陷阱:
- 样本选择偏差: 拒绝样本的评分分布天然偏向低分段,直接将其纳入 PSI 计算会人为放大偏移程度。
- 标签缺失: 拒绝样本没有真实违约标签,无法验证模型区分度,PSI 只能反映分布变化,不能反映性能变化。
- 分箱边界漂移: 拒绝样本的加入可能改变分箱边界,导致基准分布和实际分布的分箱语义不一致。
正确做法: 在计算 PSI 时,应区分"通过样本"和"拒绝样本"两个维度分别计算。对于拒绝样本,建议使用 拒绝推断(Reject Inference) 后的校正分布,而非原始分布。
4.4.2 时间窗口对齐
基准分布和实际分布的时间窗口必须对齐。常见问题包括:
- 季节性偏移: 将 1 月的基准分布与 7 月的实际分布对比,可能因季节性因素产生误报。
- 节假日效应: 节假日期间的申请客群结构与非节假日存在显著差异。
建议采用 滚动窗口对比 策略:基准窗口 = 最近 3 个月,实际窗口 = 最近 1 周,每周滚动更新。
4.4.3 多维度 PSI 监控体系
单一维度的 PSI 不足以全面反映偏移情况。建议建立以下多维监控体系:
| 监控维度 | 计算对象 | 告警阈值 |
|---|---|---|
| 模型评分 PSI | 模型输出的最终评分 | 0.1 |
| 关键特征 PSI | 收入、年龄、负债率等 | 0.15 |
| 拒绝率 PSI | 拒绝样本占比的分布 | 0.2 |
| 通过样本 PSI | 仅通过审批的样本 | 0.1 |
4.5 实战案例:PSI 误判与纠正
场景: 某消费金融公司发现模型评分 PSI 从 0.08 骤升至 0.35,触发严重告警。初步排查发现,拒绝样本占比从 15% 上升至 40%。
错误做法: 直接将所有样本(含拒绝样本)纳入 PSI 计算,得出 PSI = 0.35,判定模型失效。
正确做法:
- 分别计算通过样本 PSI = 0.09(无显著偏移)和拒绝样本 PSI = 0.42(严重偏移)。
- 进一步分析发现,拒绝样本增加是由于风控策略收紧,而非模型评分偏移。
- 结论:模型本身未偏移,但策略变化导致拒绝样本分布改变。PSI 告警应归因于策略偏移,而非模型偏移。
核心教训: 在授信决策引擎中,PSI 必须结合业务上下文解读。拒绝样本的 PSI 升高不一定是模型问题,可能是策略、客群或外部环境变化所致。始终要问:PSI 在告诉我是"什么变了"?
4.6 PSI 计算代码示例(Python)
import numpy as np
import pandas as pd
def calculate_psi(expected, actual, bins=10):
"""
计算群体稳定性指标 (PSI)
Parameters:
- expected: 基准样本的评分或特征值
- actual: 实际样本的评分或特征值
- bins: 分箱数量(等频分箱)
Returns:
- psi: PSI 值
- bin_details: 每个分箱的详细信息
"""
# 基于基准样本确定分箱边界(等频)
percentiles = np.linspace(0, 100, bins + 1)
bin_edges = np.percentile(expected, percentiles)
bin_edges[0] = -np.inf
bin_edges[-1] = np.inf
# 统计每个分箱的样本占比
expected_counts, _ = np.histogram(expected, bins=bin_edges)
actual_counts, _ = np.histogram(actual, bins=bin_edges)
expected_pct = expected_counts / len(expected)
actual_pct = actual_counts / len(actual)
# 平滑处理:避免 ln(0) 或除零
epsilon = 0.0001
expected_pct = np.clip(expected_pct, epsilon, 1 - epsilon)
actual_pct = np.clip(actual_pct, epsilon, 1 - epsilon)
# 计算 PSI
psi = np.sum((actual_pct - expected_pct) * np.log(actual_pct / expected_pct))
# 构建分箱详情
bin_details = pd.DataFrame({
'bin': range(1, bins + 1),
'expected_pct': expected_pct,
'actual_pct': actual_pct,
'contribution': (actual_pct - expected_pct) * np.log(actual_pct / expected_pct)
})
return psi, bin_details
# 使用示例
expected_scores = np.random.normal(600, 100, 10000)
actual_scores = np.random.normal(620, 110, 10000)
psi_value, details = calculate_psi(expected_scores, actual_scores, bins=10)
print(f"PSI: {psi_value:.4f}")
print(details.head())
工程化建议: 在生产环境中,建议将 PSI 计算封装为微服务,支持实时流式计算和离线批量计算两种模式。同时,建立 PSI 历史趋势库,用于识别渐进式偏移和突发性偏移。