外观
向量检索引擎:Annoy、Faiss、Milvus与ElasticSearch
内容整理自学习笔记,仅供面试备考参考;不构成录用、培训或考试承诺。
1. Annoy
GitHub: https://github.com/spotify/annoy
1.1 Annoy 介绍
Annoy 是高维空间求近似最近邻的一个开源库,全称 Approximate Nearest Neighbors Oh Yeah,是一种适合实际应用的快速相似查找算法。Annoy 构建一个二叉树,查询时间为 O(log n)。
1.2 Annoy 使用
安装:pip install annoy
python
from annoy import AnnoyIndex
import random
# 向量的维度
f = 40
# 返回一个可读可写的存储 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
print(u.get_nns_by_item(0, 1000)) # 找到 1000 个最近邻1.3 向量检索函数
| 函数 | 说明 |
|---|---|
get_nns_by_item(i, n, search_k=-1, include_distances=False) | 返回最接近 item i 的 n 个 item;include_distances=True 时返回元组(items 列表 + distances 列表) |
get_nns_by_vector(v, n, search_k=-1, include_distances=False) | 给定查询向量 v,返回 n 个最近邻的 item,常用于给定用户 embedding 查询 |
get_item_vector(i) | 返回索引 i 对应的向量 |
get_distance(i, j) | 返回 item_i 和 item_j 的平方距离 |
get_n_items() | 返回索引中的 items 个数(词典大小) |
get_n_trees() | 返回索引树的个数 |
1.4 关键调参参数
| 参数 | 说明 | 影响 |
|---|---|---|
n_trees | 构建期间提供 | 值越大结果越准确,但索引越大,影响构建时间和索引大小 |
search_k | 运行时提供 | 值越大结果越准确,但返回时间越长。默认为 n_trees * n |
提示:
search_k实现了准确性和速度之间的运行时权衡,可根据实际场景调整。
2. Faiss
GitHub: https://github.com/facebookresearch/faiss
Tutorial: https://github.com/facebookresearch/faiss/wiki/Getting-started
2.1 Faiss 介绍
Faiss 是由 Facebook 开发的适用于稠密向量匹配的开源库,支持 C++ 与 Python 调用,是向量化检索的开山鼻祖应用。支持多种向量检索方式,包括内积、欧氏距离等,同时支持精确检索与模糊搜索。
2.2 Faiss 主要特性
| 特性 | 说明 |
|---|---|
| 相似度检索和聚类 | 支持多种相似度计算方式 |
| 多种索引方式 | 灵活选择索引类型 |
| CPU 和 GPU 计算 | 支持 GPU 加速 |
| Python 和 C++ 调用 | 多语言支持 |
2.3 Faiss 使用
安装:pip install faiss-cpu --no-cache
使用方法归纳为三个步骤:
python
import numpy as np
import faiss
d = 64 # 向量维度
nb = 100000 # 向量库大小
nq = 10000 # 查询向量数量
# 步骤1:构建向量库
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:选择合适的 index,将向量 add 到 index
index = faiss.IndexFlatL2(d)
index.add(xb)
# 步骤3:search 得到最终结果
k = 4
D, I = index.search(xq[:5], k) # 分别返回距离和索引三步法:① 构建向量库(矩阵形式)→ ② 选择 index 并 add → ③ search 检索
3. Milvus
Milvus 是一款开源的特征向量相似度搜索引擎,使用方便、实用可靠、易于扩展、稳定高效和搜索迅速。
| 特性 | 说明 |
|---|---|
| 高性能 | 涵盖 Faiss、Annoy、hnswlib 等主流第三方索引库,支持海量向量相似搜索 |
| 高可用高可靠 | 支持 Kubernetes 部署和云上扩展,容灾能力保证服务高可用 |
| 云原生架构 | 使用 Pulsar、Kafka 等消息队列实现组件间通信解耦 |
| 混合查询 | 支持向量检索过程中进行标量字段过滤 |
| 开发者友好 | 支持 Python、Java、Go、Node.js,提供 Attu 等工具 |
4. ElasticSearch
4.1 ElasticSearch 介绍
Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,建立在全文搜索引擎 Apache Lucene 基础之上。它不仅是全文搜索功能,还具备以下能力:
| 能力 | 说明 |
|---|---|
| 分布式实时文件存储 | 每一个字段都编入索引,可被搜索 |
| 实时分析 | 分布式搜索引擎 |
| 海量数据处理 | 可扩展到上百台服务器,处理 PB 级结构化或非结构化数据 |
ES 本质上是一个支持全文搜索的分布式内存数据库,特别适用于内容检索、文本检索、日志检索。其核心原因是采用了倒排索引。
4.2 什么是倒排索引?
倒排索引是一种特别为搜索而设计的索引结构:
- 先对需要索引的字段进行分词
- 然后以分词为索引组成一个查找树
- 这样就把一个全文匹配的查找转换成了对树的查找
| 对比 | 倒排索引 | B树索引 |
|---|---|---|
| 适用场景 | 全文搜索 | 交易类数据 |
| 查询性能 | 全文搜索快 | 等值/范围查询快 |
| 写入更新 | 性能较差 | 性能较好 |
注意:倒排索引的写入和更新性能较差,只适合全文搜索,不适合更新频繁的交易类数据。
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"]
}ES 比较适合存储非结构化或半结构化数据。
4.4 ES 与关系型数据库术语对照
| 关系数据库 | Elasticsearch |
|---|---|
| 数据库(Database) | 索引(Index) |
| 表(Table) | 类型(Type) |
| 行(Row) | 文档(Document) |
| 列(Column) | 字段(Field) |