HNSWLib项目C++示例详解:从基础使用到高级功能

HNSWLib项目C++示例详解:从基础使用到高级功能

hnswlib Header-only C++/python library for fast approximate nearest neighbors hnswlib 项目地址: https://gitcode.com/gh_mirrors/hn/hnswlib

前言

HNSW(Hierarchical Navigable Small World)是一种高效的近似最近邻搜索算法,而HNSWLib是其C++实现库。本文将深入解析HNSWLib的C++示例代码,帮助开发者快速掌握这一强大工具的使用方法。

基础使用示例

索引创建与数据插入

首先我们来看最基本的索引创建和数据插入操作:

#include "../../hnswlib/hnswlib.h"

int main() {
    // 参数设置
    int dim = 16;               // 向量维度
    int max_elements = 10000;   // 最大元素数量
    int M = 16;                 // 图的连接数,影响内存消耗和搜索性能
    int ef_construction = 200;  // 构建时的搜索范围,影响构建质量和速度
    
    // 初始化索引空间(使用L2距离)
    hnswlib::L2Space space(dim);
    hnswlib::HierarchicalNSW<float>* alg_hnsw = 
        new hnswlib::HierarchicalNSW<float>(&space, max_elements, M, ef_construction);
    
    // 生成随机数据
    std::mt19937 rng(47);  // 固定种子保证可重复性
    std::uniform_real_distribution<> distrib_real;
    float* data = new float[dim * max_elements];
    for (int i = 0; i < dim * max_elements; i++) {
        data[i] = distrib_real(rng);
    }
    
    // 添加数据到索引
    for (int i = 0; i < max_elements; i++) {
        alg_hnsw->addPoint(data + i * dim, i);
    }
    
    // ... 后续操作
}

关键点解析:

  1. M参数控制图中每个节点的连接数,值越大索引越精确但内存消耗也越大
  2. ef_construction影响构建质量,值越大构建时间越长但索引质量越好
  3. 数据插入时需要一次性指定最大元素数量,这是HNSW的一个限制

搜索与召回率测试

构建完索引后,我们可以测试其搜索效果:

// 查询元素自身并计算召回率
float correct = 0;
for (int i = 0; i < max_elements; i++) {
    auto result = alg_hnsw->searchKnn(data + i * dim, 1);
    if (result.top().second == i) correct++;
}
std::cout << "Recall: " << correct / max_elements << "\n";

这里我们测试的是"自查询"的召回率,即每个向量能否正确找到自己,这是最基本的正确性测试。

序列化与反序列化

HNSW索引支持序列化到文件,便于保存和加载:

// 序列化索引
std::string hnsw_path = "hnsw.bin";
alg_hnsw->saveIndex(hnsw_path);
delete alg_hnsw;

// 反序列化索引
alg_hnsw = new hnswlib::HierarchicalNSW<float>(&space, hnsw_path);

序列化功能在实际应用中非常重要,可以避免每次使用时都重新构建索引。

高级功能示例

搜索时过滤

HNSWLib支持在搜索时进行结果过滤,这在很多实际场景中非常有用:

// 定义过滤函数:只允许能被divisor整除的标签
class PickDivisibleIds: public hnswlib::BaseFilterFunctor {
    unsigned int divisor;
public:
    PickDivisibleIds(unsigned int divisor): divisor(divisor) {
        assert(divisor != 0);
    }
    bool operator()(hnswlib::labeltype label_id) {
        return label_id % divisor == 0;
    }
};

// 使用过滤函数进行搜索
PickDivisibleIds pickEven(2);  // 只允许偶数标签
auto result = alg_hnsw->searchKnnCloserFirst(query_vector, k, &pickEven);

这种过滤机制可以用于实现各种业务逻辑,如:

  • 只搜索特定类别的物品
  • 排除某些不符合条件的结果
  • 实现复杂的业务规则过滤

删除元素与内存重用

HNSWLib支持标记删除元素并重用其内存空间:

// 初始化索引时开启删除功能
hnswlib::HierarchicalNSW<float>* alg_hnsw = 
    new hnswlib::HierarchicalNSW<float>(&space, max_elements, M, ef_construction, 100, true);

// 标记删除部分元素
for (int i = 0; i < num_deleted; i++) {
    alg_hnsw->markDelete(i);
}

// 重用被删除元素的内存空间
for (int i = 0; i < num_deleted; i++) {
    int new_label = max_elements + i;
    alg_hnsw->addPoint(new_data + i * dim, new_label, true);  // replace_deleted=true
}

注意事项:

  1. 需要初始化时设置allow_replace_deleted=true
  2. 删除只是标记,内存不会被立即释放
  3. 添加新元素时需显式指定replace_deleted=true才能重用空间

多线程示例

HNSWLib支持多线程操作,主要包括:

  1. 多线程构建索引和插入数据
  2. 多线程搜索
  3. 多线程过滤搜索
  4. 多线程删除和替换

由于代码较长,这里不展开,但需要注意:

  • 插入操作需要适当的同步
  • 搜索操作本质是线程安全的
  • 删除和添加操作需要协调

其他高级功能

多向量搜索

支持同时对多个向量进行搜索,可以用于批量查询场景。

Epsilon搜索

基于半径的搜索,返回距离小于给定阈值的所有邻居,适用于确定范围的搜索场景。

性能调优建议

  1. 参数M的选择:通常16-64之间,越高精度越好但内存消耗越大
  2. ef_construction设置:构建时越大越好,但会减慢构建速度
  3. 搜索时的ef参数:搜索时可以动态设置,越大结果越精确但速度越慢
  4. 内存考虑:索引大小与M和max_elements成正比
  5. 批量插入:批量插入时可以考虑预分配空间

结语

HNSWLib提供了强大而灵活的近似最近邻搜索功能,通过本文的示例解析,开发者可以快速掌握其核心用法和高级特性。在实际应用中,建议根据具体场景调整参数,并通过实验找到最佳的精度/性能平衡点。

hnswlib Header-only C++/python library for fast approximate nearest neighbors hnswlib 项目地址: https://gitcode.com/gh_mirrors/hn/hnswlib

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水鲁焘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值