AI 互動教室 ‹ MLOps 自動化技術
下載 .py 開啟實戰 notebook ↗ 留言回報
MLFLOW · EXPERIMENT TRACKING · 01

MLflow 實驗追蹤:
每一次訓練都留下證據

上週訓的模型 AUC 0.95,今天重跑變 0.92——當時 max_depth 是多少?哪一版資料?哪個 commit? 沒人記得。MLflow Tracking 把每一次訓練記成一個 run,幾週後一句查詢就能把當時的設定、指標、圖和模型全部翻出來。 先體驗「像查資料庫一樣查實驗」:下面是 notebook 實際跑出來的 9 個 run,改條件看它篩:

表格內容是 notebook 的實測紀錄(同一份程式、同一組亂數種子,你在 molab 跑出來會一樣)。 notebook 裡的查詢框接的是真的 mlflow.search_runs,錯誤訊息也是 MLflow 回的。

01 · 四種紀錄

一個 run 裡有什麼

import mlflow mlflow.set_tracking_uri("sqlite:///mlflow.db") # 紀錄簿放哪(正式環境換成 http://tracking-server) mlflow.set_experiment("churn-demo") # 之後的 run 都歸這個實驗 with mlflow.start_run(run_name="logreg-baseline"): # 一次訓練 = 一個 run mlflow.log_param("C", 1.0) # 設定值 clf = LogisticRegression(C=1.0).fit(X_train, y_train) mlflow.log_metric("auc", roc_auc_score(y_test, clf.predict_proba(X_test)[:, 1])) # 量測值 mlflow.set_tag("stage", "baseline") # 標籤 mlflow.log_figure(fig, "plots/roc.png") # 任何檔案
params你設定的:C=1.0max_depth=8。同一個 run 內同名只能記一次,改了會報錯——設定就不該中途變。
metrics你量出來的:auc=0.9508。同名可以記很多次,配 step 就是曲線。
tags你貼的標籤,之後拿來篩選;run 結束後還能改。MLflow 也自動貼:誰跑的、哪個檔案、git commit。
artifacts任何檔案:圖、JSON、混淆矩陣、資料快照、模型本身。中繼資料進 SQLite,檔案落在 artifacts 資料夾(正式環境通常是 S3)。

實測 baseline 的 LogisticRegression:AUC 0.9508、accuracy 0.882——這兩個數字之後都會在查詢結果裡再出現。 notebook 還會把整個目錄樹印出來給你看:MLflow 不神秘,就是一個資料庫檔+一個檔案夾

到 notebook 的 1️⃣–2️⃣ 節:第一個 run、artifacts、磁碟目錄樹
02 · 曲線與自動記錄

有 step 的指標,以及一行 autolog

log_metric("test_logloss", v, step=n) 同一個 key 記很多次就是一條曲線,事後用 get_metric_history 取回來畫。notebook 用 Gradient Boosting 記了 150 輪的 train/test log-loss: 實測 test loss 在第 116 輪最低(0.239),之後 train 一路掉到 0.097、test 卻回升到 0.242——典型過擬合, 只看最後一輪的數字看不出來

mlflow.sklearn.autolog() # 一行;之後每次 .fit() 自動變成一個 run with mlflow.start_run(run_name="rf-autolog"): rf = RandomForestClassifier(n_estimators=100, max_depth=6).fit(X_train, y_train) mlflow.log_metric("auc", roc_auc_score(y_test, rf.predict_proba(X_test)[:, 1])) # 測試集要自己補

手動 log_param 很快就會漏,所以常見框架(scikit-learn、PyTorch、XGBoost、LightGBM、transformers、OpenAI…)都有 autolog。實測這個 RandomForest 一行 log_* 都沒寫,run 裡卻有 19 個 params(估計器的每個超參數)、7 個 training_ 指標4 個 artifacts (estimator.html、混淆矩陣、ROC、PR 曲線)+模型本身。 但 autolog 只看得到 fit:它記的 training_roc_auc 是訓練集的 0.992, 測試集的 0.966 是我們自己補的——兩個都在,才看得出差距。

