微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

pytorch DataLoader的第一个时期非常慢

如何解决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图像)上观察到这种效果。

要找出性能差异的原因,我尝试了以下操作:

  1. 将扩充缩小为调整大小
  2. 仅测试ImgDataset.__getitem__()函数
  3. ImgDataset.__getitem__(),无需扩充
  4. 仅加载原始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 举报,一经查实,本站将立刻删除。