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语言》