Rust高效近似最近邻搜索库hnsw_rs的使用,hnsw_rs提供高性能向量索引与快速相似度检索功能

Rust高效近似最近邻搜索库hnsw_rs的使用

功能特性

hnsw_rs是一个基于分层可导航小世界图(HNSW)算法的Rust实现库,提供了高效的近似最近邻搜索功能。该库支持多种距离度量方式,包括:

  • 常见向量距离:L1范数、L2范数、余弦相似度、Jaccard相似度、汉明距离
  • 特殊数据类型:支持u16数组的Levenshtein距离
  • 概率分布距离:Hellinger距离、Jeffreys散度、Jensen-Shannon距离
  • 自定义距离:可通过实现Trait来定义自己的距离度量

核心功能

  1. 多线程支持:支持并行插入数据和并行搜索
  2. 持久化存储:可将索引结构和数据保存到磁盘
  3. 内存优化:采用紧凑的内存结构存储图拓扑
  4. 搜索过滤:支持在搜索过程中进行结果过滤
  5. 大文件支持:通过mmap处理大型数据集

完整使用示例

use hnsw_rs::prelude::*;
use hnsw_rs::dist::*;
use rand::Rng;

fn main() {
    // 初始化参数
    let dimension = 128;       // 向量维度
    let nb_elements = 10000;   // 数据集大小
    let max_connections = 32;  // 每层最大连接数
    let nb_layers = (nb_elements as f32).ln().ceil() as usize; // 自动计算层数
    let ef_construction = 200; // 构建时的搜索范围
    
    // 创建HNSW索引
    let mut hnsw_index = Hnsw::<f32, DistCosine>::new(
        max_connections,
        nb_elements,
        nb_layers,
        ef_construction,
        DistCosine{}
    );
    
    // 生成随机测试数据
    let mut rng = rand::thread_rng();
    let mut dataset = Vec::with_capacity(nb_elements);
    for _ in 0..nb_elements {
        let mut vector = Vec::with_capacity(dimension);
        for _ in 0..dimension {
            vector.push(rng.gen::<f32>());
        }
        dataset.push(vector);
    }
    
    // 准备插入数据(向量引用 + ID)
    let insert_data: Vec<(&Vec<f32>, usize)> = dataset
        .iter()
        .enumerate()
        .map(|(id, vec)| (vec, id))
        .collect();
    
    // 并行插入数据
    hnsw_index.parallel_insert(&insert_data);
    
    // 构建查询向量
    let query_vector = vec![0.5; dimension];
    let k_neighbors = 10;      // 需要查找的最近邻数量
    let search_ef = 100;       // 搜索时的扩展因子
    
    // 执行搜索
    let results = hnsw_index.search(&query_vector, k_neighbors, search_ef);
    
    // 输出结果
    println!("Top {} 最近邻:", k_neighbors);
    for neighbor in results {
        println!("ID: {}, 相似度: {:.4}", neighbor.d_id, 1.0 - neighbor.distance);
    }
    
    // 保存索引到文件
    hnsw_index.save("my_index.hnsw").unwrap();
    
    // 可以从文件重新加载索引
    let loaded_index = Hnsw::<f32, DistCosine>::load("my_index.hnsw").unwrap();
}

性能优化建议

  1. 参数调优

    • max_connections:影响构建时间和搜索质量,通常16-64之间
    • ef_construction:构建时的搜索范围,影响索引质量
    • 搜索时的ef参数:越大结果越准确但搜索越慢
  2. 内存管理

    • 对于大型数据集,考虑启用mmap功能
    • 批量插入数据时使用parallel_insert提高效率
  3. 距离度量选择

    • 欧式距离:DistL2
    • 余弦相似度:DistCosine
    • 自定义距离:实现Distance trait

应用场景

  1. 推荐系统中的相似物品查找
  2. 自然语言处理中的语义搜索
  3. 图像检索中的相似图片查找
  4. 异常检测中的最近邻分析

注意事项

  1. 索引构建时间与数据规模成正比,大数据集需要较长时间
  2. 高维数据(>1000维)性能会下降,建议先做降维处理
  3. 搜索精度与速度需要根据应用场景做权衡

1 回复

Rust高效近似最近邻搜索库hnsw_rs使用指南

介绍

hnsw_rs是Rust语言实现的高性能近似最近邻(ANN)搜索库,基于分层可导航小世界图(HNSW)算法。该库提供了高效的向量索引构建和快速相似度检索功能,特别适合处理高维向量数据。

主要特点:

  • 高性能的近似最近邻搜索
  • 支持多种距离度量(欧氏距离、余弦相似度等)
  • 内存高效的数据结构
  • 多线程支持
  • 简单的API接口

安装

在Cargo.toml中添加依赖:

[dependencies]
hnsw_rs = "0.2"

基本使用方法

1. 创建HNSW索引

use hnsw_rs::prelude::*;
use hnsw_rs::dist::*;

// 配置HNSW参数
let max_nb_connection = 15;
let nb_layer = 16;
let ef_c = 200;
let ef_s = 400;
let dim = 128; // 向量维度

// 创建使用欧氏距离的HNSW索引
let mut hnsw = Hnsw::new(Euclidean::new(), max_nb_connection, nb_layer, ef_c, ef_s, dim);

2. 添加向量到索引

