0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

chatgpt workのagent/skillでgoogle EEAT準拠のブログを生成してみた(第一弾)

0
Last updated at Posted at 2026-08-18

はじめに

HP/LP作成会社さんやWEBマーケターの方々から、AIが作成したブログはスパム判定されるので、自動作成はやめた方がよいと言われた。

一応本当なのか確かめるためにgoogleの公式見解を見た結果わかったのは、
以下の通りです。

①意味のない、②真似もののコンテンツを③大量に作成した場合はスパム認定する

ということでした。
このことからgoogleは、AIで作ろうが、人が作ろうが上記ならアウトであって誰が作ったかではないと明言していました。

googleが評価するのは以下の要素です

EEAT
https://bruceclay.jpn.com/column/about-eeat/

仮説として、EEATに準拠したブログならAIで作っても評価されるならどんどんAIで作成しようと思い立ちました。

そのために技術ブログとして有益なのは、

  • 課題設定と仮説設定
  • 検証計画
  • 検証環境の作成、
  • 検証実施
  • 検証結果の分析
  • 検証結果の考察
  • 分析と考察からの追加検証
  • 分析・考察結果からの結論
  • 一連のワークフロー実行に起こったトラブルシューティング
    っと考えました。
    そこで、EEATの基準を満たしているのか評価基盤をMLflowに任せることにしました。
    同様に、GA4のアクセス結果もMLflowに取り込むことで、品質が流入を生んでいるのか?も
    評価出来る基盤とすることにしました。

イメージとしてはこんな感じです。
スクリーンショット 2026-08-18 10.28.33.png

chatgpt workでは、AGENTS.mdとMEMORY.md、SKILL.md、resoucesディレクトリを配置して今後精度を高めていく予定です。

ここからは自動作成部分になります。

M5 Max・Python 3.14.6でMLflow 3.15.1公式Quickstartを動かす — uvとDocker Composeで実機検証

TL;DR

  • 何を調べたか: MLflow公式Tracking Quickstartを、Apple M5 Max / arm64 / Python 3.14.6で最後まで実行できるか検証した。
  • 何が分かったか: MLflow 3.15.1とscikit-learn 1.9.0の組み合わせで、autolog、手動logging、UI確認、モデル再読込、推論まで完了した。正式試行に加え、公開Repositoryを別portでCLIとNotebookからforward-testし、いずれも2 RunがFINISHED、2 Logged ModelsがREADY、予測が30/30一致した。
  • 読者が取るべき行動: Python依存関係をuvで固定し、MLflow Serverをlinux/arm64対応イメージのDocker Composeで起動する。MLflow 3ではmodelをRun artifactだけでなく「Logged Models」でも確認する。
  • 検証環境: Apple M5 Max / 128 GB / macOS 26.5.1 / arm64 / Python 3.14.6 / uv 0.12.2 / MLflow 3.15.1。

この記事で分かること

  • MシリーズMacでMLflow Tracking Quickstartを再現する構成
  • autologと手動loggingで実際に記録された内容
  • MLflow 3.15.1でmodelの保存を確認する場所
  • 実際に発生した警告と、実験条件のミスをどう扱ったか

対象読者

  • MLflowを初めて使うエンジニア
  • Apple Silicon MacでPython 3.14系を使っている人
  • pip直打ちではなくuvとDocker Composeで再現条件を残したい人

再現用成果物

Repositoryはuv.lockcompose.yaml、再利用可能なpackage、CLI、Notebook、テスト、raw JSON、MLflow画面を含む。記事のコードをコピーする方法と、実行済み成果物から検証する方法の両方を用意した。

なぜ検証したのか

MLflow公式Tracking Quickstartは、IrisデータセットとLogisticRegressionを使ってTrackingの基本を学べる。一方、MLflow 3.15.1のPyPI metadataにあるPython >=3.10だけでは、Python 3.14.6で学習、logging、model serialization、再読込まで動くとは判断できない。

また、常駐するMLflow Serverをhostへ直接インストールすると、Python環境とserver状態が混ざりやすい。そこで、clientはuv、serverはDocker Composeに分離し、M5 Maxのarm64環境で一連の操作を実行した。

Research Question

