学习机器学习实战一书,记录一下。书上原代码都是以Python2版本编写,这边我是用Python3版本,所以会与书上代码有少许不同。
k-近邻算法
简单地说,k-近邻算法采用测量不同特征值之间的距离方法来进行分类。
优缺点
优点:精度高、对异常值不敏感、无数据输入假定
缺点:计算复杂度高、空间复杂度高
适用数据范围:数值型和标称型
工作原理
存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般说来,我们只选择样本数据集中前k
个最相似的数据,这就k-近邻
算法中k
的出处,通常k
是不大于20的整数。最后选择k
个最相似数据中出现次数最多的分类(投票规则),作为新数据的分类。
一般流程
- 收集数据:可使用任何方法
- 准备数据:数据格式结构化处理
- 分析数据:任意选择方法
- 训练算法:此步骤不适用于k-近邻算法
- 测试算法:计算错误率
- 使用算法:首先输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理
导入数据
新建Python文件kNN.py
,添加数据导入函数:
当然该函数中的四组数据及其标签均是人为假定的,后期可通过读取txt、csv等文件来获取数据。
实现kNN算法
这边采用的是欧氏距离
公式来计算两个向量点之间的距离,公式为:
$$
d (x_a, x_b) = \sqrt{\sum_{p} \left( x^p_a - x^p_b \right)^2}
$$
算法的伪代码实现:
对未知类别属性的数据集(测试集)中的每个点依次执行以下操作
- 计算已知类别数据集(训练集)中的点与当前点之间的距离;
- 按照距离递增次序排序;
- 选取与当前点距离最小的k个点;
- 确定前k个点所在类别的出现频率;
- 返回前k个点出现频率最高的类别作为当前点的预测分类。
Python代码实现:
注:
dataSet.shape
返回的是一个tuple
,里面有两个元素,简单来说就是数据集数组的行和列数,也就是数据集的样本数量和单个样本的特征数。np.tile函数(import numpy as np)
:输入按照函数参数右侧的数值或元祖来进行复制扩充自己。
因此这边代码中np.tile(inX, (dataSetSize, 1))
,实际上就是将inX
整个数据在纵轴方向上复制扩充了dataSetSize
倍,横轴方向上保持1倍,即不变。
这样使得原数据inX
可以与数据集dataSet
中每个样本进行相减求差值。sqDiffMat.sum(axis=1)
:对sqDiffMat
做求和操作,axis=1
表示对列进行操作,但是是以横轴为方向(其实就是行上所有数求和)。所以这边做的是对测试样本与每个训练样本的差值的求和。若axis=0
则表示对行进行操作,但是是以列为方向(列方向上所有数求和)。sortedDistIndicies = distances.argsort()
:将distances
中的元素从小到大排列,提取其对应的index(索引),然后输出到sortedDistIndicies
。sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
:classCount.items()
返回的是一个列表,其中包含了由键值组成的元祖;operator.itemgetter(1)
表示定义一个函数,获取第1个域;reverse=True
表示逆序。所以整个sorted()
函数做的是对classCount.items()
这元祖列表做排序(逆序)操作,这排序的规则是按key
来进行的,也就是根据列表中元祖的第1个域(即第2个值)来排序(其实就是根据各个标签的统计数量来降序排序)。
测试分类器
错误率
是常用的评估方法,主要用于评估分类器在某个数据集上的执行效果。分类器的错误率:分类器给出的错误结果的次数除以测试执行的总数。完美分类器的错误率为0,最差分类器的错误率为1.0。
简单地调用运行:
示例:使用k-近邻算法改进约会网站的配对效果
准备数据
数据文件:datingTestSet2.txt
,每个样本(文件中每一行)中主要包含每一个对象的3种特征:每年获得的飞行常客里程数、玩视频游戏所耗时间百分比、每周消费的冰激凌公升数,最后一列是标签值:1、2、3,表示喜欢的程度。
在kNN.py
文件中添加file2matrix
函数,输入为文件名字符串,输出为训练样本矩阵和类标签向量。
代码如下:
调用函数,并打印数据:
数据打印显示为:
注:
np.zeros((a, b))
创建a行b列的数组,其值全为0line.split('\t')
对字符串按\t
做划分操作,得到由多个子字符串组成的列表
归一化处理
因为特征属性飞行常客里程数
的数值较大,直接使用欧氏距离的话该属性值会严重影响计算结果。
因此,在处理这种不同取值范围的特征值时,通常采用的方法是将数值归一化
,如将取值范围处理为0~1或者-1~1之间。下面公式将任意取值范围的特征值转化为0~1区间内的值:
其中min
和max
分别是数据集中的最小特征值和最大特征值。
在kNN.py
中添加新函数autoNorm()
,该函数自动将数字特征值转化为0~1的区间:
测试算法
在kNN.py
中添加测试代码,作为完整程序来验证分类器:
执行结果显示:
构建完整系统
|
|
只要输入对应特征的值,就可以获得系统的结果判定,运行结果显示:
示例:手写识别系统
准备数据
数字图像均已存储为txt形式,里面是由32行32列的0和1数字组成
首先把32x32
的二进制图像转换为1x1024
的向量,这样就可以用之前的分类器来处理这些数字图像信息了。
测试算法
上边我们已将数据处理为分类器可以识别的格式,现在我们将这些数据输入到分类器中,检测分类器的执行效果。
kNN.py
中需添加from os import listdir
,用于列出给定目录的文件名。
|
|
调用运行:
结果显示:
小结
本篇主要就是学习k-近邻算法,以及该算法的两个简单实际应用。k-近邻算法必须保存全部数据集,如果训练数据集很大,必须使用大量的存储空间。由于必须对数据集中的每个数据计算距离值,实际使用时非常耗时。
代码链接:https://github.com/asdfv1929/MachineLearningInAction_Python3/tree/master/Chapter02_kNN