冒泡~又是新的一周辣!:)今天又学习了之前简要扫过的K近邻算法(KNN)。
K近邻算法(KNN)
原理:
首先这是是一种常用于分类的算法,该方法的基本思路是:如果一个待分类样本在特征空间中的k个最相似(即特征空间中K近邻)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
其实也可以通俗地理解为古话常说的近朱者赤,近墨者黑。
下面通过例子的直观图解释:
如上图:所有样本可以使用一个二维向量表征。
蓝色方形样本和红色三角形样本为已知分类样本。若使用KNN对图中绿色圆形未知分类样本进行分类,当K=3时,其三近邻中有2个红色三角形样本和1个蓝色方形样本,因此预测该待分类样本为红色三角形样本;当K=5时,其三近邻中有3个红色三角形样本和2个蓝色方形样本,因此预测该待分类样本为蓝色方形样本。
算法要素:
通过上图解释可以得到KNN算法的几个重要要素:
a.数据集:存在一个样本数据集,也称作训练集,样本数据集中每个样本是有标签的,即我们知道样本数据集中每一个样本的分类。当获取到一个没有标签的待分类样本时,将待分类样本与样本数据集中的每一个样本进行比较,找到与当前样本最为相近的K个样本,并获取这K歌样本的标签,最后,选择这k个样本标签中中出现次数最多的分类,作为待分类样本的分类。
b.样本的向量表示:即不管是当前已知的样本数据集,还是将来可能出现的待分类样本,都必须可以用向量的形式加以表征。向量的每一个维度,刻画样本的一个特征,必须是量化的,可比较的。
c.样本间距离的计算方法:要找到待分类样本在当前样本数据集中与自己距离最近的K个邻居,必然就要确定样本间的距离计算方法。样本间距离的计算方法的构建,与样本的向量表示方法有关,当建立样本的向量表示方法时,必须考虑其是否便于样本间距离的计算。常用的距离计算方法有:欧氏距离、余弦距离、汉明距离、曼哈顿距离等等。
d.K值的选取:K是一个自定义的常量。K值的选取会影响待分类样本的分类结果,会影响算法的偏差与方差。
接下来举个简单的例子,来解释算法的过程。
我们可以通过打斗的镜头和接吻的镜头出现的次数,来判断这部电影是属于爱情片还是动作片,因此我们可以把打斗的次数和接吻的次数,设成一个二维变量(x,y),
在坐标图上表示:
那么通过坐标图直观,我们可以计算待分类的样本的坐标和这些已经有标签的样本(也就是已经是什么类别的电影)的坐标之间的距离(采用欧氏距离法),然后对得到的距离进行排序,离得越近就是那个类别的电影了。
通过计算可知,红色圆点标记的电影到动作片 (108,5)的距离最近,为16.55。如果算法直接根据这个结果,判断该红色圆点标记的电影为动作片。
算法过程
a.计算已知类别数据集中的点与当前点之间的距离;
b.按照距离递增次序排序;
c.选取与当前点距离最小的k个点;
d.确定前k个点所在类别的出现频率;
e.返回前k个点所出现频率最高的类别作为当前点的预测分类。
比如,接上图的那个电影的例子,现在我这个k值取3,那么在电影例子中,按距离依次排序的三个点分别是动作片(108,5)、动作片(115,8)、爱情片(5,89)。在这三个点中,动作片出现的频率为三分之二,爱情片出现的频率为三分之一,所以该红色圆点标记的电影为动作片。
Python实现
(实现刚刚上面提到的电影的例子)
import numpy as np
import operator
"""
函数说明:创建数据集
Parameters: 无
Returns:
group - 数据集
labels - 分类标签
"""
def createDataSet():
#四组二维特征
group = np.array([[1,101],[5,89],[108,5],[115,8]])
#四组特征的标签
labels = ['爱情片','爱情片','动作片','动作片']
return group, labels
"""
函数说明:kNN算法,分类器
Parameters:
inX - 用于分类的数据(测试集)
dataSet - 用于训练的数据(训练集)
labes - 分类标签
k - kNN算法参数,选择距离最小的k个点
Returns:
sortedClassCount[0][0] - 分类结果
"""
def classify(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]
if __name__ == '__main__':
#创建数据集
group, labels = createDataSet()
print(group)
#测试集
a = [101,20]
b = [1,90]
#kNN分类
c = classify(a, group, labels, 3)
d = classify(b, group, labels, 4)
#打印分类结果
print(c)
print(d)·
结果:
Matlab实现
trainData = [1,101;5,89;108,5;115,8];
trainClass = [1,1,2,2];
testData = [101,20];
testData = [1,90];
k = 3;
%% distance
row = size(trainData,1);
col = size(trainData,2);
test = repmat(testData,row,1);
dis = zeros(1,row);
for i = 1:row
diff = 0;
for j = 1:col
diff = diff + (test(i,j) - trainData(i,j)).^2;
end
dis(1,i) = diff.^0.5;
end
%% sort
jointDis = [dis;trainClass];
sortDis= sortrows(jointDis');
sortDisClass = sortDis';
%% find
class = sort(2:1:k);
member = unique(class);
num = size(member);
max = 0;
for i = 1:num
count = find(class == member(i));
if count > max
max = count;
label = member(i);
end
end
disp('分类结果为:');
fprintf('%d\n',label)
结果:
参考资料:(http://cuijiahua.com/blog/2017/11/ml_1_knn.html)
(https://blog.csdn.net/wangmumu321/article/details/78576916)
Ending~你今天学习了吗?