盘点高效的KNN实现算法

简介: 盘点高效的KNN实现算法

1967年,斯坦福大学的两位研究员Cover和Hart,《战国策》中 物以类聚,人以群分”的思想,提出了一种实用的机器学习算法。它的基本思想是这样的:某家餐厅新出了一款汉堡D,不知道如何定价。参考店里已有的商品发现:汉堡A-18元,汉堡B-22元,汉堡C-38元,可乐A-8元,可乐B-9元,薯条A-12元...


微信图片_20220524135943.png


根据常识,店家知道新汉堡应该参考已有的汉堡,而不是可乐、薯条的售价(相比可乐、薯条,汉堡D与同类汉堡A/B/C的特征更接近)。于是,他们决定暂时把新汉堡的价格定为汉堡A/B/C的平均价,26元。


这就是被称为数据挖掘10大算法之一的K近邻算法(KNN),利用数据集对特征向量空间进行划分,并作为其分类的模型。


简单的说,假设现在有100种食品(训练集), ,当一种新食品加入时,找到与该食品最相似的20个食品(12种是汉堡,5种是鸡肉卷,3种是三明治),这20个实例的多数属于某个类,就把该实例分为这个类(汉堡)。下面我们简单介绍KNN的三要素,重点关注它的实现方法


01 距离度量



当我们通过特征工程或神经网络获得了数据集和新样本的特征后,首先需要确定一种距离度量策略,来衡量距离的远近。


机器学习领域常用的距离度量方法有欧式距离、余弦距离、曼哈顿距离、dot内积等等。


微信图片_20220524140002.png


其中,n维特征的a、b向量的欧式距离体现数值上的绝对差异,余弦距离体现方向上的相对差异。对向量经过归一化处理后,两者的效果基本是等价的。


更多距离度量方法,可以参考:https://zhuanlan.zhihu.com/p/336946131


02 K值和决策规则



有了距离度量方法后,我们需要确定参数k和最终的决策规则。如果选择较小的k值,相当于只取少量的样本进行预测,预测结果会对近邻的样本点非常敏感。换句话说,k值减小意味着整体模型变得复杂,容易发生过拟合。如果选择较大的k值,预测的样本会更多,与实例不太相似的样本也会参与预测,意味着整体的模型变得简单。极端情况下,无论输入样本是什么,都粗暴地预测它属于训练样本中最多的类,模型过于简单,很明显并不可取。所以实际应用中,我们一般先设一个较小的k值,通过交叉验证选取最优的k。

至于决策规则,对于分类而言,一般采用多数表决规则,少数服从多数。对于回归任务,可以采用平均法则


如果不打算将该算法实际应用的读者,读到这儿就可以啦。相信今后看到KNN这个词,一定不会陌生。想将该算法予以实践的读者,请浏览到最后哦!


03 KNN实现



KNN的原理非常简单,难点在于如何实现KNN算法。受并发、时延等条件限制,高效性往往是我们实际项目中「算法落地」要考虑的重要因素。


实际上,K近邻的实现方法多达数十种,下面将抛砖引玉,介绍几种常用的方法。


线性扫描


即暴力法。将新样本和训练样本逐一比对,最终挑选距离最接近的k个样本,时间复杂度O(n)。对于样本数量比较少的情况,这种方法简单稳定,已经能有不错的效果。但是数据规模较大时,时间开销线性上升,无法接受。


KD树


KD树是一种树形结构存储算法。


KD树是二叉树,核心思想是对 k 维特征空间不断切分(假设特征维度是100,对于0-99中的每一个维度,以中值递归切分)构造的树,每一个节点是一个超矩形,小于结点的样本划分到左子树,大于结点的样本划分到右子树。构造完毕后,检索时(1)从根结点出发,递归向下访问KD树。若目标点 x 当前维的坐标小于切分点的坐标,移动到左子树,否则移动到右子树,直到叶结点;(2)以此叶结点为“最近点”,递归地向上回退,查找该结点的兄弟结点中是否存在更近的点,若存在,则更新“最近点”,否则回退;(3)未到达根结点时继续执行(2);(4)回退到根结点时,搜索结束。KD树在特征维度小于20时效率最高,一般适用于训练样本数远大于空间维数时的k近邻搜索。当空间维数接近训练实例数时,效率会迅速下降,几乎接近线形扫描。


Annoy


Annoy,全称“Approximate Nearest Neighbors Oh Yeah”,是一种适合实际应用的快速相似查找算法。Annoy 同样通过建立一个二叉树,使得每个点查找时间复杂度是O(log n)。和KD树不同的是,Annoy没有对k维特征进行切分。Annoy的每一次空间划分,可以看作聚类数为2的KMeans过程。收敛后在产生的两个聚类中心连线之间建立一条垂线(图中的黑线),把数据空间划分为两部分。

微信图片_20220524140045.png


在划分的子空间内不停的迭代划分,直到每个子空间最多只剩下k个数据节点,划分结束。

微信图片_20220524140111.png


最终生成的二叉树具有类似如下结构,二叉树底层是叶子节点,记录原始数据;中间节点记录的是分割超平面的信息。查询过程和KD树类似,先从根向叶子结点递归查找,再向上回溯即可。


微信图片_20220524140131.png


HNSW


和前几种算法不同,HNSW(Hierarchcal Navigable Small World graphs)是基于图存储的数据结构。


