2. PSI的数学原理
PSI(Population Stability Index,群体稳定性指数)是衡量模型评分分布随时间变化程度的量化指标。其核心思想是:如果模型在训练集(预期分布)上的评分分布与当前生产环境(实际分布)的评分分布发生显著偏离,则说明模型可能已失效或数据环境已发生漂移。
2.1 PSI的公式推导
PSI 的数学定义基于信息论中的 KL散度(Kullback-Leibler divergence) 的对称化形式。对于两个离散概率分布 P(预期分布)和 Q(实际分布),PSI 的计算公式为:
PSI = Σ ( (Actual_i - Expected_i) * ln(Actual_i / Expected_i) )
其中:
- Expected_i:第 i 个分箱中预期样本占比(通常来自训练集或基准期)
- Actual_i:第 i 个分箱中实际样本占比(来自当前监控期)
- ln:自然对数
公式的推导逻辑:
- 分箱对齐:将模型评分(如信用评分、概率值)划分为 K 个区间,确保预期分布和实际分布使用相同的分箱边界。
- 计算占比:分别统计每个分箱中预期样本数和实际样本数,除以各自总样本数得到占比。
- 逐箱计算:对每个分箱,计算 (实际占比 - 预期占比) × ln(实际占比 / 预期占比)。
- 求和:将所有分箱的结果累加,得到最终的 PSI 值。
关键理解: PSI 值越大,说明实际分布与预期分布的差异越大。当 PSI > 0.25 时,通常认为模型发生了显著漂移,需要立即告警并排查原因。
2.2 分箱策略
分箱是 PSI 计算的前置步骤,直接影响指标的敏感性和稳定性。常用的分箱策略有两种:
| 分箱策略 | 定义 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 等频分箱 | 每个分箱包含大致相等的样本数量 | 避免空箱或样本过少;对分布变化敏感 | 分箱边界随数据分布变化,跨期可比性略差 | 样本量充足、分布较稳定的场景 |
| 等距分箱 | 每个分箱的评分区间宽度相等 | 分箱边界固定,跨期可比性强 | 可能出现空箱或样本极少的箱子,导致计算不稳定 | 评分范围已知、需要长期对比的场景 |
实践建议: 在模型监控中,推荐优先使用 等频分箱(通常分 10 箱),因为它能保证每个箱子都有足够的样本支撑统计意义。如果业务要求固定评分阈值(如信用评分 600 分以下为高风险),则使用等距分箱。
2.3 PSI值的计算步骤详解
以下通过一个完整示例,展示 PSI 的手动计算过程:
步骤 1:确定分箱边界
假设模型评分范围为 0~100,使用等频分箱策略分为 5 箱。基于训练集(预期分布)的 1000 个样本,确定分箱边界:
| 分箱编号 | 评分区间 | 预期样本数 | 预期占比 (Expected_i) |
|---|---|---|---|
| 1 | [0, 40) | 200 | 0.20 |
| 2 | [40, 55) | 200 | 0.20 |
| 3 | [55, 70) | 200 | 0.20 |
| 4 | [70, 85) | 200 | 0.20 |
| 5 | [85, 100] | 200 | 0.20 |
步骤 2:统计实际分布
从当前生产环境抽取 800 个样本,使用相同的分箱边界统计:
| 分箱编号 | 评分区间 | 实际样本数 | 实际占比 (Actual_i) |
|---|---|---|---|
| 1 | [0, 40) | 240 | 0.30 |
| 2 | [40, 55) | 160 | 0.20 |
| 3 | [55, 70) | 120 | 0.15 |
| 4 | [70, 85) | 160 | 0.20 |
| 5 | [85, 100] | 120 | 0.15 |
步骤 3:逐箱计算 PSI 分量
对每个分箱,计算 (Actual_i - Expected_i) × ln(Actual_i / Expected_i):
| 分箱 | Expected_i | Actual_i | Actual_i - Expected_i | ln(Actual_i / Expected_i) | 分量值 |
|---|---|---|---|---|---|
| 1 | 0.20 | 0.30 | +0.10 | ln(1.5) ≈ 0.4055 | 0.04055 |
| 2 | 0.20 | 0.20 | 0.00 | ln(1.0) = 0.0000 | 0.00000 |
| 3 | 0.20 | 0.15 | -0.05 | ln(0.75) ≈ -0.2877 | 0.01439 |
| 4 | 0.20 | 0.20 | 0.00 | ln(1.0) = 0.0000 | 0.00000 |
| 5 | 0.20 | 0.15 | -0.05 | ln(0.75) ≈ -0.2877 | 0.01439 |
步骤 4:求和得到 PSI
PSI = 0.04055 + 0.00000 + 0.01439 + 0.00000 + 0.01439 = 0.06933
注意: 当某个分箱的实际占比为 0 时,ln(0) 无定义。此时通常采用平滑处理:给每个分箱的样本数加一个极小值(如 0.5),或将该分箱与相邻分箱合并。同样,如果预期占比为 0,也需要类似处理。
2.4 PSI 计算流程可视化
下图展示了从原始评分数据到最终 PSI 值的完整计算流程:
2.5 PSI 计算代码示例
以下 Python 代码展示了 PSI 的完整计算过程,可直接用于模型监控脚本:
import numpy as np
def calculate_psi(expected, actual, bins=10, strategy='equal_freq'):
"""
计算群体稳定性指数 (PSI)
参数:
expected: 预期分布(训练集)的评分数组
actual: 实际分布(当前生产)的评分数组
bins: 分箱数量,默认10
strategy: 分箱策略,'equal_freq'(等频)或 'equal_width'(等距)
返回:
psi_value: PSI 值
bin_edges: 分箱边界
"""
# 步骤1:确定分箱边界
if strategy == 'equal_freq':
# 等频分箱:基于预期分布的分位数
bin_edges = np.percentile(expected, np.linspace(0, 100, bins + 1))
else:
# 等距分箱:基于评分范围
bin_edges = np.linspace(np.min(expected), np.max(expected), bins + 1)
# 步骤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:平滑处理,避免 ln(0)
epsilon = 1e-6
expected_pct = np.clip(expected_pct, epsilon, 1 - epsilon)
actual_pct = np.clip(actual_pct, epsilon, 1 - epsilon)
# 步骤4:逐箱计算并求和
psi_value = np.sum((actual_pct - expected_pct) * np.log(actual_pct / expected_pct))
return psi_value, bin_edges
# 示例用法
expected_scores = np.random.normal(70, 15, 10000) # 训练集评分
actual_scores = np.random.normal(65, 18, 8000) # 生产环境评分
psi, edges = calculate_psi(expected_scores, actual_scores, bins=10, strategy='equal_freq')
print(f"PSI 值: {psi:.4f}")
# 判断漂移程度
if psi < 0.10:
print("状态:无漂移,模型稳定")
elif psi <= 0.25:
print("状态:轻度漂移,建议关注")
else:
print("状态:显著漂移,立即告警!")
工程实践: 在生产环境中,建议将 PSI 计算封装为定时任务(如每天/每周执行一次),并将结果写入监控数据库。当 PSI 超过阈值时,自动触发告警通知(邮件、钉钉、企业微信等),同时记录当前分箱的详细分布数据,便于后续根因分析。