我的模型交付鏈最後一棒是 promotion gate,其中一條規則是:

新模型不能比現行基準差超過 tol = 0.05

寫這行的時候我沒多想。直到有人問我:「為什麼是 0.05?」

我答不出來。


這個問題等於在問「你的產線抖多大」

用 benchmark 想最快。

你改了一段程式,跑 benchmark 從 100 ms 變成 103 ms。變慢了嗎?

答不出來——因為你不知道「什麼都不改、重跑十次」會落在哪個範圍。 如果是 98–105 ms 之間跳,那 103 ms 什麼都不代表。

tol 就是「我容許多少抖動」。要填對它,得先知道它實際抖多少

  • 如果實際抖動是 0.5,tol=0.05 太嚴 → 好模型天天被擋
  • 如果實際抖動是 0.001,tol=0.05 太鬆 → 真的退步了也放行

量法:只換 seed,其他全部固定

我跑了五次訓練,唯一變因是 random seed——同一份資料、同樣的步數、 同一條 pipeline、同樣的超參數。

seed val_loss test_loss
7 7.2547 7.1439
42 7.2507 7.1677
1337 7.2371 7.1240
2024 7.2531 7.1672
31337 7.2194 7.1267
test_loss   mean 7.1459   σ = 0.0211   2σ = 0.0422

所以 tol = 0.05 = 2.37 σ。

好消息是:它落在合理區間的上緣(統計上常用的管制界限是 2σ 到 3σ)。 我猜對了。但在量之前,我說不出這句話。

而差別不在數字要不要改,在能不能回答「為什麼是 0.05」。 一個答不出理由的門檻,改起來沒有心理成本——這才是它容易被調鬆的真正原因。


⭐ 順便分離出兩種完全不同的雜訊

我之前有另一組數字:同一個 seed 重跑三次,val_loss 分別是 7.2370 / 7.2428 / 7.2343,全距 0.0085,σ ≈ 0.004

我一度想拿這個 0.004 當 tol 的依據。那是錯的。

雜訊源 σ 它在量什麼
同 seed 重跑 0.004 評估時隨機抽 batch 的變異(評估腳本沒固定 seed)
不同 seed 0.0211 初始化 + 資料順序造成的模型差異

seed 變異是評估雜訊的 5.3 倍。

用 Java 講:同 seed 重跑像對同一個物件呼叫兩次 toString(); 不同 seed 像 new 出五個不同的 instance。

你上線時拿到的是「某一次訓練的結果」,那次的運氣是隨機的。 所以 gate 要容忍的是 instance 之間的差異(0.0211),不是同一個物件的兩次讀數(0.004)。 拿後者當 tol 會嚴 5 倍,天天擋掉健康的模型。


論文界怎麼處理這件事

這不是新問題,ML 學界被公開打臉過。

最有名的是 Henderson et al. 2018《Deep Reinforcement Learning that Matters》同一個演算法、同一組超參數,只換 random seed 跑 10 次, 拆成兩組各 5 個 seed 分別平均——兩條學習曲線看起來像來自不同的統計分布。

也就是說,「A 演算法贏 B」這種結論,可以純粹是 seed 造成的。

標準做法因此是報 mean ± std over N seeds,而不是單一數字。 Bouthillier et al. 2021 更進一步: 與其固定其他條件只換 seed,不如同時隨機化多個變異源(seed、資料切分、資料順序), 估得更準而且省算力。

但誠實講,實務上做得很差:很多論文只報最好的那個 seed, 而 LLM 級別的模型根本跑不起多 seed——GPT-3 就訓練那一次


⭐ 但你不該照抄論文的做法

這是這篇最重要的一段。

  論文 你的 gate
問題 這個差異是真的嗎 這顆能不能上線
工具 顯著性檢定 二元決策
允許的答案 「不顯著」→ 不下結論 不能說「無法判斷」

論文可以說「證據不足」然後停在那裡。你的 gate 不行——它必須放行或擋下。

而且 p 值需要樣本數與檢定力,你上線時手上只有一顆模型,做不了檢定。

所以你該借的不是統計檢定,是統計製程管制(SPC)的管制圖:

  1. 先量製程本身的自然變異(σ)
  2. 訂管制界限(常見 2σ 或 3σ)
  3. 超出界限 = 製程異常,停線查

你的 promotion gate 本質上就是一張管制圖,不是一個假設檢定。 這個框架在製造業和金融風控裡都是常識,而且比 p 值更貼近你的用途。


還沒解決的:改小 tol 治不好的病

量出 σ 之後,tol 有依據了。但有一個缺陷不是門檻大小的問題

tol 比的是「跟上一筆上線的模型比」。就算收緊成 2σ = 0.042, 每次退步 0.042 都合法,二十次之後跟最初那顆比已經退了 0.84——而每一步都通過了 gate。

用 Java 講:像 code review 規定每個 PR 最多只能多欠 5 行技術債。 每一次 review 都是對的,一年後系統爛掉,而且找不到哪一次該負責。

修法不是改小 tol,是再釘一個人工凍結的原點基準,雙重檢查:

(比上一筆差 > tol)  OR  (比凍結基準差 > tol_total)   → 擋

這個我還沒實作


三句話帶走

  1. 沒量過 σ 的門檻,是一個你答不出理由的數字——而答不出理由的門檻,改起來沒有代價
  2. 要量的是「同配方重做一次」的變異,不是「同一次量兩遍」的變異——後者會小 5 倍
  3. 借管制圖,不要借 p 值——你要的是決策,不是推論

⚠️ 最後一個限度:我量到的 σ 只對這一組設定成立(2 MB 語料 / 300 步 / BPE 500)。 換資料量或步數都要重量。


你們系統的關鍵門檻——不管是模型、效能還是告警——有哪一個是量出來的?其他的是怎麼來的?

🧪 這篇的實驗環境與 lab 檔案(最後更新 2026-08-30)

叢集

  • CRC 2.63.0 · OpenShift 4.22.7 · Kubernetes v1.35.6
  • 單節點:13 vCPU / 40 GiB RAM / 120 GB disk
  • 宿主:Framework Laptop 16(Ryzen AI 7 350 · 8C/16T · 64 GB RAM · 1 TB NVMe · RTX 5070 顯卡模組)
  • ⚠️ 叢集內看不到 GPU(CRC 是 VM,RTX 5070 未 passthrough)

Operator

  • opendatahub-operator.v3.5.0 ← 即 RHOAI 3.x 的上游開源版
  • cert-manager-operator.v1.20.0(3.x 的必要相依;2.x 不需要)
  • openshift-pipelines-operator-rh.v1.23.2openshift-gitops-operator.v1.21.3

DataScienceCluster 開啟的元件

  • kserveaipipelinesdashboardworkbenchesmodelregistrykueue(Unmanaged)
  • 其餘(raytrustyaifeastaigateway…)為 Removed

叢集外的依賴(跑在宿主的 podman 上,crc start 不會帶起來)

  • Harbor v2.15.2(私有 registry)· MinIO(S3)

模型端

  • Python 3.12.13 · PyTorch 2.11.0+cu128 · FastAPI + uvicorn · prometheus-client

lab 檔案

⚠️ ODH ≠ RHOAI:元件同源,但 namespace 與部分名稱不同 (我這裡是 opendatahub,商用版是 redhat-ods-* 那一套)。 指令的邏輯可以照用,字串要自己對一次。