4. PSI计算步骤详解(上):预期分布与实际分布
群体稳定性指标(PSI)的核心逻辑是量化两个概率分布之间的差异。在风控建模与监控场景中,这两个分布分别对应预期分布(基准集)和实际分布(验证集/监控集)。本章将拆解PSI计算的前半部分:如何从原始数据出发,完成预期分布的分箱统计,以及实际分布的对齐计算。
4.1 预期分布(训练集/基准集)的统计与分箱
预期分布通常来自模型开发时的训练集,或业务稳定期的样本集。分箱是PSI计算的基础,其质量直接影响指标灵敏度。
4.1.1 分箱方法选择
分箱方法决定了预期分布的结构。下表对比了三种主流方法:
| 分箱方法 | 原理 | 适用场景 | 注意事项 |
|---|---|---|---|
| 等距分箱 | 将数值范围等分为k个区间 | 评分卡分数、线性指标 | 易出现空箱或样本稀疏 |
| 等频分箱 | 使每个箱内样本数大致相等 | 非均匀分布、长尾数据 | 边界值可能跨箱 |
| 最优分箱 | 基于IV/卡方等统计量自动划分 | 特征筛选、高精度监控 | 计算成本高,需避免过拟合 |
实操建议:风控评分卡PSI计算通常采用等距分箱(如每20分一个箱),便于业务解释;若样本分布极度偏斜,可改用等频分箱保证每个箱都有足够样本。
4.1.2 分箱统计步骤
完成分箱后,需统计每个箱内的样本占比,形成预期分布的概率向量。具体流程如下:
- 确定分箱边界:基于训练集数据,计算各分箱的上下界。
- 统计频数:统计落在每个箱内的样本数量。
- 计算占比:将频数除以总样本数,得到每个箱的预期占比 \( P_i \)。
- 处理边界值:将小于最小边界的样本归入第一箱,大于最大边界的归入最后一箱。
- 记录分箱映射:保存分箱边界,用于后续实际分布的对齐。
关键公式:预期占比 \( P_i = \frac{n_i}{N} \),其中 \( n_i \) 为第i箱的样本数,\( N \) 为总样本数。所有箱的 \( P_i \) 之和必须等于1。
4.2 实际分布(验证集/监控集)的统计与对齐
实际分布来自模型上线后的验证集或定期监控样本。其统计必须与预期分布的分箱结构保持一致,才能进行后续的PSI计算。
4.2.1 对齐原则
对齐的核心是使用预期分布的分箱边界,将实际样本映射到相同的箱结构中。这保证了两个分布在同一概率空间下进行比较。
- 边界复用:直接使用训练集分箱的上下界,不重新分箱。
- 样本映射:根据实际样本的数值,判断其所属的箱。
- 空箱处理:若某个箱在实际分布中无样本,该箱占比记为0。
- 溢出处理:超出预期分布边界的样本,归入最近的有效箱。
4.2.2 统计步骤
实际分布的统计与预期分布类似,但分箱边界固定:
- 加载分箱映射:读取预期分布保存的分箱边界。
- 频数统计:将实际样本按边界映射到各箱,统计频数。
- 计算占比:用实际总样本数计算每个箱的实际占比 \( Q_i \)。
- 检查零箱:标记占比为0的箱,这些箱在后续PSI计算中需特殊处理(通常跳过或加极小值)。
常见陷阱:实际分布样本量远小于训练集时,容易出现大量空箱,导致PSI值虚高。建议监控集样本量不低于训练集的10%,且每个箱至少保留5个样本。
4.3 预期分布与实际分布的对齐流程
下图展示了从原始数据到两个分布对齐的完整流程:
4.4 代码实现示例
以下Python代码展示了预期分布分箱与实际分布对齐的核心逻辑:
import pandas as pd
import numpy as np
def compute_expected_distribution(train_scores, bins=10, method='equal_width'):
"""
计算预期分布的分箱边界和占比
"""
if method == 'equal_width':
# 等距分箱
bin_edges = np.linspace(train_scores.min(), train_scores.max(), bins + 1)
elif method == 'equal_freq':
# 等频分箱
percentiles = np.linspace(0, 100, bins + 1)
bin_edges = np.percentile(train_scores, percentiles)
else:
raise ValueError("Unsupported binning method")
# 统计频数
counts, _ = np.histogram(train_scores, bins=bin_edges)
total = len(train_scores)
# 计算预期占比
expected_pct = counts / total
return bin_edges, expected_pct
def align_actual_distribution(valid_scores, bin_edges):
"""
将实际分布对齐到预期分布的分箱结构
"""
# 使用预期分布的分箱边界统计实际样本
counts, _ = np.histogram(valid_scores, bins=bin_edges)
total = len(valid_scores)
# 计算实际占比
actual_pct = counts / total
return actual_pct
# 示例用法
train_scores = np.random.normal(600, 100, 10000)
valid_scores = np.random.normal(620, 110, 2000)
bin_edges, expected_pct = compute_expected_distribution(train_scores, bins=10)
actual_pct = align_actual_distribution(valid_scores, bin_edges)
print("预期分布占比:", expected_pct)
print("实际分布占比:", actual_pct)
print("分箱边界:", bin_edges)
4.5 分箱对齐检查清单
在进入PSI计算前,建议使用以下清单进行质量检查:
| 检查项 | 预期标准 | 异常处理 |
|---|---|---|
| 分箱数量 | 5~20箱(推荐10箱) | 箱数过少降低灵敏度,过多引入噪声 |
| 空箱比例 | 实际分布空箱数 ≤ 总箱数的20% | 空箱过多需检查样本代表性或合并边界箱 |
| 边界溢出 | 溢出样本占比 ≤ 5% | 溢出过多说明监控集分布漂移严重,需重新评估分箱 |
| 占比总和 | 预期占比和 = 1,实际占比和 = 1 | 若不为1,检查浮点精度或缺失值处理 |
进阶技巧:对于评分卡模型,建议将分箱边界与评分卡的分段阈值对齐(如A/B/C/D等级),这样PSI结果可以直接与业务评级稳定性挂钩,便于向非技术团队解释。