0

0

PyTorch中查找张量B元素在张量A中所有索引位置的内存优化方案

霞舞

霞舞

发布时间:2025-10-05 11:17:43

|

975人浏览过

|

来源于php中文网

原创

PyTorch中查找张量B元素在张量A中所有索引位置的内存优化方案

本文探讨了PyTorch中高效查找张量B元素在张量A中所有索引位置的策略,尤其针对大规模张量避免广播内存限制。提供了结合部分广播与Python循环的混合方案,以及纯Python循环迭代方案,旨在优化内存并生成结构化索引。文章将指导开发者根据场景选择最佳方法。

引言:大规模张量索引查找的挑战

pytorch深度学习框架中,我们经常需要处理张量(tensor)数据。一个常见的需求是,给定两个张量a和b,找出张量b中每个值在张量a中所有出现的位置(即索引)。例如,如果张量a为 [1,2,3,3,2,1,4,5,9],张量b为 [1,2,3,9],我们期望的输出是 [[0,5], [1,4], [2,3], [8]],其中每个子列表对应b中一个值在a中的所有索引。

对于小型张量,通过广播(broadcasting)进行元素比较是一种直观且简洁的方法。然而,当张量A和B的规模非常大时,直接使用广播操作(例如 A[:, None] == B)可能会导致中间张量占用巨大的内存,从而引发内存溢出问题。因此,寻找内存高效的解决方案变得至关重要。

广播方法的局限性

最初,开发者可能会尝试使用如下的广播逻辑:

import torch

def vectorized_find_indices_broadcast(A, B):
    # 扩展A的维度以与B进行广播比较
    # A_expanded = A[:, None, None] # 原始问题中的三重扩展可能并非必需,但原理相同
    # mask = (B == A_expanded)
    # ... 后续操作
    pass

这种方法的核心在于创建一个与 A 和 B 元素数量乘积大小相近的布尔掩码(或索引张量)。对于 A 的大小为 N,B 的大小为 M,中间张量的大小将是 N x M。当 N 和 M 都非常大时,例如达到数百万甚至数亿时,N * M 的元素数量将远远超出可用内存,使得这种完全广播的方案不可行。

方案一:混合式索引查找(部分广播 + Python循环)

为了在一定程度上利用PyTorch的并行计算能力,同时避免完全广播带来的内存问题,可以采用一种混合方法:首先进行部分广播以识别匹配位置,然后使用Python循环将结果组织成所需的结构。

这种方法的核心思想是利用 (a.unsqueeze(1) == b).nonzero() 来获取所有匹配的 (A_index, B_index) 对。a.unsqueeze(1) 将张量 a 变为 (N, 1) 的形状,与 b(形状为 (M,))进行比较时,会广播成 (N, M) 的布尔张量。然后 nonzero() 函数会立即将这个 (N, M) 的布尔张量转换为一个 (K, 2) 的索引张量,其中 K 是匹配的总数量,这大大减少了内存占用

import torch

def find_indices_hybrid(a: torch.Tensor, b: torch.Tensor):
    """
    使用部分广播和Python循环查找张量B中元素在张量A中的所有索引。

    Args:
        a (torch.Tensor): 目标张量A。
        b (torch.Tensor): 待查找值的张量B。

    Returns:
        list[list[int]]: 一个列表的列表,其中每个子列表包含B中对应值在A中的索引。
    """
    if a.ndim != 1 or b.ndim != 1:
        raise ValueError("输入张量a和b必须是一维张量。")

    # 步骤1: 使用unsqueeze和nonzero获取所有匹配的(a_idx, b_idx)对
    # (a.unsqueeze(1) == b) 会创建一个 N x M 的布尔张量,
    # 其中 (i, j) 为 True 表示 a[i] == b[j]。
    # nonzero() 将其转换为一个 K x 2 的张量,K是匹配的总数,
    # 每行 [a_idx, b_idx] 表示 a[a_idx] == b[b_idx]。
    overlap_idxs = (a.unsqueeze(1) == b).nonzero()

    # 步骤2: 初始化结果列表,为B中的每个元素预留一个子列表
    output = [[] for _ in b]

    # 步骤3: 遍历匹配对,将A的索引添加到B对应值的列表中
    for (a_idx, b_idx) in overlap_idxs:
        output[b_idx.item()].append(a_idx.item())

    return output

# 示例
A_tensor = torch.tensor([1, 2, 3, 3, 2, 1, 4, 5, 9])
B_tensor = torch.tensor([1, 2, 3, 9])
result_hybrid = find_indices_hybrid(A_tensor, B_tensor)
print(f"混合方法结果: {result_hybrid}")
# 预期输出: [[0, 5], [1, 4], [2, 3], [8]]

优点:

  • nonzero() 操作能有效减少内存占用,因为它只存储 True 值的位置,而非整个 N x M 的布尔矩阵。
  • 初始的比较操作在PyTorch后端进行,具有一定的并行性。

缺点:

ClipDrop Relight
ClipDrop Relight

ClipDrop推出的AI图片图像打光工具

下载
  • a.unsqueeze(1) == b 这一步仍然会生成一个 N x M 的布尔张量,尽管 nonzero() 紧随其后,但这个中间张量在某些极端情况下(N 和 M 都非常大,但匹配数量 K 相对较小)仍可能导致内存峰值。
  • 最终结果的构建依赖于Python循环,这在大规模匹配对 K 的情况下可能会较慢。

