pytorch DataLoader son derece yavaş ilk dönem
Bir PyTorch DataLoader oluşturup yinelemeye başladığımda - son derece yavaş bir ilk dönem alıyorum (x10 - x30 daha sonra sonraki tüm dönemlerden daha yavaş). Dahası, bu sorun yalnızca Kaggle'dan 2020 Google dönüm noktası tanıma sisteminden gelen tren veri kümesinde ortaya çıkıyor. Bunu sentetik görüntülerde yeniden oluşturamıyorum, ayrıca GLR2020'den 500.000 görüntü içeren bir klasör oluşturmaya çalıştım ve her şey yolunda gitti. PyTorch forumunda herhangi bir çözüm olmadan benzer birkaç sorun bulundu.
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')
num_workersDataLoader'da farklı olan yürütme hızına bazı örnekler.
#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
Görünüşe göre sorun DataLoader ile değil, veri kümesiyle ilgili. İlk "uzun" yinelemeden sonra DataLoader nesnesini silip yeniden başlattığımda, her şey hala iyi çalışıyor. Veri kümesini yeniden başlattığımda - uzun ilk yineleme yeniden görünüyor. Üstelik htopbu dönemlerde cpu kullanımımı num_workers32'ye ayarlı olarak takip ettim ve ilk çağda kullanım gerçekten çok düşük; 32 çekirdekten sadece 1-2 tanesi çalışıyor, diğer çağlarda ~ tüm çekirdekler çalışıyor.
Yanıtlar
Slavka,
GLR2020 veri setinin tamamını indirmedim, ancak yerel olarak sahip olduğum görüntü veri setinde (yaklaşık 400x400 boyutunda 80000 jpg resim) bu etkiyi gözlemleyebildim.
Performanstaki farkın nedenlerini bulmak için aşağıdakileri denedim:
- büyütmeyi sadece yeniden boyutlandırmaya indirgemek
- sadece
ImgDataset.__getitem__()işlevi test etmek ImgDataset.__getitem__()büyütme olmadan- sadece ham jpg görüntüsünü yükleyerek ve hatta donuk dönüşüm olmadan veri kümesinden geçirme.
Farkın görüntü yükleme zamanlamasından kaynaklandığı ortaya çıktı. Python (veya işletim sisteminin kendisi), aşağıdaki testte görüntüyü birden çok kez yüklerken gözlenen bir tür önbellekleme uygular.
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
aynısı dosyadan değişkene okurken de gözlemlenir
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
Yükleme hızını azaltmanın bir yolu, verileri çok hızlı yerel SSD'de tutmaktır. Boyut izin veriyorsa, veri kümesinin bir bölümünü RAM'e yüklemeyi ve oradan beslemek için özel veri yükleyici yazmayı deneyin ...
BTW Bulgularıma göre, bu efekt herhangi bir veri kümesiyle yeniden üretilebilir olmalıdır - farklı sürücüler veya bazı önbellekleme kullanıp kullanmadığınıza bakın.
İşletim sisteminin veri kümesine GÇ erişimini önbelleğe aldığı görülüyor. Sorunun kesinlikle bu olup olmadığını kontrol etmek için sync; echo 3 > /proc/sys/vm/drop_caches, ilk dönemden sonra (Ubuntu'da) çalıştırmayı deneyin . Bunu yaptığınızda ikinci dönem eşit derecede yavaşsa, o zaman sonraki okumaları çok daha hızlı yapan önbelleklemedir.
Bir HDD kullanıyorsanız, tüm küçük görüntü dosyalarınızı diskte birlikte konumlandırarak ilk döneminiz için önemli hız iyileştirmeleri elde edebilirsiniz.
SquashFS'yi (Ubuntu ile önceden yüklenmiş olarak gelir) tüm veri kümenizi tek bir dosyaya sıkıştırmak için kullanabilirsiniz, ardından bu dosyayı bir dizin olarak bağlayabilir ve daha önce olduğu gibi ona erişebilirsiniz (artık görüntüler diskte aynı yerde bulunur). Bağlanan dizin salt okunurdur.
Örneğin
mksquashfs /path/to/data data.sqsh
mount data.sqsh /path/to/data_sqsh -t squashfs -o loop
O zaman /path/to/data_sqshtam olarak kullandığınız şekilde kullanabilirsiniz /path/to/data. Bilgisayarınızı yeniden başlattığınızda yeniden takmanız gerekecek
Görmek: https://tldp.org/HOWTO/SquashFS-HOWTO/creatingandusing.html