Rust高效近似最近邻搜索库hnsw_rs的使用,hnsw_rs提供高性能向量索引与快速相似度检索功能
Rust高效近似最近邻搜索库hnsw_rs的使用
功能特性
hnsw_rs是一个基于分层可导航小世界图(HNSW)算法的Rust实现库,提供了高效的近似最近邻搜索功能。该库支持多种距离度量方式,包括:
- 常见向量距离:L1范数、L2范数、余弦相似度、Jaccard相似度、汉明距离
- 特殊数据类型:支持u16数组的Levenshtein距离
- 概率分布距离:Hellinger距离、Jeffreys散度、Jensen-Shannon距离
- 自定义距离:可通过实现Trait来定义自己的距离度量
核心功能
- 多线程支持:支持并行插入数据和并行搜索
- 持久化存储:可将索引结构和数据保存到磁盘
- 内存优化:采用紧凑的内存结构存储图拓扑
- 搜索过滤:支持在搜索过程中进行结果过滤
- 大文件支持:通过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();
}
性能优化建议
-
参数调优:
max_connections
:影响构建时间和搜索质量,通常16-64之间ef_construction
:构建时的搜索范围,影响索引质量- 搜索时的
ef
参数:越大结果越准确但搜索越慢
-
内存管理:
- 对于大型数据集,考虑启用mmap功能
- 批量插入数据时使用
parallel_insert
提高效率
-
距离度量选择:
- 欧式距离:
DistL2
- 余弦相似度:
DistCosine
- 自定义距离:实现
Distance
trait
- 欧式距离:
应用场景
- 推荐系统中的相似物品查找
- 自然语言处理中的语义搜索
- 图像检索中的相似图片查找
- 异常检测中的最近邻分析
注意事项
- 索引构建时间与数据规模成正比,大数据集需要较长时间
- 高维数据(>1000维)性能会下降,建议先做降维处理
- 搜索精度与速度需要根据应用场景做权衡
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库的主要功能:
- 创建HNSW索引
- 并行插入大量向量数据
- 执行近似最近邻搜索
- 索引的序列化和反序列化
要运行此示例,需要在Cargo.toml中添加以下依赖:
[dependencies]
hnsw_rs = "0.2"
rand = "0.8"
rayon = "1.5"