(一)算法基本思想
KNNKNNKNN是一种常用的监督学习方法。给定测试样本,基于某种距离度量找出训练集中与其最靠近的kkk个训练样本,然后基于这kkk个邻居的信息来进行预测。使用投票法,选择kkk个样本中出现最多的类别标记作为预测结果,这kkk个实例的多数属于某类,就把该输入实例分为哪类。也可以基于距离远近进行加权平均,距离越近样本权重越大。
kkk值的选择、距离的度量、以及分类决策规则是kkk邻近算法三个基本要素。
(二)算法实现
输入:训练数据集T=[(x1,y1),(x2,y2),...,(xn,yn)]T={[(x_1,y_1),(x_2,y_2),...,(x_n,y_n)]}T=[(x1,y1),(x2,y2),...,(xn,yn)],xix_ixi为特征向量,yiy_iyi为实例类别。
输出:实例xxx所属的类别yyy。
(1)
根据给定的距离度量,在训练集TTT中找出与xxx最邻近的kkk个点;
(2)
在kkk个点中进行分类表决,少数服从多数原则,决定xxx的类别yyy。y=argmax∑xI(yi=cj)y=argmax\sum_xI(y_i=c_j)y=argmax∑xI(yi=cj),选择相应数目最多的类别。当k=1k=1k=1时,称为最邻近算法。
kkk邻近模型
(1)模型
当训练集、距离度量、kkk值以及分类决策规则确定后,对于任何一个新的输入实例,它所属的类唯一的确定。
(2)距离度量
使用的距离一般是欧式距离。Lp=(∑l=1n∣xi(l)−xj(l)∣p)1pL_p={(\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|^p)^{\frac{1}{p}}}Lp=(∑l=1n∣xi(l)−xj(l)∣p)p1;当p=2p=2p=2时,为欧式距离。当p=1p=1p=1时,为曼哈顿距离。
(3)kkk值的选择
kkk值较小,学习的近似误差会减小,估计误差会增大,意味着整体模型变得更复杂,容易发生过拟合;若kkk值较大,可以减少学习的估计误差,但近似误差会变大,但意味着整体的模型变得简单。在实际中,kkk值一般选择一个比较小的数值。
(4) 分类决策规则
分类误差率为:1k∑xI(yi<>cj)=1−1k∑xI(yi=cj)\frac{1}{k}\sum_x{I(y_i<>c_j)=1-\frac{1}{k}}\sum_x{I(y_i=c_j)}k1∑xI(yi<>cj)=1−k1∑xI(yi=cj)
要使分类误差率最小,即经验风险最小,就要使∑xI(yi=cj)\sum_xI(y_i=c_j)∑xI(yi=cj)最大,所以多数表决规则等价于经验风险最小化。
kkk邻近代码实现
# -*- coding: UTF-8 -*-
import numpy as np
import operator
from os import listdir
global nums
def classify0(inX, dataSet, labels, k):
#numpy函数shape[0]返回dataSet的行数
dataSetSize = dataSet.shape[0]
#在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
#二维特征相减后平方
sqDiffMat = diffMat**2
#sum()所有元素相加,sum(0)列相加,sum(1)行相加
sqDistances = sqDiffMat.sum(axis=1)
#开方,计算出距离
distances = sqDistances**0.5
#返回distances中元素从小到大排序后的索引值
sortedDistIndices = distances.argsort()
#定一个记录类别次数的字典
classCount = {}
for i in range(k):
#取出前k个元素的类别
voteIlabel = labels[sortedDistIndices[i]]
#dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
#计算类别次数
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#python3中用items()替换python2中的iteritems()
#key=operator.itemgetter(1)根据字典的值进行排序
#key=operator.itemgetter(0)根据字典的键进行排序
#reverse降序排序字典
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#返回次数最多的类别,即所要分类的类别
return sortedClassCount[0][0]
def file2vector(filename):
fr = open(filename)
arrayOLines=fr.readlines()
numberOfLines=len(arrayOLines)
nums=numberOfLines
returnMat=np.zeros((numberOfLines,16))
classLabelVector=[]
index=0
for line in arrayOLines:
line=line.strip()
listFromLine=line.split(',')
returnMat[index,:]=listFromLine[1:17]
classLabelVector.append(listFromLine[0])
index+=1
return returnMat,classLabelVector
def alphabetClassTest():
trainingMat,alLabels=file2vector("trainingData.txt")
errorCount = 0.0
with open("testingData.txt",'r',encoding='utf-8') as f:
arrays=f.readline()
while arrays:
testMat=np.zeros((1,16))
teLabels=[]
inde=0
l=arrays.strip()
llist=l.split(',')
for i in range(1,17):
testMat[0,inde]=llist[i]
inde+=1
teLabels.append(llist[0])
classifierResult = classify0(testMat, trainingMat, alLabels, 5)
print("分类返回结果为%s\t真实结果为%s" % (classifierResult, teLabels[0]))
if(classifierResult != teLabels[0]):
errorCount += 1.0
arrays=f.readline()
print("总共4000,错了%d个数据\n正确率为%f%%" % (errorCount, (1-(errorCount)/4000)*100))
if __name__ == '__main__':
alphabetClassTest()