相似度搜索的常见算法

前面介绍过了衡量两个向量是否"相似"的算法,即语义相似度算法,如余弦相似度、欧式距离等。

那么实际在检索的时候,当数据量达到百万、千万甚至亿级别时,逐一计算查询向量与库中所有向量的相似度会变得极其缓慢。如KNN算法。

KNN (K-Nearest Neighbors,K近邻算法)

  • 最精确的搜索方式。KNN 使用的是暴力搜索,它会扫描所有数据,计算目标向量与数据库中每一个向量的距离,然后返回最近的K个。
  • 缺点:但不适合针对大量的高维向量数据的检索(尤其是数百万级别),因为计算时间随数据量线性增长。

KNN主要适合的场景是:

  • 数据规模小(如几千条以内)
  • 对精度要求极高(不能容忍近似误差)
  • 实验或原型阶段快速验证

因为KNN在大数据上面比较慢,我们需要更高效的索引算法来加速检索,这类算法通常被称为近似最近邻搜索算法。

近似最近邻搜索 (ANN - Approximate Nearest Neighbor)为了速度牺牲极少量的精度,是目前工业界的主流方案。

常见的 ANN 算法包括:

IVF (Inverted File Index,倒排索引)

IVF 是为了解决 KNN 太慢而诞生的第一种主流加速方案,它的灵感来源于搜索引擎的倒排索引。

  • 实现原理:
  • 训练(聚类):使用 K-Means 算法将所有向量聚类成 N 个簇,每个簇有一个中心点(质心)。这就像把图书馆分成了 N 个书架。
  • 建索引(分配):将每个向量分配到距离它最近的那个质心所属的列表中(倒排列表)。
  • 搜索(探路):
  • 当查询向量进来时,先计算它与这 N 个质心的距离。
  • 选出距离最近的 k个质心(参数 nprobe,例如只查最近的 5 个书架)。
  • 只在这 k 个质心包含的向量列表中进行暴力搜索。

进阶版------IVF_PQ :为了进一步省内存,IVF 常结合 PQ (Product Quantization)。它把长向量切分成小段,分别压缩编码。这样虽然损失了一点点精度,但能让内存占用降低几十倍,速度更快。

这个方案的优点是,大大减少了计算量,内存占用可控(尤其是配合 PQ 时)。缺点就是如果目标向量恰好不在选中的那几个簇里,就会漏掉(召回率受 nprobe 参数影响大)。

HNSW (Hierarchical Navigable Small World):

HNSW 是目前综合性能(速度与精度)最好的算法之一,被广泛应用于 Milvus、Qdrant 等主流向量数据库。利用"小世界网络"现象(六度分隔理论),构建多层级的"高速公路"系统来导航。

(整体实现有点像跳表)

先补一课:跳表是什么

后面的 HNSW 整体很像跳表。跳表本身不难,先把它讲明白,再看 HNSW 就不会只剩一个比喻。

跳表(Skip List) 是一种有序数据结构。它还是一条链表,只是在链表上面又搭了几层更稀疏的"快进索引",用来少走很多步。

普通链表为什么慢

有序链表里的数字从小到大排着:

text 复制代码
1 → 3 → 5 → 7 → 9 → 11 → 13 → 15 → 17 → 19 → 21 → 23 → 25

要找 15,只能从头开始,一个节点一个节点 next。数据有 N 个,最坏要走 N 步,也就是 O(N)。这和 KNN 扫全库是同一类问题:没有捷径,只能挨个看。

跳表多加了几层快进

跳表不改这条有序链表,只是在上面再加索引:

  • 最底层 Level 0:所有节点都在,一个挨一个,负责最后精确找到目标。
  • 上面每一层:只保留一部分节点。这些节点之间的指针能一次跨过好几个底层节点,相当于快进。
  • 层越高:节点越少,一步跨得越远。

一个节点能不能出现在更高层,插入时抛硬币决定:正面就再升一层,反面就停。所以大多数节点只在底层,少部分能上到中层,极少数能上到顶层。不需要像红黑树那样旋转来保持平衡,实现简单,平均查找仍然是 O(log N)。

Redis 的有序集合(zset)底层用的就是跳表。

画出来大概是这样。横线表示这一层能一次跳多远:

text 复制代码
Level 2:  1 ----------------> 17
Level 1:  1 -------> 9 -----> 17 -------> 25
Level 0:  1-3-5-7-9-11-13-15-17-19-21-23-25

同一个数字如果出现在多层,其实是同一个节点,只是它在每一层都有一条向右的指针。Level 0 的指针指向紧邻的下一个数;高层的指针指向更远的下一个数。

查找时怎么跳

