플로라도의 data workout

파이토치(PyTorch) 데이터로더(DataLoader)의 모든 것 본문

기초 노트/PyTorch

파이토치(PyTorch) 데이터로더(DataLoader)의 모든 것

플로라도 2024. 6. 15. 09:15

(거의) 모든 것

 
파이토치의 DataLoader 클래스는 데이터셋을 다루기 위한 강력한 도구로, 모델 학습을 위한 배치(batch)를 생성하고 데이터를 불러오는(load) 작업을 관리하는 기능을 한다.
각각의 인자(arugment)들은 데이터로더의 설정을 돕게 된다.
 

DataLoader

- Dataset에 대하여 data를 로드하는 iterative한 객체
- 아래와 같은 argument를 받음

import torch
from torch.utils.data import Dataset, DataLoader, SequentialSampler, RandomSampler, SubsetRandomSampler, BatchSampler



DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
           batch_sampler = None, num_workers=0, collate_fn= None,
           pin_memory=False, drop_last = False, timeout=0, 
           work_init_fn = None, *, prefetch_factor=2, 
           persistent_workers=False))

 

dataset

 
- Dataloader는 dataset 객체를 필수적으로 받으며, dataset은 데이터를 제공하는 역할을 한다. 파이토치 데이터셋에는 매직메서드인 '__getitem__' 메서드가 정의되어 있어 인덱스를 통해 데이터를 가져올 수 있게 된다.
 
- 타입 : torch.utils.data.Dataset
 
 

batch_size

 
- 한번에 불러올 배치의 크기로, 대게 전역변수 config로 별도로 관리하여, 2의 배수 값으로(1,2,4,8,16...128,192,256,512 등) 설정하여 사용하는 것이 일반적이다.

- 타입 : (int, optional)
- default : 1
 

shuffle

 
- 학습 데이터의 인덱스 배열과 상관없이 데이터를 무작위로 섞을 수 있는 옵션을 제공하는 인자이다.
trrain에서는 shuffle=True로, validation 및 evaluation에서는 shuffle=False를 사용하는 것이 일반적이나 특수한 목적으로 훈련데이터의 학습 순서를 고려할때는 shuffle이 아닌 sampler 옵션을 사용해야 한다.

- 타입 : (bool, optional)
- defualt : False
 

sampler

 
- 데이터를 샘플링할때 사용하는 인자로 'torch.utils.data.Sampler' 클래스를 받으며, batch_size, shuffle과는 함께 사용할 수 없다.
- Iterable을 인자로 삼고싶을 경우, Iterable은 ('__len__')이 정의되어 있어야한다.

- 타입 : ('torch.utils.data.Sampler' or Iterable, optional)
- default : None
 

from torch.utils.data import DataLoader, Dataset, Sampler

# 간단한 데이터셋 정의
class SimpleDataset(Dataset):
    def __init__(self, data):
        self.data = data

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

# 사용자 정의 샘플러 정의
class CustomSampler(Sampler):
    def __init__(self, data_source, indices):
        self.data_source = data_source
        self.indices = indices

    def __iter__(self):
        return iter(self.indices)

    def __len__(self):
        return len(self.indices)

# 데이터셋 생성
data = [i for i in range(100)]
dataset = SimpleDataset(data)

