進階已經雙重審閱,尚未人工抽查

變數選擇

預測模型挑變數的目標與病因學研究完全不同——不看因果、只看有沒有幫助預測。這一頁把三種常見策略跑在同一份乳癌世代上,這份資料分不出它們的外部表現差別(三個點估計只差在小數點第三位,本頁未估計不確定性),真正的差別在別的地方:同一個演算法在同一群人的重抽樣上,會給你幾十個不同的「最終模型」。最後一節談怎麼讀機器學習預測模型的論文——variable importance 與 SHAP 各自說了什麼、沒說什麼,以及為什麼驗收標準一格都沒有改變。

換了問題,挑變數的規則就整套換掉

前面 B2、B6 兩個家族的迴歸都在回答病因學的問題:這個暴露對這個結果有沒有效應。 那種模型裡「要不要放某個變項」是一個因果結構的問題——它是干擾因子(confounder)就必須放, 它是中介(mediator)就不能放,它是對撞因子(collider)放了會製造偏誤。判準在 世代研究DAG 那一頁

預測模型問的是這個人的風險是多少。於是同一個決定換了判準:

病因學模型預測模型
某變項要不要進去看它在因果圖上的位置看它有沒有讓預測變準
中介變項放進去會擋掉要估的效應可以放,只要它在部署時拿得到
共線性目標係數會不穩,是問題只要預測值穩定就不是問題
係數怎麼解讀效應量,要能講因果不要解讀,它只是權重
驗收標準估計無偏、假設成立在沒看過的人身上表現如何

三種策略,同一份資料

survival::rotterdam(開發,2982 位病人、1713 個事件) 配 survival::gbsg(外部驗證,686 位)。候選變項有 8 個: 年齡、停經狀態、腫瘤大小、分級、淋巴結數、PgR、ER、荷爾蒙治療。

三種在論文裡最常見的做法:

  1. 全模型法(full model approach)——事前把 8 個全部指定進去,不論 p 值。
  2. 逐步迴歸——用 AIC 從全模型往回刪。
  3. 單變量篩選——先一個一個變項各跑一次,p < 0.05 的才進多變量模型。
策略留下幾個變項開發世代 C-index外部 C-index外部校準斜率
全模型法(事前指定)80.6700.6620.723
向後逐步迴歸(AIC)60.6690.6610.721
單變量篩選(p < 0.05)70.6690.6620.722

逐步迴歸的問題不是它挑得不準,是它挑得不穩

模型診斷那一頁已經用 300 次模擬示範過一件事: 把 10 個純亂數變項丟進 stepAIC(),它平均會留下 1.81 個,其中 48.0% 的執行至少出現一個 「顯著」的亂數。那個示範不在這裡重做。

這一頁問一個不同的問題:同一個演算法、同一群人,只是重抽一次樣本,它會給你同一個模型嗎?

把開發世代重抽 200 次,每次都跑一遍相同的向後 AIC,記錄每個變項被留下的比例:

長條圖,橫軸是八個候選變項,縱軸是它們在重抽樣中被向後 AIC 選中的百分比。腫瘤大小、分級、淋巴結數在大樣本下接近百分之百,年齡、停經狀態、PgR、ER、荷爾蒙治療則落在二成到八成之間;樣本縮小到 200 人後,除 log ER 外每個變項的選中率都下降(log ER 從 26.5% 略升至 32.0%),長條向中間集中,只有淋巴結數不隨之集中,仍停在 99.0% 附近。
同一份開發世代重抽 200 次,每次跑一遍相同的向後 AIC。深色是用全部病人的結果,淺色是每次只抽 200 人。條形愈接近 100% 代表那個變項愈穩定地被留下;落在中間高度的變項,代表它進不進最終模型基本上是擲骰子。產圖腳本 figures/scripts/B5-01-variable-selection.R
變項單變量 p 值在完整資料的最終模型裡?重抽樣選中率(全部病人)重抽樣選中率(只抽 200 人)
age< 0.00162%32%
menopausal< 0.00140%22%
tumour size< 0.001100%79%
grade 3< 0.001100%56%
nodes< 0.001100%99%
log PgR< 0.00176%31%
log ER0.77327%32%
hormonal tx< 0.00163%26%

在 200 次重抽樣裡,向後 AIC 一共產生了 25 個不同的模型, 出現最多次的那一個也只佔 22%。 把每次抽樣縮到兩百人(更接近多數單中心研究的規模),變成 69 個不同的模型,最常見的那個只佔 12%。

單變量篩選有一個逐步迴歸沒有的毛病

單變量篩選(先各跑一次、p < 0.05 才進多變量)比逐步迴歸更常見,因為它看起來更保守。它不是。

