--- title: "01-相似度与向量索引" created: 2026-08-31 tags: - 项目筑基 --- # 相似度与向量索引 > 概览篇说清了"为什么需要向量库",这一篇往下钻一层:**相似度怎么算、索引为什么快**。内容是书上看的 + 梳理的理解,还没有实操验证,标几个等我动手时要重点核对的点。 ## 三种相似度:余弦、内积、欧氏 向量检索的本质是算"两个向量有多像",常用的就三种度量: | 度量 | 含义 | 范围 | 备注 | | --- | --- | --- | --- | | 余弦相似度 | 两个方向的夹角,只看方向不看长度 | [-1, 1],越大越像 | 文本检索最常用 | | 内积(点积) | 方向 × 长度一起算 | 不定 | **向量归一化后与余弦等价** | | 欧氏距离 L2 | 空间直线距离,越小越近 | [0, ∞) | 图像特征常用 | 我理解的关键点: - 文本 embedding 基本都会**归一化**(压成单位长度),此时三种度量排序结果完全一致(L2² = 2 − 2·cosθ,单调对应)——所以"选哪个"多数时候不纠结,跟着库默认走即可。 - 未归一化时内积会偏向"模长大"的向量,这是隐性坑:混着用不同长度的向量算内积,排序可能悄悄失真。 - 建库时就要定好度量方式(Chroma 默认 L2、建 collection 时可指定 cosine)——**建完再换度量等于重建库**,这是我标记的"动手时第一步要确认"的点。 ## 为什么暴力检索不可行 最朴素的 KNN:查询向量和库里每条向量都算一遍距离,排个序取前 K。 问题在规模:100 万条 × 1536 维,每次查询要算 1 亿次浮点乘加,还要全部读内存——单次查询几百毫秒起步,而在线服务通常要求 <10ms。这就是**维度灾难**:维度一高,"距离"的计算量和区分度都崩了。 于是 ANN(近似最近邻)登场:**放弃 100% 找到最近的,换几个数量级的速度**。衡量代价的指标是 **recall@K**——ANN 返回的前 K 条与暴力检索真值前 K 条的重合率,生产一般要求 95%+。 ## 三大索引家族 ### HNSW:分层图(当前最主流) 思想借鉴跳表:把图分成多层,**上层稀疏、边长(跨度大),下层稠密、边短**。查询从顶层出发贪心逼近,逐层下降,最后在底层精确搜索。 ``` 第 2 层: A ──────────────── E (稀疏,长边,快速跨域) 第 1 层: A ─── C ─── E ─── G (较密) 第 0 层: A - B - C - D - E - F - G (全量数据,短边,精细定位) ``` - 优点:查询快(毫秒级)、支持增量插入,是 Milvus/pgvector/Chroma 的默认或主力索引 - 关键参数:`M`(每节点连接数,越大图越准越耗内存)、`ef_construction`(建图质量)、`ef_search`(查询时候选队列长度,**调大它 = 更准但更慢**,这是运行期最常调的旋钮) - 代价:图结构本身占内存大,内存富余的机器才玩得爽 ### IVF:倒排文件(聚类分桶) 先用 k-means 把向量空间聚成 `nlist` 个簇,每个簇记一个"质心";查询时只和质心比,挑最近的 `nprobe` 个簇进去精确算。 - `nprobe` 越大召回越高、速度越慢——和 HNSW 的 ef_search 一个道理,都是"扫多少"的旋钮 - 特点:**需要先用数据训练聚类**;数据分布倾斜时(热门簇过大)会拖慢 - 适合超大规模 + 内存敏感的场景 ### PQ:乘积量化(压缩术) 严格说不是索引而是**压缩**:把 1536 维切成 m 段(比如 96 段 × 16 维),每段各自用 k-means 聚成 256 类,向量每段只用 1 字节存类号——**6KB 压到 96 字节**,代价是距离计算带量化误差。 实战里几乎都是组合拳:**IVF-PQ**(先分桶再段内压缩)是亿级向量的经典配方;HNSW 也有配 PQ 的方案。内存够就 HNSW 裸奔,内存紧再上量化。 > 💡 选型记忆:**百十万条 → HNSW 随便选;千万到亿级、内存紧 → IVF-PQ**。其余参数(ef_search / nprobe)先给个中等值,用 recall 测试集调,不背数字。 ## 混合过滤:先过滤还是后过滤 真实业务几乎都是"带条件的相似检索"(比如:只在这个用户的知识库里搜)。条件过滤和 ANN 的顺序是个真问题: - **post-filter(先检索后过滤)**:先 ANN 拿 top-K 再按条件筛——风险是筛完剩不了几条(查出来的 K 条全不满足条件) - **pre-filter(先过滤后检索)**:先按条件筛出子集再做 ANN——子集分布变了,可能伤召回 - 主流库各有各的处理(Milvus 内部做了混合策略),**用的时候查对应文档,别想当然**——这是我标记的第二个"动手核对"点 --- ⬅️ [[00-向量数据库概览|00-向量数据库概览]] 🏠 [[00-数据库|00-数据库]] ➡️ [[02-Embedding 模型与文本切块|02-Embedding 模型与文本切块]]