当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > PyTorch IterableDataset 怎样为每个 worker 分配不同数据片段

PyTorch IterableDataset 怎样为每个 worker 分配不同数据片段

来源:17golang原创 2026-10-26 22:15:29 0浏览 收藏

用 PyTorch 的 IterableDataset 配合 DataLoader(num_workers>0) 时,最容易误判的一点是:数据集对象会在每个 worker 进程里各有一份副本。如果 __iter__() 仍然从同一个起点完整遍历,每个 worker 都会读到同样的样本。

解决办法是把分片逻辑放进 __iter__():通过 torch.utils.data.get_worker_info() 取得当前 worker 的编号和总数,再把整体的半开区间 [start, end) 切成互不重叠的局部区间。单进程运行时,get_worker_info() 返回 None,代码仍然保持原来的完整遍历。

核心判断:IterableDataset 不会自动替你做 worker 分片。只要一个数据集副本没有根据 worker id 改变读取范围,多 worker 就可能重复消费同一批数据。

这篇只讨论 DataLoader 的多进程 worker 分片,不展开多机或分布式训练的采样器选型。

为什么多 worker 会读到重复样本

对普通的 map-style 数据集,DataLoader 可以由主进程生成索引,再把索引分发给 worker。IterableDataset 没有这种随机索引模型,数据是由各个副本自行迭代产生的。于是设置 num_workers=2 后,Worker 0 和 Worker 1 都可能从 0 开始读取。

DataLoader 与 IterableDataset 副本及两个 worker 读取数据源的静态关系说明图
图1:静态结构说明图,查看 IterableDataset 副本与多个 worker 的关系;它不是运行截图。

因此问题不在于 DataLoader “把同一条样本发了两次”,而在于两个独立的迭代器都认为自己拥有完整数据源。最小的修复边界是:每个 worker 只负责一个不重叠的区间或文件分片,并且这个判断要发生在 worker 进程执行 __iter__() 时。

在 __iter__ 中按 worker 切分区间

下面用整数区间模拟真实数据源。start 和 end 采用半开区间,意味着包含起点、不包含终点;这样相邻区间可以直接拼接,不会重复边界值。

import math
import torch
from torch.utils.data import DataLoader, IterableDataset, get_worker_info


class RangeDataset(IterableDataset):
    def __init__(self, start: int, end: int):
        super().__init__()
        self.start = start
        self.end = end

    def __iter__(self):
        # 主进程没有 worker 信息;多进程时每个 worker 都会拿到自己的副本。
        worker_info = get_worker_info()
        local_start, local_end = self.start, self.end

        if worker_info is not None:
            # 用向上取整给每个 worker 一个连续的半开区间。
            total = local_end - local_start
            per_worker = int(math.ceil(total / worker_info.num_workers))
            local_start = self.start + worker_info.id * per_worker
            local_end = min(local_start + per_worker, self.end)

        # 空区间自然不会产生样本,避免为了补齐数量而重复读取。
        yield from range(local_start, local_end)


dataset = RangeDataset(0, 10)
loader = DataLoader(dataset, num_workers=2, batch_size=2)
for batch in loader:
    # 这里关注的是不重复分片,DataLoader 返回顺序仍由调度和批次组成决定。
    print(batch)

以 [0, 10) 和两个 worker 为例,per_worker 为 5,两个副本分别负责 [0, 5) 与 [5, 10)。当总数不能被 worker 数整除时,向上取整会让前面的区间先拿到一个完整容量,最后一个区间可能更短,但不会越过 end。

get_worker_info 参数与半开区间分片边界的静态关系说明图
图2:区间关系说明图,查看 worker 参数与半开数据区间的对应边界;它不是运行结果截图。

不整除、尾部数据和 drop_last 怎么判断

分片正确只说明“同一条样本不会被两个 worker 主动读到”,不代表每个 batch 都完整。IterableDataset 在多进程读取时,每个 worker 都有自己的数据副本,drop_last=True 可能按副本丢弃各自最后一个不完整 batch。因此总样本数、实际 batch 数和最后一批大小不能只用全局总数简单相除。

排查时按下面三点看:

  • 先把 num_workers 设为 0,确认单进程数据源本身没有重复。
  • 再用一个明确的有限区间,记录每个 worker 的 id、局部起点和局部终点,确认区间相邻且不重叠。
  • 最后再加入真实的 batch_size 和 drop_last,把“分片重复”和“尾批被丢弃”分开判断。

如果真实数据源不是整数范围,而是文件列表、数据库游标或远程对象,也要把“打开资源”和“选择片段”放在 worker 可见的边界内。不要在主进程创建一个不可安全复制的连接对象,再指望多个副本共享它完成分片。

上线前的四个检查点

第一,确认分片依据是 worker 的稳定身份,而不是在多个进程间共享并自增的普通 Python 变量。第二,使用 self.start、self.end 计算局部变量,避免一次数据集对象被重复创建迭代器时留下错误的上次状态。第三,明确尾部策略:允许短尾批次就保留,必须整批才处理就显式设置 drop_last 并接受其丢弃规则。第四,先用可数且容易核对的样本源做去重检查,再接入无限流或远程数据源。

如果改成 worker_init_fn 来配置每个副本,也要保持同一原则:每个副本必须获得不同的读取边界;只初始化随机种子而不改变数据范围,并不能解决重复消费。

常见问题

单进程时为什么不需要特殊分片?

因为 get_worker_info() 在主进程返回 None,示例会直接使用完整的 [start, end)。这也是先用 num_workers=0 排查数据源的原因。

worker 数量变化后能继续使用旧的区间吗?

不能把旧的静态边界硬编码下来。区间应当在每次 __iter__() 中根据当前的 num_workers 和 worker_id 重新计算,否则 worker 数变化后可能出现遗漏或重叠。

为什么输出顺序看起来不是按区间连续排列?

多个 worker 会并行产出,DataLoader 还会按 batch 组织结果。输出顺序不是判断分片是否重复的可靠依据;应检查每个 worker 的局部范围和全局样本集合。

记住一句话:IterableDataset 负责“怎样产生数据”,DataLoader worker 负责“在哪个进程产生数据”,两者之间的分片契约需要由 __iter__() 显式补上。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Hugging Face Hub 怎样用缓存目录管理模型文件Hugging Face Hub 怎样用缓存目录管理模型文件
上一篇
Hugging Face Hub 怎样用缓存目录管理模型文件
Hugging Face Datasets 导出 Parquet 时怎么控制分片大小
下一篇
Hugging Face Datasets 导出 Parquet 时怎么控制分片大小
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    719次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    726次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    801次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    762次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    599次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码