PSI 基础:群体稳定性指标 (PSI) 的数学定义与计算逻辑
群体稳定性指标(Population Stability Index, PSI)是风控模型监控中最核心的量化工具,用于衡量模型评分(或特征分布)在时间维度上的偏移程度。PSI 通过比较两个样本集(通常为建模样本集与当前生产样本集)的分布差异,判断模型是否仍适用于当前环境。
1. PSI 的数学定义
PSI 基于信息论中的 KL 散度(Kullback-Leibler Divergence)对称化改进而来。其核心思想是将连续型评分或概率值离散化为 K 个等宽或等频分箱,然后逐箱计算实际占比与预期占比的差异,并加权求和。
设建模样本(预期分布)在第 i 个分箱中的占比为 \( p_i \),当前生产样本(实际分布)在第 i 个分箱中的占比为 \( q_i \),则 PSI 定义为:
PSI = Σ ( (q_i - p_i) * ln(q_i / p_i) ) for i = 1 to K
其中:
- p_i:预期占比(通常来自建模样本或基准期)
- q_i:实际占比(通常来自监控期或当前生产数据)
- K:分箱总数(通常取 10 或 20)
- ln:自然对数
关键理解:当 q_i = p_i 时,ln(q_i/p_i) = 0,该箱贡献为 0,PSI = 0 表示分布完全一致。PSI 越大,分布偏移越严重。
2. 计算逻辑与分箱策略
PSI 的计算并非直接对原始分数进行,而是需要经过 分箱 → 统计占比 → 逐箱计算 → 求和 四个步骤。分箱策略直接影响 PSI 的敏感度和稳定性。
| 分箱策略 | 方法描述 | 适用场景 | 注意事项 |
|---|---|---|---|
| 等宽分箱 | 将分数范围均匀划分为 K 个区间 | 评分卡分数范围固定(如 300-900) | 可能出现空箱,需处理 ln(0) 异常 |
| 等频分箱 | 按样本量均分,每箱样本数大致相等 | 概率型输出(如违约概率) | 分箱边界随分布变化,需固定边界 |
| 业务分箱 | 按业务阈值(如拒绝线、关注线)划分 | 业务规则明确的场景 | 分箱数较少,可能遗漏局部偏移 |
避坑提示:当某个分箱中 p_i = 0 或 q_i = 0 时,ln(q_i/p_i) 无定义。常用处理方式:对 p_i 和 q_i 做微小平滑(如加 0.0001),或将该箱与相邻箱合并。
3. 计算步骤详解
以下以等宽分箱(K=10)为例,展示 PSI 的完整计算流程:
- 确定分数范围:假设建模样本分数在 300-900 之间,等宽分为 10 箱,每箱宽度 60。
- 统计预期占比 p_i:统计建模样本落在每箱的样本数,除以总样本数。
- 统计实际占比 q_i:统计当前生产样本落在相同分数区间的样本数,除以当前总样本数。
- 逐箱计算贡献值:对每个分箱计算 (q_i - p_i) * ln(q_i / p_i)。
- 求和得到 PSI:将所有分箱的贡献值相加。
# Python 示例:PSI 计算函数
import numpy as np
def calculate_psi(expected, actual, bins=10):
"""
expected: 预期样本分数数组(建模样本)
actual: 实际样本分数数组(监控样本)
bins: 分箱数
"""
# 1. 确定分箱边界(基于预期样本的等宽分箱)
min_val, max_val = min(expected), max(expected)
bin_edges = np.linspace(min_val, max_val, bins + 1)
# 2. 统计预期占比
expected_counts, _ = np.histogram(expected, bins=bin_edges)
expected_pct = expected_counts / len(expected)
# 3. 统计实际占比
actual_counts, _ = np.histogram(actual, bins=bin_edges)
actual_pct = actual_counts / len(actual)
# 4. 平滑处理,避免 ln(0)
expected_pct = np.clip(expected_pct, 0.0001, 1)
actual_pct = np.clip(actual_pct, 0.0001, 1)
# 5. 计算 PSI
psi = np.sum((actual_pct - expected_pct) * np.log(actual_pct / expected_pct))
return psi
# 示例调用
expected_scores = np.random.normal(600, 100, 10000)
actual_scores = np.random.normal(620, 120, 10000)
psi_value = calculate_psi(expected_scores, actual_scores)
print(f"PSI = {psi_value:.4f}")
4. PSI 计算流程可视化
下图展示了从原始分数输入到最终 PSI 输出的完整计算链路:
5. PSI 阈值与业务解读
在实际风控应用中,PSI 的数值大小对应不同的业务动作:
| PSI 范围 | 偏移程度 | 建议动作 |
|---|---|---|
| PSI < 0.1 | 无显著偏移 | 模型可继续使用,定期监控 |
| 0.1 ≤ PSI < 0.25 | 轻度偏移 | 需排查原因,关注特征分布变化 |
| PSI ≥ 0.25 | 严重偏移 | 立即停止模型,启动重训或重构 |
行业经验:在信贷风控中,评分卡模型的 PSI 通常要求控制在 0.1 以内。若 PSI 超过 0.25,往往意味着客群结构发生根本性变化,模型已无法有效区分好坏客户。
6. PSI 与特征级 PSI 的对比
除了模型级别的 PSI,风控实践中还经常计算 特征级 PSI(每个入模特征单独计算),用于定位偏移来源:
| 对比维度 | 模型级 PSI | 特征级 PSI |
|---|---|---|
| 计算对象 | 模型输出分数或概率 | 单个特征的取值分布 |
| 监控频率 | 月度/季度 | 周度/月度 |
| 业务含义 | 模型整体稳定性 | 特征分布是否发生漂移 |
| 预警作用 | 触发模型重训决策 | 辅助定位漂移根因 |
特征级 PSI 的计算逻辑与模型级完全一致,只是将输入从“模型分数”替换为“特征值”。当模型级 PSI 报警时,通常需要进一步查看各特征的 PSI,找出贡献最大的漂移特征。