第7篇:深入浅出Milvus五种检索算法的核心源码
接上一篇,本文我们来简单剖析一下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存储索引
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);
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);
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);
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);
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等库,实现了高效的向量检索功能。
如果你喜欢这篇文章,别忘了收藏文章、关注作者、订阅专栏,感激不尽。
更多推荐
所有评论(0)