当我创建一个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,files, 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个在工作,所有内核都在工作。
答案 0 :(得分:9)
斯拉夫卡
我没有下载整个GLR2020数据集,但能够在本地具有的图像数据集(约800x约400x400大小的jpg图像)上观察到这种效果。
要找出性能差异的原因,我尝试了以下操作:
ImgDataset.__getitem__()
函数ImgDataset.__getitem__()
,无需扩充事实证明,差异来自图像加载时间。 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中,然后编写自定义数据加载器以从那里进行馈送...
顺便说一句,根据我的发现,这种影响在任何数据集上都可以重现-查看是否使用了不同的驱动器或缓存。
答案 1 :(得分:2)
看来,操作系统正在缓存对数据集的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