人工智能算法可以对各种数据进行分析,从而得到各种数据的特征向量,这些特征向量可以用来表示各种数据,比如图像、视频、文本等。在实际应用中,我们经常需要对这些数据进行检索,比如在图像搜索引擎中,我们输入一张图片,搜索引擎会返回与这张图片相似的图片。这种基于向量的检索技术,被称为向量搜索引擎。
下面的资料介绍了比较流行的向量搜索引擎:
根据是否开源、文档是否完善、社区活跃度等因素列出了可以根据项目实现参考的向量搜索引擎:
- Faiss 是 Facebook AI Research 开源的一款向量搜索库,基于 C++ 实现,GitHub 20k star。
- 优点:多个向量搜索引擎的基础库,支持多种向量索引算法,代码库更新仍然很活跃
- 缺点:不支持分布式搜索
- Vearch 是京东开源的向量搜索框架,基于 Go 语言实现,GitHub 1.5k star
- 优点:基于 Faiss 开发,支持分布式搜索
- 缺点:代码库已经 1 年没有更新,文档不完善
- Milvus 基于 Go 语言实现
- 优点:对比 Vearch,在社区活跃度、支持度上具有更明显的优势,也有很多公司采用 Milvus 作为底层引擎
各个搜索引擎基本上都参考了 Faiss 实现,或者直接使用 Faiss 作为基础库,所以我们可以直接详细介绍 Faiss,以及 Faiss 的一些使用方法和性能评测。
详细安装文档和编译选项可以参考官方文档 INSTALL.md,我选择了以下两种环境从源码编译安装:
- Windows 11 for Sub Linux (x86)
- M1 MacBook Air (arm)
Requirements:
- CMake
- OpenMP
- BLAS
- swig
这里禁用了 GPU 构建索引编译
cmake -DFAISS_ENABLE_GPU=OFF -B build .
源码的 demos/
和 tutorial/
目录包含了一些简单的使用样例。Faiss 包含非常多的索引类型,首先尝试最简单的 IndexFlatL2,基于 L2 距离进行暴力搜索。
// tutorial/cpp/1-Flat.cpp
// make -C build 1-Flat
#include <cstdio>
#include <cstdlib>
#include <random>
#include <chrono>
#include <iostream>
#include <faiss/IndexFlat.h>
// 64-bit int
using idx_t = faiss::idx_t;
using namespace std::chrono;
int main() {
int d = 128; // dimension
int nb = 1000000; // database size
int nq = 1; // nb of queries
std::mt19937 rng;
std::uniform_real_distribution<> distrib;
float* xb = new float[d * nb];
float* xq = new float[d * nq];
auto start = high_resolution_clock::now();
for (int i = 0; i < nb; i++) {
for (int j = 0; j < d; j++)
xb[d * i + j] = distrib(rng);
xb[d * i] += i / 1000.;
}
auto stop = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(stop - start);
std::cout << duration.count() << std::endl;
for (int i = 0; i < nq; i++) {
for (int j = 0; j < d; j++)
xq[d * i + j] = distrib(rng);
xq[d * i] += i / 1000.;
}
faiss::IndexFlatL2 index(d); // call constructor
printf("is_trained = %s\n", index.is_trained ? "true" : "false");
index.add(nb, xb); // add vectors to the index
printf("ntotal = %zd\n", index.ntotal);
int k = 4;
{ // sanity check: search 5 first vectors of xb
idx_t* I = new idx_t[k * 5];
float* D = new float[k * 5];
index.search(5, xb, k, D, I);
// print results
printf("I=\n");
for (int i = 0; i < 5; i++) {
for (int j = 0; j < k; j++)
printf("%5zd ", I[i * k + j]);
printf("\n");
}
printf("D=\n");
for (int i = 0; i < 5; i++) {
for (int j = 0; j < k; j++)
printf("%7g ", D[i * k + j]);
printf("\n");
}
delete[] I;
delete[] D;
}
{ // search xq
idx_t* I = new idx_t[k * nq];
float* D = new float[k * nq];
auto start = high_resolution_clock::now();
index.search(nq, xq, k, D, I);
auto stop = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(stop - start);
std::cout << duration.count() << std::endl;
// print results
printf("I (5 first results)=\n");
for (int i = 0; i < 1; i++) {
for (int j = 0; j < k; j++)
printf("%5zd ", I[i * k + j]);
printf("\n");
}
// printf("I (5 first results)=\n");
// for (int i = 0; i < 5; i++) {
// for (int j = 0; j < k; j++)
// printf("%5zd ", I[i * k + j]);
// printf("\n");
// }
// printf("I (5 last results)=\n");
// for (int i = nq - 5; i < nq; i++) {
// for (int j = 0; j < k; j++)
// printf("%5zd ", I[i * k + j]);
// printf("\n");
// }
delete[] I;
delete[] D;
}
delete[] xb;
delete[] xq;
return 0;
}
分别修改 d,nb,q 的值编译运行程序,结果如下:
benchmark
特征库大小 | 待查询的特征数量 | 特征维度 | 内存消耗 | 运行时间 |
---|---|---|---|---|
5k | 1 | 256 | - | 3ms |
5k | 1 | 512 | - | 7ms |
5k | 1 | 1024 | - | 14ms |
5k | 10 | 256 | - | 4ms |
5k | 10 | 512 | - | 10ms |
5k | 10 | 1024 | - | 17ms |
1w | 1 | 256 | - | 7ms |
1w | 1 | 512 | - | 14ms |
1w | 1 | 1024 | - | 28ms |
1w | 10 | 256 | - | 10ms |
1w | 10 | 512 | - | 17ms |
1w | 10 | 1024 | - | 33ms |
10w | 1 | 64 | - | 18ms |
10w | 1 | 128 | - | 36ms |
10w | 1 | 256 | - | 71ms |
10w | 1 | 512 | 169M | 154ms |
10w | 1 | 1024 | 410M | 286ms |
10w | 10 | 64 | - | 24ms |
10w | 10 | 128 | - | 41ms |
10w | 10 | 256 | - | 81ms |
10w | 10 | 512 | 141M | 161ms |
10w | 10 | 1024 | 372M | 320ms |
50w | 1 | 64 | - | 90ms |
50w | 1 | 128 | - | 180ms |
50w | 1 | 256 | - | 359ms |
50w | 10 | 64 | - | 106ms |
50w | 10 | 128 | - | 204ms |
50w | 10 | 256 | - | 404ms |
50w | 1 | 512 | 2G | 823ms |
50w | 1 | 1024 | 4G | 1423ms |
100w | 1 | 64 | - | 183ms |
100w | 1 | 128 | 1G | 361ms |
100w | 1 | 256 | 2G | 787ms |
100w | 10 | 64 | 206M | 213ms |
100w | 10 | 128 | 1G | 515ms |
100w | 10 | 256 | 2G | 806ms |