PSI

中級

PSI

最終更新 2026-03-03 読了時間 1 分
まとめ
  • PSI(Population Stability Index)は学習時と本番時の特徴量分布のずれを定量化する監視指標。
  • PSI < 0.1 は安定、0.1–0.25 は要注意、> 0.25 は分布が大きく変化したと判断する。
  • 各特徴量の PSI を定期的に計算し、モデルの再学習タイミングを客観的に判断できる。

直感 #

与信モデルを半年前のデータで学習したが、景気が変わって申込者層が変化したかもしれない。PSI は「学習時の分布」と「今の分布」を比較し、ずれの大きさをスコア化する。直感的には KL ダイバージェンスの対称版で、ビン分割した分布の差異を測る。

詳細な解説 #

計算式 #

$$ \text{PSI} = \sum_{i=1}^{B} (p_i - q_i) \cdot \ln\frac{p_i}{q_i} $$
  • $p_i$: 学習時のビン $i$ の割合
  • $q_i$: 本番時のビン $i$ の割合
  • $B$: ビン数(通常 10–20)

Python 実装 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import numpy as np

def calculate_psi(expected, actual, bins=10):
    """PSI を計算する。"""
    breakpoints = np.linspace(0, 100, bins + 1)
    breakpoints = np.percentile(expected, breakpoints)

    expected_counts = np.histogram(expected, bins=breakpoints)[0]
    actual_counts = np.histogram(actual, bins=breakpoints)[0]

    expected_pct = expected_counts / len(expected) + 1e-6
    actual_pct = actual_counts / len(actual) + 1e-6

    psi = np.sum((actual_pct - expected_pct) * np.log(actual_pct / expected_pct))
    return psi

使用例 #

1
2
3
4
5
6
7
np.random.seed(42)
train_feature = np.random.normal(50, 10, 5000)
prod_stable = np.random.normal(50, 10, 5000)
prod_drifted = np.random.normal(55, 12, 5000)

print(f"安定時の PSI: {calculate_psi(train_feature, prod_stable):.4f}")
print(f"ドリフト時の PSI: {calculate_psi(train_feature, prod_drifted):.4f}")

判定基準 #

PSI 値判定アクション
< 0.1安定監視継続
0.1 – 0.25やや変化原因調査・注視
> 0.25大きな変化モデルの再学習を検討