Apple M5 Max / arm64 / macOS 26.5.1 / Python 3.14.6で、MLflow 3.15.1の公式Tracking Quickstartを、package管理をuv、常駐Tracking ServerをDocker Composeへ置き換える以外は同等のAPI手順で最後まで実行できるか。

結論

今回固定したMLflow 3.15.1、scikit-learn 1.9.0、Iris/LogisticRegressionの範囲では完走できた。正式試行は2 RunともFINISHED、accuracyは0.9666666666666667、再読込modelの予測は30件すべて元modelと一致した。Docker containerもlinux/arm64 / aarch64で動作した。

これはM5 MaxのGPU性能を示す結果ではない。今回のLogisticRegressionはCPU処理であり、確認したのはarm64・Python 3.14.6・MLflow 3.15.1のQuickstart互換性である。

技術背景

公式仕様

MLflow公式Tracking Quickstartは、IrisデータでLogisticRegressionを学習し、次を確認する構成になっている。

  1. mlflow.sklearn.autolog()による自動記録
  2. MLflow UIでのRun確認
  3. parameter、metric、modelの手動記録
  4. mlflow.pyfunc.load_model()による記録済みmodelの再利用

MLflow 3.15.1のPyPI metadataはPython 3.10以上を要求する。ただし、今回使用するPython 3.14.6の全機能を明示的に保証する記述ではないため、実行結果で確認した。

今回確認したい点

  • Python 3.14.6でimportだけでなくmodel再読込まで完了するか
  • linux/arm64のMLflow Serverへhost clientから記録できるか
  • autologと手動loggingの両方で必要なEvidenceが残るか
  • 元modelと再読込modelの予測が一致するか

中核技術の役割

中核技術の定義

MLflow公式のTracking解説では、MLflow Trackingを、機械学習コードの実行時にparameter、code version、metric、output fileを記録し、後から結果を可視化するためのAPIとUIとして説明している。1回の実行をRunとして記録し、同じ目的のRunとmodelをExperimentへまとめる。

解決する課題

機械学習の検証では、学習commandが成功したという事実だけでは、どのparameterで、どのmetricが得られ、どのmodelが保存されたのかを後から比較できない。MLflow Trackingは、実行条件、結果、model、artifactをRunへ対応づけ、UIとAPIの両方から追跡できる形にする。

今回なぜ必要か

今回のResearch Questionは、Python 3.14.6でscriptがexit 0になるかだけではなく、autologと手動loggingでparameter・metric・modelが保存され、保存済みmodelを再読込して同じ予測を返せるかまで確認する問いである。そのためMLflowは単なるチュートリアル名ではなく、実行条件と保存結果を結び、互換性を判定するEvidenceを残す中核技術として必要になる。

仕組みとデータフロー

役割は次のように分かれる。

  1. hostのuv環境がIrisの分割、LogisticRegressionの学習、予測を行う。
  2. clientはHTTP経由でparameter、metric、modelをTracking Serverへ送る。
  3. ServerはRun metadataをSQLite、modelとartifactをvolumeへ保存する。
  4. UIとAPIからRunを確認し、Logged Modelをclientへ再読込する。
  5. 元modelと再読込modelの30予測を比較し、結果をJSON Evidenceとして残す。

この分離により、Python 3.14.6互換性はhost client、linux/arm64動作はserver container、保存と再読込はMLflow Tracking/Logged Modelsというように、失敗箇所を切り分けられる。

技術選定理由

解決したい課題

MLflow公式Tracking QuickstartをPython 3.14.6とApple Siliconで実行できるかを、import確認だけでなく、学習、HTTP tracking、model保存、再読込、推論まで一続きに検証したい。また、読者が同じ依存関係とserver構成を再現できる形で残したい。

採用した構成

今回採用したのは、MLflow 3.15.1の公式Quickstart相当を、host側のuv管理Python clientと、Docker Composeで動かすlinux/arm64のMLflow Serverへ分離する構成である。clientは学習と検証、serverはRun metadata、Logged Models、artifact、UIを担当する。

この構成を選んだ理由

今回は「MLflowが他のtracking製品より優れているか」を比較するのではなく、Research Questionで指定したMLflow公式Quickstartが固定環境で完走するかを優先して試すと決めた。そのためMLflow自体は比較選定の対象ではなく検証対象である。

