pytorch DataLoader kỷ nguyên đầu tiên cực kỳ chậm
Khi tôi tạo PyTorch DataLoader và bắt đầu lặp lại - tôi nhận được kỷ nguyên đầu tiên cực kỳ chậm (x10 - x30 chậm hơn tất cả các kỷ tiếp theo). Hơn nữa, sự cố này chỉ xảy ra với tập dữ liệu về chuyến tàu từ sự công nhận mốc 2020 của Google từ Kaggle. Tôi không thể tái tạo điều này trên các hình ảnh tổng hợp, tôi cũng đã cố tạo một thư mục có 500 nghìn hình ảnh từ GLR2020 và mọi thứ đều hoạt động tốt. Tìm thấy một số vấn đề tương tự trong diễn đàn PyTorch mà không có bất kỳ giải pháp nào.
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')
Dưới đây là một số ví dụ về tốc độ thực thi với các khác nhau num_workerstrong 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
Có vẻ như vấn đề không phải với DataLoader, mà là với tập dữ liệu. Khi tôi xóa và khởi động lại đối tượng DataLoader sau lần lặp "dài" đầu tiên, mọi thứ vẫn hoạt động tốt. Khi tôi khởi động lại tập dữ liệu - lần lặp đầu tiên dài xuất hiện lại. Hơn nữa, tôi đã theo dõi việc sử dụng cpu của mình htoptrong suốt kỷ nguyên này với thiết num_workerslập là 32 và trong kỷ nguyên đầu tiên, việc sử dụng thực sự rất thấp; chỉ 1-2 trong số 32 lõi hoạt động, trong các kỷ nguyên khác ~ tất cả các lõi đều hoạt động.
Trả lời
Slavka,
Tôi đã không tải xuống toàn bộ tập dữ liệu GLR2020 nhưng tôi có thể quan sát hiệu ứng này trên tập dữ liệu hình ảnh mà tôi có cục bộ (80000 hình ảnh jpg có kích thước khoảng 400x400).
Để tìm lý do cho sự khác biệt về hiệu suất, tôi đã thử những cách sau:
- giảm mức tăng để chỉ thay đổi kích thước
- chỉ kiểm tra
ImgDataset.__getitem__()chức năng ImgDataset.__getitem__()không tăng- chỉ cần tải hình ảnh jpg thô và chuyển nó từ tập dữ liệu mà không cần chuyển đổi.
Nó chỉ ra rằng sự khác biệt đến từ thời gian tải hình ảnh. Python (hoặc chính hệ điều hành) thực hiện một số loại bộ nhớ đệm được quan sát thấy khi tải hình ảnh nhiều lần trong thử nghiệm sau.
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
điều tương tự cũng được quan sát khi chỉ đọc từ tệp này sang biến khác
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
Một cách để giảm tốc độ tải là giữ dữ liệu trên SSD cục bộ rất nhanh. Nếu kích thước cho phép, hãy thử tải một phần của tập dữ liệu vào RAM và viết bộ dữ liệu tùy chỉnh để cấp dữ liệu từ đó ...
BTW Dựa trên những phát hiện của tôi, hiệu ứng này có thể được tái tạo với bất kỳ tập dữ liệu nào - hãy xem bạn đã sử dụng các ổ đĩa khác nhau hay một số bộ nhớ đệm.
Có vẻ như hệ điều hành đang lưu IO vào bộ đệm để truy cập vào tập dữ liệu. Để kiểm tra xem đây có chắc chắn là sự cố hay không, hãy thử chạy sync; echo 3 > /proc/sys/vm/drop_caches(trên Ubuntu) sau kỷ nguyên đầu tiên. Nếu kỷ nguyên thứ hai chậm như nhau khi bạn làm điều này, thì chính bộ nhớ đệm đang làm cho các lần đọc tiếp theo nhanh hơn rất nhiều.
Nếu bạn đang sử dụng ổ cứng HDD, thì bạn có thể nhận được những cải tiến đáng kể về tốc độ cho kỷ nguyên đầu tiên của mình bằng cách đồng định vị tất cả các tệp hình ảnh nhỏ của bạn trên đĩa.
Bạn có thể sử dụng SquashFS (nó được cài đặt sẵn với Ubuntu) để nén toàn bộ tập dữ liệu của bạn thành một tệp duy nhất, sau đó gắn tệp đó dưới dạng thư mục và truy cập nó giống như trước đây (ngoại trừ bây giờ các hình ảnh được đặt chung trên đĩa). Thư mục được gắn kết là chỉ đọc.
ví dụ
mksquashfs /path/to/data data.sqsh
mount data.sqsh /path/to/data_sqsh -t squashfs -o loop
Sau đó, bạn có thể sử dụng /path/to/data_sqshchính xác theo cách bạn đã sử dụng /path/to/data. Bạn sẽ phải gắn lại nó khi khởi động lại máy tính của mình
Xem: https://tldp.org/HOWTO/SquashFS-HOWTO/creatingandusing.html