부스트캠프 AI Tech 4기

Datasets과 Dataloaders

쉬엄쉬엄블로그 2023. 5. 18. 18:08
728x90

모델에 데이터를 먹이는 방법

  • _getitem_
    • 하나의 데이터를 불러올 때 어떻게 반환을 해주는지에 대해서 정의해줌
  • transforms
    • 데이터 전처리를 정의

Dataset 클래스

  • 데이터 입력 형태를 정의하는 클래스
  • 데이터를 입력하는 방식의 표준화
  • Image, Text, Audio 등에 따라 다른 입력 정의

Dataset 클래스의 스타일 2가지

Map-style

  • index가 존재하여 data[index]로 데이터를 참조할 수 있음
  • _getitem__과 _len 선언 필요
import torch
from torch.utils.data import Dataset, DataLoader

class CustomDataset(Dataset):
    # 초기 데이터 생성 방법 지정
    def __init__(self, text, labels):
        self.labels = labels
        self.data = text
    # 데이터의 전체 길이 반환
    def __len__(self):
        return len(self.labels)
    # index 값을 주었을 때 반환되는 데이터 형태 (X, y)
    def __getitem__(self, idx):
        label = self.labels[idx]
        text = self.data[idx]
        sample = {"Text": text, "Class": label}
        return sample

# 사용 예시
text = ['Happy', 'Amazing', 'Sad', 'Unhappy', 'Glum']
labels = ['Positive', 'Positive', 'Negative', 'Negative', 'Negative']
MyDataset = CustomDataset(text, labels)

MyDataLoader = DataLoader(MyDataset, batch_size=2, shuffle=True)
next(iter(MyDataLoader))
'''
{'Text': ['Glum', 'Amazing'], 'Class': ['Negative', 'Positive']}
'''

MyDataLoader = DataLoader(MyDataset, batch_size=3, shuffle=True)
for dataset in MyDataLoader:
    print(dataset)
'''
{'Text': ['Unhappy', 'Amazing', 'Glum'], 'Class': ['Negative', 'Positive', 'Negative']}
{'Text': ['Happy', 'Sad'], 'Class': ['Positive', 'Negative']}
'''

Iterable-style

  • random으로 읽기에 어렵거나, data에 따라 batch size가 달라지는 데이터(dynamic batch size)에 적합
  • 비교하자면 stream data, real-time log 등에 적합
  • _iter_ 선언 필요
import torch
from torch.utils.data import IterableDataset, DataLoader

class CustomDataset(IterableDataset):
    def __init__(self,data):
        self.data = data

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

# 사용 예시
data = [1, 2, 3, 4, 5]
MyDataset = CustomDataset(data)
dataset = DataLoader(MyDataset, batch_size=2)

# 데이터셋 순회
for item in dataset:
    # 아이템 처리
    # ...
    print(item)

'''
tensor([1, 2])
tensor([3, 4])
tensor([5])
'''

Dataset 클래스 생성시 유의점

  • 데이터 형태에 따라 각 함수를 다르게 정의함
  • 모든 것을 데이터 생성 시점에 처리할 필요는 없음
    • image의 Tensor 변화는 학습에 필요한 시점에 변환
  • 최근에는 HuggingFace 등 표준화된 라이브러리 사용

DataLoader 클래스

  • Data의 Batch를 생성해주는 클래스
  • 학습직전(GPU feed전) 데이터의 변환을 책임
  • Tensor로 변환 + Batch 처리가 메인 업무
  • 병렬적인 데이터 전처리 코드의 고민 필요
DataLoader(dataset,            # Dataset 인스턴스
           batch_size=1,       # 배치 사이즈 설정
           shuffle=False,      # 데이터를 섞을지 설정
           sampler=None,       # sampler는 index를 컨트롤
           batch_sampler=None, # 위와 비슷하므로 생략
           num_workers=0,      # 데이터를 불러올 때 사용하는 서브 프로세스 개수
           collate_fn=None,    # map-style 데이터셋에서 sample list를 batch 단위로 바꾸기 위해 필요한 기능
           pin_memory=False,   # Tensor를 CUDA 고정 메모리에 할당
           drop_last=False,    # 마지막 batch 사용 여부
           timeout=0,          # data load 제한시간
           worker_init_fn=None # 어떤 worker를 불러올 것인가를 리스트로 전달
          )
  • collate_fn 예시

    def collate_fn(batch):
      # 배치에서 샘플의 리스트를 가져온다.
      texts = [item["Text"] for item in batch]
    
      # 배치에서 레이블의 리스트를 가져온다.
      labels = [item["Class"] for item in batch]
    
      # 필요한 경우 데이터 전처리 또는 변환 작업을 수행한다.
      # 예를 들어, 샘플 텍스트에 !를 추가할 수 있다.
      texts = [f'{text}!' for text in texts]
    
      return texts, labels
  • collate_fn 적용 예시

    MyDataLoader = DataLoader(MyDataset, batch_size=3, collate_fn=collate_fn)
    for dataset in MyDataLoader:
      texts, labels = dataset
      print(texts, labels)
    '''
    ['Happy!', 'Amazing!', 'Sad!'] ['Positive', 'Positive', 'Negative']
    ['Unhappy!', 'Glum!'] ['Negative', 'Negative']
    '''
    MyDataLoader = DataLoader(MyDataset, batch_size=3, shuffle=True, collate_fn=collate_fn)
    for dataset in MyDataLoader:
      texts, labels = dataset
      print(texts, labels)
    '''
    ['Sad!', 'Unhappy!', 'Happy!'] ['Negative', 'Negative', 'Positive']
    ['Amazing!', 'Glum!'] ['Positive', 'Negative']
    '''