写在前面:
kNN算法,或者说K最近邻(kNN,k-NearestNeighbor)分类算法,它是分类技术中最简单的方法之一。所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用它最接近的k个邻居来代表。
我们来看一个简单的例子:
如图1,有两类数据,分别是蓝色方块和红色三角形,现在,我们在图正中间有了一个绿色圆圈,并且需要判断它属于这两类中的哪一类。要怎么做呢?
这个时候我们就用到kNN算法了,假设取k=3,那么离绿色圆圈最近的3个中有2个是红色三角形,1个是蓝色方块。红色三角形所占比例高,为2/3,所以可以把绿色圆圈归为红色三角形类。
但如果取k=5,由于蓝色方块所占比例高,为3/5,所以可以把绿色圆圈归为蓝色方块类。
从这个例子不仅可以看出kNN算法的工作原理,而且也能够看出k取值的重要性。
给出kNN的伪代码(文字流程)如下:
0)假设现有已知类别的训练集一份和未知类别的测试数据一条;
1)计算训练集中的点与测试数据点之间的距离;
2)按照距离递增次序排序;
3)选取与当前点距离最小的k个点;
4)确定前k个点所在类别的出现频率;
5)返回前k个点出现频率最高的类别作为当前点的预测分类。
那么,我们结合实际例子分析下kNN的实现:
情景为: 海伦一直使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的人选,但她没有从中找到喜欢的人。经过一番总结,她发现曾交往过三种类型的人:
①不喜欢的人;
②魅力一般的人;
③极具魅力的人。
她现在总结好的数据中(即训练集)包含三种特征:
①每年获得的飞行常客里程数
②玩视频游戏所耗时间百分比
③每周消费的冰淇淋公升数
她希望根据现有的数据及类型标签来判断一个陌生男人会被她归到哪一类。于是,她使用了kNN……
这里先给出数据集地址https://github.com/ocAreTheBestLanguage/fileRepository
上图为数据的格式样本,前三列是特征,最后一列是标签,总共有1000行样本。
此处考虑到篇幅问题,略去对数据进行的预处理及归一化处理代码,假设我们已经获得了归一化后的shape为(1000,3)的矩阵dataSet及shape为(1000,)标签数组labels,且k为要求得的最近邻居的个数。并且使用欧式距离公式来计算点与点之间的距离,如distance = √( (x1-x2)2+(y1-y2)2+(z1-z2)2 )。
那么代码如下:
#dataSet为训练集
#labels为标签集
#inX为要预测的那条数据
#获取数据集的行数
dataSetRows = dataSet.shape[0]
#用tile()函数把要预测那条数据重复扩充到(1000,1) 然后与数据集相减并平方
temp = (tile(inX, (dataSetRows, 1)) - dataSet)**2
#求得每一行数据的总和并开平方,完成求得欧式距离
distances = temp.sum(axis=1) ** 0.5
#返回从小到大排列的元素下标
sortedIndicies = distances.argsort()
#用与计算某类出现的次数
classCount = {}
#计算类别出现的对应次数
for i in range(k):
label = labels[sortedIndicies[i]]
classCount[label] = classCount.get(label, 0) + 1
#利用类别出现的次数,从大到小排序,其中的参数itemgetter(1)代表按照第二个域进行排序,即按照第二列进行排序。
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
#求得最终的预测类别
finalClass = sortedClassCount[0][0]
写在最后:
最后简单评价一下kNN吧,kNN是机器学习中可以说是最简单的算法啦。
kNN的优点在于简单,容易理解,而且无须估计参数,这也意味着它不需要训练这种操作。另外它适合对稀有事件进行分类。
kNN的缺点在于当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。这就使得运行结果受到数量的影响,而偏离了因为相似所以归为同一类的原则。
谢谢阅读本文!