原帖把 train_dl 传给 Subset,后续 Subset.getitem 又试图执行 self.dataset[index]:
TypeError: 'DataLoader' object is not subscriptable
数据集负责索引,loader 负责取批次
原题错误是 Subset 内部试图索引传入的 DataLoader。把随机划分放在可索引的 train1 上,再创建 loader,能保留清楚的样本索引空间。不要仅将 loader[0] 改成 next 就跳过原划分逻辑。
DataLoader 是批次迭代器,没有普通样本索引接口。Subset 保存的对象应是可索引数据集,而不是已经组批的 loader。
先在创建 Subset 前确认类型:
print("josh050608")
print("train1:", type(train1), "length:", len(train1))
print("train_dl:", type(train_dl), "dataset:", type(train_dl.dataset))
原题 get_splits() 已返回 train1、test1,应在 train1 的索引空间里再划分验证集。下面 val_count 是明确的验证样本数,不是比例:
import torch
from torch.utils.data import Subset, DataLoader
val_count = 100 # 按自己的样本数修改
assert 0 < val_count < len(train1)
indices = torch.randperm(len(train1)).tolist()
train_subset = Subset(train1, indices[:-val_count])
valid_subset = Subset(train1, indices[-val_count:])
train_dl = DataLoader(train_subset, batch_size=32, shuffle=True)
valid_dl = DataLoader(valid_subset, batch_size=32, shuffle=False)
不要从原始全集生成索引,再拿来索引已划分的 train1;也不要为验证集另建一个内容或顺序可能不同的 Owndata 对象。训练函数究竟接收 Dataset 还是 DataLoader,也应保持一致。
原帖的 np.asarray(list(loader)) 会一次性读取并保留全部 batch,通常没必要。正常训练直接遍历 loader,避免额外内存占用和不规则数组转换。
验证 train_subset[0] 返回单样本、next(iter(train_dl)) 返回批次,并检查训练/验证索引不重叠且覆盖预期训练划分。
用固定种子验证划分不重叠
下面合成 12 条样本,先在 Dataset 上划分,再分别组批,验证训练和验证样本 ID:
import torch
from torch.utils.data import TensorDataset, Subset, DataLoader
dataset = TensorDataset(torch.arange(12), torch.arange(12) % 2)
generator = torch.Generator().manual_seed(87)
indices = torch.randperm(len(dataset), generator=generator).tolist()
train_ids, valid_ids = indices[:9], indices[9:]
assert set(train_ids).isdisjoint(valid_ids)
assert set(train_ids) | set(valid_ids) == set(range(12))
train_subset = Subset(dataset, train_ids)
valid_subset = Subset(dataset, valid_ids)
train_loader = DataLoader(train_subset, batch_size=4, shuffle=True)
valid_loader = DataLoader(valid_subset, batch_size=4, shuffle=False)
assert len(train_subset) == 9 and len(train_loader) == 3
seen = [int(item) for batch, _ in valid_loader for item in batch]
assert seen == valid_ids
print("sample/batch counts:", len(train_subset), len(train_loader))
len(loader) 是批次数,len(dataset) 是样本数。把批次数当作索引上界生成划分,会漏掉大量样本;对已经划分的 train1 再划验证集时,必须使用 train1 的局部索引空间。
训练函数若约定接收 Dataset,就在函数内统一创建 loader;若接收 loader,就直接遍历,避免二次包装。原题 np.asarray(list(loader)) 会提前读取所有批次,可能产生不规则数组与额外内存占用,应按真实后续需求决定是否保留。
验收 subset 单项、loader 首批和划分覆盖范围,再运行小训练 step。若数据有同一患者或同一序列多条记录,还要按任务分组划分,普通随机样本不重叠未必足以避免语义泄漏。
参考:原始求助帖;技术依据:PyTorch 数据加载(2.5)。