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 的计算遵循“分箱 → 计算占比 → 逐箱求和”的标准化流程。以下为完整步骤:
- 确定分箱边界:基于期望分布,采用选定的分箱策略(如分位数)将评分区间划分为 K 个互斥的箱。
- 计算期望占比:统计期望分布中落入每个箱的样本数,除以总样本数,得到
P_i(i = 1, 2, …, K)。 - 计算实际占比:将实际分布的样本按相同的箱边界映射到各箱,统计样本数并除以实际总样本数,得到
Q_i。 - 处理零值:若某箱
P_i = 0或Q_i = 0,通常用极小值(如 0.0001)替代,或合并相邻箱。 - 计算单箱贡献:对每个箱计算
(Q_i - P_i) * ln(Q_i / P_i)。 - 求和得到 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 图
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 图
3.8 分箱策略选择决策表
| 场景 | 推荐分箱策略 | 箱数建议 | 原因 |
|---|---|---|---|
| 评分呈正态分布,样本量 > 10万 | 等距分箱 | 10~20 | 区间固定,便于业务解释 |
| 评分呈偏态分布(反洗钱常见) | 分位数分箱 | 10 | 尾部高风险客群被充分捕捉 |
| 样本量较少(< 5000) | 等频分箱 | 5~8 | 避免空箱,保证统计稳定性 |
| 监管要求固定阈值区间 | 等距分箱 | 按监管要求 | 满足合规审计需求 |
实战口诀: 反洗钱选分位数,样本少用等频,监管固定用等距。箱数通常取 10,不多不少刚刚好。