- 技術 💻
MLOpsとは|主要ツールとMLflowで学習・推論API・ドリフト検知を実演
結論
MLOpsとは、機械学習モデルを「作って終わり」にせず、学習・登録・デプロイ・監視・再学習を仕組みにして回し続けるための考え方と手法です。DevOps との違いは、コードに加えて「データ」と「モデル」の変化を扱い、精度が落ちたら学習し直す(CT:継続的トレーニング)点にあります。
この記事でわかること
- MLOpsの意味・DevOpsとの違い・成熟度レベル・主要ツール
- MLflowで実験記録→モデル登録→推論APIを動かした原文
- データドリフトで精度が0.81→0.69に落ち、再学習で戻る数値
機械学習のモデルは、作った時点がいちばん精度が高く、世の中のデータが変わるにつれて少しずつ外れていきます。MLOps は、その「外れ」に気づき、学習し直し、安全に入れ替えるところまでを、人の記憶ではなく仕組みで回すための考え方です。
flowchart TD
D[データ収集・準備] --> T[学習]
T --> E[評価]
E --> R[モデル登録]
R --> P[デプロイ<br>推論API]
P --> M[監視<br>ドリフト・精度]
M -->|劣化を検知| D
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| MLOpsとは | 機械学習モデルの開発から運用・改善までを継続して回す仕組み | MLOpsとは |
| MLOpsとDevOpsの違い | データとモデルも管理対象。CI/CD に CT(継続的トレーニング)が加わる | MLOpsとDevOpsの違い |
| MLOpsのライフサイクル | データ→学習→評価→登録→デプロイ→監視→再学習の輪 | MLOpsのライフサイクル |
| MLOpsのレベル(成熟度) | Google は0〜2の3段階、Microsoft は0〜4の5段階 | MLOpsの成熟度レベル |
| MLOpsツールは何がある | 実験管理・データ管理・パイプライン・推論・監視の役割別に選ぶ | MLOpsツール一覧 |
| まず何から始める | 実験の記録とモデルの登録(MLflow なら pip で始められる) | MLflowの環境を作る |
| データドリフトとは | 入力の分布や、入力と正解の関係が学習時から変わること | モデル監視とデータドリフト |
| 精度が落ちたら | 新しいデータで再学習し、比べてから登録・切り替え。前の版に戻せるようにする | 再学習して入れ替える |
確認環境:Ubuntu 24.04.5 LTS(Intel Xeon 2.10GHz・2コア)の上に Python 3.13.18 の venv を作り、MLflow 3.17.2・scikit-learn 1.9.1・pandas 3.1.0・NumPy 2.5.4・SciPy 1.18.1・skops 0.16.0 を入れました。トラッキングサーバー(SQLite)と推論 API を 127.0.0.1 で起動し、架空の顧客データ(解約予測)で実験の記録・モデル登録・推論・ドリフトの検知・再学習を動かしました。応答は curl 8.5.0 で確かめ、学習とサーバーの実行ではテレメトリーを環境変数で止め、クラウドの MLOps 製品には何も送っていません。成熟度レベルは Google Cloud と Microsoft Learn の公開資料、各ツールの説明は各公式ドキュメントで確かめています。図は mermaid 11.16.0 で描画を確かめました(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「MLOpsとは|主要ツールとMLflowで学習・推論API・ドリフト検知を実演」
MLOpsとは(意味と、なぜ必要か)
この節では、MLOps という言葉の意味と、機械学習に運用の仕組みが要る理由が分かります。
MLOps(エムエルオプス、Machine Learning Operations)は、機械学習(ML)と運用(Operations)を合わせた言葉で、モデルを本番で安定して動かし続けるための文化・手順・道具の集まりです。 AWS は「ML の開発と、デプロイ・運用を一つにする文化と実務」、Red Hat は「DevOps と GitOps の原則に着想を得た、モデルのデプロイと保守を最適化するワークフロー」と説明しています。
機械学習のモデルは、コードを1行も変えなくても、入ってくるデータが変われば当たらなくなります。この記事の実験でも、データの性質だけが変わった月に ROC AUC(予測の当たり具合を0.5〜1で表す指標)が 0.813 から 0.690 に下がりました。
1PoCで止まらないため
ノートブックで精度が出ても、API・データの流れ・監視が無ければ使われない。届けるまでを手順にする。
2精度の低下に気づくため
データドリフト(データの変化)でモデルは静かに外れていく。監視が無いと苦情で初めて気づく。
3再現し、戻せるため
どのデータ・コード・設定で作ったモデルかを記録し、問題が出たら前の版へすぐ戻せるようにする。
MLOpsとDevOpsの違い(CI/CD/CT)
この節では、MLOps が DevOps と何が同じで何が違うのかが、管理する対象とパイプラインの違いで分かります。
DevOps が「コード」の変更を素早く安全に届ける仕組みなら、MLOps は「コード・データ・モデル」の3つの変更を扱う仕組みです。 Google Cloud の資料は、ML システムでは CI(継続的インテグレーション)がコードだけでなくデータ・スキーマ・モデルの検証も含み、CD(継続的デリバリー)がモデル単体ではなく学習パイプラインを届けるものになり、さらに ML 特有の CT(Continuous Training:継続的トレーニング)が加わる、と説明しています。
| DevOps | MLOps | |
|---|---|---|
| 管理する対象 | コード・設定・インフラ | コード・設定・インフラ+データ+モデル |
| 品質が落ちる原因 | コードの変更(バグ) | コードの変更+データの変化(ドリフト) |
| テスト | 単体・結合テスト | 左に加え、データの検証・モデルの精度の検証 |
| パイプライン | CI/CD | CI/CD+CT(自動の再学習) |
| 開発の進め方 | 仕様に沿って作る | 実験を何度も試す(記録が欠かせない) |
| 本番の監視 | エラー率・応答時間 | 左に加え、入力の分布・予測の分布・精度 |
「動いているか」と「当たっているか」
実験では、精度が落ちた月も推論 API はずっと200を返していた
CI/CD そのものの考え方と最初のパイプラインの作り方は、関連記事「CI/CDとは|GitHub Actionsで最初のパイプラインを作るまで」で扱っています。
MLOpsのライフサイクル(データ→学習→評価→登録→デプロイ→監視→再学習)
この節では、MLOps で回す一連の工程と、各工程で何を残すのかが分かります。
MLOps のライフサイクルは、最後の「監視」が最初の「データ」に戻る輪です。 Google Cloud の資料は8つの手順(データ抽出・分析・準備・学習・評価・検証・提供・モニタリング)を挙げています。この記事では次の7段で動かしました。
データを集めて準備する
学習に使ったデータの範囲・作り方・版を残す。後で「何で学習したモデルか」をたどるため。
学習する
アルゴリズムとハイパーパラメーター(学習の設定値)を変えて何度も試す。
評価する
学習に使っていないデータで精度を測り、候補を比べる。
登録する
採用したモデルをレジストリに版として登録し、「本番用」などの名札(エイリアス)を付ける。
デプロイする
推論 API として起動し、ほかのシステムから呼べるようにする。
監視する
入力の分布の変化(ドリフト)と、正解が分かった後の精度を見る。
再学習して入れ替える
新しいデータで学習し、今のモデルと同じデータで比べ、勝った方を本番に。前の版へ戻せるようにしておく。
MLOpsの成熟度レベル(Google・Microsoftの公開資料)
この節では、MLOps がどこまで自動化されているかを表す「レベル」の考え方が分かります。
よく引用されるのは、Google Cloud のアーキテクチャ資料「MLOps: ML における継続的デリバリーと自動化のパイプライン」の3段階です。 AWS の解説も同じ3段階で説明しています。
- 0レベル0:手動のプロセスノートブックとスクリプトで手作業。リリースは年に数回。モデルの性能を能動的に監視しない
- 1レベル1:ML パイプラインの自動化学習パイプラインごとデプロイし、新しいデータで自動的に再学習(CT)。データとモデルの検証、メタデータ管理、必要に応じて Feature Store
- 2レベル2:CI/CD パイプラインの自動化パイプライン自体のコードも CI/CD で検証・配布。ソース管理・テストとビルド・デプロイ・モデルレジストリ・Feature Store・メタデータストア・オーケストレーター
Google の資料は、再学習のきっかけとして、手動・スケジュール・新しい学習データの到着・性能の低下・データの分布の大きな変化の5つを挙げています。この記事の実験は後ろの2つを再現しています。
Microsoft Learn の「MLOps 成熟度モデル」(2026年6月3日更新)は、もっと細かい5段階で表しています。
| レベル | 名前 | 要点 |
|---|---|---|
| 0 | MLOps なし | ビルド・デプロイ・テスト・学習がすべて手動 |
| 1 | DevOps はあるが MLOps はない | アプリのビルドとテストは自動。モデルはデータチーム頼みで再現が難しい |
| 2 | 自動トレーニング | 学習が自動化され、追跡と再現ができる。リリースは手動 |
| 3 | モデルの自動デプロイ | モデルのリリースも CI/CD で自動。デプロイからデータまでたどれる |
| 4 | 完全に自動化された MLOps | 監視とドリフトの検知が再学習を自動で起動する |
レベルは高いほどよいわけではありません。 月に1回しか使わないモデルにレベル2の仕組みを作ると、保守のほうが重くなります。まず「記録」と「登録」をそろえ、ドリフトが起きるかを見てから自動化します。
MLOpsツール一覧(役割別)
この節では、MLOps のツールを役割ごとに整理し、どれが何を担当するのかが分かります。
MLOps ツールは「全部入りの製品」か「役割ごとに組み合わせる」かで選びます。 次の表は各公式ドキュメントの説明に沿った事実です(2026年10月29日時点)。
| 役割 | ツール | 形態・公式の説明 |
|---|---|---|
| 実験管理・モデル登録・推論 | MLflow | OSS(Apache-2.0)。実験の記録、モデルレジストリ、モデルのパッケージ化と推論。最新は 3.17.2(2026年10月21日公開、PyPI) |
| Kubernetes 上の ML 基盤 | Kubeflow | OSS。Pipelines・Trainer・Katib・Notebooks などの部品の集まり。公式サイトは CNCF の graduated になったと告知 |
| データ・パイプラインの版管理 | DVC | OSS。Git に近い操作で大きなデータの版管理・パイプライン・実験の追跡 |
| 特徴量の管理(Feature Store) | Feast | OSS。学習時と推論時で同じ特徴量を一貫して渡す |
| データ品質・ドリフトの監視 | Evidently | OSS(Apache-2.0)。Python ライブラリ単体でも、セルフホストの監視基盤としても使える |
| クラウドの統合製品 | Amazon SageMaker・Vertex AI・Azure Machine Learning | 各クラウドの ML 基盤。SageMaker は Experiments・Pipelines・Model Registry を持つ(AWS) |
| 実験管理(SaaS) | Weights & Biases など | 実験の記録と可視化をクラウドのサービスとして提供 |
| LLM アプリの記録・評価 | Langfuse・MLflow など | LLM の呼び出しの記録(トレース)と評価。LLMOps と呼ばれる領域 |
MLflow から始める
pip で入り、ノート PC 1台で動く
実験の記録・登録・推論 API までそろう
ワークフローの自動実行は持たない(別の道具と組む)
Kubeflow から始める
Kubernetes のクラスターが前提
多段のパイプライン・分散学習・GPU の割り当てに強い
基盤を運用する人手が要る
LLM アプリの記録と評価は、関連記事「Langfuseとは|使い方・料金・セルフホストとSDKの送信を実例で解説」で扱っています。ここからは、MLflow を手元で動かして、ライフサイクルを1周させます。
MLflowの環境を作る(インストールとトラッキングサーバーの起動)
この節では、MLflow を venv に入れ、記録の置き場所になるトラッキングサーバーを手元だけで起動する手順と、その重さが分かります。
python3 -m venv venv
PIP_CACHE_DIR=./pipcache ./venv/bin/pip install mlflow scikit-learn pandas
# 使用状況の送信(テレメトリー)を止める
export MLFLOW_DISABLE_TELEMETRY=true DO_NOT_TRACK=true
mlflow server --host 127.0.0.1 --port 45629 \
--backend-store-uri sqlite:///$PWD/mlflow.db \
--artifacts-destination $PWD/artifacts
MLflow は 3.2.0 から、匿名の使用状況を既定で送信します(MLflow 公式「Usage Tracking」)。モデルやコードの中身は送られないと書かれていますが、社内のルールで外部への送信を止めたいときは、MLFLOW_DISABLE_TELEMETRY=true か DO_NOT_TRACK=true を、サーバーと学習スクリプトの両方の環境に設定します。手元のソースコードでも、この2つの変数を見て送信を止める処理を確かめました。変数を設定する前に一度 import mlflow しただけで、~/.config/mlflow/telemetry.json(installation_id)が作られていたため、変数は最初の実行より前に設定します。
起動直後のログです。3.5.0 から入った保護(Host ヘッダーの検査など)が、localhost だけを許す設定で有効になっています。
Registry store URI not provided. Using backend store URI.
INFO mlflow.store.db.utils: Creating initial MLflow database tables...
[MLflow] Security middleware enabled with default settings (localhost-only). To allow connections from other hosts, use --host 0.0.0.0 and configure --allowed-hosts and --cors-allowed-origins.
INFO: Uvicorn running on http://127.0.0.1:45629 (Press CTRL+C to quit)
$ curl http://127.0.0.1:45629/health
OK
$ curl http://127.0.0.1:45629/version
3.17.2
$ curl -i -H 'Host: evil.example.com' http://127.0.0.1:45629/api/2.0/mlflow/experiments/search
HTTP/1.1 403 Forbidden
Invalid Host header - possible DNS rebinding attack detected
起動の速さとメモリ(実測)
| 起動のしかた | /health が200になるまで | プロセス数・メモリ(PSS) |
|---|---|---|
| 既定(初回。DB の表を作る。メモリは学習後に測定) | 約1分半 | 15・約2,190MB |
| 既定(2回目以降) | 37.0秒 | 15・1,683MB |
--workers 1+ジョブ実行オフ |
26.6秒 | 2・348MB |
既定では uvicorn のワーカーが4つと、サーバー側のジョブを動かすプロセス(job_runner と huey)が8つ起動しました。個人の検証やメモリの少ない PC では、--workers 1 と環境変数 MLFLOW_SERVER_ENABLE_JOB_EXECUTION=false で軽くできます(ジョブ実行を使う機能は動かなくなります)。「Uvicorn running」と表示されてから実際に接続できるまでにも時間がかかり、その間の curl は Connection refused になりました。 起動を待つスクリプトは、/health が200になるまで繰り返し確かめる形にします。
実験を記録する(MLflow Tracking)
この節では、学習のたびにパラメーター・指標・モデルを自動で残す書き方と、記録した実験を比べる方法が分かります。
架空の通信サービスの顧客データ(契約月数・月額料金・問い合わせ回数・利用時間)から、解約するかどうかを予測するモデルを3種類学習しました。中心の部分です。
import mlflow, mlflow.sklearn
from mlflow.models import infer_signature
mlflow.set_tracking_uri("http://127.0.0.1:45629")
mlflow.set_experiment("churn")
for name, model in candidates.items(): # ロジスティック回帰2つ・ランダムフォレスト
with mlflow.start_run(run_name=name):
model.fit(X_tr, y_tr)
pred = model.predict(X_te); proba = model.predict_proba(X_te)[:, 1]
mlflow.log_params({"model": name, "n_train": len(X_tr), "data_seed": 1})
mlflow.log_metrics({"accuracy": accuracy_score(y_te, pred),
"f1": f1_score(y_te, pred),
"roc_auc": roc_auc_score(y_te, proba)})
mlflow.sklearn.log_model(
model, name="model",
signature=infer_signature(X_tr, pred), # 入力の列名と型を残す
input_example=X_tr.head(2),
skops_trusted_types=["sklearn.tree._tree.Tree"] if name.startswith("rf") else None)
記録した実験は mlflow.search_runs で表として取り出せます。同じスクリプトを3回流したので、同じ名前の run が3つずつ並びました(logreg_C1.0 の2行を省いています)。
tags.mlflow.runName status metrics.accuracy metrics.f1 metrics.roc_auc metrics.fit_sec
logreg_C0.1 FINISHED 0.7888 0.6071 0.8322 0.0669
logreg_C0.1 FINISHED 0.7888 0.6071 0.8322 0.0652
logreg_C0.1 FINISHED 0.7888 0.6071 0.8322 0.1225
logreg_C1.0 FINISHED 0.7880 0.6062 0.8322 0.0088
rf_depth5 FINISHED 0.7880 0.5800 0.8286 0.7008
rf_depth5 FAILED 0.7880 0.5800 0.8286 0.5966
rf_depth5 FAILED 0.7880 0.5800 0.8286 0.7014
best: b935d71035a94a5ca5399155ea795d52 logreg_C0.1
同じ seed なら同じ値
データの seed とモデルの random_state を固定したので、3回とも精度は小数4桁まで一致。学習時間だけが毎回違った。
再現できることの確認
失敗した run も残る
モデルの保存で止まった run は FAILED のまま、保存より前に書いた指標は残っていた。
status で絞り込む
単純なモデルが勝った
ROC AUC はロジスティック回帰 0.8322、ランダムフォレスト 0.8286。保存したファイルは 26KB と 4.3MB。
軽さも比べる材料
一番よいものは filter_string="status = 'FINISHED'" と order_by=["metrics.roc_auc DESC"] で選べます。ブラウザで http://127.0.0.1:45629/ を開くと、同じ記録を画面で比べられます。
モデルと一緒に保存されたもの
log_model 1回で、次のファイルがアーティファクト(成果物の置き場)に保存されました。
| ファイル | 中身 |
|---|---|
MLmodel |
読み込み方(flavor)・シグネチャ・保存形式(serialization_format: skops)・MLflow の版 |
model.skops |
学習済みモデル本体(26,029 バイト) |
requirements.txt |
mlflow==3.17.2・numpy==2.5.4・pandas==3.1.0・scikit-learn==1.9.1・scipy==1.18.1・skops==0.16.0 |
serving_input_example.json |
推論 API にそのまま送れる入力の例(dataframe_split の形) |
モデルを登録する(MLflow Model Registryとエイリアス)
この節では、採用したモデルを版として登録し、「本番用」の名札を付け替えて使う方法が分かります。
モデルレジストリは、モデルに名前と版番号を付けて管理する台帳です。 同じ名前で登録するたびに版が1つずつ増え、エイリアス(例:champion)という付け替えできる名札で「今の本番」を指します。
from mlflow import MlflowClient
c = MlflowClient()
mv = mlflow.register_model(f"runs:/{run_id}/model", "churn-model")
c.set_registered_model_alias("churn-model", "champion", mv.version)
c.set_model_version_tag("churn-model", mv.version, "validation_status", "approved")
m = mlflow.pyfunc.load_model("models:/churn-model@champion") # 名札で読み込む
Successfully registered model 'churn-model'.
WARNING mlflow.tracking._model_registry.fluent: Run with id b935d71035a94a5ca5399155ea795d52 has no artifacts at artifact path 'model', registering model based on models:/m-9380d40acb524efea376e9186b43f368 instead
Created version '1' of model 'churn-model'.
alias -> 1
['tenure_months': double (required), 'monthly_fee': double (required), 'support_calls': double (required), 'usage_hours': double (required)]
pred: [0 0 0]
WARNING は、MLflow 3 でモデルが run とは別の「LoggedModel」(m- で始まる ID)として保存されるためで、登録は成功しています。log_model(..., registered_model_name="churn-model") と書けば、保存と登録を1回で行えます(再学習の節で使っています)。
「本番」の指し方
transition_model_version_stage(..., "Production") で Stage を使うset_registered_model_alias(名前, "champion", 版) で名札を付け、models:/名前@champion で読む手元では FutureWarning:「deprecated since 2.9.0. Model registry stages will be removed in a future major release.」
推論APIとしてデプロイする(mlflow models serve)
この節では、登録したモデルを1つのコマンドで REST API にし、curl で予測を受け取る方法と、入力を間違えたときの応答が分かります。
export MLFLOW_TRACKING_URI=http://127.0.0.1:45629
mlflow models serve -m "models:/churn-model@champion" \
-h 127.0.0.1 -p 45630 --env-manager local
--env-manager を省くと既定は virtualenv で、モデルの requirements.txt から新しい環境を作ります(パッケージを取りに行きます)。今の venv をそのまま使うときは local です。起動から /ping が200になるまで 16.5 秒でした。
$ curl -X POST http://127.0.0.1:45630/invocations -H 'Content-Type: application/json' \
-d '{"dataframe_split":{"columns":["tenure_months","monthly_fee","support_calls","usage_hours"],
"data":[[58,5061,1,21.0],[3,7200,5,4.5]]}}'
{"predictions": [0, 1]}
$ curl -X POST http://127.0.0.1:45630/invocations -H 'Content-Type: application/json' \
-d '{"dataframe_records":[{"tenure_months":3,"monthly_fee":7200,"support_calls":5,"usage_hours":4.5}]}'
{"predictions": [1]}
$ printf 'tenure_months,monthly_fee,support_calls,usage_hours\n3,7200,5,4.5\n' | \
curl -X POST http://127.0.0.1:45630/invocations -H 'Content-Type: text/csv' --data-binary @-
{"predictions": [1]}
MLflow の公式ドキュメントによると、推論サーバーのエンドポイントは /invocations(予測)・/ping と /health(死活確認)・/version(MLflow の版)の4つです。JSON の入力は dataframe_split・dataframe_records・instances・inputs のどれか1つで、dataframe_records は列の順番が保証されないため推奨されていません。
応答の速さ(実測)
| 1回に送った行数 | 中央値 | 95パーセンタイル |
|---|---|---|
| 1行 | 5.3ms | 7.5ms |
| 100行 | 4.9ms | 6.9ms |
| 1,000行 | 7.1ms | 8.6ms |
| 10,000行 | 22.4ms | 25.7ms |
同じマシンから30回(10,000行は10回)送った値です。まとめて送るほど1行あたりは安くなります。 夜間のバッチ推論なら、API を通さず mlflow.pyfunc.load_model で読み込んで predict する方法もあります。
モデル監視とデータドリフトの検知(scikit-learn・SciPyで再現)
この節では、データドリフトの2つの種類と、それぞれが監視の数値にどう表れるかが、実際の値で分かります。
データドリフトには、入力の分布が変わる「データ(共変量)ドリフト」と、入力と正解の関係が変わる「コンセプトドリフト」があります。 学習に使ったデータ(5,000件)を基準に、本番の3つの月(各2,000件)を作り、各特徴量の分布の差と、正解が分かった後の精度を測りました。
- 月1・変化なし:学習時と同じ作り方のデータ。
- 月2・値上げ:月額料金だけが平均1,500円上がった(入力の分布が変わる)。
- 月3・関係の変化:入力の分布は同じまま、「利用時間が長い人ほど解約する」へ関係が逆転した(競合への乗り換えを想定)。
import numpy as np
from scipy.stats import ks_2samp
def psi(ref, cur, bins=10):
"""Population Stability Index:基準データの十分位で区切って比べる"""
edges = np.unique(np.quantile(ref, np.linspace(0, 1, bins + 1)))
edges[0], edges[-1] = -np.inf, np.inf
r = np.histogram(ref, edges)[0] / len(ref)
c = np.histogram(cur, edges)[0] / len(cur)
r, c = np.clip(r, 1e-6, None), np.clip(c, 1e-6, None)
return float(np.sum((c - r) * np.log(c / r)))
for col in COLS:
ks = ks_2samp(ref[col], cur[col]) # 2つの分布が同じかの検定
mlflow.log_metrics({f"psi_{col}": psi(ref[col].values, cur[col].values),
f"ks_{col}": ks.statistic})
1_変化なし: accuracy=0.755 roc_auc=0.813 実際の解約率=0.327 予測の解約率=0.251
2_値上げ(fee+1500): accuracy=0.738 roc_auc=0.810 実際の解約率=0.476 予測の解約率=0.471
3_関係の変化: accuracy=0.703 roc_auc=0.690 実際の解約率=0.326 予測の解約率=0.235
| 月 | 月額料金の PSI | ほかの3列の PSI | KS 検定(月額料金) | ROC AUC |
|---|---|---|---|---|
| 月1・変化なし | 0.007 | 0.002〜0.013 | D=0.023・p=0.45 | 0.813 |
| 月2・値上げ | 1.445 | 0.002〜0.009 | D=0.466・p=3.9e-281 | 0.810 |
| 月3・関係の変化 | 0.001 | 0.002〜0.008 | D=0.011・p=0.99 | 0.690 |
1分布が大きく動いても精度は保てた
月2は月額料金の PSI が 1.445 と大きく動いたが、ROC AUC は 0.810。解約率の上昇(0.327→0.476)にモデルの予測(0.471)も沿っていた。
2分布が同じでも精度が落ちた
月3は全列で PSI が 0.01 未満、KS の p 値も高く「変化なし」に見えたが、ROC AUC は 0.690 に下がった。入力の監視では見つからない。
3正解との突き合わせが要る
コンセプトドリフトは、後から分かった正解と予測を比べて初めて見える。正解が届く仕組みも MLOps の一部。
PSI は「0.1 未満は小さい、0.25 以上は大きい」という目安がよく使われますが、公的な基準ではありません。自分のデータで、どれくらい動くと精度に響いたかを記録して決めます。
KS 検定の p 値は件数で変わる
月額料金を平均60円だけずらしたデータを、件数を変えて基準と比べました。
n= 500 fee+60円: KS=0.062 p=5.6e-02 PSI=0.028
n= 5000 fee+60円: KS=0.038 p=1.6e-03 PSI=0.008
n= 50000 fee+60円: KS=0.025 p=6.2e-03 PSI=0.005
n=200000 fee+60円: KS=0.026 p=2.7e-03 PSI=0.005
件数が多いと、業務上は無視できる60円の差でも p 値が0.01を下回ります。 本番のデータは件数が多いため、p 値だけで警報を出すと鳴りっぱなしになります。差の大きさ(KS の D 値や PSI)と組み合わせて判断します。p 値の読み方は、関連記事「ABテストとは|やり方・サンプルサイズ・有意差の判定をPythonで実演」で扱っています。
監視の指標は、MLflow の別の実験(churn-monitoring)に月ごとに記録し、画面で並べて見られるようにしました。推論 API の応答時間やエラー率など、システムとしての監視は、関連記事「Prometheus Grafana入門|監視構築とPromQL一覧」で扱っています。
再学習して入れ替える(エイリアスの切り替えとロールバック)
この節では、精度が落ちたときに再学習し、比べてから本番を切り替え、前の版へ戻せるようにする手順が分かります。
関係が変わった後のデータ(5,000件)で同じ設定のモデルを学習し直し、精度が落ちた月3のデータで、今の本番(版1)と比べました。
with mlflow.start_run(run_name="logreg_C0.1_retrain"):
m = make_pipeline(StandardScaler(), LogisticRegression(C=0.1)).fit(new[COLS], new.churn)
mlflow.log_params({"model": "logreg_C0.1", "data_seed": 5, "trigger": "concept_drift"})
info = mlflow.sklearn.log_model(m, name="model", signature=..., input_example=...,
registered_model_name="churn-model") # 版2として登録
c.set_registered_model_alias("churn-model", "previous", 1) # 戻し先を残す
c.set_registered_model_alias("churn-model", "champion", info.registered_model_version)
champion_v1: accuracy=0.703 roc_auc=0.690
challenger: accuracy=0.757 roc_auc=0.802
Registered model 'churn-model' already exists. Creating a new version of this model...
Created version '2' of model 'churn-model'.
{'champion': '2', 'previous': '1'}
再学習したモデル(challenger)は ROC AUC が 0.690 から 0.802 に戻りました。REST API でも、名札がどの版を指すかを確かめられます。
$ curl 'http://127.0.0.1:45629/api/2.0/mlflow/registered-models/alias?name=churn-model&alias=champion'
{
"model_version": {
"name": "churn-model",
"version": "2",
"current_stage": "None",
"source": "models:/m-72601d985d9744a3987853d0b160c759",
"run_id": "ca74dcaee84b44b1b6f2f20d19e4d0f0",
"status": "READY",
"aliases": ["champion"]
}
}
名札を替えても、動いている推論 API は替わらない
版1と版2で答えが分かれる顧客(利用時間45時間)を、切り替えの前から動いていた推論 API に送りました。
切り替え前から動いていた推論 API: {"predictions": [0]} ← 版1の答え
推論 API を止めて起動し直した後: {"predictions": [1]} ← 版2の答え
mlflow models serve -m "models:/churn-model@champion" は、起動したときに名札が指していた版を読み込み、その後は名札を見に行きませんでした。 エイリアスを替えたら、推論 API を起動し直す(またはコンテナを入れ替える)までが切り替えの手順です。戻すときは champion を版1に付け直して起動し直します。新しい版へ一部の通信だけを流して様子を見る方法は、関連記事「デプロイ方式の種類と違い|ブルーグリーン・カナリア・ローリング比較」で扱っています。
- 1同じデータで比べてから切り替える今の本番と新しいモデルを、同じ評価データで比べる。新しい方が負けたら登録だけして名札は動かさない
- 2戻し先の名札を残すprevious のような名札を前の版に付けておくと、戻す版を探さずに済む
- 3再学習のきっかけを記録するtrigger=concept_drift のように、なぜ学習し直したかを run のパラメーターに残す
- 4推論 API を起動し直す名札の変更だけでは、動いている API の答えは変わらなかった
MLflowのよくあるエラーとつまずき(原文つき)
この節では、手元で実際に出たエラーの原文と、その原因・直し方が一覧で分かります。
| 出た文言(原文) | 原因 | 直し方 |
|---|---|---|
| The saved sklearn model references untrusted types. … Untrusted types found in the file: ['sklearn.tree._tree.Tree'] | 3.17 の既定の保存形式 skops が、決定木系の内部の型を「信頼済み」と見なさない | 自分で作ったモデルなら skops_trusted_types=["sklearn.tree._tree.Tree"] を渡す |
| Model is missing inputs ['usage_hours'].(400・SCHEMA_ENFORCEMENT_FAILED) | 推論 API に送った列が、シグネチャの列より少ない | serving_input_example.json と同じ列で送る |
| Failed to convert column tenure_months to type 'float64'. … could not convert string to float: 'three'(400) | 数値の列に文字列を送った | 送る側で型をそろえる |
| The input must be a JSON dictionary with exactly one of the input fields {'instances', 'dataframe_split', 'dataframe_records', 'inputs'}.(400) | {"data": …} のように、決まったキーで包んでいない。MLflow 1 系の形 |
dataframe_split などで包む |
| This predictor only supports the following content types: … Got 'text/plain'.(415) | Content-Type の指定が違う | application/json か text/csv を付ける |
| Registered model alias prod not found. | 存在しないエイリアスで読み込んだ | 名札の名前を確かめる(大文字・小文字も) |
| Failed to establish a new connection: [Errno 111] Connection refused | トラッキングサーバーが動いていない・起動の途中・ポート違い | /health が200になるまで待つ。MLFLOW_TRACKING_URI を確かめる |
| Invalid Host header - possible DNS rebinding attack detected(403) | 許可されていない Host 名でサーバーに接続した | 別の PC から使うなら --host 0.0.0.0 と --allowed-hosts を設定 |
| FutureWarning: … transition_model_version_stage is deprecated since 2.9.0. | Stage(Staging・Production)による管理は将来なくなる | エイリアスとタグに移す |
skops で信頼済みにする型は、自分で作ったモデルのものだけに絞ります。 保存形式を pickle に変える方法もありますが、MLflow は「読み込むときに任意のコードが実行されうる」と警告を出します。出どころが分からないモデルファイルは読み込みません。
MLOpsの始め方:判断表
この節では、自分のチームの状況から、まず何を入れるかを決める目安が分かります。
| 今の状況 | まずやること | 道具の例 |
|---|---|---|
| ノートブックの結果をメモで残している | 実験の記録を自動にする(レベル0の中の改善) | MLflow Tracking |
| どのファイルが本番のモデルか分からない | モデルレジストリに登録し、エイリアスで本番を指す | MLflow Model Registry |
| 学習データがどれだったか再現できない | データの版管理と seed の記録 | DVC・データの保存先の版管理 |
| 予測は API で毎日使われている | 入力の分布と、正解が分かった後の精度を監視する | 自作の PSI・KS、Evidently |
| ドリフトが定期的に起きている | 再学習をパイプラインにし、スケジュールか監視で起動(レベル1) | Kubeflow Pipelines・クラウドのパイプライン |
| モデルとパイプラインが何本もある | パイプラインのコード自体を CI/CD に載せる(レベル2) | CI/CD+オーケストレーター |
| 月に1回、人が見て使うだけ | 記録と登録だけ。自動化は見送る | MLflow のみ |
よくある質問
この節では、MLOps について検索でよく聞かれる疑問に短く答えます。
Q. MLOpsとは何ですか? A. 機械学習モデルの開発から本番での運用、監視、再学習までを、継続して回せる仕組みにする考え方と手法です。DevOps の考え方を、データとモデルにも広げたものです。
Q. MLOpsとDevOpsの違いは何ですか? A. DevOps はコードの変更を扱い、MLOps はコードに加えてデータとモデルの変化を扱います。データが変わると、コードを変えなくても精度が落ちるため、CI/CD に加えて CT(継続的トレーニング)と精度の監視が加わります。
Q. MLOpsのツールは何から入れればよいですか? A. 実験の記録とモデルの登録から始めるのが入りやすいです。MLflow は pip で入り、1台の PC でトラッキングサーバー・レジストリ・推論 API まで動きました。Kubernetes を使っているなら Kubeflow、クラウドに寄せるなら各クラウドの ML 基盤も候補です。
Q. MLflowは無料ですか? A. Apache-2.0 のオープンソースで、自分のサーバーで動かす分に利用料はかかりません(サーバー代と保守は自分持ち)。各社のマネージド版はそれぞれの料金に従います。
Q. データドリフトとコンセプトドリフトの違いは? A. データドリフトは入力の分布が変わること、コンセプトドリフトは入力と正解の関係が変わることです。この記事の実験では、前者は PSI で見つかって精度はほぼ落ちず、後者は PSI では見つからず精度が 0.813 から 0.690 に落ちました。
Q. 正解(ラベル)がすぐに分からない場合、監視はどうしますか? A. 入力の分布(PSI・KS)と、予測の分布(予測した解約率など)の変化を先に見ます。それだけではコンセプトドリフトを見逃すため、遅れてでも正解を集め、精度を測る仕組みを作ります。
Q. 再学習はどれくらいの頻度で行えばよいですか? A. 決まった値はありません。Google の資料は、手動・スケジュール・新しいデータの到着・性能の低下・分布の大きな変化の5つをきっかけに挙げています。まず監視で「どれくらいで精度が落ちるか」を測り、それに合わせて決めます。
Q. モデルレジストリのStageは使わないほうがよいですか? A. MLflow 3.17.2 では、Stage を変える関数を呼ぶと「2.9.0 から非推奨で、将来のメジャーリリースで削除される」という FutureWarning が出ました。新しく作るならエイリアス(champion など)とタグを使います。
Q. LLMOpsとMLOpsの違いは? A. LLMOps は LLM を使うアプリの運用で、プロンプトの版管理・呼び出しの記録(トレース)・回答の評価が中心です。自分で学習したモデルの再学習が中心の MLOps とは、監視と評価の中身が違います。
まとめ
- MLOps は、機械学習モデルの学習・登録・デプロイ・監視・再学習を輪にして回す仕組み。DevOps との違いは、データとモデルの変化を扱い、CT(継続的トレーニング)が加わる点。
- 成熟度は Google の0〜2(手動→パイプラインの自動化→CI/CD の自動化)が基本。いきなり自動化せず、記録と登録から始める。
- MLflow 3.17.2 は pip で入り、127.0.0.1 だけで実験の記録・モデル登録・推論 API まで動いた。既定のサーバーは15プロセス・約1.7GB、
--workers 1とジョブ実行オフで2プロセス・348MB。 - データの分布が大きく動いても精度は保てた一方、分布が同じまま関係が変わると ROC AUC は 0.813→0.690 に落ちた。入力の監視と、正解との突き合わせの両方が要る。
- 再学習で 0.802 まで戻った。エイリアスを替えても動いている推論 API は前の版のままで、起動し直して初めて切り替わった。
参考・出典
確認日はいずれも 2026年10月29日です。
- Google Cloud「MLOps: ML における継続的デリバリーと自動化のパイプライン」(2024年8月28日更新):https://docs.cloud.google.com/architecture/mlops-continuous-delivery-and-automation-pipelines-in-machine-learning?hl=ja
- Microsoft Learn「MLOps 成熟度モデル」:https://learn.microsoft.com/ja-jp/azure/architecture/ai-ml/guide/mlops-maturity-model
- AWS「MLOps とは何ですか?」:https://aws.amazon.com/what-is/mlops/
- Red Hat「MLOps とは」:https://www.redhat.com/ja/topics/ai/what-is-mlops
- MLflow「Tracking Server」:https://mlflow.org/docs/latest/self-hosting/architecture/tracking-server/
- MLflow「Model Registry」:https://mlflow.org/docs/latest/ml/model-registry/
- MLflow「Deploy MLflow Model as a Local Inference Server」:https://mlflow.org/docs/latest/ml/deployment/deploy-model-locally/
- MLflow「Usage Tracking」:https://mlflow.org/docs/latest/community/usage-tracking/
- MLflow(PyPI):https://pypi.org/project/mlflow/
- Kubeflow「Introduction」:https://www.kubeflow.org/docs/started/introduction/
- DVC「User Guide」:https://doc.dvc.org/user-guide
- Feast「Introduction」:https://docs.feast.dev/
- Evidently「Introduction」:https://docs.evidentlyai.com/introduction
- scikit-learn「Model persistence」:https://scikit-learn.org/stable/model_persistence.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「MLOpsとは|主要ツールとMLflowで学習・推論API・ドリフト検知を実演」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。