当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > IterableDataset 只有一个 shard 时怎么继续切分数据

IterableDataset 只有一个 shard 时怎么继续切分数据

来源:17golang原创 2026-10-15 02:18:12 0浏览 收藏

Hugging Face Datasets 的 IterableDataset 在流式读取大数据时可以通过 shard 分配工作;但如果 reshard() 之后 num_shards 仍然是 1,就不应该继续把希望寄托在物理分片上。此时可以把数据看成一条有顺序的流,先确定顺序,再用 skip(offset) 和 take(limit) 划出逻辑窗口。

官方地址:https://huggingface.co/docs/datasets/stream

单 shard 的处理口诀是:能增加物理 shard 就用分片;增加不了就先 shuffle,再用 skip 定位起点、用 take 限定长度。切片之后不要再改变流的顺序,并为短流和重复遍历留出边界判断。

先判断 shard 还能不能继续增加

这里要区分两种“切分”。物理 shard 是 IterableDataset 底层可分配给读取者的分片数量;逻辑窗口则是从同一条可迭代数据流中,按位置取出一段记录。前者适合让多个读取者各拿一组底层文件,后者适合底层只有一个文件或格式无法继续重分片的情况。

官方文档给出的边界是:如果数据在 reshard() 之后仍然只有一个 shard,就使用 take() 和 skip() 做进一步切分。这个结论并不意味着每个窗口都拥有一个独立的底层文件,它只说明调用方可以把消费范围明确下来。

load_dataset、IterableDataset、num_shards、reshard 与 skip take 逻辑窗口之间的静态关系
图1:IterableDataset 的物理 shard 与 skip/take 逻辑窗口关系示意图;这是静态结构图,不是运行截图。

切片前先固定流的顺序

IterableDataset.shuffle() 不只是改变样本顺序,它还可能改变底层 shard 的访问顺序。为了让窗口划分有清晰语义,应当在 skip 或 take 之前完成 shuffle。官方文档还特别提醒,take 和 skip 会锁定后续的 shard 顺序;完成切片后再调用 shuffle,调用链就不再是同一个可自由重排的流。

from datasets import load_dataset

# streaming=True 返回可迭代数据集,避免入口一次性加载全部样本
dataset = load_dataset(
    "HuggingFaceFW/fineweb",
    split="train",
    streaming=True,
)

# 先决定随机顺序,再创建逻辑窗口;seed 让同一配置可复现
shuffled = dataset.shuffle(seed=42, buffer_size=10_000)

# 如果 reshard 后仍只有一个底层 shard,就保留这条流做 skip/take
print("num_shards:", shuffled.num_shards)

这里的 buffer_size 是流式 shuffle 的采样缓冲区大小,不是 shard 数量,也不是窗口长度。它越大,局部随机化的范围通常越宽,但会增加内存占用;它不能替代后面的 skip 和 take。

用 skip 和 take 划出不重叠窗口

假设每个消费方需要 limit 条记录,第 worker_id 个窗口从 worker_id * limit 开始,就可以把起点和长度显式写出来。窗口的关系是 offset = worker_id * limit,然后先跳过 offset 条,再读取最多 limit 条。

def make_window(dataset, worker_id, window_size):
    # 每个窗口使用固定长度,offset 决定它在逻辑流中的起点
    offset = worker_id * window_size

    # skip 定位起点,take 限制本窗口最多消费多少条记录
    return dataset.skip(offset).take(window_size)


# 先创建窗口,不在窗口创建后再次调用 shuffle
window_a = make_window(shuffled, worker_id=0, window_size=1_000)
window_b = make_window(shuffled, worker_id=1, window_size=1_000)

# 两个窗口的逻辑范围分别是 [0, 1000) 与 [1000, 2000)
for row in window_a:
    consume(row)
for row in window_b:
    consume(row)

