模型可解釋性:
上線前要能說出「為什麼」
模型評審會上,AUC 0.9684 投影在牆上。業務主管問:「它憑什麼說這位客戶會流失? 我要打電話過去,總得說個理由。」法遵同事問:「有沒有用到不該用的欄位?」—— AUC 一個都答不出來,它是一個總分,總分不會告訴你分數是怎麼來的。 SHAP 給的是明細:把預測跟平均之間的差距,一分不剩地分給每個特徵。挑一位客戶,看它怎麼分——
貢獻值、機率、排名全部來自 notebook 的實測輸出(shap 0.52.0,RandomForest 100 棵樹 depth 8, 測試集 500 位客戶);特徵的中文名字是為了好讀而貼上去的,資料本身是模擬的 f0–f11。
「為什麼」有兩種問法,別答錯題
可解釋性不是一個問題,是兩個。搞混了,你會拿全域的答案去回答局部的問題——而那正是最常見的錯。
| 全域(global) | 局部(local) | |
|---|---|---|
| 問題長什麼樣 | 整體來說,哪些特徵在決定這個模型的輸出? | 這一筆為什麼被判成 0.986? |
| 誰在問 | 模型審核、法遵、監控 | 客戶、客服、申訴處理 |
| 本課的工具 | 三種重要度、蜂群圖 | SHAP 值、waterfall 圖 |
| 典型誤用 | 拿「全域前三名」去解釋某一位客戶——實測那位低風險客戶身上,全域排第 3 的欄位是他的第 2 大原因 | |
SHAP 用一句白話講完就是:把「這一筆的預測」跟「平均」之間的差距,公平地分給每一個特徵。 「公平」不是形容詞,是有數學定義的(合作賽局理論的 Shapley value)。實務上你只要記住一個檢查:
一分不多、一分不少。這條等式讓 SHAP 跟「感覺型」的解釋徹底分開:它不是「大概是這幾個原因」, 而是把 0.9860 減 0.4882 的那 0.4978 分完。第 3 節會拿真實的一位客戶驗算到小數點後 15 位。
到 notebook 的 1️⃣ 節:資料與 champion 模型同一個模型,三種答案,而三種都對
三行程式,三個不同的問題。所以答案不一樣是正常的,不是誰算錯了:
| 特徵 | ① 內建(佔比) | ② permutation(AUC 掉多少) | ③ mean|SHAP|(機率) |
|---|---|---|---|
| f2 | 0.4078 (#1) | +0.2424 (#1) | 0.2137 (#1) |
| f3 | 0.1608 (#2) | +0.0862 (#2) | 0.1003 (#2) |
| f9 | 0.0868 (#3) | +0.0218 (#3) | 0.0509 (#3) |
| f4 | 0.0747 (#4) | +0.0090 (#5) | 0.0359 (#4) |
| f1 | 0.0449 (#7) | +0.0093 (#4) | 0.0142 (#6) |
| f11 | 0.0499 (#6) | +0.0038 (#7) | 0.0117 (#8) |
| f10 | 0.0189 (#10) | −0.0005 (#12) | 0.0037 (#10) |
前三名 f2 → f3 → f9 三種完全一致——這是好消息:三個問法不同的方法都指向同一組主角, 你可以放心跟業務說「模型主要靠這三件事在判斷」。第 4 名開始就分家了,而每一處分歧都有原因: f4 被模型用得多(內建與 SHAP 都排第 4),但打亂它 AUC 只掉 0.0090——用得多不等於有用。 尾巴那幾欄 permutation 是負的,那不代表「有害」,只代表「打亂它反而剛好好一點點」,也就是雜訊。
①「加起來一定等於 1」是它最大的限制:它只講得出相對佔比,講不出「重要多少」。 而且它偏愛切得動的特徵——連續值與高基數類別可以切出很多刀,每刀貢獻一點,加起來就不低了。 在資料裡加一欄純亂數的客戶編號重訓,三種算法對它的評價是:
| 算法 | customer_id(純亂數)的分數 | 排名(共 13 欄) |
|---|---|---|
| RF 內建 | 0.0172 | 第 11 名——排在兩個真特徵之上(f10 0.0165、f5 0.0158) |
| permutation | +0.0003 | 第 9 名,數值基本上是 0(打亂它 AUC 一動也不動) |
| mean|SHAP| | 0.0038 | 第 10 名,很小但不是 0——模型確實在用它,SHAP 照實說 |
一句話記住三者的分工:內建說「建樹時切了幾刀」、permutation 說「有沒有用」、SHAP 說「模型實際上怎麼用」。 看到「客戶編號很重要」這種結果,先別急著寫進報告——換一種算法問一次。
到 notebook 的 2️⃣ 節:三張長條圖、排名對照與亂數陷阱SHAP 值可以驗算——這是它跟「感覺」的分界線
拿測試集第 1 位客戶(編號 #405,預測機率 0.7460)實際走一遍。從基準出發,把 12 個貢獻依序加上去:
這條等式有兩個立即可用的價值。第一,它是你「有沒有把 SHAP 用錯」最快的自我檢查: 算完加總一次,對不上就是哪裡錯了(多半是忘了選類別、或欄序跟訓練時不同——第 11 節的測驗有實例)。 第二,它讓「最不可能流失的客戶」有唯一解:因為預測 = 基準 + 總和,而基準對每一筆都一樣, 所以「SHAP 總和最小」必然等於「預測最小」。
另外兩個一定要知道的細節。形狀:shap_values(Xte) 回的是 (500, 12, 2)——(列數, 特徵數, 類別數)。二元分類的兩面完全對稱, 要「往流失推」那一面就得寫 [:, :, 1]。忘了這一刀是新手最常見的錯,而且它多半不會噴錯。 單位:sklearn 樹模型的 TreeExplainer 預設解釋 predict_proba,所以 SHAP 值的單位是機率;XGBoost/LightGBM 預設是 log-odds, 加總會等於 margin 而不是機率。看到「加起來不等於機率」,先確認單位。
到 notebook 的 3️⃣ 節:逐欄累積、驗算到 1e-15三張圖,各回答一個問題
| 圖 | 回答什麼 | 拿給誰看 |
|---|---|---|
| waterfall | 這一位客戶,從基準走到預測值的每一步 | 客服、申訴處理、業務 |
| beeswarm | 全部客戶疊在一起:這個特徵越大,機率越高還是越低 | 模型審核、領域專家 |
| scatter | 一個特徵的值 vs 它的 SHAP 值:形狀與交互作用 | 你自己(除錯與理解) |
兩位客戶,兩個世界。高風險的 #1340(0.986)從 0.488 出發, f2 = +1.45 推了 +0.235、f3 再推 +0.103, 一路加到 0.986——他身上幾乎沒有任何一項在往下拉(最大的負貢獻只有 −0.001), 所以他不是「某一項特別糟」,而是全面偏向流失。 低風險的 #775(0.004)則是十二項全部為負: f2 拉了 −0.215、f9 拉了 −0.097。
注意 f9:它在全域重要度只排第 3,但在這一位身上是第 2 大原因。 全域重要度不能拿來解釋個案——這是可解釋性最常被誤用的地方,也是第 1 節那張表最後一列講的事。
蜂群圖用來看方向:每個點是一位客戶,橫軸是 SHAP 值,顏色是那位客戶的特徵值(紅=高、藍=低)。 把方向量化成「特徵值與它的 SHAP 值的相關係數」:
| 特徵 | corr(值, SHAP) | 讀法 |
|---|---|---|
| f2 | +0.924 | 越大,流失機率越高 |
| f3 | −0.921 | 越大,流失機率越低 |
| f4 | −0.895 | 越大,流失機率越低 |
| f0 | +0.838 | 越大,流失機率越高 |
| f9 | +0.775 | 越大,流失機率越高(散得比較開=交互作用較強) |
這張圖是上線前審核最好用的一張:拿給業務看,問一句「照你們的經驗,這個方向對嗎?」 ——方向跟常識相反的特徵,十次有九次是資料處理出了問題(符號寫反、欄位對錯位、缺值被填成 0)。
依賴圖則揭露兩件事。形狀:f2 對 SHAP 的曲線是 S 形,兩端都會飽和—— 分箱平均是 −0.311 / −0.133 / +0.054 / +0.176 / +0.247,中段變化最劇烈、兩端趨平。 樹模型學到的是「切點」不是直線,所以線性模型的係數講不出這件事。 交互作用:f2 落在 1.0 附近的那 26 位客戶,f2 幾乎一樣,SHAP 值卻從 0.111 散到 0.256、相差超過兩倍。 所以:SHAP 值不是「f2 的效果」,是「f2 在這位客戶身上的效果」。
到 notebook 的 4️⃣ 節:兩張 waterfall、蜂群圖、依賴圖解釋要跟模型一起版本化,不然它只是一張漂亮的圖
要變成 MLOps 資產,解釋必須滿足三個條件:跟模型版本綁在一起、之後查得到、跟當時的數字一模一樣。 log_figure 讓圖不用先存檔就進 artifact;log_dict 存的是 可以被程式讀回來比對的數字——第 6 節的監控就靠它。重點在掛到哪一個 run: 解釋是訓練完之後才算的,用 start_run(run_id=...) 續掛回訓練那個 run, 否則半年後你會有一堆孤兒報告,對不回是哪一版模型。
有了報告,模型評審就有東西可以問。三個問題按殺傷力排序:① 有沒有一枝獨秀的特徵? ② 方向合不合常識?③ 有沒有用到不該用的欄位?第 ① 個值得當場做一次—— 假設有人在特徵表加了一欄 days_since_cancel(距離上次取消服務幾天), 聽起來很合理,但它是客戶流失之後才算得出來的:
| 正常模型 | 混進洩漏特徵 | |
|---|---|---|
| 測試集 AUC | 0.9684 | 0.9996 |
| 重要度冠軍 | f2(0.2137) | days_since_cancel(0.3542) |
| 冠軍佔全部重要度 | 44.4% | 65.7% |
AUC 0.9996——只看指標,這是一個該開香檳的模型;而它上線之後會是一場災難,因為預測的那一刻根本拿不到這一欄。 重要度分佈是抓洩漏最便宜的雷達:看到一欄吃掉大半重要度、而且 AUC 好得不像話, 先問一句「這一欄在預測的那一刻真的存在嗎?」。這不是判決(真的有主導特徵的問題確實存在), 它是一個要求你解釋的訊號。
到 notebook 的 5️⃣ 節:掛 artifact + 洩漏偵測重要度什麼時候會變?答案可能跟你想的相反
第 7 課(模型監控)看的是「哪個特徵的分佈變了」,這一課看的是「哪個特徵在決定」。 很容易混為一談,做兩個實驗就分得清楚。
實驗 A:同一個模型 + 漂移的資料。把生產資料的 f0 整體平移 +1.5 (跟第 7 課同一種漂移),丟給同一個模型重算 SHAP:
| 正常資料 | f0 漂移 +1.5 之後 | |
|---|---|---|
| 測試集 AUC | 0.9684 | 0.9676 |
| 平均預測機率 | 0.5070 | 0.5225 ← 第 7 課抓得到的預測漂移 |
| 12 欄重要度排名 | 一名都沒換(f0 自己從 0.0261 微升到 0.0337,但誰都沒換位置) | |
第一次看到會意外,但很合理:模型是同一個模型、樹是同一批樹,它的「想法」當然沒變,變的是資料。 所以 SHAP 重要度不是資料漂移偵測器——想知道「輸入變了沒」用 PSI/KS, 想知道「模型的想法變了沒」才看重要度排名。混用會讓你在最需要警報的時候什麼都收不到。
實驗 B:重訓之後,才是重要度該警報的時候。假設上游改版, f2 被灌成常數 0(欄位改名、join 失敗、預設值填 0,這種事每季發生一次)。 管線照常跑完、模型照常重訓、照常註冊成新版本,沒有任何一個步驟報錯:
| 上一版 champion | 重訓後的新版 | |
|---|---|---|
| 測試集 AUC | 0.9684 | 0.8884 ← 掉了,但不到會有人尖叫 |
| 前五名 | f2 → f3 → f9 → f4 → f0 | f3 → f9 → f4 → f6 → f0 |
| f2 的 mean|SHAP| | 0.2137 | 0.0000 |
| 換過位置的欄位 | — | 9 / 12 |
品質閘門若設在 0.85,這個模型會被放過去。而重要度講的是完全不同的故事: 原本一個人扛起 44% 決策權的 f2,貢獻變成 0。警報寫起來只有三行:
把它寫成 Dagster 的 @asset_check(第 3 課)或 pytest(第 13 課),每次重訓都跟上一版比。 這就是第 5 節那份 artifact 的真正用途:它不是給人看的圖,是給下一次重訓當比較基準的資料。 門檻用「前三名有沒有換人」比「數值差多少」穩,因為數值本來就會浮動; 而警報的動作是要求人來看一眼,不是自動擋——重要度變了,也可能只是模型真的變好了。
到 notebook 的 6️⃣ 節:兩個實驗並排比較禁用欄位檢查:寫得出來,也會被繞過去
法遵給你一張清單:「這幾欄是敏感資訊,可以用來統計,不得作為個案決策的依據。」 「沒有放進訓練資料」是最乾淨的做法,但真實專案的特徵表是共用的、是別的團隊維護的、上週還多了三欄—— 所以你需要一個每次上線都跑一次的檢查。注意它回傳的是報告不是 True/False: 出事的時候你需要知道差多少。
f6 沒過。這時候該做的不是把門檻調到 0.02(雖然那樣報告會變綠色), 而是三步:① 從訓練資料把它拿掉重訓,看代價有多大——實測拿掉 f6 之後 AUC 是 0.9699,比原本的 0.9684 還高一點點(合規常常沒有你以為的那麼貴,做過才知道); ② 確認沒有代理特徵頂上來;③ 把檢查寫進管線,不是上線前才想起來。
第 ② 步比第 ① 步重要得多。把 f2 當成禁用欄位、照規定移除, 但特徵表裡另外有一欄 region_score(與 f2 的相關係數 0.97):
| 檢查項目 | 結果 |
|---|---|
| 禁用欄位檢查 | PASS——f2 根本不在欄位清單裡 |
| 模型 AUC | 0.9655(原本 0.9684,幾乎沒損失) |
| 新的重要度冠軍 | region_score 0.2104(原本 f2 是 0.2137) |
模型照樣在用那個資訊,只是換了個名字。這叫代理特徵(proxy),是所有「禁用欄位」制度共同的漏洞, 而且不需要有人故意——只要特徵表裡有任何跟禁用欄位相關的東西就會自然發生。 SHAP 幫得上忙的地方是讓它現形:一個新欄位突然衝上第一名,你至少會問一句「這欄是怎麼算出來的?」。 真正的解法在資料治理那一層(查代理欄位的相關係數、要求特徵有來源說明), 但這一節至少告訴你:只查名字的合規檢查,是會被繞過去的。
到 notebook 的 7️⃣ 節:檢查函式與代理特徵實驗把 waterfall 變成三句話
瀑布圖是給你看的,不是給客戶看的。客服打電話出去需要的是三句話,而這完全可以自動生成 ——SHAP 值本來就是排好序的數字:
這段模板有三個刻意的設計,每一個都是被投訴教出來的:
| 設計 | 不這樣做會怎樣 |
|---|---|
| 先給基準再給分數 | 單獨一個「98.6%」客戶只會問「所以呢」;有了 48.8% 才有意義 |
| 推高與拉低都講 | 只講壞消息像在找碴 |
| 「沒有」也要有分支 | 低風險那位十二項全是負的,沒有這個分支就會印出半句話——上線後炸掉的都是這種邊界情況 |
| 特徵值與 SHAP 值分開講 | 混在一起講,客戶會以為你在說「中斷 3 次就一定會流失」 |
⚠️ 最後一件事,也是最重要的一件:這三句話講的是模型怎麼算的,不是為什麼會發生。下一節就講這件事。
到 notebook 的 8️⃣ 節與 🎛 互動格:換一位客戶看解釋誠實比方便重要:SHAP 做不到的三件事
① SHAP 不是因果。f2 的 SHAP 是 +0.26,正確的說法是「這個模型因為 f2 的值而把機率調高了 0.26」; 錯誤的說法是「f2 高導致客戶流失」,更錯的是「把 f2 降下來就能留住客戶」。 模型只學到相關;相關可能來自因果,也可能來自共同原因或反向因果—— 「客服接觸次數多」不是流失的原因,是已經想走的症狀。 實務分界線:SHAP 可以拿來排優先順序(先打給誰)與做審核(模型有沒有亂來), 不能拿來制定干預(改哪個欄位就會怎樣)。
② 相關的特徵會分攤貢獻。把 f2 原封不動複製一欄叫 f2_copy 再訓練:
| 原本 | 多了一欄一模一樣的 f2_copy | |
|---|---|---|
| 模型 AUC | 0.9684 | 0.9653(一樣強) |
| f2 的 mean|SHAP| | 0.2137 | 0.1150 ← 腰斬 |
| f2_copy 的 mean|SHAP| | — | 0.1368(兩者相加 0.2517) |
f2 沒有變得比較不重要,只是有人跟它平分功勞。真實特徵表裡這太常見了 (「近 30 天訂單數」和「近 30 天訂單金額」高度相關),後果是你會低估一整組相關特徵裡每一個成員, 甚至誤以為某個關鍵資訊「模型沒在用」。對策不是換工具,是先看特徵之間的相關係數、把高度相關的欄位當一組看。
③ TreeExplainer 只吃樹模型。餵它一個 LogisticRegression 會直接拒絕(InvalidModelError)。兩條退路,代價差很多:
| TreeExplainer | KernelExplainer | LinearExplainer | |
|---|---|---|---|
| 適用 | 只有樹 | 任何模型 | 線性模型 |
| 這次算了幾列 | 500 | 20 | 500 |
| 花了多久 | 0.6–1.2 秒 | 5–6 秒 | 0.3 毫秒 |
| 每列成本 | 1–3 毫秒 | 約 270 毫秒(差 200 倍上下) | — |
| 結果 | 精確 | 與 TreeSHAP 相關 0.9906 | 封閉解,完全精確 |
KernelExplainer 靠反覆遮蔽特徵、重新預測來估計,所以要給它一份背景資料 (被遮掉的特徵用什麼值代替),成本大約正比於背景筆數——用 shap.sample(X, 100) 壓縮,別把整個訓練集丟進去。 實務規則:能用專用 explainer 就用專用的,Kernel 是最後手段(抽樣看全域、個案來申訴時才現算那一筆)。
LinearExplainer 快到不用計時(500 列 0.3 毫秒),而且給了理解 SHAP 最好的入口 ——它的結果不是估計,是封閉解:
線性模型的 SHAP 就是「係數 × 這一筆偏離平均多少」,樹模型只是把同一個概念推廣到非線性。 順帶戳破一個統計課上的簡化:mean|SHAP| 的排名跟「|係數| × 標準差」的排名完全一致, 但跟「只看係數大小」從第 6 名起就不同——一個係數再大,如果那一欄在資料裡幾乎不變動,它對實際預測就沒有影響力。
到 notebook 的 9️⃣ 節:三種 explainer 實測與計時換你動手
找出測試集裡「SHAP 貢獻總和最負」的那位客戶(模型最確定不會流失的人),印出他的前三大原因並產出三句說明。 想一下:他跟「機率最低」的那位是同一個人嗎?為什麼?
用 LinearExplainer 解釋 logistic regression,把 mean|SHAP| 排名、|coef| 排名、 |coef|×標準差 排名三者並排印出來,找出排名不一致的那一對特徵,並解釋為什麼。
寫一個「禁用欄位檢查」的失敗案例:把 f2 改名成 is_vip 訓練, 斷言它的 mean|SHAP| < 0.01——這個斷言應該要失敗。接著把它修好(提示:修的方法不是調門檻), 並回答「修好之後模型損失多少 AUC」。
卡住了?每一題在 notebook 末節都有折疊解答——先自己做,再打開對照。
情境測驗
離開前試試看:下面的情境都真的會遇到。每題選一個你認為的最佳做法,選了馬上看得到解釋。
Q1 情境題
客服要打電話給一位被判為高流失風險(0.986)的客戶,問你「跟他說什麼理由」。你手上有全域重要度報告(前三名 f2 → f3 → f9)。最好的做法是?
這是局部問題,要用局部工具。全域重要度講的是「模型整體靠什麼」,不是「這一位為什麼」——實測就有反例:全域排第 3 的 f9,在低風險客戶 #775 身上是他的第 2 大原因;而某些客戶的主因根本不在全域前三名裡。C 的 waterfall 直接把 0.986 減 0.488 的差距分完,每一項都有數字可以講,這也正是第 8 節那個三句話模板的來源。A 是最常見的誤用,講出來的理由可能跟這位客戶完全無關。B 放棄太早,SHAP 就是為這件事發明的。D 概念上混了:permutation 是把整欄在客戶之間洗牌來看指標掉多少,單筆資料無從洗起,而且它衡量的是「對準確度有沒有用」,不是「這一筆為什麼」。
Q2 錯誤診斷
同事照文件寫了下面這段,程式跑完沒有任何錯誤訊息,但畫出來的圖只剩兩個特徵、x 軸寫著 SHAP interaction value。最可能的原因?
shape 已經把答案印出來了:(500, 12, 2) =(列數, 特徵數, 類別數)。二元分類的兩面完全對稱,要「往流失推」那一面必須寫 [:, :, 1]。而 summary_plot 收到三維陣列時會把它當成交互作用值(interaction values)來畫——這是本課最危險的一種錯:它不噴任何錯誤,只是安靜地畫出一張看起來很專業、但完全不是你要的圖。防身法有兩個:形狀先 print 出來看;以及第 3 節那條加總等式,base + vals.sum(1) 必須等於 predict_proba。A 不成立,DataFrame 正是建議的傳法(欄名是解釋的一部分)。C 是杜撰,summary_plot 沒有被棄用,而且 beeswarm 收到同樣的三維輸入一樣會出問題。D 是誤解,explainer 建立時就知道模型的輸出形狀。
Q3 情境題
團隊想加一個「模型的想法有沒有變」的監控,提案是:每天把當天的生產資料丟給現在線上這個模型重算 SHAP,比對重要度排名,變了就發警報。這個設計最大的問題是?
實測直接打臉這個設計:把 f0 整體平移 +1.5(明顯的資料漂移,平均預測機率從 0.5070 跳到 0.5225)丟給同一個模型,12 欄的重要度一名都沒換。原因很簡單——模型是同一個模型、樹是同一批樹,它的「想法」當然沒變,變的是資料。所以這個監控會安安靜靜地永遠不響。正確的分工是:資料變了沒用第 7 課的 PSI/KS;模型的想法變了沒在每次重訓後、拿新模型的重要度跟 champion 的 explain/global_importance.json 比。實測那個「上游把 f2 灌成常數」的案例,重訓後前五名整批換人、9/12 欄換位置,而 AUC 只從 0.9684 掉到 0.8884——閘門設 0.85 還會放它過。A 不對,TreeSHAP 每列只要 1–3 毫秒,成本不是問題。B 是誤解,SHAP 解釋的是模型的輸出,完全不需要標籤(這正是它適合上線後使用的原因)。C 的觀察本身沒錯(所以實務上比「前三名有沒有換人」比較穩),但它沒有點出這個設計根本不會偵測到任何東西。
Q4 錯誤診斷
解釋報告產出來了、圖也畫得很漂亮,但同事做自我檢查時發現加總對不上。他把同一份 DataFrame 丟給模型預測時看到下面的錯誤。最可能的原因與修法?
錯誤訊息最後一句寫得很明白:「欄名必須跟 fit 時同一個順序」。真正危險的是另一半——實測同一份亂序資料丟給 explainer.shap_values() 完全不報錯,照樣回一個形狀正確的陣列,只是每個值都對到錯的欄位。這種錯畫得出圖、有小數點、看起來很專業,全部是錯的。第 3 節那條加總等式就是為這種情況存在的自我檢查:對不上就是哪裡錯了。B 不成立,取 [0] 只會讓整批差一個固定常數(而且方向相反),不會每一筆差不同的量。C 不成立,這份資料沒有缺值,而且錯誤訊息講的是欄名順序不是 NaN。D 明顯不對,實測 500 筆的最大誤差是 1.8e-15,差 0.3 是十四個數量級以外的事。
Q5 情境題
法遵要求某一欄不得作為決策依據。你把它從訓練資料移除、重訓、跑禁用欄位檢查——PASS,AUC 幾乎沒掉。可以送審了嗎?
只查名字的合規檢查會被繞過去。實測:拿掉 f2、留下一欄與它相關 0.97 的 region_score,禁用欄位檢查 PASS(f2 根本不在清單裡)、AUC 只從 0.9684 掉到 0.9655、而 region_score 以 0.2104 直接坐上重要度第一名——模型照樣在用那個資訊,只是換了個名字。而且這不需要有人故意,只要特徵表裡有任何相關的欄位就會自然發生。SHAP 在這裡的價值是讓它現形(一個新欄位突然衝上第一名,你至少會去問「這欄怎麼算的」),真正的解法在資料治理那一層。A 正是這一題要打破的直覺。B 調門檻沒有意義,被移除的欄位根本不在報告裡,門檻再嚴也查不到不存在的東西。D 更糟:把欄位灌成常數就是第 6 節那個「上游 bug」的樣子,模型會沉默地失去 0.08 AUC,而且留著一個隨時可能被還原的洞。
實作在 molab 跑(免費)
molab 的登入狀態進不了內嵌框架(瀏覽器的跨站 cookie 保護), 所以 notebook 要在新分頁執行——把它跟本頁並排開,左邊教學照樣對照。
- 登入 molab(GitHub / Google)
- 開啟課程 notebook,Fork 成自己的副本即可編輯
- 從第一格往下全部執行(首次安裝套件約 1–2 分鐘)——免費 CPU 環境即可,不需要 GPU;全部計算加起來不到半分鐘
不想用 molab?下載 model-explainability_ext.py 後在自己電腦
uvx marimo edit --sandbox model-explainability_ext.py,依賴會自動安裝。
molab 的線上編輯器在手機上體驗有限——動手這一段建議用電腦進行。