LESSON

CI/CD for ML

田中VPoE「学習パイプラインの設計ができた。次はこれをCI/CDに組み込む。MLのCI/CDは通常のソフトウェアとは少し違う。コードだけでなく、データやモデルのテストも含まれる。」

あなた「GitHub Actionsで自動化するんですね。プルリクエストでモデルのテストが走るイメージですか?」

田中VPoE「その通り。コード変更、データ変更、モデル変更のそれぞれでトリガーが異なるのがポイントだ。」

ML CI/CDの特殊性

従来のソフトウェアCI/CDとML CI/CDの違いを理解しましょう。

観点通常のCI/CDML CI/CD
テスト対象コードコード + データ + モデル
ビルド成果物バイナリ/コンテナモデルアーティファクト
テスト基準全テスト通過全テスト通過 + 精度閾値
トリガーコード変更コード変更 + データ変更 + スケジュール
実行時間数分数分〜数時間

ML CI/CDの3つのパイプライン

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│     CI      │ →  │     CT      │ →  │     CD      │
│ Continuous  │    │ Continuous  │    │ Continuous  │
│ Integration │    │ Training    │    │ Delivery    │
│             │    │             │    │             │
│ コードテスト  │    │ モデル学習   │    │ モデルデプロイ│
│ データバリデ  │    │ モデル評価   │    │ サービング   │
│ ーション     │    │ レジストリ   │    │ モニタリング  │
└─────────────┘    └─────────────┘    └─────────────┘

CI(Continuous Integration)

GitHub Actionsでの実装

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

on:
  pull_request:
    branches: [main]
    paths:
      - 'src/**'
      - 'tests/**'
      - 'config/**'
      - 'requirements.txt'

jobs:
  code-quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.10'

      - name: Install dependencies
        run: pip install -r requirements.txt -r requirements-dev.txt

      - name: Lint
        run: |
          ruff check src/
          mypy src/

      - name: Unit tests
        run: pytest tests/unit/ -v --cov=src --cov-report=xml

  data-validation:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Validate training data schema
        run: python tests/data/test_schema.py

      - name: Check data quality
        run: python tests/data/test_quality.py

  model-test:
    runs-on: ubuntu-latest
    needs: [code-quality, data-validation]
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Model smoke test
        run: python tests/model/test_smoke.py

      - name: Model performance test (sample data)
        run: python tests/model/test_performance.py --sample-size 1000

      - name: Comment PR with results
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const results = fs.readFileSync('test_results.md', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: results
            });

データバリデーションテスト

# tests/data/test_schema.py
import pandas as pd
import great_expectations as gx

def test_training_data_schema():
    """学習データのスキーマチェック"""
    df = pd.read_parquet("data/training_data.parquet")

    # 必須カラムの存在確認
    required_columns = [
        "customer_id", "age", "tenure_days",
        "order_count_30d", "avg_order_amount", "churn"
    ]
    for col in required_columns:
        assert col in df.columns, f"Missing column: {col}"

    # データ型の確認
    assert df["customer_id"].dtype in ["int64", "int32"]
    assert df["churn"].dtype in ["int64", "int32"]
    assert df["churn"].isin([0, 1]).all()

def test_training_data_quality():
    """学習データの品質チェック"""
    df = pd.read_parquet("data/training_data.parquet")

    # 最低行数
    assert len(df) >= 1000, f"Too few rows: {len(df)}"

    # Null率
    for col in df.columns:
        null_rate = df[col].isnull().mean()
        assert null_rate < 0.1, f"{col} null rate: {null_rate:.2%}"

    # クラスバランス
    churn_rate = df["churn"].mean()
    assert 0.05 < churn_rate < 0.95, f"Extreme class imbalance: {churn_rate:.2%}"

CT(Continuous Training)

自動学習パイプライン

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

on:
  # スケジュール実行(毎週月曜日)
  schedule:
    - cron: '0 3 * * 1'

  # 手動トリガー
  workflow_dispatch:
    inputs:
      model_name:
        description: 'Model to retrain'
        required: true
        default: 'churn_predictor'

  # データ変更トリガー
  repository_dispatch:
    types: [data-updated]

jobs:
  train:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Run training pipeline
        run: python src/pipelines/training_pipeline.py
        env:
          MLFLOW_TRACKING_URI: ${{ secrets.MLFLOW_TRACKING_URI }}

      - name: Model evaluation gate
        run: python src/pipelines/evaluation_gate.py
        # 精度が閾値を下回った場合、ここでfail

CD(Continuous Delivery)

モデルデプロイパイプライン

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

on:
  # モデルレジストリでProductionに昇格された時
  repository_dispatch:
    types: [model-promoted]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Pull model from registry
        run: |
          python -c "
          import mlflow
          model = mlflow.pyfunc.load_model('models:/churn_predictor@production')
          mlflow.pyfunc.save_model(model, 'deploy/model')
          "

      - name: Build Docker image
        run: |
          docker build -t churn-predictor:${{ github.sha }} -f Dockerfile.serving .

      - name: Push to ECR
        run: |
          aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URI
          docker tag churn-predictor:${{ github.sha }} $ECR_URI/churn-predictor:${{ github.sha }}
          docker push $ECR_URI/churn-predictor:${{ github.sha }}

      - name: Deploy to ECS (Canary)
        run: |
          python scripts/canary_deploy.py \
            --image $ECR_URI/churn-predictor:${{ github.sha }} \
            --canary-percentage 10

トリガー設計のまとめ

トリガーパイプライン実行内容
PR作成/更新CIコードテスト、データバリデーション、スモークテスト
mainマージCI + CTフルテスト + モデル学習
スケジュール(週次)CT定期再学習
データ更新イベントCTデータ駆動の再学習
モデル昇格イベントCDモデルデプロイ
ドリフト検知CT自動再学習

まとめ

項目ポイント
ML CI/CDの特殊性コード + データ + モデルの3つをテスト
CIコード品質 + データバリデーション + モデルスモークテスト
CTスケジュール/イベント駆動の自動学習
CDモデルのコンテナ化とデプロイ自動化

チェックリスト

  • ML CI/CDの3つのパイプライン(CI/CT/CD)を説明できる
  • GitHub ActionsでCI パイプラインを定義できる
  • データバリデーションテストを書ける
  • トリガー設計の使い分けを理解している

次のステップへ

CI/CD for ML を学びました。次は、MLシステム特有のテスト戦略を学びましょう。


推定読了時間:30分