scipy.spatial.distance.cdist 是计算两组样本间成对欧氏距离的最快方法,底层为c实现、支持分块计算与内存优化;避免使用广播差值(如 x[:, none] - x[none, :])以防oom,大数据应配合 pairwise_distances_chunked 或降维处理。

用 scipy.spatial.distance.cdist 最快,别手写双重循环
对上万样本计算两两欧氏距离,纯 Python 循环或嵌套 numpy.linalg.norm 会慢到卡死。实际中,scipy.spatial.distance.cdist 是首选——它底层用 C 实现,支持广播优化,且内存占用可控。
常见错误是直接调用 np.linalg.norm(X[:, None] - X[None, :], axis=2):小数据没问题,但当 X 有 10k 行时,中间差值数组会占约 800GB 内存(float64 × 10⁸ × 10⁸ × 8 字节),直接 OOM。
-
cdist(X, X, metric='euclidean')不显式构造全尺寸差值矩阵,而是分块计算 + 缓冲复用 - 若只需下三角(如聚类、去重),加
np.tril或用scipy.spatial.distance.pdist+squareform - 输入必须是二维数组;一维向量要 reshape 成
(n, 1),否则报ValueError: Input must be 2-dimensional
当数据太大放不下内存,用 sklearn.metrics.pairwise_distances_chunked
哪怕用了 cdist,如果样本数超 50k 且特征维数高(比如 >1000),仍可能触发内存不足。这时不能硬算全距矩阵,得流式分块。
sklearn.metrics.pairwise_distances_chunked 是专为这设计的:它把 X 拆成批,每批只和全部 X 计算部分距离,返回生成器,你可逐批处理(比如找最近邻、过滤阈值内配对)。
- 默认 chunk size 约 1024,可通过
working_memory参数(单位 MB)调节,值越大批越少、越快,但单批内存越高 - 不支持直接返回完整矩阵,适合“边算边用”场景;若真需要完整矩阵,只能降维(PCA)或采样
- 注意返回的是 (chunk_i_start, chunk_i_end, distances_array) 元组,不是扁平结果
numpy.einsum 手动展开能省内存,但只适合中等规模
如果不用 SciPy/Scikit-learn,或需完全控制计算逻辑(比如加自定义权重),可用 einsum 避开显式广播:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
dist_sq = np.einsum('ij,ij->i', X, X)[:, None] + \
np.einsum('ij,ij->i', X, X)[None, :] - \
2 * X @ X.T
dist = np.sqrt(np.maximum(dist_sq, 0))
原理是展开 $ \|x_i - x_j\|^2 = \|x_i\|^2 + \|x_j\|^2 - 2x_i^\top x_j $,避免 X[:, None] - X[None, :] 的内存爆炸。
- 比广播版省内存约 3 倍,但比
cdist慢 2–5 倍(NumPy 解释器开销) - 必须加
np.maximum(..., 0):浮点误差可能导致负值,sqrt会产出nan - 特征维度高时,
X @ X.T仍是 $O(n^2d)$ 空间,n 超 20k 就吃紧
GPU 加速?别急着上 cupy,先看瓶颈在哪
有人一上来就换 cupy 或 numba,但实际多数情况不划算——欧氏距离计算本身不重,瓶颈常在数据搬运或内存带宽。
测试过:10k×100 的 float32 数据,在 RTX 4090 上用 cupy 版 cdist 反而比 CPU 慢 1.2 倍,因为 PCIe 传输时间 > 计算节省时间。只有当 n > 100k 且 GPU 显存足够缓存全数据时,加速才明显。
- 确认瓶颈:用
time.perf_counter()分段测加载、计算、保存耗时,别假设“GPU 一定快” -
cupy要求所有数组已在 GPU 上;从 NumPy 拷贝过去本身就有延迟 - 开源库如
faiss(CPU/GPU 通用)更适合近似最近邻,而非精确全距矩阵
真正卡住的往往不是算法,而是没意识到 cdist 和 pdist 的语义区别,或误以为“向量化=无内存压力”。小数据随便写,大规模必须先看内存公式,再选工具链。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