clientにuvを採用したのは、Python 3.14.6と依存versionをpyproject.tomluv.lockuv sync --lockedで固定し、Python側の互換性を独立して確認するためである。serverにDocker Composeを採用したのは、常駐processとSQLite/artifact volumeをhost Pythonから分離し、linux/arm64aarch64、HTTP healthを個別に確認するためである。実測ではuv同期がexit code 0、containerがlinux/arm64 / aarch64/healthがHTTP 200となり、この切り分け方でResearch Questionを判定できた。

Apple Siliconを使ったのは、GPU製品間の性能比較から導いた結論ではない。筆者がすでに保有するM5 Maxを優先利用し、NVIDIA GPUを追加購入するコストとCUDA前提へ運用を固定することを避けたい、という今回の意思決定背景による。この記事ではNVIDIA GPUの価格、性能、移植コストを測定していないため、Apple Siliconの費用対効果や優位性までは主張しない。

適用条件・制約

この判断は、1台のApple Silicon MacでMLflow 3.15.1の学習用Quickstartを再現する場合に適用する。認証、TLS、外部DB、object storage、複数workerを含むproduction構成の選定理由には使えない。今回は他のtracking製品やhostへのserver直接導入を比較していないため、それらより性能または運用性が優れるとは主張しない。

仮説

ID 仮説 反証条件
H1 Python 3.14.6でQuickstart client処理が完了する import、学習、logging、保存、読込のPython 3.14固有エラー
H2 autologと手動loggingでparameter、metric、modelを記録できる 必須情報の欠落
H3 再読込modelのtest予測が30/30一致する 読込失敗、shape差、1件以上の不一致
H4 arm64 Compose Serverへ記録・取得できる arm64 imageなし、起動・health・API・Run取得の失敗

検証計画

Irisをtest_size=0.2, random_state=42, stratify=targetで分割し、LogisticRegressionをsolver=lbfgs, max_iter=1000, random_state=8888に固定した。同じデータ分割とmodel parameterでautologと手動loggingを1回ずつ行い、手動保存modelを再読込してtest 30件の予測を比較した。

正式判定条件は次のとおりである。

  • command exit code 0
  • 2つのRunがFINISHED
  • 必須parameter、accuracy、modelが存在
  • 元modelと再読込modelの予測が30/30一致
  • serverがlinux/arm64で稼働し、health/APIへ到達可能

検証環境

項目
Hardware Apple M5 Max / 128 GB unified memory
Architecture host: arm64 / container: aarch64
OS macOS 26.5.1 (Build 25F80)
Python 3.14.6
Package manager uv 0.12.2
MLflow client/server 3.15.1
scikit-learn 1.9.0
pandas 2.3.3
Docker 29.6.2
Docker Compose v5.3.1
Server image ghcr.io/mlflow/mlflow:v3.15.1

Article Projectにはpyproject.tomluv.lockを保存し、uv sync --lockedがPython 3.14.6で成功することを別途確認した。

検証手順

1. Python環境をuvで固定する

目的

最初に、使用するPythonと依存関係を固定する。今回保存したpyproject.tomlは次の内容である。

[project]
name = "mlflow-m5max-quickstart"
version = "0.1.0"
description = "Reproducible environment for the MLflow M5 Max Quickstart experiment"
requires-python = "==3.14.6"
dependencies = [
  "mlflow==3.15.1",
  "pandas==2.3.3",
  "scikit-learn==1.9.0",
]

lock fileどおりに環境を復元する。

実行

uv python install 3.14.6
uv sync --locked

観測結果

今回の同期結果は94 packageを解決し、89 packageを.venvへ導入してexit code 0だった。import確認値は次のとおりである。

3.14.6 3.15.1 1.9.0 2.3.3

順にPython、MLflow、scikit-learn、pandasのversionである。

判断

計画で固定したPythonと主要packageの組み合わせを再現できたため、同じ環境で実験へ進んだ。

2. MLflow ServerをDocker Composeで起動する

目的

常駐serverをPython client環境から分離するため、次のcompose.yamlを使った。

services:
  mlflow:
    image: ghcr.io/mlflow/mlflow:v3.15.1
    container_name: technical-blog-mlflow
    restart: unless-stopped
    ports:
      - "127.0.0.1:5000:5000"
    volumes:
      - mlflow_db:/mlflow/db
      - mlflow_artifacts:/mlflow/artifacts
    command:
      - mlflow
      - server
      - --host
      - 0.0.0.0
      - --port
      - "5000"
      - --backend-store-uri
      - sqlite:////mlflow/db/mlflow.db
      - --artifacts-destination
      - /mlflow/artifacts

