高效计算Python中的稀疏成对距离

高效计算python中的稀疏成对距离

本文旨在解决在Python中高效计算两组向量之间稀疏成对距离的问题。针对传统NumPy方法在处理大量向量时因计算冗余而导致的性能瓶颈,本文提出了一种结合Numba即时编译和SciPy稀疏矩阵(特别是CSR格式)的优化方案。通过在Numba加速的循环中仅计算所需的距离并构建稀扑矩阵,该方法显著提升了计算效率和内存利用率,特别适用于距离矩阵高度稀疏的场景。

问题背景与传统方法分析

在许多数据处理和机器学习任务中,我们可能需要计算两组向量集 A 和 B 之间的所有成对距离。然而,在某些特定场景下,我们仅对其中一小部分成对距离感兴趣,例如,当一个掩码矩阵 M 指定了需要保留的距离对时。

传统的NumPy方法通常涉及计算所有可能的成对距离,然后通过掩码矩阵进行筛选。以下是一个示例:

import numpy as npA = np.array([[1, 2], [2, 3], [3, 4]])                              # (3, 2)B = np.array([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]])              # (5, 2)M = np.array([[0, 0, 0, 1, 0], [1, 1, 0, 0, 0], [0, 0, 0, 0, 1]])   # (3, 5)# 计算所有向量对的差值diff = A[:, None] - B[None, :]                                      # (3, 5, 2)# 计算所有成对距离(L2范数)distances = np.linalg.norm(diff, ord=2, axis=2)                     # (3, 5)# 应用掩码,保留所需距离masked_distances = distances * M                                    # (3, 5)print("计算的距离矩阵:n", distances)print("掩码后的距离矩阵:n", masked_distances)

这种方法虽然简洁,但当 A 和 B 的行数非常大时(例如数千行),diff 和 distances 矩阵会变得非常庞大,导致计算大量不必要的距离,从而消耗大量的计算资源和内存。即使通过 np.vectorize 尝试创建条件函数,也可能因为Python循环的开销而导致性能不佳,甚至更慢。

优化方案:Numba加速与CSR稀疏矩阵

为了解决上述性能瓶颈,我们引入一种结合 Numba 即时编译和 SciPy 稀疏矩阵(特别是 Compressed Sparse Row, CSR 格式)的优化方案。该方案的核心思想是:

立即学习“Python免费学习笔记(深入)”;

避免冗余计算:仅计算掩码矩阵 M 中指定为 True 的那些成对距离。高效存储:使用 CSR 稀疏矩阵来存储结果,只存储非零距离值,显著减少内存占用性能提升:利用 Numba 对核心计算逻辑进行 JIT 编译,将 Python 循环的性能提升至接近 C 语言的水平。

1. 自定义欧几里得距离函数

首先,我们定义一个 Numba 加速的欧几里得距离函数。在 Numba 环境下,自定义的循环计算通常比调用 np.linalg.norm 更快。

import numba as nbimport numpy as npimport scipy.sparseimport math@nb.njit()def euclidean_distance(vec_a, vec_b):    """    计算两个向量之间的欧几里得距离。    使用 Numba 加速,避免 np.linalg.norm 的开销。    """    acc = 0.0    for i in range(vec_a.shape[0]):        acc += (vec_a[i] - vec_b[i]) ** 2    return math.sqrt(acc)

这里,@nb.njit() 装饰器指示 Numba 在函数首次调用时将其编译为优化的机器码。

2. 核心距离计算与稀疏数据填充函数

接下来,我们创建 masked_distance_inner 函数。这是一个 Numba 加速的核心函数,负责遍历掩码矩阵,只计算所需的距离,并将结果填充到 CSR 矩阵所需的 data、indicies 和 indptr 数组中。

