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 的完整计算流程:

  1. 确定分数范围:假设建模样本分数在 300-900 之间,等宽分为 10 箱,每箱宽度 60。
  2. 统计预期占比 p_i:统计建模样本落在每箱的样本数,除以总样本数。
  3. 统计实际占比 q_i:统计当前生产样本落在相同分数区间的样本数,除以当前总样本数。
  4. 逐箱计算贡献值:对每个分箱计算 (q_i - p_i) * ln(q_i / p_i)。
  5. 求和得到 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 输出的完整计算链路:

PSI 计算逻辑流程图 建模样本分数 生产样本分数 等宽分箱 (K=10) 确定分箱边界 统计每箱占比 p_i (预期) / q_i (实际) 逐箱计算贡献值 (q_i - p_i) × ln(q_i / p_i) 求和得到 PSI Σ (q_i - p_i) × ln(q_i / p_i)

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,找出贡献最大的漂移特征。