kNN演算法根據不同病理特徵來預測乳腺癌轉移與否

  • 2019 年 12 月 5 日
  • 筆記

kNN演算法也稱k最近鄰演算法,因其沒有複雜的數據推導公式、且核心思想易於理解,作為機器學習「敲門磚」再合適不過。 因為個人時間關係,我們生信技能樹的統計學專題一直無法收官,感興趣的還是可以查看往期精彩:

這個間隙推薦一下生信補給站的筆記!

一 、kNN概念

本文介紹機器學習中的分類演算法kNN(k-NearestNeighbor),即k鄰近演算法。核心思想類似「近朱者赤近墨者黑」,每個樣本都可以用它最接近的k個鄰居來代表。

給定一個訓練數據集,對新的輸入實例,在訓練數據集中找到與該實例最近鄰的K個實例,這K個實例的多數屬於某個類,就把該輸入實例分為這個類。

img

例如如果要判斷Xu 的顏色,只需要找到與其距離最近的5個點,發現有4個紅色,1個綠色,因此可以認為Xu是屬於紅色的集合。

根據上述kNN的演算法思想,可以梳理kNN演算法主要流程如下:

  1. 計算測試對象到訓練集中每個對象的距離,並按照距離的遠近排序;
  2. 選取與當前測試對象最近的k個訓練對象(K值自己定義);
  3. 測試對象即為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語言》