【Bug已解决】[bug report] DataSourcesShufflingDisallowed when training using split datasets.IterableDatas
【Bug已解决】[bug report] DataSourcesShufflingDisallowed when training using split datasets.IterableDataset 解决方案一、现象长什么样用datasets的IterableDataset流式数据集做训练先做了 train/valsplit再喂给 Accelerate 的DataLoader时报错DataSourcesShufflingDisallowed: Cannot shuffle a dataset that is sharded/streamed或者ValueError: DataSourcesShufflingDisallowed when training using split datasets.IterableDataset几个特征只在IterableDataset流式上炸普通map-styleDataset做 split 正常。崩在Accelerator.prepare(dataloader)或构造DataLoader(shuffleTrue)阶段还没开始训练。往往伴随「我明明只想正常打乱训练顺序」的困惑——打乱是训练常规操作这里却被禁止。数据集是「先 split 再流式读取」的组合比如从云端大数据做streamingTrue的 train_test_split。本质IterableDataset 是「流」没法像数组那样随机打乱它没有__len__、不能随机下标访问当它还被按 rank 分片sharded后打乱会破坏各 rank 分片的确定性。Accelerate 检测到「流式/分片数据集 请求 shuffle」就直接拒绝于是报DataSourcesShufflingDisallowed。二、背景先区分两种数据集Map-style Dataset整份数据在本地dataset[i]可随机访问可以shuffleTrue随机排列下标。IterableDataset流式数据从流里逐个产生如远程大数据、不能全下本地只能顺序迭代没有随机下标。要「打乱」只能用固定大小的缓冲池 shuffleshuffle(buffer_sizeN)——读 N 条进缓冲、从中随机抽、再补新条是一种「近似打乱」。然后 Accelerate 在分布式训练时会把数据集按 rank 分片每个 rank 读不同片段保证不重复。问题叠加你是 IterableDataset流本来就不能整体 shuffle。又做了 splittrain/valsplit 在流式下往往也是「基于分片边界」切的进一步强化了「分片不可随意打乱」的约束。你或框架默认给DataLoader设了shuffleTrue。Accelerate 的prepare看到「这是个被分片的 IterableDataset、却要求 shuffle」判定这会破坏分片确定性各 rank 打乱后可能读到重叠/错乱数据于是抛DataSourcesShufflingDisallowed明确禁止。一句话流式 分片的数据集不支持传统 shuffle而你或默认配置请求了 shuffle被框架拒绝。三、根因根因是对 IterableDataset流式/分片使用了只适用于 map-style 的shuffleTrue框架为保分片确定性而禁止三层第一层主因shuffleTrue用在 IterableDataset 上语义不成立。DataLoader(shuffleTrue)依赖「随机排列样本下标」而 IterableDataset 没有下标、只能顺序流。这个开关对它是错的必须用流式近似打乱dataset.shuffle(buffer_size...)。第二层split 强化了分片约束。流式数据集做 train/val split 后train 和 val 各自是分片流。Accelerate 对「分片流」的 shuffle 特别敏感——因为打乱会让分片边界失效某 rank 可能既读到 train 又读到 val或重复。于是 split IterableDataset 组合更容易触发该禁止。第三层框架默认shuffleTrue未因数据集类型调整。很多训练脚本写死DataLoader(shuffleTrue)对 map-style 合理换到流式数据集时没改。Accelerate 应该在prepare时自动检测 IterableDataset 并降级 shuffle但实际是「直接报错」而非「自动改」把适配责任甩给用户。一句话流式分片数据集被请求传统 shuffle框架为保分片确定性禁止且未自动降级适配。四、最小可运行复现下面用纯 Python 模拟「IterableDataset shuffleTrue 被框架拒绝」的控制流不需要装 datasetsclass IterableDataset: def __init__(self, shardedFalse): self.sharded sharded def shuffle(self, buffer_sizeNone): # 流式近似打乱正确做法 return fstreaming-shuffle(buf{buffer_size}) class DataLoader: def __init__(self, dataset, shuffleFalse): self.dataset dataset self.shuffle shuffle def accelerator_prepare_buggy(dataloader): ds dataloader.dataset if dataloader.shuffle and isinstance(ds, IterableDataset): if ds.sharded: raise RuntimeError( DataSourcesShufflingDisallowed: Cannot shuffle a sharded/streamed IterableDataset ) return dataloader def main(): ds IterableDataset(shardedTrue) dl DataLoader(ds, shuffleTrue) # 错误对流式用 shuffleTrue try: accelerator_prepare_buggy(dl) except RuntimeError as e: print(复现成功:, e) if __name__ __main__: main()跑出来打印复现成功: DataSourcesShufflingDisallowed: Cannot shuffle a sharded/streamed IterableDataset和线上完全一致流式分片数据集 shuffleTrue 被拒。五、解决方案第一层最小直接修复最省事的救火对 IterableDataset 不要用shuffleTrue改用流式近似打乱 DataLoader(shuffleFalse)from datasets import load_dataset from torch.utils.data import DataLoader from accelerate import Accelerator accelerator Accelerator() # 流式加载并 split ds_stream load_dataset(my_big_corpus, streamingTrue) # 注意流式下用 skip/take 或 datasets 的 split 参数而非 train_test_split train_ds ds_stream[train] eval_ds ds_stream[validation] # 关键 1用数据集自带的流式 shuffle近似打乱而不是 DataLoader(shuffleTrue) train_ds train_ds.shuffle(buffer_size10_000) # 关键 2DataLoader 必须 shuffleFalse流式不支持传统 shuffle train_dl DataLoader(train_ds, batch_size8, shuffleFalse) eval_dl DataLoader(eval_ds, batch_size8, shuffleFalse) train_dl, eval_dl accelerator.prepare(train_dl, eval_dl)这样shuffleFalse不会触发禁止而train_ds.shuffle(buffer_size...)提供了流式近似打乱训练效果不受影响。六、解决方案第二层结构性改进第一层是「手动改 shuffleFalse」第二层是「封装一个 DataLoader 工厂自动按数据集类型选正确打乱方式」从设计上消灭误用from dataclasses import dataclass from typing import Optional dataclass class DatasetSpec: is_iterable: bool sharded: bool buffer_size: Optional[int] 10_000 def make_dataloader(dataset, spec: DatasetSpec, batch_size: int): 按数据集类型自动选正确的打乱策略。 if spec.is_iterable: if spec.sharded and False: # 永远不为分片流式用 shuffleTrue raise RuntimeError(DataSourcesShufflingDisallowed) # 流式数据集自带近似打乱 DataLoader(shuffleFalse) if spec.buffer_size: dataset dataset.shuffle(buffer_sizespec.buffer_size) return DataLoader(dataset, batch_sizebatch_size, shuffleFalse) else: # map-style传统 shuffleTrue 合法 return DataLoader(dataset, batch_sizebatch_size, shuffleTrue) def validate_no_illegal_shuffle(dataset, shuffle: bool) - None: 框架应在 prepare 前主动校验并给出可读提示。 if shuffle and isinstance(dataset, IterableDataset) and dataset.sharded: raise RuntimeError( DataSourcesShufflingDisallowed: 流式分片数据集不支持 DataLoader(shuffleTrue)。请改用 dataset.shuffle(buffer_sizeN) 做流式近似打乱 并保持 DataLoader(shuffleFalse)。 ) # 用法 spec DatasetSpec(is_iterableTrue, shardedTrue, buffer_size10000) dl make_dataloader(train_ds, spec, batch_size8) # 自动正确关键改进工厂函数把「IterableDataset → shuffleFalse 流式 shuffle」和「map-style → shuffleTrue」固化用户不再手写错开关validate_no_illegal_shuffle在 prepare 前给出可读错误而非裸拒绝。七、解决方案第三层断言 / CI 守护把「流式禁用 shuffleTrue」「自动选策略」「可读报错」固化成测试import pytest def test_iterable_sharded_rejects_shuffle_true(): ds IterableDataset(shardedTrue) with pytest.raises(RuntimeError): validate_no_illegal_shuffle(ds, shuffleTrue) def test_mapstyle_allows_shuffle_true(): ds object() # 非 IterableDataset validate_no_illegal_shuffle(ds, shuffleTrue) # 不抛 def test_factory_iterable_uses_false(): spec DatasetSpec(is_iterableTrue, shardedTrue) dl make_dataloader(ds, spec, 8) assert dl.shuffle is False def test_factory_mapstyle_uses_true(): spec DatasetSpec(is_iterableFalse, shardedFalse) dl make_dataloader(ds, spec, 8) assert dl.shuffle is True def test_streaming_shuffle_applied(): ds IterableDataset(shardedTrue) spec DatasetSpec(is_iterableTrue, shardedTrue, buffer_size5000) dl make_dataloader(ds, spec, 8) # 确认用了流式近似打乱而非传统 shuffle assert dl.shuffle is False再加一个端到端回归流式 split 数据集 Accelerate prepare 不报DataSourcesShufflingDisalloweddef test_streaming_split_prepare_ok(): train_ds IterableDataset(shardedTrue).shuffle(buffer_size1000) dl DataLoader(train_ds, batch_size8, shuffleFalse) # prepare 不应抛 DataSourcesShufflingDisallowed prepared accelerator_prepare_safe(dl) assert prepared is not None八、排查清单看报错是不是DataSourcesShufflingDisallowed且数据集是 IterableDataset流式→ 坐实本问题。检查DataLoader是否写了shuffleTrue流式数据集必须False。检查是否对流式数据集做了train_test_split应用 split 参数或 skip/take 替代。临时救火DataLoader(shuffleFalse)dataset.shuffle(buffer_sizeN)做流式近似打乱。长期修复封装 DataLoader 工厂按数据集类型自动选打乱策略prepare 前做可读校验。升级 accelerate/datasets 到合了流式 shuffle 自动适配的版本并跑上面的用例。若需要更强打乱增大buffer_size但注意流式打乱只是近似非全局随机。九、小结DataSourcesShufflingDisallowed不是数据集坏了而是对 IterableDataset流式/分片用了只适用于 map-style 的shuffleTrue框架为保分片确定性而禁止。最小修复是DataLoader(shuffleFalse)dataset.shuffle(buffer_size...)做流式近似打乱结构性修复是封装 DataLoader 工厂按类型自动选策略、prepare 前做可读校验最后用 pytest 把「流式禁用 shuffleTrue」「自动选策略」「split 流式 prepare 不崩」锁死。抓住「流式数据集只能近似打乱、绝不能用传统 shuffleTrue」这条所有 IterableDataset Accelerate 的 shuffle 报错都能照此化解。