knn算法也称k最近邻算法,其乃十大最有影响力的数据挖掘算法之一,该算法是一种有监督的挖掘算法,既可以解决离散因变量的分类问题,也可以做连续因变量的预测问题,而且该算法没有复杂的数据推导公式、更易于常人理解。接下来我们就来看看这个流行算法到底是个什么鬼?
k最近邻算法,从名称上来看还是比较容易理解的,以分类问题为例,该算法实际上就是寻找与未知分类最近的k个已知分类训练集,然后通过投票的方式,将票数最多的那个类定性为未知分类样本。也许你觉得这一长串话太啰嗦,还是不易理解,那我们来看一看下面这张图:
如图所示,图中蓝色点为已知分类的训练集,红色点为未知分类的测试集,如果需要得知未知分类该属于哪种类别,可以在其周围寻找最近的k个蓝色点。不妨,我们在红点周围寻找到的3个黄点即为最近的点,从而根据3个已知分类的黄色点来判断红点属于哪个类(投票方式)。
OK,理解了上面的过程,就是理解了knn算法的思想。既然是计算距离,那距离如何计算呢?这里可能面临着3种情况的距离计算:
1)连续变量间的距离计算?
2)离散变量间的距离计算?
3)含有缺失值的样本间距离计算?
首先我们摆出计算距离的公式,即最常用的欧式距离:
接下来我们就是利用这个公式,针对以上三种不同的情形计算观测点之间的距离。我们曾经说过,只要跟距离相关的算法,一般都需要对原始数据进行标准化处理,目的是避免距离的值受到不同量纲的影响。至于标准化方法,主要有两种,即Z标准化或0-1标准化处理,具体公式如下:
针对以上三种不同情形计算观测点之间的距离,我们利用简单的例子加以说明,目的是帮助读者有一个很好的理解。
如果手中的训练样本如下:
经过0-1标准化的结果为:
则观测1与其余观测之间的距离可计算为:
在这里给出计算方法的结论:
1)对于连续变量之间的距离可以直接按数值差计算;
2)对于离散变量之间的距离可以按照值是否相同计算,如果值相同则距离差值为0,否则差值为1;
3)对于离散变量(2个水平以上)需要将其转换为哑变量,然后再按照2)的方法计算距离。
好了,对于第一和第二种情况的距离计算我们已轻松解决,那如果数据中有缺失值该怎么办呢?这里提供三种解决方案:
1)删除缺失严重的列或删除缺失非常少的观测,然后对无缺失的情况计算距离;
2)使用替补法或多重插补法完成缺失值的填充,然后对清洗后的数据计算距离;
3)将缺失值当作特殊值处理,具体操作如下:
如果原始观测有缺失值,
数据标准化处理的结果,
计算含缺失值的距离:
在这里给出计算方法的结论:
1)对于离散变量,如果两个观测存在一个或两个都是缺失,则差值为1;
2)对于连续变量,如果两个观测都是缺失,则差值为1,如果存在一个缺失,则差值取这两个值(|1-x|和|0-x|)的最大值。
对于含缺失值的样本距离可能会稍微复杂一点,但好在三种情况的距离都可以计算。现在还存在最后一个难题,即knn算法中的k如何确定?到底该选择最近的几个点才合适?这个问题一旦解决,knn便可自如行走。
关于k值的确定,一般我们会遍历k的某些值,然后从中选择误差率最低或正确率最高的k值即可,正如下图所示:
图中的红圈对应的k值即为我们所要寻找的合理值,因为在该k值下,所计算出来的模型准确率最高。
讲完了knn的思想和问题的解决方案,接下来就需要我们做一做实战的内容了,数据来源于网站http://archive.ics.uci.edu/ml/,后面也会给出数据的下载链接。在讲实战之前,需要介绍一下R落地的函数及参数含义:
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近的点与待判样本点的距离相等,默认情况下将这些点都纳入判别样本点,当该参数设为FALSE时,则随机挑选一个样本点作为第K近的判别点。
实战:乳腺癌诊断
#数据读取
cancer <- read.table(file = file.choose(), header = FALSE, sep = ',')
#给原始数据编辑变量名
#数据探索
dim(cancer)
该数据集含有569个样本和32个变量。
str(cancer)
summary(cancer)
从运行结果看(这里没有将结果贴出了,需要读者自己运行试试),数据集中不含有缺失值,除Diagnosis变量外,其余变量均为数值型变量,ID变量为编号,没有任何意义,故在建模过程中将删除该变量。发现各个变量间存在明显的量纲大小,需要进一步对原始数据进行标注化。
#构建标准化函数
standard <- function(x) {
(x-mean(x))/sd(x)
}
#将该函数应用到数据框cancer中的每一列(除Diagnosis变量)
cancer_standard <- sapply(X = cancer[,3:32], FUN = standard)
summary(cancer_standard)
#将数据框cancer中的Diagnosis变量合并到cancer_standard中
cancer_standard <- as.data.frame(cbind(cancer$Diagnosis,as.data.frame(cancer_standard)))
names(cancer_standard)[1] <- 'Diagnosis'
Diagnosis变量为因子变量,表示诊断结果,M表示恶性,B表示良性。我们初步查看一下这两个水平的统计结果。
freq <- table(cancer_standard$Diagnosis)
prop.table(freq)
#构建训练集、测试集和验证集
set.seed(1)
index <- sample(x = 1:2,size = nrow(cancer_standard), replace = TRUE, prob = c(0.7,0.3))
train <- cancer_standard[index == 1,]
test <- cancer_standard[index == 2,]
训练集和测试集已经抽样完毕,接下来我们需要查看训练集和测试集中因变量的比重是否与总体吻合,从而看抽样是否具有代表性
抽出来的效果还不错,训练集和测试集的因变量比例与总体因变量比例大体相当。
#构建模型,以循环的方式选择knn算法中的k值
library(class)
res = NULL
for (i in 1:round(sqrt(nrow(train)))){
model <- knn(train= train[,-1], test = test[,-1], cl = train$Diagnosis, k = i)
Freq <- table(test[,1], model)
accu <- sum(diag(Freq))/sum(Freq)
res <- c(res,accu)
}
plot(1:round(sqrt(nrow(train))),res,type = 'b', pch = 20, col= 'blue',
main = 'k vs. accuracy',
xlab = 'k', ylab = 'accuracy')
我的天呐,当k等于1时,模型的准确达到了最高,那么我们就不妨使用k=1,作为knn算法的参数。
#建模
fit <- knn(train = train[,-1], test = test[,-1], cl = train$Diagnosis, k = 17)
fit
此乃模型在测试集上的预测结果。
#查看模型预测与实际类别的交叉表
Freq <- table(fit, test[,1])
Freq
模型的预测效果确实不错,仅有5个观测预测错误,即将实际为恶性,错误预测为良性的有4条,将实际为为良性错误预测为恶性的有1条。
#预判正确率
sum(diag(Freq))/sum(Freq)
通过模型的准确率计算表明,简单而易用的knn算法能够有97%的把握,将乳腺癌的良性或恶性诊断出来。
到此为止,我们已把knn算法的来龙去脉做了介绍和应用,希望您在读完这篇文章后能有所收获。在接下来的一期中,我们将对文本数据进行处理,并简单的预测出哪些评论是正面的,哪些是负面的。
原文的数据链接:http://pan.baidu.com/s/1mhCh1eK
每天进步一点点2015
学习与分享,取长补短,关注小号!
长按识别二维码 马上关注