K-近邻(KNN)算法

物以类聚,人与群分。 ——《易经》
KNN模型(K-Nearest Neighbor,K-近邻算法)是一个典型的非参数模型,也就是说,计算机通过KNN学到的知识并不是以数值权重的方式存储下来的。
1、模型原理
最简单最初级的分类器是将全部的训练数据所对应的类别都记录下来,当测试对象的属性和某个训练对象的属性完全匹配时,便可以对其进行分类。但是怎么可能所有测试对象都会找到与之完全匹配的训练对象呢,其次就是存在一个测试对象同时与多个训练对象匹配,导致一个训练对象被分到了多个类的问题,基于这些问题呢,就产生了KNN。
KNN是通过测量不同特征值之间的距离进行分类。它的的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。K通常是不大于20的整数。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。
该算法涉及3个主要因素:训练集、距离或相似的衡量、k的大小。
下面通过一个简单的例子说明一下:如下图,绿色圆要被决定赋予哪个类,是红色三角形还是蓝色四方形?显然,当K=3时,将以1:2的投票结果分类于红色;而K=5时,将以3:2的投票结果分类于蓝色。
这里写图片描述

之所以称呼为“近”邻是因为我们用多维空间的欧氏距离衡量样本之间的相似程度,距离越大越不相似。这里的维度指特征维度,即样本有几个特征就属于几维。样本x,y的距离公式:

这里写图片描述
i是样本标号。
2、算法要点
a、计算步骤如下:
1)算距离:给定测试对象,计算它与训练集中的每个对象的距离
2)找邻居:圈定距离最近的k个训练对象,作为测试对象的近邻
3)做分类:根据这k个近邻归属的主要类别,来对测试对象分类
b、类别的判定
投票决定:少数服从多数,近邻中哪个类别的点最多就分为该类。
加权投票法:根据距离的远近,对近邻的投票进行加权,距离越近则权重越大(例如:权重可为距离平方的倒数)
c、时间复杂度
KNN算法简单有效,但没有优化的暴力法效率容易达到瓶颈。如样本个数为N,特征维度为D的时候,该算法时间复杂度呈O(DN)增长。所以通常KNN的实现会把训练数据构建成K-D Tree(K-dimensional tree),构建过程很快,甚至不用计算D维欧氏距离,而搜索速度高达O(D*log(N))。不过当D维度过高,会产生所谓的”维度灾难“,最终效率会降低到与暴力法一样。因此通常D>20以后,最好使用更高效率的Ball-Tree,其时间复杂度为O(D*log(N))。
3、优缺点
人们经过长期的实践发现KNN算法虽然简单,但能处理大规模的数据分类,尤其适用于样本分类边界不规则的情况。最重要的是该算法是很多高级机器学习算法的基础。特别适合于多分类问题(multi-modal,对象具有多个类别标签),例如根据基因特征来判断其功能分类,kNN比SVM的表现要好。
当然,KNN算法也存在一切问题。比如如果训练数据大部分都属于某一类,投票算法就有很大问题了。这时候就需要考虑设计每个投票者票的权重了。可解释性较差,无法给出决策树那样的规则。
4、常见问题
a、k值设定为多大?
k太小,分类结果易受噪声点影响;k太大,近邻中又可能包含太多的其它类别的点。(对距离加权,可以降低k值设定的影响)
k值通常是采用交叉检验来确定(以k=1为基准)
经验规则:k一般低于训练样本数的平方根
b、类别如何判定最合适?
投票法没有考虑近邻的距离的远近,距离更近的近邻也许更应该决定最终的分类,所以加权投票法更恰当一些。
c、如何选择合适的距离衡量?
高维度对距离衡量的影响:众所周知当变量数越多,欧式距离的区分能力就越差。
变量值域对距离的影响:值域越大的变量常常会在距离计算中占据主导作用,因此应先对变量进行标准化。
d、训练样本是否要一视同仁?
在训练集中,有些样本可能是更值得依赖的。可以给不同的样本施加不同的权重,加强依赖样本的权重,降低不可信赖样本的影响。
e、性能问题?
kNN是一种懒惰算法,平时不好好学习,考试(对测试样本分类)时才临阵磨枪(临时去找k个近邻)。懒惰的后果:构造模型很简单,但在对测试样本分类地的系统开销大,因为要扫描全部训练样本并计算距离。已经有一些方法提高计算的效率,例如压缩训练样本量等。
f、能否大幅减少训练样本量,同时又保持分类精度?
浓缩技术(condensing)
编辑技术(editing)
5、python实现
a、测试数据,身高体重数据格式如下:
(保存为:1.txt)

