快速向量相似性库
介绍
快速向量相似性库旨在提供高效的向量相似性度量的计算。它适用于数据分析、机器学习和统计等需要测量向量关系的任务。该库使用Rust编写,提供高性能解决方案,并通过提供的绑定轻松集成到Python中。
功能特点
相似性度量
该库实现了几种流行的相似性度量,包括:
- 斯皮尔曼等级相关系数 (
spearman_rho
) - 肯德尔等级相关系数 (
kendall_tau
) - 近似距离相关 (
approximate_distance_correlation
) - 杰森-香农相似性 (
jensen_shannon_similarity
) - 赫夫丁D度量 (
hoeffding_d
)
自举技术
该库支持自举技术用于稳健的相似性计算。这种技术涉及随机重采样数据集来估计相似性度量的分布,从而提供更高的结果置信度。
性能优化
为了实现高效,该库利用:
- 并行计算:使用
rayon
crate,将计算并行化到可用的CPU核心上。 - 向量化操作:该库利用
ndarray
crate提供的高效向量化操作。
Python绑定
该库包含Python绑定,以实现与Python代码的无缝集成。提供py_compute_vector_similarity_stats
和py_compute_bootstrapped_similarity_stats
函数,分别允许你计算相似性统计和自举相似性统计。
安装
Rust
通过向Cargo.toml文件添加该库,将其包含到你的Rust项目中。
Python
你可以通过以下命令直接从PyPI安装快速向量相似性库的Python绑定:
pip install fast_vector_similarity
该命令将下载并安装该包,使其可以在你的Python项目中使用。
使用LLM生成的文本嵌入向量
下面的Python示例代码介绍了使快速向量相似性库能够轻松处理由语言模型(例如Llama2)生成的文本嵌入向量的额外功能,例如由我的另一个库Llama2 Embeddings FastAPI Service生成的嵌入向量(特别是/get_all_embedding_vectors_for_document
端点的输出):
1. 将文本嵌入转换为Pandas DataFrame
convert_embedding_json_to_pandas_df
函数读取JSON文件中的文本嵌入,并将其转换为Pandas DataFrame。每个嵌入与特定文本(例如莎士比亚十四行诗中的一句)关联,DataFrame结构有助于这些嵌入的操作和分析。
2. 将快速向量相似性应用于文本嵌入
apply_fvs_to_vector
函数接受一行嵌入和一个查询嵌入,应用所选的相似性度量,并以JSON对象的形式返回结果。该函数利用快速向量相似性库提供的核心相似性计算功能,允许其直接应用于文本嵌入。
3. 使用大型数据集比较嵌入
代码的主部分包括加载和分析Llama2生成的文本嵌入的示例。这部分代码演示了如何:
- 从数据集中选择一个随机查询嵌入。
- 计算查询嵌入和数据集其余部分之间的相似性度量。
- 创建一个保存相似性结果的DataFrame。
- 按Hoeffding的D度量排序并显示前10个最相似嵌入。
4. 兼容高维嵌入
该示例处理4096维向量,展示了该库处理现代语言模型典型的高维数据的能力。这种兼容性确保该库可以用于各种语言模型和文本嵌入技术。事实上,该库可以轻松处理更高维度。
示例Python代码
import time
import numpy as np
import json
import pandas as pd
from random import choice
import fast_vector_similarity as fvs
def convert_embedding_json_to_pandas_df(file_path):
# 读取JSON文件
with open(file_path, 'r') as file:
data = json.load(file)
# 提取文本和嵌入
texts = [item['text'] for item in data]
embeddings = [item['embedding'] for item in data]
# 确定向量的总数和每个向量的维度
total_vectors = len(embeddings)
vector_dimensions = len(embeddings[0]) if total_vectors > 0 else 0
# 打印向量的总数和维度
print(f"向量总数: {total_vectors}")
print(f"每个向量的维度: {vector_dimensions}")
# 将嵌入转换为DataFrame
df = pd.DataFrame(embeddings, index=texts)
return df
def apply_fvs_to_vector(row_embedding, query_embedding):
params = {
"vector_1": query_embedding.tolist(),
"vector_2": row_embedding.tolist(),
"similarity_measure": "all"
}
similarity_stats_str = fvs.py_compute_vector_similarity_stats(json.dumps(params))
return json.loads(similarity_stats_str)
def main():
length_of_test_vectors = 15000
print(f"生成长度为{length_of_test_vectors}的2个测试向量...")
vector_1 = np.linspace(0., length_of_test_vectors - 1, length_of_test_vectors)
vector_2 = vector_1 ** 0.2 + np.random.rand(length_of_test_vectors)
print("使用线性间隔生成的vector_1,以及用0.2次方和一些随机噪声生成的vector_2。\n")
similarity_measure = "all" # 或者指定一个特定的度量
params = {
"vector_1": vector_1.tolist(),
"vector_2": vector_2.tolist(),
"similarity_measure": similarity_measure
}
# 计时精确的相似性计算
print("计算精确的相似性度量...")
start_time_exact = time.time()
similarity_stats_str = fvs.py_compute_vector_similarity_stats(json.dumps(params))
similarity_stats_json = json.loads(similarity_stats_str)
elapsed_time_exact = time.time() - start_time_exact
print(f"精确计算用时: {elapsed_time_exact:.5f} 秒")
# 打印结果
print("_______________________________________________________________________________________________________________________________________________\n")
print("斯皮尔曼等级相关系数:", similarity_stats_json["spearman_rho"])
print("肯德尔等级相关系数:", similarity_stats_json["kendall_tau"])
print("距离相关性:", similarity_stats_json["approximate_distance_correlation"])
print("杰森-香农相似性:", similarity_stats_json["jensen_shannon_similarity"])
print("赫夫丁D度量:", similarity_stats_json["hoeffding_d"])
print("_______________________________________________________________________________________________________________________________________________\n")
# 自举计算
number_of_bootstraps = 2000
n = 15
sample_size = int(length_of_test_vectors / n)
print(f"用{number_of_bootstraps}次自举和样本量为{sample_size}计算自举相似性度量...")
start_time_bootstrapped = time.time()
params_bootstrapped = {
"x": vector_1.tolist(),
"y": vector_2.tolist(),
"sample_size": sample_size,
"number_of_bootstraps": number_of_bootstraps,
"similarity_measure": similarity_measure
}
bootstrapped_similarity_stats_str = fvs.py_compute_bootstrapped_similarity_stats(json.dumps(params_bootstrapped))
bootstrapped_similarity_stats_json = json.loads(bootstrapped_similarity_stats_str)
elapsed_time_bootstrapped = time.time() - start_time_bootstrapped
print(f"自举计算用时: {elapsed_time_bootstrapped:.5f} 秒")
time_difference = abs(elapsed_time_exact - elapsed_time_bootstrapped)
print(f"精确计算与稳健自举计算的时间差: {time_difference:.5f} 秒")
# 打印自举结果
print("_______________________________________________________________________________________________________________________________________________\n")
print("自举迭代次数:", bootstrapped_similarity_stats_json["number_of_bootstraps"])
print("自举样本量:", bootstrapped_similarity_stats_json["sample_size"])
print("\n稳健斯皮尔曼等级相关系数:", bootstrapped_similarity_stats_json["spearman_rho"])
print("稳健肯德尔等级相关系数:", bootstrapped_similarity_stats_json["kendall_tau"])
print("稳健距离相关性:", bootstrapped_similarity_stats_json["approximate_distance_correlation"])
print("稳健杰森-香农相似性:", bootstrapped_similarity_stats_json["jensen_shannon_similarity"])
print("稳健赫夫丁D度量:", bootstrapped_similarity_stats_json["hoeffding_d"])
print("_______________________________________________________________________________________________________________________________________________\n")
计算精确结果与自举结果之间的差异
measures = ["spearman_rho", "kendall_tau", "approximate_distance_correlation", "jensen_shannon_similarity", "hoeffding_d"] for measure in measures: exact_value = similarity_stats_json[measure] bootstrapped_value = bootstrapped_similarity_stats_json[measure] absolute_difference = abs(exact_value - bootstrapped_value) percentage_difference = (absolute_difference / exact_value) * 100
print(f"\n精确值与自举值在 {measure} 上的差异: {absolute_difference}")
print(f"差异占精确值的比例: {percentage_difference:.2f}%")
print("现在测试更大的数据集,使用 Llama2 (4096 维向量)对一些莎士比亚十四行诗进行句子嵌入...")
将嵌入加载到 DataFrame 中
input_file_path = "sample_input_files/Shakespeare_Sonnets_small.json" embeddings_df = convert_embedding_json_to_pandas_df(input_file_path)
随机选择一行作为查询嵌入
query_embedding_index = choice(embeddings_df.index)
query_embedding = embeddings_df.loc[query_embedding_index]
print(f"选择的查询嵌入句子: {query_embedding_index}
")
从 DataFrame 中移除选中的行
embeddings_df = embeddings_df.drop(index=query_embedding_index)
将函数应用于 embeddings_df 的每一行
json_outputs = embeddings_df.apply(lambda row: apply_fvs_to_vector(row, query_embedding), axis=1)
从 JSON 输出列表创建 DataFrame
vector_similarity_results_df = pd.DataFrame.from_records(json_outputs) vector_similarity_results_df.index = embeddings_df.index
向 DataFrame 添加所需列
columns = ["spearman_rho", "kendall_tau", "approximate_distance_correlation", "jensen_shannon_similarity", "hoeffding_d"] vector_similarity_results_df = vector_similarity_results_df[columns]
按 Hoeffding_d 列降序排序 DataFrame
vector_similarity_results_df = vector_similarity_results_df.sort_values(by="hoeffding_d", ascending=False)
print("\n按 Hoeffding's D 排序的前 10 个最相似嵌入结果:") print(vector_similarity_results_df.head(10))
结果截图:
用法
在 Rust 中
可以通过调用 compute_vector_similarity_stats
和 compute_bootstrapped_similarity_stats
等函数来利用 Rust 中的核心功能。
在 Python 中
安装提供的 Python 包,并按照上述示例使用这些函数。
关于不同的相似性度量
-
斯皮尔曼等级相关 (
spearman_rho
): 斯皮尔曼 rho 评估两个排序变量之间单调关系的强度和方向。与皮尔逊相关性不同,它不假设线性关系,并且对异常值不敏感,适用于非线性关系。 -
肯德尔等级相关 (
kendall_tau
): 肯德尔 tau 测量两个变量之间的序数关联。它在处理重复值方面非常有用,并且其解释性作为概率。与其他相关性度量不同,它基于一致对和不一致对之间的差异,使其具有鲁棒性和多功能性。 -
近似距离相关 (
approximate_distance_correlation
): 距离相关量化变量之间的线性和非线性依赖性。它具有零当且仅当变量独立的强大特性。这使其成为比传统相关性指标更全面的关联度量。 -
Jensen-Shannon 相似性 (
jensen_shannon_similarity
): Jensen-Shannon 相似性源自 Jensen-Shannon 散度,它是 Kullback-Leibler 散度的对称和平滑版本。它量化了两个概率分布之间的相似性,尤其适用于比较可能不重叠的分布。 -
霍夫丁 D 度量 (
hoeffding_d
): 霍夫丁 D 是一种非参数度量,可检测变量之间复杂的非线性关系。D 统计量对各种独立性的替代选择具有鲁棒性,包括非单调关系。当变量之间的关系未知或非常规时,它特别有用。
这些度量中的每一个都有其独特的特性和适用性,提供了全面的工具包,用于理解不同背景下变量之间的关系。通过包括经典和更专业的度量,库提供了灵活性和深度以分析向量相似性。
鲁棒估计的自举技术
自举法是一种强大的统计方法,通过从观察到的数据重复进行有放回的重采样,以估计统计量的分布。在快速向量相似库的上下文中,自举法用于获得向量之间相似性度量的鲁棒估计器。如下所示:
过程
-
随机子集选择: 对于每次自举迭代,从原始向量中选择一个随机子集索引。使用这些索引创建重新采样的向量,保留原始数据的结构和关系。
-
计算相似性: 使用所选的随机子集索引,根据所选的相似性度量计算重新采样向量之间的相似性。这个过程重复多次,生成相似性估计的分布。
-
鲁棒平均: 为获得对异常值不敏感的鲁棒估计,考虑相似性估计分布的四分位距(IQR)。通过仅保留 IQR 内的值,极值的影响被最小化。IQR 内值的鲁棒平均提供最终的鲁棒相似性估计。
为什么有用
自举技术提供了几个优势:
- 对异常值的鲁棒性: 通过关注四分位距并使用鲁棒平均,自举法最小化了异常值的影响。这使得估计更可靠,尤其是当原始数据可能包含异常值时。
- 模型无关估计: 自举法不假设特定的基本分布,使其成为非参数方法。该灵活性允许其应用于各种类型的数据和相似性度量。
- 置信区间: 自举法还可用于构建相似性度量的置信区间。这些区间提供估计的不确定性,增强了可解释性。
- 增强对关系的理解: 通过评估相似性度量的分布,自举法提供了对向量之间关系的更全面了解。在数据分析等背景下,这种更深层次的理解可能具有重要意义。
快速向量相似库中包含自举技术,增加了一层鲁棒性和灵活性,以计算相似性度量。它为用户提供获得更加可靠和有见地的估计的方法,以适应各种数据特征和分析需求。无论是处理潜在的异常值,还是寻求更细致的理解,自举法都提供了库内功能的宝贵工具。