注:基于现有小案例攥写
K 近邻算法采用测量不同特征值之间的距离方法进行分类:
优点:精度高、对异常值不敏感、无数据输入假定。
缺点:计算复杂度高、空间复杂度高。
K 近邻算法适用数据范围为:数值型和标称型
标称型:标称型目标变量的结果只在有限目标集中取值,如真与假
数值型:数值型目标变量则可以从无限的数值集合中取值,如0.100,42.001等
工作原理:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前 kk 个最相似的数据,这就是 K 近邻算法中 kk 的出处,通常 kk 是不大于 20 的整数。最后,选择 kk个最相似数据中出现次数最多的分类,作为新数据的分类。
k邻近算法的一般流程:
- 收集数据:可以使用任何方法。
- 准备数据:距离计算所需要的数值,最好是结构化的数据格式。
- 分析数据:可以使用任何方法。
- 训练算法:此步骤不适用于 K 近邻算法。
- 测试算法:计算错误率。
- 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行K 近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。
准备数据集:
http://labfile.oss.aliyuncs.com/courses/777/digits.zip
trainingDigits
:训练数据,1934 个文件,每个数字大约 200 个文件。
testDigits
:测试数据,946 个文件,每个数字大约 100 个文件。
全部代码如下:
import numpy as np
import operator
from os import listdir
def handwritingclassTest():
hwLabels =[]
trainingFileList = listdir('digits/trainingDigits')
m = len(trainingFileList)
trainingMat = np.zeros((m,1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:]=im2vector('digits/trainingDigits/%s' % fileNameStr)
testFileList = listdir('digits/testDigits')
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
vectorUnderTest = im2vector('digits/testDigits/%s' % fileNameStr)
classifierResult = classify0(vectorUnderTest,trainingMat,hwLabels,3)
print("测试样本 %d,分类器预测: %d,真实类别:%d" % (i+1,classifierResult,classNumStr))
if(classifierResult != classNumStr):
errorCount += 1.0
print("\n错误分类计数:%d" % errorCount)
print("\n错误分类 比例:%f" % (errorCount/float(mTest)))
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
def im2vector(filename):
#创建向量
returnVect = np.zeros((1,1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0,32*i+j]=int(lineStr[j])
return returnVect
#im2vector('digits/testDigits/0_1.txt')
def classify0(inX,dataSet,labels,k):
#获取dataSet维度值
dataSetSize = dataSet.shape[0]
#矩阵运算,计算测试数据与每个 样本数据对应数据项的差值
diffMat = np.tile(inX,(dataSetSize,1)) - dataSet
#进行平方运算
sqDiffMat = diffMat**2
#平方运算后以行 求和
sqDistances = sqDiffMat.sum(axis=1)
#取平方根,得到距离向量
distances = sqDistances**0.5
#按照距离进行排序,取出索引值
sortedDistIndicies = distances.argsort()
print(sortedDistIndicies)
classCount={}
#依次取出最近的样本数据
for i in range(k):
#记录改样本所属的类别
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
#对类别出现的频次进行排序,从高到低,确定前 k 个点所在类别的出现频率
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#返回前 k 个点出现频率最高的类别作为当前点的预测分类。
return sortedClassCount[0][0]
#group,labels = createDataSet()
#print(classify0([0,0],group,labels,3))
handwritingclassTest()
最后运行情况:
解释:
函数 img2vector
,将图像转换为向量:该函数创建 1x1024
的 NumPy 数组,然后打开给定的文件,循环读出文件的前 32
行,并将每行的头 32
个字符值存储在 NumPy 数组中,把一个 32x32
的二进制图像矩阵转换为 1x1024
的向量,最后返回数组。
函数classify0,通过欧式距离公式,计算临近点,选取与当前点距离最小的 k 个点,确定前 k 个点所在类别的出现频率,返回前 k 个点出现频率最高的类别作为当前点的预测分类。
函数handwritingClassTest(),打开 两个数据集,带入上面两个函数进行计算,进行分类,并与真实分类结果进行对比,得到最后 的错误分类比例。
上面的代码中的错误分类比例是0.0105,可以通过修改k值,改变训练样本进行测试,进一步调进错误分类 比例。