3. PSI 计算原理:期望分布与实际分布的概念、分箱策略与计算步骤详解

群体稳定性指标(Population Stability Index, PSI)是反洗钱模型监测中最核心的量化工具。它通过比较两个分布——期望分布(通常为模型开发时的训练集或基准期)与实际分布(当前监测期的模型评分输出)——来衡量模型评分分布是否发生显著偏移。PSI 值越小,分布越稳定;PSI 值越大,模型失效或客群漂移的风险越高。

3.1 期望分布与实际分布

在反洗钱评分卡或机器学习模型中,每个样本都会获得一个风险评分(如 0~100 分或概率值)。将这些评分按一定规则分组后,每组内的样本占比就构成了一个分布。

分布类型 定义 反洗钱场景示例
期望分布 模型开发时训练集或验证集的评分分布,代表模型“预期”的客群结构。 2024年1月训练集:高风险组占 5%,中风险组占 25%,低风险组占 70%。
实际分布 当前监测周期(如本月)模型输出的评分分布,代表“当前”客群结构。 2025年1月线上评分:高风险组占 12%,中风险组占 30%,低风险组占 58%。
关键认知: 期望分布是“基准线”,实际分布是“当前线”。PSI 衡量的是两条线之间的形状差异,而非绝对分数高低。

3.2 分箱策略:等距、等频与分位数

计算 PSI 前必须对连续评分进行分箱(Binning)。分箱策略直接影响 PSI 的敏感度和稳定性。三种主流策略对比如下:

策略 原理 优点 缺点 反洗钱适用性
等距分箱 将评分区间 [min, max] 等分成 K 个宽度相同的区间。 实现简单,区间边界固定,便于跨期对比。 数据稀疏时某些箱可能样本数为 0,导致 PSI 计算异常。 适用于评分范围稳定、客群分布均匀的场景。
等频分箱 使每个箱包含大致相同数量的样本(基于期望分布)。 避免空箱,每个箱都有统计意义。 边界随期望分布变化,跨期对比时边界不固定。 适用于样本量充足、期望分布已知的常规监测。
分位数分箱 基于期望分布的分位数(如 10%、20%…90%)切分区间。 对长尾分布鲁棒,边界由数据驱动。 分位数点计算稍复杂,需保证期望分布分位数稳定。 推荐用于反洗钱评分卡,尤其是评分呈偏态分布时。
实战建议: 反洗钱模型通常推荐使用 分位数分箱(如 10 箱,每箱 10% 样本),既能保证每箱有足够样本,又能捕捉尾部高风险客群的分布变化。

3.3 PSI 分箱计算步骤详解

PSI 的计算遵循“分箱 → 计算占比 → 逐箱求和”的标准化流程。以下为完整步骤:

  1. 确定分箱边界:基于期望分布,采用选定的分箱策略(如分位数)将评分区间划分为 K 个互斥的箱。
  2. 计算期望占比:统计期望分布中落入每个箱的样本数,除以总样本数,得到 P_i(i = 1, 2, …, K)。
  3. 计算实际占比:将实际分布的样本按相同的箱边界映射到各箱,统计样本数并除以实际总样本数,得到 Q_i。
  4. 处理零值:若某箱 P_i = 0 或 Q_i = 0,通常用极小值(如 0.0001)替代,或合并相邻箱。
  5. 计算单箱贡献:对每个箱计算 (Q_i - P_i) * ln(Q_i / P_i)。
  6. 求和得到 PSI:PSI = Σ[(Q_i - P_i) * ln(Q_i / P_i)],i 从 1 到 K。
避坑提醒: 实际分布必须使用期望分布确定的箱边界,不能重新分箱。否则两个分布的分箱不一致,PSI 将失去可比性。

3.4 PSI 计算示例(10 箱分位数)

以下为一个反洗钱评分卡 PSI 计算的实际数值示例(K=10,基于期望分布分位数分箱):