到 notebook 的 3️⃣–4️⃣ 節:曲線重建、autolog 記了什麼
03 · 掃參數與查詢

nested runs,然後 search_runs 就是你的 UI

with mlflow.start_run(run_name="rf-depth-sweep"): # parent:代表這次掃描 for depth in [2, 4, 8, 16]: with mlflow.start_run(run_name=f"depth={depth}", nested=True): # child:一組參數 mlflow.log_params({"model": "rf", "max_depth": depth}) ... mlflow.log_metrics({"auc": auc, "accuracy": acc}) df = mlflow.search_runs(experiment_names=["churn-demo"], filter_string="params.model = 'rf' and metrics.auc > 0.96", order_by=["metrics.auc DESC"]) # 回傳 pandas DataFrame

調參一次跑很多組,用 parent run + nested 子 run 收成一組;查詢時 tags.mlflow.parentRunId = '…' 就能撈出整組。實測 max_depth 掃描:

max_depth24816
test AUC0.9250.9560.9670.969
test accuracy0.850.900.910.92

search_runs 回傳的是 DataFrame:一列一個 run,欄位 params.*metrics.*tags.* 加上狀態與時間——實測 9 個 run、51 欄。之後「比較 run」就是普通的 pandas+matplotlib。查詢語言很小:

寫法意思
metrics.auc > 0.95數值比較(實測命中 7 個 run)
params.model = 'rf'字串一定要加引號,params 與 tags 的值都是字串
tags.stage != 'autolog'比較子只有 = != < <= > >= LIKE ILIKE,沒有 ==
attributes.run_name LIKE 'depth%'run 本身的屬性(名稱、狀態、時間)
... and ...只有 and,沒有 or(要 or 就查兩次再 concat)

notebook 最後有一組拉桿+按鈕:按一下就真的訓練一次、記成你的 run(tag stage='yours'), 多按幾次再回查詢框看紀錄簿長大。

到 notebook 的 5️⃣–7️⃣ 節:nested runs、查詢框、比較圖、你自己的 run
04 · 重現與介面

誰、用哪份程式、什麼設定——以及網頁 UI

每個 run 自動帶 mlflow.usermlflow.source.name(哪個檔案跑的),在 git repo 內執行還有 mlflow.source.git.commit。加上 params,就是重現的線索;資料版本用 log_input(第 2 課)。

mlflow ui --backend-store-uri sqlite:///mlflow.db --port 5000 # 讀同一個 SQLite 檔 # 團隊共用:mlflow server ...,大家 set_tracking_uri("http://那台機器:5000")

本課全程用程式讀紀錄簿,是為了讓你知道 UI 底下沒有魔法——網頁 UI 讀的是同一個檔,點選、並排比較、看曲線、下載 artifacts 都在裡面。 整理紀錄:delete_run 是軟刪除(實測 active 9→8、deleted 1,restore_run 又回 9), mlflow gc 才真的清磁碟。

到 notebook 的 8️⃣ 節:自動標籤、刪除與還原
05 · 實戰

換你動手

LEVEL 1

再跑一個 class_weight="balanced" 的 LogisticRegression,補記 recall,用 search_runs 把兩個 logreg run 並排比。

LEVEL 2

把「AUC > 0.96 且 model = rf」的 run 全部貼上 candidate=true,再用 tags.candidate = 'true' 查回來確認。

LEVEL 3

把你手上任何一支訓練腳本包成 run:至少一個 param、一個 metric、一個 artifact。驗證:新開一個直譯器 search_runs 找得到、download_artifacts 拿得到。

卡住了?每一題在 notebook 末節都有折疊解答——先自己做,再打開對照。

06 · 驗收

情境測驗

離開前試試看:下面的情境都真的會遇到。每題選一個你認為的最佳做法,選了馬上看得到解釋。

Q1 情境題

你和兩位同事各自在自己筆電上用 MLflow 記錄實驗,現在想把三個人的 run 放在一起比較、找出最好的設定。最佳做法是?

