KNN:分类算法
目标:
对未知类别的样本进行分类预测
步骤:
1.对于某个未知类别样本,根据距离度量计算每个已知类别样本与其距离。
2.选出K个与该未知类别样本距离最小的已知类别的样本。
3.在K个已知类别样本里得到频数最多的类别,该类别就是未知类别样本的预测。
KNN算法:
# 寻找K值 1~60 打印误差最小的K值和对应的误差个数及误差率
from numpy import *
import operator # 引入运算符模块,K邻近算法会使用到其中的函数
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 classifyO(inX, dataSet, labels, k): # KNN算法
# 用于分类的输入向量是inX,
# 输入的训练样本集为dataSet,标签向量为labe1s,最后的参数k表示用于选择最近邻居的数目,
# 其中标签向量的元素数目和矩阵dataset的行数相同。
dataSetSize = dataSet.shape[0] # shape[0]就是读取矩阵第一维度的长度。
diffMat = tile(inX, (dataSetSize, 1)) - dataSet
#根据公式可推测出计算差值
# 把inx行列重复copy(dataSetSize,1)次。
# inX是个向量,而dataset是个矩阵,两者之间要进行相减的运算,需要把这个向量也补成一个和dataset有相同行数列数的矩阵,
# tile()的第二个参数,也就是(datasetsize,1),这个参数的意思就是把inX补成有datasetsize行数的矩阵。
# 然后和dataset相减就是根据矩阵的减法进行的。因此diffMat是一个差值矩阵。
sqDiffMat = diffMat ** 2 # 欧氏距离计算公式
sqDistances = sqDiffMat.sum(axis=1) # axis这个参数,它决定对矩阵求和时候的顺序,axis=0是按照行求和,axis=1是按照列进行求和。
distances = sqDistances ** 0.5 # 0.5次方即开根号
sortedDisIndicies = distances.argsort() # 就是把向量中每个元素进行排序,而它的结果是元素的索引(原来的下标)形成的向量。
classCount = {} # 生成字典,对classCount元素赋值,其实是个计数器
# 选择距离待分类点最小的k个点
for i in range(k): # 遍历k,寻找该样本标签的类型
voteIlabel = labels[sortedDisIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
# python3字典的 items() 方法,以列表的形式返回可遍历的(键,值)元组数组。
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
# 内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作与sort方法不一样。
# reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
# sorted函数返回类型为list
return sortedClassCount[0][0] # 返回前k个点出现频率最高的类别作为当前点的预测分类。即最符合的标签
# 文件导入
def file2matrix(filename):
fr = open(filename)
# 得到文件行数
arrayOLines = fr.readlines()
# 创建返回的NumPy矩阵
numberOfLines = len(arrayOLines)
returnMat = zeros((numberOfLines, 3))
# 解析文件数据到列表
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split(' ')
returnMat[index, :] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat, classLabelVector
#散点图代码块 (已注释需要运行出图片解注释即可)
#from numpy import array
#import matplotlib
#import matplotlib.pyplot as plt
#fig = plt.figure()
#ax = fig.add_subplot(111)
#datingDataMat,datingLabels = file2matrix('F:python项目KNNdatingTestSet2.txt')
#ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
#plt.show()
#归一化特征值
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
#将每列的最小值放在变量minvals中,将最大值放在变量maxVals中,
# 其中dataset.min(0)中的参数0使得函数可以从列中选取最小值,而不是选取当前行的最小值。
# 然后,函数计算可能的取值范围,并创建新的返回矩阵。
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - tile(minVals, (m,1))#特征在短阵有1000×3个值,而minvals与raange的值都为1×3。因此利用tile()
#函数将变量内容复制成输入矩阵同样大小的矩阵
normDataSet = normDataSet/tile(ranges, (m,1)) #矩阵除法
return normDataSet, ranges, minVals
#自动化调试模块代码
def Autodebug(k):
minCount = 20 #假如其中有20个误差,结果当然没有这么多,设置这么大也没关系,反正minCount会被覆盖掉
for k in range(1,60):
datingClassTest(k)
#print("the total error rate is : ", datingClassTest(k)[1]) #该条注释用来验证结果
#print("the total error count is : ", datingClassTest(k)[0]) #该条注释用来验证结果
if(datingClassTest(k)[0]<minCount):
#误差最小一定误差数最少,误差数最少,误差率一定最小
minCount=datingClassTest(k)[0]
print("The minErroK's minErroCount= ",minCount)
print("The minErroK's minErroRate=",minCount/100)
for k in range(1,60):
datingClassTest(k)
if datingClassTest(k)[0]==minCount:
print("The nice K= ",datingClassTest(k)[2])
#分类器
def datingClassTest(k):
hoRatio=0.1
datingDataMat,datingLabels=file2matrix('F:python项目KNNdatingTestSet2.txt')
normMat,minVals,ranges=autoNorm(datingDataMat)
m=normMat.shape[0]
errorCount=0.0
numTestVecs=int(m*hoRatio)
for i in range(numTestVecs):
classifierResult=classifyO(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],k) #//K取3时
if (classifierResult!=datingLabels[i]):
errorCount+=1.0
x=errorCount/float(numTestVecs)
return (errorCount,x,k) #稍作修改,方便Autodebug()函数调用,自动化测试,返回误差个数和误差率即可
Autodebug(1) #直接Run即可
#如果k值选择较大的话,距离较远的训练样本也能够对实例预测结果产生影响。
# 这时候,模型相对比较鲁棒,不会因为个别噪声点对最终预测结果产生影响。
# 但是缺点也十分明显:算法的近邻误差会偏大,距离较远的点(与预测实例不相似)也会同样对预测结果产生影响,
# 使得预测结果产生较大偏差,此时模型容易发生欠拟合。
#因此,在实际工程实践中,我们一般采用交叉验证的方式选取k值。
# 通过以上分析可知,一般k值选得比较小,我们会在较小范围内选取k值。
# 同时把测试集上准确率最高的那个确定为最终的算法超参数k。
#虽然K值4与7都满足最小误差率,但k值4作为超参数更好