4. 漂移检测指标体系:PSI、CSI 与模型性能衰减指标
在信用额度定价的持续监控中,仅靠单一指标无法全面捕捉模型与业务环境的偏离。本章构建一套三层漂移检测指标体系:群体稳定性指标(PSI)衡量评分分布的整体偏移,特征稳定性指标(CSI)定位偏移来源,模型性能衰减指标评估预测能力的实际损失。三者形成“检测→定位→评估”的闭环。
4.1 群体稳定性指标(PSI)
PSI 衡量模型评分(或关键风险变量)在两个时间窗口上的分布差异,是漂移检测的“第一道防线”。
| 维度 | 说明 |
|---|---|
| 定义 | PSI = Σ (实际占比 - 预期占比) × ln(实际占比 / 预期占比) |
| 预期分布 | 模型开发时或上一监控周期的评分/变量分箱占比 |
| 实际分布 | 当前监控周期的评分/变量分箱占比 |
| 阈值规则 | PSI < 0.1:无显著漂移;0.1 ≤ PSI < 0.25:需关注;PSI ≥ 0.25:严重漂移 |
实操要点:分箱数量通常取 10~20 等宽箱或等频箱。等频箱对尾部变化更敏感,等宽箱便于跨模型对比。建议同时计算两种分箱方式的 PSI,取较高值作为预警依据。
4.2 特征稳定性指标(CSI)
CSI 将 PSI 的粒度下沉到每个入模特征,回答“哪个特征在漂移”。CSI 的计算公式与 PSI 完全相同,但作用对象从模型评分切换为单个特征的分箱分布。
CSI 与 PSI 的核心区别:
- PSI:监控模型输出的整体分布偏移,是“结果指标”。
- CSI:监控每个输入特征的分布偏移,是“原因指标”。
- 联动规则:当 PSI 触发预警时,立即计算所有特征的 CSI,按贡献度排序定位漂移根因。
特征贡献度分解
单个特征对整体 PSI 的贡献度 = 该特征的 CSI 值 / 所有特征 CSI 之和。贡献度超过 20% 的特征应优先排查数据采集、业务策略或客群结构变化。
| 特征名称 | CSI 值 | 贡献度 | 建议动作 |
|---|---|---|---|
| 近 6 月收入中位数 | 0.082 | 41% | 排查收入数据源是否变更 |
| 当前负债率 | 0.045 | 22.5% | 检查授信策略是否调整 |
| 征信查询次数 | 0.028 | 14% | 观察近期信贷市场热度 |
| 其他 7 个特征 | 0.045 | 22.5% | 持续监控 |
| 合计 | 0.200 | 100% | — |
常见陷阱:不要只关注 CSI 绝对值大的特征。一个 CSI 为 0.05 但其他特征都小于 0.01 的特征,其相对贡献度可能超过 50%,同样需要深入排查。
4.3 模型性能衰减指标
分布偏移最终会传导至模型预测能力。性能衰减指标直接从模型效果层面评估漂移影响,是决策是否触发模型重训的核心依据。
核心衰减指标矩阵
| 指标类别 | 具体指标 | 衰减判定标准 | 适用场景 |
|---|---|---|---|
| 区分能力 | KS / AUC | KS 下降 > 0.05 或 AUC 下降 > 0.03 | 二分类模型(如违约预测) |
| 排序能力 | Gini 系数 / 秩相关 | Gini 下降 > 5% | 评分卡与额度定价模型 |
| 校准能力 | 校准曲线斜率 / 截距 | 斜率偏离 1.0 超过 ±0.15 | 概率输出型模型 |
| 误差分布 | RMSE / MAE | RMSE 上升 > 10% | 回归型额度定价模型 |
| 业务指标 | 坏账率 / 核准率 | 坏账率上升 > 20% 或核准率偏移 > 15% | 业务效果验证 |
衰减检测流程
- 基准期设定:取模型上线后前 3 个月或最近 6 个月作为性能基准窗口。
- 滚动窗口计算:以月为步长,计算当前窗口(通常为最近 1~3 个月)的各项性能指标。
- 衰减幅度计算:衰减幅度 = (基准期指标值 - 当前窗口指标值) / 基准期指标值。
- 触发重训条件:任意核心指标衰减幅度超过阈值,或连续两个月衰减幅度超过阈值的 70%。
4.4 指标体系落地检查清单
- 频率:PSI 和 CSI 至少每周计算一次;性能衰减指标至少每月计算一次。
- 分层:按客群(新客/老客、优质/次级)分别计算,避免整体指标掩盖局部漂移。
- 联动:PSI 触发关注级预警时,自动触发 CSI 计算和特征贡献度排序。
- 阈值调优:初始阈值参考行业标准,运行 3 个月后根据实际误报率/漏报率调整。
- 文档化:每次漂移检测结果记录至监控日志,包含 PSI/CSI 值、贡献度 Top3 特征、性能衰减幅度及处置动作。
代码示例:PSI 与 CSI 计算核心逻辑
import numpy as np
import pandas as pd
def calculate_psi(expected, actual, bins=10):
"""计算 PSI / CSI"""
# 等频分箱
expected_bins = pd.qcut(expected, q=bins, duplicates='drop')
expected_dist = expected_bins.value_counts(normalize=True).sort_index()
actual_dist = pd.qcut(actual, q=bins, duplicates='drop').value_counts(normalize=True).sort_index()
# 对齐分箱
all_bins = expected_dist.index.union(actual_dist.index)
expected_pct = expected_dist.reindex(all_bins, fill_value=1e-6)
actual_pct = actual_dist.reindex(all_bins, fill_value=1e-6)
# PSI 公式
psi = np.sum((actual_pct - expected_pct) * np.log(actual_pct / expected_pct))
return round(psi, 4)
# 示例:计算模型评分的 PSI
score_expected = np.random.normal(600, 100, 10000)
score_actual = np.random.normal(620, 110, 10000)
psi_value = calculate_psi(score_expected, score_actual)
print(f"模型评分 PSI: {psi_value}")