检索 · 2020

稠密检索

用向量距离寻找语义相近的段落,而不只匹配相同的词。

成熟 · 使用现成模型

要解决的问题

关键词检索依赖字面重合。用户的问法和文档的写法经常不一样,相关段落会因此漏掉。

相对前一代的变化

相对 BM25 这类稀疏检索,稠密检索把问题和段落编码成向量,用内积或余弦距离排序。RAG 论文里的检索器就沿用了这种双编码器。

做法

离线时用向量模型把每个文本块编码入库。在线时只编码问题,再在向量索引里找最近的块。DPR 当年需要专门训练双编码器;现在多数系统直接使用现成的嵌入模型。索引类型和参数会同时改变召回与延迟,更换嵌入模型后要用同一组问题重新核对。

局限

专有名词、编号和罕见词仍可能比不过关键词。索引要占用向量存储,更换嵌入模型通常就要重建索引。

在链路中的位置

位于切块之后、生成之前,是朴素 RAG 的默认召回方式。

详解

「详解」根据 Datawhale《All-in-RAG》 的 向量嵌入、向量数据库、索引优化 改写,有删节和重组,未包含原教程的代码、图片和练习。原教程与本节详解均按 CC BY-NC-SA 4.0 许可提供:使用时需署名并标明修改,不得用于商业目的,再次改编须以相同许可共享。

用一个向量表示一段文本的位置

嵌入模型把文本映射到连续向量空间。训练目标是让语义接近的文本距离更近。查询和文档使用同一个模型编码后,就可以用内积或余弦距离排序。

早期的 Word2Vec、GloVe 给每个词一个固定向量,同一个词在任何句子里都一样。BERT 一类模型会看上下文,所以同一个词在不同句子中的表示可以不同。检索系统通常还要把整段 token 表示池化成一个向量,方便在大规模索引里比较。

模型选择先看任务,再看榜单

MTEB 一类榜单可以用来建立候选名单,但不能代替自己的文档。至少要看检索分数、支持的语言、输入长度、向量维度、推理速度,以及是否允许本地部署。

换模型几乎总是意味着重建索引。比较两个模型时,应使用同一批文档、同一套切块和同一组问题,记录 Recall@k,而不是只看演示查询的第一名是否顺眼。专有名词和编号仍可能需要关键词检索补上。

向量库负责近邻检索,不负责回答

传统数据库擅长等值、范围和事务。向量库擅长近似最近邻,也就是在不必逐条精确比较的情况下快速找到相近向量。它还要保存元数据,以便按来源、时间或权限过滤,并把索引持久化。

FAISS 适合作为本地索引:把向量加入索引、保存到磁盘、加载后做相似度搜索。它很轻,但过滤、服务化和多副本通常要自己补。Milvus、Qdrant、pgvector 等把这些能力放进数据库服务。

Milvus 里要先定集合,再定索引

一个集合由 Schema 定义字段,例如主键、正文、来源和向量。分区可以把数据按租户或时间分开,别名则允许在不改查询的情况下切换集合。

索引决定速度和召回的交换。FLAT 精确但慢,适合小数据。IVF 系列先分桶再搜索,nlist 和 nprobe 分别控制桶的数量和查询时检查多少桶。HNSW 用分层图换内存和更高召回。数据量、内存、延迟预算和能否接受重建,比默认参数更重要。

检索可以是纯向量搜索,也可以叠加标量过滤、范围限制和混合向量。多模态实践里,图像和文本可以进入同一集合,但编码器和字段定义必须从写入时就一致。

索引优化常发生在切块和查询之间

句子窗口索引用较小的句子召回,再把窗口内的上下文交给模型,避免为了召回精度牺牲阅读所需的前后文。多表或长文档也可以先为每部分写摘要,顶层只检索摘要,命中后再递归进入对应部分。

这些优化没有改变“用向量找近邻”这个核心,它们改变的是什么被编码、编码前有没有摘要,以及命中后展开哪一层。