方案二:纯Python循环迭代查找

为了彻底避免任何大规模的中间张量,我们可以采用纯Python循环的方式,逐个处理张量B中的每个元素。这种方法牺牲了部分PyTorch的并行性,但在内存使用上最为保守。

import torch

def find_indices_pure_loop(a: torch.Tensor, b: torch.Tensor):
    """
    使用纯Python循环查找张量B中元素在张量A中的所有索引。

    Args:
        a (torch.Tensor): 目标张量A。
        b (torch.Tensor): 待查找值的张量B。

    Returns:
        list[list[int]]: 一个列表的列表,其中每个子列表包含B中对应值在A中的索引。
    """
    if a.ndim != 1 or b.ndim != 1:
        raise ValueError("输入张量a和b必须是一维张量。")

    output = []
    for _b_val in b:
        # 对于B中的每个值,在A中查找其所有索引
        # (a == _b_val) 会生成一个 N 长度的布尔张量
        # .nonzero() 找到所有为True的索引
        # .squeeze() 移除不必要的维度(例如,如果只有一个索引,结果是(1,)而不是(1,1))
        # .tolist() 转换为Python列表
        idxs = (a == _b_val).nonzero().squeeze().tolist()

        # 确保结果是列表形式,即使只有一个或没有匹配
        if not isinstance(idxs, list):
            idxs = [idxs] # 如果只有一个匹配,squeeze().tolist()可能返回一个int

        output.append(idxs)

    return output

# 示例
A_tensor = torch.tensor([1, 2, 3, 3, 2, 1, 4, 5, 9])
B_tensor = torch.tensor([1, 2, 3, 9])
result_pure_loop = find_indices_pure_loop(A_tensor, B_tensor)
print(f"纯循环方法结果: {result_pure_loop}")
# 预期输出: [[0, 5], [1, 4], [2, 3], [8]]

优点:

  • 内存使用最为优化。在任何时刻,只处理 A 与 B 中单个元素的比较,中间张量的大小始终是 N(A 的长度),避免了 N x M 级别的内存开销。
  • 对于内存极其受限的场景,这是最稳妥的选择。

缺点:

  • 由于外部循环是Python循环,其执行效率通常低于PyTorch的完全向量化操作。当 B 的长度 M 非常大时,性能可能会成为瓶颈。

性能与内存考量及选择

选择哪种方法取决于具体的应用场景:

  • 内存优先级最高: 如果你的张量A和B都非常大,以至于 N x M 的布尔张量绝对无法在内存中创建,那么纯Python循环迭代查找(方案二)是唯一的选择。它以牺牲计算速度为代价,换取了最小的内存占用。
  • 性能与内存平衡: 如果 N x M 的布尔张量虽然大但其 nonzero() 后的结果 K x 2 张量可以接受,并且你希望利用PyTorch的并行性,那么混合式索引查找(方案一)可能是一个不错的折衷方案。它在 nonzero() 之前存在一个内存峰值,但之后内存占用会迅速下降。
  • 小规模张量: 对于 N 和 M 都不太大的情况,直接使用完全广播的向量化方法(如 (A[..., None] == B).any(-1).nonzero() 的变体或原始问题中提及的 vectorized_find_indices 的优化版本)可能是最快和最简洁的。然而,本文主要关注大规模张量的内存优化。

注意事项:

  • 上述两种方案都将返回一个列表的列表。如果 B 中的某个值在 A 中不存在,对应的子列表将为空。
  • 确保输入张量是一维的,如果不是,可能需要先进行 flatten() 或其他重塑操作。
  • 在实际应用中,可以通过对 B 进行去重处理(B.unique())来优化,减少不必要的重复查找,但需要额外的逻辑来将结果映射回原始 B 的结构。

总结

高效查找大规模张量中元素的索引是一个常见的挑战,尤其是在内存受限的环境中。本文介绍了两种内存优化的策略:一种是结合部分广播和Python循环的混合方法,它通过 nonzero() 及时减少内存占用;另一种是纯Python循环迭代的方法,它以最保守的内存使用方式,逐个处理元素。开发者应根据其张量规模、内存预算和性能要求,权衡利弊,选择最适合的解决方案。理解这些方法的内存和性能特性,是构建健壮且高效PyTorch应用的关键。

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

746

2023.06.15

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

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

634

2023.07.20

python能做什么
python能做什么

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

758

2023.07.25

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

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

617

2023.07.31

python教程
python教程

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

1261

2023.08.03

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

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

547

2023.08.04

python eval
python eval

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

577

2023.08.04

scratch和python区别
scratch和python区别

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

705

2023.08.11

Java 项目构建与依赖管理(Maven / Gradle)
Java 项目构建与依赖管理(Maven / Gradle)

本专题系统讲解 Java 项目构建与依赖管理的完整体系,重点覆盖 Maven 与 Gradle 的核心概念、项目生命周期、依赖冲突解决、多模块项目管理、构建加速与版本发布规范。通过真实项目结构示例,帮助学习者掌握 从零搭建、维护到发布 Java 工程的标准化流程,提升在实际团队开发中的工程能力与协作效率。

9

2026.01.12

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Django 教程
Django 教程

共28课时 | 3万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号