注意力
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.data
torch.utils.data.DataLoader
state_dict/load_state_dict
torchdata.nodes
努力包含尽可能多的有用运算符
可能,但它设计为可扩展。需要新节点
到子类 , (它本身是子类 )并实现 , 和操作(值得注意的是,不是 , , , 也不是torchdata.nodes.BaseNode
typing.Iterator
next()
reset(initial_state)
get_state()
__next__
load_state_dict
state_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): 10
len(dsB): 20
sampler.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.nodes
torch.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 基础,原因如下:
我们需要在 BaseNode 实现中进行显式状态处理。 生成器将 state 隐式存储在堆栈上,我们发现 需要跳过重重障碍并编写非常复杂的代码来获得 使用 Generator 的基本状态
End-of-iteration state dict:但是,Iterables 可能感觉更自然 围绕状态管理出现了一系列问题。考虑一下 end-of-iteration state dict 的 DICT 中。如果您将此state_dict加载到 iterable 的 下一次迭代?
加载状态:如果你在可迭代对象上调用 load_state_dict(),则大多数 用户希望从它请求的下一个迭代器以 loaded 状态。但是,如果 iter 之前被调用两次怎么办 迭代开始?
多个 Live Iterator 问题:如果您有一个 Iterable,但是两个实时迭代器,调用什么 state_dict() 在 Iterable 上?在数据加载中,这种情况非常罕见, 但是我们仍然需要解决它并制作一堆 假设。强制实现 BaseNode 的开发人员进行推理 在我们看来,这比不允许更糟糕 generators 和 Iterables 中。
torchdata.nodes.BaseNode
implementations 是 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