volumes:
  mlflow_db:
  mlflow_artifacts:

起動とhealth確認を行う。

実行

docker compose config --quiet
docker compose up -d
docker compose ps
curl -fsS -o /dev/null -w 'health_http=%{http_code}\n' \
  http://127.0.0.1:5000/health
docker exec technical-blog-mlflow uname -m

観測結果

実測した要点は次のとおりだった。

technical-blog-mlflow ... Up ... 127.0.0.1:5000->5000/tcp
health_http=200
aarch64

さらにlocal image inspectionはlinux/arm64を返した。x86_64 emulationを使ったというEvidenceはない。

判断

container process、HTTP health、architectureを別々に確認し、arm64 Tracking Serverが利用可能と判定した。

3. Quickstart scriptを実行する

目的

実際に保存・実行したscriptの主要部分を以下に示す。完全版はArticle Projectのscripts/run_tracking_quickstart.pyに保存している。

from __future__ import annotations

import json
import platform
import sys

import mlflow
import mlflow.sklearn
import numpy as np
import sklearn
from mlflow import MlflowClient
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

TRACKING_URI = "http://127.0.0.1:5000"
EXPERIMENT_NAME = "MLflow Quickstart M5 Max"
SPLIT_RANDOM_STATE = 42
MODEL_RANDOM_STATE = 8888

mlflow.set_tracking_uri(TRACKING_URI)
experiment = mlflow.set_experiment(EXPERIMENT_NAME)

iris = load_iris(as_frame=True)
x_train, x_test, y_train, y_test = train_test_split(
    iris.data,
    iris.target,
    test_size=0.2,
    random_state=SPLIT_RANDOM_STATE,
    stratify=iris.target,
)
model_params = {
    "solver": "lbfgs",
    "max_iter": 1000,
    "random_state": MODEL_RANDOM_STATE,
}

# autolog
mlflow.sklearn.autolog(log_input_examples=True, silent=False)
with mlflow.start_run(run_name="quickstart-autolog") as run:
    autolog_run_id = run.info.run_id
    autolog_model = LogisticRegression(**model_params)
    autolog_model.fit(x_train, y_train)
    autolog_accuracy = float(autolog_model.score(x_test, y_test))
    mlflow.log_metric("test_accuracy", autolog_accuracy)

# manual logging
mlflow.sklearn.autolog(disable=True)
with mlflow.start_run(run_name="quickstart-manual") as run:
    manual_run_id = run.info.run_id
    manual_model = LogisticRegression(**model_params)
    manual_model.fit(x_train, y_train)
    original_predictions = manual_model.predict(x_test)
    manual_accuracy = float(accuracy_score(y_test, original_predictions))
    mlflow.log_params(model_params)
    mlflow.log_metric("accuracy", manual_accuracy)
    model_info = mlflow.sklearn.log_model(
        sk_model=manual_model,
        name="iris_model",
        input_example=x_train.iloc[:5],
    )

# reload and compare
loaded_model = mlflow.pyfunc.load_model(model_info.model_uri)
loaded_predictions = np.asarray(loaded_model.predict(x_test))
matching_predictions = int(np.sum(original_predictions == loaded_predictions))

client = MlflowClient()
autolog_run = client.get_run(autolog_run_id)
manual_run = client.get_run(manual_run_id)

assert autolog_run.info.status == "FINISHED"
assert manual_run.info.status == "FINISHED"
assert np.array_equal(original_predictions, loaded_predictions)

print(
    json.dumps(
        {
            "python": sys.version.split()[0],
            "platform": platform.platform(),
            "machine": platform.machine(),
            "mlflow": mlflow.__version__,
            "scikit_learn": sklearn.__version__,
            "experiment_id": experiment.experiment_id,
            "autolog_run_id": autolog_run_id,
            "manual_run_id": manual_run_id,
            "autolog_test_accuracy": autolog_accuracy,
            "manual_accuracy": manual_accuracy,
            "matching_predictions": matching_predictions,
            "prediction_count": len(original_predictions),
        },
        indent=2,
    )
)

