3. 模型监控体系设计:监控指标体系(性能、稳定性、数据质量)、监控频率与触发机制
模型上线后并非终点,而是持续监控的起点。一个完整的监控体系需要覆盖模型运行的三个核心维度:性能(模型是否仍然有效)、稳定性(模型行为是否发生漂移)、数据质量(输入输出数据是否可靠)。同时,需要定义合理的监控频率与触发机制,确保问题能被及时发现并响应。
3.1 监控指标体系
监控指标是模型风险管理的“仪表盘”。根据模型生命周期的不同阶段,我们将指标分为三大类:
| 维度 | 核心指标 | 说明 | Spark 监控示例 |
|---|---|---|---|
| 性能 | KS / AUC / RMSE / MAE | 评估模型预测能力是否衰减 | 每日计算评分卡 KS,与基准期对比 |
| 稳定性 | PSI / 特征分布偏移 / 模型输出分布 | 检测模型输入或输出分布是否发生显著变化 | 每小时计算特征 PSI,监控特征漂移 |
| 数据质量 | 缺失率 / 异常值率 / 数据延迟 / 格式合规率 | 确保数据管道健康,避免“垃圾进垃圾出” | 每批次统计缺失率,超过阈值告警 |
3.1.1 性能指标
- 分类模型:KS(Kolmogorov-Smirnov)、AUC(Area Under Curve)、准确率、召回率、F1-score。
- 回归模型:RMSE(Root Mean Square Error)、MAE(Mean Absolute Error)、R²。
- 对比基准:通常以模型开发时的验证集表现作为基准线,允许一定波动范围(如 ±5%)。
3.1.2 稳定性指标
- 群体稳定性指标(PSI):衡量模型评分分布相对于预期分布的变化。PSI < 0.1 表示无显著变化;0.1 ≤ PSI < 0.25 需关注;PSI ≥ 0.25 表示发生重大漂移。
- 特征分布偏移:对每个特征计算 PSI 或使用 KS 检验,识别具体哪个特征发生了漂移。
- 模型输出分布:监控模型预测值的均值、标准差、分位数变化。
3.1.3 数据质量指标
- 缺失率:每个特征的空值比例。
- 异常值率:超出业务合理范围的数据比例。
- 数据延迟:从数据产生到进入 Spark 处理的时间差。
- 格式合规率:字段类型、长度、枚举值是否符合预期。
实践建议:在 Spark 中,建议将监控指标的计算封装为独立的
DataFrame 转换函数,便于复用和集成到调度任务中。例如,使用 groupBy + agg 计算每日 KS 值。
3.2 监控频率与触发机制
监控频率应根据模型风险等级和数据更新速度动态调整。触发机制则定义了当指标异常时,系统如何响应。
3.2.1 监控频率设计
| 监控类型 | 推荐频率 | 适用场景 |
|---|---|---|
| 实时/准实时 | 每批次或每小时 | 高频率交易模型、风控模型 |
| 日频 | 每日一次 | 评分卡、推荐模型、营销模型 |
| 周频/月频 | 每周或每月 | 低频模型、辅助决策模型 |
3.2.2 触发机制与告警策略
- 阈值触发:当指标超过预设阈值(如 PSI > 0.25)时,立即触发告警。
- 趋势触发:当指标连续 N 个周期呈恶化趋势(如 KS 连续下降 3 天),触发预警。
- 组合触发:多个指标同时异常(如性能下降 + 数据质量恶化),触发高优先级告警。
- 告警渠道:邮件、短信、企业微信/钉钉机器人、Spark 日志聚合系统。
注意:避免“告警风暴”。建议对同一模型设置告警冷却时间(如 30 分钟内不重复告警),并将告警分级(P0 严重 / P1 警告 / P2 提示)。
3.3 监控体系架构图
下图展示了基于 Spark 的模型监控体系整体架构,涵盖数据采集、指标计算、告警触发与可视化展示。
3.4 监控指标计算示例(Spark SQL)
以下代码展示了如何使用 Spark SQL 计算每日模型评分分布的 PSI 值,并与基准分布对比。
-- 假设表 model_score_daily 包含每日评分,model_score_baseline 包含基准分布
WITH daily_dist AS (
SELECT
CASE
WHEN score < 0.2 THEN '0-0.2'
WHEN score < 0.4 THEN '0.2-0.4'
WHEN score < 0.6 THEN '0.4-0.6'
WHEN score < 0.8 THEN '0.6-0.8'
ELSE '0.8-1.0'
END AS bucket,
COUNT(*) AS cnt
FROM model_score_daily
WHERE dt = '2025-03-01'
GROUP BY bucket
),
baseline_dist AS (
SELECT
bucket,
cnt AS base_cnt
FROM model_score_baseline
)
SELECT
d.bucket,
d.cnt,
b.base_cnt,
ROUND(
(d.cnt / SUM(d.cnt) OVER()) * 100, 2
) AS actual_pct,
ROUND(
(b.base_cnt / SUM(b.base_cnt) OVER()) * 100, 2
) AS expected_pct,
ROUND(
( (d.cnt / SUM(d.cnt) OVER()) - (b.base_cnt / SUM(b.base_cnt) OVER()) )
* LN( (d.cnt / SUM(d.cnt) OVER()) / (b.base_cnt / SUM(b.base_cnt) OVER()) )
, 6
) AS psi_component
FROM daily_dist d
JOIN baseline_dist b ON d.bucket = b.bucket
ORDER BY d.bucket;
关键点:PSI 的计算需要确保分桶方式与基准期一致。在 Spark 中,建议将分桶逻辑封装为 UDF,避免硬编码。同时,对于实时流场景,可使用
mapGroupsWithState 实现滑动窗口内的 PSI 计算。
3.5 触发机制流程图
下图展示了从指标异常到告警响应的完整触发流程,帮助理解自动化监控的闭环。
最佳实践:在 Spark 中实现触发机制时,建议将阈值配置存储在外部配置中心(如 ZooKeeper 或 Apollo),方便动态调整而无需重启任务。同时,对于实时流任务,可使用
foreachBatch 结合外部告警 API 实现低延迟告警。