4、模型分数分布监控:分数分布直方图、分数段通过率变化、分数均值与标准差趋势
模型上线后,我最怕什么?不是模型效果差,而是模型「悄悄变坏」而你浑然不知。分数分布监控,就是给模型装上一台心电图机——实时监测它的「心跳」是否正常。
说白了,分数分布的变化是模型退化的第一信号。我见过太多团队,模型上线后只看KS和AUC,结果某天突然发现通过率飙升,一查才发现评分分布早就偏移了。嗯,今天我们就来聊聊怎么盯住分数分布。
4.1 分数分布直方图:一眼看出偏移
分数分布直方图是最直观的工具。把模型输出的分数按区间分组,画成柱状图,你就能看到分数的整体形态。
我个人习惯,每次模型迭代后,先对比训练集和线上实时数据的分布。如果形状变了,比如双峰变单峰,或者整体右移/左移,那就要警惕了。
- 分布形态变化:是否从正态分布变成偏态?
- 中心位置偏移:均值是否明显移动?
- 尾部变化:高分或低分段的样本是否异常增多?
举个例子。我之前做过一个信贷模型,上线三个月后,突然发现低分段(300-400分)的样本占比从5%涨到了15%。一查原因,原来是某渠道导入了大量「包装客户」,特征分布完全变了。如果没有直方图监控,这个问题可能要等到坏账爆发才能发现。
4.2 分数段通过率变化:业务视角的预警
分数分布是技术指标,但业务方更关心通过率。把分数分成若干段(比如每50分一段),计算每个分数段的通过率,然后观察通过率随时间的变化趋势。
为什么要看这个?因为分数分布偏移不一定导致通过率变化,但通过率变化一定影响业务。你想想看,如果模型分数整体右移,但阈值没变,通过率就会飙升——这对风控来说是灾难。
| 分数段 | 上月通过率 | 本月通过率 | 变化幅度 |
|---|---|---|---|
| 300-350 | 12.3% | 11.8% | -0.5% |
| 350-400 | 28.7% | 30.2% | +1.5% |
| 400-450 | 55.4% | 58.1% | +2.7% |
| 450-500 | 78.9% | 82.3% | +3.4% |
| 500-550 | 92.1% | 94.5% | +2.4% |
看到没?每个分数段的通过率都在涨。这说明什么?要么是评分卡太松了,要么是客群质量在变好。但如果是后者,为什么低分段的通过率也在涨?这里就有问题了。
4.3 分数均值与标准差趋势:量化监控
直方图是定性分析,均值与标准差是定量分析。把每天的分数均值、标准差画成折线图,你就能看到模型分数的「体温」和「波动性」。
均值上升,说明整体评分变高,模型更「宽容」了。均值下降,说明模型更「严格」了。标准差变大,说明模型对客群的区分度在增强——但也要警惕是不是出现了极端值。
我建议设置两个阈值:
- 均值偏移阈值:±5% 或 ±10分(根据业务敏感度调整)
- 标准差变化阈值:±10%
一旦触发,自动告警。别等到周报才发现问题,那黄花菜都凉了。
4.4 知识体系与监控逻辑
下面这张图,是我自己总结的分数分布监控框架。说白了,就是三个维度、四个指标、一个闭环。
这个框架的核心逻辑是:从三个维度交叉验证,任何一个维度出现异常,都要联动分析其他维度。比如均值上升了,要去看是整体右移还是高分极端值导致的——这就需要结合直方图来判断。
4.5 实操代码示例
下面是我常用的监控代码片段。用Python实现,每天跑一次,自动生成报告。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
def score_distribution_monitor(train_scores, online_scores, bins=20):
"""
分数分布监控函数
train_scores: 训练集分数
online_scores: 线上实时分数
"""
# 1. 直方图对比
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.hist(train_scores, bins=bins, alpha=0.5, label='训练集')
plt.hist(online_scores, bins=bins, alpha=0.5, label='线上')
plt.xlabel('分数')
plt.ylabel('频数')
plt.legend()
plt.title('分数分布对比')
# 2. 计算PSI
train_hist, _ = np.histogram(train_scores, bins=bins, range=(300, 700))
online_hist, _ = np.histogram(online_scores, bins=bins, range=(300, 700))
train_pct = train_hist / len(train_scores) + 1e-10
online_pct = online_hist / len(online_scores) + 1e-10
psi = np.sum((online_pct - train_pct) * np.log(online_pct / train_pct))
# 3. 统计量
train_mean = np.mean(train_scores)
train_std = np.std(train_scores)
online_mean = np.mean(online_scores)
online_std = np.std(online_scores)
print(f"训练集: 均值={train_mean:.2f}, 标准差={train_std:.2f}")
print(f"线上: 均值={online_mean:.2f}, 标准差={online_std:.2f}")
print(f"PSI={psi:.4f}")
# 告警逻辑
if psi > 0.1:
print("⚠️ PSI超过0.1,分布发生显著偏移!")
if abs(online_mean - train_mean) / train_mean > 0.05:
print("⚠️ 均值偏移超过5%!")
return psi
4.6 避坑指南
最后分享几个我踩过的坑:
- 别只看均值:均值没变不代表分布没变。有可能高分和低分同时增多,均值不变但方差变大了。
- 注意样本量:如果某天线上样本太少(比如节假日),分布波动是正常的。我一般要求每天至少1000个样本才做监控。
- 区分季节性:信贷业务有淡旺季,分数分布也会跟着波动。记得做同比分析,而不是环比。
我曾经犯过一个错误:某月分数均值下降了5%,我以为是模型出了问题,急急忙忙去调参。结果后来发现是那个月学生群体申请量暴增,而学生本身评分就偏低。嗯,从那以后,我每次分析分布变化前,都会先看看客群结构有没有变化。
分数分布监控,说白了就是给模型做「体检」。直方图是CT扫描,通过率是血压,均值标准差是体温。三者结合,才能准确判断模型是否健康。别等到模型「病入膏肓」了才去救,那时候就晚了。
公众号:蓝海资料掘金营,微信deep3321