接上一篇,本文我们来简单剖析一下Milvus的五种主要检索算法(FLAT、IVF、HNSW、ANNOY、DISKANN)的源码实现。为了便于理解,我们将依次探讨每种算法的实现原理和架构,并提供核心代码片段和详细解释。

Milvus主要使用Faiss、HNSWlib和其他库来实现这些检索算法。因此,我们将参考这些库的源码来理解Milvus中的实现。

1. FLAT(Brute-force)

原理和架构

FLAT(Brute-force)是最简单的检索算法。其原理是遍历所有向量,计算查询向量与每个向量的距离,找到最近的向量。

核心实现

在Faiss库中,FLAT索引用一个数组存储所有向量,并通过线性扫描进行检索。

// Faiss中的Flat索引实现
#include <faiss/IndexFlat.h>

// 创建FLAT索引
faiss::IndexFlatL2 index(d); // d是向量的维度

// 添加向量到索引中
index.add(n, xb); // n是向量的数量,xb是向量数据

// 查询向量
index.search(1, xq, k, D, I); // xq是查询向量,k是返回的最近邻数量,D存储距离,I存储索引
FLAT原理
存储向量
数组存储
搜索
遍历所有向量
计算距离
找到最近邻

Milvus中的实现

在Milvus中,FLAT索引通过调用Faiss库来实现。

#include <faiss/IndexFlat.h>

// Milvus中的FLAT索引实现
void MilvusFlatIndex::Add(const float* data, size_t num_vectors) {
    faiss::IndexFlatL2 index(dimension_);
    index.add(num_vectors, data);
}

void MilvusFlatIndex::Search(const float* query, size_t topk, float* distances, int64_t* labels) {
    index.search(1, query, topk, distances, labels);
}

2. IVF(Inverted File)

原理和架构

IVF(Inverted File)通过K-means聚类将数据划分为若干个簇,创建倒排列表。查询时,首先找到最近的簇,然后在该簇中进行搜索。

核心实现

在Faiss库中,IVF索引用聚类和倒排文件来加速检索。

#include <faiss/IndexIVFFlat.h>

// 创建IVF索引
faiss::IndexFlatL2 quantizer(d);
faiss::IndexIVFFlat index(&quantizer, d, nlist); // nlist是簇的数量

// 训练索引
index.train(n, xb); // xb是训练数据

// 添加向量到索引中
index.add(n, xb);

// 查询向量
index.search(1, xq, k, D, I);
IVF原理
聚类
K-means算法
创建倒排列表
簇与倒排列表
搜索
找到最近簇
倒排列表精确搜索

Milvus中的实现

在Milvus中,IVF索引通过调用Faiss库来实现。

#include <faiss/IndexIVFFlat.h>

// Milvus中的IVF索引实现
void MilvusIVFIndex::Train(const float* data, size_t num_vectors) {
    faiss::IndexFlatL2 quantizer(dimension_);
    faiss::IndexIVFFlat index(&quantizer, dimension_, nlist_);
    index.train(num_vectors, data);
}

void MilvusIVFIndex::Add(const float* data, size_t num_vectors) {
    index.add(num_vectors, data);
}

void MilvusIVFIndex::Search(const float* query, size_t topk, float* distances, int64_t* labels) {
    index.search(1, query, topk, distances, labels);
}

3. HNSW(Hierarchical Navigable Small World)

原理和架构

HNSW(Hierarchical Navigable Small World)通过构建层次化的小世界图,实现快速的近似最近邻搜索。HNSW包含多层,每层是一个小世界图。查询时,从最高层逐层向下导航,找到最相似的向量。

核心实现

在HNSWlib库中,HNSW通过构建多层图结构实现高效检索。

#include <hnswlib/hnswlib.h>

// 创建HNSW索引
hnswlib::L2Space space(d);
hnswlib::HierarchicalNSW<float> index(&space, max_elements, M, ef_construction);

// 添加向量到索引中
index.addPoint(data_point, label);

// 查询向量
auto result = index.searchKnn(query_point, k);
HNSW原理
构建层次结构
高层次连接较多
低层次连接较少
构建小世界图
遵循小世界特性
搜索
从最高层导航
逐层向下
找到最相似向量

Milvus中的实现

在Milvus中,HNSW索引通过调用HNSWlib库来实现。

#include <hnswlib/hnswlib.h>

