結論

MLOpsとは、機械学習モデルを「作って終わり」にせず、学習・登録・デプロイ・監視・再学習を仕組みにして回し続けるための考え方と手法です。DevOps との違いは、コードに加えて「データ」と「モデル」の変化を扱い、精度が落ちたら学習し直す(CT:継続的トレーニング)点にあります。

この記事でわかること

  • MLOpsの意味・DevOpsとの違い・成熟度レベル・主要ツール
  • MLflowで実験記録→モデル登録→推論APIを動かした原文
  • データドリフトで精度が0.81→0.69に落ち、再学習で戻る数値

2026年10月29日更新|MLflow 3.17.2・scikit-learn 1.9.1・Python 3.13で実行確認

機械学習のモデルは、作った時点がいちばん精度が高く、世の中のデータが変わるにつれて少しずつ外れていきます。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(自動の再学習)
開発の進め方 仕様に沿って作る 実験を何度も試す(記録が欠かせない)
本番の監視 エラー率・応答時間 左に加え、入力の分布・予測の分布・精度

「動いているか」と「当たっているか」

DevOps の監視API が200を返し、応答が速ければ正常とみなす
↔
MLOps の監視200で速くても、予測が外れていれば異常。入力の分布と正解との突き合わせまで見る

実験では、精度が落ちた月も推論 API はずっと200を返していた

CI/CD そのものの考え方と最初のパイプラインの作り方は、関連記事「CI/CDとは|GitHub Actionsで最初のパイプラインを作るまで」で扱っています。

MLOpsのライフサイクル(データ→学習→評価→登録→デプロイ→監視→再学習)

この節では、MLOps で回す一連の工程と、各工程で何を残すのかが分かります。

MLOps のライフサイクルは、最後の「監視」が最初の「データ」に戻る輪です。 Google Cloud の資料は8つの手順(データ抽出・分析・準備・学習・評価・検証・提供・モニタリング)を挙げています。この記事では次の7段で動かしました。

1

データを集めて準備する

学習に使ったデータの範囲・作り方・版を残す。後で「何で学習したモデルか」をたどるため。

記録:データの版・乱数の種(seed)

2

学習する

アルゴリズムとハイパーパラメーター(学習の設定値)を変えて何度も試す。

記録:パラメーター・学習時間

3

評価する

学習に使っていないデータで精度を測り、候補を比べる。

記録:accuracy・F1・ROC AUC

4

登録する

採用したモデルをレジストリに版として登録し、「本番用」などの名札(エイリアス)を付ける。

記録:版・エイリアス・承認のタグ

5

デプロイする

推論 API として起動し、ほかのシステムから呼べるようにする。

確かめる:入力の形(シグネチャ)

6

監視する

入力の分布の変化(ドリフト)と、正解が分かった後の精度を見る。

記録:PSI・KS・精度

7

再学習して入れ替える

新しいデータで学習し、今のモデルと同じデータで比べ、勝った方を本番に。前の版へ戻せるようにしておく。

記録:きっかけ(trigger)・比較の結果

MLOpsの成熟度レベル(Google・Microsoftの公開資料)

この節では、MLOps がどこまで自動化されているかを表す「レベル」の考え方が分かります。

よく引用されるのは、Google Cloud のアーキテクチャ資料「MLOps: ML における継続的デリバリーと自動化のパイプライン」の3段階です。 AWS の解説も同じ3段階で説明しています。

  1. 0レベル0:手動のプロセスノートブックとスクリプトで手作業。リリースは年に数回。モデルの性能を能動的に監視しない
  2. 1レベル1:ML パイプラインの自動化学習パイプラインごとデプロイし、新しいデータで自動的に再学習(CT)。データとモデルの検証、メタデータ管理、必要に応じて Feature Store
  3. 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)が作られていたため、変数は最初の実行より前に設定します。

622MBpip install 直後の venv最大は pyarrow の 152MB。別に pip のキャッシュが 195MB
15個既定の mlflow server のプロセスuvicorn ×4+ジョブ実行。PSS 合計 1,683MB
2個軽くしたときのプロセス--workers 1+ジョブ実行オフ。PSS 合計 348MB

起動直後のログです。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
1

同じ seed なら同じ値

データの seed とモデルの random_state を固定したので、3回とも精度は小数4桁まで一致。学習時間だけが毎回違った。

再現できることの確認

2

失敗した run も残る

モデルの保存で止まった run は FAILED のまま、保存より前に書いた指標は残っていた。

status で絞り込む

3

単純なモデルが勝った

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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?