12. MLOps 与实验管理

MLOps 工程化实践:MLflow 实验追踪、DVC 数据版本管理、模型注册中心、CI/CD for ML 与自动化流水线设计

MLOps 将 DevOps 的工程化理念引入机器学习,解决模型实验不可复现、数据版本混乱、部署流程断裂等问题。本文构建从实验追踪到自动化部署的完整 MLOps 工作流。

1. MLOps 核心挑战

挑战表现MLOps 解决方案
实验不可复现参数、数据、代码的版本不一致实验追踪 + 版本管理
数据漂移训练数据与线上数据分布差异监控 + 自动重训练
模型版本混乱多个模型版本,不知哪个上线模型注册中心
部署手动化每次上线依赖人工操作CI/CD Pipeline
资源利用率低GPU 闲置与争抢并存调度编排
测试覆盖不足没有模型级别的测试模型测试框架

2. 实验追踪:MLflow

2.1 MLflow 核心组件

组件功能
Tracking记录参数、指标、模型、工件 (artifact)
Projects打包可复现的 ML 项目
Models模型打包格式与部署工具
Registry模型版本管理与阶段转换

2.2 Tracking 基础用法

import mlflow
import mlflow.sklearn
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

# 设置追踪 URI
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("customer-churn-prediction")

with mlflow.start_run(run_name="rf_baseline"):
    # 记录参数
    mlflow.log_param("n_estimators", 200)
    mlflow.log_param("max_depth", 10)
    mlflow.log_param("random_state", 42)
    
    # 训练
    model = RandomForestClassifier(n_estimators=200, max_depth=10, random_state=42)
    model.fit(X_train, y_train)
    
    # 记录指标
    train_acc = accuracy_score(y_train, model.predict(X_train))
    test_acc = accuracy_score(y_test, model.predict(X_test))
    mlflow.log_metric("train_accuracy", train_acc)
    mlflow.log_metric("test_accuracy", test_acc)
    mlflow.log_metric("overfitting", train_acc - test_acc)
    
    # 记录模型
    mlflow.sklearn.log_model(model, "model")
    
    # 记录工件
    import matplotlib.pyplot as plt
    plt.figure(figsize=(10, 6))
    pd.Series(model.feature_importances_, index=X.columns).sort_values().plot(kind='barh')
    plt.savefig("feature_importance.png")
    mlflow.log_artifact("feature_importance.png")

2.3 嵌套实验与超参搜索

from sklearn.model_selection import GridSearchCV
import numpy as np

mlflow.set_experiment("hyperparameter-tuning")

with mlflow.start_run(run_name="xgb_grid_search"):
    param_grid = {
        'max_depth': [3, 6, 9],
        'learning_rate': [0.01, 0.1, 0.3],
        'n_estimators': [100, 200]
    }
    
    for i, params in enumerate(ParameterGrid(param_grid)):
        with mlflow.start_run(nested=True, run_name=f"run_{i}"):
            mlflow.log_params(params)
            
            model = XGBClassifier(**params, random_state=42)
            model.fit(X_train, y_train)
            
            train_f1 = f1_score(y_train, model.predict(X_train), average='macro')
            val_f1 = f1_score(y_val, model.predict(X_val), average='macro')
            
            mlflow.log_metric("train_f1", train_f1)
            mlflow.log_metric("val_f1", val_f1)
            mlflow.log_metric("gap", train_f1 - val_f1)
            
            mlflow.sklearn.log_model(model, "model")

2.4 自动记录 (Autologging)

# 为支持的框架自动记录参数、指标、模型
mlflow.sklearn.autolog()
mlflow.tensorflow.autolog()
mlflow.pytorch.autolog()
mlflow.xgboost.autolog()

# 只需正常训练,MLflow 自动追踪
model = XGBClassifier(n_estimators=100)
model.fit(X_train, y_train, eval_set=[(X_val, y_val)])
# 参数、指标、模型自动记录

2.5 MLflow 服务启动

# 启动 Tracking Server
mlflow server \
  --backend-store-uri postgresql://user:pass@localhost/mlflow \
  --default-artifact-root s3://mlflow-artifacts \
  --host 0.0.0.0 \
  --port 5000

# 或使用本地文件系统(开发环境)
mlflow server --backend-store-uri sqlite:///mlflow.db \
  --default-artifact-root ./mlruns \
  --host 0.0.0.0

3. 数据版本管理:DVC

DVC (Data Version Control) 用 Git 管理数据版本,用云存储实际存储大文件。

3.1 基础用法

# 初始化
git init
dvc init
git commit -m "Initialize DVC"

# 添加数据到版本控制
dvc add data/train.csv
# 生成 train.csv.dvc 和 .gitignore,将大文件存入本地缓存

git add data/train.csv.dvc data/.gitignore
git commit -m "Add training data"