Article Projectの完全版はstructured JSONも保存するため、次のように実行できる。

実行

uv run --locked python scripts/run_tracking_quickstart.py \
  --output data/raw/quickstart-result.json

観測結果

正式試行で実際に使ったのもuv管理のPython 3.14.6環境であり、command exit codeは0だった。autolog/manualの2 RunはFINISHED、再読込予測は30/30一致した。

判断

事前に固定した4仮説の判定へ使えるraw JSONとRunを取得できた。

結果

Runとaccuracy

正式試行の結果を、事前に決めた判定基準と並べる。

項目 実測 基準 判定
Autolog Run status FINISHED FINISHED PASS
Manual Run status FINISHED FINISHED PASS
Autolog test accuracy 0.9666666666666667 記録される PASS
Manual accuracy 0.9666666666666667 記録される PASS
予測一致 30 / 30 30 / 30 PASS
MLflow health HTTP 200 到達可能 PASS
Container linux/arm64 / aarch64 arm64 PASS

仮説と結果の対応

ID 反証条件 実測 判定 Reader-visible Evidence
H1 import、学習、logging、保存、読込のPython 3.14固有エラー 正式試行、公開CLI、Notebookの3経路で学習から推論まで完了 SUPPORTED formal-result.jsonreproduction-cli.jsonnotebook-result.json
H2 param、metric、modelのいずれかが欠落 各経路でautolog/manualの2 RunがFINISHED、2 Logged ModelsがREADY SUPPORTED 仮説と実測の記録
H3 model load失敗または1件以上の予測不一致 3経路すべてで30/30一致 SUPPORTED 各結果JSONのverification
H4 arm64 image、health、API、Run取得のいずれかが失敗 linux/arm64、aarch64、HTTP 200、Run作成・取得を確認 SUPPORTED 公開Compose

正式Run IDは次のとおりである。

  • autolog: 44a232af37de4762878b9b1cfad96dc5
  • manual: 4505ff965d6e4cceb1e205b925fefe3e

MLflow Quickstart M5 MaxのRuns一覧

画面に4 Runある理由は、後述する条件ミスの初回試行2 Runも削除せず保持しているためである。正式判定には新しい2 Runだけを使った。

accuracyのUI確認

MLflow UIでは正式manual Runのaccuracyが丸められて0.97と表示された。判定にはUIの丸め値ではなく、API/JSONの0.9666666666666667を使った。

manual Runのaccuracy

model保存と再読込

manual model URIはmodels:/m-0f40ad730a144244a7d4e652794d3a41だった。再読込後の30予測は元modelとすべて一致した。

MLflow 3.15.1ではmodelがfirst-classのLogged Modelとして表示された。正式試行のmanual iris_modelとautolog modelはいずれもREADYで、各7 fileを確認した。

Logged ModelsがReadyになった画面

manual modelには次が含まれていた。

MLmodel
conda.yaml
input_example.json
model.skops
python_env.yaml
requirements.txt
serving_input_example.json

結果の分析

H1: SUPPORTED

Python 3.14.6でimport、学習、HTTP logging、serialization、model load、inferenceまで例外なく完了した。ただし、この結果はMLflowの全機能がPython 3.14.6で動くことを保証しない。

H2: SUPPORTED

autologではLogisticRegressionのparameter、training metric、test accuracy、confusion matrixなどを確認した。manual loggingでは3 parameter、accuracy、READY modelを確認した。

H3: SUPPORTED

再読込modelの予測一致率は30/30 = 1.0だった。

H4: SUPPORTED

imageとcontainerはarm64で動作し、hostのPython clientからHTTPでRunを作成・取得できた。

考察

「Run artifactが空だからmodelがない」とは限らない

今回、manual Runへlist_artifacts(run_id)を実行すると通常artifact一覧は空だった。しかし、Logged Models APIではiris_modelREADYで、model.skopsを含む7 fileを取得できた。

そのためMLflow 3.15.1では、model保存の確認をRunのArtifacts tabだけで終わらせず、Models viewまたはsearch_logged_models() / list_logged_model_artifacts()でも確認した方がよい。

accuracyはM5 Max性能ではない

正式試行は0.9667、条件を誤った初回試行は0.9333だった。差はtrain/test splitが異なるためであり、M5 Maxの速度やMLflowの優劣を示さない。今回の中心はaccuracyの高さではなく、決めた条件でtrackingとmodel round-tripが成立したことにある。

