偶然之间看到,很多R包中都有knn算法的应用,他们有什么不同之处吗?到底应该选择哪个包来实现knn呢?
为了解决这些疑惑,我对class包、DMwR包和kknn包中的knn实现做了一个初步整理,结果如下(算法的原理、流程、优缺点就不写了,很多现成的资料)。
1.说明
以下涉及到的数据来自R中的鸢尾花数据集iris,根据花的种类平均分为训练集和测试集。
2.R包中的knn实现
R中有许多包都可以实现knn算法,以下以class包、DMwR包和kknn包为例进行说明。
2.1 class包中的knn
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近的点与待判样本点的距离相等,默认情况下将这些点都纳入判别样本点,当该参数设为FALSE时,则随机挑选一个样本点作为第K近的判别点
实现代码:
> # z-score数据标准化
> iris_scale <- scale(iris[-5])
> train <- iris_scale[c(1:25,50:75,100:125),] #训练集
> test <- iris_scale[c(26:49,76:99,126:150),] #测试集
> train_lab <- iris[c(1:25,50:75,100:125),5]
> test_lab <- iris[c(26:49,76:99,126:150),5]
> pre <- knn(train=train,test=test,cl=train_lab,k=round(sqrt(dim(train)[1])),prob = F)
> table(pre,test_lab)
test_lab
pre setosa versicolor virginica
setosa 24 0 0
versicolor 0 24 3
virginica 0 0 22
2.2 DMwR包中的KNN
KNN()函数的语法和参数如下:
kNN(form, train, test, norm = T, norm.stats = NULL, ...)
form:分类模型
train:指定训练样本集
test:指定测试样本集
norm:布尔值,指示是否在KNN预测前将数据标准化,默认为TRUE
norm.stats:默认FALSE,采用scale()进行标准化,也可提供其他标准化方法(不太懂)
实现代码:
> train<-iris[c(1:25,50:75,100:125),] #训练集
> test<-iris[c(26:49,76:99,126:150),] #测试集
> pre2 <- kNN(Species~.,train,test,norm=T,k=round(sqrt(dim(train)[1])))
> table(pre2,test$Species)
pre2 setosa versicolor virginica
setosa 24 0 0
versicolor 0 24 3
virginica 0 0 22
2.3 kknn包中的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近邻数值选择,默认为7;
distance闵可夫斯基距离参数,p=2时为欧氏距离;
其他参数略
实现代码:
> train<-iris[c(1:25,50:75,100:125),] #训练集
> test<-iris[c(26:49,76:99,126:150),] #测试集
> # 调用kknn
> pre3 <- kknn(Species~., train, test, distance = 1, kernel = "triangular")
> # 获取fitted.values
> fit <- fitted(pre3)
> table(fit,test$Species)
fit setosa versicolor virginica
setosa 24 0 0
versicolor 0 22 4
virginica 0 2 21
3.knn算法中K值的确定
参考分类算法之knn
knn为k近邻算法,需要解决的一个问题是选择合适的k值,k值过小或过大都会影响模型的准确性,一般考虑将k值设为3~10,或是将k值设为训练集样本数量的平方根。还有一种选择k值的方法是结合训练集和测试集,循环k值,直到挑选出使测试集的误差率最小的k值。
以class包knn举例说明,寻找误差率最低的K值:
> for(i in 1:round(sqrt(dim(train)[1]))) {
+ pre_result <- knn(train=train,test=test,cl=train_lab,k=i)
+ Freq <- table(pre_result,test_lab)
+ print(1-sum(diag(Freq))/sum(Freq)) #误差率
+ }
[1] 0.08219178
[1] 0.06849315
[1] 0.08219178
[1] 0.06849315
[1] 0.05479452
[1] 0.06849315
[1] 0.05479452
[1] 0.09589041
[1] 0.04109589
根据结果,k=9时误差率最低,和训练集样本数量的平方根一致。但是如果样本数量过大,以上的K值确定方法就比较困难了,也正验证了knn不适合大样本数据的说法。
4.总结
总体来看,我认为三种实现knn的函数区别不大,只在参数上有一些差别,可以根据个人喜好选择实现knn的函数。
需要注意的点:
数据标准化:knn()函数在调用前需标准化数据,其他2个函数默认调用时进行标准化;
缺失值:k近邻以距离为依据,因此数据中不能含有缺失值;
k值大小:k过小,噪声对分类的影响就会变得非常大,K过大,很容易误分类;
距离计算:上面算法默认欧式距离,如果有时间,可以看看不同距离计算方法的效果。
补充的点:
VIM包中也有KNN()函数,是用knn算法来填补缺失值;
DMwR包中的knnImputation也可以用来填补缺失值(均值、均值权重)