看上表的停經狀態:單變量 p 值遠小於 0.001,所以它通過篩選、進了多變量模型; 但向後 AIC 把它刪掉了。原因是它和年齡幾乎重疊——校正年齡之後它沒有剩下什麼

反過來的情況更危險:一個變項可以在單變量下毫無訊號,卻在校正之後變得重要。 單變量篩選會在它有機會被校正之前就把它丟掉,而且丟掉的痕跡不會出現在最終模型的任何一個數字裡。

那到底該怎麼挑

現在的建議(TRIPOD+AI、Riley 等人的方法學文獻)基本上是同一句話: 在看到結果之前就把候選變項與模型形式定下來,然後全部放進去。

做法什麼時候合理要注意
依領域知識事前指定,全部保留(全模型法)預設做法。候選變項來自既有模型、guideline、臨床上實際拿得到的東西候選數量必須先過 樣本數那一關
用懲罰迴歸讓資料決定權重候選變項多、樣本量不寬裕時lasso 的「挑掉」同樣不穩定,見 收縮那一頁
依「拿不拿得到」刪減永遠可以做,而且應該先做這不是統計決定,不會產生樂觀偏誤
逐步迴歸幾乎沒有若真的用了,選擇過程必須整個進到重抽樣迴圈裡,見 內部驗證那一頁

動手跑一次

library(survival); library(MASS)
data(cancer, package = "survival")

rot <- rotterdam
rot$rfs_time  <- pmin(rot$rtime, rot$dtime)
rot$rfs_event <- as.integer(rot$recur == 1 | rot$death == 1)
rot$size_mm   <- c("<=20" = 15, "20-50" = 35, ">50" = 60)[as.character(rot$size)]
rot$grade3    <- as.integer(rot$grade >= 3)
rot$log_pgr   <- log1p(rot$pgr); rot$log_er <- log1p(rot$er)

cand <- c("age", "meno", "size_mm", "grade3", "nodes", "log_pgr", "log_er", "hormon")
f <- as.formula(paste("Surv(rfs_time, rfs_event) ~", paste(cand, collapse = " + ")))

full <- coxph(f, data = rot)          # 全模型法:就這一行
step <- stepAIC(full, direction = "backward", trace = 0)

# 單變量篩選:注意它問的是「不校正時有沒有關聯」
sapply(cand, function(v)
  summary(coxph(as.formula(paste("Surv(rfs_time, rfs_event) ~", v)),
                data = rot))$coefficients[1, "Pr(>|z|)"])

# 穩定度:同一份資料重抽,看它每次挑到什麼
tally <- setNames(numeric(length(cand)), cand)
for (b in 1:200) {
  d  <- rot[sample(nrow(rot), replace = TRUE), ]
  ff <- f; environment(ff) <- environment()   # 少這行會整批靜默失敗
  st <- stepAIC(coxph(ff, data = d), direction = "backward", trace = 0)
  tally[names(coef(st))] <- tally[names(coef(st))] + 1
}
round(100 * tally / 200)

驗證環境:R 4.6.0 + survival 3.8.6 + MASS 7.3.65。⚠️ stepAIC() 會在「呼叫它的那一層」重新求值模型呼叫式,所以迴圈裡的資料物件必須讓公式看得見;產圖腳本裡有一行 environment(ff) <- environment() 就是在處理這件事,少了它每一次重抽樣都會靜默失敗。

當論文用的是機器學習模型時

「XGBoost 預測 AKI」「隨機森林預測敗血症」這類論文在臨床資訊與重症領域已經很多, 而醫學生第一次撞到它們通常是在書報討論上。這一節只教怎麼讀,不教怎麼跑—— 把模型換成森林或梯度提升是另一條技能樹,但驗收標準一格都沒有變

以下依序談 variable importance、impurity 偏誤、SHAP,以及那件沒有改變的事。

一、variable importance 不是迴歸係數。 它沒有方向、沒有信賴區間、也不是效應量。 一個變項排在第一名,只代表在那個套件所用的重要度定義下它排第一—— permutation importance 的意思是「把它打亂之後模型的預測變差最多」, 而樹系套件的預設往往是下一點要講的 impurity-based 版本,兩者可以排出不同的名次。 不論是哪一種,它都不告訴你風險是升是降、升多少, 更不能被引用成「某因子使風險上升多少」。論文的 Discussion 把重要度排序寫成危險因子清單時, 那是把兩種完全不同的量當成同一個。

