如何使用Python NumPy计算大规模数据集的欧氏距离矩阵?

大晨同学_6872

大晨同学_6872

2026-08-12

539人浏览

原创

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

如何使用python numpy计算大规模数据集的欧氏距离矩阵?

用 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-script-generator
python-script-generator

快速生成专业的 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 的核心概念和高级技巧!

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

2023.07.20

1671

4

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

2023.07.25

4144

7

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.31

1669

3

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

2023.08.03

23977

23

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

2927

5

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

2967

5

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

1143

5

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.10

596

4

python是前端还是后端
python是前端还是后端

Python属于前端也属于后端,其灵活性和丰富的生态系统使得开发人员能够在不同的领域中灵活运用。本专题为大家提供python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

2303

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程