3. PSI 计算步骤:分箱策略、实际分布与期望分布的计算、PSI 值的聚合与解读

PSI(Population Stability Index,群体稳定性指标)是衡量特征分布随时间或样本集变化程度的量化工具。其核心思想是将连续分布离散化为若干区间(分箱),然后比较每个区间内实际样本占比与期望样本占比的差异。以下从分箱策略、分布计算、聚合解读三个环节展开。

3.1 分箱策略:离散化的关键选择

分箱是 PSI 计算的第一步,直接影响指标的敏感性和稳定性。常见的分箱策略包括等距分箱、等频分箱和基于业务规则的分箱。

分箱策略 方法描述 适用场景 注意事项
等距分箱 将特征值范围均匀划分为 k 个区间,每个区间宽度相同 特征分布均匀、无极端异常值 若数据分布偏斜,部分箱可能样本极少,导致 PSI 不稳定
等频分箱 按样本数量均分,使每个箱包含大致相同的样本数 分布偏斜、长尾特征 箱边界可能随分布变化而移动,需固定边界用于后续监控
业务规则分箱 基于业务含义或监管要求划分,如信用评分中的“好/中/差”分段 有明确业务分段标准 需定期验证业务分段是否仍合理
实践建议:在模型监控中,通常使用等频分箱(如 10 箱)作为默认策略,并在模型开发阶段固定分箱边界。后续监控时,即使实际分布发生变化,仍使用相同的边界计算占比,以保证 PSI 的可比性。

3.2 实际分布与期望分布的计算

在分箱完成后,需要分别计算每个箱中样本在期望分布(通常为训练集或基准期)和实际分布(当前监控期)中的占比。

计算步骤:

  1. 确定分箱边界:基于期望数据集(如训练集)确定每个箱的上下界。
  2. 统计期望分布:统计期望数据集中落入每个箱的样本数 \( n_{e,i} \),计算占比 \( p_{e,i} = n_{e,i} / N_e \)。
  3. 统计实际分布:使用相同的分箱边界,统计实际数据集中落入每个箱的样本数 \( n_{a,i} \),计算占比 \( p_{a,i} = n_{a,i} / N_a \)。
  4. 处理零值:若某箱在期望或实际分布中占比为 0,需进行平滑处理(如加极小值 0.0001),避免对数计算无定义。
公式:每个箱的 PSI 贡献 = \( (p_{a,i} - p_{e,i}) \times \ln(p_{a,i} / p_{e,i}) \)

以下 SVG 展示了从原始数据到分布占比的计算流程:

实际分布与期望分布计算流程 原始数据 分箱(固定边界) 期望分布占比 实际分布占比 PSI 聚合计算 关键点 • 分箱边界固定不变 • 占比 = 箱内样本数 / 总样本数 • 零占比需平滑处理

3.3 PSI 值的聚合与解读

PSI 的最终值是对所有分箱贡献的求和。聚合公式如下:

PSI = Σ[(p_a,i - p_e,i) * ln(p_a,i / p_e,i)]   (i = 1, 2, ..., k)

其中:

  • p_a,i:实际分布中第 i 箱的样本占比
  • p_e,i:期望分布中第 i 箱的样本占比
  • k:分箱总数

解读标准:

PSI 值范围 稳定性判断 建议行动
PSI < 0.1 稳定 无需干预,持续监控
0.1 ≤ PSI < 0.25 轻度漂移 检查漂移方向,评估是否需要模型更新
PSI ≥ 0.25 显著漂移 立即排查原因,考虑模型重训或特征替换
注意:PSI 阈值并非绝对,需结合业务场景调整。例如,在信贷风控中,PSI > 0.2 通常触发预警;而在营销响应模型中,由于分布波动较大,阈值可放宽至 0.3。此外,PSI 对分箱数量敏感,建议在模型开发阶段通过交叉验证确定最优分箱数。

以下 SVG 展示了 PSI 聚合计算与解读的完整逻辑:

PSI 聚合与解读流程 各箱 PSI 贡献 PSI = Σ 贡献 阈值判断 行动决策 解读示例 • PSI=0.08 → 稳定 • PSI=0.18 → 轻度漂移 • PSI=0.35 → 显著漂移 • 需结合业务容忍度调整

3.4 计算示例(Python 伪代码)

以下代码展示了 PSI 计算的核心逻辑,使用等频分箱策略:

import numpy as np

def calculate_psi(expected, actual, bins=10):
    """
    计算 PSI 值
    :param expected: 期望分布数据(一维数组)
    :param actual: 实际分布数据(一维数组)
    :param bins: 分箱数
    :return: PSI 值
    """
    # 1. 基于期望数据确定分箱边界(等频分箱)
    percentiles = np.linspace(0, 100, bins + 1)
    bin_edges = np.percentile(expected, percentiles)
    
    # 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. 平滑处理(避免 log(0))
    epsilon = 1e-4
    expected_pct = np.clip(expected_pct, epsilon, 1 - epsilon)
    actual_pct = np.clip(actual_pct, epsilon, 1 - epsilon)
    
    # 5. 计算 PSI
    psi = np.sum((actual_pct - expected_pct) * np.log(actual_pct / expected_pct))
    
    return psi

# 示例调用
expected_data = np.random.normal(0, 1, 10000)
actual_data = np.random.normal(0.2, 1.1, 10000)
psi_value = calculate_psi(expected_data, actual_data, bins=10)
print(f"PSI = {psi_value:.4f}")
关键点总结:
  • 分箱策略决定了 PSI 的粒度,等频分箱是默认推荐。
  • 期望分布与实际分布使用相同的分箱边界,确保可比性。
  • PSI 聚合是对数比率与差异的加权和,值越大表示分布偏移越严重。
  • 解读时需结合业务阈值,避免机械套用 0.1/0.25 标准。