最近学习了K-近邻算法,在这里进行一个总结。
简单地说,K 近邻算法采用测量不同特征值之间的距离方法进行分类。它具有的优缺点如下:
- 优点:精度高、对异常值不敏感、无数据输入假定。
- 缺点:计算复杂度高、空间复杂度高。
K 近邻算法适用数据范围为:数值型和标称型。
在输入想要得到标签得的数据之后,将输入数据的每个特征与样本集中的数据相对应的特征进行比较,之后通过提取前k个最相似的分类标签,这也是K近邻算法中K的出处,通常K的值是不大于20的证书。最后根据K个最相似数据中出现最多的标签分类作为输入数据的分类。
K 近邻算法的一般流程
- 收集数据:可以使用任何方法。
- 准备数据:距离计算所需要的数值,最好是结构化的数据格式。
- 分析数据:可以使用任何方法。
- 训练算法:此步骤不适用于 K 近邻算法。
- 测试算法:计算错误率。
- 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行K 近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理
这里,自己设值标签以及类别,如下。
import numpy as np
def createDataSet():
group = np.array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]])
labels = ['A', 'A', 'B', 'B']
return group, labels
准备数据:将数据转换为测试向量
假设输入为32*32的图像,那么我们就需要将图像转换为测试向量,所以将32*32的二进制图像矩阵转换为1*1024的向量,因为这里是一张图片,所以为1*1024,如果为m张图片,则应该为m*1024,根据情况而定。
我们首先编写一段函数 img2vector
,用于将图像转换为向量:该函数创建 1x1024
的 NumPy 数组,然后打开给定的文件,循环读出文件的前 32
行,并将每行的头 32
个字符值存储在 NumPy 数组中,最后返回数组。
def img2vector(filename):
# 创建向量
returnVect = np.zeros((1, 1024))
# 打开数据文件,读取每行内容
fr = open(filename)
for i in range(32):
# 读取每一行
lineStr = fr.readline()
# 将每行前 32 字符转成 int 存入向量
for j in range(32):
returnVect[0, 32*i+j] = int(lineStr[j])
return returnVect
分析数据
算法实现过程:
- 计算已知类别数据集中的点与当前点之间的距离;
- 按照距离递增次序排序;
- 选取与当前点距离最小的 k 个点;
- 确定前 k 个点所在类别的出现频率;
- 返回前 k 个点出现频率最高的类别作为当前点的预测分类。
这里,使用欧氏距离公式,计算两个向量点 𝑋𝑎Xa 和 𝑋𝑏Xb 之间的距离:
计算完所有点之间的距离后,可以对数据按照从小到大的次序排序。然后,确定前K个距离最小元素所在的主要分类,输入K总是正整数;最后,将 classCount 字典分解为元组列表,然后使用程序第二行导入运算符模块的itemgetter方法,按照第二个元素的次序对元组进行排序。 此处的排序为逆序,即按照从最大到最小次序排序,最后返回发生频率最高的元素标签。
import operator
def classify0(inX, dataSet, labels, k):
"""
参数:
- inX: 用于分类的输入向量
- dataSet: 输入的训练样本集
- labels: 样本数据的类标签向量
- k: 用于选择最近邻居的数目
"""
# 获取样本数据数量
dataSetSize = dataSet.shape[0]
# 矩阵运算,计算测试数据与每个样本数据对应数据项的差值
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
# sqDistances 上一步骤结果平方和
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
# 取平方根,得到距离向量
distances = sqDistances**0.5
# 按照距离从低到高排序
sortedDistIndicies = distances.argsort()
classCount = {}
# 依次取出最近的样本数据
for i in range(k):
# 记录该样本数据所属的类别
voteIlabel = labels[sortedDistIndicies[i]]
print(voteIlabel)
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
print(classCount)
# 对类别出现的频次进行排序,从高到低
sortedClassCount = sorted(
classCount.items(), key=operator.itemgetter(1), reverse=True)
# 返回出现频次最高的类别
return sortedClassCount[0][0]
代码中列表sortedDistIndicies记录了出现前K次的索引号,通过循环,将这些索引号取出,即循环sortedDistIndicies[i]表示所选择的0-K次的索引号,labels[sortedDistIndicies[i]]表示取出的K个数据所代表的类别。
classCount.get(voteIlabel, 0)返回字典classCount中votelabel元素对应的值,若无,则进行初始化。
-
若不存在voteIlabel,则字典classCount中生成voteIlabel元素,并使其对应的数字为0,即
classCount = {voteIlabel:0},此时classCount.get(voteIlabel,0)作用是检测并生成新元素,括号中的0只用作初始化,之后再无作用。 -
当字典中有voteIlabel元素时,classCount.get(voteIlabel,0)作用是返回该元素对应的值,即0。
进行测试。
group, labels = createDataSet()
classify0([0, 5], group, labels, 3)
学习了K近邻算法可是尝试使用K近邻算法识别手写数字数据集,莺尾花分类等实际问题。K近邻算法是分离数据最简单有效的算法,但是计算复杂度高,空间复杂度高。K决策树就是K近邻算法的优化版,可以减少存储空间和计算时间的开销。
以上是关于学习K近邻算法的一些总结,如内容有误,欢迎指正。
关于get()函数作用参考文章: (2条消息) 【Python】get()函数作用_Mr.Gu的博客-CSDN博客