@nb.njit()def masked_distance_inner(data, indicies, indptr, matrix_a, matrix_b, mask):    """    Numba 加速的核心函数,根据掩码计算距离并填充 CSR 矩阵的内部数组。    参数:        data (np.ndarray): 存储非零距离值的数组。        indicies (np.ndarray): 存储非零距离值对应列索引的数组。        indptr (np.ndarray): 存储每行在 data/indicies 中起始位置的数组。        matrix_a (np.ndarray): 第一个向量集。        matrix_b (np.ndarray): 第二个向量集。        mask (np.ndarray): 布尔掩码矩阵,指示哪些距离需要计算。    """    write_pos = 0    N, M = matrix_a.shape[0], matrix_b.shape[0]    # 遍历所有可能的向量对    for i in range(N):        for j in range(M):            # 只有当掩码为 True 时才计算距离            if mask[i, j]:                # 记录距离值                data[write_pos] = euclidean_distance(matrix_a[i], matrix_b[j])                # 记录该距离值对应的列索引                indicies[write_pos] = j                write_pos += 1        # 记录当前行结束后,data/indicies 中元素的总数,作为下一行的起始位置        indptr[i + 1] = write_pos    # 确保所有预分配的空间都被使用    assert write_pos == data.shape[0]    assert write_pos == indicies.shape[0]    # data, indicies, indptr 会在函数外部被修改并用于构建 CSR 矩阵

3. 稀疏距离矩阵构建函数

最后,我们定义 masked_distance 函数,它负责设置算法的参数、预分配内存,并调用 masked_distance_inner 来执行计算,最终返回一个 scipy.sparse.csr_matrix 对象。

def masked_distance(matrix_a, matrix_b, mask):    """    计算两组向量之间掩码指定的稀疏成对距离。    参数:        matrix_a (np.ndarray): 第一个向量集。        matrix_b (np.ndarray): 第二个向量集。        mask (np.ndarray): 布尔掩码矩阵。    返回:        scipy.sparse.csr_matrix: 包含指定成对距离的稀疏矩阵。    """    N, M = matrix_a.shape[0], matrix_b.shape[0]    assert mask.shape == (N, M), "掩码矩阵的形状必须与向量集兼容。"    # 确保掩码是布尔类型    mask = mask != 0    # 计算稀疏矩阵中非零元素的总数    sparse_length = mask.sum()    # 为 CSR 矩阵预分配内存。这些数组不需要初始化为零,直接分配内存更高效。    data = np.empty(sparse_length, dtype='float64')    # 存储非零数据值    indicies = np.empty(sparse_length, dtype='int64')  # 存储列索引    indptr = np.zeros(N + 1, dtype='int64')            # 存储行指针    # 调用 Numba 加速的核心函数进行计算和填充    masked_distance_inner(data, indicies, indptr, matrix_a, matrix_b, mask)    # 使用填充好的数据构建 CSR 稀疏矩阵    return scipy.sparse.csr_matrix((data, indicies, indptr), shape=(N, M))

示例用法与性能分析

为了演示和评估其性能,我们使用更大的随机生成数据集进行测试。

# 准备大型测试数据A_big = np.random.rand(2000, 10)B_big = np.random.rand(4000, 10)# 创建一个高度稀疏的掩码(0.1% 的元素为 True)M_big = np.random.rand(A_big.shape[0], B_big.shape[0]) < 0.001# 使用优化的方法计算稀疏距离sparse_distances = masked_distance(A_big, B_big, M_big)print(f"稀疏距离矩阵的形状: {sparse_distances.shape}")print(f"稀疏距离矩阵的非零元素数量: {sparse_distances.nnz}")print(f"稀疏距离矩阵的密度: {sparse_distances.nnz / (sparse_distances.shape[0] * sparse_distances.shape[1]):.6f}")# 性能基准测试 (在Jupyter/IPython环境中运行)# %timeit masked_distance(A_big, B_big, M_big)# # 原始方法的性能基准测试 (仅供参考,不推荐在生产环境运行大型矩阵)# %timeit np.linalg.norm(A_big[:,None] - B_big[None,:], ord=2, axis=2) * M_big

在上述 A_big (2000×10) 和 B_big (4000×10) 的测试场景中,当掩码 M_big 只有约 0.1% 的元素为 True 时,此优化方案相比原始的 NumPy 全量计算方法,可以实现显著的性能提升(例如,40倍甚至更高)。具体的加速效果会随着矩阵大小和掩码稀疏度的增加而更加明显。

