pytorch DataLoader ยุคแรกที่ช้ามาก

Aug 30 2020

เมื่อฉันสร้าง PyTorch DataLoader และเริ่มทำซ้ำ - ฉันได้รับยุคแรกที่ช้ามาก (x10 - x30 ช้าลงจากนั้นทุกยุคถัดไป) ยิ่งไปกว่านั้นปัญหานี้เกิดขึ้นเฉพาะกับชุดข้อมูลรถไฟจากการรับรู้จุดสังเกตของ Google ในปี 2020 จาก Kaggle ฉันไม่สามารถทำซ้ำสิ่งนี้ในภาพสังเคราะห์ได้ฉันพยายามสร้างโฟลเดอร์ที่มีภาพ 500k จาก GLR2020 และทุกอย่างก็ทำงานได้ดี พบปัญหาที่คล้ายกันเล็กน้อยในฟอรัม 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')

นี่คือตัวอย่างบางส่วนของความเร็วในการดำเนินการที่แตกต่างกันnum_workersใน 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

ดูเหมือนว่าปัญหาไม่ได้อยู่ที่ DataLoader แต่เป็นชุดข้อมูล เมื่อฉันลบและเริ่มต้นออบเจ็กต์ DataLoader ใหม่หลังจากการทำซ้ำ "นาน" ครั้งแรกทุกอย่างยังคงทำงานได้ดี เมื่อฉันเริ่มต้นชุดข้อมูลใหม่การทำซ้ำครั้งแรกที่ยาวจะปรากฏขึ้นอีกครั้ง ยิ่งไปกว่านั้นฉันติดตามการใช้งาน cpu ของฉันhtopในช่วงยุคนี้ด้วยnum_workersการตั้งค่าเป็น 32 และในช่วงยุคแรกการใช้ประโยชน์ต่ำมาก มีเพียง 1-2 คอร์จาก 32 คอร์เท่านั้นที่ใช้งานได้ในช่วงยุคอื่น ๆ ~ คอร์ทั้งหมดกำลังทำงาน

คำตอบ

10 PoeDator Sep 04 2020 at 01:51

สลาฟกา

ฉันไม่ได้ดาวน์โหลดชุดข้อมูล GLR2020 ทั้งหมด แต่ฉันสามารถสังเกตเห็นเอฟเฟกต์นี้กับชุดข้อมูลรูปภาพที่ฉันมีในเครื่องได้ (ภาพ 80000 jpg ขนาดประมาณ 400x400)

เพื่อค้นหาสาเหตุของความแตกต่างในประสิทธิภาพฉันลองทำดังต่อไปนี้:

  1. ลดการเพิ่มเป็นเพียงการปรับขนาด
  2. ทดสอบImgDataset.__getitem__()ฟังก์ชั่นเพียง
  3. ImgDataset.__getitem__() โดยไม่ต้องเสริม
  4. เพียงแค่โหลดภาพ jpg ดิบและส่งต่อจากชุดข้อมูลโดยไม่ต้องแปลงเป็นตัวเลข

ปรากฎว่าความแตกต่างมาจากระยะเวลาในการโหลดภาพ 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 และเขียนข้อมูลที่กำหนดเองเพื่อป้อนจากที่นั่น ...

BTW จากการค้นพบของฉันเอฟเฟกต์นี้ควรทำซ้ำได้กับชุดข้อมูลใด ๆ - ดูว่าคุณใช้ไดรฟ์อื่นหรือแคชบางตัว

2 Multihunter Sep 10 2020 at 12:26

ดูเหมือนว่า OS กำลังแคชการเข้าถึง 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