Skip to content

向量检索常见面试篇

来源:AiGC面试宝典 作者:宁静致远 发布时间:2024年1月12日


一、向量检索库总结

1.1 Annoy

GitHub:https://github.com/spotify/annoy

1.1.1 Annoy 介绍

Annoy 是高维空间求近似最近邻的开源库。全称:Approximate Nearest Neighbors Oh Yeah,是一种适合实际应用的快速相似查找算法。

📝通俗解释:Annoy 的核心思想是将高维向量映射到低维空间,通过构建多棵随机二叉树来加速最近邻搜索。想象一下你在一个陌生的城市找最近的餐厅,Annoy 就像先把你带到一个大致区域,然后再细找,比逐个问所有餐厅要快得多。

Annoy 构建一个随机二叉树森林,查询时间为 $O(\log n)$。

📝通俗解释:$O(\log n)$ 意味着搜索时间随着数据量增长很慢。比如数据量从1000增加到100万,搜索时间只增加约3倍,非常高效。

1.1.2 Annoy 使用

通过 pip 安装:

bash
pip install annoy

基本使用示例:

python
from annoy import AnnoyIndex
import random

# 向量的维度
f = 40  # Length of item vector that will be indexed

# 返回一个可读可写的存储 f 维向量的索引
t = AnnoyIndex(f, 'angular')

for i in range(1000):
    # random.gauss 为随机生成高斯分布的随机数
    v = [random.gauss(0, 1) for z in range(f)]
    # 在位置 i 添加向量
    t.add_item(i, v)

# 建立 n_trees 的森林,树越多,精度越高
t.build(10)  # 10 trees

# 保存索引到文件
t.save('test.ann')

# 加载索引
u = AnnoyIndex(f, 'angular')
u.load('test.ann')  # super fast, will just mmap the file

# 查询:返回最接近 item 0 的 1000 个近邻
print(u.get_nns_by_item(0, 1000))

📝通俗解释:上面的代码展示了 Annoy 的完整使用流程——创建索引、添加向量、构建索引、保存和加载。angular 是距离度量方式,类似于余弦相似度。

向量检索常用函数:

函数说明
get_nns_by_item(i, n, search_k=-1, include_distances=False)根据 item ID 查询 n 个最近邻
get_nns_by_vector(v, n, search_k=-1, include_distances=False)根据向量查询 n 个最近邻
get_item_vector(i)获取索引 i 对应的向量
get_distance(i, j)返回 item_i 和 item_j 的平方距离

📝通俗解释:get_nns_by_item 常用于已知某个物品,找相似物品;get_nns_by_vector 常用于给定用户 embedding,找推荐物品。

索引属性函数:

函数说明
get_n_items()返回索引中的 items 个数
get_n_trees()返回索引树的个数

关键参数调优:

  • n_trees:在构建期间设置,影响构建时间和索引大小。值越大,结果越准确,但索引越大。
  • search_k:在运行时设置,影响搜索性能。值越大,结果越准确,但查询时间越长。默认为 n_trees * n

📝通俗解释:n_trees 像是"多找几个朋友帮忙推荐",树越多,推荐越准但记忆(存储)需要越多;search_k 像是"问多少人",问得多准但累。


1.2 Faiss

GitHub:https://github.com/facebookresearch/faiss Tutorial:https://github.com/facebookresearch/faiss/wiki/Getting-started

1.2.1 Faiss 介绍

Faiss 库是由 Meta(原 Facebook) 开发的适用于稠密向量匹配的开源库,支持 C++ 与 Python 调用。Faiss 提供了高效的索引类库,是向量化检索的开山鼻祖。

📝通俗解释:Faiss 是向量检索领域的"老前辈",很多其他库都是在 Faiss 基础上封装的。它就像一个"向量加速器",能让大规模的向量相似度计算变得飞快。

Faiss 支持多种向量检索方式,包括内积、欧氏距离等,同时支持精确检索近似搜索

📝通俗解释:精确检索就像逐一比对所有候选人找出最相似的;近似搜索则像通过简历快速筛选,再精细面试。Faiss 两者都支持。

1.2.2 Faiss 主要特性

特性说明
支持相似度检索和聚类既能找相似向量,也能对向量分组
支持多种索引方式如 IVF、PQ、HNSW 等,适用于不同场景
支持 CPU 和 GPU 计算GPU 加速适合超大规模数据
支持 Python 和 C++ 调用方便不同技术栈使用

📝通俗解释:Faiss 的多种索引方式就像不同的"找人之道"——有的是先分区再找(IVF),有的是压缩信息来快速比较(PQ),有的是用图结构导航(HNSW)。

1.2.3 Faiss 使用

bash
pip install faiss-cpu  # 或者 pip install faiss-gpu

Faiss 使用流程(三个步骤):

  1. 构建向量库:对已知数据进行向量化,最终以矩阵形式表示
  2. 选择索引:为矩阵选择合适的 index,将向量 add 到 index 中
  3. 搜索:search 得到最终结果

完整示例:

python
import numpy as np
import faiss

# 参数设置
d = 64      # 向量维度
nb = 100000  # 数据库大小
nq = 10000   # 查询数量

# 构建向量库(训练数据)
xb = np.random.random((nb, d)).astype('float32')
xb[:, 0] += np.arange(nb) / 1000.