// 生成一些随机向量作为示例
let nb_elem = 1000;
let mut rng = rand::thread_rng();
let data: Vec<Vec<f32>> = (0..nb_elem)
    .map(|_| (0..dim).map(|_| rng.gen()).collect())
    .collect();

// 将向量添加到索引
for (i, vec) in data.iter().enumerate() {
    hnsw.insert((i, vec));
}

3. 最近邻搜索

// 创建一个查询向量
let query: Vec<f32> = (0..dim).map(|_| rng.gen()).collect();

// 搜索最近的5个邻居
let ef_search = 100;
let res = hnsw.search(&query, 5, ef_search);

println!("Search results:");
for r in res {
    println!("id: {}, distance: {}", r.d_id, r.distance);
}

高级功能

并行插入

use rayon::prelude::*;

// 并行插入大量向量
data.par_iter().enumerate().for_each(|(i, vec)| {
    hnsw.insert((i, vec));
});

使用不同距离度量

// 使用余弦相似度
let mut hnsw_cos = Hnsw::new(Cosine::new(), max_nb_connection, nb_layer, ef_c, ef_s, dim);

// 使用点积
let mut hnsw_dot = Hnsw::new(DotProduct::new(), max_nb_connection, nb_layer, ef_c, ef_s, dim);

保存和加载索引

use std::fs::File;
use std::io::{BufWriter, BufReader};

// 保存索引到文件
let file = File::create("hnsw_index.bin").unwrap();
let mut writer = BufWriter::new(file);
hnsw.dump(&mut writer).unwrap();

// 从文件加载索引
let file = File::open("hnsw_index.bin").unwrap();
let mut reader = BufReader::new(file);
let loaded_hnsw = Hnsw::<Euclidean, u32>::load(&mut reader).unwrap();

性能调优参数

  • max_nb_connection: 每层的最大连接数,影响索引构建时间和搜索质量
  • nb_layer: 图的层数,通常与数据量对数相关
  • ef_c: 构建时的搜索范围,影响构建时间和索引质量
  • ef_s: 搜索时的搜索范围,影响搜索质量和速度

实际应用示例

// 假设我们有一些文本嵌入向量
let embeddings: Vec<Vec<f32>> = get_text_embeddings(); // 伪代码函数

// 构建索引
let mut hnsw = Hnsw::new(Cosine::new(), 16, 8, 200, 400, 768); // 假设嵌入维度是768

// 插入数据
for (i, emb) in embeddings.iter().enumerate() {
    hnsw.insert((i, emb));
}

// 查询相似文本
let query_embedding = embed_text("Rust programming"); // 伪代码函数
let results = hnsw.search(&query_embedding, 10, 200);

// 获取最相似的文本ID
let most_similar_id = results[0].d_id;

hnsw_rs库非常适合需要快速相似度搜索的应用场景,如推荐系统、语义搜索、图像检索等。通过调整参数可以在搜索精度和速度之间取得平衡。

完整示例代码

use hnsw_rs::prelude::*;
use hnsw_rs::dist::*;
use rand::Rng;
use rayon::prelude::*;
use std::fs::File;
use std::io::{BufWriter, BufReader};

fn main() {
    // 1. 创建HNSW索引
    let max_nb_connection = 16;
    let nb_layer = 8;
    let ef_c = 200;
    let ef_s = 400;
    let dim = 128;
    
    // 使用欧氏距离
    let mut hnsw = Hnsw::new(Euclidean::new(), max_nb_connection, nb_layer, ef_c, ef_s, dim);
    
    // 2. 生成并添加数据
    let nb_elem = 10000;
    let mut rng = rand::thread_rng();
    let data: Vec<Vec<f32>> = (0..nb_elem)
        .map(|_| (0..dim).map(|_| rng.gen()).collect())
        .collect();
    
    // 并行插入数据
    data.par_iter().enumerate().for_each(|(i, vec)| {
        hnsw.insert((i, vec));
    });
    
    // 3. 执行搜索
    let query: Vec<f32> = (0..dim).map(|_| rng.gen()).collect();
    let results = hnsw.search(&query, 5, 200);
    
    println!("Top 5 neighbors:");
    for (i, result) in results.iter().enumerate() {
        println!("{}. id: {}, distance: {:.4}", i+1, result.d_id, result.distance);
    }
    
    // 4. 保存和加载索引
    // 保存
    let file = File::create("hnsw_index.bin").unwrap();
    let mut writer = BufWriter::new(file);
    hnsw.dump(&mut writer).unwrap();
    
    // 加载
    let file = File::open("hnsw_index.bin").unwrap();
    let mut reader = BufReader::new(file);
    let loaded_hnsw = Hnsw::<Euclidean, usize>::load(&mut reader).unwrap();
    
    // 使用加载的索引搜索
    let new_results = loaded_hnsw.search(&query, 5, 200);
    println!("Results from loaded index:");
    for r in new_results {
        println!("id: {}, distance: {:.4}", r.d_id, r.distance);
    }
}

这个完整示例展示了hnsw_rs库的主要功能:

  1. 创建HNSW索引
  2. 并行插入大量向量数据
  3. 执行近似最近邻搜索
  4. 索引的序列化和反序列化

要运行此示例,需要在Cargo.toml中添加以下依赖:

[dependencies]
hnsw_rs = "0.2"
rand = "0.8"
rayon = "1.5"
回到顶部