原理:如果一个样本在特征空间中的K个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性,KNN方法在类别决策上仅仅依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。
图解: (其中“ ” 代表A类别,“
” 代表B类别 ,“
”代表待判断的样本)
通过计算 “ ” 与 “
和 “
”各点的距离(
),选择距离 “
”最邻近的一个或者几个样本(也就是 “
和 “
”)然后根据这些样本的类别来决定待判断的样本属于 A类别 还是 B类别 ,通俗点说就是假如我们选择了距离 “
”最近的3个样本(解释:在编写程序时,要选取距离最近的样本的个数可以自己设计 eg: k=3),其中2个是 “
”,1个是 “
”,由于 “
”是属A类别,所以判断 “
”也属于A类,即 “ 少数服从多数 ”
【典例】利用K-最近邻法来进行简单的电影分类
1.将 电影中的 “ 爱情片 ” 和 “ 动作片 ” 通过 接吻次数 和 打斗次数 这两个特征,创建一个二维特征矩阵[1,100],其中 1 代表接吻次数 ,100 代表打斗次数,给它打上 “ 动作片 ”的标签, 给[[3, 104], [2, 100], [1, 81], [100, 10], [99, 5], [98, 2]] 也同理打上标签, ['爱情片','爱情片','爱情片','动作片','动作片','动作片'],具体python实现:
# -*- coding: UTF-8 -*-
'''
使用numpy实现简单的kNN算法,电影分类
'''
import numpy as np
import operator
"""
函数说明:创建数据集
"""
def createDataSet():
# 4组二维特征
group = np.array([[3, 104], [2, 100], [1, 81], [100, 10], [99, 5], [98, 2]])
# 4组特征的标签
labels = ['爱情片','爱情片','爱情片','动作片','动作片','动作片']
return group, labels
"""
函数说明:kNN算法,分类器
"""
#用于分类的输入向量是inX;输入的训练样本集为dataSet;标签向量为labels;参数k表示用于选择最近邻居的数目,其中labels向量的元素数目与矩阵dataSet的行数相同,通过矩阵运算来计算和每个样本之间的距离
def class1(inX, dataSet, labels, k):
# numpy函数shape[0]返回dataSet的行数,求矩阵dataset的第一维度(列)的长度
dataSetSize = dataSet.shape[0]
print(dataSetSize)
# 在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向),title(对象矩阵,([m,] k )):title函数是个复制函数,作用是将对象矩阵作为一个单元进行横向和纵向复制,形成一个m*k的矩阵。注意的是,当()只有一个数值时,只会横向复制
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
print(diffMat)
# 二维特征相减后平方
sqDiffMat = diffMat**2
# sum()所有元素相加,sum(0)列相加,sum(1)行相加
sqDistances = sqDiffMat.sum(axis=1)
print(sqDistances )
# 开方,计算出距离
distances = sqDistances**0.5
# 返回distances中元素从小到大排序后的索引值
sortedDistIndices = distances.argsort()
print(sortedDistIndices)
# 定一个记录类别次数的字典
classCount = {}
for i in range(k):#k是我们自己定的
# 取出前k个元素的类别
voteIlabel = labels[sortedDistIndices[i]]
print(voteIlabel)
# dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
# 计算类别次数
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
print(classCount[voteIlabel] )
# key=operator.itemgetter(1)根据字典的值进行排序
# key=operator.itemgetter(0)根据字典的键进行排序
# reverse降序排序字典
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)#sorted这里的意思是:classCount.items()将classCount字典分解为元组列表
print(sortedClassCount )
# 返回次数最多的类别,即所要分类的类别
return sortedClassCount[0][0]
if __name__ == '__main__':
# 创建数据集
group, labels = createDataSet()
print("group = \n",group,"\nlabels = \n", labels)
# 测试集
test = [18,90]
# kNN分类
test_class = class1(test, group, labels, 3)
# 打印分类结果
print(test_class)
对其中classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 操作进行详细解释如下:
1.对classout字典进行初始化classCount = {}时,此时输入classCount,输出为: {},在循环体中第一次遇到votellabel时,将新的votellabel添加到字典classCount,并初始化其对应数值为0
2.再对其进行+1操作,证明该label已经出现过一次,此时输入classCount,输出为: {voteIlabel:1},在第二次遇到相同votellabel时,classCount.get(voteIlabel,0)返回对应的数值(因为已经对votellable初始化过了并且给它赋了一个新的值),然后继续执行+1操作,此时输入classCount,输出为: {voteIlabel:2}
结果:
大家如果有不理解的可以挨个输出,便于理解。