4. PSI计算公式详解:期望分布与实际分布、分箱方法、PSI分段计算与汇总
群体稳定性指标(Population Stability Index, PSI)是评分卡监控中最核心的量化工具,用于衡量模型评分在时间维度上的分布偏移程度。本节将从公式拆解、分箱策略、分段计算到汇总输出,完整解析PSI的数学原理与工程实现。
4.1 PSI标准公式拆解
PSI的核心思想是比较两个分布——期望分布(Expected Distribution)与实际分布(Actual Distribution)——在每个分箱上的比例差异,并通过对数加权求和得到单一指标。标准公式如下:
PSI = Σ (Actual_i - Expected_i) × ln(Actual_i / Expected_i)
其中:
- i = 第i个分箱(共N个分箱)
- Expected_i = 期望样本在第i箱中的占比(开发样本/基准样本)
- Actual_i = 实际样本在第i箱中的占比(监控样本/当前样本)
- ln = 自然对数
公式的直观含义:当实际分布与期望分布完全一致时,每个分箱的差值为0,PSI=0;差异越大,PSI值越高。该公式本质上是离散化后的KL散度对称化形式。
关键理解:PSI并非直接比较分数值,而是比较分数落入各分箱的比例。因此分箱方式直接影响PSI的敏感度和稳定性。
4.2 期望分布与实际分布的定义
| 分布类型 | 别名 | 数据来源 | 用途 |
|---|---|---|---|
| 期望分布 | 基准分布、开发分布 | 模型开发时的训练集/验证集 | 作为稳定性的参考基准 |
| 实际分布 | 监控分布、当前分布 | 模型上线后的月度/季度样本 | 与基准对比,检测漂移 |
注意事项:
- 期望分布通常使用开发样本的评分分布,而非全量拒绝样本(避免样本偏差)。
- 实际分布必须与期望分布使用相同的分箱边界,否则PSI计算无效。
- 当实际分布中出现某分箱比例为0时,公式中的ln(0)无定义,需做平滑处理(如加极小值ε=1e-6)。
4.3 分箱方法详解
分箱是PSI计算的前置步骤,分箱策略直接影响PSI的数值大小和业务解释性。常用的分箱方法有三种:
| 分箱方法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 等距分箱 | 按分数区间等宽划分(如每50分一箱) | 边界固定,易于跨时间对比 | 样本分布不均时可能出现空箱 | 评分卡分数范围已知且稳定 |
| 等频分箱 | 按期望分布的百分位数划分,每箱样本量相等 | 避免空箱,统计稳定性好 | 边界随基准分布变化,跨期对比不便 | 开发样本分布不均匀时首选 |
| 业务分箱 | 按评分卡等级边界划分(如A/B/C/D/E级) | 业务可解释性强,直接对应风险等级 | 分箱数少,可能降低PSI敏感度 | 面向业务汇报的监控报告 |
行业实践建议:评分卡监控通常采用等频分箱(10箱)作为默认方案。10箱能在统计稳定性和敏感度之间取得较好平衡。若样本量较小(<1000),可减少至5箱;若样本量极大(>50000),可增加至20箱。
4.4 PSI分段计算与汇总
PSI的计算过程可以分解为四个步骤,每一步都有明确的数学操作和工程注意事项。
4.4.1 计算步骤
- 确定分箱边界:基于期望分布,使用等频法(或其他方法)确定N个分箱的边界值。
- 统计各箱占比:分别统计期望样本和实际样本在各箱中的数量,并计算占比。
- 计算分段PSI:对每个分箱,计算 (Actual_i - Expected_i) × ln(Actual_i / Expected_i)。
- 汇总总PSI:将所有分箱的分段PSI求和,得到最终PSI值。
4.4.2 计算示例(10箱等频)
| 分箱 | 分数区间 | 期望占比(%) | 实际占比(%) | 差值 | ln(实际/期望) | 分段PSI |
|---|---|---|---|---|---|---|
| 1 | 300-420 | 10.0 | 12.5 | +2.5 | 0.2231 | 0.00558 |
| 2 | 420-480 | 10.0 | 11.0 | +1.0 | 0.0953 | 0.00095 |
| 3 | 480-530 | 10.0 | 10.5 | +0.5 | 0.0488 | 0.00024 |
| 4 | 530-570 | 10.0 | 10.0 | 0.0 | 0.0000 | 0.00000 |
| 5 | 570-610 | 10.0 | 9.5 | -0.5 | -0.0513 | 0.00026 |
| 6 | 610-650 | 10.0 | 9.0 | -1.0 | -0.1054 | 0.00105 |
| 7 | 650-690 | 10.0 | 8.5 | -1.5 | -0.1625 | 0.00244 |
| 8 | 690-730 | 10.0 | 8.0 | -2.0 | -0.2231 | 0.00446 |
| 9 | 730-780 | 10.0 | 7.5 | -2.5 | -0.2877 | 0.00719 |
| 10 | 780-900 | 10.0 | 13.5 | +3.5 | 0.3001 | 0.01050 |
| 总PSI = | 0.03267 | |||||
⚠ 计算陷阱:当实际占比为0时,ln(0)无定义。解决方案:对每个分箱的占比做平滑处理,常用方法为加极小值(如1e-6)或使用贝叶斯平滑。另一种方案是合并空箱到相邻分箱,但需保持期望分布和实际分布的分箱结构一致。
4.4.3 PSI分段贡献分析
总PSI由各分箱的分段PSI累加得到,但不同分箱对总PSI的贡献度差异很大。通过分析分段贡献,可以定位漂移发生的具体分数区间。
4.5 PSI计算代码实现
以下为Python实现PSI计算的完整函数,包含平滑处理和分箱逻辑:
import numpy as np
import pandas as pd
def calculate_psi(expected, actual, bins=10, method='equal_freq', epsilon=1e-6):
"""
计算群体稳定性指标(PSI)
Parameters:
-----------
expected : array-like
期望分布(开发样本)的评分数据
actual : array-like
实际分布(监控样本)的评分数据
bins : int
分箱数量
method : str
分箱方法:'equal_freq'(等频), 'equal_width'(等距)
epsilon : float
平滑因子,防止log(0)
Returns:
--------
psi : float
总PSI值
psi_table : DataFrame
各分箱的详细计算结果
"""
# 1. 基于期望分布确定分箱边界
if method == 'equal_freq':
# 等频分箱:按百分位数
percentiles = np.linspace(0, 100, bins + 1)[1:-1]
bin_edges = np.percentile(expected, percentiles)
bin_edges = np.concatenate(([-np.inf], bin_edges, [np.inf]))
elif method == 'equal_width':
# 等距分箱
min_val, max_val = expected.min(), expected.max()
bin_edges = np.linspace(min_val, max_val, bins + 1)
bin_edges[0] = -np.inf
bin_edges[-1] = np.inf
else:
raise ValueError("method must be 'equal_freq' or 'equal_width'")
# 2. 统计各箱占比
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)
# 3. 平滑处理(防止log(0))
expected_pct = np.clip(expected_pct, epsilon, 1 - epsilon)
actual_pct = np.clip(actual_pct, epsilon, 1 - epsilon)
# 4. 计算分段PSI
psi_segment = (actual_pct - expected_pct) * np.log(actual_pct / expected_pct)
# 5. 汇总总PSI
psi_total = np.sum(psi_segment)
# 6. 构建结果表格
psi_table = pd.DataFrame({
'bin': range(1, bins + 1),
'expected_pct': expected_pct,
'actual_pct': actual_pct,
'diff': actual_pct - expected_pct,
'log_ratio': np.log(actual_pct / expected_pct),
'segment_psi': psi_segment
})
return psi_total, psi_table
# 使用示例
# psi_value, psi_df = calculate_psi(expected_scores, actual_scores, bins=10)
# print(f"总PSI: {psi_value:.5f}")
# print(psi_df)
工程要点:
- 分箱边界必须基于期望分布确定,并在整个监控周期内固定不变。
- 平滑因子epsilon不宜过大(建议1e-6 ~ 1e-4),过大会压低真实漂移信号。
- 当样本量差异较大时(如期望样本10万,实际样本5000),建议对实际占比做置信区间校正,避免小样本波动导致PSI虚高。
4.6 PSI阈值与业务解读
| PSI范围 | 漂移程度 | 业务含义 | 建议行动 |
|---|---|---|---|
| < 0.10 | 无显著漂移 | 模型分布稳定,客群结构未发生明显变化 | 常规监控,无需干预 |
| 0.10 ~ 0.25 | 轻度漂移 | 分布出现一定偏移,需关注趋势 | 增加监控频率,分析分段贡献 |
| > 0.25 | 显著漂移 | 分布发生结构性变化,模型可能失效 | 立即触发告警,启动模型复核与重开发 |
行业经验:上述阈值是通用参考值,实际应用中应根据业务场景调整。例如,在贷前审批场景中,PSI阈值可放宽至0.15/0.30;在贷中管理场景中,由于客群变化更敏感,建议收紧至0.05/0.15。关键原则是:阈值设定应使告警频率与业务承受能力匹配。