二、impurity-based importance 對高基數變項有系統性偏誤。 樹在每個節點挑切點, 取值愈多的變項可切的位置愈多,愈容易在隨機的雜訊上找到一個看起來有用的切法。 所以連續變項與類別很多的變項(例如就診科別、藥品代碼)會系統性地排在前面, 而二元變項會系統性地被低估——這與它們實際上有沒有用無關。 用 permutation importance 或在獨立資料上重算會緩解,但不會消失。

三、SHAP 是局部歸因,不是因果效應。 它回答的是「對這一位病人, 模型的預測值相對於基準被這個變項推了多少」,而那是在模型已經長成這樣的前提下的分解。 模型學到的關聯若來自干擾、選擇或反向因果,SHAP 會忠實地把那份關聯歸因出來, 而且畫得非常有說服力。一張漂亮的 SHAP 圖不是因果證據,它是模型的自述。

四、驗收標準完全沒變。 鑑別力、校準決策曲線外部驗證——這四項一件都不能少。 而 ML 論文最常跳過的正是校準:樹系模型的預測值天然往中間集中, 校準斜率大於 1 是常見結果,而只報 AUC 的論文不會讓你看到這件事。

讀論文時要問的四件事

  1. 候選變項是事前定的,還是資料挑的? Methods 沒寫「事先指定」就要當作是資料挑的。
  2. 挑完之後有沒有重新驗證? 選變數本身用掉了自由度;事後的 p 值、CI、C-index 全部偏樂觀。
  3. 模型裡的變項在部署時真的拿得到嗎? 這是 data leakage 唯一能被讀者抓到的地方。
  4. 有沒有報告被刪掉的那些? 只列最終模型的論文,讀者無從判斷刪的過程有多勉強。

常見誤用

誤用為什麼錯
用逐步迴歸挑變項,再照常報 p 值與 CI選擇本身是多重比較;重抽一次就會換一組變項
說「最終模型納入的都是獨立危險因子」那是預測用的權重,不是因果效應;而且換個子樣本就會變
單變量 p 值不顯著就把變項丟掉有些變項只在校正之後才有貢獻,丟掉的痕跡不會留在任何數字裡
把預測模型的變數選擇當成病因學的變數選擇判準完全不同:一個看因果結構,一個看預測貢獻
因為擔心共線性而刪掉變項預測模型只要預測值穩定就好,共線性不是問題
把部署時拿不到的變項放進模型data leakage;所有統計檢查都會通過,只有實際上線才會發現
候選變項數量不看樣本量就開始篩順序反了,先過樣本數那一關
用「模型比較簡潔」當作刪變項的理由簡潔本身不是驗收標準,預測表現才是

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B5-01-variable-selection.R

讀讀看這張圖

答案取自產生本頁圖表的同一份統計輸出,不是另外打上去的。

三種變數選擇策略在同一個外部世代上算出來的校準斜率分別是 0.723、0.721、0.722。這組數字最該被讀成什麼?

看答案與解析

正確答案: 三個值都明顯小於 1、彼此又只差在小數點第三位,0.723 與另外兩個說的是同一件事:模型把外部世代的風險差異拉得太極端

校準斜率小於 1 的意思是預測出來的風險差異太極端——高風險的人被推得太高、低風險的人被壓得太低。這跟「整體高估」不是同一件事:整體水準偏移屬於截距那一層,把預測值一律往下調只會動到平均,動不到斜率。至於 0.721、0.722、0.723 誰高誰低,那是在同一個外部世代上各算一次的點估計,本頁沒有估計它們的不確定性,第三位小數的排名不能拿來選策略。三個數字一致說的是同一件事,而且是三種策略共同的校準問題,不是它們彼此優劣的證據。

年齡的單變量 p 值遠小於千分之一,但在重抽樣裡它並不是每次都被向後 AIC 留下。這兩件事放在一起說明什麼?

看答案與解析

正確答案: 年齡被留下的比例是百分之 62.0;單變量 p 值問的是不校正時有沒有關聯,AIC 問的是校正之後還剩多少貢獻,兩個問題可以給出不同答案

62.0 與 100.0 的對比就是重點:腫瘤大小在每一次重抽樣都被留下,年齡只有六成出頭。年齡的單變量關聯很強,但它與停經狀態等變項重疊,校正之後剩下的獨立貢獻不足以每次都讓 AIC 願意付那個參數的代價。把落差讀成「重抽樣次數不夠」也站不住——同一批重抽樣裡腫瘤大小拿到 100.0,雜訊不會只挑年齡欺負。停經狀態的 40.0 確實比年齡更不穩定,但那不能反過來證明年齡是穩的;論文上讀者看到的只有「最終模型納入了哪些變項」,看不到它們各自有多勉強。

把開發世代重抽 200 次,每次跑一遍完全相同的向後 AIC,記錄每次得到的最終模型。這個實驗最重要的產出是什麼?

