如何优化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
相关产品推荐
相关产品推荐

