險(xiǎn)預(yù)測(cè)中的實(shí)戰(zhàn)應(yīng)用)
1. 項(xiàng)目概述當(dāng)數(shù)據(jù)“不聽話”時(shí)我們?nèi)绾晤A(yù)測(cè)心臟病風(fēng)險(xiǎn)在醫(yī)療數(shù)據(jù)分析尤其是像心臟病預(yù)測(cè)這類關(guān)乎生命健康的領(lǐng)域我們常常會(huì)遇到一個(gè)棘手的問題數(shù)據(jù)“不聽話”。這里的“不聽話”指的是數(shù)據(jù)不滿足許多經(jīng)典統(tǒng)計(jì)模型比如邏輯回歸所依賴的嚴(yán)格假設(shè)比如線性關(guān)系、正態(tài)分布、同方差性等?,F(xiàn)實(shí)世界采集到的臨床數(shù)據(jù)如年齡、膽固醇水平、最大心率等其與患病風(fēng)險(xiǎn)之間的關(guān)系往往是復(fù)雜、非線性的。強(qiáng)行用線性模型去套就像用一把直尺去測(cè)量蜿蜒的河流結(jié)果難免失真。這正是非參數(shù)方法大顯身手的地方。它們不預(yù)設(shè)數(shù)據(jù)服從某種特定的分布形式而是“讓數(shù)據(jù)自己說話”直接從數(shù)據(jù)本身的結(jié)構(gòu)中學(xué)習(xí)規(guī)律。今天要聊的這個(gè)項(xiàng)目核心就是運(yùn)用兩種強(qiáng)大的非參數(shù)技術(shù)——核方法Kernel Methods與K-近鄰算法K-Nearest Neighbors, K-NN來構(gòu)建心臟病風(fēng)險(xiǎn)的分類預(yù)測(cè)模型。我們手頭有一套真實(shí)的心臟病臨床數(shù)據(jù)集目標(biāo)是根據(jù)一系列生理指標(biāo)判斷患者是否患有心臟病。這個(gè)項(xiàng)目對(duì)于數(shù)據(jù)科學(xué)家和醫(yī)療分析從業(yè)者來說極具實(shí)踐價(jià)值它繞開了參數(shù)估計(jì)的陷阱直接擁抱數(shù)據(jù)的復(fù)雜性特別適合處理那些關(guān)系曖昧、邊界模糊的醫(yī)學(xué)分類問題。簡(jiǎn)單來說如果你正在為你的分類模型總是精度不高、無法捕捉復(fù)雜模式而頭疼或者你的數(shù)據(jù)看起來“奇形怪狀”不服從常見分布那么跟隨這篇筆記一起深入核方法與K-NN的實(shí)戰(zhàn)世界或許能給你帶來新的解題思路。我們將從原理入手拆解每一個(gè)關(guān)鍵步驟分享我踩過的坑和總結(jié)的技巧最終實(shí)現(xiàn)一個(gè)穩(wěn)健的預(yù)測(cè)模型。2. 核心思路與算法選型為什么是核方法與K-NN面對(duì)一個(gè)分類預(yù)測(cè)任務(wù)尤其是醫(yī)療領(lǐng)域的二分類問題患病/健康算法庫里的選擇琳瑯滿目。為什么偏偏聚焦于核方法和K-NN這背后是基于數(shù)據(jù)特性和問題本質(zhì)的深思熟慮。2.1 數(shù)據(jù)特性與線性不可分困境心臟病預(yù)測(cè)數(shù)據(jù)通常包含多個(gè)特征例如胸痛類型、靜息血壓、血清膽固醇、空腹血糖等。這些特征與目標(biāo)變量是否患病之間的關(guān)系在原始特征空間里很可能是非線性、交織在一起的。想象一下如果我們把“膽固醇”和“最大心率”兩個(gè)特征畫成散點(diǎn)圖患病和健康的點(diǎn)很可能像兩團(tuán)糾纏在一起的云無法用一條直線線性分類器干凈利落地分開。這就是所謂的“線性不可分”問題。傳統(tǒng)的邏輯回歸雖然強(qiáng)大但其本質(zhì)是尋找一個(gè)線性決策邊界。當(dāng)數(shù)據(jù)線性不可分時(shí)它的表現(xiàn)就會(huì)大打折扣除非我們手動(dòng)進(jìn)行復(fù)雜的特征工程如添加多項(xiàng)式項(xiàng)、交互項(xiàng)但這又引入了新的復(fù)雜度和過擬合風(fēng)險(xiǎn)。2.2 核方法升維打擊的藝術(shù)核方法的精髓可以用一個(gè)經(jīng)典的比喻來理解在一張平鋪的紙上有兩團(tuán)墨水漬混在一起你無法畫一條線分開它們。但如果你把這張紙揉成一團(tuán)一種非線性變換扔進(jìn)三維空間可能突然發(fā)現(xiàn)這兩團(tuán)墨水漬在三維空間中變得涇渭分明可以用一個(gè)平面輕松隔開。核方法做的就是這件事但它非常巧妙它通過一個(gè)“核函數(shù)”Kernel Function將數(shù)據(jù)從原始的低維空間映射到一個(gè)更高維甚至無限維的特征空間而不需要顯式地計(jì)算這個(gè)高維空間中的坐標(biāo)那計(jì)算量是災(zāi)難性的。在更高維的空間里數(shù)據(jù)變得線性可分的可能性大大增加。注意這里說的“核”與操作系統(tǒng)內(nèi)核無關(guān)它是一個(gè)數(shù)學(xué)函數(shù)用于計(jì)算兩個(gè)數(shù)據(jù)點(diǎn)在變換后空間中的內(nèi)積。常用的核函數(shù)包括線性核、多項(xiàng)式核和高斯徑向基核RBF。對(duì)于心臟病數(shù)據(jù)這種可能具有復(fù)雜邊界的情況高斯RBF核往往是首選因?yàn)樗梢援a(chǎn)生非常光滑、非線性的決策邊界。在R語言中我們最常用的核方法分類器是支持向量機(jī)SVM的核化版本通過e1071或kernlab包實(shí)現(xiàn)。它尋找那個(gè)在高維空間中能將兩類數(shù)據(jù)間隔最大化的超平面。這個(gè)方法對(duì)于中小型數(shù)據(jù)集、且特征間存在復(fù)雜非線性關(guān)系時(shí)通常能表現(xiàn)出色。2.3 K-NN算法基于相似度的直觀判決如果說核方法是“宏觀戰(zhàn)略”上的升維那么K-NN就是“微觀戰(zhàn)術(shù)”上的鄰里守望。它的思想極其直觀要判斷一個(gè)新患者是否患病就去看看在特征空間里離他最近的K個(gè)已知病例大多數(shù)是什么情況?!敖煺叱嘟吆凇?。K-NN是一種典型的惰性學(xué)習(xí)算法它不需要在訓(xùn)練階段構(gòu)建一個(gè)顯式的模型而是把所有的計(jì)算推遲到預(yù)測(cè)階段。它的決策邊界是局部的、不規(guī)則的能夠很好地適應(yīng)數(shù)據(jù)分布的局部特性。對(duì)于心臟病數(shù)據(jù)如果患病與否的規(guī)律在局部區(qū)域內(nèi)表現(xiàn)一致即相似生理特征的人患病風(fēng)險(xiǎn)相似那么K-NN會(huì)非常有效。它的優(yōu)勢(shì)在于原理簡(jiǎn)單、無需參數(shù)估計(jì)、對(duì)異常值有一定魯棒性。但劣勢(shì)也很明顯預(yù)測(cè)時(shí)計(jì)算開銷大需要計(jì)算與所有訓(xùn)練樣本的距離對(duì)特征尺度和無關(guān)特征敏感且在類別不平衡時(shí)容易被多數(shù)類主導(dǎo)。2.4 為何將兩者結(jié)合分析在這個(gè)項(xiàng)目中同時(shí)使用核方法SVM和K-NN并非簡(jiǎn)單堆砌而是出于比較和互補(bǔ)的考量方法論對(duì)比一個(gè)基于全局間隔最大化SVM一個(gè)基于局部投票K-NN從兩個(gè)截然不同的哲學(xué)角度解決同一問題能讓我們更全面地理解數(shù)據(jù)中蘊(yùn)含的模式。性能基準(zhǔn)我們可以將兩者的性能如準(zhǔn)確率、召回率進(jìn)行對(duì)比作為后續(xù)模型優(yōu)化的基線。穩(wěn)健性檢查如果兩種原理迥異的模型在測(cè)試集上表現(xiàn)相近那么我們對(duì)預(yù)測(cè)結(jié)果的信心會(huì)更強(qiáng)。如果差異很大則提示我們需要深入檢查數(shù)據(jù)質(zhì)量或特征工程。適用性探索通過實(shí)踐我們能切身感受兩種方法對(duì)數(shù)據(jù)預(yù)處理如標(biāo)準(zhǔn)化、參數(shù)調(diào)優(yōu)的不同敏感度為未來類似項(xiàng)目積累經(jīng)驗(yàn)。因此這個(gè)項(xiàng)目的技術(shù)路線圖就很清晰了獲取并探索心臟病數(shù)據(jù) - 進(jìn)行必要的預(yù)處理處理缺失值、標(biāo)準(zhǔn)化 - 分別構(gòu)建核SVM模型和K-NN模型 - 通過交叉驗(yàn)證調(diào)優(yōu)關(guān)鍵參數(shù) - 評(píng)估并比較模型性能 - 分析結(jié)果并得出實(shí)踐啟示。3. 數(shù)據(jù)準(zhǔn)備與探索性分析讀懂你的數(shù)據(jù)在讓任何算法工作之前我們必須先成為數(shù)據(jù)的“知音”。這一步做得好能避免后續(xù)很多坑。我們假設(shè)使用的數(shù)據(jù)集是像UCI機(jī)器學(xué)習(xí)倉庫中的“Heart Disease”數(shù)據(jù)集它包含了約300個(gè)樣本14個(gè)屬性包括目標(biāo)變量。3.1 數(shù)據(jù)加載與初步審視首先在R中加載數(shù)據(jù)并查看其結(jié)構(gòu)。# 假設(shè)數(shù)據(jù)已保存為CSV文件‘heart.csv‘ heart_data - read.csv(heart.csv, stringsAsFactors TRUE) # 查看數(shù)據(jù)結(jié)構(gòu)、維度、前幾行 str(heart_data) dim(heart_data) head(heart_data) # 檢查缺失值 sum(is.na(heart_data))通過str()函數(shù)我們需要確認(rèn)每個(gè)變量的類型。通常分類變量如胸痛類型cp、性別sex應(yīng)被正確識(shí)別為因子數(shù)值變量如年齡age、膽固醇chol應(yīng)為數(shù)值型。如果類型不對(duì)需要用as.factor()或as.numeric()進(jìn)行轉(zhuǎn)換。3.2 關(guān)鍵特征與目標(biāo)變量解析數(shù)據(jù)集通常包含以下典型特征具體名稱可能不同人口統(tǒng)計(jì)學(xué)特征age年齡sex性別。醫(yī)療測(cè)量特征cp胸痛類型trestbps靜息血壓chol血清膽固醇fbs空腹血糖restecg靜息心電圖結(jié)果。運(yùn)動(dòng)相關(guān)特征thalach達(dá)到的最大心率exang運(yùn)動(dòng)誘發(fā)心絞痛oldpeak運(yùn)動(dòng)誘發(fā)的ST段壓低。其他slope運(yùn)動(dòng)高峰ST段斜率ca熒光透視著色的主要血管數(shù)thal地中海貧血癥一種血液狀況。目標(biāo)變量target0 無心臟病 1 有心臟病。實(shí)操心得務(wù)必仔細(xì)閱讀數(shù)據(jù)集的文檔理解每個(gè)特征編碼的真實(shí)含義。例如cp胸痛類型的1、2、3、4分別代表典型心絞痛、非典型心絞痛、非心絞痛性疼痛、無癥狀。錯(cuò)誤的理解會(huì)導(dǎo)致無意義的分析。3.3 數(shù)據(jù)可視化與洞察可視化是發(fā)現(xiàn)模式、異常和關(guān)系的利器。library(ggplot2) library(GGally) # 目標(biāo)變量分布類別是否平衡 ggplot(heart_data, aes(xas.factor(target), fillas.factor(target))) geom_bar() labs(title目標(biāo)變量分布心臟病 vs 健康, x診斷結(jié)果, y計(jì)數(shù)) # 數(shù)值特征與目標(biāo)變量的關(guān)系箱線圖 ggplot(heart_data, aes(xas.factor(target), ythalach, fillas.factor(target))) geom_boxplot() labs(title最大心率與心臟病關(guān)系, x診斷結(jié)果, y最大心率) # 特征間相關(guān)性熱圖僅數(shù)值特征 numeric_data - heart_data[, sapply(heart_data, is.numeric)] cor_matrix - cor(numeric_data, usecomplete.obs) library(corrplot) corrplot(cor_matrix, methodcolor, typeupper, tl.cex0.7)通過可視化我們可能發(fā)現(xiàn)目標(biāo)變量target的分布相對(duì)平衡這對(duì)建模是利好避免需要復(fù)雜的重采樣技術(shù)。患病人群的平均thalach最大心率可能顯著低于健康人群這是一個(gè)強(qiáng)預(yù)測(cè)信號(hào)。特征age和thalach可能呈現(xiàn)負(fù)相關(guān)這在生理上是合理的。3.4 數(shù)據(jù)預(yù)處理為模型鋪平道路這是至關(guān)重要的一步對(duì)K-NN尤其關(guān)鍵。處理分類變量對(duì)于K-NN和SVM通常需要將分類變量因子轉(zhuǎn)換為數(shù)值形式。我們可以使用獨(dú)熱編碼。library(caret) # 創(chuàng)建虛擬變量獨(dú)熱編碼并移除原始的因子列 dummies - dummyVars(~ . - target, data heart_data) heart_data_encoded - predict(dummies, newdata heart_data) heart_data_encoded - as.data.frame(heart_data_encoded) # 將目標(biāo)變量添加回來 heart_data_encoded$target - heart_data$target特征標(biāo)準(zhǔn)化K-NN基于距離因此所有特征必須在同一尺度上否則數(shù)值大的特征如膽固醇會(huì)主導(dǎo)距離計(jì)算。SVM使用核函數(shù)其計(jì)算也受特征尺度影響特別是使用RBF核時(shí)。我們使用Z-score標(biāo)準(zhǔn)化。preProc - preProcess(heart_data_encoded[, -ncol(heart_data_encoded)], method c(center, scale)) heart_data_scaled - predict(preProc, heart_data_encoded) # 確認(rèn)標(biāo)準(zhǔn)化后數(shù)據(jù)均值為0標(biāo)準(zhǔn)差為1近似 sapply(heart_data_scaled[, -ncol(heart_data_scaled)], function(x) c(meanmean(x), sdsd(x)))數(shù)據(jù)分割將數(shù)據(jù)劃分為訓(xùn)練集和測(cè)試集通常按7:3或8:2的比例。set.seed(123) # 確保結(jié)果可重現(xiàn) trainIndex - createDataPartition(heart_data_scaled$target, p0.7, listFALSE) train_data - heart_data_scaled[trainIndex, ] test_data - heart_data_scaled[-trainIndex, ]至此一份干凈、標(biāo)準(zhǔn)化的數(shù)據(jù)就準(zhǔn)備好了可以分別喂給我們的核SVM和K-NN模型。4. 核方法SVM模型構(gòu)建與調(diào)優(yōu)實(shí)戰(zhàn)支持向量機(jī)配合核函數(shù)是處理非線性分類的利器。在R中我們使用e1071包它提供了用戶友好的接口。4.1 模型訓(xùn)練與核函數(shù)選擇首先安裝并加載包然后進(jìn)行初步訓(xùn)練。我們直接使用最強(qiáng)大的高斯RBF核。library(e1071) # 使用訓(xùn)練集訓(xùn)練一個(gè)RBF核SVM模型 # 注意svm()函數(shù)會(huì)自動(dòng)檢測(cè)目標(biāo)變量是否為因子并執(zhí)行分類任務(wù) svm_model_rbf - svm(target ~ ., data train_data, type C-classification, # 用于分類 kernel radial, # 徑向基核RBF scale FALSE) # 我們已經(jīng)標(biāo)準(zhǔn)化過了所以這里設(shè)為FALSE # 查看模型摘要 summary(svm_model_rbf)summary會(huì)輸出模型的基本信息包括SVM類型、核函數(shù)、支持向量的數(shù)量等。支持向量數(shù)量少通常意味著模型比較簡(jiǎn)潔。4.2 核心參數(shù)調(diào)優(yōu)成本C與伽馬γRBF核SVM有兩個(gè)關(guān)鍵超參數(shù)它們對(duì)模型性能有巨大影響成本參數(shù)C懲罰系數(shù)。C越大對(duì)誤分類的懲罰越重模型越傾向于在訓(xùn)練集上做到完美分類可能導(dǎo)致過擬合。C越小則允許更多的誤分類決策邊界更平滑可能導(dǎo)致欠擬合。伽馬參數(shù)gamma定義了單個(gè)訓(xùn)練樣本的影響范圍。gamma越大每個(gè)樣本的影響范圍越小決策邊界越曲折復(fù)雜容易過擬合。gamma越小影響范圍越大邊界越平滑容易欠擬合。我們需要通過交叉驗(yàn)證來尋找最優(yōu)的(C, gamma)組合。# 設(shè)置參數(shù)網(wǎng)格進(jìn)行網(wǎng)格搜索 tune_grid - expand.grid(C c(0.1, 1, 10, 100), gamma c(0.01, 0.1, 1, 10)) # 使用tune.svm進(jìn)行交叉驗(yàn)證調(diào)優(yōu) set.seed(123) svm_tune - tune.svm(target ~ ., data train_data, kernel radial, ranges list(C c(0.1, 1, 10, 100), gamma c(0.01, 0.1, 1, 10)), tunecontrol tune.control(cross 5)) # 5折交叉驗(yàn)證 # 查看最優(yōu)參數(shù)和性能 print(svm_tune) summary(svm_tune$best.model)tune.svm會(huì)遍歷所有參數(shù)組合并用交叉驗(yàn)證評(píng)估每一組的平均準(zhǔn)確率。輸出結(jié)果會(huì)告訴我們哪個(gè)組合在訓(xùn)練集上表現(xiàn)最好。4.3 使用最優(yōu)參數(shù)重建模型并進(jìn)行預(yù)測(cè)獲得最優(yōu)參數(shù)后我們用整個(gè)訓(xùn)練集重新訓(xùn)練最終模型并在測(cè)試集上評(píng)估。# 獲取最優(yōu)參數(shù) best_C - svm_tune$best.parameters$C best_gamma - svm_tune$best.parameters$gamma # 用最優(yōu)參數(shù)訓(xùn)練最終模型 final_svm_model - svm(target ~ ., data train_data, kernel radial, cost best_C, gamma best_gamma, probability TRUE) # 允許輸出概率 # 在測(cè)試集上進(jìn)行預(yù)測(cè) svm_predictions - predict(final_svm_model, newdata test_data[, -ncol(test_data)]) svm_probabilities - predict(final_svm_model, newdata test_data[, -ncol(test_data)], probability TRUE) svm_prob - attr(svm_probabilities, probabilities)[, 1] # 獲取患病概率 # 創(chuàng)建混淆矩陣 svm_cm - confusionMatrix(svm_predictions, as.factor(test_data$target)) print(svm_cm)混淆矩陣會(huì)給出準(zhǔn)確率、精確率、召回率、F1值等一系列指標(biāo)這是我們?cè)u(píng)估模型性能的基礎(chǔ)。5. K-NN模型構(gòu)建與調(diào)優(yōu)實(shí)戰(zhàn)K-NN的實(shí)現(xiàn)相對(duì)直接但調(diào)優(yōu)過程同樣重要。我們使用class包中的knn函數(shù)但為了更方便的調(diào)優(yōu)和評(píng)估配合caret包使用是更佳實(shí)踐。5.1 模型訓(xùn)練與關(guān)鍵參數(shù)K的選擇K-NN的核心超參數(shù)就是K即鄰居的數(shù)量。library(class) library(caret) # 準(zhǔn)備數(shù)據(jù)分離特征和目標(biāo)變量 train_features - train_data[, -ncol(train_data)] train_labels - as.factor(train_data$target) test_features - test_data[, -ncol(test_data)] test_labels - as.factor(test_data$target) # 嘗試一個(gè)初始的K值比如K5 set.seed(123) knn_predictions_k5 - knn(train train_features, test test_features, cl train_labels, k 5, prob TRUE) # 評(píng)估 knn_cm_k5 - confusionMatrix(knn_predictions_k5, test_labels) print(knn_cm_k5)5.2 通過交叉驗(yàn)證確定最優(yōu)K值我們需要系統(tǒng)性地尋找最優(yōu)的K。caret包讓這個(gè)過程變得簡(jiǎn)單。# 使用caret包進(jìn)行交叉驗(yàn)證和網(wǎng)格搜索 set.seed(123) ctrl - trainControl(methodcv, number10, savePredictionsTRUE) # 10折交叉驗(yàn)證 # 定義參數(shù)網(wǎng)格搜索K從1到20的奇數(shù)避免平票 knn_grid - expand.grid(k seq(1, 20, by2)) # 訓(xùn)練模型 knn_fit - train(target ~ ., data train_data, method knn, trControl ctrl, tuneGrid knn_grid, preProcess c(center, scale), # 如果之前沒做這里可以做 metric Accuracy) # 以準(zhǔn)確率為優(yōu)化指標(biāo) # 查看調(diào)優(yōu)結(jié)果 print(knn_fit) plot(knn_fit) # 繪制不同K值對(duì)應(yīng)的準(zhǔn)確率曲線caret的train函數(shù)會(huì)自動(dòng)進(jìn)行交叉驗(yàn)證并給出在驗(yàn)證集上平均準(zhǔn)確率最高的K值。通常準(zhǔn)確率曲線會(huì)呈現(xiàn)先升后降的趨勢(shì)過低K值如1容易過擬合噪聲過高K值會(huì)使模型過于平滑而欠擬合。5.3 最終模型評(píng)估與概率輸出獲得最優(yōu)K后我們可以用class::knn或直接用caret的最終模型進(jìn)行預(yù)測(cè)。# 獲取最優(yōu)K best_k - knn_fit$bestTune$k cat(最優(yōu)K值為, best_k, \n) # 使用最優(yōu)K進(jìn)行最終預(yù)測(cè) final_knn_predictions - knn(train train_features, test test_features, cl train_labels, k best_k, prob TRUE) # 獲取預(yù)測(cè)概率注意knn的prob是獲勝類別的比例不是嚴(yán)格的后驗(yàn)概率 knn_prob - attr(final_knn_predictions, prob) # 對(duì)于二分類需要將屬于“1”類的概率計(jì)算出來 # 當(dāng)預(yù)測(cè)為1時(shí)prob就是屬于1的比例當(dāng)預(yù)測(cè)為0時(shí)prob是屬于0的比例屬于1的比例就是1-prob knn_prob_adj - ifelse(final_knn_predictions 1, knn_prob, 1 - knn_prob) # 評(píng)估最終模型 final_knn_cm - confusionMatrix(final_knn_predictions, test_labels) print(final_knn_cm)6. 模型評(píng)估、比較與結(jié)果深度解讀現(xiàn)在我們手上有兩個(gè)訓(xùn)練好的模型一個(gè)調(diào)優(yōu)后的RBF核SVM一個(gè)調(diào)優(yōu)后的K-NN。是時(shí)候?qū)λ鼈冞M(jìn)行全面的評(píng)估和比較了。6.1 性能指標(biāo)對(duì)比我們不應(yīng)只盯著準(zhǔn)確率。在醫(yī)療診斷中召回率查全率即實(shí)際患病者中被正確預(yù)測(cè)的比例往往比精確率查準(zhǔn)率即預(yù)測(cè)患病者中真正患病的比例更重要。因?yàn)槁┰\將病人誤判為健康的代價(jià)通常遠(yuǎn)高于誤診將健康人誤判為病人。F1分?jǐn)?shù)是精確率和召回率的調(diào)和平均是一個(gè)綜合指標(biāo)。讓我們計(jì)算并對(duì)比這些指標(biāo)# 提取SVM和K-NN的評(píng)估指標(biāo) svm_metrics - svm_cm$byClass[c(Precision, Recall, F1)] knn_metrics - final_knn_cm$byClass[c(Precision, Recall, F1)] comparison_df - data.frame( Model c(SVM (RBF Kernel), K-NN), Accuracy c(svm_cm$overall[Accuracy], final_knn_cm$overall[Accuracy]), Precision c(svm_metrics[Precision], knn_metrics[Precision]), Recall c(svm_metrics[Recall], knn_metrics[Recall]), F1 c(svm_metrics[F1], knn_metrics[F1]) ) print(comparison_df)6.2 ROC曲線與AUC面積對(duì)于輸出概率的模型接收者操作特征曲線ROC和曲線下面積AUC是評(píng)估分類器整體性能的黃金標(biāo)準(zhǔn)。AUC越接近1說明模型區(qū)分能力越強(qiáng)。library(pROC) # 計(jì)算ROC曲線 roc_svm - roc(response test_data$target, predictor svm_prob) roc_knn - roc(response test_data$target, predictor knn_prob_adj) # 繪制ROC曲線 plot(roc_svm, colblue, mainROC曲線對(duì)比SVM vs K-NN, lwd2) lines(roc_knn, colred, lwd2) legend(bottomright, legendc(paste(SVM (AUC , round(auc(roc_svm), 3), )), paste(K-NN (AUC , round(auc(roc_knn), 3), ))), colc(blue, red), lwd2) # 輸出AUC值 cat(SVM模型AUC, auc(roc_svm), \n) cat(K-NN模型AUC, auc(roc_knn), \n)6.3 結(jié)果解讀與模型選擇根據(jù)上述對(duì)比你可能會(huì)發(fā)現(xiàn)SVM在調(diào)優(yōu)后通常能獲得較高且穩(wěn)定的準(zhǔn)確率和AUC。它的決策邊界是基于全局“支持向量”構(gòu)建的對(duì)于清晰間隔的數(shù)據(jù)表現(xiàn)極佳。如果AUC顯著高于K-NN說明數(shù)據(jù)中的全局非線性模式被RBF核很好地捕捉了。K-NN其性能非常依賴于數(shù)據(jù)局部結(jié)構(gòu)的純凈度和特征的相關(guān)性。如果最優(yōu)K值較小如3或5且性能與SVM接近說明數(shù)據(jù)中存在很強(qiáng)的局部相似性規(guī)律。實(shí)操心得在我的多次實(shí)踐中對(duì)于特征經(jīng)過精心篩選和標(biāo)準(zhǔn)化、樣本量不是特別大的醫(yī)學(xué)數(shù)據(jù)集RBF核SVM往往略勝一籌因?yàn)樗ㄟ^最大化間隔提供了更好的泛化能力。而K-NN對(duì)噪聲和無關(guān)特征更敏感。但這不是絕對(duì)的最終選擇應(yīng)基于測(cè)試集上的客觀指標(biāo)尤其是業(yè)務(wù)最關(guān)心的指標(biāo)如召回率。如果兩個(gè)模型性能相差在1-2%以內(nèi)考慮到K-NN模型更簡(jiǎn)單、無需訓(xùn)練時(shí)間但預(yù)測(cè)慢在某些實(shí)時(shí)性要求不高的場(chǎng)景下也是可選的。7. 常見問題、避坑指南與進(jìn)階思考在實(shí)際操作中從數(shù)據(jù)到模型每一步都可能遇到坑。這里記錄下我踩過的一些雷和總結(jié)的技巧。7.1 數(shù)據(jù)預(yù)處理相關(guān)問題1類別不平衡怎么辦如果數(shù)據(jù)中健康人遠(yuǎn)多于病人或反之模型會(huì)傾向于預(yù)測(cè)多數(shù)類。對(duì)于SVM可以通過class.weights參數(shù)為少數(shù)類設(shè)置更高的權(quán)重。對(duì)于K-NN可以考慮使用加權(quán)投票距離的倒數(shù)作為權(quán)重或者在使用caret訓(xùn)練時(shí)選擇sampling up或down進(jìn)行上/下采樣。問題2缺失值如何處理本項(xiàng)目假設(shè)數(shù)據(jù)完整但現(xiàn)實(shí)中缺失很常見。對(duì)于數(shù)值變量可以用中位數(shù)或均值填充對(duì)于分類變量用眾數(shù)或單獨(dú)作為一個(gè)“未知”類別。更復(fù)雜的方法如K-近鄰插補(bǔ)DMwR2::knnImputation或多重插補(bǔ)mice包也可考慮但需謹(jǐn)慎評(píng)估其對(duì)最終模型的影響。問題3特征標(biāo)準(zhǔn)化一定要做嗎對(duì)于K-NN是必須的否則模型毫無意義。對(duì)于使用RBF核或多項(xiàng)式核的SVM強(qiáng)烈推薦做因?yàn)榫嚯x或內(nèi)積計(jì)算受尺度影響。對(duì)于使用線性核的SVM標(biāo)準(zhǔn)化不是必須但通常有益。一個(gè)簡(jiǎn)單的原則只要算法涉及距離、內(nèi)積或梯度標(biāo)準(zhǔn)化總是一個(gè)好習(xí)慣。7.2 模型訓(xùn)練與調(diào)優(yōu)相關(guān)問題4SVM調(diào)優(yōu)時(shí)參數(shù)網(wǎng)格怎么設(shè)C和gamma的搜索范圍沒有固定答案。一個(gè)常用的啟發(fā)式方法是使用對(duì)數(shù)尺度如C 10^seq(-3, 3, by1)gamma 10^seq(-5, 2, by1)。先從大范圍粗搜找到表現(xiàn)較好的區(qū)域后再精細(xì)搜索。caret包中的svmRadial方法可以自動(dòng)進(jìn)行更高效的搜索。問題5K-NN的K值選多大總是選擇奇數(shù)以避免平票。起始搜索范圍可以從1到訓(xùn)練樣本數(shù)的平方根。通過交叉驗(yàn)證曲線選擇通常準(zhǔn)確率曲線會(huì)有一個(gè)峰值。要小心K1它極度過擬合也要小心K值過大模型會(huì)過于平滑。同時(shí)觀察隨著K增大驗(yàn)證集性能是否快速下降這可能是欠擬合的信號(hào)。問題6計(jì)算距離用什么度量歐氏距離是默認(rèn)且最常用的。但如果某些特征明顯相關(guān)馬氏距離可能更合適因?yàn)樗紤]了特征間的協(xié)方差結(jié)構(gòu)。不過在實(shí)踐中如果數(shù)據(jù)已標(biāo)準(zhǔn)化且特征經(jīng)過篩選歐氏距離通常足夠好。7.3 性能評(píng)估與部署相關(guān)問題7如何避免“數(shù)據(jù)窺探”偏差務(wù)必在開始任何模型調(diào)整包括特征選擇、參數(shù)調(diào)優(yōu)之前就劃分出獨(dú)立的測(cè)試集。調(diào)優(yōu)過程應(yīng)僅使用訓(xùn)練集和驗(yàn)證集通過交叉驗(yàn)證實(shí)現(xiàn)。測(cè)試集只在最后評(píng)估一次以得到對(duì)泛化性能的無偏估計(jì)。問題8模型不穩(wěn)定怎么辦如果每次運(yùn)行劃分?jǐn)?shù)據(jù)后模型性能波動(dòng)很大說明模型可能對(duì)數(shù)據(jù)劃分敏感或者數(shù)據(jù)集本身太小??梢試L試使用分層抽樣caret::createDataPartition已默認(rèn)考慮確保每次劃分的類別比例一致。增加交叉驗(yàn)證的折數(shù)如10折或留一法LOOCV后者對(duì)小數(shù)據(jù)集適用但計(jì)算量大。使用自助法或多次重復(fù)交叉驗(yàn)證來獲得性能的分布而不僅僅是一個(gè)點(diǎn)估計(jì)。問題9如何解釋模型SVM可以通過查看支持向量來了解哪些樣本對(duì)決策邊界至關(guān)重要。對(duì)于線性核可以查看權(quán)重向量。但對(duì)于RBF核模型是“黑箱”的解釋性較差。K-NN解釋性直觀——“因?yàn)槟愕奶卣骱瓦@K個(gè)病人最像所以他們中多數(shù)患病因此預(yù)測(cè)你患病”??梢酝ㄟ^分析鄰居的構(gòu)成來提供解釋。7.4 進(jìn)階思考與擴(kuò)展特征工程本項(xiàng)目中我們直接使用了原始特征。但特征工程能極大提升模型性能??梢試L試領(lǐng)域知識(shí)驅(qū)動(dòng)根據(jù)醫(yī)學(xué)知識(shí)創(chuàng)建新特征如“血壓年齡比”。交互項(xiàng)與多項(xiàng)式特征雖然核方法隱式處理了高維交互但顯式地添加重要特征的交互項(xiàng)有時(shí)仍有幫助。特征選擇使用遞歸特征消除或基于模型的重要性排序如SVM的權(quán)重或基于置換的重要性移除不相關(guān)或冗余特征這尤其能提升K-NN的性能和速度。集成學(xué)習(xí)如果單一模型性能達(dá)到瓶頸可以考慮集成。例如將SVM和K-NN的預(yù)測(cè)概率進(jìn)行平均軟投票或者訓(xùn)練多個(gè)不同核/參數(shù)的SVM進(jìn)行集成可能會(huì)獲得更穩(wěn)健的預(yù)測(cè)??紤]其他非線性模型梯度提升機(jī)如xgboost、隨機(jī)森林等樹模型同樣能處理非線性關(guān)系且通常具有更好的可解釋性特征重要性??梢詫⑺鼈冏鳛榛鶞?zhǔn)模型加入比較。這個(gè)項(xiàng)目不僅僅是一次簡(jiǎn)單的建模練習(xí)而是一個(gè)完整的、從數(shù)據(jù)理解到模型部署決策的分析流程。核方法SVM和K-NN為我們提供了兩把強(qiáng)大的、原理迥異的“手術(shù)刀”來解剖心臟病數(shù)據(jù)中復(fù)雜的非線性關(guān)系。實(shí)際選擇哪把刀或者是否需要組合使用取決于具體的數(shù)據(jù)表現(xiàn)和業(yè)務(wù)需求。記住沒有最好的模型只有最合適的模型。持續(xù)迭代、嚴(yán)謹(jǐn)評(píng)估才是數(shù)據(jù)科學(xué)實(shí)踐的核心。