【博客的主要内容主要是自己的学习笔记,并结合个人的理解,供各位在学习过程中参考,若有疑问,欢迎提出;若有侵权,请告知博主删除,原创文章转载还请注明出处。】
1.什么是kNN#
k-近邻算法(kNN k-NearestNeighbor)采用测量不同特征值之间的距离方法进行分类。在确定分类决策上仅依据最邻近的一个或多个样本的类别来决定待分样本所属类别。
2.用途#
kNN算法主要被用于文本分类、相似推荐.
3. kNN工作原理#
存在一个样本数据集合(即训练样本集),并且样本集中每个数据都存在标签(即每个数据与所属分类的对应关系)。输入没有标签的新数据之后,将新数据的每个特征与样本集中数据对应的特征进行比较,算法将提取出样本集中特征最相似数据(最近邻)的分类标签。
一般选择样本数据集中前K个最相似的数据,k一般不大于20的整数。
说明:图中绿色圆点表示新数据,蓝色正方形和红色三角形表示已分类的样本集。
如果K=3,由于红色三角形所占比例为2/3,绿色圆点则属于“红色三角形”分类;
如果K=5,由于蓝色正方形所占比例为3/5,绿色圆点则属于“蓝色正方形”分类。
4. kNN算法流程#
使用K-近邻算法将每组数据划分到某个类中:
1)计算已知类别数据集中的点与当前点之间的距离;
2)按照距离递增次序排序;
3)选取与当前点距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点出现频率最高的类别作为当前点的预测分类。
5. kNN算法Code#
1.Python算法实现
from numpy import *
import operator
from os import listdir
def classify0(inX, dataSet, labels,k):
#0、获取数据Count
dataSetSize = dataSet.shape[0]
#1、计算距离
diffMat = tile(inX,(dataSetSize,1)) - dataSet
sqDiffMat = diffMat ** 2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances ** 0.5
#2、选择最小距离的k个点
sortedDistIndicies = distances.argsort()
#3、确定前K个点所在类别的出现频率
classCount={}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#4、返回前K个点出现频率最高的类别作为当前点的预测分类
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
2、kNN算法测试
def createDataSet():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group,labels
6. kNN算法特点#
优点
1、简单,易于理解,易于实现,无需估计参数,无需训练;
2、适合对稀有事件进行分类;
3、适用于多分类问题。
缺点
1、计算复杂度高、空间复杂度高;
2、可理解性差,无法给出像决策树类的规则。
7. kNN示例分析#
7.1 示例1:使用k-近邻算法改进约会网站的配对效果##
1.背景
海伦通过在线约会网寻找自己合适的约会对象。根据自己一番总结将约会对象分为三类:
1、不喜欢的人;
2、魅力一般的人;
3、极具魅力的人.
海伦收集网站中每位约会对象的以下数据来划分其约会类型:
1、每年获得的飞行公里数;
2、玩视频游戏所耗时间百分比;
3、每周消费的冰淇淋公升数.
编写个小程序,海伦输入会员特征值程序显示该会员所属分类。
2. 分析
1)收集数据:提供文本文件
2)准备数据:使用Python解析文本文件
3)分析数据:使用Matplotlib画二维扩散图
4)训练数据:不适合kNN
5)测试数据:使用海伦提供的部分数据作为测试样本
6)使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断是否为自己喜欢的类型。
3. Code
使用Python解析文本文件
def file2matrix(filename):
fr = open(filename)
numberOfLines = len(fr.readlines())
#初始化矩阵
returnMat = zeros((numberOfLines,3))
#初始化标签矩阵
classLabelVector = []
fr = open(filename)
index = 0
for line in fr.readlines():
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat,classLabelVector
4. 使用Matplotlib画二维扩散图
#读取样本数据
datingDataMat,datingLabels = file2matrix('H:\workspacePy\machinelearninginaction\Ch02\datingTestSet2.txt')
#散点图显示样本数据
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.font_manager
fig = plt.figure()
ax = fig.add_subplot(111)
##1、散点图显示“玩视频游戏所耗时间百分比”、“每周消费的冰淇淋公升数”
ax.scatter(datingDataMat[:,1], datingDataMat[:,2], 15.0*array(datingLabels),15.0*array(datingLabels))
plt.show()
##2、散点图显示“每年获得的飞行常客里程数”、“玩视频游戏所耗时间百分比”
ax.scatter(datingDataMat[:,0],datingDataMat[:,1],15.0*array(datingLabels),15.0*array(datingLabels))
plt.show()
5.归一化特征值
特征值数值间相差较大,在计算距离时易产生较大偏差。例如:
”每年获得飞行常客里程数”远大于“玩视频游戏所耗时间百分比”和“每周消费冰淇淋公升数”,仅仅因为“每年获得飞行常客里程数”远大于其它2个特征值,计算距离结果将放大。我们将“每年获得飞行常客里程数”特征换算为“0到1”或“-1到1”之间,称为“归一化特征值”。
def autoNorm(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
6.测试算法
机器学习算法很大重要工作就是评估算法的正确率,通常将提供已有数据的90%作为训练样本来训练分类器,而使用其余的10%数据去测试分类器,检查分类器的正确率。
def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
normDataSet,ranges,minVals = autoNorm(datingDataMat)
m = normDataSet.shape[0]
numTestVecs = int(m * hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(normDataSet[i,:],normDataSet[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
print "the classifier 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/float(numTestVecs))
7.2 示例2:手写识别系统##
1. 背景
数字09,通过32*32位图来展现。现有1900个位图数据,分别表示了09数字内容。需通过kNN方法能快速分辨出位图的数字。
2. 分析
1)收集数据:提供文本文件;
2)准备数据:将图像格式转换为分类器使用的list格式;
3)分析数据:在Python命令提示符中检查数据,确保它符合要求;
4)训练算法:不适合kNN算法;
5)测试数据:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
6)使用算法:省略
3.Code
#将32**32位图片数据转换为1*1024的list
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
4.测试算法
#测试算法
def handwritingClassTest():
#1、获取样本数据集合,
hwLabels = []
path = 'H:\\workspacePy\\machinelearninginaction\\Ch02\\digits\\trainingDigits'
trainingFileList = listdir(path)
m = len(trainingFileList)
trainingMat = zeros((m,1024))
#2、将样本数据并转换为list
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector(path + '/%s' % fileNameStr)
#3、使用测试样本进行测试对比。
path1 = 'H:\\workspacePy\\machinelearninginaction\\Ch02\\digits\\testDigits'
testFileList = listdir(path1)
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
vectorUnderTest = img2vector(path1 + '/%s' % fileNameStr)
##knn
classifierResult = classify0(vectorUnderTest,trainingMat,hwLabels,3)
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
print "\nthe total error rate is:%f" % (errorCount/float(mTest))