kNN算法根据不同病理特征来预测乳腺癌转移与否
kNN算法也称k最近邻算法,因其没有复杂的数据推导公式、且核心思想易于理解,作为机器学习“敲门砖”再合适不过。
因为个人时间关系,我们生信技能树的统计学专题一直无法收官,感兴趣的还是可以查看往期精彩:
这个间隙推荐一下生信补给站的笔记!
一 、kNN概念
本文介绍机器学习中的分类算法kNN(k-NearestNeighbor),即k邻近算法。核心思想类似“近朱者赤近墨者黑”,每个样本都可以用它最接近的k个邻居来代表。
给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最近邻的K个实例,这K个实例的多数属于某个类,就把该输入实例分为这个类。

例如如果要判断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语言》
最后的最后
我们送书吧,图灵出版社每个月赞助我们生信技能树10本书,好像有几个月没有使用了,一忙就忘记了,粉丝最近支持力度(活跃度)也很小,活动搞起来没有啥意思。但,没意思也总比浪费好,还是搞一搞吧。
就是下面这本书咯!(老规矩,真粉丝可以拿到,根据留言精选)
规则参考:中秋节讲义赠送发货通知

Mastering Machine Learning with R, Second Edition
《精通机器学习:基于R(第2版)》
利用 R 包轻松应用机器学习方法
展示各类机器学习方法的优势与潜在问题
技术与理论并重,通过丰富的商业案例实现机器学习高级概念
在 AWS 等云平台上利用 R 亲手实践机器学习
