3、流式机器学习管道概述:离线训练 vs 在线推理、流式推理架构、模型版本管理

在构建流式机器学习管道时,需要明确区分离线训练与在线推理的职责边界。离线训练侧重于利用历史批量数据构建高精度模型,而在线推理则要求模型在毫秒级延迟内对实时数据做出预测。本章将系统阐述这两种模式的差异、流式推理的典型架构以及模型版本管理的核心策略。

3.1 离线训练 vs 在线推理

离线训练与在线推理在数据来源、计算模式、延迟要求、资源消耗等方面存在本质区别。下表从多个维度对比两者的关键差异:

对比维度 离线训练 在线推理
数据来源 历史批量数据(HDFS、S3、数据湖) 实时流数据(Kafka、Kinesis、Pulsar)
计算模式 批处理(Spark Batch、MapReduce) 流处理(Spark Structured Streaming、Flink)
延迟要求 分钟级到小时级 毫秒级到秒级
资源消耗 高吞吐、可弹性伸缩 低延迟、持续运行
模型更新频率 按天/周/月触发 实时或近实时(增量更新)
数据量级 TB 到 PB 级 MB 到 GB 级/秒
容错机制 任务重试、Checkpoint 状态一致性、Exactly-Once 语义
关键原则:离线训练与在线推理应解耦运行。训练管道负责模型生产与验证,推理管道负责模型加载与预测。两者通过模型注册中心(如 MLflow Model Registry)进行版本化衔接,避免训练任务影响推理稳定性。

3.2 流式推理架构

流式推理架构的核心是将训练好的模型部署到流处理引擎中,对实时数据流进行逐条或微批预测。典型的流式推理架构包含数据接入、特征工程、模型推理、结果输出四个阶段。

下图展示了基于 Spark Structured Streaming 的流式推理架构:

流式推理架构(Spark Structured Streaming) 实时数据源 Kafka / Kinesis 特征工程 窗口聚合 / 编码 模型推理 MLflow 模型加载 结果输出 Kafka / DB 模型版本管理 模型漂移监控 架构要点: • 数据源与结果输出解耦 • 特征工程在流中实时计算 • 模型通过注册中心动态加载 • 漂移监控触发模型版本切换 • 支持 Exactly-Once 语义
实践建议:在 Spark Structured Streaming 中,推荐使用 mapGroupsWithState 或 flatMapGroupsWithState 算子实现带状态的模型推理。对于深度学习模型,可结合 UDF 加载 ONNX 或 TensorFlow SavedModel 格式,避免 Python 进程间通信开销。

3.3 模型版本管理

模型版本管理是流式机器学习管道中衔接训练与推理的关键环节。没有完善的版本管理,模型漂移监控将无法准确追溯模型行为变化,回滚操作也会变得混乱。

下表列出了模型版本管理中的核心概念与推荐实践:

概念 说明 推荐工具/方法
模型注册 将训练完成的模型及其元数据(算法、超参数、训练数据范围)注册到中央仓库 MLflow Model Registry、DVC
版本标签 为每个模型版本分配语义化版本号(如 v1.2.3)或时间戳标签 SemVer 规范 + Git Tag
阶段管理 模型经历 Staging → Production → Archived 三个阶段 MLflow 阶段标记、自定义 Pipeline
模型回滚 当新版本模型出现漂移或性能下降时,快速切换回稳定版本 蓝绿部署、金丝雀发布
版本对比 对比不同版本模型在相同数据集上的指标差异 MLflow 实验对比、自定义 Dashboard

下图展示了模型版本管理的生命周期流程:

模型版本管理生命周期 离线训练 Spark Batch 模型注册 MLflow Registry 模型评估 A/B 测试 生产部署 Structured Streaming 漂移触发回滚 版本管理核心: 每个版本保留完整元数据与模型文件
常见陷阱:在流式推理中,模型版本切换不能中断正在处理的流数据。建议采用“双缓冲”策略:新版本模型加载完成后,先进行影子测试(Shadow Testing),确认无误后再切换主推理路径。避免直接覆盖模型文件导致推理线程读取到不完整的模型。

3.4 流式推理代码示例

以下代码展示了如何在 Spark Structured Streaming 中加载 MLflow 模型并进行实时推理:

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions._
import org.mlflow.spark.autologging.MlflowAutologEventPublisher
import org.mlflow.tracking.MlflowClient

// 初始化 Spark Session
val spark = SparkSession.builder()
  .appName("StreamingInference")
  .config("spark.sql.streaming.schemaInference", "true")
  .getOrCreate()

// 从 MLflow Model Registry 加载模型(指定版本)
val modelUri = "models:/fraud_detection_model/Production"
val model = spark.read.mlflow.load(modelUri)

// 定义流式数据源
val streamDF = spark.readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "localhost:9092")
  .option("subscribe", "transactions")
  .load()
  .selectExpr("CAST(value AS STRING) as json")
  .select(from_json(col("json"), schema).as("data"))
  .select("data.*")

// 特征工程与推理
val predictions = model.transform(streamDF)
  .withColumn("prediction_timestamp", current_timestamp())
  .withColumn("model_version", lit("v2.1.0"))

// 输出结果到 Kafka
val query = predictions.selectExpr("to_json(struct(*)) AS value")
  .writeStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "localhost:9092")
  .option("topic", "predictions")
  .option("checkpointLocation", "/tmp/checkpoint")
  .outputMode("append")
  .start()

query.awaitTermination()
版本切换策略:在实际生产环境中,建议将模型版本号作为配置参数从外部传入(如环境变量或配置中心),避免硬编码。当漂移监控触发版本切换时,只需更新配置并重启流式查询,即可无缝切换到新版本模型。