前言
K近邻算法通常写作Knn算法,它是一种有监督的分类算法。Knn算法与K-means算法不同,K-means算法是是无监督的聚类算法。今天我就用iris的数据和大家聊聊什么是Knn
图为iris部分数据,其中蓝色表示setosa、绿色表示virginica,红色表示我们需要预测的点
正文
Knn原理
通俗的讲,就是找与自己特征距离最近的k个点,根据k个点中出现类别最多的,作为其预测分类。
假如我们先不考虑用什么度量距离,从图可以直观的看到,当K=3时,我们的预测的点是virginica类别。
那么K值取大一点可以吗,答案是可以的。K值与分类的错误率变化曲线是先下降后上升的。在很多资料中提到,通常K是不大于20的整数,这应该是由预测类别数和样本总数预估的结果。
距离度量
计算距离其实就是度量特征的相似性,计算方法有:欧式距离、曼哈顿距离、马氏距离、余弦相似度、汉明距离
Knn常用是欧式距离,在二维平面上其表达式为
其在三维空间上的表达式为
由此,我们可以扩展得到更高维的计算表达式。那么我们可以想到Knn暴力解决方法是遍历计算所有点与预测点的距离,最终选取K个值,作为分类参考。
整理一下Knn算法的实现逻辑:
- 计算已知类别数据集中的点与当前点之间的距离;
- 按照距离递增次序排序;
- 选取与当前点距离最小的k个点;
- 确定前k个点所在类别的出现频率;
- 返回前k个点出现频率最高的类别作为当前点的预测分类。
代码实现
代码运行环境:python 3.5.2
1、自定义函数实现
# -*- coding: UTF-8 -*-
import pandas as pd
import numpy as np
import operator
iris = pd.read_csv('F:\ML\iris.data',header = None)
iris.columns=['sepal_lengh_cm','sepal_width_cm','petal_length_cm','petal_width_cm','class']
seto = iris.iloc[0:50,:]
virg = iris.iloc[100:150,:]
data = pd.concat([seto,virg]).reset_index(drop=True)
labels = data['class']
data = data[['sepal_lengh_cm','sepal_width_cm']]
def classify_knn(pred, trainData, labels, k):
"""
@Function: knn分类
@Args: pred:用于分类的输入向量 (1xN)
trainData:输入的训练样本集 (NxM)
labels:标签向量 (1xM vector)
k:用于比较的近邻数量 (should be an odd number)
@Returns: sortedClassCount[0][0]:分类结果
"""
trainDataSize = trainData.shape[0]
diffMat = np.tile(pred, (trainDataSize, 1)) - trainData
#对求差后的矩阵求平方
sqDiffMat = diffMat**2
#对矩阵的每一行求和
sqDistances = sqDiffMat.sum(axis=1)
#求矩阵的和的平方根
distances = sqDistances**0.5
#对上式结果进行排序,argsort函数返回的是:数组值从小到大的索引值
sortedDistIndicies = distances.argsort()
#创建字典
classCount = {}
#给字典赋值
for i in range(k):
#字典的key
voteIlabel = labels[sortedDistIndicies[i]]
#classCount.get(voteIlabel,0):如果字典键的voteIlabel有值就返回值,无值则返回0,
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#对classCount进行排序,默认的sorted是升序,所以加入reverse=True
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
#返回分类结果
return sortedClassCount[0][0]
---------------------------------------------------------------------------------
Input:
classify_knn([6.0,3.5], data, labels, 3)
Output: Iris-virginica
2、sklearn方法调用
import pandas as pd
import numpy as np
from sklearn import neighbors
iris = pd.read_csv('F:\ML\iris.data',header = None)
iris.columns=['sepal_lengh_cm','sepal_width_cm','petal_length_cm','petal_width_cm','class']
seto = iris.iloc[0:50,:]
vers = iris.iloc[50:100,:]
virg = iris.iloc[100:150,:]
data = pd.concat([seto,virg]).reset_index(drop=True)
labels = data['class']
data = data[['sepal_lengh_cm','sepal_width_cm']]
classify_knn = neighbors.KNeighborsClassifier(3)
classify_knn.fit(data, labels)
result = classify_knn.predict([[6.0,3.5]])
print(result)
-----------------------------------------------------------------
Output:
['Iris-virginica']
小结
优点:精度高、对异常值不敏感、无数据输入假定
缺点:由于所有的训练数据都会在内存中计算,所以时间复杂度高、空间复杂度
由本次练习得出,学习scikit-learn也能提高自己编码的效率,后面会对scikit-learn做一个专题的学习分享