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 的模型监控体系整体架构,涵盖数据采集、指标计算、告警触发与可视化展示。

Spark 模型监控体系架构图 数据源层 Kafka 实时流 | HDFS 离线批 | 数据库增量 Spark 计算层 Structured Streaming(实时)| Spark SQL(离线) 指标计算:KS / PSI / 缺失率 / 分布统计 指标存储层 InfluxDB(时序)| MySQL(配置)| HBase(明细) 告警与展示层 Grafana 看板 | 邮件/短信告警 | 工单系统

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 指标计算 是否超阈值? 正常,跳过 触发告警 告警分发与工单 人工排查与模型迭代
最佳实践:在 Spark 中实现触发机制时,建议将阈值配置存储在外部配置中心(如 ZooKeeper 或 Apollo),方便动态调整而无需重启任务。同时,对于实时流任务,可使用 foreachBatch 结合外部告警 API 实现低延迟告警。