街對面新開了一家店,銷售悄悄變了,程式碼卻一行沒改。上個月的模型已經悄悄錯了。什麼檢查能讓它早點被發現?
昨天有效的模型,今天可能因為輸入或行為變化而悄悄失效。監控把目前視窗與可信基線比較,用門檻值控制雜訊,並只記錄聚合指標而不是個人資料。這些小檢查能在漂移變成事故之前發出訊號。 你還會用分箱 PSI 比較分布,並區分輸入漂移和結果漂移。
把目前輸入與基線比較
從一個容易解釋的指標開始,例如某個數值特徵的平均值。儲存一段可信時期的基線視窗,再與最近視窗比較。差異只是需要調查的訊號,不是模型已經壞掉的證明。
baseline = [10, 12, 14]
live = [13, 15, 17]
shift = sum(live) / len(live) - sum(baseline) / len(baseline)
print(round(shift, 2))最簡單的平均值偏移
用基線單位表示差距
原始差值很難跨不同量綱的特徵比較。除以基線標準差,可以把偏移表示成熟悉的單位。絕對值越大訊號越強,但門檻值仍取決於具體業務。
import statistics
baseline = [10, 12, 14]
live = [13, 15, 17]
gap = (sum(live) / len(live) - sum(baseline) / len(baseline)) / statistics.pstdev(baseline)
print(round(gap, 2))三個單位約等於 1.84 個基線標準差
證據足夠才警示
很小的目前樣本可能因偶然產生誇張的平均值。要求最小樣本量、設定門檻值,並顯式處理基線標準差為零的情況。警示應觸發有記錄的檢查,而不是自動改模型。
import statistics
def should_alert(baseline, live, min_n=5, threshold=1.0):
if len(live) < min_n:
return False
baseline_mean = sum(baseline) / len(baseline)
baseline_std = statistics.pstdev(baseline)
if not baseline_std:
return False
gap = (sum(live) / len(live) - baseline_mean) / baseline_std
return abs(gap) >= threshold
baseline = [10, 11, 12, 13, 14]
print(should_alert(baseline, [14, 15, 16, 17, 18]))
print(should_alert(baseline, [15, 16]))小樣本不會觸發警示
監控聚合值,而不是身份
記錄比率和計數,而不是原始使用者文字、提示詞或標識符。正例預測率的變化可以暴露輸入或模型行為變化,同時不洩露個人資料。保留期限和存取權限要寫清楚。
baseline = [0, 0, 0, 0, 1]
live = [0, 0, 1, 1, 1]
base_rate = sum(baseline) / len(baseline)
live_rate = sum(live) / len(live)
print(round(live_rate - base_rate, 2))記錄比率,而不是某個人
分箱 PSI 比較分布
群體穩定性指數比較基線和即時視窗中固定分箱的佔比。很小的佔比會被替換為下限,以避免除以零。PSI 是篩查訊號,不是模型出錯的證明;要檢查移動最大的分箱。
import math
def shares(values, edges):
counts = [0] * (len(edges) - 1)
for value in values:
for i in range(len(edges) - 1):
if edges[i] <= value < edges[i + 1]:
counts[i] += 1
break
total = sum(counts)
return [count / total for count in counts]
edges = [0, 12, 16, 20, 30]
base = shares([10, 12, 14, 16, 18], edges)
live = shares([14, 16, 18, 20, 22], edges)
psi = sum((l - b) * math.log(max(l, 0.001) / max(b, 0.001)) for b, l in zip(base, live))
print([round(x, 2) for x in base])
print([round(x, 2) for x in live])
print(round(psi, 3))比較佔比,而不是身份
區分輸入漂移和結果漂移
輸入漂移表示特徵分布移動了。結果漂移表示標籤到達後,模型的準確率或錯誤成本發生了變化。兩者可能同時出現,也可能各自出現,因此都要監控,並讓警示門檻值與最小樣本量掛鉤。
def accuracy(actual, predicted):
return sum(a == p for a, p in zip(actual, predicted)) / len(actual)
baseline_actual = [1, 0, 1, 1, 0, 1]
baseline_pred = [1, 0, 1, 1, 0, 1]
live_actual = [1, 1, 0, 1, 0, 1]
live_pred = [1, 1, 0, 1, 0, 0]
print("baseline", round(accuracy(baseline_actual, baseline_pred), 2))
print("live", round(accuracy(live_actual, live_pred), 2))輸入和標籤是不同訊號
本課要點
- 基線定義模型仍被信任時的正常狀態。
- 標準化差距讓不同特徵的偏移可以比較。
- 最小樣本量和門檻值能減少誤報。
- 輸入漂移和概念漂移是不同的問題。
- 監控聚合指標,避免保留原始個人資料。
- PSI 篩查分布移動;輸入漂移和結果漂移回答的是不同問題。
常見錯誤
- 只憑一個觀測或極短視窗就警示。
- 把統計偏移當成模型一定出錯的證據。
- 本來記錄比率就夠了,卻儲存完整提示詞或使用者記錄。
- 沒有樣本量或成本策略,卻對每一次微小的 PSI 變化都警示。
比較基線 [0, 0, 1, 1] 與目前 [0, 1, 1, 1] 的正例預測率,列印差值並保留兩位小數。
動手試試
改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。
練習本課
12 道題,作答後立即看到解釋。