注意事项与优化建议

Numba 编译开销:euclidean_distance 和 masked_distance_inner 函数在首次调用时会有编译开销。在后续调用中,性能将大幅提升。数据类型选择:data 数组默认使用 float64。如果对精度要求不高,可以考虑使用 float32 来减少内存占用并可能提高计算速度。indicies 和 indptr 数组默认使用 int64。如果矩阵的维度和非零元素数量都小于 231,可以安全地使用 int32,进一步节省内存。稀疏度影响:此方法的性能优势主要体现在掩码矩阵高度稀疏的场景。如果掩码矩阵非常稠密(例如,超过 50% 的元素为 True),那么全量计算后筛选的方法可能反而更简单或性能差异不显著。正确性验证:在实际应用中,务必通过 np.allclose() 等方法验证优化后的结果与原始方法的结果是否一致。内存管理:在 masked_distance 函数中,data 和 indicies 数组是使用 np.empty 创建的,它们不进行零初始化,这比 np.zeros 更快,因为我们会在 masked_distance_inner 中完全覆盖这些内存。

总结

通过结合 Numba 的即时编译能力和 SciPy 的 CSR 稀疏矩阵格式,我们能够高效地计算两组向量之间指定的一小部分成对距离。这种方法通过避免不必要的计算和优化内存使用,为处理大规模稀疏距离计算问题提供了一个强大且高性能的解决方案。在面临大量数据且仅需少量成对距离的场景时,采用此教程介绍的方案将显著提升应用程序的性能和资源利用率。

以上就是高效计算Python中的稀疏成对距离的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1374632.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
python多值参数是什么
上一篇 2025年12月14日 14:19:17
解决Flask AJAX图片更新不生效:后端JSON响应与前端动态更新
下一篇 2025年12月14日 14:19:25

