这篇文章主要是关于KNN算法的一些简单介绍,包括它的算法主要思想、算法完整代码、算法流程图以及算法的引用数据集的说明。
一、算法主要思想
KNN算法主要就是对两个数据集(一个是训练集,一个是测试集)中的数据与标签间的对应关系上做文章,具体地说就是:对于输入的测试集(此训练集不存在标签) 通过对训练集的整体数据(此数据集存在标签)的特征与对应关系经行比较而得出测试集的标签,其中得出的标签是与训练集中最邻近的分类标签。一般的,只选择K个最相近的数据,所以叫做K近邻算法(即KNN,K-Nearest Neighbors)。
举个例子,如图:
在图中,左边两个蓝点是B点,右边两个红点是A点,而绿点则是没有标签的未知点,此时,我们就可以通过计算绿点周围一定距离内A点与B点出现个数,并不断调整这个值,就会得知答案为A点。这个就是KNN算法的原理,当然也可以一言蔽之,即离谁近就和谁的标签一样。
二、算法完整代码
在知晓KNN算法的思想后,那么我便给出KNN的完整代码,其中对于代码的解释我会在第三部分算法的流程图这一部分详细解释。
那么,代码如下:
from numpy import *
import operator
import numpy as np
# KNN算法的距离处理
def classify0(inx, dataSet, labels, k):
dataSetSize = dataSet.shape[0]
diffMat = tile(inx, (dataSetSize,1)) - dataSet
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]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
sortedClassCounted = sorted(classCount.items(),
key=operator.itemgetter(1),reverse=True,)
return sortedClassCounted[0][0]
# 文件读取与打开
def fileDealPro(filPath):
# 使用 with 语句确保文件正确关闭
with open(filPath) as fr:
arrayLines = fr.readlines()
numberOfLines = len(arrayLines)
returnMat = np.zeros((numberOfLines, 4))
classLabelVector = []
labelMap = {'Iris-setosa': 0, 'Iris-versicolor': 1, 'Iris-virginica': 2}
index = 0
for line in arrayLines:
line = line.strip()
listFromLine = line.split(",")
# 尝试转换为浮点数,如果失败则跳过该行
try:
returnMat[index, :] = [float(num) for num in listFromLine[0:4]]
except ValueError as e:
print(f"Error converting line {index + 1}: {e}")
continue
classLabelVector.append(labelMap[listFromLine[-1]])
index += 1
return returnMat, classLabelVector
# 归一化特征值
def autoNorm(dataSet):
dataSet = np.array(dataSet)
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - tile(minVals, (m,1))
normDataSet = normDataSet/tile(ranges, (m,1))
return normDataSet,ranges,minVals
# 测试代码
if __name__ == "__main__":
# 文件路径
filePath = "D:\computerProgramming\PRO\python\LrisDataML\iris.data"
testfilePath = "D:\computerProgramming\PRO\python\LrisDataML\irisTest.data"
# 加载数据
dataMat, labels = fileDealPro(filePath)
testMat, testLabels = fileDealPro(testfilePath)
# 归一化数据
normDataMat, ranges, minVals = autoNorm(dataMat)
testNormMat, testRanges, testMinVals = autoNorm(testMat)
# 设置 K 值
k = 10
# 分类测试样本
m = normDataMat.shape[0]
hoRatio = 0.25
numTestVecs = int(m * hoRatio)
errorCount = 0
for i in range(numTestVecs):
result = classify0(testNormMat[i,:], normDataMat, labels, k)
print("The predicted class is:", result, "; But the real answer is:", testLabels[i])
if not testLabels[i] == result:
errorCount += 1
print("Error!!!")
print("The total error rate is: ",errorCount/numTestVecs)
三、算法的流程图
首先,给出的是测试部分的代码流程图,即如何运用三个自定义函数(KNN计算距离的函数、文件处理函数与归一化函数)
流程图如下:(我是用visio制图,然后直接截图的,所以有些不够清楚)
如图,其实很简单,就是先通过文件路径用专门的文件导出函数(稍后解释,其余二者同样)转为python可以处理的矩阵格式,然后再将这个导出的格式进行归一化处理,最后用KNN算法运算距离,预测标签,在预测时,统计每一次的预测结果的正确性,最后计算预测的错误率。通过这个错误率,我们可以相对调整K值,以达到目前最佳结果。
接下来,是关于提及的三个自定义函数的解释。
第一个是classify0()函数,其中,首先是三个变量的解释:inx为测试数据, dataSet为训练数据集, labels为训练数据集的标签, k为之前提及的选择最近邻数据的个数。
然后,这是关于这个自定义函数的流程图:
接着是第二个自定义函数fileDealPro(filePath)的流程图:
在这个文件处理函数中,关于数据集的格式是存在一定要求的,关于这个格式的要求我会在最后一部分,即算法引用的数据集部分进行一定解释,在此不再多言。
以下是最后一个自定义函数autoNorm(deteSet)函数的流程图,不过我先简单说明下归一化特征值这个操作的意义:它主要用于确保不同特征之间的尺度差异不会对距离度量产生不公平的影响。归一化的目的在于将所有特征缩放到相同的尺度范围内,通常是[0, 1]区间内,这样可以避免某一个特征因为其数值范围较大而对距离度量产生主导作用。
流程图如下:
四、算法的引用数据集的说明
我在进行代码测试时,所采用的数据集来源均是UCI ML Repository一网站的,该网站是一个广泛使用的数据集集合,由加州大学欧文分校(University of California, Irvine, UCI)的信息与计算机科学学院维护。在使用时,找到需要的数据集后点击标题进入到相应页面,点击左上角的下载即可。下载后,会发现文件夹中有四份文件,(以最为熟知的iris数据集为例)第一个bezdekIris.data不必理会,它在不会使用到;第二份Index是整体的目录,类似如下:
Index of iris
02 Dec 1996 105 Index
08 Mar 1993 4551 iris.data
30 May 1989 2604 iris.names
最先的是对应文件的最后修改日期,然后是对应文件的大小,最后是文件名称;第三份文件是iris.data文件,这是我们进行机器学习时所需要的数据集,在iris数据集中,一共有三组标签共150行数据(在之前的文件读取操作时大概率会报错说151行存在空行问题而导致程序无法正常读取文件,这时,我们只需要将151行的回车删除即可),在这个文件中大致类似于这样:
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
5.4,3.9,1.7,0.4,Iris-setosa
5.4,3.9,1.3,0.4,Iris-setosa
(这是我随便截取的一小段数据集)
在数据集中,每个数据以“ , ”作为分隔符,所以在之前的文件处理函数中,我们所选用的文件分割部分的代码是这样的:
listFromLine = line.split(",")
然后,从方才的一小段数据中还可以发现数据集的组成是先是数值,然后是对应的标签,以此方便文件的读取;最后是第四份文件,文件名为iris.name,这个文件是关于对应数据集的详细介绍,所以在此不必多说了。
我们都知道在机器学习中是既有训练数据又有测试数据,但在该网站中,它所提供的是一个数据的整体,所以我们要进行划分,将之分为训练数据和测试数据,具体方法如下
1、导入numpy和panda库,以及sklearn中的train_test_split
函数用于数据集划分。
2、使用pandas
的read_csv()
函数从网站上下载的数据文件加载数据集。
3、清洗数据,例如处理缺失值、异常值等。如果需要,进行特征工程,如特征选择、特征转换等。
4、使用sklearn.model_selection.train_test_split()
函数将数据集划分为训练集和测试集。
(而我在进行KNN算法之前,就已经将划分的数据写入到两个如上述代码中的文件里了,所以我的代码里并无pandas等的使用)
最后,我想说明下,目前我也只是在自学(参考是书籍是Peter Harrington的Machine Learning in Action)机器学习部分的东西,所以我想在我上述的文章里大概会有许多目前我还未知的错误等,因此,这篇文章更应该属于是我自己的一个回顾与总结。
此上