# 构建查询向量
xq = np.random.random((nq, d)).astype('float32')
xq[:, 0] += np.arange(nq) / 1000.

# 步骤2:创建索引(使用 L2 距离的 Flat 索引)
index = faiss.IndexFlatL2(d)
index.add(xb)  # 将向量添加到索引

# 步骤3:搜索
k = 4  # 返回最近邻的个数
D, I = index.search(xq[:5], k)  # D 返回距离,I 返回索引

print("距离:", D)
print("索引:", I)

📝通俗解释:这个例子展示了最基础的 Faiss 用法:IndexFlatL2 是最简单但最准确的索引(暴力搜索),适合小规模数据。d 是向量维度,nb 是底库大小,k 是要返回的近邻数量。


1.3 Milvus

官网:https://milvus.io/ 中文社区:https://gitee.com/milvus-io/milvus

Milvus 是一款开源的特征向量相似度搜索引擎,使用方便、实用可靠、易于扩展、稳定高效和搜索迅速。

特性说明
高性能涵盖 Faiss、Annoy 和 HNSWlib 等主流第三方索引库,性能高,支持对海量向量数据进行相似搜索
高可用、高可靠支持使用 Kubernetes 部署,支持在云上扩展;容灾能力保证服务高可用;采用日志及数据分离的设计,使用 Pulsar、Kafka 等消息队列实现组件间通信
混合查询支持在向量检索过程中进行标量字段过滤,实现混合查询(向量 + 条件)
开发者友好支持 Python、Java、Go、Node.js 等多语言;提供 Attu 等可视化工具

📝通俗解释:Milvus 是一个完整的向量数据库系统,就像 MySQL 存储结构化数据一样,Milvus 专门存储和检索向量数据。它内置了多种索引算法,可以一键部署使用,适合生产环境。


1.4 ElasticSearch

1.4.1 ElasticSearch 介绍

Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,建立在全文搜索引擎 Apache Lucene 基础上。

📝通俗解释:Elasticsearch 最初是为全文搜索设计的,比如在大量文章中快速找到包含某个关键词的内容。现在也支持向量检索(通过 k-NN 插件)。

Elasticsearch 不仅包括全文搜索功能,还可以:

  • 分布式实时文件存储,并将每一个字段都编入索引,使其可以被搜索
  • 实时分析的分布式搜索引擎
  • 可以扩展到上百台服务器,处理 PB 级别的结构化或非结构化数据

ES 本质上是一个支持全文搜索的分布式内存数据库,特别适用于构建搜索系统,比如内容检索、文本检索、日志检索。其原因是采用了倒排索引

📝通俗解释:ES 适合做搜索系统,但原始的 ES 并不是为向量检索设计的。如果要做高维向量检索,建议使用 ES 8.0+ 的 k-NN 插件或结合 Milvus 使用。

1.4.2 什么是倒排索引?

倒排索引是一种专为搜索设计的索引结构。

先对需要索引的字段进行分词,然后以分词为索引组成一个查找树,这样就把一个全文匹配的查找转换成了对树的查找。

📝通俗解释:想象一本书的目录(正排索引:章节→页码)和索引词(倒排索引:关键词→页码)。当你搜索"苹果"时,倒排索引直接告诉你"苹果"在哪些页面,不需要逐页翻找。

倒排索引相比于一般数据库采用的 B 树索引,写入和更新性能较差,因此倒排索引只适合全文搜索,不适合更新频繁的交易类数据。

📝通俗解释:倒排索引就像把一本小说的所有词汇都做成卡片,每张卡片标明在哪些页出现。找词快了,但每次小说内容改动(比如修改错别字),就要重新整理很多卡片。

1.4.3 ES 机制

Elasticsearch 是面向文档型数据库,一条数据就是一个文档,用 JSON 作为文档序列化的格式。

示例文档:

json
{
  "name": "John",
  "sex": "Male",
  "age": 25,
  "birthDate": "1990/05/01",
  "about": "I love to go rock climbing",
  "interests": ["sports", "music"]
}

Elasticsearch 比较适合存储非结构化或半结构化数据

📝通俗解释:ES 存储的是 JSON 文档,不像关系型数据库需要预先定义严格的表结构,非常灵活。

关系型数据库与 Elasticsearch 术语对照:

关系型数据库Elasticsearch
数据库索引(Index)
类型(type)
文档(Document)
列(Columns)字段(Fields)

📝通俗解释:可以把 Elasticsearch 的"索引"理解为关系型数据库的"数据库",里面可以存放各种类型的文档。就像一个大型图书馆,不同类型的书放在不同的书架上。


总结对比

向量检索库特点适用场景
Annoy轻量级、内存占用小、适合静态数据中小规模向量检索、Spotify 音乐推荐
Faiss性能高、支持 GPU、算法丰富大规模向量检索、深度学习向量匹配
Milvus完整数据库功能、支持混合查询生产环境、向量化搜索系统
ElasticSearch全文搜索能力强、支持 k-NN已有 ES 生态、需要文本+向量混合搜索

📝通俗解释:选型建议——小数据、快速验证用 Annoy;大规模、高性能用 Faiss;生产环境、需要完整数据库功能用 Milvus;已有 ES 生态用 ES 的 k-NN 插件。

基于 MIT 许可发布