通过本文,你将了解并深刻理解什么是 KNN算法。
当然,阅读本文前,你最好会点python
,
这样阅读起来才会没有障碍噢
春节后的第一篇文章, 在这里祝大家新的一年工作顺心!心想事成!新年又有新高度!
通常我们都知道这么一句话 “近朱者赤近墨者黑” , KNN算法就是这句话的完美诠释了。
我们想要判断某个东西属于哪个分类, 那么我们只需要找到最接近该东西的 K 个邻居, 这些邻居中哪种分类占比最大, 那么我们就认为该东西就属于这个分类!
这里我们会使用到 sklearn
和 numpy
两个库,
当然就算你不熟悉也没关系,
这里主要就是为了直观的感受一下 KNN 算法
。
sklearn
中自带的测试数据集:鸢尾花数据。 import numpy as np import matplotlib import matplotlib.pyplot as plt from sklearn import datasets #这里我们采集的是 sklearn 包里面自带的 鸢尾花 的数据 digits = datasets.load_iris() # 鸢尾花的种类 用 0,1,2 标识 y = digits.target # 鸢尾花的 特征,为了可视化的需求,我们这里只取前两个特征 x = digits.data[:,:2] # 在2d平面上画出鸢尾花的分布情况 #为了方便显示,我们这里只取标识为 0 和 1 两种鸢尾花的数据 plt.scatter(x[y==0,0],x[y==0,1],color='r') plt.scatter(x[y==1,0],x[y==1,1],color='b') plt.show()
真实鸢尾花分布图.png
通过两幅图的对比, 我们很明显的看到 左下角的一个点预测错误,其余都正确 , 这里我们很直观的就可以感受到 KNN 算法的整个流程, 其中最关键的还是在 预测数据那块, 那么接下来我们就来剖析下 KNN 的原理吧
欧拉距离
公式计算, 如下:
当然,真正要写好 KNN算法 肯定不是我们考虑的这么简单, 但是主要思路是这样, 所以我们根据这个思路先来把简单的 KNN 实现一下吧。
from math import sqrt
from collections import Counter
class MyKNN:
# 初始化
def __init__(self,n_neighbors=5):
self.n_neighbors = n_neighbors
self.X = None
self.Y = None
def fit(self,x,y):
"""
KNN 算是一个比较特殊的算法,其实它是没有一个训练过程的,
这里简单的将训练数据保存起来就好了
"""
self.X = x
self.Y = y
def _predict(self,x):
"""
预测单个样本的所属分类
"""
# 欧拉距离的计算
distances = [sqrt(np.sum((i-x)**2)) for i in self.X]
# 排序
sort_distances_index = np.argsort(distances)
# 找出最近的 n_neighbors 个邻居
neighbors_index = sort_distances_index[:self.n_neighbors]
neighbors = self.Y[neighbors_index]
# 返回最近邻居中分类占比最大的那个分类标识
return Counter(neighbors).most_common(1)[0][0]
def predict(self, X_predict):
"""
预测多个样本的分类
"""
# 通过单个样本分类直接 预测就 ok了
y_predict = [self._predict(x) for x in X_predict]
return np.array(y_predict)
上面这个代码应该是相当简单了, 如果你有兴趣,可以把 KNN近邻算法 实践 这一节的 预测代码用我们手写的跑一遍, 这里就不重复了,实现的效果大同小异,
但是从上面的代码我们也可以看出来,咱们是采用遍历的方式来求所有距离的,
如果你和 sklearn
中的算法做下对比,
会发现运行的速度会有非常大的区别,
这其中是有很多优化的点的,
不过这里不再我们的讨论这列,
或者说,其实我自己也没怎么去研究那些 KD-tree,ball-tree 之类的数据结构,
某种程度来说,
其实这也是数学的魅力,
就像一个排序...都能给你整出那么多幺儿子,
实践了,手写了, 不知道现在你对knn是不是有了一个比较深入的了解, 嗯,只想说一句, 不愧是最简单的算法之一,是真的很简单... 话虽如此,但是如果你觉得这样就可以用好 KNN 那就有点太想当然了, 学好一个算法不是目的, 用好一个算法才是真正的牛逼... 下面我们就来谈谈 KNN 的 调参... 这也是用好 KNN 的最最关键的一步
score
来返回模型的 正确率def score(y_predict,y_true): """ 传入 预测的 y 和 真实的 y ,判断一下他们的正确率 """ return np.sum(y_predict == y_true)/len(y_true)
sklearn
中你是可以找到 weight
这个超参数的
两者距离越近,那么权重越高,
从而得出一个带权重的结果,
具体模型需不需要带权重,
根据业务肯定是会不一样的,
并没有绝对的好坏之分,
但是带权重有一个好处就是:
可以有效避免 平票
的问题。
欧拉距离
作为距离的计算方式,
实际上,在 sklearn 中,采用的是另外一种方式,
叫做明可夫斯基距离
,公式如下:
其实仔细观察我们会发现,
当 P = 2
就是我们用到的 欧拉距离
了,
当 P = 1
就是所谓的 曼哈顿距离
了,
所以这里我们又得到一个可以调节的 超参数 P,
在sklearn
中你是可以找到 P
这个 超参数的,
来调整距离计算的方式
当然还有很多距离的计算方式,
比如:余弦相似度,皮尔森相似度等
这一小节我们主要介绍了 KNN的 另外两个超参数, 并且知道通过循环遍历的方式可以求解 最优 超参数, 而实际上,针对这个寻找最优超参数的问题, sklearn 提供了 一种叫做 网格搜索的 方式, 这里我们就不多说了, 如果你感兴趣,可以自行百度找找相关资料。
前面我们说了,KNN算法是一个分类算法, 但事实上其同样可以用来处理回归问题, 思路也很简单, 找到相应的邻居,然后根据邻居的打分来求自己的打分, 将分类问题就转换成了回归问题了。