三十篇寫完了。這一篇不談平台,算我自己的帳。

我在這個系列裡反覆說「不要相信沒驗過的數字」。 那句話對我自己一樣成立——而且我犯了四次。


第一次:用錯的量法,差點讓工作量翻倍

我給稿子訂了字數上限,量完發現12 篇全部超標,最長的是上限的 1.76 倍。

於是有人問:「那要不要每篇拆成兩篇?」

12 拆成 24。那會吃掉整個檔期,後面真正想寫的東西一篇都放不進去。

我正要開始評估怎麼拆,停了一下,回去看量法—— 我是拿整份 Markdown 直接數的,把兩種讀者永遠看不到的東西算了進去: 發文前要刪的註解,和程式碼區塊。合計約佔每篇 40%。

重量之後:平均 1,553 字,沒有一篇超過 2,000。一篇都不用拆。

教訓:這個錯誤最危險的地方不是它錯,是它看起來剛好合理。 如果它回報「每篇 50 萬字」,我立刻會懷疑。 但「超標 1.76 倍」正好落在「有點糟但可信」的區間。


第二次:消融實驗的評估,沒有套用消融

我做了三個拆零件的實驗(拆掉 causal mask、residual、/√d), 用 monkeypatch 改模型再訓練。

但評估是另一個 process 跑的。 03_eval.py 重新 import 原始的模型結構—— 所以我是拿「沒有 residual 訓練出來的權重」餵進「有 residual 的結構」去評估。

  錯的 修正後
no-resid test_loss 4.1130 3.3576
no-scale test_loss 2.6724 1.9331
baseline 1.9534 1.9446(差 0.009=評估雜訊)

baseline 幾乎沒動,正好證明問題出在消融不匹配。

而抓到它的不是我的檢查。 是第三個實驗跑出一組矛盾的數字 (val 比 baseline 好、test 卻爛掉),我追那個矛盾才發現前兩篇全錯。

教訓錯誤的數字不會自己招供。 通常是另一個實驗的矛盾把它逼出來——而如果我沒做第三個實驗, 那兩篇會就這樣發出去。


第三次:我猜錯了,而且方向相反

拆掉 attention 的 /√d 之前,我把預測寫死在檔案裡:

分數會放大 5.66 倍 → softmax 過度尖銳 → 訓練不穩 → val loss 掉到 2.0~2.6

實際跑出來是 1.8090——比 baseline 的 1.8479 還低。

四點預測錯了三點,方向也錯。

但這裡有第二層:我差點寫成「拿掉 √d 反而更好」。

因為我另外量過這條產線的自然波動:只換 random seed 跑五次, test_lossσ = 0.0211。而這兩顆模型的差距是 0.0115——小於一個標準差

正確的結論是第三種:「在這個設定下量不出差異」,不是「更好」也不是「更差」。

教訓沒有 σ,你手上的每一個比較都是意見。


第四次:我把一個錯誤傳進了要交出去的文件

這個最嚴重,因為它已經流出去了。

早期的紀錄裡有一條:

「pipeline 內建的 MariaDB 是 latin1_swedish_ci,含中文的 pipeline 上傳即失敗」

我沒有重驗就把它寫進了兩篇文章一份驗收條件

寫第 28 篇之前我去實查——那條在這個版本上不成立

schema mlpipeline           utf8mb4_unicode_ci
run_details.Name/Description  utf8mb4
metadata 表(Artifact/Execution/Context)  utf8mb4

中文存得進去,dashboard 也顯示得正確。 而且我早就有反證沒注意到:我自己那些 run 的中文描述,一直好好地存在裡面。

但真正的風險還在,只是位置不同——在連線端:

character_set_client      latin1
character_set_connection  latin1
character_set_results     latin1

所以自己寫的查詢會拿到 ?????,而且不報錯。 更嚴重的是 mysqldump 沒指定字元集,會產生一份「看起來成功但中文全毀」的備份。

那對災難復原的影響,比原本那條敘述更嚴重。

教訓繼承來的結論最危險。 它有一份紀錄背書,看起來已經被驗證過—— 而你不會去驗一件「已經寫在文件上」的事。


四次的共同點

# 錯在哪 被誰抓到
1 量法把不該算的算進去 別人問了一個問題
2 評估沒套用被測的改動 另一個實驗的矛盾
3 用雜訊內的差異下結論 我自己量了 σ
4 沿用沒驗過的舊紀錄 動筆前的例行重驗

四次裡只有兩次是我自己的機制抓到的。 另外兩次是運氣。

而這正好對應到我在這個系列裡罵別人的話: 檢查在它自己的位置上是通過的。


我最後留下的兩個習慣

① 當一個數字要求你做一個很貴的決定時,先回頭驗那個數字。

驗證的力氣應該跟決定的代價成正比。「拆成 24 篇」是高成本難回頭的動作, 在付出之前回頭花十分鐘驗輸入,是我做過投資報酬率最高的一次除錯。

反過來,如果指標建議的動作很便宜(改個標題、調個參數),不驗也還好。

② 動筆前重跑一次,即使那件事寫在自己的筆記上。

第四個錯誤就是這樣抓到的——而它已經進了要交出去的文件。 如果我沒有訂「每篇提到的東西動筆前必須重跑」這條規矩,它會就這樣出去。


為什麼要寫這一篇

因為這個系列唯一難被複製的,不是那些指令。

指令會過期——ODH 3.6 出來,一半的路徑會變。 但「我量錯了四次,這是怎麼發現的」不會過期。

而且它是這整批文章可信度的來源:一個會公開自己量錯過的人, 比一個只展示成功的人可信。


如果你也在寫技術文章或做評估報告,你怎麼確認自己沒搞錯?

🧪 這篇的實驗環境與 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-* 那一套)。 指令的邏輯可以照用,字串要自己對一次。