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