2、PSI计算原理:期望分布与实际分布的对数比值

群体稳定性指标(Population Stability Index, PSI)是量化风控中衡量两个分布之间差异程度的标准化度量。在因子监控场景中,PSI用于判断当前评分或特征分布相对于建模基准分布是否发生了显著偏移。本节从数学定义出发,逐步推导PSI的计算公式,并解释其核心——期望分布与实际分布的对数比值。

2.1 从信息论到PSI:KL散度的对称化

PSI的数学根源来自信息论中的Kullback-Leibler散度(KL散度),又称相对熵。KL散度衡量的是用一个分布Q去近似另一个分布P时所产生的信息损失:

KL(P || Q) = Σ P(i) * log( P(i) / Q(i) )

然而KL散度是非对称的,即KL(P||Q) ≠ KL(Q||P)。在风控实践中,我们既关心实际分布相对于期望分布的偏离,也关心反向的偏离。因此PSI采用对称化处理,将两个方向的KL散度相加:

PSI = KL(P || Q) + KL(Q || P)
    = Σ (P(i) - Q(i)) * log( P(i) / Q(i) )

其中:

  • P(i):第i个分箱中实际分布的占比(当前监控期)
  • Q(i):第i个分箱中期望分布的占比(建模基准期)
  • log(P(i)/Q(i)):对数比值,衡量两个分布在该分箱上的偏离方向与幅度
关键理解:PSI本质上是两个分布之间所有分箱上“差异的加权和”,权重由分布差异本身决定。当P(i)=Q(i)时,该项贡献为0;差异越大,贡献越大。

2.2 分箱策略与占比计算

在计算PSI之前,必须将连续变量或离散变量划分为若干个分箱(bins)。分箱方式直接影响PSI的敏感性和稳定性。常用的分箱策略包括:

分箱方式 说明 适用场景
等距分箱 按数值范围均匀划分,如[0,10),[10,20),… 变量分布均匀,无极端值
等频分箱 按样本量均匀划分,每箱样本数大致相等 长尾分布、偏态分布
自定义分箱 根据业务含义或风险阈值划分 评分卡分数段、风险等级
决策树分箱 利用目标变量进行监督分箱 特征与目标关系明确的场景

分箱完成后,分别计算每个分箱在期望分布和实际分布中的样本占比:

Q(i) = 期望分布中第i箱的样本数 / 期望分布总样本数
P(i) = 实际分布中第i箱的样本数 / 实际分布总样本数
避坑提示:当某个分箱在期望分布或实际分布中样本数为0时,log(P(i)/Q(i))会出现无穷大。通常做法是:对空箱进行合并,或对占比做微小平滑处理(如加0.001%),避免计算异常。

2.3 PSI计算公式的完整推导

将对称化KL散度展开,得到PSI的最终计算公式:

PSI = Σ (P(i) - Q(i)) * ln( P(i) / Q(i) )

其中:
i = 1, 2, ..., n  (n为分箱总数)
P(i) = 实际分布第i箱占比
Q(i) = 期望分布第i箱占比
ln 为自然对数

推导过程如下:

  1. 计算每个分箱的占比差:Δ(i) = P(i) - Q(i)
  2. 计算对数比值:R(i) = ln( P(i) / Q(i) )
  3. 计算分箱贡献:C(i) = Δ(i) × R(i)
  4. 求和得到PSI:PSI = Σ C(i)

当PSI = 0时,表示两个分布完全一致;PSI越大,分布偏移越严重。风控行业通常采用以下阈值判断:

PSI值范围 稳定性判断 建议动作
PSI < 0.1 稳定 无需干预
0.1 ≤ PSI < 0.25 轻度偏移 加强监控,排查原因
PSI ≥ 0.25 严重偏移 立即告警,模型需复审

2.4 PSI计算流程SVG图

下图展示了从原始数据到PSI值的完整计算流程,包含分箱、占比计算、对数比值和加权求和四个核心步骤:

PSI计算流程 期望分布数据 实际分布数据 等频分箱 等频分箱 Q(i)占比计算 P(i)占比计算 ln(P(i)/Q(i)) Σ(P(i)-Q(i))×ln(P(i)/Q(i)) PSI值

2.5 对数比值的业务含义

对数比值 ln(P(i)/Q(i)) 是PSI计算中最敏感的组成部分,它反映了每个分箱中实际占比相对于期望占比的偏离方向与幅度:

  • ln(P(i)/Q(i)) > 0:实际占比大于期望占比,该分箱样本增多,可能表示风险客群向该区间集中
  • ln(P(i)/Q(i)) < 0:实际占比小于期望占比,该分箱样本减少,客群正在远离该区间
  • ln(P(i)/Q(i)) = 0:两个占比相等,该分箱完全稳定

