Day 29:漂移監控要量什麼
這是什麼、解決什麼問題
「模型上線後會退化,所以要監控漂移」——這句話大家都會說。
然後就沒有然後了。量什麼?門檻多少?響了要做什麼?
這三題不回答,漂移監控就會變成一個沒人看的儀表板, 或是一個一直響、大家都學會忽略的告警。
什麼時候你會用到
- 模型上線之後(不是之前)
- 有人問「你怎麼知道模型還準」
- 要設計重訓的觸發條件
步驟一:三種漂移,分清楚
| 種類 | 什麼變了 | 多久知道 |
|---|---|---|
| 輸入漂移 | 進來的資料分布變了 | 立刻 |
| 預測漂移 | 模型輸出的分布變了 | 立刻 |
| 概念漂移 | 輸入和正確答案的關係變了 | 要等標籤 |
⭐ 最重要的差別在最後一欄。
前兩種你今天就量得到。第三種——也就是「模型變不準了」—— 要等真實結果回來才知道,那可能是幾天(詐欺)到幾個月(信用違約)之後。
所以實務上:用前兩種當早期警訊,用第三種當真正的判定。
不要把「輸入分布變了」講成「模型不準了」——那是推論不是量測。
步驟二:輸入漂移怎麼量——PSI
最常用的是 PSI(Population Stability Index):
import numpy as np
def psi(expected, actual, bins=10):
edges = np.percentile(expected, np.linspace(0, 100, bins + 1))
edges[0], edges[-1] = -np.inf, np.inf
e = np.histogram(expected, edges)[0] / len(expected)
a = np.histogram(actual, edges)[0] / len(actual)
e, a = np.clip(e, 1e-6, None), np.clip(a, 1e-6, None)
return float(np.sum((a - e) * np.log(a / e)))
業界慣用的判讀:
| PSI | 一般說法 |
|---|---|
| < 0.1 | 沒什麼變化 |
| 0.1 – 0.25 | 中度變化,注意 |
| > 0.25 | 明顯變化 |
⚠️ 但這三個數字不是物理常數。 它們是經驗法則,來自信用評分模型的實務, 不保證適用於你的資料、你的樣本數、你的特徵。
步驟三:⭐ 先量雜訊,再定門檻
這是本篇最重要的一節。
在你設任何門檻之前,先回答:「什麼都沒變的時候,這個指標會抖多少?」
做法:
- 拿一段你確定沒有變化的歷史資料
- 切成兩半(或用滑動視窗)
- 算 PSI
- 重複幾十次,看分布
得到的就是雜訊水準 σ。
然後門檻應該定在雜訊之上,例如 3σ,
而不是直接抄 0.25。
抄來的門檻只有兩種結局:太鬆(永遠不響,等於沒有) 或太緊(一直響,大家學會忽略,等於沒有)。
兩種結局一樣。
下一節是我真的量出來的數字——比我預期的嚴重。
同樣的邏輯適用在模型指標本身。我在 lab 上量過 同一份設定只改隨機種子,重跑數次的分數差異—— 那個差異就是「什麼都沒改」時的波動。 比它小的變化,不值得當成訊號。
⭐ 我實際量了一次——結果比我預期的嚴重
上面那段是原則,這裡是數字。
我拿 lab 的語料(當時是 4,346 篇文章),把同一份資料隨機切兩半算 PSI, 重複 200 次。兩半來自同一個分布,所以理論上「什麼都沒變」。
📌 可重現性說明:那份語料是清理流程早期的版本,現在 repo 裡的
artifacts/data_report.json已經是docs_out: 10953——你去 clone 會對不到 4,346。 2026-09-21 我用現在的語料、改用「每個非空行」當樣本單位、重寫一支腳本再跑一次, 下面每一張表的形狀和數量級都重現得出來(誤報率 86% vs 85%、 5%/10%/20% 漂移的 PSI 幾乎逐位相同)。 具體數字會隨語料變,結論不會。
| 每半的樣本數 | PSI 平均 | σ | 最大 | 3σ 門檻 |
|---|---|---|---|---|
| 100 | 0.1874 | 0.0902 | 0.6574 | 0.4581 |
| 500 | 0.0378 | 0.0172 | 0.0891 | 0.0895 |
| 1,000 | 0.0174 | 0.0087 | 0.0554 | 0.0434 |
| 2,000 | 0.0092 | 0.0046 | 0.0332 | 0.0230 |
看第一列。
樣本數 100 的時候,什麼都沒變的 PSI 平均是 0.187—— 按教科書的判讀,那是「中度變化,注意」。而最大值 0.657, 是「明顯變化」門檻的 2.6 倍。
換成誤報率更清楚:
| 樣本數 | 超過 0.1 的比例 | 超過 0.25 的比例 |
|---|---|---|
| 100 | 85% | 24% |
| 500 | 0% | 0% |
| 1,000 | 0% | 0% |
樣本數 100 的時候,你有 85% 的機率收到「資料漂移了」的告警—— 而資料一點都沒變。
這就是為什麼「抄 0.1/0.25」會失敗。那兩個數字沒有錯, 但它們預設了一個你沒注意到的前提:樣本要夠大。
另一半:門檻對了,也不代表抓得到
反過來我注入真的漂移(把文章長度整體拉長 X%),看多大才超得過 3σ 門檻:
| 漂移幅度 | PSI | 抓得到嗎 |
|---|---|---|
| 5% | 0.0210 | ✗ 淹在雜訊裡 |
| 10% | 0.0225 | ✗ |
| 20% | 0.0377 | ✗(3σ 門檻是 0.0434) |
| 50% | 0.1064 | ✓ |
n=1,000、3σ 的設定下,20% 的漂移抓不到。
所以「先量雜訊」量完之後,還有第二個問題要問: 這個門檻抓得到我在乎的那種變化嗎?
如果答案是「要 50% 才抓得到」,而你在乎的是 10%—— 那不是把門檻調低就好(調低就回到誤報),是要加大樣本或換特徵。
⚠️ 誠實說明:我量的特徵是「每篇文章的字元長度」, 不是真實模型的輸入分布。具體數字不能直接搬到你的資料上—— 但「雜訊隨樣本數縮小、而教科書門檻假設了樣本夠大」這個形狀是通用的。 你要做的是拿你自己的資料跑一次上面這件事。
步驟四:用控制圖,不要用單點比較
單次超過門檻就告警,會很吵。
更好的做法是控制圖(SPC)的規則,例如:
- 連續 3 點超過 2σ
- 連續 7 點同方向
- 單點超過 3σ
這些規則對「持續的小漂移」比單點門檻敏感得多, 而對單次雜訊不敏感。
Day 28 那個「連續 20 次小退步」的滑坡, 單點門檻抓不到,控制圖規則抓得到。
步驟五:響了要做什麼
告警要對應一個動作,不然它只是噪音。
| 訊號 | 動作 |
|---|---|
| 輸入漂移中度 | 記錄、觀察,不動模型 |
| 輸入漂移明顯 | 檢查上游資料源是不是壞了(多數時候是這個) |
| 預測分布明顯變化 | 檢查是不是有異常輸入 |
| 真實指標下降 | 觸發重訓評估 |
| 延遲/錯誤率異常 | 工程問題,跟模型無關 |
⭐ 第二列要特別強調: 輸入漂移最常見的原因不是「世界變了」,是「上游壞了」—— 某個欄位開始傳空值、單位改了、編碼變了。
先查資料管線,再懷疑模型。
怎麼確認做對了
| 檢查 | 判準 | |
|---|---|---|
| 1 | 有基準資料 | 存下來的,不是憑印象 |
| 2 | 量過雜訊 σ | ⭐ 有數字 |
| 3 | 門檻高於雜訊 | 而不是抄來的 |
| 4 | 每個告警有對應動作 | 寫下來 |
| 5 | ⭐ 注入一個假漂移,確認會響 | 見下 |
第 5 項:故意送一批分布不同的資料,確認告警真的觸發。
這是 Day 28 說的反向測試,套在監控上一樣成立: 沒驗過會響的告警,不知道它會不會響。
常見問題
Q:多久量一次? A:看你的資料流量和決策週期。 太頻繁會被雜訊淹沒(回到步驟三:先知道雜訊多大)。 日結、週結是常見的起點。
Q:TrustyAI 能做嗎? A:ODH/RHOAI 有 TrustyAI 元件(Day 2 的清單裡), 提供偏誤偵測和部分漂移功能。 我沒有在 lab 上跑過它的漂移功能,所以不能給你實測評價—— 但要用之前,先確認它算的是哪一種漂移, 以及它的門檻是不是也是抄來的。
Q:LLM 怎麼監控? A:更難,因為沒有「正確答案」可以比對。 實務上會看:輸出長度分布、拒答率、 以及一組固定的評估題每天跑一次(golden set)。 最後一項最實在。
Q:資料要留多久才能做漂移監控? A:至少要有一個完整的業務週期(含月結、季節性)。 而且要留得下來——這是為什麼監控要在上線前就規劃, 不然你會缺一段基準。
你們的漂移告警,上次響是什麼時候?後來做了什麼?
🧪 這篇的實驗環境與 lab 檔案(最後更新 2026-08-30)
本篇的 PSI 實驗在 2026-09-21 用不同語料、不同樣本單位獨立重跑過一次:n=100 的誤報率 86%(原 85%)、超過 0.25 是 28%(原 24%),注入 5%/10%/20% 漂移的 PSI 為 0.0206/0.0215/0.0403(原 0.0210/0.0225/0.0377)。結論一致。
叢集
- 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.2、openshift-gitops-operator.v1.21.3
DataScienceCluster 開啟的元件
kserve、aipipelines、dashboard、workbenches、modelregistry、kueue(Unmanaged)- 其餘(
ray/trustyai/feast/aigateway…)為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 檔案
- YAML/Containerfile/腳本:github.com/ryanGTR/openshift-ai-30days
(含 Day 對照表;主機名是佔位符,跑
set-lab-host.sh換成你自己的) - 服務的那個模型:llm-from-scratch(從零手刻的小 GPT)
⚠️ ODH ≠ RHOAI:元件同源,但 namespace 與部分名稱不同
(我這裡是 opendatahub,商用版是 redhat-ods-* 那一套)。
指令的邏輯可以照用,字串要自己對一次。
留言與指正
我特別想知道:你們的漂移告警,上次響是什麼時候?後來做了什麼?