// Milvus中的HNSW索引实现
void MilvusHNSWIndex::Add(const float* data, size_t num_vectors) {
    hnswlib::L2Space space(dimension_);
    hnswlib::HierarchicalNSW<float> index(&space, max_elements_, M_, ef_construction_);
    index.addPoint(data, label);
}

void MilvusHNSWIndex::Search(const float* query, size_t topk, float* distances, int64_t* labels) {
    auto result = index.searchKnn(query, topk);
    // 处理结果
}

4. ANNOY(Approximate Nearest Neighbors Oh Yeah)

原理和架构

ANNOY(Approximate Nearest Neighbors Oh Yeah)通过构建多棵随机树,实现近似最近邻搜索。查询时,利用多棵随机树进行搜索,并合并结果。

核心实现

在Annoy库中,ANNOY通过随机投影树实现快速检索。

#include <annoylib.h>

// 创建ANNOY索引
AnnoyIndex<int, float, Euclidean, Kiss32Random> index(d);

// 添加向量到索引中
index.add_item(i, vector);

// 构建索引
index.build(n_trees);

// 查询向量
std::vector<int> result;
index.get_nns_by_vector(query, k, -1, &result, &distances);
ANNOY原理
构建随机树
随机选择数据点
随机选择分裂点
搜索
多棵随机树搜索
合并结果
找到近似最近邻

Milvus中的实现

在Milvus中,ANNOY索引通过调用Annoy库来实现。

#include <annoylib.h>

// Milvus中的ANNOY索引实现
void MilvusANNOYIndex::Add(const float* data, size_t num_vectors) {
    AnnoyIndex<int, float, Euclidean, Kiss32Random> index(dimension_);
    index.add_item(i, vector);
    index.build(n_trees_);
}

void MilvusANNOYIndex::Search(const float* query, size_t topk, float* distances, int64_t* labels) {
    std::vector<int> result;
    index.get_nns_by_vector(query, topk, -1, &result, &distances);
    // 处理结果
}

5. DISKANN(Disk-based Approximate Nearest Neighbors)

原理和架构

DISKANN(Disk-based Approximate Nearest Neighbors)通过将数据存储在磁盘上,实现大规模数据集的高效检索。DISKANN通过分块存储数据,并创建索引文件。在查询时,从磁盘加载索引文件,利用索引进行快速的近似搜索。

核心实现

DISKANN的实现主要依赖于自定义的磁盘存储和索引管理机制,具体源码实现可以参考DISKANN库。

// 示例代码片段
#include <diskann/diskann.h>

// 创建DISKANN索引
diskann::Index<int, float>

 index(d, max_elements, L2, index_file_size);

// 添加向量到索引中
index.add(data_point);

// 查询向量
index.search(query_point, k, distances, labels);
DISKANN原理
构建索引
数据分块
存储在磁盘
加载索引
从磁盘加载
搜索
利用索引快速搜索

Milvus中的实现

在Milvus中,DISKANN索引通过调用DISKANN库来实现。

#include <diskann/diskann.h>

// Milvus中的DISKANN索引实现
void MilvusDISKANNIndex::Add(const float* data, size_t num_vectors) {
    diskann::Index<int, float> index(dimension_, max_elements_, L2, index_file_size_);
    index.add(data_point);
}

void MilvusDISKANNIndex::Search(const float* query, size_t topk, float* distances, int64_t* labels) {
    index.search(query, topk, distances, labels);
    // 处理结果
}

总结

通过深入剖析Milvus的五种主要检索算法(FLAT、IVF、HNSW、ANNOY、DISKANN)的实现原理和架构,我们可以看到每种算法都有其特定的实现细节和应用场景。Milvus通过集成Faiss、HNSWlib、Annoy和DISKANN等库,实现了高效的向量检索功能。

总结
深入剖析Milvus检索算法
FLAT算法
IVF算法
HNSW算法
ANNOY算法
DISKANN算法
结合核心代码示例
理解实现细节

如果你喜欢这篇文章,别忘了收藏文章、关注作者、订阅专栏,感激不尽。

GitHub 加速计划 / mi / milvus
28.68 K
2.76 K
下载
A cloud-native vector database, storage for next generation AI applications
最近提交(Master分支:3 个月前 )
00edec2e issue: https://github.com/milvus-io/milvus/issues/35853 Signed-off-by: Buqian Zheng <zhengbuqian@gmail.com> 5 天前
3cdb4850 action for https://github.com/milvus-io/milvus/issues/37166#issuecomment-2469502955 Signed-off-by: zhuwenxing <wenxing.zhu@zilliz.com> 5 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