一、k-近邻算法概述
1、算法介绍
k-近邻算法(K-Nearest Neighbors,简称KNN)是一种用于分类和回归的统计方法。KNN 可以说是最简单的分类算法之一,同时,它也是最常用的分类算法之一。
2、算法原理
k-近邻算法基于某种距离度量来找到输入样本在训练集中的k个最近邻居,并且根据这k个邻居的属性来预测输入样本的属性。
比如我们的输入样本是图中的蓝色,那么k个近邻就是距离绿色小圆最近的k个邻居,然后在这k个邻居中,若黑色小圆的数量多于红色小圆,那么输入样本的属性就与蓝黑色小圆相同,反之则与红色小圆的属性相同,这就是k-近邻算法的算法思想。
3、KNN算法中常用的距离指标
在knn算法中怎样计算输入点与其他向量点之间的距离呢?这里就用到了两种距离公式。
欧几里得距离
欧几里得距离是我们在平面几何中最常用的距离计算方法,即两点之间的直线距离。
曼哈顿距离
曼哈顿距离是计算两点在一个网格上的路径距离,与上述的直线距离不同,它只允许沿着网格的水平和垂直方向移动。
4、算法优缺点
优点:
- 准确度较高:K近邻算法准确度较高为它可以适应不同的数据分布。
- 适用性广泛:K近邻算法可用于分类和回归问题,同时也支持多分类和多回归问题。
- 实现简单:K近邻算法的实现非常简单,特别适用于初学者学习模式识别的入门算法
缺点:
- 计算复杂度高:当数据集很大时,计算距离的时间和空间开销都会很大,影响算法执行效率。
- 受样本分布影响大:K近邻算法对训练集中样本的密度很敏感,对于密度相差很大的数据集,分类精度会受到较大影响。
- 数据不平衡问题:当训练集中某些类别的样本数目远远大于其他类别的样本数目时,K近邻算法的准确度会明显下降。
5、算法流程
1、准备数据集:
收集数据集,包括特征与对应的类别标签
对数据进行预处理,例如数据清洗、归一化等。
2、选择k值:
选择一个合适的k值,即确定最近邻居的个数。
3、选择距离度量方法
确定用于比较样本之间相似性的度量方法,常见的如欧几里得距离、曼哈顿距离等。
4、确定最近邻居
选择与待分类样本距离最近的k个训练样本
5、预测
对于分类任务:查看K个最近邻居中最常见的类别,作为预测结果。
对于回归任务:预测结果可以是K个最近邻居的平均值或加权平均值。
6、评估
使用适当的评价的评级骄傲指标评估模型的性能。
7、优化
基于性能评估结果,可能需要返回并调整某些参数,如K值、距离度量方法等,以获得更好的性能。
二、knn分类算法实例——性别预测
1、想法:设计一个KNN分类来用人的身高和体重来预测人的性别。
2、创建数据集:创立一个包含身高,体重与性别的数据集用来训练模型。
3、读取数据集
# 准备数据:从文本文件中解析数据
import numpy as np
def file2matrix(filename):
#打开文件
fr = open(filename)
arrayOLines = fr.readlines()
#得到文件行数
numberOfLines = len(arrayOLines)
#返回的NumPy矩阵
returnMat = np.zeros((numberOfLines,2))
#返回的分类标签向量
classLabelVector = []
#行的索引值
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t')
returnMat[index, :] = listFromLine[0:2]
if listFromLine[-1] == 'male':
classLabelVector.append(1)
else:
classLabelVector.append(0)
index += 1
return returnMat, classLabelVector
filename = r'C:\Users\Yusua\Desktop\1234.txt'
datingDataMat, datingLabels = file2matrix(filename)
- 读取文件内容:使用
readlines()
函数读取文件的所有行,每一行作为一个字符串存储在列表arrayOLines
中。 - 获取文件行数:使用
len()
函数获取arrayOLines
列表的长度,即文件的行数,存储在变量numberOfLines
中。 - 创建返回的NumPy矩阵:使用
np.zeros()
函数创建一个形状为(numberOfLines, 2)
的全零矩阵,存储在变量returnMat
中。 - 在数据集的最后一行,将male的标签转化为1,将female的标签转化为0
print(datingDataMat)
print(datingLabels)
将数据矩阵与标签矩阵打印出来
4、分析数据,创建身高-体重散点图。
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
def showData(datingDataMat, datingLabels):
fig, axs = plt.subplots(nrows=1, ncols=1, sharex=False, sharey=False, figsize=(13, 8))
LabelsColors = []
for i in datingLabels:
if i == 1:
LabelsColors.append('black')
elif i == 0:
LabelsColors.append('red')
axs.scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 1], color=LabelsColors, s=15, alpha=.5)
axs0_title_text = axs.set_title('high_weight')
axs0_xlabel_text = axs.set_xlabel('high')
axs0_ylabel_text = axs.set_ylabel('weight')
plt.setp(axs0_title_text, size=9, weight='bold', color='red')
plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black')
plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black')
male = mlines.Line2D([], [], color='black', marker='.',
markersize=6, label='male')
female = mlines.Line2D([], [], color='red', marker='.',
markersize=6, label='female')
axs.legend(handles=[male, female])
plt.show()
for i in datingLabels:
通过遍历datingLabels标签
列表中的每个元素,判断标签是否为1或者0,然后将相应的颜色添加到LabelsColors
列表中。
axs.scatter
根据特征数据datingDataMat
绘制散点图,其中x=datingDataMat[:, 0]
表示取特征数据矩阵的第一列即身高作为x轴坐标,y=datingDataMat[:, 1]
表示取特征数据矩阵的第二列即体重作为y轴坐标,来绘制散点图
showData(datingDataMat,datingLabels)
展示散点图
5、数据集的归一化处理
def autoNorm(dataSet):
#获得数据的最小值
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
#最大值和最小值的范围
ranges = maxVals - minVals
#shape(dataSet)返回dataSet的矩阵行列数
normDataSet = np.zeros(np.shape(dataSet))
#返回dataSet的行数
m = dataSet.shape[0]
#原始值减去最小值
normDataSet = dataSet - np.tile(minVals, (m, 1))
#除以最大和最小值的差,得到归一化数据
normDataSet = normDataSet / np.tile(ranges, (m, 1))
#返回归一化数据结果,数据范围,最小值
return normDataSet, ranges, minVals
数据的归一化处理采用了newValue=(oldValue-min)/(max-min)的方法处理数据,使得数据变得更加合理。
normDataSet, ranges, minVals = autoNorm(datingDataMat)
print(normDataSet)
print(ranges)
print(minVals)
显示结果
6、测试算法:作为完成程序验证分类器
# 分类器
import operator
# 输入:inX - 用于分类的数据(测试集);dataSet - 训练集;labes - 分类标签;K - KNN算法参数,选择距离最小的K个点
# 输出:sortedClassCount[0][0] - 分类结果
def classify0(inX, dataSet, labels, k):
#numpy函数shape[0]返回dataSet的行数
dataSetSize = dataSet.shape[0]
#在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
#二维特征相减后平方
sqDiffMat = diffMat**2
#sum()所有元素相加,sum(0)列相加,sum(1)行相加
sqDistances = sqDiffMat.sum(axis=1)
#开方,计算出距离
distances = sqDistances**0.5
#返回distances中元素从小到大排序后的索引值
sortedDistIndices = distances.argsort()
#定一个记录类别次数的字典
classCount = {}
for i in range(k):
#取出前k个元素的类别
voteIlabel = labels[sortedDistIndices[i]]
#dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
#计算类别次数
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#python3中用items()替换python2中的iteritems()
#key=operator.itemgetter(1)根据字典的值进行排序
#key=operator.itemgetter(0)根据字典的键进行排序
#reverse降序排序字典
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#返回次数最多的类别,即所要分类的类别
return sortedClassCount[0][0]
- 首先,计算训练集
dataSet
的行数,即数据集中样本的数量,赋值给dataSetSize
。- 接着,将测试样本
inX
通过np.tile(inX, (dataSetSize, 1)) - dataSet
得到一个与训练集形状相同的矩阵diffMat
。这个矩阵的每一行表示测试样本与训练集中某个样本在每个特征上的差值。- 然后,对
diffMat
中的每个元素进行平方操作,得到矩阵sqDiffMat
,其中的每个元素表示测试样本与训练集中某个样本在每个特征上的差值的平方。- 接下来,对
sqDiffMat
按行求和,得到一个包含每个样本与测试样本之间距离平方和的向量sqDistances
。- 对
sqDistances
中的每个元素进行开方操作,得到一个包含每个样本与测试样本之间距离的向量distances
。- 接着,将
distances
中的元素按从小到大进行排序,返回排序后的索引值,赋值给sortedDistIndices
。- 然后,定义一个记录类别次数的字典
classCount
。- 接下来,遍历
sortedDistIndices
中的前k
个元素,取出对应的训练样本的类别voteIlabel
。- 对于每个训练样本的类别
voteIlabel
,将其加入到classCount
字典中,如果已经存在,则将对应的值加1;如果不存在,则设置初始值为1。- 遍历完所有的训练样本后,得到一个包含各个类别及其出现次数的有序列表
sortedClassCount
,按照每个类别的出现次数从大到小排序。- 最后,返回出现次数最多的类别,即为所要分类的类别。
7、使用算法:构建完整可用系统
# 通过输入一个人的三维特征,进行分类输出
def classifyPerson():
#输出结果
resultList = ['male','female']
#三维特征用户输入
high = float(input("身高:"))
weight = float(input("体重:"))
#打开的文件名
filename = r'C:\Users\Yusua\Desktop\1234.txt'
#打开并处理数据
datingDataMat, datingLabels = file2matrix(filename)
#训练集归一化
normMat, ranges, minVals = autoNorm(datingDataMat)
#生成NumPy数组,测试集
#inArr = np.array([precentTats, ffMiles, iceCream])
inArr = np.array([high, weight])
#测试集归一化
norminArr = (inArr - minVals) / ranges
#返回分类结果
classifierResult = classify0(norminArr, normMat, datingLabels, 3)
#打印结果
print("你的性别是%s" % (resultList[classifierResult-1]))
classifyPerson()
构建了完整的系统后开始测试使用:输入身高:178 体重:60
根据k近邻算法的出结果
三、总结
KNN是一种基于实例的学习算法,它根据训练样本的特征和对应的类别标签,通过计算测试样本与训练样本之间的距离来进行分类预测。本次实验使我们了解了K近邻算法的基本原理,概念模型以及算法流程,学会了如何使用KNN算法来解决一些简单的分类问题,
解决问题:在导入数据源后输出打印资源矩阵与标签矩阵时标签矩阵输出全为0,或者是显示字符串转换为float类型失败,在查找问题后发现是资源文件中数据的储存必须按照float型进行存储,否则资源数据读取有误,无法正常导入,