kNN演算法根據不同病理特徵來預測乳腺癌轉移與否
- 2019 年 12 月 5 日
- 筆記
kNN演算法也稱k最近鄰演算法,因其沒有複雜的數據推導公式、且核心思想易於理解,作為機器學習「敲門磚」再合適不過。 因為個人時間關係,我們生信技能樹的統計學專題一直無法收官,感興趣的還是可以查看往期精彩:
這個間隙推薦一下生信補給站的筆記!
一 、kNN概念
本文介紹機器學習中的分類演算法kNN(k-NearestNeighbor),即k鄰近演算法。核心思想類似「近朱者赤近墨者黑」,每個樣本都可以用它最接近的k個鄰居來代表。
給定一個訓練數據集,對新的輸入實例,在訓練數據集中找到與該實例最近鄰的K個實例,這K個實例的多數屬於某個類,就把該輸入實例分為這個類。

img
例如如果要判斷Xu 的顏色,只需要找到與其距離最近的5個點,發現有4個紅色,1個綠色,因此可以認為Xu是屬於紅色的集合。
根據上述kNN的演算法思想,可以梳理kNN演算法主要流程如下:
- 計算測試對象到訓練集中每個對象的距離,並按照距離的遠近排序;
- 選取與當前測試對象最近的k個訓練對象(K值自己定義);
- 測試對象即為k個鄰居里頻率最高的類別。
二 ,kNN預測乳腺癌
下面以一個乳腺癌預測的實例完成kNN演算法的深入了解:
1 載入數據
使用威斯康星州臨床科學中心的關於乳腺癌腫瘤的數據集。https://www.kaggle.com/uciml/breast-cancer-wisconsin-data 簡單註冊即可下載 breast-cancer-wisconsin-data.zip文件,解壓後就可以進行下面我們演示的流程啦。
#載入數據 cancer <- read.csv('breast_cancer.csv',stringsAsFactors = F) #查看數據情況 dim(cancer) head(cancer,2) str(cancer)
首先是對數據的一些認知,基礎函數,其實大家看官網對這個乳腺癌腫瘤的數據集更炫酷。

乳腺癌數據包括569例細胞活檢案例,每個案例有32個特徵。
其中通過str(cancer)發現id是int,diagnosis診斷類型為character(編碼「M」表示惡性,用編碼「B」表示良性),其餘30個特徵均為numeric的實驗室測量結果(10個不同特徵的均值、標準差和最差值(即最大值))。
這些特徵包括:Radius(半徑)、Texture(質地)、、Perimeter(周長)、 Area(面積)、Smoothness(光滑度)、 Compactness(緻密性)、 Concavity(凹度)、 Concave points(凹點)、 Symmetry(對稱性)、Fractal dimension(分形維數)。可猜到似乎都與細胞核的形狀和大小有關,但是很難知道每個特徵如何診斷良性或者惡性的。
沒關係,可以使用kNN(機器學習)演算法進行「診斷」,並判斷準確性如何?
2 數據探索和準備
2.1 數據探索
機器學習分類器要求將目標屬性編碼為因子類型,重新編碼diagnosis變數,使用labels參數對B值和M值給出更多資訊
#標識id列去掉 cancer_new <- cancer[,c(-1)] ##查看數據缺失情況,重要 sum(is.na(cancer_new)) [1] 0 #把diagnosis特徵加上標籤,和M的簡寫改成全稱方便識別(推薦) cancer_new$diagnosis <- factor(cancer_new$diagnosis, levels = c("B","M"), labels = c("benign","malignant")) #查看乳腺癌良性和惡性的分布概率 round(prop.table(table(cancer_new$diagnosis))*100,2) benign malignant 62.74 37.26
2.2 數值歸一化
當數據的量綱不同,就不能反映樣本中每一個特徵的重要程度。處理方式就需要數據歸一化,把所有的數據都映射到同一個尺度(量綱)上。
#取幾個特徵觀察數值差異 summary(cancer_new[,2:5]) radius_mean texture_mean perimeter_mean area_mean Min. : 6.981 Min. : 9.71 Min. : 43.79 Min. : 143.5 1st Qu.:11.700 1st Qu.:16.17 1st Qu.: 75.17 1st Qu.: 420.3 Median :13.370 Median :18.84 Median : 86.24 Median : 551.1 Mean :14.127 Mean :19.29 Mean : 91.97 Mean : 654.9 3rd Qu.:15.780 3rd Qu.:21.80 3rd Qu.:104.10 3rd Qu.: 782.7 Max. :28.110 Max. :39.28 Max. :188.50 Max. :2501.0
30個特徵都是numeric值,通過以上取幾個特徵值發現差異較大,需要通過標準化normalization減少數值之間的差異。
#min-max標準化 normalization_01 <- function(x){ return((x-min(x))/(max(x)-min(x))) } #所有numeric類型的特徵min-max標準化 cancer_new[2:31] <-as.data.frame(lapply(cancer_new[2:31], normalization_01)) summary(cancer_new[,2:5]) radius_mean texture_mean perimeter_mean area_mean Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000 1st Qu.:0.2233 1st Qu.:0.2185 1st Qu.:0.2168 1st Qu.:0.1174 Median :0.3024 Median :0.3088 Median :0.2933 Median :0.1729 Mean :0.3382 Mean :0.3240 Mean :0.3329 Mean :0.2169 3rd Qu.:0.4164 3rd Qu.:0.4089 3rd Qu.:0.4168 3rd Qu.:0.2711 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000
到此為止,數據清洗階段才算是完成,這個過程需要有對數據的整體認知!
2.3 拆分測試集和訓練集
數據清洗完成之後,通過劃分測試集和訓練集來建模預測
#隨機取樣分為訓練集和測試集,按8:2比例拆分 set.seed(123) ## 設置隨機數種子,方便重複性研究 index <- sample(1:nrow(cancer_new),size = nrow(cancer_new)*0.8,replace = F) cancer_train <- cancer_new[index,] cancer_test <- cancer_new[-index,] dim(cancer_train) [1] 455 31 dim(cancer_test) [1] 114 31
查看訓練集和測試集中因變數的比重是否與總體吻合,很容易被忽視
round(prop.table(table(cancer_train$diagnosis))*100,2) benign malignant 62.64 37.36 round(prop.table(table(cancer_test$diagnosis))*100,2) benign malignant 63.16 36.84
抽樣效果還不錯,訓練集和測試集的因變數比例與總體因變數比例大體相當。
除了我們這樣的隨機數抽樣,還有可以使用成熟的R包進行劃分訓練集和測試集。
3 KNN演算法建模預測
3.1 R-class包中knn參數
本文使用的是R-class包裡面的knn()函數:
knn(train, test, cl, k = 1, l = 0, prob = FALSE, use.all = TRUE)
train:指定訓練樣本集 test :指定測試樣本集 cl :指定訓練樣本集中的分類變數 k :指定最鄰近的k個已知分類樣本點,默認為1 l :指定待判樣本點屬於某類的最少已知分類樣本數,默認為0 prob:設為TRUE時,可以得到待判樣本點屬於某類的概率,默認為FALSE use.all:如果有多個第K近的點與待判樣本點的距離相等,默認情況下將這些點都納入判別樣本點.
大部分參數了解一下就好,總之就是使用所有其它參數來預測benign malignant。
3.2 kNN預測乳腺癌
將k值設為訓練集樣本數量的平方根(K=21)
library(class) knn_model_predict <- knn(train=cancer_train[,-1],test=cancer_test[,-1],cl=cancer_train$diagnosis,k=21) #查看準確率 sum(knn_model_predict == cancer_test[,1])/dim(cancer_test)[1] [1] 0.9736842 #交叉表展示 library(gmodels) CrossTable(x=cancer_test[,1],y=knn_model_predict,prop.chisq = F)

