k-近邻算法实现
最后更新于:2022-04-01 06:49:13
上一学期主要的学习和研究任务是模式识别、信号理论和图像处理,实际上这些领域都与机器学习有或多或少的交集。因此,仍在继续深入阅读《机器学习》、观看斯坦福大学的机器学习课程。在此过程中因为未来课题组项目的要求,需要接触Python,因此选择了《机器学习实战》这本书,同时参考教材和视频一起学习。事实上该书的理论研究不够深入,只能算是练习Python并验证一些著名的机器学习算法的工具书了。
在介绍k-近邻算法之前,对机器学习算法进行简单的分类和梳理:简单来说,机器学习主要分为两大类,有监督学习(supervised learning)和无监督学习(unsupervised learning)。有监督学习又可分两类:分类(classification.)和回归(regression),分类的任务就是把一个样本划为某个已知类别,每个样本的类别信息在训练时需要给定,比如人脸识别、行为识别、目标检测等都属于分类。回归的任务则是预测一个数值,比如给定房屋市场的数据(面积,位置等样本信息)来预测房价走势。而无监督学习也可以成两类:聚类(clustering)和密度估计(density estimation),聚类则是把一堆数据聚成弱干组,没有类别信息;密度估计则是估计一堆数据的统计参数信息来描述数据,比如深度学习的RBM。
作为开头章节,作者介绍了简单而容易理解的k-近邻(kNN)算法。该算法在模式识别一书中与Parzen窗估计放在一起讲,属于非参数估计的方法。这类方法用于处理任意形式的概率分布而不必事先考虑概率密度的参数形式。
更多关于非参数估计方法的介绍:[http://blog.csdn.net/liyuefeilong/article/details/45274325](http://blog.csdn.net/liyuefeilong/article/details/45274325)
在使用k-近邻算法前,需要了解算法的优缺点和适用性:
优点:精度高,对异常数据不敏感,无数据输入假定
缺点:时间和空间复杂度均较高
适用的数据类型:数值型和标识型
基本K均值算法的基本思路为:首先你需要有一组训练样本,也称作训练样本集。该集合中每个样本的分类类别都是已知的,也就是我们知道每个数据与所属分类的对应关系。此时,可以输入一个待预测的数据,将新数据的每个特征与训练样本集中每个数据的特征进行比较,根据一种比较结果(如欧氏距离),找出`k` 个与待预测数据最相似的训练样本,看看这些训练样本都是属于哪一类的,最后就是秉承“多数占优”的原则:既然待预测数据与很多个某A类的训练样本都很近,和其他类不是很相近,那就把预测数据判定为A类吧。
在以上描述中提出使用欧氏距离作为待预测数据和训练样本集的比较结果,即假设测试样本为`a`,而`xi`表示训练样本集中的第`i`个样本,测试样本和训练样本均有`n`个特征属性,则测试样本和训练样本之间的欧氏距离定义为:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-05_568b3834e8d1d.jpg)
通常该算法的k是不大于20的正整数。当`k=7` 时,根据上述欧氏距离公式计算出78个与待预测数据最近的训练样本,在这7个实例中,某个分类出现的次数最多,则预测数据就被分到该类。
以下是根据《机器学习实战》一书写的代码,直接实现了k-近邻算法,由于对python不十分熟悉,因此对于kNN的算法实现难度主要存在于Numpy的使用上,但毕竟这是学习python的一个好机会,以下为算法的代码实现:
~~~
# -*- coding: utf-8 -*-
"""
Created on Sat Aug 29 14:36:02 2015
Input: data: vector of test sample (1xN)
sample: size m data set of known vectors (NxM)
labels: labels of the sample (1xM vector)
k: number of neighbors
Output: the class label
@author: peng__000
"""
from numpy import *
import operator
from os import listdir
# training samples
sample = array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]])
# the labels of samples
label = ['A', 'A', 'B', 'B']
def classify(data, sample, label, k):
SampleSize = sample.shape[0] # 训练样本集的行数
DataMat = tile(data, (SampleSize, 1)) #将data扩展到和训练样本集sample一样的行数
delta = (DataMat - sample)**2
distance = (delta.sum(axis = 1))**0.5 # 以上三步计算欧氏距离
sortedDist = distance.argsort() # 对欧氏距离向量进行排序
classCount = {}
# 以下操作获取距离最近的k个样本的标签
for i in range(k):
votedLabel = label[sortedDist[i]]
classCount[votedLabel] = classCount.get(votedLabel, 0) + 1
result = sorted(classCount.iteritems(), key = operator.itemgetter(1), reverse = True)
return result[0][0]
print classify([10,0], sample, label, 3) # test
~~~
这段简短的代码除了一些矩阵的操作、简单的排序操作外没有复杂的运算。
在简单完成k-近邻算法的实现后,接下来需要将算法的应用到别的场景,根据书本《机器学习实战》上的教程,主要测试了针对约会网站的分类和手写识别系统。
总的来说,k-近邻算法是模式识别和机器学习领域中最简单且最有效的分类算法,缺点是运算速度差强人意,而且在算法执行过程中须保存全部数据集,如果训练数据集非常巨大,必须使用大量的存储空间,此时算法的性能会大大降低。k-近邻算法的另一个缺陷是无法给出任何数据的基础结构信息,因此也无法知晓平均实例样本和典型实例样本具有什么样的特征。通过实验过程也可以发现,对参数的选取和调整要根据实际情况进行多次实验才能达到比较理想的效果。