Descartes(笛卡尔向量数据库)

重要说明

[ 返回顶部 ⬆️ ]

项目简介

Descartes 是零一万物自研的向量数据库,其搜索内核通过全导航图、自适应邻居选择、两级量化等技术,既能保证超高精度(大于 99%),又能实现超高性能(千万数据量毫秒级响应)

优势

功能

[ 返回顶部 ⬆️ ]

快速上手

系统要求

索引配置

注意

# vector type: float
vector.global.vector_type = float

# dimension of vector:must less than maximum value of uint16_t
vector.global.dimension = 128

# metric type: l2, square_l2, ip
vector.global.metric_type = square_l2

# maximum document count
vector.global.max_doc_cnt = 1000000

# index directory
vector.global.index_dir = /home/ubuntu/indexes

# build result count:optional, default is 400
vector.fng.build.build_res_cnt = 500

# maximum neighbor count:optional, default is 64, can't bigger than 255
vector.fng.build.max_neighbor_cnt = 32

# search result count:optional, default is 400
vector.fng.search.search_res_cnt = 40

vector.pqg.pq.subquantizer_cnt = 128

接口说明

class GraphIndex {
public:
    GraphIndex() = default;
    virtual ~GraphIndex() = default;

public:
    // index init from config file
    virtual int Init(const std::string &configFilePath) = 0;
    
    // add vector to index
    virtual int AddVector(const void *vector, size_t bytes, uint64_t key) = 0;
    
    // search vector in index with context
    virtual int Search(const void *vector, size_t bytes, SearchContext &context) = 0;
    
    // refine the index. Will quantize the index if  quantize is true
    virtual int RefineIndex(bool quantize) = 0;
    
    // dump index
    virtual int Dump() = 0;
    
    virtual uint32_t GetCurrentDocCnt() const = 0;
};

// create index
std::shared_ptr<GraphIndex> CreateGraphIndex();

使用示例

#include <vector>
#include <string>
#include <assert.h>

#include "descartes_index.h"

using namespace descartes;

int main()
{
    auto index = CreateGraphIndex();
    std::string file("./cfg");
    assert(index->Init(file) == 0);
    int dims = 128;
    int cnt = 100000;
    std::vector<float> vecs(dims * cnt);
    for (size_t i = 0; i < vecs.size(); ++i) {
        vecs[i] = i;
    }


    for (int i = 0; i <cnt; ++i) {
        int ret = index->AddVector(vecs.data() + i * dims, sizeof(float) * dims, i);
        assert(ret == 0);
    }
    assert(index->RefineIndex(false) == 0);

    SearchContext ctx;
    ctx.topk = 10;
    ctx.searchResCnt = 20;
    for (size_t i = 0; i < 100; ++i) {
        int ret = index->Search(vecs.data() + i * dims, sizeof(float) * dims, ctx);
        assert(ret == 0);         
    }
    assert(index->Dump() == 0);
}

[ 返回顶部 ⬆️ ]

性能评测

召回与 QPS 结果对比

从 ANN-Benchmarks 测试结果可以看出,Descartes 登顶 6 份数据集评测第一名,比之前榜单上同业第一名有显著性能提升,部分数据集上的性能提升甚至超过 2 倍以上。

本次测试 6 份评测数据集涵盖 6 大数据集:glove-25-angular、glove-100-angular、sift-128-euclidean、nytimes-256-angular、fashion-mnist-784-euclidean、gist-960-euclidean。

其中,横坐标代表召回,纵坐标代表 QPS(每秒内处理的请求数)。曲线位置越偏右上角意味着算法性能越好。可以看出,Descartes 在 6 项数据集评测中都处于最高位。

QPS 结果对比

[ 返回顶部 ⬆️ ]

技术特性

Descartes 搜索内核在处理复杂查询、提高检索效率以及优化数据存储方面相比业界拥有显著的比较优势。

RAG 向量检索主要解决的问题 Descartes 技术特性 业界现状
减少检索考察的候选集
(通过建立某种索引结构)
全导航图

自研图上坐标系导航,既能保证超高精度(大于 99%),又能实现超高性能(千万数据量下毫秒级响应)
通过哈希、KD-Tree、VP-Tree 或随机等方式,导航效果不够精确,裁剪力度不够。
(同上) 自适应邻居选择

首创自适应邻居选择策略,较大提升了 RAG 向量检索性能
没有或者简单的边选择,容易陷入局部最优,潜力挖掘不充分。
降低单个向量计算的复杂度 两级量化

相比于传统 PQ 查表,性能大幅提升 2-3 倍
简单 PQ 量化,在量化本身,以及索引存储上没有特别处理,无法更进一步发挥硬件能力。

全导航图

向量数据库中最核心的技术是「向量检索技术」。随着向量检索技术的不断发展,实践已经证明「基于图的最近邻算法」在检索的精度和性能上脱颖而出,成为了向量检索的主流技术。

因此,零一万物自研了「全导航图」,其本质也是图算法的一种。全局多层缩略图导航技术,图上坐标系导航,既能保证超高精度,又能实现超高性能。

如上图所示,「全导航图」由 2 层次导航组成:

自适应邻居选择

零一万物自研的「自适应邻居选择策略」,突破了以往全依赖真实 topk 或固定边选择策略的局限,使每个节点可以根据自身及邻居的分布特征,动态地选取最佳邻居边,更快收敛接近目标向量。

[ 返回顶部 ⬆️ ]

连通性保障

冗余邻居消除

索引结构优化

两级量化

零一万物通过两层量化,将 float 类型量化到 int8,大幅减少检索过程中需要的带宽和计算量。

同时,列式存储充分利用 avx512 的单指令多数据特性,进一步发挥硬件能力。相比传统 PQ 查表,性能得到大幅提升到 2-3 倍。

索引构建优化

[ 返回顶部 ⬆️ ]

选型考量

零一万物在研发 Descartes 搜索内核时,学习和参考了多种经典思路。

[ 返回顶部 ⬆️ ]

更多资源

[ 返回顶部 ⬆️ ]