结合占比差(P(i)-Q(i))的符号,可以判断每个分箱对PSI的贡献是正向还是负向:

P(i)-Q(i) ln(P(i)/Q(i)) 分箱贡献 业务解读
正 正 正贡献(增大PSI) 该区间客群占比上升,且上升幅度显著
负 负 正贡献(增大PSI) 该区间客群占比下降,且下降幅度显著
正 负 负贡献(减小PSI) 占比上升但幅度很小,实际差异不大
负 正 负贡献(减小PSI) 占比下降但幅度很小,实际差异不大
实战技巧:在监控面板中,除了展示整体PSI值外,建议同时展示每个分箱的贡献度柱状图。这样可以快速定位导致分布偏移的关键分箱,为后续归因分析提供方向。

2.6 PSI计算Python代码示例

以下代码实现了PSI的完整计算逻辑,包含分箱、占比计算、对数比值和最终求和:

import numpy as np
import pandas as pd

def calculate_psi(expected, actual, bins=10, method='equal_freq'):
    """
    计算群体稳定性指标(PSI)
    
    Parameters:
    -----------
    expected : array-like
        期望分布数据(建模基准期)
    actual : array-like
        实际分布数据(当前监控期)
    bins : int
        分箱数量
    method : str
        分箱方法:'equal_freq' 等频分箱, 'equal_width' 等距分箱
    
    Returns:
    --------
    psi : float
        PSI值
    bin_details : DataFrame
        每个分箱的详细计算过程
    """
    # 合并数据确定分箱边界
    all_data = np.concatenate([expected, actual])
    
    if method == 'equal_freq':
        # 等频分箱:基于期望分布的分位数
        percentiles = np.linspace(0, 100, bins + 1)[1:-1]
        bin_edges = np.percentile(expected, percentiles)
        bin_edges = np.concatenate([[-np.inf], bin_edges, [np.inf]])
    elif method == 'equal_width':
        # 等距分箱
        min_val, max_val = np.min(all_data), np.max(all_data)
        bin_edges = np.linspace(min_val, max_val, bins + 1)
        bin_edges[0] = -np.inf
        bin_edges[-1] = np.inf
    else:
        raise ValueError("method must be 'equal_freq' or 'equal_width'")
    
    # 计算每个分箱的样本数
    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
    
    # 计算对数比值和分箱贡献
    ratio = np.log(actual_pct / expected_pct)
    contribution = (actual_pct - expected_pct) * ratio
    
    # 计算PSI
    psi = np.sum(contribution)
    
    # 构建详细结果DataFrame
    bin_labels = [f'Bin_{i+1}' for i in range(bins)]
    bin_details = pd.DataFrame({
        'bin': bin_labels,
        'expected_count': expected_counts,
        'actual_count': actual_counts,
        'expected_pct': expected_pct - epsilon,
        'actual_pct': actual_pct - epsilon,
        'ratio_log': ratio,
        'contribution': contribution
    })
    
    return psi, bin_details

# 示例用法
np.random.seed(42)
expected_data = np.random.normal(600, 100, 10000)
actual_data = np.random.normal(620, 120, 10000)

psi_value, details = calculate_psi(expected_data, actual_data, bins=10)
print(f"PSI值: {psi_value:.4f}")
print("\n分箱详情:")
print(details[['bin', 'expected_pct', 'actual_pct', 'ratio_log', 'contribution']])

2.7 PSI与KL散度的关系SVG图

下图展示了PSI与KL散度之间的数学关系,以及PSI如何通过对称化处理实现双向度量:

PSI与KL散度的关系 KL(P || Q) = Σ P(i)·ln(P(i)/Q(i)) KL(Q || P) = Σ Q(i)·ln(Q(i)/P(i)) + PSI = KL(P||Q) + KL(Q||P) = Σ (P(i)-Q(i))·ln(P(i)/Q(i)) 对称化处理:双向度量分布差异,避免KL散度的非对称性 当P=Q时,PSI=0;差异越大,PSI值越大

2.8 本节要点总结

  • PSI的数学本质:对称化的KL散度,通过两个方向的信息损失之和衡量分布差异
  • 核心计算要素:分箱占比差(P(i)-Q(i)) × 对数比值ln(P(i)/Q(i))
  • 对数比值的作用:放大占比差异的敏感度,正负号指示偏离方向
  • 分箱策略的重要性:等频分箱在风控中最为常用,能有效处理偏态分布
  • 阈值判断标准:PSI<0.1稳定,0.1~0.25轻度偏移,≥0.25严重偏移