You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化Scipy中多向量相似度计算,降低耗时减少函数调用?

嘿,这个问题我太有共鸣了——之前用Scipy的两两距离函数循环计算批量向量时,那速度慢得让人抓狂!其实完全不用这么折腾,下面给你几个高效的解决方案,能帮你把计算时间砍下来一大截:

1. 用Scipy自带的批量距离计算函数(最省心)

Scipy其实早就为批量距离计算准备了专门的工具:scipy.spatial.distance.cdist。这个函数可以一次性计算两组向量之间的所有两两距离,内部用向量化运算实现,比手动循环调用单向量函数效率高N倍。

支持你提到的cosine、euclidean、chebyshev等几乎所有常用距离度量,用法也超简单:

from scipy.spatial.distance import cdist

# 假设targets是(m, d)形状的矩阵:m个目标向量,每个向量d维
# vectors是(n, d)形状的矩阵:n个待比较的向量
targets = ...  # 你的目标向量组
vectors = ...  # 你的待比较向量组

# 批量计算余弦距离(注意:Scipy的cosine返回的是距离,不是相似度,相似度=1-距离)
cosine_distances = cdist(targets, vectors, metric='cosine')
# 批量计算欧氏距离
euclidean_distances = cdist(targets, vectors, metric='euclidean')
# 批量计算切比雪夫距离
chebyshev_distances = cdist(targets, vectors, metric='chebyshev')

返回的结果是(m, n)的矩阵,每个元素对应targets[i]和vectors[j]之间的距离,完全不用写循环!

2. 用NumPy手动实现向量化运算(更灵活)

如果想完全掌控计算过程,或者对性能有更高要求,直接用NumPy的向量化运算来实现距离计算也是个好选择——没有额外的函数调用开销,速度可能比cdist还快一点。

举两个例子:

欧氏距离

import numpy as np

targets = np.random.rand(10, 5)  # 10个5维目标向量
vectors = np.random.rand(100, 5) # 100个5维待比较向量

# 用广播机制批量计算欧氏距离
euclidean_distances = np.sqrt(np.sum((targets[:, np.newaxis] - vectors)**2, axis=2))

余弦距离

先对向量做归一化,再通过点积计算相似度,最后转成距离:

# 归一化目标向量和待比较向量
targets_norm = targets / np.linalg.norm(targets, axis=1, keepdims=True)
vectors_norm = vectors / np.linalg.norm(vectors, axis=1, keepdims=True)

# 余弦相似度=归一化向量的点积,距离=1-相似度
cosine_distances = 1 - np.dot(targets_norm, vectors_norm.T)

这种方法的好处是可以根据需求自定义计算逻辑,比如加权重、做特殊预处理等。

3. 大规模数据用FAISS库(性能天花板)

如果你的向量规模特别大(比如n超过10万甚至百万级),上面两种方法可能会遇到内存或速度瓶颈,这时候推荐用FAISS——Facebook开源的专门用于大规模相似性搜索的库,支持CPU/GPU加速,能高效处理亿级向量的近邻搜索。

比如用FAISS计算余弦距离的近邻:

import faiss

# FAISS要求向量是float32格式
targets = targets.astype('float32')
vectors = vectors.astype('float32')

# 余弦距离需要先对向量做L2归一化,这样点积就等价于余弦相似度
faiss.normalize_L2(vectors)
# 构建内积索引(因为归一化后内积=余弦相似度)
index = faiss.IndexFlatIP(vectors.shape[1])
index.add(vectors)  # 将待比较向量加入索引

# 搜索每个目标向量的Top k近邻(比如k=5)
k = 5
faiss.normalize_L2(targets)
similarities, indices = index.search(targets, k)
# 转成余弦距离:distances = 1 - similarities
distances = 1 - similarities

FAISS适合不需要计算全量距离,只需要找Top N近邻的场景,能节省大量内存和计算时间。

总结一下
  • 小规模数据/快速实现:优先用scipy.spatial.distance.cdist,简单省心,支持多种度量;
  • 中等规模/自定义需求:用NumPy向量化实现,灵活高效;
  • 大规模数据/近邻搜索:用FAISS,性能拉满,适合工业级场景。

内容的提问来源于stack exchange,提问作者Isaac Sim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:00:58