最近在做一个图像分类项目,数据集是自己爬的,大概有10万张图片。我按网上教程写了个自定义Dataset类,里面用了torchvision的transforms做预处理(resize、归一化这些),但训练时每次load数据都特别慢,一个epoch要跑快两个小时。我看别人说用DataLoader的num_workers能加速,但我设了4个worker之后反而报错了,好像是内存炸了。想问下各位大佬,这种自定义数据集的情况,有没有什么常规的优化技巧?比如是不是应该先把图片转成Tensor存起来,还是说transforms里少用点随机操作?或者PyTorch有没有像TF那样可以直接从tfrecord读数据的方式?先谢过大家了。
用PyTorch写自定义Dataset时,数据加载太慢怎么办?
全部回复
共 152 条试试把图片预处理完存成npy格式,加载时直接读npy能快很多,不然每次都resize太费时间了。
10万张图预处理全压在CPU上确实容易崩,建议先把图片预处理成tensor存成pt文件或者h5py格式,这样加载时直接读张量省去每次resize和归一化的开销。num_workers设4报错很可能是内存不够,你可以试试降成2或者用prefetch_factor=2控制预取量。另外transforms里的RandomCrop这类操作确实吃性能,如果模型能接受,能省就省。
10万张图用transforms实时预处理确实扛不住,尤其resize和归一化这种操作每次都要读原图再算一遍。我是先把所有图片统一处理成.pt张量存起来,训练时直接从硬盘load tensor,速度快了不止一个量级。num_workers报错可以试试pin_memory=True或者把worker数量降到2,同时调低prefetch_factor。
可以试试把图片预处理成张量存成pth或npy格式,省得每轮都重新做transforms。
我也碰到过类似的问题,10万张图其实不算太大,关键还是IO和预处理那块容易卡脖子。num_workers设了4个就炸内存,很可能是transforms里有些操作太吃内存了,比如随机裁剪或者旋转这种需要额外缓存的操作,可以先试试把resize和归一化这种确定性操作提前到数据加载之前做掉,比如在爬数据的时候就直接批量处理成统一尺寸的png或者jpg存起来,省得每次训练都重复算。另外你提到存成Tensor,这个确实是个思路,用torch.save或者lmdb把图片转成序列化格式,读的时候直接反序列化会快很多,但要注意硬盘空间和读写次数之间的平衡。至于像tfrecord那样的东西,PyTorch其实有WebDataset或者FFCV这类第三方库,不过学习成本有点高,我感觉你先从减少transforms的随机操作和换SSD硬盘入手,效果应该就很明显了。对了,你DataLoader的pin_memory打开了吗?这个配合num_workers能减少CPU到GPU的传输开销,但也要注意内存占用。
我之前也踩过这个坑,10万张图用默认的DataLoader确实慢得离谱。你num_workers炸内存可能是图片尺寸太大或者worker数太多,可以先试试设2个worker,同时把batch_size调小一点看看。另外transforms里的随机操作尽量少用,尤其是那些需要多次计算的,能提前做归一化就提前做,比如先把图片resize成固定尺寸存成npy或者lmdb格式,读取会快很多。PyTorch自己没tfrecord,但可以用webdataset或者FFmpeg搞成流式读取,内存占用能降不少。
10万张图的话,建议先把图片预处理成张量存成.pt文件,这样加载时就直接读tensor了,能省掉每次resize和归一化的开销。num_workers报内存炸可能是worker数太多或者prefetch_factor没调小,可以试试设成2个worker再把prefetch_factor=2。另外transforms里那些随机操作如果对精度影响不大,可以先做一遍离线增强存下来,训练时就不用在线做了。
可以试试先把图片转成LMDB或HDF5格式存起来,读取速度会快很多,内存问题也能缓解。
可以先试试把图片预处理完存成LMDB或HDF5格式,读取时直接加载张量能快很多。num_workers报错的话调低点或者用shared memory试试。
这个问题我正好也踩过坑。10万张图用PyTorch原生Dataset硬读确实容易卡在IO上,尤其是transforms里有随机裁剪或翻转这类操作,每次都要重新解码图片再变换,CPU直接跑满。我觉得最立竿见影的办法是把预处理后的图片提前缓存成LMDB或HDF5格式,或者干脆直接用torchvision的datasets.ImageFolder配合ramdisk,读内存比读硬盘快十倍不止。另外num_workers设4个就炸,估计是你的transform里用了PIL的多进程不兼容,可以试试把transforms放在worker初始化里做,或者调低batch_size配合pin_memory=True。至于转Tensor存成.pt文件,我试过确实能提速,但要注意磁盘占用,10万张RGB图大概要占几十G。对了,PyTorch官方有个FruitVeggies示例里用了WebDataset,类似tensorflow的tfrecord,支持流式读取,你可以去搜一下那个方案,应该能解决你的痛点。
我之前也踩过这个坑,10万张图用默认方式读确实慢。可以试试先把图片预处理成tensor存成.pt或者h5py格式,训练时直接从文件加载,能省掉大部分I/O和transform的时间。num_workers设4内存炸可能是每个worker都复制了一份transforms的随机状态,改成2或者用prefetch_factor调低预取数量试试。另外torchdata这个库也可以了解一下,它支持类似tfrecord的流式读取,虽然生态还没那么成熟但解决这种场景挺稳的。
10万张图的话,瓶颈很可能在磁盘IO和图片解码上,建议先把图片转为png或者jpeg压缩的tensor存成npy或者h5py格式,读起来快很多。num_workers报内存炸可以试试把prefetch_factor调小,或者把batch_size先降下来看看。另外transforms里那些随机操作比如RandomCrop其实可以放数据加载之后做,用GPU来算会快不少。
你这情况我也踩过坑,10万张图预处理全堆在Dataset里确实扛不住。建议你试试先把图片离线处理成.pt或.npy文件,训练时直接load tensor,能省掉transforms那部分时间。num_workers报内存错误很可能是worker数跟batch size没调好,可以试试把batch size降一点,或者用pin_memory=True配合non_blocking=True传给GPU。另外如果内存实在紧张,可以考虑用lmdb或h5py这种格式存数据,读写效率比散图高很多。
哎这个问题太真实了,我自己也踩过类似的坑。10万张图每个epoch两小时,明显是CPU预处理成了瓶颈。你提到的num_workers炸内存,很可能是每个worker都复制了一份完整的数据集引用,加上transforms里的随机操作临时占了很多内存,建议先从2个worker开始试,同时把batch_size调小一点看看会不会稳定。
另外有个很实用的技巧是把预处理过的图片提前存成.pt或者.npy格式,比如resize和归一化这些确定性操作离线跑一次,训练时直接从硬盘读tensor,这样能省掉大半CPU时间。随机增强(像随机翻转、色彩抖动)可以放到训练时的transforms里,但别太多,否则worker还是会忙不过来。
至于PyTorch有没有类似tfrecord的东西,其实可以用WebDataset或者MosaicML的 StreamingDataset,它们能直接从tar包或远程存储流式读取,对大规模自定义数据集特别友好。不过如果你不想引入新库,最简单的就是先跑个脚本把所有预处理后的图片转成单个.pt文件(比如分shard),然后用torch的TensorDataset加载,亲测能快三四倍。还有个小细节:检查下你的图片是不是都压缩得很小,如果IO本身是瓶颈,可以考虑把图片统一转成JPEG 80%质量并缩小尺寸再存,训练时再解码会快很多。
你这情况我也遇到过,num_workers设太高确实容易爆内存,可以先从2个worker开始试,或者把batch size调小点。另外建议试试把图片预处理后的结果缓存成.pt文件,这样训练时直接加载tensor能快不少。transforms里的随机操作如果影响不大也可以先去掉几个,毕竟10万张图迭代一次太慢了确实影响调参心情。
10万张图用transforms实时处理确实扛不住,建议先把预处理后的tensor存成.pt文件,训练时直接加载能快很多。num_workers报错可能是内存不够,试试把prefetch_factor调小或者用pin_memory=False。另外random resized crop这类操作特别吃CPU,可以换成固定的resize+center crop。至于tfrecord那种格式,PyTorch也有类似方案,比如用webdataset或者自己写个LMDB缓存。
我最近也刚踩过这个坑,10万张图用默认方式读确实扛不住。你的问题核心其实不是transforms本身,而是IO瓶颈——每次训练从硬盘读原始图片再解码,CPU根本忙不过来。把图片预转成Tensor存起来是个好思路,比如用torch.save存成.pt文件,或者转成LMDB、HDF5格式,读的时候直接load tensor能省掉解码和大部分预处理时间。不过要注意磁盘空间,原始图片可能才几百KB,但未压缩的tensor会大好几倍。num_workers报内存炸很可能是每个worker都复制了一份完整的transforms和图片缓存,建议先试试把num_workers降到2,同时把DataLoader的prefetch_factor调小一点(比如2),另外看看是不是用了随机resize/crop这类操作,如果训练时不需要强数据增强,可以先统一resize到固定尺寸再存。PyTorch没有直接对标TFRecord的东西,但可以用WebDataset或者FFmpeg把图片打包成tar或video格式,顺序读取时IO效率会高很多。我自己的做法是把图片提前转成256x256的JPEG再存,同时用albumentations做在线增强,搭配4个worker基本能跑到800张/秒。
可以试试把图片预处理完存成npy或lmdb格式,省得每次训练都重新读图做变换。
10万张图用默认方式硬扛确实慢,我遇到过类似情况。建议先把图片预处理成.pt文件存起来,训练时直接load tensor,能省掉每次resize和归一化的计算。num_workers内存炸可能是prefetch_factor设太高了,试着调小到2或者1,或者把batch_size降一点。另外transforms里的随机操作如果不太影响精度,可以先在预处理阶段固定掉,减少每轮计算量。
我之前也踩过这个坑,十万张图全走transforms确实扛不住。建议先把resize和归一化这些耗时操作一次性做完,存成npy或者jpg的预处理好,训练时Dataset里只做ToTensor,能快不少。另外num_workers报内存炸很可能是你机器物理内存不够,试着降到2或者1,再配合persistent_workers=True试试。至于转成Tensor存,除非你内存特别大,不然10万张图光原始数据就好几个G,反而更容易爆。