R语言与机器学习学习笔记(分类算法)(1)K-近邻算法
来源:互联网 发布:阿里云 数据库 用法 编辑:程序博客网 时间:2024/05/18 03:36
K-近邻算法(KNN)
原理及举例
样本集中每一个数据与所属分类有对应关系,输入没有标签的新数据后,将新数据与训练集数据的对应特征进行比较,找出“距离”最近的k个数据,选择这k个数据中出现最多的分类作为新数据的分类。
算法描述
(1) 计算已知类别数据集中的点与当前点的距离;
(2) 选取与当前点距离最小的k个点
(3) 确定前K个点所在类别出现的频率
(4) 返回频率最高的类别作为当前类别的预测
距离度量
距离计算方法有"euclidean"(欧氏距离),”minkowski”(明科夫斯基距离), "maximum"(切比雪夫距离), "manhattan"(绝对值距离),"canberra"(兰式距离), 或 "minkowski"(马氏距离)等.
详情戳:距离和相似性度量
优点
1. 简单,高效。重新训练的代价低;
2. 计算时间和空间与训练集规模线性相关;
3. 靠临近样本分类,适用于类域交叉或重叠较多的待分样本集,可以生成任意形状的决策边界,可以提供更加灵活的模型。决策边界依赖于训练样例的组合,具有很高的可变性,增加K值,可以降低这种可变性。
缺点
1. 依赖样本,样本容量小时,容易误分;
2. 基于局部信息预测,K太小,对噪声敏感;K太大,可能会误分测试样例,因为最近邻列表中可能包含远离其近邻的数据点。
3. 消极学习法。相对积极学习的算法慢很多。推迟对训练数据的建模,给定训练数据时,只是简单的存储训练数据或稍加处理,直到需要分类测试样例时才进行分类;
4. 计算待分类点与所有样本的距离,需要存储全部训练样本。
K的选取
1. 一般情况下K取奇数,避免投票表决时,票数相等难以决策;
2. 交叉验证。先选取比较小的K,不断调整K值使得分类最优,最后得到在该数据集下最合适的K值;
3. K <= sqrt(样本数)
改进
1. 优化查找K近邻的过程。
对于每个待分类样本都要计算与所有训练样本的距离是KNN的一大缺点。第一种改进,将样本集分群分层,尽可能将计算压缩到在接近测试样本邻域的小范围内,避免盲目地与训练样本集中的每个样本进行距离计算(KD树)。第二种是在原有样本集中挑选出对分类计算有效的样本,使样本总数合理地减少,以同时达到既减少计算量,又减少存储量的双重效果(压缩近邻法)。
压缩近邻法
首先定义两个存储器,一个用来存放即将生成的样本集,称为Store;另一存储器则存放原样本集,称为Grabbag。其算法是:
1) 初始化。Store是空集,原样本集存入Grabbag;从Grabbag中任意选择一样本放入Store中作为新样本集的第一个样本。
2) 样本集生成。在Grabbag中取出第i个样本用Store中的当前样本集按最近邻法分类。若分类错误,则将该样本从Grabbag转入Store中,若分类正确,则将该样本放回Grabbag中。
3) 结束过程。若Grabbag中所有样本在执行第二步时没有发生转入Store的现象,或Grabbag已成空集,则算法终止,否则转入第二步。
2. 降低K的影响。
多数表决对K的选择很敏感。降低K的影响的一种途径是根据每个最近邻Xi距离的不同对其作用加权,最简单的就是取两者距离之间的倒数,距离越小,越相似,权重越大,将权重累加,最后选择累加值最高类别属性作为该待测样本点的类别。
这里我们使用最常见欧氏距离作为衡量标准,以鸢尾花数据集为例来说明K-近邻算法:
鸢尾花数据集包含150个数据,测量变量为花瓣,花萼的长度与宽度,分类变量为setosa, versicolor, 和 virginica。
准备数据:
为了了解数据,我们先通过作图分析,相关分析来看看数据分类指标的合理性,这一点十分重要,有助于减少分类指标中的噪声。
从上图可以看出,我们通过这2个变量大致是可以把鸢尾花分类的,也就是说分类的特征变量选择是合理的,(同理可以分析另外2个,分类效果不如这两个,但大致上还是能区分的)当然我们也可以选择计算相关系数来看特征变量的合理性。
我们很容易发现,数值差最大的属性对距离的影响最大,所以在特征值等权重的假定下,我们先得归一化特征值,计算公式为:
Newvalue=(oldvalue-min)/(max-min)
R代码:
autonorm<-function(data){
for(iin 1:length(data))
data[i]<-(data[i]-min(data))/(max(data)-min(data))
return(data)
}
data<-as.matrix(apply(iris[,1:4],2,autonorm))
得到了归一化后的数据集,下面计算距离。我们在这里取三个数据作为验证集来看看分类的效果,首先将验证集归一化:
x<-iris[13,1:4]
y<-iris[79,1:4]
z<-iris[100,1:4]
x<-(x-apply(iris[c(-13,-79,-100),1:4],2,min))/(apply(iris[c(-13,-79,-100),1:4],2,max)-apply(iris[c(-13,-79,-100),1:4],2,min))
y<-(y-apply(iris[c(-13,-79,-100),1:4],2,min))/(apply(iris[c(-13,-79,-100),1:4],2,max)-apply(iris[c(-13,-79,-100),1:4],2,min))
z<-(z-apply(iris[c(-13,-79,-100),1:4],2,min))/(apply(iris[c(-13,-79,-100),1:4],2,max)-apply(iris[c(-13,-79,-100),1:4],2,min))
计算距离,仅以x为例,运行代码:(k取5)
dis<-rep(0,length(data[,1]))
for(iin 1:length(data[,1]))
dis[i]<-sqrt(sum((z-data[i,1:4])^2))
table(data[order(dis)[1:5],5])
x,y,z的输出结果为
标签xyyz
分类1233
频数5415
虽然对测试y出现了错误分类,但根据多数投票法x,y,z为setosa, versicolor, 和virginica,得到了正确分类结果。
值得一提的是,我们用同样的办法计算K=3时的情形,会发现没有出现误分类。这也就引出了一个值得思考的问题:k应该如何选取?k过小,噪声对分类的影响就会变得非常大,K过大,那么包含错误就理所当然,误分类也不足为奇。虽然这里我们对K的取值并未进行讨论,但在实际中,我们应该通过交叉验证的办法来确定k值。
R语言内置函数kknn简介
R语言里的kknn包也可以实现最邻近算法——使用kknn函数。
kknn(formula = formula(train),train, test, na.action = na.omit(),
k= 7, distance = 2, kernel = "optimal", ykernel = NULL, scale=TRUE,
contrasts= c('unordered' = "contr.dummy", ordered ="contr.ordinal"))
参数解释:
formula 一个回归模型,具体为:分类变量~特征变量
train 训练集
test 测试集
na.action 缺失值处理,默认为去掉缺失值
k k值选择,默认为7
distance 这个是明科夫斯基距离,p=2时为欧氏距离
其他参数 略
上面的鸢尾花例子使用kknn包可以实现(k=5):
library(kknn)
data(iris)
m <- dim(iris)[1]
val <- sample(1:m, size =round(m/3), replace = FALSE,
prob= rep(1/m, m))
iris.learn <- iris[-val,]
iris.valid <- iris[val,]
iris.kknn <- kknn(Species~.,iris.learn, iris.valid, distance = 5,
kernel= "triangular")
summary(iris.kknn)
fit <- fitted(iris.kknn)
table(iris.valid$Species, fit)
这里我们的训练集选取更随机化,得到结果是:
fit
setosa versicolor virginica
setosa 12 0 0
versicolor 0 22 0
virginica 0 0 16
分类完全正确。
应用举例:手写数字识别
下面我们来做一个规模大一些的数据处理,利用k-近邻实现一下数字的模式识别。这个例子来自《机器学习实战》,具体数据集已上传至百度云盘(点击这里下载)。数据为了简单起见,仅提供0~9,10个数字的识别。需要识别的数字你可以看做是被图像处理软件处理为了32*32的黑白图像。尽管文本格式储存图片不能够有效地利用存储空间,但是为了方便理解还是提供了这个文本版的图片数据。至于图像版本的数据,你可以找到《手写数字的光学识别》一文(登载于2010年的UCI机器学习资料库中)的数据集合,并下载它。
完整的R实现:
setwd("D:/R/data/digits/trainingDigits")
names<-list.files("D:/R/data/digits/trainingDigits")
data<-paste("train",1:1934,sep="")
for(i in 1:length(names))
assign(data[i],as.matrix(read.fwf(names[i],widths=rep(1,32))))
dis<-function(datatest,datatrain,len){
distance<-rep(0,len)
for(i in 1:len)
distance[i]<-sqrt(sum((get(datatest)-get(datatrain[i]))^2))
return((distance))
}
judge<-function(test,data,names){
index<-rep(0:9,c(189,198,195,199,186,187,195,201,180,204))
di<-rep(0,1934)
di[1:1934]<-dis(test,data,length(names))
return(names(which.max(table(index[order(di)[1:5]]))))
}
setwd("D:/R/data/digits/testDigits")
name<-list.files("D:/R/data/digits/testDigits")
test<-paste("test",1:946,sep="")
for(i in 1:length(name))
assign(test[i],as.matrix(read.fwf(name[i],widths=rep(1,32))))
index1<-rep(0:9,c(87,97,92,85,114,108,87,96,91,89))
error<-0
for(i in 1:946){
if(judge(test[i],data,names)!=index1[i])
error<-error+1
}
运行结果:
>error
[1]19
>19/946
[1]0.02008457
也就是说,使用5-近邻算法,误差率为2%,属于一个可以接受的范围。
这里由于本人没有找到较好的批量导入数据的办法,所以代码有些复杂,也出现了hardcode和magicnumber的毛病,但是泛化也不是那么的复杂,所以也没再做更进一步的改进。希望读者告诉我如何解决R里导入批量数据的方法。
其中有两个函数是我在之前的博客中没有使用过的,现在简单介绍如下:
赋值函数assign:
assign("x", c(10.4, 5.6, 3.1, 6.4, 21.7)) 与x <- c(10.4,5.6, 3.1, 6.4, 21.7)等价
读取赋值函数的函数get:
a<- 1:4
assign("a[1]",2)
a[1]== 2 #FALSE
get("a[1]") == 2 #TRUE
在R中,我没有找到求众数的函数,简单编写了一个names(which.max(table(index[order(di)[1:5]]))),这个函数有两个众数时会输出两个,所以K近邻为了保证多数投票法有用,麻烦仔细选择合理的k值。
这里我在做训练集时并没有选择k值得过程(因为这个算法实在是太慢了,没有那个耐心)
实际使用这个算法,执行效率相当的低下,每个距离的计算包含了1024个维度的浮点运算,总计900多次,还要为测试向量准备2M的存储空间。所以k决策树是你需要进一步了解的。
K决策树的种类也有不少,比如kd树,但是他们的问题就是k的选取总是一个麻烦的过程,kd树找最近邻是十分高效的,但是找k近邻,删除结点重新建树还是比较麻烦的。
Further reading:
JULY大神的《从K近邻算法、距离度量谈到KD树、SIFT+BBF算法》
【参考】
1. http://my.oschina.net/u/1412321/blog/194174
- R语言与机器学习学习笔记(分类算法)(1)K-近邻算法
- R语言与机器学习学习笔记(分类算法)(1)K-近邻算法
- 【转】 R语言与机器学习学习笔记(分类算法)(1)K-近邻算法
- R语言与机器学习学习笔记(分类算法)(1)K-近邻算法
- R语言与机器学习学习笔记(分类算法)
- R语言与机器学习学习笔记(分类算法)
- 机器学习实战笔记-K近邻算法1(分类动作片与爱情片)
- 机器学习算法(分类算法)—k-近邻算法
- 机器学习(二):分类算法之k-近邻算法
- 机器学习笔记(1)K-近邻算法
- 机器学习(二)k-近邻分类算法(kNN)
- R语言与机器学习学习笔记(分类算法)(2)决策树算法
- R语言与机器学习学习笔记(分类算法)(3)朴素贝叶斯算法
- R语言与机器学习学习笔记(分类算法)(2)决策树算法
- 【转】R语言与机器学习学习笔记(分类算法)(2)决策树算法
- 【转】R语言与机器学习学习笔记(分类算法)(3)朴素贝叶斯算法
- R语言与机器学习学习笔记(分类算法)(2)决策树算法
- 机器学习算法笔记之K近邻算法(KNeighborsClassifier)
- VS2013生成DLL却没有lib文件
- js和jstl标签混用,太强大了!
- 吐槽iOS国际化:关于NSLocalizedString的使用
- eclipse中如何在当前工程中查找一个字符串
- String.format参加字符串拼接大比拼
- R语言与机器学习学习笔记(分类算法)(1)K-近邻算法
- jQuery 事件
- HDU 1754 I Hate It!(线段树)
- SCU2016-07 T题 prim算法
- jeasyui插件扩展datagrid-filter的改善
- 改变图片尺寸大小不改变像素
- HDOJ 1702 ACboy needs your help again!
- HDU2087 剪花布条 KMP模板
- 模拟Home键点击效果