看答案與解析

正確答案: 同一群人、同一支程式,只換抽樣就產生了 25 個不同的最終模型;論文那句「最終模型納入下列變項」描述的是一次抽樣

用全部病人的版本產生了 25 個不同的模型,不是一個——「收斂到唯一一個」的說法在同一份輸出上就被推翻了;每次只抽兩百人的版本更誇張,69 個。重抽樣也不是把完整資料上那個模型再確認一次,它問的是「同一個演算法遇到另一批同樣來源的人會挑出什麼」,而答案常常是別的東西。真正被繼承下去的是後面每一句以「被納入最終模型」為前提的敘述——某某是獨立危險因子、某某在校正後仍有預測價值——它們全部帶著這個不穩定性。

把每次重抽的樣本從全部病人縮到兩百人之後,多數變項的選中率都下降,只有淋巴結數幾乎沒動。這代表什麼?

看答案與解析

正確答案: 淋巴結數在小樣本下仍有百分之 99.0,訊號強到樣本縮小也撐得住;其他變項一起下滑則說明樣本量也在決定誰進得了最終模型

99.0 與 100.0 幾乎一樣,而分級從 100.0 掉到 55.5——同一個演算法、同一群人的子集,差別只在每次看到幾個人。選中率量的是「這個變項的訊號強到能不能穩定通過 AIC 的門檻」,那由訊號強度與樣本量一起決定,不是這個變項在臨床上重不重要。所以把 55.5 讀成「分級在小族群裡比較不重要」,是把統計檢定力的問題講成了生物學;而只看淋巴結數兩欄都接近滿分就說樣本量沒有影響,是挑了唯一一個不受影響的變項來代表整張表。

停經狀態的單變量 p 值遠小於千分之一,所以它通過了單變量篩選;但向後 AIC 把它從模型裡刪掉了。最合理的解讀是什麼?

看答案與解析

正確答案: 停經狀態只在百分之 40.0 的重抽樣裡被留下;它與年齡幾乎重疊,校正年齡之後沒剩下多少獨立貢獻

40.0 這個選中率跟它的單變量 p 值並不矛盾:單變量看的是停經狀態自己跟結果的關聯,而年齡與停經狀態量的幾乎是同一件事,兩個一起放進模型時,停經狀態能多解釋的部分就很小了。年齡的 62.0 高一些,但那不是「誰是真正的危險因子」的判決——選中率是統計上的穩定度,不是因果地位,這一頁的模型本來就不是為了估計效應而配的。log ER 的 26.5 更低沒錯,但它被刪的理由完全不同:它連單變量關聯都沒有。AIC 也不是按選中率排序刪人,它每一步都在問「這個參數付出的代價換不換得到概似的改善」。

三種策略在完整開發資料上留下的變項個數不同,而它們的外部 C-index 差在小數點第三位。這組對照可以支持什麼結論?

看答案與解析

正確答案: 向後 AIC 留下 6 個變項而外部表現沒有掉下來,說明事件數相對參數非常充足時,被刪掉的本來就貢獻很小

這份開發世代每一個參數分到兩百多個事件,在這種條件下逐步迴歸刪掉的通常是本來就沒什麼貢獻的變項,外部表現當然看不出差別——換到事件數吃緊的資料上,同一個刪法會刪掉真正有用的東西。第二個選項把「這一次分不出差別」讀成「事前指定沒有必要」,可是事前指定要防的不是這一次的 C-index,而是上一節那個問題:同一個演算法重抽一次就換一組變項。第三個選項更直接:變項個數介於中間不是任何一種驗收標準,預測表現才是,而預測表現在這裡分不出高下。

延伸觀看

What Are Clinical Prediction Rules?
ENTerry Shaneyfelt· 10 min先確定「預測規則」這個東西在臨床上長什麼樣,再談它裡面的變數怎麼挑。
AI 臨床研究實戰 EP7|預測 vs 分類、Data Leakage、Propensity Score
繁中Colon & Code· 10 min繁中,前半段正好在講「預測問題和因果問題不是同一件事」,對應本頁第一節。
Key Steps and Common Pitfalls in Clinical Prediction Model Research
ENRichard_D_Riley· 57 min一小時的完整版,變數選擇只是其中一段;想把 B5 整個家族串起來時看它。

素材來源與授權

本頁為原創內容

回報內容問題

這個站的統計內容由 AI 撰寫、AI 互審,人工只做抽查。你看得出來的錯,我們不一定看得出來。

寫得越具體越修得動,例如哪一句話跟哪本教科書/哪篇論文的說法不一致。

留了才回得了信;不留也會看。

一併送出的資訊

這些是自動帶上的,每一項都可以取消。