第96回 実務で回すAIのデータバージョン管理と再現可能なトレーニングワークフロー

まずはじめに。データ版管理を現場に取り入もうとすると、「どこから手を付ければいいか」「コストや運用負荷が増えないか」と戸惑う方が多いはずです。本記事では、実務でそのまま使える手順に絞り、Pythonスクリプト例やCIの断片を提示します。小規模チームでも導入できる現実的な選択肢を優先します。

狙いと位置づけ

第95回(SLO/インシデント)や第82回(再学習)、第94回(CI/CD)で扱ったテーマを踏まえ、今回は「データそのもの」の再現性と監査対応を担保するための設計と運用ルールに焦点を当てます。目的は、モデルの学習時に使ったデータを確実に追跡し、再現・検証・再学習トリガーに結び付けることです。

ツール選定指針(要点)

ツール/方式 利点 欠点 実務選定基準
DVC + git-lfs Gitワークフローと親和性が高く、小〜中規模で使いやすい 大容量・多数バージョンで管理コストが上がる チームにGit慣れがあり、比較的小さなデータでコスト重視なら推奨
Delta Lake / Parquet 差分処理が効率的。分析向けに最適化 基盤の導入コストと運用が必要 分析ワークロードやETLが重い場合、再現性と性能を両立したいとき
オブジェクトストレージ + manifest 安価に長期保存。柔軟なスナップショット設計が可能 メタデータ管理を自前で作る必要がある コスト重視かつメタデータを自分で管理できるチーム向け
軽量メタデータDB(SQLite/Postgres) 検索性が高く、監査出力を簡単に作れる データ本体は別途保存する必要あり 監査やクエリ要件がある場合、必須に近い

実務では「運用負荷」「コスト」「監査要件」「再現性」のバランスで決めます。まずはオブジェクトストレージ+manifest+SQLiteの組合せで始め、必要に応じてDVCやDeltaを導入するのが現実的です。

設計: データプロダクト単位で分割する

データは用途ごとに分割します。粒度を適切に決めるとスナップショット管理が楽になります。