还是找 15。从最高层最左边出发,规则就一条:下一跳如果还没超过目标,就跳过去;下一跳已经超过目标,就在当前节点下楼。

  1. Level 2 站在 1。右边是 17,17 已经比 15 大,不能跳,下楼。
  2. Level 1 还站在 1。右边是 9,9 比 15 小,跳到 9。9 的右边是 17,又超过了,下楼。
  3. Level 0 站在 9。顺着底层看过去:11、13、15,找到。

全程只看了几个节点,没有把 1 到 15 之间的人全拜访一遍。层数大约是 log N,所以平均只走大约 log N 步。

插入、删除也是先这样跳到位置,再改几条指针。新节点升到第几层,同样靠抛硬币,不用把整张表重排。

  • 实现原理:
  • 图结构:数据点不再是孤立的,而是通过边连接成一张图。每个点连接几个"邻居"。
  • 分层(关键创新):
  • 底层 (Layer 0):包含所有数据点,连接距离很近的邻居(像城市里的街道,用于精确定位)。
  • 高层 (Layer 1, 2...):节点越来越少,连接距离较远的节点(像高速公路或飞机航线,用于快速跨越空间)。
  • 搜索(跳跃导航):
  • 从顶层开始搜索,快速定位到大致区域。
  • 找到入口后,"下楼梯"进入下一层。
  • 在下一层继续通过贪婪算法(Greedy Search)找更近的点,直到最底层。

HNSW的优点是,搜索速度极快(接近 O(log⁡N) ),召回率极高,支持动态插入数据。缺点则是索引构建比较耗时,且因为要存大量的边(连接关系),内存占用较高。

LSH (Locality Sensitive Hashing,局部敏感哈希)

LSH 的思路与 IVF 类似,但它不依赖聚类中心,而是依赖数学上的概率哈希。

  • 实现原理:
  • 哈希函数设计 :传统的哈希函数(如 MD5)是为了让输入哪怕差一点,输出也完全不同。LSH 反其道而行,设计特殊的哈希函数,使得距离近的向量碰撞(Hash Collision)概率高。
  • 映射:将高维向量通过随机超平面投影等方法,映射成短的二进制码或整数(哈希值)。
  • 建表:准备多张哈希表。
  • 搜索:
  • 计算查询向量的哈希值。
  • 直接去对应的"桶"里把数据捞出来。
  • 因为可能漏掉,通常会查多张表,或者查相邻的桶。

优点:理论速度最快(接近 O(1)),非常适合海量数据的粗筛。

缺点:召回率不稳定,容易漏掉真正的近邻(假阴性),且对参数调优比较敏感。

如何选择

算法 核心原理 形象类比 速度 精度 (召回率) 内存占用 适用场景
KNN 暴力计算所有距离 挨家挨户敲门找人 慢 100% 高 (存原始数据) 小数据量,或对精度要求极高的校验场景
IVF 聚类分桶,只查局部 先去对应小区,再挨家找 快 中/高 (可调) 中 (可压缩) 数据量极大,内存有限,追求性价比
HNSW 多层图导航 坐飞机(高层) -> 打车(底层) 极快 极高 高 (存图结构) 目前最主流,对延迟和精度都有高要求
LSH 概率哈希分桶 把人按姓氏首字母分组,只查同组 最快 较低 低 极大规模数据的快速粗筛,或对精度要求不高
  • 如果你需要综合性能最好 ,首选 HNSW。
  • 如果你内存非常吃紧 (例如要在手机端或内存小的服务器上跑亿级数据),首选 IVF_PQ。
  • 如果你只是做简单的去重或极快速过滤,可以考虑 LSH。
相关推荐
土星云SaturnCloud20 小时前
基于 PP-OCR 的车牌识别:从数据整理到土星云 SE110S 系列边缘部署
人工智能·ai·ocr·边缘计算
AI_work20 小时前
43 号令下招投标全流程留痕分析:电子卷宗 15 年保存与投标人文档合规体系重构
大数据·人工智能·ai·重构
Navigator_Z20 小时前
LeetCode //C - 1313. Decompress Run-Length Encoded List
c语言·算法·leetcode
七牛开发者20 小时前
AI Agent 的安全风险,正在从 Prompt 扩展到运行环境
ai·agent·security·权限管理·cursor
dialect_20 小时前
CODEFORCES ROUND 1127 DIV2
开发语言·c++·算法
Nuanyt21 小时前
LeetCode Hot 100 链表专题(四)· 3 题(Java)
java·算法·leetcode·链表·双指针
灯澜忆梦21 小时前
LeetCode 1861. 旋转盒子|Go 双指针解法
算法
软件派1 天前
生产动态数据智能风控:阈值判别、趋势预测、异常识别算法工程落地实战
前端·数据库·算法
树欲静·而风不止1 天前
许愿式编程:我把 Typability 改成了能日常顶替 Typora 的样子
ai·markdown
ZhengEnCi1 天前
模型训练完整流程:从数据准备到模型上线的 10 个关键步骤(全文可跑)
后端·算法