这个报错的根源就是你把 DataLoader 传给了 torch.utils.data.Subset,Subset 只支持接收 Dataset 数据集对象,不支持 DataLoader,DataLoader 是迭代器,不是可下标索引的数据集,不能用 [] 取子集,Subset 内部会尝试用下标访问传入对象,DataLoader 不支持下标访问,直接触发not subscriptable报错,正确做法是先拿原始 Dataset 丢进 Subset 划分训练集、验证集,再把 Subset 输出的子集对象交给 DataLoader 去打包成批次,顺序不能颠倒,很多新手写代码时容易提前构造 DataLoader 再做切分,只要调换顺序,先切数据集、再封装加载器,这个报错就能直接解决。
一、对象能力对比表
| 对象 | 是否支持下标 [] | 能不能传给 Subset | 作用 |
|---|---|---|---|
| Dataset | ✅ 支持 | ✅ 可以 | 定义单条数据读取逻辑 |
| Subset | ✅ 支持 | ✅ 可以 | 对 Dataset 做切片、划分子集 |
| DataLoader | ❌ 不支持 | ❌ 禁止 | 批次打包、shuffle、多进程加载 |
二、错误写法(触发报错)
from torch.utils.data import Dataset, DataLoader, Subset
先构造loader,再给Subset,错误
full_dataset = MyDataset()
full_loader = DataLoader(full_dataset, batch_size=8)
train_subset = Subset(full_loader, indices=[0,1,2]) 报错!
三、正确写法(Subset 包裹 Dataset)
from torch.utils.data import Dataset, DataLoader, Subset
full_dataset = MyDataset()
第一步:Subset包装原始Dataset
train_subset = Subset(full_dataset, indices=train_idx)
val_subset = Subset(full_dataset, indices=val_idx)
第二步:子集再交给DataLoader
train_loader = DataLoader(train_subset, batch_size=8, shuffle=True)
val_loader = DataLoader(val_subset, batch_size=8, shuffle=False)
四、完整操作步骤
- 定义并实例化原始 Dataset,这一步只负责单样本读取,不做批次加载
- 准备划分索引,比如随机划分、按序号切分 train/val 索引列表
- 使用 Subset,传入原始 Dataset 和索引,生成子集数据集
- 把 Subset 得到的子集传入 DataLoader,设置 batch_size、shuffle、num_workers 等参数
- 循环遍历 loader 取批次数据,不要尝试对 loader 使用loader[0]这种下标取值
五、高频踩坑清单
- 混淆 Dataset 和 DataLoader:Dataset 管数据样本,DataLoader 管迭代批次,二者职责完全分开
- 想通过下标取 loader 数据:loader[0]写法本身不合法,loader 只能 for 循环迭代
- 多次嵌套 Subset:Subset 可以嵌套 Subset,但不能嵌套 DataLoader
- 索引越界:传给 Subset 的 indices 不能超出原始 Dataset 长度,否则读取时报索引错误
- 随机种子问题:划分索引时固定 seed,保证每次实验训练验证集划分一致
六、补充:如果已经拿到 loader,想切分怎么办
不能直接切 loader,要回到最开始的 Dataset,重新做索引划分;loader 里面没有原始索引映射能力,它只是迭代器,没法反向切片。
【相关 FAQ】
问题 1:出现 'DataLoader' object is not subscriptable,为什么 Subset 不能包装 DataLoader?
回答 1:Subset 要求传入的对象支持下标索引,DataLoader 是迭代器,不支持 [] 下标访问,所以 Subset 不能包装 DataLoader,Subset 只能包装 Dataset。
问题 2:Subset 包装 Dataset 之后,还能不能再传给 DataLoader?
回答 2:可以,Subset 返回的依然是 Dataset 类型对象,Subset 包装 Dataset 得到子集,再送入 DataLoader 生成批次加载器,这是标准流程。
问题 3:除了 Subset 包 loader,还有哪些写法会触发 'DataLoader' object is not subscriptable?
回答 3:直接写loader[0]、loader[:10]用下标读取 DataLoader,同样会报这个错,DataLoader 只能 for 循环迭代读取批次。
问题 4:Subset 包装 Dataset 之后,子集里面的 transform 和原始 Dataset 是一样的吗?
回答 4:是的,Subset 只是做索引映射,不会修改数据读取逻辑,Subset 包装 Dataset 后,子集沿用原始 Dataset 的 transform、读取逻辑。
【参考资料】
[2025-07-18]('DataLoader' object is not subscriptable 子集划分错误)PyTorch 中文论坛
[2024-09-03](Can I use torch.utils.data.Subset on a DataLoader?)Stack Overflow
[2023-04-12](torch.utils.data.Subset official usage and example)PyTorch Docs
[2022-11-20](DataLoader 和 Dataset 区分,Subset 划分数据集踩坑)知乎
[2021-06-08](Subset 接收对象类型报错问题)GitHub PyTorch Issues
【免责声明】
本文基于 PyTorch 官方 Dataset、Subset、DataLoader 接口规则整理,针对 'DataLoader' object is not subscriptable 报错做技术科普;不同版本 PyTorch、自定义数据集类、采样器组合场景行为存在差异,代码使用前请打印类型校验,不保证适配所有复杂数据集组合场景。
