「模型上線後會退化,所以要監控漂移」——這句話大家都會說。
然後就沒有然後了。量什麼?門檻多少?響了要做什麼?
這三題不回答,漂移監控就會變成一個沒人看的儀表板,
或是一個一直響、大家都學會忽略的告警。
| 種類 | 什麼變了 | 多久知道 |
|---|---|---|
| 輸入漂移 | 進來的資料分布變了 | 立刻 |
| 預測漂移 | 模型輸出的分布變了 | 立刻 |
| 概念漂移 | 輸入和正確答案的關係變了 | 要等標籤 |
⭐ 最重要的差別在最後一欄。
前兩種你今天就量得到。第三種——也就是「模型變不準了」——
要等真實結果回來才知道,那可能是幾天(詐欺)到幾個月(信用違約)之後。
所以實務上:用前兩種當早期警訊,用第三種當真正的判定。
不要把「輸入分布變了」講成「模型不準了」——那是推論不是量測。
最常用的是 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 | 明顯變化 |
⚠️ 但這三個數字不是物理常數。
它們是經驗法則,來自信用評分模型的實務,
不保證適用於你的資料、你的樣本數、你的特徵。
這是本篇最重要的一節。
在你設任何門檻之前,先回答:「什麼都沒變的時候,這個指標會抖多少?」
做法:
得到的就是雜訊水準 σ。
然後門檻應該定在雜訊之上,例如 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)的規則,例如:
這些規則對「持續的小漂移」比單點門檻敏感得多,
而對單次雜訊不敏感。
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:至少要有一個完整的業務週期(含月結、季節性)。
而且要留得下來——這是為什麼監控要在上線前就規劃,
不然你會缺一段基準。
本篇的 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)。結論一致。
opendatahub-operator.v3.5.0(即 RHOAI 3.x 的上游開源版)、cert-manager-operator.v1.20.0(3.x 的必要相依,2.x 不需要)kserve、aipipelines、dashboard、workbenches、modelregistry
⚠️ ODH ≠ RHOAI:元件同源,但 namespace 與部分名稱不同
(這裡是 opendatahub,商用版是 redhat-ods-*)。指令邏輯可照用,字串要自己對一次。
你們的漂移告警,上次響是什麼時候?後來做了什麼? 留言或到原文留言都可以,我會回。