相关推荐

  • 怎么用豆包AI帮我实现CQRS模式 3步教你用AI分离读写模型

    怎么用豆包AI帮我实现CQRS模式 3步教你用AI分离读写模型怎么用豆包AI帮我实现CQRS模式 3步教你用AI分离读写模型怎么用豆包AI帮我实现CQRS模式 3步教你用AI分离读写模型怎么用豆包AI帮我实现CQRS模式 3步教你用AI分离读写模型

    实现cqrs模式可通过三步借助豆包ai快速完成:一、理清业务场景,将写操作(如用户下单)与读操作(如查看订单列表)分离,可复制代码给豆包ai分析归类;二、让豆包ai生成基础结构代码,输入类似“基于cqrs的订单管理系统,用python flask实现”的指令,获取命令处理器、查询处理器等模块模板;三…

    2026年9月24日 用户投稿
    000
  • WPS如何制作个人简历_WPS简历模板选择与内容填写教程

    WPS如何制作个人简历_WPS简历模板选择与内容填写教程WPS如何制作个人简历_WPS简历模板选择与内容填写教程WPS如何制作个人简历_WPS简历模板选择与内容填写教程WPS如何制作个人简历_WPS简历模板选择与内容填写教程

    使用WPS制作简历需先选择合适模板,填写个人信息、求职意向、教育背景、工作经历等内容,突出成果与技能,调整格式后导出为PDF。关键在于内容真实、条理清晰、重点突出,便于HR快速识别优势。 在求职过程中,一份清晰、专业的简历至关重要。WPS Office 提供了多种简历模板和便捷的编辑功能,帮助用户快…

    2026年9月24日 用户投稿
    300
  • VSCode如何设置代码缩进和制表符 VSCode缩进与制表符的自定义调整方法

    要解决vscode缩进混乱问题,需将”editor.detectindentation”设为false,避免自动检测干扰;2. 统一使用空格或制表符的关键在于团队一致性,推荐通过settings.json明确设置”editor.insertspaces&#8221…

    2026年9月24日
    100
  • php-gd怎么销毁图像资源_php-gd释放内存中的图像

    使用imagedestroy()函数销毁PHP-GD图像资源以避免内存泄漏。创建的资源如$image需在处理后调用imagedestroy($image)释放,尤其在循环中应每轮结束前销毁资源,推荐结合is_resource()判断有效性,遵循“谁创建,谁销毁”原则,确保内存高效管理。 在使用 PH…

    2026年9月24日
    000
  • Agent Zero— 开源可扩展AI框架,通过用户指令和任务动态学习

    Agent Zero— 开源可扩展AI框架,通过用户指令和任务动态学习Agent Zero— 开源可扩展AI框架,通过用户指令和任务动态学习Agent Zero— 开源可扩展AI框架,通过用户指令和任务动态学习Agent Zero— 开源可扩展AI框架,通过用户指令和任务动态学习

    agent zero 是一个开源的、可扩展的人工智能框架,能够作为用户的个性化智能助手。它不是基于预设功能的工具,而是通过用户指令和任务来动态学习与成长。agent zero 具备持久记忆能力,可以存储过往的解决方案、代码和事实信息,从而更快速地应对未来的任务。该框架将操作系统视为执行任务的工具,具…

    2026年9月24日 用户投稿
    100
  • DeepSeek能不能帮我写代码 简单编程任务如何交给DeepSeek完成

    DeepSeek能不能帮我写代码 简单编程任务如何交给DeepSeek完成DeepSeek能不能帮我写代码 简单编程任务如何交给DeepSeek完成DeepSeek能不能帮我写代码 简单编程任务如何交给DeepSeek完成DeepSeek能不能帮我写代码 简单编程任务如何交给DeepSeek完成

    很多用户好奇,像DeepSeek这样的AI模型能否帮助完成编程任务,特别是那些相对简单的编程需求。答案是肯定的。DeepSeek具备理解自然语言描述并尝试生成相应代码的能力,这使得它成为完成一些简单编程任务的有力工具。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepS…

    2026年9月24日 用户投稿
    200
  • 为什么GPU显存带宽比容量更重要?

    显存带宽比容量更重要,因其直接决定数据传输速度,影响GPU计算单元的利用率。在AI训练和高分辨率渲染中,高带宽可避免“数据饥饿”,确保海量数据高效流转,而HBM技术凭借3D堆叠和宽接口提供远超GDDR的带宽,成为高性能计算的关键。 GPU显存带宽比容量更重要,核心在于现代GPU的工作模式和其处理的数…

    2026年9月24日
    200
  • VSCode如何实现代码热重载 VSCode实时预览开发的高效配置方案

    使用live server扩展实现静态文件的实时预览,保存后浏览器自动刷新;2. 利用现代前端框架(如react、vue)内置的开发服务器(如vite、webpack dev server)实现hmr热模块替换,修改代码后仅更新变动模块而不刷新页面;3. 结合browsersync等工具实现多设备同…

    2026年9月24日
    100
  • APM开发阅读

    APM开发阅读APM开发阅读APM开发阅读APM开发阅读

    我阅读apm的源码有两个主要目的:一是学习,了解飞控系统和大型项目的组织结构;二是为了移植的需要,满足项目需求。近年来,少儿编程市场非常火热,许多厂商推出了相关的产品,但这些产品大多使用空心杯电机,导致动力不足,且扩展性有限。许多任务需要io或图像识别的支持。 因此,我在考虑使用APM裁剪版的飞控系…

    2026年9月24日 用户投稿
    1700
  • 固态硬盘主控芯片的算法如何影响长期使用性能?

    固态硬盘主控算法直接决定SSD的寿命、性能一致性与数据安全。其核心在于磨损均衡、垃圾回收(GC)和错误校正码(ECC)三大算法:磨损均衡确保闪存块均匀使用,防止局部过早失效;GC通过清理无效数据释放空间,影响写入放大(WAF)和性能稳定性;ECC则纠正数据错误,保障长期可靠性。WAF受GC效率、预留…

    2026年9月24日
    200
  • VSCode的扩展设置是全局的还是局部的?

    VSCode扩展设置默认全局生效,存储于用户配置文件中,但部分扩展如ESLint、Prettier和Python支持项目级局部配置,通过在项目根目录的.vscode/settings.json文件中定义,可覆盖全局设置;在设置界面中,齿轮图标表示可被工作区覆盖,锁图标表示仅限全局修改,用户可根据需求…

    2026年9月24日
    300
  • PHP如何批量处理图片_PHP实现多张图片自动化处理

    批量处理图片时需循环读取并逐个处理,核心是使用scandir()获取文件列表,通过GD库或Imagick处理图像,每处理完一张用imagedestroy()释放内存以避免内存溢出;为提升效率可分批处理、优化算法、使用多进程或异步队列,并选用Intervention Image等高效第三方库。 批量处…

    2026年9月24日
    200
  • Python创建模块并调用函数

    在PyCharm中创建新项目后,于项目根目录下新建一个名为 jisuanqi.py 的Python脚本文件。 在该文件中定义一个函数 ys,该函数包含三个形参:a、b 和 c。其中,a 与 b 为参与数学运算的操作数,c 用于指定运算类型——当值为0时执行加法,1时为减法,2时为乘法,3时则进行除法…

    2026年9月24日
    100
  • 如何分析Linux进程内存 pmap内存映射检查方法

    如何分析Linux进程内存 pmap内存映射检查方法如何分析Linux进程内存 pmap内存映射检查方法如何分析Linux进程内存 pmap内存映射检查方法如何分析Linux进程内存 pmap内存映射检查方法

    要分析linux进程的内存,特别是利用pmap工具,核心操作是获取目标进程pid后执行pmap -x 。1. 获取pid可通过ps aux | grep your_process_name;2. 执行pmap -x 命令查看扩展格式信息,包括address、kbytes、rss、dirty、mode…

    2026年9月24日 用户投稿
    300
  • 解决MySQL事件event定义中文乱码的方法

    mysql的event事件处理中文乱码问题主要由字符集设置不当引起,解决方法包括以下步骤:1. 统一数据库、表和字段的字符集为utf8mb4,创建或修改时显式指定字符集;2. 设置连接层字符集,在连接后执行set names ‘utf8mb4’或在程序连接参数中指定chars…

    2026年9月24日
    400
  • VSCode如何优化多语言混编 VSCode复合工程项目的管理技巧

    #%#$#%@%@%$#%$#%#%#$%@_e2fc++805085e25c9761616c00e065bfe8处理多语言混编和复杂项目的核心策略是使用多根工作区(multi-root workspace),通过创建.code-workspace文件将不同语言或模块的目录统一管理,实现跨项目文件浏…

    2026年9月24日
    100
  • Java中接口常量和类常量的使用区别

    接口常量默认public static final,用于行为契约但易导致职责模糊;类常量可用不同访问修饰符,更适合封装和维护。现代Java推荐使用专用常量类、枚举、私有静态常量或配置文件管理常量,以提升代码清晰度与可维护性。 Java中接口常量和类常量,核心区别在于它们的定义位置和隐式属性。接口常量…

    2026年9月24日
    100
  • VSCode如何调试React前端应用 VSCode调试React组件的完整教程

    要调试react前端应用,首先需安装vscode的浏览器调试插件并配置launch.json文件,1. 安装“debugger for chrome”或对应浏览器的插件;2. 在项目根目录的.vscode文件夹中创建launch.json,配置type为chrome、request为launch、n…

    2026年9月24日
    100
  • VSCode如何通过Dev Containers开发 VSCode开发容器环境的搭建与使用

    vscode通过dev containers提供容器化开发环境,解决了“在我的机器上能运行”的问题。1. 安装docker并配置vscode访问;2. 安装remote – containers扩展;3. 创建.devcontainer文件夹和devcontainer.json文件;4.…

    2026年9月24日
    100
  • VSCode如何集成Cassandra数据库工具 VSCode NoSQL数据库管理插件指南

    解决vscode连接cassandra认证问题的方法是确认cassandra集群是否启用认证,若启用则检查连接配置中的用户名、密码是否正确,并确保authenticator和authorizer配置匹配,如使用passwordauthenticator需提供正确凭据,若使用kerberos等其他认证…

    2026年9月24日
    500

发表回复

登录后才能评论
关注微信