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:自然对数

公式的推导逻辑:

  1. 分箱对齐:将模型评分(如信用评分、概率值)划分为 K 个区间,确保预期分布和实际分布使用相同的分箱边界。
  2. 计算占比:分别统计每个分箱中预期样本数和实际样本数,除以各自总样本数得到占比。
  3. 逐箱计算:对每个分箱,计算 (实际占比 - 预期占比) × ln(实际占比 / 预期占比)。
  4. 求和:将所有分箱的结果累加,得到最终的 PSI 值。
关键理解: PSI 值越大,说明实际分布与预期分布的差异越大。当 PSI > 0.25 时,通常认为模型发生了显著漂移,需要立即告警并排查原因。

2.2 分箱策略

分箱是 PSI 计算的前置步骤,直接影响指标的敏感性和稳定性。常用的分箱策略有两种:

分箱策略 定义 优点 缺点 适用场景
等频分箱 每个分箱包含大致相等的样本数量 避免空箱或样本过少;对分布变化敏感 分箱边界随数据分布变化,跨期可比性略差 样本量充足、分布较稳定的场景
等距分箱 每个分箱的评分区间宽度相等 分箱边界固定,跨期可比性强 可能出现空箱或样本极少的箱子,导致计算不稳定 评分范围已知、需要长期对比的场景
实践建议: 在模型监控中,推荐优先使用 等频分箱(通常分 10 箱),因为它能保证每个箱子都有足够的样本支撑统计意义。如果业务要求固定评分阈值(如信用评分 600 分以下为高风险),则使用等距分箱。

2.3 PSI值的计算步骤详解

以下通过一个完整示例,展示 PSI 的手动计算过程:

步骤 1:确定分箱边界

假设模型评分范围为 0~100,使用等频分箱策略分为 5 箱。基于训练集(预期分布)的 1000 个样本,确定分箱边界:

分箱编号评分区间预期样本数预期占比 (Expected_i)
1[0, 40)2000.20
2[40, 55)2000.20
3[55, 70)2000.20
4[70, 85)2000.20
5[85, 100]2000.20

步骤 2:统计实际分布

从当前生产环境抽取 800 个样本,使用相同的分箱边界统计:

分箱编号评分区间实际样本数实际占比 (Actual_i)
1[0, 40)2400.30
2[40, 55)1600.20
3[55, 70)1200.15
4[70, 85)1600.20
5[85, 100]1200.15

步骤 3:逐箱计算 PSI 分量

对每个分箱,计算 (Actual_i - Expected_i) × ln(Actual_i / Expected_i):

分箱Expected_iActual_iActual_i - Expected_iln(Actual_i / Expected_i)分量值
10.200.30+0.10ln(1.5) ≈ 0.40550.04055
20.200.200.00ln(1.0) = 0.00000.00000
30.200.15-0.05ln(0.75) ≈ -0.28770.01439
40.200.200.00ln(1.0) = 0.00000.00000
50.200.15-0.05ln(0.75) ≈ -0.28770.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 值的完整计算流程:

PSI 计算流程 原始评分数据 分箱策略 预期分布 (Expected) 实际分布 (Actual) 逐箱计算分量 求和 → PSI 值 分箱策略选择 等频分箱:每箱样本数相等 等距分箱:每箱区间宽度相等 推荐:等频分箱(10箱) PSI 告警阈值 PSI < 0.10:无漂移 0.10 ≤ PSI ≤ 0.25:轻度漂移 PSI > 0.25:显著漂移 → 告警

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 超过阈值时,自动触发告警通知(邮件、钉钉、企业微信等),同时记录当前分箱的详细分布数据,便于后续根因分析。