箱号 评分区间 期望占比 P_i (%) 实际占比 Q_i (%) Q_i - P_i ln(Q_i / P_i) 单箱贡献
1 [0, 15) 10.0 8.5 -1.5 -0.1625 0.00244
2 [15, 28) 10.0 9.2 -0.8 -0.0834 0.00067
3 [28, 40) 10.0 10.5 0.5 0.0488 0.00024
4 [40, 52) 10.0 11.0 1.0 0.0953 0.00095
5 [52, 63) 10.0 10.8 0.8 0.0770 0.00062
6 [63, 73) 10.0 9.8 -0.2 -0.0202 0.00004
7 [73, 82) 10.0 10.2 0.2 0.0198 0.00004
8 [82, 90) 10.0 11.5 1.5 0.1398 0.00210
9 [90, 96) 10.0 12.0 2.0 0.1823 0.00365
10 [96, 100] 10.0 6.5 -3.5 -0.4308 0.01508
PSI = Σ 单箱贡献 0.02583

本例中 PSI ≈ 0.026,处于 0.1 以下,表示分布稳定,无需告警。

3.5 PSI 计算流程 SVG 图

PSI 分箱计算流程 PSI 分箱计算流程 期望分布(基准) 实际分布(当前) 选择分箱策略 确定箱边界(基于期望分布) 计算期望占比 P_i 计算实际占比 Q_i 逐箱计算 (Q_i-P_i)*ln(Q_i/P_i) 求和 → PSI 值 PSI 阈值:<0.1 稳定,0.1~0.25 需关注,>0.25 显著漂移

3.6 PSI 计算 Python 代码示例

以下代码展示了基于分位数分箱的 PSI 计算核心逻辑:

import numpy as np
import pandas as pd

def calculate_psi(expected, actual, bins=10):
    """
    计算群体稳定性指标 (PSI)
    :param expected: 期望分布评分数组
    :param actual: 实际分布评分数组
    :param bins: 分箱数(默认10箱)
    :return: PSI 值
    """
    # 1. 基于期望分布确定分位数边界
    percentiles = np.linspace(0, 100, bins + 1)
    bin_edges = np.percentile(expected, percentiles)
    # 确保边界唯一(处理极端重复值)
    bin_edges = np.unique(bin_edges)
    
    # 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. 处理零值:用极小值替代
    eps = 1e-4
    expected_pct = np.where(expected_pct == 0, eps, expected_pct)
    actual_pct = np.where(actual_pct == 0, eps, actual_pct)
    
    # 5. 计算 PSI
    psi = np.sum((actual_pct - expected_pct) * np.log(actual_pct / expected_pct))
    return psi

# 示例用法
expected_scores = np.random.beta(2, 5, 10000) * 100  # 模拟期望分布
actual_scores = np.random.beta(2.5, 4.5, 10000) * 100  # 模拟实际分布
psi_value = calculate_psi(expected_scores, actual_scores, bins=10)
print(f"PSI = {psi_value:.4f}")
代码要点: 使用 np.percentile 基于期望分布生成分位数边界;np.histogram 确保实际分布使用相同边界;零值替换为极小值 1e-4 避免对数计算错误。

3.7 期望分布 vs 实际分布对比 SVG 图

期望分布与实际分布对比(10箱) 期望分布 vs 实际分布对比(10箱分位数) 期望占比 实际占比 1 2 3 4 5 6 7 8 9 10 高 低 箱9和箱10差异最大 → 高风险客群占比上升,低风险客群占比下降

3.8 分箱策略选择决策表

场景 推荐分箱策略 箱数建议 原因
评分呈正态分布,样本量 > 10万 等距分箱 10~20 区间固定,便于业务解释
评分呈偏态分布(反洗钱常见) 分位数分箱 10 尾部高风险客群被充分捕捉
样本量较少(< 5000) 等频分箱 5~8 避免空箱,保证统计稳定性
监管要求固定阈值区间 等距分箱 按监管要求 满足合规审计需求
实战口诀: 反洗钱选分位数,样本少用等频,监管固定用等距。箱数通常取 10,不多不少刚刚好。