• KNN算法/HNSW算法


    高维稀疏数据进行快速相似查找,可以采用learning to hash,但高维稠密数据查找则采用annoy

    learning to hash 参考:

    https://blog.csdn.net/hero_fantao/article/details/70245284
    《海量数据相似查找系列1 -- Minhashing & LSH & Simhash 技术汇总》

    Annoy以及其他KNN算法可以看下面:

    参考这篇文章:

    https://zhuanlan.zhihu.com/p/152522906

    《K近邻算法哪家强?KDTree、Annoy、HNSW原理和使用方法介绍》

    关于KNN算法,一个核心问题是:如何快速从数据集中找到和目标样本最接近的K个样本?

    本文将从这个角度切入,介绍常用的K近邻算法的实现方法。具体将从原理、使用方法、时间开销和准确率对比等方面进行分析和实验。

    KNN算法的三要素:距离度量、k值的选择和分类决策规则。

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

    主流的近邻算法都支持上述不同的距离度量。其中n维特征空间的a、b向量的欧式距离 [公式] 体现数值上的绝对差异,而余弦距离基于余弦相似度(两个向量间夹角的余弦值),体现方向上的相对差异。如果对向量做归一化处理,二者的结果基本是等价的。

    首先最直观的想法(暴力法),是线性扫描法。将待预测样本和候选样本逐一比对,最终挑选出距离最接近的k个样本即可,时间复杂度O(n)。

    常用的存储结构可以分为树和图两大类。树结构的代表是KDTree,以及改进版BallTree和Annoy等;基于图结构的搜索算法有HNSW等。

    KDTree

    KD树的构造:

    一颗构建好的KD-Tree中,任何一个非叶节点都有一个特征空间切割维度,在这个维度上,所有左子树的节点都小于它,所有右子树的节点都大于等于它;

    构建的步骤如下:

    1. 计算数据集在特征空间每个维度上的方差,将方差最大的维度作为当前的分割维度;
    2. 将数据集在分割维度上排序后,找到中位数,假设在排序的数据集中位置为m;
    3. 构建节点,保存中位数和当前分割维度;
    4. 将数据集中在当前维度上小于中位数(从0到m-1)作为左子树的数据集;大于等于中位数(从m+1到最后)的数据作为右子树的数据集;重复上面的步骤,继续构建左右子树;直到整个KD-Tree构建完成;

    KD树的查找:

    从root节点开始,DFS搜索直到叶子节点,同时在stack中顺序存储已经访问的节点。
    如果搜索到叶子节点,当前的叶子节点被设为最近邻节点。
    然后通过stack回溯:
    如果当前点的距离比最近邻点距离近,更新最近邻节点.
    然后检查以最近距离为半径的圆是否和父节点的超平面相交.
    如果相交,则必须到父节点的另外一侧,用同样的DFS搜索法,开始检查最近邻节点。
    如果不相交,则继续往上回溯,而父节点的另一侧子节点都被淘汰,不再考虑的范围中.
    当搜索回到root节点时,搜索完成,得到最近邻节点。

    kd树在维数小于20时效率最高,一般适用于训练实例数远大于空间维数时的k近邻搜索;当空间维数接近训练实例数时,它的效率会迅速下降,几乎接近线形扫描。

    Annoy

    annoy全称“Approximate Nearest Neighbors Oh Yeah”

    是一种适合实际应用的快速相似查找算法。Annoy 同样通过建立一个二叉树来使得每个点查找时间复杂度是O(log n),和kd树不同的是,annoy没有对k维特征进行切分。

    annoy的每一次空间划分,可以看作聚类数为2的KMeans过程。收敛后在产生的两个聚类中心连线之间建立一条垂线(图中的黑线),把数据空间划分为两部分。

     

    直到每个子空间最多只剩下K个数据节点,划分结束。

     

    最终生成的二叉树具有如下类似结构,二叉树底层是叶子节点记录原始数据节点,其他中间节点记录的是分割超平面的信息。

    在 ANN 领域,最常见的两个问题是:

    1. 如果我们想要 Top K 的点,但是该区域的点集数量不足 K,该怎么办?
    2. 如果真实的 Top K 中部分点不在这个区域,该怎么办?

    作者用了两个技巧来解决这个问题:

      1. 使用优先队列(priority queue):将多棵树放入优先队列,逐一处理;并且通过阈值设定的方式,如果查询的点与二叉树中某个节点比较相似,那么就同时走两个分支,而不是只走一个分支;
      2. 使用森林(forest of trees):构建多棵树,采用多个树同时搜索的方式,得到候选集 Top M(M > K),然后对这 M 个候选集计算其相似度或者距离,最终进行排序就可以得到近似 Top K 的结果。

    最后提一点,annoy接口中一般需要调整的参数有两个:查找返回的topk近邻和树的个数。一般树越多,精准率越高但是对内存的开销也越大,需要权衡取舍(tradeoff)。

    HNSW

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

    假设我们现在有13个2维数据向量,我们把这些向量放在了一个平面直角坐标系内,隐去坐标系刻度,它们的位置关系如上图所示。
    朴素查找法:不少人脑子里都冒出过这样的朴素想法,把某些点和点之间连上线,构成一个查找图,存储备用;当我想查找与粉色点最近的一点时,我从任意一个黑色点出发,计算它和粉色点的距离,与这个任意黑色点有连接关系的点我们称之为“友点”(直译),然后我要计算这个黑色点的所有“友点”与粉色点的距离,从所有“友点”中选出与粉色点最近的一个点,把这个点作为下一个进入点,继续按照上面的步骤查找下去。如果当前黑色点对粉色点的距离比所有“友点”都近,终止查找,这个黑色点就是我们要找的离粉色点最近的点。

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

     HNSW低配版NSW论文中配了这样一张图,短黑线是近邻点连线,长红线是“高速公路机制”,如此可以大幅减少平均搜索的路径长度。

     

     在NSW基础之上,HNSW加入了跳表结构做了进一步优化。最底层是所有数据点,每一个点都有50%概率进入上一层的有序链表。这样可以保证表层是“高速通道”,底层是精细查找。通过层状结构,将边按特征半径进行分层,使每个顶点在所有层中平均度数变为常数,从而将NSW的计算复杂度由多重对数复杂度降到了对数复杂度。

    小结

    本文介绍了几种常用的k近邻查找算法,kdtree是KNN的一种基本实现算法;考虑到并发、延时等要素,annoy、hnsw是可以在实际业务中落地的算法,其中bert/sentence-bert+hnsw的组合会有不错的召回效果。

     

  • 相关阅读:
    [转]C#、VB.NET使用HttpWebRequest访问https地址(SSL)的实现
    C#设置System.Net.ServicePointManager.DefaultConnectionLimit,突破Http协议的并发连接数限制
    [转]WebBrowser控件禁用超链接转向、脚本错误提示、默认右键菜单和快捷键
    [转]C#打印DataGridView的例子源码
    c# TreeView 父节点选中/不选时子节点都同步选中/不选
    C#中PictureBox异步加载图片
    [转]FusionCharts 3.1 破解版 – 非常好用的Flash图表控件
    配合JavaScript拖动页面中控件
    在ThinkPad T400上安装win2003 所遇问题
    C# 抛弃MoveTo来实现文件重命名
  • 原文地址:https://www.cnblogs.com/charlesblc/p/15932792.html
Copyright © 2020-2023  润新知