如何解决pytorch DataLoader的第一个时期非常慢
当我创建一个PyTorch DataLoader并开始进行迭代时,我得到了一个非常慢的第一个时期(x10--x30慢于所有下一个时期)。此外,仅使用来自kaggle的Google地标识别2020的火车数据集会出现此问题。我也无法在合成图像上重现此图像,而且,我尝试从glr2020创建包含50万张图像的文件夹,并且一切正常。没有任何解决方案,在PyTorch论坛中发现了一些类似的问题。
import argparse
import pandas as pd
import numpy as np
import os,sys
import multiprocessing,ray
import time
import cv2
import logging
import albumentations as albu
from torch.utils.data import Dataset,DataLoader
samples = 50000 # count of samples to speed up test
bs = 64 # batch size
dir = '/hdd0/datasets/ggl_landmark_recognition_2020/train' # directory with train data
all_files = pd.read_csv('/hdd0/datasets/ggl_landmark_recognition_2020/train.csv')
files = np.random.choice(all_files.id.values,50000)
files = [os.path.join(_[0],_[1],_[2],_+'.jpg') for _ in files]
# augmentations
aug = albu.Compose([albu.Resize(400,400),albu.Rotate(limit=15),albu.ChannelDropout(p=0.1),albu.normalize(),])
class ImgDataset:
def __init__(self,path,files,augmentation = None):
self.path = path
self.files = {k:v for k,v in enumerate(files)}
self.augmentation = augmentation
def __len__(self):
return len(self.files)
def __getitem__(self,idx):
img_name = self.files[idx]
img = np.array(cv2.imread(os.path.join(self.path,img_name)))
if self.augmentation is not None:
return self.augmentation(image=img)['image']
dtset = ImgDataset(dir,aug)
torchloader = DataLoader(dataset= dtset,batch_size=64,num_worker=16,shuffle=True)
for _ in range(3):
t1 = time.time()
for idx,val in enumerate(torchloader):
pass
t2 = time.time()
print(str(t2-t1) +' sec')
以下是在DataLoader中使用不同num_workers
的执行速度的一些示例
#num_workers=0
273.1584792137146 sec
83.15653467178345 sec
83.67923021316528 sec
# num_workers = 8
165.62366938591003 sec
10.405716896057129 sec
10.495309114456177 sec
# num_workers = 16
156.60744667053223 sec
8.051618099212646 sec
7.922858238220215 sec
问题似乎不在于DataLoader,而在于数据集。在第一次“长时间”迭代后删除并重新初始化DataLoader对象时,一切仍然正常。当我重新初始化数据集时-长时间的第一次迭代再次出现。
此外,我在htop
设置为32的这个时期内通过num_workers
跟踪了我的cpu利用率,在第一个时期,利用率确实很低。在其他时期〜32个内核中只有1-2个在工作,所有内核都在工作。
解决方法
斯拉夫卡
我没有下载整个GLR2020数据集,但能够在本地具有的图像数据集(约800x约400x400大小的jpg图像)上观察到这种效果。
要找出性能差异的原因,我尝试了以下操作:
- 将扩充缩小为调整大小
- 仅测试
ImgDataset.__getitem__()
函数 -
ImgDataset.__getitem__()
,无需扩充 - 仅加载原始jpg图像并将其从数据集中传递,甚至不进行numpy转换。
事实证明,差异来自图像加载时间。 Python(或OS本身)实现了某种缓存,在以下测试中多次加载图像时会观察到这种缓存。
for i in range(5):
t0 = time.time()
data = cv2.imread(filename)
print (time.time() - t0)
0.03395271301269531
0.0010004043579101562
0.0010004043579101562
0.0010008811950683594
0.001001119613647461
从文件读取到变量时会观察到相同的情况
for i in range(5):
t0 = time.time()
with open(filename,mode='rb') as file:
data = file.read()
print (time.time() - t0)
0.036234378814697266
0.0028831958770751953
0.0020024776458740234
0.0031833648681640625
0.0028734207153320312
降低加载速度的一种方法是将数据保留在非常快的本地SSD上。如果大小允许,请尝试将部分数据集加载到RAM中,然后编写自定义数据加载器以从那里进行馈送...
顺便说一句,根据我的发现,这种影响在任何数据集上都可以重现-查看是否使用了不同的驱动器或缓存。
,看来,操作系统正在缓存对数据集的IO访问。要检查是否绝对是问题所在,请尝试在第一个时期之后运行sync; echo 3 > /proc/sys/vm/drop_caches
(在Ubuntu上)。如果执行此操作时第二个纪元同样慢,则是缓存使后续读取变得更快。
如果您使用的是HDD,则可以通过将所有小映像文件同时放置在磁盘上,从而在第一时间获得显着的速度改进。
您可以使用SquashFS(Ubuntu预装)将整个数据集压缩为单个文件,然后将该文件作为目录挂载并像以前一样访问它(现在将映像放置在同一位置)磁盘)。挂载的目录是只读的。
例如
mksquashfs /path/to/data data.sqsh
mount data.sqsh /path/to/data_sqsh -t squashfs -o loop
然后,您可以像使用/path/to/data_sqsh
一样使用/path/to/data
。重新启动计算机后,您将不得不重新安装它
请参阅:https://tldp.org/HOWTO/SquashFS-HOWTO/creatingandusing.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。