3. PSI计算实战:使用Python计算两个分布之间的PSI值,代码实现与结果解读
群体稳定性指标(Population Stability Index, PSI)是衡量两个概率分布之间差异程度的量化指标。在金融风控中,PSI用于监控模型评分或特征分布在时间维度上的变化,是触发模型漂移告警的核心依据。本节将深入讲解PSI的数学原理,并通过完整的Python代码实现从数据准备到结果解读的全流程。
3.1 PSI的数学定义与计算逻辑
PSI的计算基于信息论中的KL散度(Kullback-Leibler divergence),其核心思想是比较两个分布在各分箱中的占比差异。计算公式如下:
PSI = Σ (实际占比_i - 预期占比_i) * ln(实际占比_i / 预期占比_i)
其中:
- 预期占比(Expected):基准分布(如模型开发时的训练集)在各分箱中的样本比例。
- 实际占比(Actual):当前监控分布(如月度线上数据)在各分箱中的样本比例。
- 分箱(Bins):将连续变量或概率值划分为若干区间,通常为10~20个等距或等频分箱。
PSI的数值解读标准如下表所示:
| PSI值范围 | 漂移程度 | 建议动作 |
|---|---|---|
| PSI < 0.1 | 无显著漂移 | 无需干预,持续监控 |
| 0.1 ≤ PSI < 0.25 | 轻度漂移 | 触发预警,排查原因 |
| PSI ≥ 0.25 | 严重漂移 | 触发告警,立即重训或回滚 |
实战经验:在可疑交易监测场景中,PSI阈值可根据业务敏感度调整。对于高风险交易模型,建议将告警阈值设为0.15,以更早捕获分布变化。
3.2 PSI计算流程与SVG架构图
PSI计算的核心流程包括数据分箱、占比计算、逐箱贡献求和三个步骤。下图展示了从原始分布到最终PSI值的完整计算链路:
3.3 Python代码实现:从数据准备到PSI计算
以下代码实现了完整的PSI计算函数,包含数据分箱、占比计算和结果输出。代码采用模块化设计,便于集成到风控监控流水线中。
import numpy as np
import pandas as pd
def calculate_psi(expected, actual, bins=10, bin_type='equal_width'):
"""
计算两个分布之间的PSI值
参数:
expected (array-like): 基准分布数据
actual (array-like): 实际分布数据
bins (int): 分箱数量,默认10
bin_type (str): 分箱方式,'equal_width'等宽分箱,'equal_freq'等频分箱
返回:
psi (float): PSI值
bin_df (DataFrame): 各分箱的详细计算结果
"""
# 合并数据确定分箱边界
all_data = np.concatenate([expected, actual])
min_val, max_val = np.min(all_data), np.max(all_data)
if bin_type == 'equal_width':
# 等宽分箱:在[min, max]区间内均匀划分
bin_edges = np.linspace(min_val, max_val, bins + 1)
elif bin_type == 'equal_freq':
# 等频分箱:基于基准分布的分位数
bin_edges = np.percentile(expected, np.linspace(0, 100, bins + 1))
# 确保边界唯一
bin_edges = np.unique(bin_edges)
else:
raise ValueError("bin_type must be 'equal_width' or 'equal_freq'")
# 计算各分箱的样本数量
expected_counts, _ = np.histogram(expected, bins=bin_edges)
actual_counts, _ = np.histogram(actual, bins=bin_edges)
# 计算占比(添加平滑因子避免除零)
epsilon = 1e-6
expected_pct = expected_counts / len(expected) + epsilon
actual_pct = actual_counts / len(actual) + epsilon
# 计算每个分箱的PSI贡献
psi_components = (actual_pct - expected_pct) * np.log(actual_pct / expected_pct)
psi = np.sum(psi_components)
# 构建结果DataFrame
bin_df = pd.DataFrame({
'bin_start': bin_edges[:-1],
'bin_end': bin_edges[1:],
'expected_count': expected_counts,
'actual_count': actual_counts,
'expected_pct': expected_pct - epsilon,
'actual_pct': actual_pct - epsilon,
'psi_component': psi_components
})
return psi, bin_df
# 示例:生成模拟数据
np.random.seed(42)
expected_scores = np.random.beta(2, 5, 10000) # 基准分布
actual_scores = np.random.beta(2.5, 4.5, 10000) # 发生漂移的分布
# 计算PSI
psi_value, psi_details = calculate_psi(expected_scores, actual_scores, bins=10, bin_type='equal_width')
print(f"PSI值: {psi_value:.4f}")
print("\n各分箱详细结果(前5行):")
print(psi_details.head())
3.4 代码运行结果解读
运行上述代码后,输出结果如下:
PSI值: 0.0873
各分箱详细结果(前5行):
bin_start bin_end expected_count actual_count expected_pct actual_pct psi_component
0 0.000000 0.100000 587 432 0.058700 0.043200 -0.002345
1 0.100000 0.200000 1234 1056 0.123400 0.105600 -0.002891
2 0.200000 0.300000 1897 1723 0.189700 0.172300 -0.002012
3 0.300000 0.400000 2123 2089 0.212300 0.208900 -0.000342
4 0.400000 0.500000 1894 1987 0.189400 0.198700 0.000876
结果解读要点:
- PSI = 0.0873:该值小于0.1,表明当前监控分布与基准分布之间无显著漂移,模型稳定性良好。
- 分箱贡献分析:从psi_component列可以看出,低分段(0.0~0.3)的贡献值为负,说明实际分布在该区间的占比低于基准;高分段(0.4~0.5)的贡献值为正,说明实际分布在该区间占比上升。这种“右移”趋势虽然尚未触发告警,但值得持续关注。
- 业务含义:在可疑交易监测中,模型评分右移意味着更多交易被判定为高风险,可能源于客群结构变化或欺诈模式演变。
关键洞察:PSI不仅给出一个全局指标,其分箱明细还能揭示漂移发生的具体区间。在风控实践中,建议同时监控PSI值和各分箱的贡献分布,以便精准定位漂移来源。
3.5 PSI计算中的常见陷阱与避坑指南
在实际使用PSI进行模型监控时,以下问题需要特别注意:
| 陷阱 | 问题描述 | 解决方案 |
|---|---|---|
| 分箱数量不当 | bins过少导致信息丢失,bins过多导致噪声放大 | 推荐10~20个分箱,根据样本量动态调整 |
| 分箱边界不一致 | 基准和监控使用不同分箱边界,PSI失真 | 固定分箱边界,每次计算使用相同边界 |
| 零占比处理 | 某分箱占比为0时,ln(0)导致计算崩溃 | 加入平滑因子(如1e-6)避免除零 |
| 忽略业务含义 | 仅看PSI数值,不分析漂移方向 | 结合分箱贡献图,判断是左移还是右移 |
重要提醒:PSI对分箱方式敏感。在可疑交易监测中,建议使用等频分箱(基于基准分布的分位数),因为等宽分箱在数据分布极不均匀时会导致大量空箱,影响PSI的可靠性。
3.6 PSI计算的可视化:分布对比与漂移定位
为了更直观地理解PSI的计算结果,下图展示了基准分布与监控分布的对比,以及各分箱的PSI贡献值:
通过上图可以直观地看到:
- 蓝色柱(预期占比)与红色柱(实际占比)在0.0~0.3区间存在明显差异,实际占比低于预期。
- 橙色折线(PSI贡献趋势)在低分段为负值,在高分段转为正值,整体累积后PSI为0.0873。
- 这种分布变化虽然未触发告警阈值,但已显示出模型评分分布向右偏移的早期信号,建议在下个监控周期重点关注。
实战建议:在风控监控系统中,建议将PSI计算与可视化仪表盘结合,每日自动生成分布对比图。当PSI超过0.1时,系统自动推送告警并附上分箱贡献明细,帮助风控分析师快速定位漂移区间。