上圖左上角的格子表示真陰性,分類器結果和臨床結果一致認為是良性。右下角就是真陽性結果,分類器和臨床一致認為是惡性(重要)。左下是假陰性,預測為良性實際是惡性(糟糕)。右上角是假陽性,預測是惡性實際是良性。
計算得到模型的準確率為(39+72)/ (39+72+3) * 100 = 97.37%,接下來我們測試一下不同k值對模型準確率的影響。
註:除準確率外,還有其他評價分類結果準確性的判斷依據:精準率、召回率、F1 Score、ROC曲線等。
3.3 knn演算法中K值的確定
knn為k近鄰演算法,需要解決的是選擇一個合適的k值,可以結合訓練集和測試集,循環k值,直到挑選出使測試集的準確率最高的k值。
#計算不同k值下的accuracy result_k <- NULL #從1循環到樣本數量均方根 for(k in 1:round(sqrt(dim(cancer_train)[1]))){ knn_model_predict <- knn(train=cancer_train[,-1],test=cancer_test[,-1],cl=cancer_train$diagnosis,k) result <- (table(knn_model_predict,cancer_test[,1])[1,1]+table(knn_model_predict,cancer_test[,1])[2,2])/dim(cancer_test)[1] result_k <- append(result_k,result) print(result) } [1] 0.9561404 [1] 0.9736842 [1] 0.9824561 [1] 0.9824561 [1] 0.9824561 [1] 0.9649123 [1] 0.9912281 [1] 0.9824561 [1] 0.9824561 [1] 0.9912281 [1] 0.9912281 [1] 0.9912281 [1] 0.9824561 [1] 0.9736842 [1] 0.9824561 [1] 0.9824561 [1] 0.9736842 [1] 0.9736842 [1] 0.9736842 [1] 0.9736842 [1] 0.9736842 #繪製結果折線圖 x <- c(1:21) plot(result_k,type = "o",xaxt="n") axis(1,at=seq(1,21),labels = x)

可以看到當k為7、10、11、12時模型準確率最高。
3.4 最優K值模型預測
knn_model_predict <- knn(train=cancer_train[,-1],test=cancer_test[,-1],cl=cancer_train$diagnosis,k=7) CrossTable(x=cancer_test[,1],y=knn_model_predict,prop.chisq = F)

發現假陰性減少了2個,總體上預測的精度提高到了(72+41)/(72+41+1+0) * 100 =99.12% 。
三 kNN演算法注意點
1)缺失值:k近鄰需要計算距離,因此數據中不能含有缺失值;
2)數據標準化:knn()函數在調用前需標準化數據,可嘗試其他標準化方式;
3)最優K值確定:k過小,雜訊對分類的影響就會變得非常大,K過大,很容易誤分類;
參考資料
《機器學習與R語言》