kNN算法
kNN算法概述
knn工作原理:
存在一个样本数据集合(训练样本集),并且每个样本都具有标签,输入新的样本后,我们将样本的特征与训练样本集中的数据特征比较,算法提取特征最相似的k个样本的标签,采用少数服从多数的形式,认为新样本的标签就是最相似的k个样本中的主要标签.
def classify0(inX,dataSet,labels,k):
'''
inX 待分类元素,是一个list
dataset 训练样本集,是一个array对象
labels 训练样本集标签
k kNN中的取前几相近数据判断
'''
dataSetSize = dataSet.shape[0]
diffMat = tile(inX,(dataSetSize,1)) - dataSet
# 下面三句其实就是做了个求待测数据到所有训练样本集数据的欧式距离
sqdiffMat = diffMat**2
sqDistances = sqdiffMat.sum(axis=1)
distances = sqDistances**0.5
# argsort是对distance进行排序(升序),返回的是元素的index 类似桶排序?
sortedDistIndicies = distances.argsort()
classCount = {}
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)
# 第一次见到operator.itemgetter(i) 顾名思义返回的是一个方法(函数),用来获取输入参数的第i+1个值
# 在sorted函数中给key赋值,使得key作为一个表达式,以item的第2个元素为索引排序
# 3.x中用items返回一个list,元素是键值对构成的元组,同时keys返回一个所有键的list,value返回一个所有值的list
return sortedClassCount[0][0]
def createSet():
group = array([[1,1.1],[1,1],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group,labels
group,labels = createSet()
classify0([0,0.14],group,labels,3)
[('B', 2), ('A', 1)]
'B'
tile(item,dim)
–tile函数用于将item按照dim复制成一个更大的向量,返回一个array
operator.itemgetter(i)
– 顾名思义返回的是一个方法(函数),用来获取输入参数的第i+1个值
以上实现了一个简单的knn分类器,完成了knn算法的核心部分,但并不实用.
书中通过两个实例,提高约会网站配对和?完整的演示机器学习算法的流程
例1 实用knn算法改进约会网站的配对效果
准备数据 从文本解析数据
数据是1000行约会网站的男性信息,包括
- 每年获得的飞行常客里程数
- 玩视频游戏所耗的时间百分比
- 每周消费的冰淇淋公升数
首先对数据进行处理和初步的观察
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
returnMat = zeros((numberOfLines,3))
classLableVector = []
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLableVector.append(int(listFromLine[-1]))
index += 1
return returnMat,classLableVector
datingDataMat, datingLabels = file2matrix('/home/caid/mycode/MachineLearningInAction/MLIA_self/ch_02/datingTestSet2.txt')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
<matplotlib.collections.PathCollection at 0x7f4001515898>
准备数据:归一化数据
为了避免特征之间,差值大的特征对结果影响大,差值小的数据对结果影响小,需要对数据进行归一化.归一化的几种方式,和标准化的区别.
归一化的原理:
在下面这个自动归一化的函数中,多次使用tile将一个一维的数组复制成dataset的大小,利用Numpy中运算符是对元素操作的特点,两步计算得到归一化的数据集.
def autoNorm(DataSet):
Maxval = DataSet.max(0)
Minval = DataSet.min(0)
ranges = Maxval - Minval
NormDataSet = zeros(shape(DataSet))
m = DataSet.shape[0]
NormDataSet = DataSet - tile(Minval,(m,1))
NormDataSet = NormDataSet/tile(ranges,(m,1))
return NormDataSet,ranges,Minval
normMat,ranges,minVals = autoNorm(datingDataMat)
normMat
ranges
minVals
array([[ 0.44832535, 0.39805139, 0.56233353],
[ 0.15873259, 0.34195467, 0.98724416],
[ 0.28542943, 0.06892523, 0.47449629],
...,
[ 0.29115949, 0.50910294, 0.51079493],
[ 0.52711097, 0.43665451, 0.4290048 ],
[ 0.47940793, 0.3768091 , 0.78571804]])
array([ 9.12730000e+04, 2.09193490e+01, 1.69436100e+00])
array([ 0. , 0. , 0.001156])
def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = \
file2matrix('/home/caid/mycode/MachineLearningInAction/MLIA_self/ch_02/datingTestSet2.txt')
m = datingDataMat.shape[0]
numTestVecs = int(m*hoRatio)
NormDataMat,ranges,minvals = autoNorm(datingDataMat)
errorCount = 0.0
for i in range(numTestVecs):
label = classify0(NormDataMat[i,:],\
NormDataMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
# print("hte classifier came back with: %d, the real answer is %d" %(label,datingLabels[i]))
if label != datingLabels[i]:errorCount += 1.0
print("the total error rate is %.3f%%" %(errorCount/float(numTestVecs)*100.0))
datingClassTest()
the total error rate is 5.000%
通过测试算法,可以看出这个分类器的结果的错误率在5%左右.为了使用算法,需要提供一个接口
使用算法:构建可用的系统
def classifyPerson():
resultList = ['not at all','in small doses','in large doses']
percentTats = float(input("percentage of time spent playing video game?"))
ffMiles = float(input("ffmiles:"))
iceCream = float(input("iceCream:"))
datingDataMat,datingLabels = file2matrix('/home/caid/mycode/MachineLearningInAction/MLIA_self/ch_02/datingTestSet2.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
inArr = array([ffMiles,percentTats,iceCream])
label = classify0((inArr-minVals)/ranges,datingDataMat,datingLabels,3)
print("you will probably like this person:",\
resultList[label-1])
classifyPerson()
percentage of time spent playing video game?10
ffmiles:10000
iceCream:0.5
you will probably like this person: in small doses
上例介绍了如何在数据中使用kNN分类器,下面这个例子将介绍如何在二进制数据中利用kNN
例2 手写识别系统
def img2vector(filename):
returnVector = zeros((1,1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVector[0,32*i+j] = int(lineStr[j])
return returnVector
这是一个将测试数据转换为数据集的一部分的函数,将一个32*32的方阵转化为一个1*1024的行向量
接下来测试一下这个函数
testVector = img2vector('MLIA_self/ch_02/testDigits/0_13.txt')
testVector[0:32]
array([[ 0., 0., 0., ..., 0., 0., 0.]])
from os import listdir
def handWritingClassTest():
hwLabels = []
trainingFileList = listdir('MLIA_self/ch_02/trainingDigits')
m = len(trainingFileList)
trainingMat = zeros((m,1024))
for i in range(m):
filenameStr = trainingFileList[i]
fileStr = filenameStr.split('.')[0]
classLabel = int(fileStr.split('_')[0])
hwLabels.append(classLabel)
trainingMat[i,:] = img2vector('MLIA_self/ch_02/trainingDigits/%s' %filenameStr)
testFileList = listdir('MLIA_self/ch_02/testDigits')
n = len(testFileList)
errorcount = 0.0
for i in range(n):
filenameStr = testFileList[i]
fileStr = filenameStr.split('.')[0]
classLabel = int(fileStr.split('_')[0])
#hwLabels.append(classLabel)
testImg = img2vector('MLIA_self/ch_02/trainingDigits/%s' %filenameStr)
classifyResult = classify0(testImg,trainingMat,hwLabels,3)
# 第一次做计算还是没有想清楚k是什么,是最邻近范围,而不是测试集大小,也不是分成几类
if classifyResult != classLabel:
errorcount += 1.0
print("the number of error is : %f" % errorcount)
print("the error rate is : %f " %(errorcount/float(n)))
handWritingClassTest()
the number of error is : 11.000000
the error rate is : 0.011628
之所以kNN算法能够用在手写数字识别上,并且能够得到较好的结果,我认为从图像的角度看,同一个数字在图像中存在像素的位置是相近的,如果两个手写数字的图像,有像素的位置重合最多(即计算出来的距离最小)那么它们就应该是同一个数字,对字母的识别相类似,另一方面,训练集的数量足够多,也是能够达到较好效果的关键.
kNN算法总结
kNN是分类数据最有效最简单的算法之一,但是使用算法时,knn需要保存全部数据集,并且对数据集中的每个数据计算距离,实际上需要大量的空间和时间开销,另一个缺陷是无法给出任何数据的基础结构信息(什么是基础结构信息?)因此,也无法知晓平均实例样本和典型实例样本具有什么特征.