split条件が異なるTrialのaccuracy比較

このグラフは性能比較ではない。split条件が違えば同じmodel設定でもaccuracyが変わるため、成功したRunでも事前計画から外れたTrialを正式結果へ混ぜてはいけないことを示している。

失敗したこと・TIPS

実際の失敗と判定不採用を、エラーメッセージまたは主要な不一致から再実行結果まで残す。

F-01 公開ComposeのHostヘッダーがHTTP 403になった

発生条件

既存の5000番環境と分離するため、公開ComposeをMLFLOW_PORT=5001で起動した。当初の--allowed-hostslocalhost,127.0.0.1,mlflowで、port付きloopbackを許可していなかった。

失敗した操作

MLFLOW_PORT=5001 docker compose up -d
MLFLOW_TRACKING_URI=http://127.0.0.1:5001 \
  uv run python scripts/run_tracking_quickstart.py \
  --output evidence/results/reproduction-cli.json

エラー全文または主要行

HTTP/1.1 403 Forbidden
Invalid Host header - possible DNS rebinding attack detected

この本文は同じMLflow 3.15.1 security middlewareに未許可Hostを送って再確認した。元のforward-testでも、host clientのHost: 127.0.0.1:5001がHTTP 403で拒否されたことを実験記録へ保存している。

原因

--allowed-hosts127.0.0.1が、clientの送るport付きHost 127.0.0.1:5001に一致しなかった。

切り分け

container process、container内/health、hostからの/health、client APIを分けて確認した。processとhealthは成功し、Runを記録するAPIだけが403になったため、network停止やserver起動失敗ではなくHost header検証へ絞った。

効果がなかった方法

portなしの127.0.0.1をallowlistへ記載するだけでは、port付きHostのclient APIを許可できなかった。

修正内容

security middleware全体は無効化せず、loopbackの任意portだけを許可した。

 - --allowed-hosts
-  localhost,127.0.0.1,mlflow
+  localhost,127.0.0.1:*,mlflow

再実行

MLFLOW_PORT=5001 docker compose up -d --force-recreate
MLFLOW_TRACKING_URI=http://127.0.0.1:5001 \
  uv run python scripts/run_tracking_quickstart.py \
  --output evidence/results/reproduction-cli.json

再実行結果

CLI:      2 Runs FINISHED / 2 Logged Models READY / predictions 30/30 / exit 0
Notebook: 2 Runs FINISHED / 2 Logged Models READY / predictions 30/30 / 4/4 cells errorなし

F-02 Dockerの.State.Health.Statusが取得できない

発生条件

Docker HEALTHCHECKを定義していない当初のtechnical-blog-mlflow containerに対して、DockerのHealth objectを読もうとした。

失敗した操作

docker inspect -f '{{.State.Health.Status}}' technical-blog-mlflow

エラー全文または主要行

template parsing error: template: :1:8: executing "" at <.State.Health.Status>: map has no entry for key "Health"

原因

containerにDocker HEALTHCHECKがなく、.State mapにHealth keyが存在しなかった。

切り分け

Docker process状態、port mapping、MLflow applicationの/healthを別々に確認した。containerはrunningで、/healthはHTTP 200だった。

効果がなかった方法

同じ.State.Health.Statusを再度読む方法では、存在しないHealth objectを確認できない。

修正内容

当初環境の稼働判定はapplication healthへ切り替え、公開Composeには同じ混乱を防ぐDocker healthcheckを追加した。

- docker inspect -f '{{.State.Health.Status}}' technical-blog-mlflow
+ curl -fsS -o /dev/null -w 'health_http=%{http_code}\n' \
+   http://127.0.0.1:5000/health

再実行

curl -fsS -o /dev/null -w 'health_http=%{http_code}\n' \
  http://127.0.0.1:5000/health

再実行結果

health_http=200

Docker Health objectがないことと、MLflow applicationが停止していることは同義ではない。

F-03 初回試行のrandom stateが実験計画と一致しなかった

このケースはPython例外ではなく、exit code 0の結果を事前計画と照合して発見したプロトコル逸脱である。

発生条件

初回scriptで、model用のrandom_state=8888をtrain/test splitにも使用した。実験計画はsplitを42に固定していた。