微信图片_20220524140145.png


假设我们现在有13个2维数据向量,我们把这些向量放在了一个平面直角坐标系内,隐去坐标系刻度,它们的位置关系如上图所示。


朴素查找法:把某些点和点之间连上线,构成一个查找图,存储备用;当我想查找与粉色点最近的一点时,我从任意一个黑色点出发,计算它和粉色点的距离,与这个任意黑色点有连接关系的点我们称之为“友点”(直译),然后我要计算这个黑色点的所有“友点”与粉色点的距离,从所有“友点”中选出与粉色点最近的一个点,把这个点作为下一个进入点,继续按照上面的步骤查找下去。


如果当前黑色点对粉色点的距离比所有“友点”都近,终止查找,这个黑色点就是我们要找的离粉色点最近的点。

https://blog.csdn.net/u011233351/article/details/85116719

HNSW算法就是对上述朴素思想的改进和优化。为了达到快速搜索的目标,HNSW在构建图时还至少要满足如下要求:1)图中每个点都有“友点”;2)相近的点都互为“友点”;3)图中所有连线的数量最少;4)配有高速公路机制的构图法


微信图片_20220524140201.png


HNSW低配版——NSW论文中配了这样一张图,黑线是近邻点连线,红线是“高速公路”,如此可以大幅减少平均搜索的路径长度。在NSW基础之上,HNSW加入跳表结构做了进一步优化。最底层是所有数据点,每一个点都有50%概率进入上一层的有序链表。这样可以保证表层是“高速通道”,底层是精细查找通过层状结构,将边按特征半径进行分层,使每个顶点在所有层中平均度数变为常数,从而将NSW的计算复杂度由多重对数复杂度降到了对数复杂度

微信图片_20220524140217.png


关于HNSW的详细介绍可以参考原论文[2]和博客HNSW算法理论的来龙去脉[3]。


04 实验



下面,我将用一组对比实验评估每种算法的性能。实验在CLUE发布的今日头条短文本分类数据集上进行,训练集规模:53,360条短文本。


实验环境:Ubuntu 16.04.6,CPU:126G/20核,python 3.6requirement:scikit-learn、annoy、hnswlib、bert-as-service


实验中使用了肖涵博士开源的bert-as-service服务预先将文本编码为768维度的特征向量,作为输入特征。距离度量统一采用欧式距离。为了方便测试,我从验证集中随机抽取了200条样本,使用不同的KNN实现方法统计top1-top3的查找准确率和时间开销。具体如下:


微信图片_20220524140235.png


注:Ball树是KD树的一种改良算法我们发现,Annoy算法的Top准确率相差不大,200条样本的批量查找速度却从之前的15秒(75ms/条)降到了0.08秒(0.4ms/条),提升了100倍以上HNSW算法预测200条样本耗时0.05秒(0.25ms/条),略优于Annoy。

此外,同样的53,360条特征向量(768维度),保存为静态索引文件后,ann索引的大小是227MB,hnsw索引是171MB,从这一点看hnsw也略胜一筹,可以节约部分内存。实验部分代码可以参考:https://zhuanlan.zhihu.com/p/152522906/


05 总结



本文介绍了K近邻算法,重点关注其实现方式。通过理论介绍和实验证明,Annoy和HNSW是可以在实际业务中落地的算法。其中Bert/Sentence-Bert+HNSW的组合会有不错的(粗排)召回效果。K近邻算法的实现方法其实还有很多,感兴趣的同学可以阅读相关论文进一步研究。


微信图片_20220524140253.png

相关文章
|
1月前
|
机器学习/深度学习 算法
机器学习入门(三):K近邻算法原理 | KNN算法原理
机器学习入门(三):K近邻算法原理 | KNN算法原理
|
1月前
|
机器学习/深度学习 算法 API
机器学习入门(五):KNN概述 | K 近邻算法 API,K值选择问题
机器学习入门(五):KNN概述 | K 近邻算法 API,K值选择问题
|
2月前
|
算法 Python
KNN
【9月更文挑战第11天】
52 13
|
2月前
|
算法 大数据
K-最近邻(KNN)
K-最近邻(KNN)
|
2月前
|
机器学习/深度学习 算法 数据挖掘
R语言中的支持向量机(SVM)与K最近邻(KNN)算法实现与应用
【9月更文挑战第2天】无论是支持向量机还是K最近邻算法,都是机器学习中非常重要的分类算法。它们在R语言中的实现相对简单,但各有其优缺点和适用场景。在实际应用中,应根据数据的特性、任务的需求以及计算资源的限制来选择合适的算法。通过不断地实践和探索,我们可以更好地掌握这些算法并应用到实际的数据分析和机器学习任务中。
|
4月前
knn增强数据训练
【7月更文挑战第27天】
39 10
|
4月前
|
机器人 计算机视觉 Python
K-最近邻(KNN)分类器
【7月更文挑战第26天】
47 8
|
4月前
创建KNN类
【7月更文挑战第22天】创建KNN类。
35 8
|
4月前
knn增强数据训练
【7月更文挑战第28天】
41 2
|
4月前
|
机器学习/深度学习 数据采集 算法
Python实现PCA降维和KNN人脸识别模型(PCA和KNeighborsClassifier算法)项目实战
Python实现PCA降维和KNN人脸识别模型(PCA和KNeighborsClassifier算法)项目实战
117 3
下一篇
无影云桌面