1.5 40 thin  
1.5 50 fat  
1.5 60 fat  
1.6 40 thin  
1.6 50 thin  
1.6 60 fat  
1.6 70 fat  
1.7 50 thin  
1.7 60 thin  
1.7 70 fat  
1.7 80 fat  
1.8 60 thin  
1.8 70 thin  
1.8 80 fat  
1.8 90 fat  
1.9 80 thin  
1.9 90 fat

b、scikit-learn提供了优秀的KNN算法支持。
安装scikit-learn 之前,你可能需要安装python, NumPy, SciPy, Matplotlib等。安装说明见(http://scikit-learn.org/stable/install.html
If you already have a working installation of numpy and scipy, the easiest way to install scikit-learn is using pip

pip install -U scikit-learn

or conda:

conda install scikit-learn

使用Python代码如下:
(保存为knn.py,环境python2.7)

# -*- coding: utf-8 -*-  
import numpy as np  
from sklearn import neighbors  
from sklearn.metrics import precision_recall_curve  
from sklearn.metrics import classification_report  
from sklearn.cross_validation import train_test_split  
import matplotlib.pyplot as plt  

''''' 数据读入 '''  
data   = []  
labels = []  
with open("1.txt") as ifile:  
        for line in ifile:  
            tokens = line.strip().split(' ')  
            data.append([float(tk) for tk in tokens[:-1]])  
            labels.append(tokens[-1])  
x = np.array(data)  
labels = np.array(labels)  
y = np.zeros(labels.shape)  

''''' 标签转换为0/1 '''  
y[labels=='fat']=1  

''''' 拆分训练数据与测试数据 '''  
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2)  

''''' 创建网格以方便绘制 '''  
h = .01  
x_min, x_max = x[:, 0].min() - 0.1, x[:, 0].max() + 0.1  
y_min, y_max = x[:, 1].min() - 1, x[:, 1].max() + 1  
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),  
                     np.arange(y_min, y_max, h))  

''''' 训练KNN分类器 '''  
clf = neighbors.KNeighborsClassifier(algorithm='kd_tree')  
clf.fit(x_train, y_train)  

'''''测试结果的打印'''  
answer = clf.predict(x)  
print(x)  
print(answer)  
print(y)  
print(np.mean( answer == y))  

'''''准确率与召回率'''  
precision, recall, thresholds = precision_recall_curve(y_train, clf.predict(x_train))  
#answer = clf.predict_proba(x)[:,1]  
answer = clf.predict(x)
print(classification_report(y, answer, target_names = ['thin', 'fat']))  

''''' 将整个测试空间的分类结果用不同颜色区分开'''  
answer = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:,1]  
z = answer.reshape(xx.shape)  
plt.contourf(xx, yy, z, cmap=plt.cm.Paired, alpha=0.8)  

''''' 绘制训练样本 '''  
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train, cmap=plt.cm.Paired)  
plt.xlabel(u'high')   #身高
plt.ylabel(u'weight') #体重
plt.show()  

参考:
http://blog.csdn.net/jmydream/article/details/8644004
http://blog.csdn.net/lsldd/article/details/41357931
https://www.cnblogs.com/ybjourney/p/4702562.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值