失敗した操作

uv run --locked python scripts/run_tracking_quickstart.py \
  --output data/raw/quickstart-result.trial1-protocol-deviation.json

エラー全文または主要行

runtime errorは発生していない。判定時に確認した主要な不一致は次のとおりである。

expected split_random_state: 42
actual split_random_state:   8888
command exit code:            0

原因

split用とmodel用のrandom stateを1つの値として扱っていた。

切り分け

初回JSON、実行script、事前のexperiment-plan.mdを照合し、MLflowやPythonの障害ではなく実験条件の実装ミスと判断した。

効果がなかった方法

exit code 0とRunのFINISHEDだけで成功判定する方法では、計画条件からの逸脱を検出できなかった。

修正内容

- random_state = 8888
+ SPLIT_RANDOM_STATE = 42
+ MODEL_RANDOM_STATE = 8888

再実行

uv run --locked python scripts/run_tracking_quickstart.py \
  --output data/raw/quickstart-result.json

再実行結果

split_random_state: 42
model_random_state: 8888
Autolog Run: FINISHED
Manual Run: FINISHED
matching_predictions: 30/30
exit code: 0

初回JSONと2 Runは削除せず「プロトコル逸脱」として保存し、正式判定から除外した。

実行時に出たwarning

  • autolog modelのpickle/cloudpickleには、信頼できないmodelをdeserializeすると任意コード実行につながる注意が出た。manual modelはmodel.skopsで保存された。
  • MLflowはuv projectから93 dependenciesをexportしたが、conda.yamlへ書くpip versionを特定できないwarningを出した。
  • scikit-learn 1.9.0の内部metric pathで将来の引数名変更に関するFutureWarningが出た。
  • schema inferenceでinteger columnと欠損値に関するwarningが出た。

いずれも今回のRun status、accuracy、30/30一致には影響しなかった。ただし、warningを一般に無視してよいという意味ではない。

追加検証

記事初版後、読者が本当に再現できる形かを確かめるため、公開Repositoryを新しいMLflow Server(port 5001)でforward-testした。

経路 Run Logged Models accuracy 再読込予測 実行状態
公開CLI 2 FINISHED 2 READY 0.9666666666666667 30/30 exit 0
実行済みNotebook 2 FINISHED 2 READY 0.9666666666666667 30/30 4/4 code cells errorなし

この追加検証で、元のArticle Project内だけに閉じていたEvidenceを、第三者がcloneできるpackageとNotebookへ変換できた。また、公開Compose固有のHost header拒否を発見し、security middlewareを無効化せず修正できた。これは元のQuickstartを一度動かしただけでは得られなかった運用上の知見である。

一方、次はResearch Questionの範囲外であり、production readinessを誤って主張しないため未実施とした。

  • 認証とTLS
  • SQLite以外のbackend DBと同時書き込み
  • S3互換object storage
  • backup / restore
  • 異なる依存version間のmodel loading
  • untrusted modelのserialization security

制約・今回分からなかったこと

  • 1台のM5 Max、1つのDocker Desktop環境である。正式試行に加えて公開CLIとNotebookを再実行したが、別hardwareでの再現ではない。
  • MLflowの全API、plugin、model flavorは検証していない。
  • GPU、MPS、Metal、MLXは使っていない。
  • server負荷、training速度、消費memoryは測定していない。
  • remote tracking、認証、object storage、production durabilityは未確認である。
  • 公式latest文書は将来変更される可能性がある。version固定のEvidenceは今回の3.15.1を対象にしている。

まとめ

Apple M5 Max / arm64 / Python 3.14.6上で、MLflow 3.15.1のTracking Quickstart相当をuv clientとDocker Compose serverへ分離して完走できた。

重要だったのは次の3点である。

  1. Python、MLflow、scikit-learnのversionをuvで固定する。
  2. arm64 imageとapplication healthを実測する。
  3. MLflow 3のmodel EvidenceはRun artifactだけでなくLogged Modelsでも確認する。

正式試行、公開CLI、Notebookの3経路で、各2 RunがFINISHED、accuracyは0.9667、model round-tripの予測は30/30一致した。初回の条件ミスと公開Composeの403も削除せず残したことで、成功結果だけでなく再現packageへ至る判断過程を追跡できる状態にした。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?