Rust高效近邻搜索库anndists的使用:基于近似最近邻算法的快速距离计算与相似性搜索
Rust高效近邻搜索库anndists的使用:基于近似最近邻算法的快速距离计算与相似性搜索
简介
anndists是一个Rust库,为相关crate如hnsw_rs、annembed和coreset提供距离计算功能。
核心特性
所有距离计算都实现了Distance
trait:
pub trait Distance<T: Send + Sync> {
fn eval(&self, va: &[T], vb: &[T]) -> f32;
}
功能
该库提供以下功能:
- 常用距离度量: L1、L2、余弦、Jaccard、汉明距离(适用于标准数值类型的向量),以及u16上的Levenshtein距离
- 概率分布之间的Hellinger距离和Jeffreys散度(f32和f64)
- 概率分布之间的Jensen-Shannon距离(f32和f64)
- 允许用户实现自定义距离的Trait
- 为最常用情况提供Simd实现
实现细节
Simd支持通过simdeez crate在Intel处理器上提供,并使用std::simd
进行部分实现。
构建选项
Simd支持
- 使用
simdeez_f
特性启用simdeez提供的SIMD(x86_64处理器) - 使用
stdsimd
特性实验std::simd(需要nightly编译器)
示例代码
以下是使用anndists库进行距离计算的完整示例:
use anndists::prelude::*;
fn main() {
// 创建两个向量
let vec1 = vec![1.0, 2.0, 3.0];
let vec2 = vec![4.0, 5.0, 6.0];
// 计算L2距离(欧几里得距离)
let l2_dist = DistL2::eval(&vec1, &vec2);
println!("L2 Distance: {}", l2_dist);
// 计算余弦距离
let cosine_dist = DistCosine::eval(&vec1, &vec2);
println!("Cosine Distance: {}", cosine_dist);
// 计算L1距离(曼哈顿距离)
let l1_dist = DistL1::eval(&vec1, &vec2);
println!("L1 Distance: {}", l1_dist);
// 创建自定义距离
struct CustomDistance;
impl Distance<f32> for CustomDistance {
fn eval(&self, va: &[f32], vb: &[f32]) -> f32 {
// 实现自定义距离计算逻辑
va.iter().zip(vb.iter()).map(|(a, b)| (a - b).abs()).sum()
}
}
let custom_dist = CustomDistance.eval(&vec1, &vec2);
println!("Custom Distance: {}", custom_dist);
}
构建说明
要使用SIMD优化:
cargo build --release --features "simdeez_f" # 对于x86_64处理器
# 或
cargo build --release --features "stdsimd" # 需要nightly编译器
许可证
该库采用以下任一许可证:
- Apache License, Version 2.0
- MIT license
贡献者
Petter Egesund贡献了DistLevenshtein距离实现。
版本
当前版本为0.1.3,已切换到edition=2024。
完整示例demo
以下是一个更完整的示例,展示了anndists库的多种距离计算方式:
use anndists::prelude::*;
fn main() {
// 示例1: 数值向量距离计算
let vec_a = vec![1.0, 2.0, 3.0];
let vec_b = vec![4.0, 5.0, 6.0];
println!("数值向量距离计算:");
println!("欧几里得距离(L2): {}", DistL2::eval(&vec_a, &vec_b));
println!("曼哈顿距离(L1): {}", DistL1::eval(&vec_a, &vec_b));
println!("余弦距离: {}", DistCosine::eval(&vec_a, &vec_b));
// 示例2: 二进制向量汉明距离
let bin_vec1 = vec![1, 0, 1, 0];
let bin_vec2 = vec![0, 1, 1, 0];
println!("\n二进制向量汉明距离: {}", DistHamming::eval(&bin_vec1, &bin_vec2));
// 示例3: 概率分布距离计算
let prob_a = vec![0.2, 0.3, 0.5];
let prob_b = vec![0.1, 0.4, 0.5];
println!("\n概率分布距离计算:");
println!("Hellinger距离: {}", DistHellinger::eval(&prob_a, &prob_b));
println!("Jensen-Shannon距离: {}", DistJensenShannon::eval(&prob_a, &prob_b));
// 示例4: 字符串相似度(Levenshtein距离)
let str1 = vec![b'h' as u16, b'e' as u16, b'l' as u16, b'l' as u16, b'o' as u16];
let str2 = vec![b'h' as u16, b'a' as u16, b'l' as u16, b'o' as u16];
println!("\nLevenshtein距离: {}", DistLevenshtein::eval(&str1, &str2));
// 示例5: 自定义距离实现
struct SquaredEuclidean;
impl Distance<f32> for SquaredEuclidean {
fn eval(&self, va: &[f32], vb: &[f32]) -> f32 {
va.iter().zip(vb.iter())
.map(|(a, b)| (a - b).powi(2))
.sum()
}
}
let sq_dist = SquaredEuclidean.eval(&vec_a, &vec_b);
println!("\n自定义平方欧几里得距离: {}", sq_dist);
}
这个完整示例展示了:
- 标准数值向量的多种距离计算
- 二进制向量的汉明距离
- 概率分布之间的距离度量
- 字符串的Levenshtein距离计算
- 自定义距离度量的实现
要运行这些示例,请确保在Cargo.toml中添加anndists依赖:
[dependencies]
anndists = "0.1.3"
1 回复
Rust高效近邻搜索库anndists使用指南
简介
anndists是一个基于Rust的高效近似最近邻(ANN)搜索库,专注于快速距离计算和相似性搜索。它提供了多种近似最近邻算法实现,适用于大规模数据集的相似性搜索场景。
主要特性
- 支持多种距离度量:欧氏距离、余弦相似度等
- 提供多种近似最近邻算法实现
- 内存高效,适合大规模数据集
- 线程安全的API设计
- 提供Rust原生接口和Python绑定
安装
在Cargo.toml中添加依赖:
[dependencies]
anndists = "0.3"
基本使用方法
1. 创建索引并添加数据
use anndists::ann::Hnsw;
use anndists::distance::Euclidean;
// 创建一个HNSW索引,维度为128,使用欧氏距离
let mut index = Hnsw::<f32, Euclidean>::new(128);
// 添加向量到索引
let data = vec![
vec![1.0, 2.0, 3.0, /* ... */],
vec![4.0, 5.0, 6.0, /* ... */],
// 更多数据...
];
for (i, vec) in data.iter().enumerate() {
index.add(i, vec);
}
// 构建索引
index.build();
2. 最近邻搜索
let query = vec![1.5, 2.5, 3.5, /* ... */];
let k = 5; // 查找5个最近邻
// 搜索最近的k个邻居
let results = index.search(&query, k);
for (id, distance) in results {
println!("ID: {}, Distance: {}", id, distance);
}
3. 使用不同距离度量
use anndists::distance::Cosine;
// 使用余弦相似度
let mut index = Hnsw::<f32, Cosine>::new(128);
4. 批量搜索
let queries = vec![
vec![1.5, 2.5, 3.5, /* ... */],
vec![4.5, 5.5, 6.5, /* ... */],
];
for result in index.batch_search(&queries, k) {
println!("Query results: {:?}", result);
}
高级配置
调整HNSW参数
use anndists::params::HnswParams;
let params = HnswParams {
m: 16, // 每个节点的最大连接数
ef_construction: 200, // 构建时的搜索范围
ef_search: 100, // 搜索时的搜索范围
..Default::default()
};
let mut index = Hnsw::<f32, Euclidean>::with_params(128, params);
持久化索引
use std::fs::File;
use std::io::{BufWriter, BufReader};
// 保存索引
let file = File::create("index.bin").unwrap();
let writer = BufWriter::new(file);
bincode::serialize_into(writer, &index).unwrap();
// 加载索引
let file = File::open("index.bin").unwrap();
let reader = BufReader::new(file);
let index: Hnsw<f32, Euclidean> = bincode::deserialize_from(reader).unwrap();
性能建议
- 对于大型数据集,分批添加数据并定期调用
build_partial
方法 - 根据数据特性调整HNSW参数
- 考虑使用并行构建索引(如果可用)
- 对于高维数据,可能需要增加
ef_construction
和ef_search
值
应用场景
- 推荐系统
- 图像/视频检索
- 自然语言处理中的语义搜索
- 异常检测
- 聚类分析
完整示例demo
use anndists::ann::Hnsw;
use anndists::distance::{Euclidean, Cosine};
use anndists::params::HnswParams;
fn main() {
// 示例1: 使用欧氏距离创建索引并搜索
let mut index_euclidean = Hnsw::<f32, Euclidean>::new(3);
let data = vec![
vec![1.0, 2.0, 3.0],
vec![4.0, 5.0, 6.0],
vec![7.0, 8.0, 9.0],
];
for (i, vec) in data.iter().enumerate() {
index_euclidean.add(i, vec);
}
index_euclidean.build();
let query = vec![1.5, 2.5, 3.5];
let results = index_euclidean.search(&query, 2);
println!("欧氏距离搜索结果: {:?}", results);
// 示例2: 使用余弦相似度
let mut index_cosine = Hnsw::<f32, Cosine>::new(3);
for (i, vec) in data.iter().enumerate() {
index_cosine.add(i, vec);
}
index_cosine.build();
let results = index_cosine.search(&query, 2);
println!("余弦相似度搜索结果: {:?}", results);
// 示例3: 高级配置
let params = HnswParams {
m: 8,
ef_construction: 100,
ef_search: 50,
..Default::default()
};
let mut custom_index = Hnsw::<f32, Euclidean>::with_params(3, params);
for (i, vec) in data.iter().enumerate() {
custom_index.add(i, vec);
}
custom_index.build();
let batch_results = custom_index.batch_search(&[query.clone(), vec![4.5, 5.5, 6.5]], 2);
println!("批量搜索结果: {:?}", batch_results);
// 示例4: 索引持久化
{
use std::fs::File;
use std::io::{BufWriter, BufReader};
// 保存索引
let file = File::create("index.bin").unwrap();
let writer = BufWriter::new(file);
bincode::serialize_into(writer, &custom_index).unwrap();
// 加载索引
let file = File::open("index.bin").unwrap();
let reader = BufReader::new(file);
let loaded_index: Hnsw<f32, Euclidean> = bincode::deserialize_from(reader).unwrap();
println!("从文件加载的索引搜索结果: {:?}", loaded_index.search(&query, 2));
}
}
这个完整示例展示了anndists库的主要功能,包括:
- 使用不同距离度量(欧氏距离和余弦相似度)创建索引
- 添加数据和构建索引
- 单查询和批量查询
- 自定义HNSW参数
- 索引的序列化和反序列化