pytorch DataLoader epoch pertama sangat lambat

Aug 30 2020

Ketika saya membuat PyTorch DataLoader dan mulai mengulang - saya mendapatkan epoch pertama yang sangat lambat (x10 - x30 lebih lambat dari semua epoch berikutnya). Selain itu, masalah ini hanya terjadi dengan kumpulan data kereta dari Google landmark recognition 2020 dari Kaggle. Saya tidak dapat mereproduksi ini pada gambar sintetis, juga, saya mencoba membuat folder dengan gambar 500k dari GLR2020, dan semuanya bekerja dengan baik. Menemukan beberapa masalah serupa di forum PyTorch tanpa solusi apa pun.

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')

Berikut beberapa contoh kecepatan eksekusi yang berbeda num_workersdi DataLoader

#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

Sepertinya masalahnya bukan pada DataLoader, tapi pada dataset. Ketika saya menghapus dan menginisialisasi ulang objek DataLoader setelah iterasi "lama" pertama, semuanya masih berfungsi dengan baik. Ketika saya menginisialisasi ulang dataset - iterasi pertama yang panjang muncul lagi. Selain itu, saya melacak penggunaan cpu saya melalui htopselama periode ini dengan num_workersditetapkan ke 32, dan selama periode pertama, penggunaan sangat rendah; hanya 1-2 dari 32 core yang berfungsi, selama periode lain ~ semua core berfungsi.

Jawaban

10 PoeDator Sep 04 2020 at 01:51

Slavka,

Saya tidak mengunduh seluruh kumpulan data GLR2020 tetapi saya dapat mengamati efek ini pada kumpulan data gambar yang saya miliki secara lokal (gambar 80000 jpg dengan ukuran kira-kira 400x400).

Untuk menemukan alasan perbedaan kinerja, saya mencoba yang berikut ini:

  1. mengurangi augmentasi menjadi hanya mengubah ukuran
  2. menguji ImgDataset.__getitem__()fungsi saja
  3. ImgDataset.__getitem__() tanpa augmentasi
  4. hanya memuat gambar jpg mentah dan meneruskannya dari kumpulan data tanpa konversi numpy.

Perbedaannya ternyata terletak pada waktu pemuatan gambar. Python (atau OS itu sendiri) mengimplementasikan beberapa jenis caching yang diamati saat memuat gambar beberapa kali dalam pengujian berikut.

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

hal yang sama diamati ketika hanya membaca dari file ke variabel

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

Salah satu cara untuk mengurangi kecepatan pemuatan adalah dengan menyimpan data di SSD lokal yang sangat cepat. Jika ukuran memungkinkan, coba muat bagian dari kumpulan data ke dalam RAM dan tulis dataloader khusus untuk memberi makan dari sana ...

BTW Berdasarkan temuan saya, efek ini harus dapat direproduksi dengan set data apa pun - lihat apakah Anda menggunakan drive yang berbeda atau beberapa caching.

2 Multihunter Sep 10 2020 at 12:26

Tampaknya OS sedang menyimpan akses IO ke dataset. Untuk memeriksa apakah ini masalahnya, coba jalankan sync; echo 3 > /proc/sys/vm/drop_caches(di Ubuntu) setelah epoch pertama. Jika epoch kedua sama lambatnya saat Anda melakukan ini, maka cachinglah yang membuat pembacaan berikutnya jauh lebih cepat.

Jika Anda menggunakan HDD, Anda mungkin mendapatkan peningkatan kecepatan yang signifikan untuk masa pertama Anda dengan menempatkan semua file gambar kecil Anda di disk.

Anda dapat menggunakan SquashFS (sudah diinstal sebelumnya dengan Ubuntu) untuk memampatkan seluruh dataset Anda ke dalam satu file, kemudian me-mount file itu sebagai direktori dan mengaksesnya seperti sebelumnya (kecuali sekarang gambar-gambar tersebut ditempatkan bersama di disk). Direktori yang dipasang adalah hanya-baca.

misalnya

mksquashfs /path/to/data data.sqsh
mount data.sqsh /path/to/data_sqsh -t squashfs -o loop

Kemudian Anda dapat menggunakannya /path/to/data_sqshdengan cara yang persis sama dengan yang Anda gunakan /path/to/data. Anda harus memasangnya kembali saat Anda memulai ulang komputer

Lihat: https://tldp.org/HOWTO/SquashFS-HOWTO/creatingandusing.html