ディレクトリ/キー 説明
raw/ 取得直後の未加工データ(不変で保存) raw/2026-06-24/*
processed/ 前処理済み。学習に使う形式 processed/v1.2/train.parquet
train/val/test/ 学習用に分割したデータ train/2026-06-snapshot-001/
manifests/ 各スナップショットのmanifest(メタデータ) manifests/2026-06-24-s001.json

ハンズオン手順(概要)

目標フロー:「スナップショット作成 → manifest生成 → オブジェクトストレージ保存 → メタDB記録 → モデルと紐付け → CIで検証」。以下は最小限でそのまま動く手順です。

ステップ1: データプロダクト設計(粒度決定)

  • データ単位は「データプロダクト(API出力、センサーフィード、バッチETL生成物)」で定義する。
  • スナップショットの頻度は業務要件で決める(日次/週次/イベントトリガー)。頻度が高い場合は差分のみ記録する戦略を検討。
  • 保存するファイル形式は分析効率を考えParquetを推奨。小規模ならCSVでも可。

ステップ2: スナップショット作成とmanifest生成

ここでは簡単なPythonスニペットを示します。目的はファイルごとのハッシュ算出、manifest(schema, row_count, sample_ids, provenance)を作ることです。

import hashlib
import json
import os
import pandas as pd
from datetime import datetime

def file_sha256(path):
    h = hashlib.sha256()
    with open(path, 'rb') as f:
        for chunk in iter(lambda: f.read(8192), b''):
            h.update(chunk)
    return h.hexdigest()

def build_manifest(file_paths, provenance):
    manifest = {
        'snapshot_id': f"snap-{datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')}",
        'created_at': datetime.utcnow().isoformat() + 'Z',
        'files': [],
        'provenance': provenance
    }
    for p in file_paths:
        # 簡易にCSV/Parquetを扱う
        if p.endswith('.parquet'):
            df = pd.read_parquet(p)
        else:
            df = pd.read_csv(p)
        manifest['files'].append({
            'path': os.path.basename(p),
            'sha256': file_sha256(p),
            'row_count': len(df),
            'schema': list(df.columns)[:50]  # 簡易スキーマ
        })
    return manifest

# 使い方例
provenance = {'source': 's3://bucket/raw/2026-06-24', 'query': 'SELECT * FROM events WHERE ds=2026-06-24'}
files = ['processed/train.parquet', 'processed/val.parquet']
manifest = build_manifest(files, provenance)
with open('manifests/snap.json', 'w') as f:
    json.dump(manifest, f, indent=2)

生成されたmanifestの例:

{
  "snapshot_id": "snap-20260624T121314Z",
  "created_at": "2026-06-24T12:13:14Z",
  "files": [
    {
      "path": "train.parquet",
      "sha256": "...",
      "row_count": 12000,
      "schema": ["id", "ts", "feature_a", "label"]
    }
  ],
  "provenance": {
    "source": "s3://bucket/raw/2026-06-24",
    "query": "SELECT * FROM events WHERE ds=2026-06-24"
  }
}

このmanifestをオブジェクトストレージに保存し、メタデータDB(例: SQLite)に登録します。

-- SQLite schema (例)
CREATE TABLE snapshots (
  id TEXT PRIMARY KEY,
  created_at TEXT,
  manifest_uri TEXT,
  owner TEXT
);

ステップ3: メタデータとモデルの紐付け

学習ジョブはmanifestのsnapshot_idを取り込み、モデルのmetadata(model card)にdataset_versionを記録します。サンプルのmodel metadata(JSON):

{
  "model_name": "churn-predictor",
  "model_version": "v1.4",
  "trained_at": "2026-06-24T13:00:00Z",
  "dataset_version": "snap-20260624T121314Z",
  "training_command": "python train.py --snapshot=snap-20260624T121314Z",
  "code_hash": "sha256:...",
  "environment": {
    "python": "3.10.8",
    "packages": {"pandas": "1.5.3", "scikit-learn": "1.2.2"}
  }
}

このmetadataはモデルアーティファクトにバンドルして保存します。検索できるようにメタDBにも登録しておくと監査時に便利です。

CI/CD連携とテスト

CIでは下記のテストを自動化します。

  • schemaチェック(必須カラムの有無)
  • row count増減閾値チェック(例: 前スナップショット比で±20%)
  • sample verification(重要IDのサンプル存在確認)

CIパイプラインの雛形(YAML断片):

jobs:
  validate_snapshot:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Run snapshot validation
        run: |
          python scripts/validate_snapshot.py --manifest manifests/snap.json

validate_snapshot.py の簡易例(schemaとrow count):

import json
import sys

with open('manifests/snap.json') as f:
    m = json.load(f)

# 期待schema
expected_cols = {'id', 'ts', 'feature_a', 'label'}
for file in m['files']:
    cols = set(file['schema'])
    if not expected_cols.issubset(cols):
        print('Schema mismatch in', file['path'])
        sys.exit(2)

# row count check(前回値はメタDBから取得する想定)
# 前回: prev_rows
# if abs(file['row_count'] - prev_rows) / prev_rows > 0.2: fail

print('OK')

差分検知に応じた再学習トリガーは次のように分けます。自動再学習: 小さなデータ差分やラベル増加等。手動レビュー: schema変更や大きなデータ増減。

監査と再現性: 保存すべきメタ情報

項目 理由 / 備考
データハッシュ(ファイル単位) データ改変の検出と完全性確認
取得クエリ/取得手順 データの出所を追跡するために必須
前処理コードhash 前処理ロジックの変更検出と再現
環境情報(python, package versions) 再現時の環境再構築のため
manifest(schema, row_count, sample_ids) 検証・監査レポートの基礎データ

監査用の簡易runbookテンプレート(提示すべき最小出力):

Runbook for snapshot: snap-20260624T121314Z
1) manifest URI: s3://bucket/manifests/snap-20260624T121314Z.json
2) files:
   - train.parquet (sha256: ... , rows: 12000)
3) provenance: s3://bucket/raw/2026-06-24, query: SELECT ...
4) preprocessing code: repo@commit: abcdef123
5) model artifacts using this snapshot: churn-predictor v1.4

運用ルールとコスト管理

保存ポリシーは必ず定めます。例:

保管クラス 保存期間 用途
short-term cache (hot) 30日 CIや頻繁な再学習でアクセスするデータ
long-term archive (cold) 3年(監査要件に応じて延長) 監査や将来の完全再学習用

大容量データを長期保管するとコストがかかるため、頻繁に使うスナップショットのみhot保存、残りはcoldに移すルールを運用で決めます。削除手順は必ずmanifestとメタDBの参照整合性チェックを実施してから行ってください。

落とし穴と対策

  • 差分処理での不整合: ファイル分割ポリシーを明確にし、必ずファイルごとのハッシュを記録する。
  • スキーマドリフト: CIでスキーマチェックを設け、重大変更は手動レビューに回す。
  • 手作業での上書き: 書き込みは原則不可、スナップショットは不変にする(イミュータブル)。

実践サンプル: リポジトリファイルレイアウト

パス 用途
scripts/ hash、manifest生成、アップロードスクリプト
manifests/ 生成されたmanifestを保存(CI/監査参照用)
models/ 学習済みモデルとmetadata.json
db/ SQLiteのメタDB
.github/workflows/ CI定義(snapshot validationなど)

短期タスク(次の一歩)

1週間でできることと1ヶ月での到達目標を示します。

  • 1週間: データプロダクトの分割設計、1つのサンプルスナップショット作成とmanifest生成を試す。
  • 1ヶ月: manifestをメタDBに登録、CIでスキーマ/rowcount検証を導入し、モデルmetadataにdataset_versionを書き込む。

第97回では「メタデータカタログ/Feature Store運用」への橋渡しをします。

まとめ

  • データ版管理は、モデルの再現性・監査・再学習トリガーに直結する重要な運用要素です。
  • まずはオブジェクトストレージ+manifest+軽量メタDBという最小構成で始め、実運用の負荷に応じてDVCやDeltaを追加するのが現実的です。
  • スナップショットごとにファイルハッシュ・schema・row_count・provenanceを保存し、モデルmetadataにdataset_versionを必ず残す運用を徹底してください。
  • CIでのschemaチェックやrow count閾値チェックを導入し、重大変更は手動レビューに回すルールを作ると安全です。

Manage AI(https://manageai.online)シリーズ「AIとPythonの実務」は、今回の内容を踏まえて現場で回る運用を段階的に解説していきます。次回もぜひご期待ください。