# 配置远程存储
dvc remote add -d myremote s3://mybucket/dvcstore
dvc push  # 上传到远程

# 团队协作
git pull
dvc pull  # 拉取数据

3.2 流水线定义

# dvc.yaml
stages:
  prepare:
    cmd: python src/prepare.py data/raw data/prepared
    deps:
      - src/prepare.py
      - data/raw
    outs:
      - data/prepared
  
  train:
    cmd: python src/train.py data/prepared model.pkl
    deps:
      - src/train.py
      - data/prepared
    params:
      - train.epochs
      - train.lr
    outs:
      - model.pkl
    metrics:
      - metrics.json:
          cache: false
  
  evaluate:
    cmd: python src/evaluate.py model.pkl data/prepared
    deps:
      - src/evaluate.py
      - model.pkl
      - data/prepared
    metrics:
      - evaluation/metrics.json:
          cache: false
    plots:
      - evaluation/plots:
          cache: false
# 运行流水线
dvc repro

# 对比实验
dvc metrics diff  # 显示指标变化

3.3 参数管理

# params.yaml
prepare:
  split: 0.2
  seed: 42

train:
  epochs: 50
  lr: 0.001
  batch_size: 32
  dropout: 0.3

evaluate:
  threshold: 0.5
# 实验矩阵:批量运行不同参数组合
for lr in 0.001 0.01 0.1; do
  dvc exp run --set-param train.lr=$lr
done

# 查看实验结果
dvc exp show

4. 模型注册中心

4.1 MLflow Model Registry

import mlflow
from mlflow.tracking import MlflowClient

client = MlflowClient()

# 注册模型
result = mlflow.register_model(
    "runs:/some_run_id/model",
    "churn-prediction-model"
)

# 模型版本管理
client.transition_model_version_stage(
    name="churn-prediction-model",
    version=1,
    stage="Staging"
)

client.transition_model_version_stage(
    name="churn-prediction-model",
    version=2,
    stage="Production"
)

# 加载生产环境模型
model = mlflow.pyfunc.load_model("models:/churn-prediction-model/Production")
predictions = model.predict(X_test)

4.2 模型版本阶段

阶段含义操作
None初始状态注册时默认
Staging预发布,测试验证中CI 推送
Production线上服务人工审批
Archived历史版本自动降级

5. CI/CD for ML

5.1 GitHub Actions 流水线

# .github/workflows/ml-pipeline.yml
name: ML Pipeline

on:
  push:
    paths:
      - 'src/**'
      - 'data/**'
      - 'params.yaml'

jobs:
  prepare:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: iterative/setup-dvc@v1
      
      - name: Pull DVC data
        run: |
          dvc remote modify myremote access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
          dvc remote modify myremote secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          dvc pull
      
      - name: Prepare data
        run: dvc repro prepare
      
      - uses: actions/upload-artifact@v4
        with:
          name: prepared-data
          path: data/prepared

  train:
    needs: prepare
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Download prepared data
        uses: actions/download-artifact@v4
        with:
          name: prepared-data
          path: data/prepared
      
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.10'
      
      - name: Install dependencies
        run: pip install -r requirements.txt
      
      - name: Train model
        run: |
          dvc repro train
          echo "METRICS=$(cat metrics.json)" >> $GITHUB_ENV
      
      - name: Upload model
        uses: actions/upload-artifact@v4
        with:
          name: trained-model
          path: model.pkl

  evaluate:
    needs: train
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Download model
        uses: actions/download-artifact@v4
        with:
          name: trained-model
      
      - name: Evaluate
        run: dvc repro evaluate
      
      - name: Check metrics threshold
        run: |
          ACCURACY=$(jq '.accuracy' evaluation/metrics.json)
          if (( $(echo "$ACCURACY < 0.85" | bc -l) )); then
            echo "Accuracy $ACCURACY below threshold 0.85"
            exit 1
          fi

  deploy:
    needs: evaluate
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Deploy to production
        run: |
          mlflow models serve -m models:/churn-prediction-model/Production -p 5001

5.2 测试策略

# tests/test_model.py
import pytest
import numpy as np
from model import MyModel

class TestModel:
    def test_prediction_shape(self):
        model = MyModel.load('model.pkl')
        X = np.random.randn(10, 20)
        preds = model.predict(X)
        assert preds.shape == (10,)
    
    def test_prediction_range(self):
        model = MyModel.load('model.pkl')
        X = np.random.randn(10, 20)
        preds = model.predict(X)
        assert np.all((preds >= 0) & (preds <= 1))
    
    def test_invariance(self):
        """输入微小变化,输出不应剧变"""
        model = MyModel.load('model.pkl')
        X = np.random.randn(1, 20)
        X_perturbed = X + np.random.normal(0, 0.01, X.shape)
        
        pred1 = model.predict(X)
        pred2 = model.predict(X_perturbed)
        assert np.abs(pred1 - pred2) < 0.1
    
    def test_drift_detection(self):
        """检测数据漂移"""
        from scipy import stats
        
        # 当前批次 vs 训练数据
        _, p_value = stats.ks_2samp(current_batch, training_data)
        assert p_value > 0.05, "Data drift detected!"

