pytorch DataLoader 매우 느린 첫 번째 시대

Aug 30 2020

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

다음은 num_workersDataLoader에서 다른 실행 속도의 몇 가지 예입니다.

#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 개체를 삭제하고 다시 초기화해도 모든 것이 여전히 잘 작동합니다. 데이터 세트를 다시 초기화하면 긴 첫 번째 반복이 다시 나타납니다. 또한, 32 htopnum_workers설정된 이 Epoch 동안을 통해 내 CPU 사용률을 추적 했으며 첫 번째 Epoch 동안에는 사용률이 매우 낮습니다. 32 개 코어 중 1-2 개만 작동하고 다른 시대에는 모든 코어가 작동합니다.

답변

10 PoeDator Sep 04 2020 at 01:51

슬라브 카,

전체 GLR2020 데이터 세트를 다운로드하지는 않았지만 로컬에있는 이미지 데이터 세트 (약 400x400 크기의 80000 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에로드하고 거기에서 피드 할 사용자 지정 데이터 로더를 작성해보십시오.

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