紀錄簿放哪裡只由 set_tracking_uri 決定——換成 tracking server 的網址,log_*search_runs、autolog 一個字都不用改,之後的 run 直接落在同一個地方、UI 也是同一個。A 能動但每次都要重做,而且不同機器的 run id 與 artifact 路徑合併起來很痛;B 把 MLflow 最有價值的部分(可查詢、可回溯 artifacts)丟掉,只剩數字;D 沒有解決問題,只是把三個人綁在一台機器上。

Q2 錯誤診斷

你在一個 run 裡先記了 max_depth=4,跑完覺得不好,同一個 run 內又改記 8,結果炸了。最可能的原因與正確做法?

MlflowException: Changing param values is not allowed. Param with key='max_depth' was already logged with value='4' for run ID='cd047f42...'. Attempted logging new value '8'.

這是 MLflow 刻意的設計:params 代表「這個 run 是用什麼設定跑的」,允許中途改就失去重現的意義。要試另一組設定,就是另一個 run(掃參數時用 parent+nested=True 的子 run 收成一組)。A 方向錯了,任何後端都擋;C 軟刪除只是把 run 標成 deleted,不會讓你「重記」;D 把設定塞進 metric 雖然不報錯,但之後 params.max_depth 查不到、比較時也對不上——症狀消失、問題還在。

Q3 情境題

你要訓練一個跑 200 個 epoch 的模型,想事後看每個 epoch 的 train/val loss 曲線判斷有沒有過擬合。應該怎麼記?

metric 天生可以同名記很多次,配 step 就是曲線;notebook 裡 150 輪的 GBDT 就是這樣記的,事後才看得出 test loss 在第 116 輪之後回升。B 做得到但把一次訓練拆成 200 個 run,parent/child 關係、params 全都要重複記,UI 也塞爆;C 正是本課開頭的悲劇——只剩最後一個數字,過擬合看不見;D 會撞上 params 的設計(同名不能改、而且 200 個 key 沒有順序語義),並且查詢時 params.* 全是字串。

Q4 錯誤診斷

你想找出所有 RandomForest 的 run,寫了 filter_string="params.model = rf",MLflow 回了下面這句。怎麼修?

MlflowException: Parameter value is either not quoted or unidentified quote types used for string value rf. Use either single or double quotes.

錯誤訊息已經把答案講白了:「not quoted」。MLflow 的查詢語言裡 params/tags 的值全是字串,比較時必須用單引號或雙引號括起來;數值比較只有 metrics.* 才能裸寫。A 是另一種錯誤(欄位不存在會回 Invalid attribute key),跟這句訊息對不上;B 更糟——== 根本不是合法比較子(實測回 Invalid comparator '==');C 換成 tag 一樣要加引號,沒解決問題。

Q5 情境題

你開了 mlflow.sklearn.autolog() 訓練,UI 上看到 training_roc_auc = 0.992,很開心地把模型交出去了。同事問「那測試集呢?」——最佳做法是?

notebook 實測的 rf-autolog 正是這個情況:training_roc_auc 0.992、自己補的測試集 auc 0.966——差距就是過擬合的程度。autolog 幫你記設定、訓練指標、診斷圖與模型,但它不知道你的測試集在哪。A 會把訓練集分數當成真實表現;C 把測試集混進訓練,之後再也沒有乾淨的評估資料;D 因噎廢食,手寫反而更容易漏掉那 19 個超參數——正確用法是 autolog+自己補測試集指標。

HANDS-ON · MOLAB

實作在 molab 跑(免費)

molab 的登入狀態進不了內嵌框架(瀏覽器的跨站 cookie 保護), 所以 notebook 要在新分頁執行——把它跟本頁並排開,左邊教學照樣對照。

  1. 登入 molab(GitHub / Google)
  2. 開啟課程 notebook,Fork 成自己的副本即可編輯
  3. 從第一格往下全部執行(首次安裝套件約 1–2 分鐘)——免費 CPU 環境即可,不需要 GPU;紀錄簿全在暫存資料夾,不連任何伺服器

不想用 molab?下載 mlflow-tracking_ext.py 後在自己電腦 uvx marimo edit --sandbox mlflow-tracking_ext.py,依賴會自動安裝。

molab 的線上編輯器在手機上體驗有限——動手這一段建議用電腦進行。