6. 模型监控

6.1 Prometheus + Grafana 监控

from prometheus_client import Counter, Histogram, Gauge, start_http_server

# 定义指标
inference_counter = Counter('model_inferences_total', 'Total inferences')
latency_histogram = Histogram('model_latency_seconds', 'Inference latency')
prediction_drift = Gauge('model_prediction_drift', 'Prediction distribution drift')

class MonitoredModel:
    def __init__(self, model):
        self.model = model
        self.baseline_preds = []
    
    @latency_histogram.time()
    def predict(self, X):
        inference_counter.inc()
        return self.model.predict(X)
    
    def check_drift(self, recent_preds, threshold=0.05):
        from scipy import stats
        _, p_value = stats.ks_2samp(recent_preds, self.baseline_preds)
        prediction_drift.set(1 - p_value)
        return p_value < threshold

# 启动指标服务
start_http_server(8000)

6.2 告警规则

# alert_rules.yml
groups:
  - name: ml-model
    rules:
      - alert: HighPredictionLatency
        expr: histogram_quantile(0.99, rate(model_latency_seconds_bucket[5m])) > 0.5
        for: 5m
        annotations:
          summary: "模型推理延迟过高"
      
      - alert: ModelDriftDetected
        expr: model_prediction_drift > 0.1
        for: 10m
        annotations:
          summary: "模型预测分布漂移,需重新训练"
      
      - alert: LowPredictionConfidence
        expr: avg(model_prediction_entropy) > 2.0
        for: 15m
        annotations:
          summary: "模型预测不确定性增加"

7. Kubernetes 上的 MLOps

7.1 Kubeflow 基础

Kubeflow 提供 Kubernetes 上的 ML 工作流编排。

# pipeline.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  name: ml-pipeline
spec:
  entrypoint: training-pipeline
  templates:
    - name: training-pipeline
      steps:
        - - name: data-preparation
            template: prepare-data
        - - name: model-training
            template: train-model
            arguments:
              parameters:
                - name: prepared-data
                  value: "{{steps.data-preparation.outputs.artifacts.data}}"
        - - name: model-evaluation
            template: evaluate-model
        - - name: model-deployment
            template: deploy-model
            when: "{{steps.model-evaluation.outputs.parameters.accuracy}} > 0.85"

7.2 KServe 模型推理服务

# inference-service.yaml
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: sklearn-iris
spec:
  predictor:
    sklearn:
      storageUri: "s3://models/sklearn/iris"
      resources:
        limits:
          cpu: "1"
          memory: 2Gi
    minReplicas: 1
    maxReplicas: 10
    scaleTarget: 1  # 并发请求数
    canaryTrafficPercent: 10  # 金丝雀发布

8. 特征存储 (Feature Store)

# Feast 特征服务
from feast import FeatureStore
import pandas as pd

store = FeatureStore(repo_path=".")

# 离线特征(训练)
training_df = store.get_historical_features(
    entity_df=entity_df,
    features=[
        "user_stats:avg_purchase_amount",
        "user_stats:total_orders",
        "item_stats:ctr_7d"
    ]
).to_df()

# 在线特征(推理)
online_features = store.get_online_features(
    features=["user_stats:avg_purchase_amount"],
    entity_rows=[{"user_id": 123}]
).to_dict()

总结

MLOps 成熟度模型:

等级特征工具
L1: 手动脚本化,手动运行Jupyter, Python scripts
L2: 可复现实验追踪,版本管理MLflow, DVC
L3: 自动化CI/CD,自动测试GitHub Actions, Jenkins
L4: 持续训练监控触发重训练Airflow, Kubeflow
L5: 全自动化A/B 测试,自动回滚Full K8s + Feature Store

关键实践:

  1. 一切版本化:代码(Git) + 数据(DVC) + 模型(MLflow) + 环境(Docker)
  2. 实验即代码:用 Git 分支管理实验,而非本地 notebook
  3. 自动化测试:数据验证 + 模型测试 + API 测试
  4. 持续监控:预测漂移、数据漂移、延迟与错误率
  5. 可观察性:每个预测关联到模型版本、特征版本、训练数据版本

MLOps 的目标不是更复杂的工具链,而是让 ML 系统的迭代更快、更可靠。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ai-ml」更多文章

  1. 13. 大语言模型应用开发
  2. 11. 模型部署与推理优化
  3. 10. 特征工程实战