Python 3.6 WIN10环境下,PyCharm IDE写代码,前半部分(电影分类问题)使用cmd执行数据输入,后半部分(约会网站问题开始)直接在IDE的Console区域执行数据输入;path环境路径使用aconda,暂时发现其他的环境路径遇到matplotlib无法安装成功的问题:
k-近邻算法思想如下:
1.计算已知类别数据集中的点与当前点之间的距离
2.按照距离递增次序排序
3.选取与当前点距离最小的k个点
4.确定前k个点所在类别的出现频率
主要运用到高中数学知识,求欧几里得空间两点距离:
创建kNN.py文件,cmd环境需要cd到kNN.py所在目录。
1.电影分类问题,代码如下:
①收集数据:可使用任何方法
②准备数据:距离计算所需要的数值,最好是结构化的数据格式
③分析数据:可使用任何方法
④训练算法:此步骤不适用于k-近邻算法
⑤测试算法:计算错误率
⑥使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。
首先创建数据集:
from numpy import *
import operator
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
其cmd对应执行命令如下:
>>> group,labels = kNN.createDataSet()
>>> group
array([[ 1. , 1.1],
[ 1. , 1. ],
[ 0. , 0. ],
[ 0. , 0.1]])
>>> labels
['A', 'A', 'B', 'B']
分类方法,在cmd中调用执行:inX-要分类的输入量; dataSet-输入的训练样本集;labels-标签向量;k-旋转最近邻居的数目
最后返回的就是,该输入量最可能所属的元素标签,即发生频率最高的标签。
def classify0(inX, dataSet, labels, k):
#求出样本集的行数,也就是labels标签的数目
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()
#对距离最小的k个点统计对应的样本标签
classCount={}
for i in range(k): #选择距离最小的k个点
#取第i+1邻近的样本对应的类别标签
voteIlabel = labels[sortedDistIndicies[i]]
#以标签为key,标签出现的次数为value将统计到的标签及出现次数写进字典
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#对字典按value从大到小排序
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True) #排序
#返回排序后字典中最大value对应的key
return sortedClassCount[0][0]
其对应的cmd命令执行操作:
>>> kNN.classify0([0,0], group, labels, 3)
'B'
>>> kNN.classify0([0,1], group, labels, 3)
'B'
>>> kNN.classify0([1,1.1], group, labels, 3)
'A'
2.约会网站问题
解析已知样本数据,样本数据包含特征值和目标值。将未知对象(由特征值定义)归类(目标值),三类:1.不喜欢的人 2.魅力一般的人 3.极具魅力的人
流程如下:
①收集数据:提供文本文件datingTestSet.txt
②准备数据:使用Python解析文本文件
③分析数据:使用Matplotlib画二维扩散图
④训练算法:此步骤不适用于k-近邻算法
⑤测试算法:使用部分数据作为测试样本。
测试样本和非测试样本的区别在于:测试样本时已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
⑥使用算法:产生简单的命令行程序,然后可输入一些特征数据以判断对方是否为自己喜欢的类型
代码如下:
def file2matrix(filename):
#打开文件
fr = open(filename)
#得到文件的行数
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
#创建以零填充的矩阵,为了简化处理,将该矩阵的另一维度设置为固定值3,可按照自己的实际需求增加相应的代码以适应变化的输入值
returnMat = zeros((numberOfLines, 3))
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip()#截取掉所有的回车字符
listFromLine = line.split('\t') #然后使用tab字符\t将上一步得到的整行数据分割成一个元素列表
returnMat[index,:] = listFromLine[0:3] #选取前3个元素,将它们存储到特征矩阵中
if(listFromLine[-1] == 'largeDoses'): #Python语言可使用索引值-1表示列表中的最后一列元素,利用该负索引,可方便地将列表的最后一列存储到向量classLabelVector中
classLabelVector.append(3)#listFromLine[-1] = '3' #为之后的使用该算法来判断是否喜欢一个人时而改
elif (listFromLine[-1] == 'smallDoses'):
classLabelVector.append(2)#listFromLine[-1] = '2' #为之后的使用该算法来判断是否喜欢一个人时而改
else:
classLabelVector.append(1)#listFromLine[-1] = '1' #为之后的使用该算法来判断是否喜欢一个人时而改
#classLabelVector.append(float(listFromLine[-1]))#必须明确地通知解释器,告诉它列表中存储的元素值为整型,否则Python语言会将这些元素当做字符串处理
index += 1
return returnMat,classLabelVector
cmd下命令如下,包括使用matplotlib图像化:
>>> import kNN
>>> from numpy import *
>>> datingDataMat,datingLabels = kNN.file2matrix('datingTestSet.txt')
>>> import matplotlib
>>> import matplotlib.pyplot as plt
>>> 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 object at 0x000002B853F7F860>
>>> plt.show()
若报错:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'array' is not defined
则添加:
>>>from numpy import *
效果如下,带有样本分类标签的约会数据散点图,虽然能够比较容易地区分数据点从属类别,但依然很难根据这张图得出结论性信息:
将数据列1,2改为0,1:
ax.scatter(datingDataMat[:,1], datingDataMat[:,2], 15.0*array(datingLabels), 15.0*array(datingLabels))
改为:
ax.scatter(datingDataMat[:,0], datingDataMat[:,1], 15.0*array(datingLabels), 15.0*array(datingLabels))
归一化处理:
由欧式距离公式可知,每年获得的飞行常客里程数,对计算结果影响最大。而一般认为这三种特征值应该是同等重要的,因此作为三个等权重的特征之一,飞行常客里程数不应该如此严重地影响到计算结果,因此要对数值进行归一化处理,如将取值范围处理为0到1或-1到1之间。下面的公式可以将任意取值范围的特征值转化为0到1区间内的值:
newValue = (oldValue - min)/(max - min)
其中min和max分别是数据集中的最小特征值和最大特征值。虽然改变数值取值范围增加了分类器的复杂度,但为了得到准确结果,必须这么做。因此就需要再增加一个新函数autoNorm()来将数字特征值转化为0到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)) #tile将变量内容复制成输入矩阵同样大小的矩阵
normDataSet = normDataSet/tile(ranges, (m,1)) #特征值相除,为了归一化特征值,必须使用当前值减去最小值,然后除以取值范围;在某些数值处理软件包,/可能意味着矩阵除法,NumPy库中,
return normDataSet,ranges,minVals #矩阵除法需要使用函数linalg.solve(matA,matB)
cmd:
>>> import kNN
>>> datingDataMat,datingLabels=kNN.file2matrix('datingTestSet.txt')
>>> normMat,ranges,minVals = kNN.autoNorm(datingDataMat)
>>> normMat
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]])
>>> ranges
array([ 9.12730000e+04, 2.09193490e+01, 1.69436100e+00])
>>> minVals
array([ 0. , 0. , 0.001156])
测试算法,验证分类器:
测试分类器,若分类器的正确率满足要求,则可使用该软件来处理约会网站提供的约会名单了。机器学习算法一个很重要的工作就是评估算法的正确率,通常只提供已有数据的90%作为训练样本来训练分类器,而使用其余的10%数据去测试分类器,检测分类器的正确率。需注意的是,10%的测试数据应该是随机选择的,由于datingTestSet.txt提供的数据并没有按照特定目的来排序,所以可随意选择10%数据而影响其随机性。
要测试分类器效果,在文件中再添加函数datingClassTest,该函数是自包含的,可在任何时候在Python运行环境中使用该函数测试分类器效果,代码如下:
def datingClassTest():
hoRatio = 0.1 #前10%的数据作为测试数据集,后90%数据作为训练数据集
#从文件中读取数据并将其转换为归一化特征值
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
#计算测试向量的数量,决定了normMat向量中哪些数据用于测试,哪些数据用于分类器的训练样本
m = normMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
#将测试数据和训练数据输入到分类器中
#normMat[i,:] 取出第i行的所有数据
#normMat[numTestVecs:m,:]取出numTestVecs之后到m的每行数据
#datingLabels[numTestVecs:m]取出numTestVecs之后到m的每行的标签
#k值为3
classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:],datingLabels[numTestVecs:m], 3)
print("the classifier came back with: %d, the real answer is: %d" %(classifierResult, datingLabels[i]))
#如果错误不一致,则错误数加1
if(classifierResult != datingLabels[i]):errorCount += 1.0
#计算出错误率并输出结果
print("the total error rate is:%f"%(errorCount/float(numTestVecs)))
cmd,错误率为5%:
>>> import kNN
>>> from numpy import *
>>> kNN.datingClassTest()
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 3, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 1
the total error rate is:0.050000
其中,若改变hoRatio的值为0.01,也就是前1%的数据作为测试数据集,后99%的数据作为训练数据集,可看到,测试数据样本为10个,同时错误率也变为了0%:
>>> import kNN
>>> from numpy import *
>>> kNN.datingClassTest()
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 2, the real answer is: 2
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 3, the real answer is: 3
the classifier came back with: 1, the real answer is: 1
the classifier came back with: 3, the real answer is: 3
the total error rate is:0.000000
若hoRation改为0.2,也就是前20%数据作为测试数据集,后80%作为样本数据集,则可看到错误率为8%。
最后使用该算法来判断是否喜欢一个人:直接在PyCharm IDE中运行了,不再cmd中去运行了,再添加如下代码:
def classifyPerson():
#输出结果
resultList = ['不喜欢','有些喜欢','非常喜欢']
#三维特征用户输入
precentTats = float(input("玩视频游戏所耗时间百分比:"))
ffMiles = float(input("每年获得的飞行常客里程数:"))
iceCream = float(input("每周消费的冰激淋公升数:"))
#打开的文件名
filename = "datingTestSet.txt"
#打开并处理数据
datingDataMat, datingLabels = file2matrix(filename)
#训练集归一化
normMat, ranges, minVals = autoNorm(datingDataMat)
#生成NumPy数组,测试集
inArr = np.array([ffMiles, precentTats, iceCream])
#测试集归一化
norminArr = (inArr - minVals) / ranges
#返回分类结果
classifierResult = classify0(norminArr, normMat, datingLabels, 3)
#打印结果
print("你可能%s这个人" % (resultList[classifierResult-1]))
if __name__ == '__main__':
# datingClassTest()
classifyPerson()
直接Run kNN.py,在Console区域输出结果如下:
玩视频游戏所耗时间百分比:10
每年获得的飞行常客里程数:10000
每周消费的冰激淋公升数:0.5
你可能有些喜欢这个人
到此,约会网站的算法已基本完成,之后只要具有某个人的三个特征值:玩视频游戏所耗时间百分比,每年获得的飞行常客里程数,每周消费的冰激凌公升数;然后将它们输入该算法,即可判断出对此人的喜欢程度。该模型所谓的喜欢程度的判断标准完全基于之前的样本训练数据,因此这个数据选择的前提也很重要。
整个完整代码如下:
from numpy import *
import numpy as np
import operator
#分类器,k-近邻算法
def classify0(inX, dataSet, labels, k):
#求出样本集的行数,也就是labels标签的数目
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()
#对距离最小的k个点统计对应的样本标签
classCount={}
for i in range(k): #选择距离最小的k个点
#取第i+1邻近的样本对应的类别标签
voteIlabel = labels[sortedDistIndicies[i]]
#以标签为key,标签出现的次数为value将统计到的标签及出现次数写进字典
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#对字典按value从大到小排序
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True) #排序
#返回排序后字典中最大value对应的key
return sortedClassCount[0][0]
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
#解析文本记录
def file2matrix(filename):
#打开文件
fr = open(filename)
#得到文件的行数
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
#创建以零填充的矩阵,为了简化处理,将该矩阵的另一维度设置为固定值3,可按照自己的实际需求增加相应的代码以适应变化的输入值
returnMat = zeros((numberOfLines, 3))
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip()#截取掉所有的回车字符
listFromLine = line.split('\t') #然后使用tab字符\t将上一步得到的整行数据分割成一个元素列表
returnMat[index,:] = listFromLine[0:3] #选取前3个元素,将它们存储到特征矩阵中
if(listFromLine[-1] == 'largeDoses'): #Python语言可使用索引值-1表示列表中的最后一列元素,利用该负索引,可方便地将列表的最后一列存储到向量classLabelVector中
classLabelVector.append(3)# listFromLine[-1] = '3'
elif (listFromLine[-1] == 'smallDoses'):
classLabelVector.append(2)#listFromLine[-1] = '2'
else:
classLabelVector.append(1)#listFromLine[-1] = '1'
# classLabelVector.append(float(listFromLine[-1]))#必须明确地通知解释器,告诉它列表中存储的元素值为整型,否则Python语言会将这些元素当做字符串处理
index += 1
return returnMat,classLabelVector
#归一化特征值
def autoNorm(dataSet):
minVals = dataSet.min(0) #每列最小值
maxVals = dataSet.max(0) #每列最大值
ranges = maxVals - minVals #函数计算可能的取值范围
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - np.tile(minVals, (m,1)) #tile将变量内容复制成输入矩阵同样大小的矩阵
normDataSet = normDataSet/np.tile(ranges, (m,1)) #特征值相除,为了归一化特征值,必须使用当前值减去最小值,然后除以取值范围;在某些数值处理软件包,/可能意味着矩阵除法,NumPy库中,
return normDataSet,ranges,minVals #矩阵除法需要使用函数linalg.solve(matA,matB)
#测试算法:作为完整程序验证分类器
def datingClassTest():
hoRatio = 0.1
#从文件中读取数据并将其转换为归一化特征值
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
#计算测试向量的数量,决定了normMat向量中哪些数据用于测试,哪些数据用于分类器的训练样本
m = normMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
#将测试数据和训练数据输入到分类器中
#normMat[i,:] 取出第i行的所有数据
#normMat[numTestVecs:m,:]取出numTestVecs之后到m的每行数据
#datingLabels[numTestVecs:m]取出numTestVecs之后到m的每行的标签
#k值为3
classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:],datingLabels[numTestVecs:m], 3)
print("the classifier came back with: %d, the real answer is: %d" %(classifierResult, datingLabels[i]))
#如果错误不一致,则错误数加1
if(classifierResult != datingLabels[i]):errorCount += 1.0
#计算出错误率并输出结果
print("the total error rate is:%f"%(errorCount/float(numTestVecs)))
#预测函数:
"""
def classifyPerson():
resultList = ['不喜欢', '有点喜欢', '非常喜欢']
percentTats = float(input("玩视频游戏所耗时间百分比: "))
ffMiles = float(input("每年获得飞行常客里程数: "))
iceCream = float(input("每周消费冰激凌公升数: "))
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
inArr = np.array([percentTats, ffMiles, iceCream])
norminArr = (inArr - minVals) / ranges
classifierResult = classify0(norminArr, normMat, datingLabels, 3)
print("你可能对这个人:", resultList[classifierResult-1])
"""
def classifyPerson():
#输出结果
resultList = ['不喜欢','有些喜欢','非常喜欢']
#三维特征用户输入
precentTats = float(input("玩视频游戏所耗时间百分比:"))
ffMiles = float(input("每年获得的飞行常客里程数:"))
iceCream = float(input("每周消费的冰激淋公升数:"))
#打开的文件名
filename = "datingTestSet.txt"
#打开并处理数据
datingDataMat, datingLabels = file2matrix(filename)
#训练集归一化
normMat, ranges, minVals = autoNorm(datingDataMat)
#生成NumPy数组,测试集
inArr = np.array([ffMiles, precentTats, iceCream])
#测试集归一化
norminArr = (inArr - minVals) / ranges
#返回分类结果
classifierResult = classify0(norminArr, normMat, datingLabels, 3)
#打印结果
print("你可能%s这个人" % (resultList[classifierResult-1]))
if __name__ == '__main__':
# datingClassTest()
classifyPerson()
手写识别系统:
from numpy import *
import numpy as np
import operator
from os import listdir
import time
#分类器,k-近邻算法
def classify0(inX, dataSet, labels, k):
#求出样本集的行数,也就是labels标签的数目
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()
#对距离最小的k个点统计对应的样本标签
classCount={}
for i in range(k): #选择距离最小的k个点
#取第i+1邻近的样本对应的类别标签
voteIlabel = labels[sortedDistIndicies[i]]
#以标签为key,标签出现的次数为value将统计到的标签及出现次数写进字典
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#对字典按value从大到小排序
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1), reverse=True) #排序
#返回排序后字典中最大value对应的key
return sortedClassCount[0][0]
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
#解析文本记录
def file2matrix(filename):
#打开文件
fr = open(filename)
#得到文件的行数
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
#创建以零填充的矩阵,为了简化处理,将该矩阵的另一维度设置为固定值3,可按照自己的实际需求增加相应的代码以适应变化的输入值
returnMat = zeros((numberOfLines, 3))
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip()#截取掉所有的回车字符
listFromLine = line.split('\t') #然后使用tab字符\t将上一步得到的整行数据分割成一个元素列表
returnMat[index,:] = listFromLine[0:3] #选取前3个元素,将它们存储到特征矩阵中
if(listFromLine[-1] == 'largeDoses'): #Python语言可使用索引值-1表示列表中的最后一列元素,利用该负索引,可方便地将列表的最后一列存储到向量classLabelVector中
classLabelVector.append(3)# listFromLine[-1] = '3'
elif (listFromLine[-1] == 'smallDoses'):
classLabelVector.append(2)#listFromLine[-1] = '2'
else:
classLabelVector.append(1)#listFromLine[-1] = '1'
# classLabelVector.append(float(listFromLine[-1]))#必须明确地通知解释器,告诉它列表中存储的元素值为整型,否则Python语言会将这些元素当做字符串处理
index += 1
return returnMat,classLabelVector
#归一化特征值
def autoNorm(dataSet):
minVals = dataSet.min(0) #每列最小值
maxVals = dataSet.max(0) #每列最大值
ranges = maxVals - minVals #函数计算可能的取值范围
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - np.tile(minVals, (m,1)) #tile将变量内容复制成输入矩阵同样大小的矩阵
normDataSet = normDataSet/np.tile(ranges, (m,1)) #特征值相除,为了归一化特征值,必须使用当前值减去最小值,然后除以取值范围;在某些数值处理软件包,/可能意味着矩阵除法,NumPy库中,
return normDataSet,ranges,minVals #矩阵除法需要使用函数linalg.solve(matA,matB)
#测试算法:作为完整程序验证分类器
def datingClassTest():
hoRatio = 0.1
#从文件中读取数据并将其转换为归一化特征值
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
#计算测试向量的数量,决定了normMat向量中哪些数据用于测试,哪些数据用于分类器的训练样本
m = normMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
#将测试数据和训练数据输入到分类器中
#normMat[i,:] 取出第i行的所有数据
#normMat[numTestVecs:m,:]取出numTestVecs之后到m的每行数据
#datingLabels[numTestVecs:m]取出numTestVecs之后到m的每行的标签
#k值为3
classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:],datingLabels[numTestVecs:m], 3)
print("the classifier came back with: %d, the real answer is: %d" %(classifierResult, datingLabels[i]))
#如果错误不一致,则错误数加1
if(classifierResult != datingLabels[i]):errorCount += 1.0
#计算出错误率并输出结果
print("the total error rate is:%f"%(errorCount/float(numTestVecs)))
#预测函数:
"""
def classifyPerson():
resultList = ['不喜欢', '有点喜欢', '非常喜欢']
percentTats = float(input("玩视频游戏所耗时间百分比: "))
ffMiles = float(input("每年获得飞行常客里程数: "))
iceCream = float(input("每周消费冰激凌公升数: "))
datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
inArr = np.array([percentTats, ffMiles, iceCream])
norminArr = (inArr - minVals) / ranges
classifierResult = classify0(norminArr, normMat, datingLabels, 3)
print("你可能对这个人:", resultList[classifierResult-1])
"""
def classifyPerson():
#输出结果
resultList = ['不喜欢','有些喜欢','非常喜欢']
#三维特征用户输入
precentTats = float(input("玩视频游戏所耗时间百分比:"))
ffMiles = float(input("每年获得的飞行常客里程数:"))
iceCream = float(input("每周消费的冰激淋公升数:"))
#打开的文件名
filename = "datingTestSet.txt"
#打开并处理数据
datingDataMat, datingLabels = file2matrix(filename)
#训练集归一化
normMat, ranges, minVals = autoNorm(datingDataMat)
#生成NumPy数组,测试集
inArr = np.array([ffMiles, precentTats, iceCream])
#测试集归一化
norminArr = (inArr - minVals) / ranges
#返回分类结果
classifierResult = classify0(norminArr, normMat, datingLabels, 3)
#打印结果
print("你可能%s这个人" % (resultList[classifierResult-1]))
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
#把一个32x32的二进制图像矩阵转换为1x1024的向量,就可使用前面的分类器处理数字图像信息。
def handwritingClassTest():
cpu_start = time.time()
print('start:%f' % cpu_start)
hwLabels = []
#获取目录内容
trainingFileList = listdir('trainingDigits')
m = len(trainingFileList)
trainingMat = 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,:] = img2vector('trainingDigits/%s' % fileNameStr)
testFileList = listdir('testDigits')
errorCount = 0.0
mTest = len(testFileList)
#循环读出文件的前32个字符值存储在NumPy数组中,最后返回数组
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileNameStr.split('_')[0])
vectorUnderTest = img2vector('testDigits/%s' %fileNameStr)
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
print("分类器返回值为:%d, 实际值为:%d" %(classifierResult, classNumStr))
if(classifierResult != classNumStr): errorCount += 1.0
print("\n错误总数为: %d" % errorCount)
print("\n总错误率为: %f" %(errorCount/float(mTest)))
cpu_end = time.time()
print('end:%f' % cpu_end)
print("total time: %f S" % (cpu_end - cpu_start))
if __name__ == '__main__':
# datingClassTest()
#classifyPerson()
# testVector=img2vector('testDigits/0_13.txt')
#print(testVector[0,0:31])
# print(testVector[0,32:63])
handwritingClassTest()
运行结果类似如下:
分类器返回值为:9, 实际值为:9
分类器返回值为:9, 实际值为:9
分类器返回值为:9, 实际值为:9
错误总数为: 10
总错误率为: 0.010571
end:1516780726.425686
total time: 33.872470 S
错误个数10个,错误率1.05%,耗时太长33.8s,这个还可以用其他方法改进。修改变量k值,函数handwritingClassTest随机选取训练样本、改变训练样本的数目,都会对k-近邻算法的错误率产生影响。
执行效率不高,k决策树就是k-近邻算法的优化版,可节省大量的计算开销。
datingTestSet.txt如下:
第1列为每年获得的飞行常客里程数,第2列为玩视频游戏所耗时间百分比,第3列为每周消费的冰淇淋公升数,第4列为目标值:非常喜欢-largeDoses 有点喜欢-smallDoses 不喜欢-didntLike:
40920 8.326976 0.953952 largeDoses
14488 7.153469 1.673904 smallDoses
26052 1.441871 0.805124 didntLike
75136 13.147394 0.428964 didntLike
38344 1.669788 0.134296 didntLike
总结:k-近邻算法是分类数据最简单最有效的算法。本篇通过约会问题和手写识别系统使用k-近邻算法构造的分类器。k-近邻算法是基于实例的学习,使用算法时需要有接近实际数据的训练样本数据。k-近邻算法必须保存全部数据集,若训练数据集越大,则使用的存储空间也会越大,此外,由于必须对数据集中的每个数据计算距离值,实际使用时会非常耗时,由上面的手写识别系统可知。
k-近邻算法的另一个缺陷是它无法给出如何数据的基础结构信息,因此也无法知晓平均实例样本和典型是两样本具有什么特征。在使用概率测量方法处理分类问题时,可解决该问题。