机器学习入门☞k-近邻算法(kNN)
程序员文章站
2024-01-18 21:53:28
...
k-邻近算法
下图有四个元组(坐标),分为两类(A,B)
- 我们通过计算(这里使用欧氏距离)一个待输入的点与其他点的距离,并按照小->大排序.
- 确定前K个(K<元组数)距离最小的点的主要分类
k近邻算法的一般流程
- 收集数据:可以使用任何方法。
- 准备数据:距离计算所需要的数值,最好是结构化的数据格式。
- 分析数据:可以使用任何方法。
- 训练算法:此步骤不适用于k近邻算法。
- 测试算法:计算错误率。
- 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k 近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分 类执行后续的处理。
疑问:耗时
如果有大量数据需要计算,我们该如何优化获取最近邻结果?
k-近邻算法的官方版本时间复杂度是O(n),如果数据量过多,
《统计学习方法-李航》一书中提到了一个kd树,这只是其中一种优化的思想,读者可以不予深究,爱好算法的可以试着实现一下
算法实现
import numpy as np
import operator
def createDataSet():
group = np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1],[0.4,0.6]])
labels = ['A','A','B','B','C']
return group, labels
group, labels = createDataSet()
def classify0(inX, dataSet, labels, k):
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()
classCount = {}
#选择距离最小的k个点
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
sortedClassCount = sorted(classCount.items(),
key = operator.itemgetter(1), reverse = True)
return sortedClassCount[0][0]
test一下
classify0([0.4,0.6], group, labels, 3)
运行完这段代码,你会发现得到的结果是A.
当然你可以修改classify0()函数的入参,来更好的理解
疑问2:如何准备数据?
解析datingTestSet2.txt文本
def file2matrix(filename):
fr = open(filename)
arrayOlines = fr.readlines()
numberOfLines = len(arrayOlines)
returnMat = zeros((numberOfLines,3))
classLabelVector = []
index = 0
for line in arrayOlines:
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat, classLabelVector
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
分析数据
import matplotlib as mb
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1], datingDataMat[:,2])
plt.show()
分析结果图
这样看不直观
将上面代码修改为
import matplotlib as mb
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
#ax.scatter(datingDataMat[:,1], datingDataMat[:,2])
ax.scatter(datingDataMat[:,1], datingDataMat[:,2],
15.0*np.array(datingLabels), 15.0*np.array(datingLabels))#使用第2列与第3列生成图
plt.show()
分析结果图2:
归一化数值
为了让下图中里程数不占有太高的比重,这里添加一点代码
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = np.zeros(np.shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - np.tile(minVals, (m, 1))
normDataSet = normDataSet/np.tile(ranges, (m,1))
return normDataSet, ranges, minVals
normMat, ranges, minVals = autoNorm(datingDataMat)
测试
def datingClassTest():
hoRatio = 0.10
datingDataMat, datingLabels = file2matrix('/home/ptt/datingTestSet2.txt')
normMat, ranges, minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
numTestVecs = int(m * hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m], \
datingLabels[numTestVecs:m], 3)
print ("the classifer came back with: %d the real answer is: %d" \
% (classifierResult, datingLabels[i]))
if(classifierResult != datingLabels[i]) :errorCount += 1.0
print ("the total error rate is: %f" % (errorCount/np.float(numTestVecs)))
>>>datingClassTest()
测试结果
整合系统
调用下面一段代码,输入个人参数即可获取结果
def classifyPerson():
resultList = ['not at all', 'in small doses', 'in large doses']
percentTats = np.float(input("percentage of time spent playing video games?"))
ffMiles = np.float(input("frequent flier miles earned per year?"))
iceCream = np.float(input("liters of ice cream consumed per year?"))
datingDataMat, datingLabels = file2matrix('/home/ptt/datingTestSet2.txt')
normMat, ranges, minVals = autoNorm(datingDataMat)
inArr = np.array([ffMiles, percentTats, iceCream])
classifierResult = classify0((inArr - minVals)/ranges, normMat, datingLabels, 3)
print("You will probably like this person: ", resultList[classifierResult - 1])
>>>classifyPerson()
显示结果:
手写识别系统
为了使用前面两个例子的分类器,我们必须将图像格式化处理为一个向量。我们将把一个32x32的二进制图像矩阵转换为1x1024的向量,这样前两节使用的分类器就可以处理数字图像信息了。
我们首先编写一段函数img2vector,将图像转换为向量:该函数创建1x1024的NumPy数组,然后打开给定的文件,循环读出文件的前32行,并将每行的头32个字符值存储在NumPy数组中,最后返回数组。
def img2vector(filename):
returnVect = 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
handwritingClassTest()是测试分类器的代码,将其写入kNN.py文件中。在写入这些代码之前,我们必须确保将from os import listdir写入文件的起始部分,这段代码的主要功能是从os模块中导入函数listdir,它可以列出给定目录的文件名。
def handwritingClassTest():
hwLabels = []
trainingFileList = listdir('trainingDigits') #load the
training set
m = len(trainingFileList)
trainingMat = zeros((m,1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0] #take off .txt
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameS
tr)
testFileList = listdir('testDigits') #iterate through the test set
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0] #take off .txt
classNumStr = int(fileStr.split('_')[0])
vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
classifierResult = classify0(vectorUnderTest, trainingMat, hw
Labels, 6)
print ("the classifier came back with: %d, the real answer is
: %d" % (classifierResult, classNumStr))
if (classifierResult != classNumStr): errorCount += 1.0
print ("\nthe total number of errors is: %d" % errorCount)
#测试:
>>>handwritingClassTest()