这个写法表达的是“逻辑区间不重叠”,不是“底层读取成本已经被均摊”。如果底层只能提供一个 shard,不同窗口可能仍然需要从流的开头扫描到各自的 offset;想减少重复扫描,应优先准备更多底层文件或让数据格式支持更细的 reshard,而不是只改窗口公式。

原始 IterableDataset、offset、limit、skip take 与两个逻辑窗口之间的静态参数关系
图2:offset、limit 与两个逻辑数据窗口的静态关系示意图;这是参数结构图,不是运行截图。

短流、重复遍历和 worker 分配要单独处理

take(window_size) 的语义是“最多取这么多”,数据不足时不会凭空补齐。最后一个窗口可能短于固定大小,因此下游不能把窗口长度无条件当成真实样本数。需要固定 batch 大小时,应在消费层处理不足一批的情况,而不是把它误判成 shard 切分失败。

还要留意流式数据的可重复遍历成本。为多个 worker 分别构造 skip(offset) 视图,能够清晰表达不重叠范围,但每个视图都可能重新走过前面的记录。若数据源是网络流或压缩文件,重复跳过的代价会比较明显。此时应把窗口分配和数据格式一起设计,必要时先落成多个可独立读取的文件。

如果使用 PyTorch DataLoader,可以优先利用 IterableDataset 的 shard 分配能力;只有在官方文档所说的单 shard 边界下,才把 skip/take 作为逻辑切片工具。不要同时让 DataLoader worker 和业务代码各自按 offset 切一次,否则很容易出现重复跳过或窗口重叠。

一段紧凑的窗口工厂

把顺序固定、窗口参数和消费函数分开,后续更容易替换数据源。下面的工厂不运行检查,也不假设流一定足够长;它只负责把窗口边界写成明确对象。

def window_plan(dataset, count, window_size):
    # 返回逻辑窗口,调用方决定何时真正迭代数据
    for worker_id in range(count):
        offset = worker_id * window_size
        yield {
            "worker_id": worker_id,
            "offset": offset,
            "limit": window_size,
            "dataset": dataset.skip(offset).take(window_size),
        }


# shuffle 应在传入工厂前完成,避免窗口建立后改变顺序
plans = window_plan(shuffled, count=2, window_size=1_000)
for plan in plans:
    print(plan["worker_id"], plan["offset"], plan["limit"])

如果你要的是底层文件级并行,优先检查 num_shards 和 reshard() 的结果;如果你要的是单流上的确定范围,使用 skip/take 并把 offset、limit、短流策略记录在任务配置中。两者解决的问题不同,不要用一个指标替代另一个。

常见问题

为什么 reshard 后 num_shards 还是 1?

这通常意味着当前数据格式或底层文件结构无法继续提供更多独立 shard。此时按照官方边界使用 skip 和 take,或者在数据准备阶段生成更多可独立读取的文件。

skip 和 take 的顺序能交换吗?

如果目标是从原始流的某个位置取固定长度,应先 skip(offset) 再 take(limit)。先 take 再 skip 作用于已经截短的视图,得到的范围就不是同一个窗口定义。

shuffle 应该放在哪里?

放在创建窗口之前。因为 take 和 skip 会锁定后续的 shard 顺序,切片之后再 shuffle 可能触发限制,也会让窗口含义变得不清楚。

归纳起来,单 shard 不是“不能切分”,而是不能继续依赖物理 shard 做切分:先确定流式顺序,再用 offset、skip 和 take 建立逻辑窗口,最后单独评估短流和重复扫描成本。这样既符合 Hugging Face Datasets 的官方边界,也能让窗口分配规则在代码和配置中保持可读。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Hugging Face Datasets 怎样从 JSON 加载数据集Hugging Face Datasets 怎样从 JSON 加载数据集
上一篇
Hugging Face Datasets 怎样从 JSON 加载数据集
Accelerate 怎样配置梯度累积
下一篇
Accelerate 怎样配置梯度累积
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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模型性能。
    474次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    533次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    552次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    506次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    324次使用