# 특정 순서의 인덱스 정의
custom_indices = [99, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # 예시로 첫 번째 배치가 마지막 인덱스를 포함하도록 설정

# CustomSampler를 사용하는 DataLoader 생성
custom_sampler = CustomSampler(dataset, custom_indices)
dataloader = DataLoader(dataset, sampler=custom_sampler, batch_size=10)

# 데이터 로딩 예시
for batch in dataloader:
    print(batch)

 
만약 object detection과 같은 task에서 큰 객체에서 작은 객체로 학습 순서를 고려한다던지,
text recognition task에서 한글자부터 학습하고 점차 여러 글자로된 단어를 학습 한다던지 등
학습에 있어 순서를 정하고 싶은 경우
train_loader이더라도, 원하는 학습 순서의 dataset index배열을 취하고 shuffle=False하는 방법도 있지만
sampler를 통해 이러한 학습 전략을 취할 수 있다.
 
 

num_workers

 
오픈소스 혹은 pretraiend 모델을 활용하다보면 에러의 주범이 되는 인자중 하나이다.
num_workers는 dataset의 데이터를 gpu로 전송할 때 필요한 전처리를 수행할 때 사용하는 subprocess의 수를 말한다.
num_workers의 수를 늘리면 cpu에서 gpu로 전송하는 병렬처리가 더 빨라져 성능이 좋아지지만, 너무 많게 될 경우 오히려 다른 부분에서 사용해야할 리소스가 줄어들어 성능이 줄어들 수 있어 적절한 숫자를 선택해야 한다.
 
-타입 : ('int', optional)
-기본값 : 0
-CPU 코어수에 따라 성능에 영향을 받는다.
-num_workers의 수에 따라 메모리 사용량이 증가할 수 있다. 각 워커가 별도의 메모리 공간을 차지함
-disk I/O에 따라 num_workers의 효과가 상이할 수 있다, SSD와 같은 디스크를 사용할 경우 더 큰 성능 향상 기대 가능
 

collate_fn

 
-타입 : ('callable' ;: 함수나 __call__을가져 호출가능한 클래스,메서드,인스턴스 객체, optional)
-기본값 : None

- collate_fn은 기본적으로는 지정하지 않아도 되는 파라미터지만, DataLoader가 배치(Batch)를 특정한 처리를 하여 결합하고자 할때 사용하는 함수이다. 통상 배치 내의 input sequence의 길이가 다르면 이를 동일하게 맞추어 주기 위하여, padding 기능을 추가하는 collate_fn을 많이 사용한다.
 

import torch
from torch.nn.utils.rnn import pad_sequence

def collate_fn(batch):
    sequences = [torch.tensor(item[0]) for item in batch]
    labels = torch.tensor([item[1] for item in batch])
    padded_sequences = pad_sequence(sequences, batch_first=True, padding_value=0)
    return padded_sequences, labels

 

pin_memory

 
-타입 : ('bool', optional)
-default : False

-pin_memory=True를 설정하면, 데이터를 CPU에서 GPU로 전송할 때 좀 더 빠른 속도로 가능하게 한다고 한다.
자세한 이유와 원리가 궁금한데,..  아직 파악을 하지 못하였다.
 

drop_last

 
-타입 : ('bool', optional)
-default : False

-drop_last=True로 설정하면, 데이터셋의 크기가 배치 크기로 나누어 떨어지지 않는 경우, 마지막 배치 단위를 버리는 옵션이다. 예를 들어 전체 데이터 샘플의 수가 '100'이고, 배치 사이즈가 '24'일 경우, 100%24 = 4임으로, 마지막 배치는 데이터 샘플이 4개가 되어 이를 배치 단위에 제외하는 옵션이다
 
 

timeout

 
-타입 : ('int' 또는 'float', optional)
-default : 0

-데이터 로딩 시 각 워커(worker)가 데이터를 반환하는데 걸리는 최대 시간을 설정한다. 지정된 시간 내에 데이터가 반환되지 않으면 'TimeoutError'를 발생시킨다.
 
 

worker_init_fn

 
-타입 : ('callable', optional)
-default : None

-각 워커 프로세스가 시작될 때 실행될 초기화 함수를 지정할 수 있는 옵션이다. 주로 랜덤 시드 설정 등의 워커 초기화에 사용된다고 한다.
 

def worker_init_fn(worker_id):
    np.random.seed(np.random.get_state()[1][0] + worker_id)

dataloader = DataLoader(dataset, batch_size=4, worker_init_fn=worker_init_fn)

 

prefetch_factor

 
-타입 : ('int', optional)
-default : 2

-각 워커가 배치를 미리 로드하는 수를 지정한다. 디폴트값은 2로, 각 워커가 두 배치를 미리 로드하게 된다.
 

 

persistent_workers

 
-타입 : ('bool', optional)
-default : False

-persistent_workers =True 로 설정하면, 데이터 로더의 모든 배치를 처리한 후에도 워커가 종료되지 않고 유지된다. 이를 통해 반복적인 데이터 로딩에서의 오버헤드를 줄일 수 있다고 한다.