MLflow 實驗追蹤:
每一次訓練都留下證據
上週訓的模型 AUC 0.95,今天重跑變 0.92——當時 max_depth 是多少?哪一版資料?哪個 commit? 沒人記得。MLflow Tracking 把每一次訓練記成一個 run,幾週後一句查詢就能把當時的設定、指標、圖和模型全部翻出來。 先體驗「像查資料庫一樣查實驗」:下面是 notebook 實際跑出來的 9 個 run,改條件看它篩:
表格內容是 notebook 的實測紀錄(同一份程式、同一組亂數種子,你在 molab 跑出來會一樣)。 notebook 裡的查詢框接的是真的 mlflow.search_runs,錯誤訊息也是 MLflow 回的。
一個 run 裡有什麼
實測 baseline 的 LogisticRegression:AUC 0.9508、accuracy 0.882——這兩個數字之後都會在查詢結果裡再出現。 notebook 還會把整個目錄樹印出來給你看:MLflow 不神秘,就是一個資料庫檔+一個檔案夾。
到 notebook 的 1️⃣–2️⃣ 節:第一個 run、artifacts、磁碟目錄樹有 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——典型過擬合, 只看最後一輪的數字看不出來。
手動 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 記了什麼nested runs,然後 search_runs 就是你的 UI
調參一次跑很多組,用 parent run + nested 子 run 收成一組;查詢時 tags.mlflow.parentRunId = '…' 就能撈出整組。實測 max_depth 掃描:
| max_depth | 2 | 4 | 8 | 16 |
|---|---|---|---|---|
| test AUC | 0.925 | 0.956 | 0.967 | 0.969 |
| test accuracy | 0.85 | 0.90 | 0.91 | 0.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誰、用哪份程式、什麼設定——以及網頁 UI
每個 run 自動帶 mlflow.user、mlflow.source.name(哪個檔案跑的),在 git repo 內執行還有 mlflow.source.git.commit。加上 params,就是重現的線索;資料版本用 log_input(第 2 課)。
本課全程用程式讀紀錄簿,是為了讓你知道 UI 底下沒有魔法——網頁 UI 讀的是同一個檔,點選、並排比較、看曲線、下載 artifacts 都在裡面。 整理紀錄:delete_run 是軟刪除(實測 active 9→8、deleted 1,restore_run 又回 9), mlflow gc 才真的清磁碟。
到 notebook 的 8️⃣ 節:自動標籤、刪除與還原換你動手
再跑一個 class_weight="balanced" 的 LogisticRegression,補記 recall,用 search_runs 把兩個 logreg run 並排比。
把「AUC > 0.96 且 model = rf」的 run 全部貼上 candidate=true,再用 tags.candidate = 'true' 查回來確認。
把你手上任何一支訓練腳本包成 run:至少一個 param、一個 metric、一個 artifact。驗證:新開一個直譯器 search_runs 找得到、download_artifacts 拿得到。
卡住了?每一題在 notebook 末節都有折疊解答——先自己做,再打開對照。
情境測驗
離開前試試看:下面的情境都真的會遇到。每題選一個你認為的最佳做法,選了馬上看得到解釋。
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,結果炸了。最可能的原因與正確做法?
這是 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 回了下面這句。怎麼修?
錯誤訊息已經把答案講白了:「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+自己補測試集指標。
實作在 molab 跑(免費)
molab 的登入狀態進不了內嵌框架(瀏覽器的跨站 cookie 保護), 所以 notebook 要在新分頁執行——把它跟本頁並排開,左邊教學照樣對照。
- 登入 molab(GitHub / Google)
- 開啟課程 notebook,Fork 成自己的副本即可編輯
- 從第一格往下全部執行(首次安裝套件約 1–2 分鐘)——免費 CPU 環境即可,不需要 GPU;紀錄簿全在暫存資料夾,不連任何伺服器
不想用 molab?下載 mlflow-tracking_ext.py 後在自己電腦
uvx marimo edit --sandbox mlflow-tracking_ext.py,依賴會自動安裝。
molab 的線上編輯器在手機上體驗有限——動手這一段建議用電腦進行。