目录

注意力

2024 年 6 月状态更新:删除 DataPipes 和 DataLoader V2

我们将 torchdata 存储库重新调整为torch.utils.data.DataLoader的迭代增强。我们不打算 继续开发或维护 [DataPipes] 和 [DataLoaderV2] 解决方案,它们将从 torchdata 存储库。我们还将重新访问 pytorch/pytorch 中的 DataPipes 引用。在 torchdata==0.8.0(2024 年 7 月)版本中,它们将被标记为已弃用,而在 0.10.0(2024 年末)中,它们将被删除。现存 建议用户固定到 torchdata<=0.9.0 或更早版本,直到他们能够迁移出去。随后的 版本将不包含 DataPipes 或 DataLoaderV2。 如果您有建议或评论,请联系我们(请使用此问题进行反馈)

什么是 (beta)?torchdata.nodes

torchdata.nodes是可组合迭代器库(不是 可迭代对象!允许您将常见的 Dataloading 和 Pre-proc 链接在一起 操作。它遵循流式编程模型,尽管“sampler + Map-style“ 如果您愿意,仍然可以配置。

torchdata.nodes为标准产品增加了更大的灵活性,并引入了多线程并行性 除了多进程(中唯一支持的方法)以及对 mid-epoch 检查点。torch.utils.datatorch.utils.data.DataLoaderstate_dict/load_state_dict

torchdata.nodes努力包含尽可能多的有用运算符 可能,但它设计为可扩展。需要新节点 到子类 , (它本身是子类 )并实现 , 和操作(值得注意的是,不是 , , , 也不是torchdata.nodes.BaseNodetyping.Iteratornext()reset(initial_state)get_state()__next__load_state_dictstate_dict)

请参阅 torchdata.nodes 入门 (beta) 以开始使用

为什么?torchdata.nodes

我们明白,它适用于很多很多用例。 然而,它肯定有一堆粗糙的地方:torch.utils.data

多进程很糟糕

  • 您需要复制存储在 Dataset 中的内存(由于 Python 读取时复制)

  • IPC 在多进程队列上速度较慢,并且可能会导致启动缓慢 次

  • 您被迫对 worker 执行批处理,而不是 main-process 来减少 IPC 开销,增加峰值内存。

  • 借助 GIL 发布函数和自由线程 Python, 多线程可能不像以前那样受 GIL 限制。

torchdata.nodes支持多线程和多处理 SO 您可以选择最适合您的特定设置的方法。排比 主要在 Mapper 运算符中配置,为您提供 并行化的内容、时间和方式。

地图样式和随机访问无法缩放

当前的地图数据集方法非常适合适合内存的数据集, 但是,一旦你的 数据集超出内存限制,除非您跳过一些 带有特殊采样器的箍。

torchdata.nodes遵循流数据模型,其中运算符为 迭代器,可以组合在一起以定义数据加载和 Pre-proc 管道。采样器仍然受支持(参见上面的示例)和 可以与 Mapper 结合使用以生成 Iterator

多数据集与当前的实现不太适合torch.utils.data

当前的 Sampler(每个数据加载器一个)概念开始崩溃 当您开始尝试合并多个数据集时。(对于单人 数据集,它们是一个很好的抽象,并将继续如此 支持!

  • 对于多数据集,请考虑以下场景:。现在我们想要执行循环(或均匀采样) 在这两个数据集之间提供给我们的培训师。只需一个 Sampler,您如何实施该策略?也许是一个采样器 发出元组?如果您想与 RandomSampler 交换,或者 DistributedSampler 的如何运作?len(dsA): 10len(dsB): 20sampler.set_epoch

torchdata.nodes有助于解决和扩展多数据集数据加载 通过仅处理 Iterators,从而强制采样器和数据集 一起,专注于将较小的 Primitives 节点组合成一个 复杂的数据加载管道。

IterableDataset + multiprocessing 需要额外的数据集分片

数据集分片是数据并行训练所必需的,这是相当公平的 合理。但是 dataloader worker 之间的分片呢?跟 地图样式的数据集,worker 之间的工作分配由 主进程,将采样器索引分发给 worker。跟 IterableDatasets,每个 worker 都需要(通过 )弄清楚它应该返回哪些数据。torch.utils.data.get_worker_info

表现如何?torchdata.nodes

我们在 PyTorch Conf 2024 上展示了早期版本的视频解码基准测试的一些结果,其中我们展示了:torchdata.nodes

  • torchdata.nodes 的性能与使用多进程时相同或更好(请参阅从 torch.utils.data 迁移到 torchdata.nodestorch.utils.data.DataLoader)

  • 使用 GIL python 时,具有多线程的 torchdata.nodes 性能优于 多处理,但使 GPU 预处理等功能 更容易执行,可以提升

我们运行了一个基准测试,从磁盘加载 Imagenet 数据集, 并使用自由线程 Python (3.13t) 设法使主内存带宽饱和 CPU 利用率明显低于多进程工作程序 (预计 2025 年发布的博客文章)。看。examples/nodes/imagenet_benchmark.py

设计选择

无生成器 BaseNode

更多想法见 https://github.com/pytorch/data/pull/1362

我们做出的一个艰难选择是在定义 新的 BaseNode 实现。但是,我们放弃了它并移动到 仅 Iterator 基础,原因如下:

  1. 我们需要在 BaseNode 实现中进行显式状态处理。 生成器将 state 隐式存储在堆栈上,我们发现 需要跳过重重障碍并编写非常复杂的代码来获得 使用 Generator 的基本状态

  2. End-of-iteration state dict:但是,Iterables 可能感觉更自然 围绕状态管理出现了一系列问题。考虑一下 end-of-iteration state dict 的 DICT 中。如果您将此state_dict加载到 iterable 的 下一次迭代?

  3. 加载状态:如果你在可迭代对象上调用 load_state_dict(),则大多数 用户希望从它请求的下一个迭代器以 loaded 状态。但是,如果 iter 之前被调用两次怎么办 迭代开始?

  4. 多个 Live Iterator 问题:如果您有一个 Iterable,但是两个实时迭代器,调用什么 state_dict() 在 Iterable 上?在数据加载中,这种情况非常罕见, 但是我们仍然需要解决它并制作一堆 假设。强制实现 BaseNode 的开发人员进行推理 在我们看来,这比不允许更糟糕 generators 和 Iterables 中。

torchdata.nodes.BaseNodeimplementations 是 Iterators。迭代器 定义 、 和 。 所有重新初始化都应该在 reset() 中完成,包括初始化 如果传递了一个特定状态。next()get_state()reset(initial_state | None)

但是,最终用户习惯于处理 Iterables,例如,

for epoch in range(5):
  # Most frameworks and users don't expect to call loader.reset()
  for batch in loader:
    ...
  sd = loader.state_dict()
  # Loading sd should not throw StopIteration right away, but instead start at the next epoch

为了处理这个问题,我们保留了所有的假设和特殊的 end-of-epoch handle 在单个类中处理,该类接受任何 BaseNode 并使 它是一个 Iterable,处理 reset() 调用和 end-of-epoch state_dict 装载。Loader

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源