시작은 미약하였으나 , 그 끝은 창대하리라

[Pytorch 스크래치 코드] Custom Dataset, DataLoader 본문

인공지능/딥러닝 스크래치 코드

[Pytorch 스크래치 코드] Custom Dataset, DataLoader

애플파ol 2023. 11. 26. 18:16

➢ Pytorch 에서 dataset을 쉽게 다룰 수 있도록 모듈을 제공하고 있다.

아래와 같이 두가지 Step으로 구성 된다.  

 

❏  (Step1) CustomDataset 뼈대  - 데이터셋 상속

from torch.utils.data import Dataset

# 데이터셋 상속
class CustomDataset_name(Dataset):
	
    #데이터 전처리 과정 작성.
    def __init__(self):
    	pass
    
    # 데이터의 길이 (총 샘플수) 작성.
    def __len__(self):
    	pass
    
    # 전처리 된 데이터셋을 인덱스(idx)에 맞게, Pytorch Tensor 형태로 반환 함. 
    def __getitem__(self,idx):
        pass

 

❏ (Step2)   DataLoader 뼈대

중요 파라미터 설명:

 

  • shuffle : 매번의 에포크 마다 배치 순서를 섞을지 여부. ('배치 내부의 구성물들을 바꾸는것이 아님(seed가 고정되어 있다는 전제하에')
    좀더 자세히 설명하자면,
    shuffle=False 일때는 batch_1, batch_2, batch_3, ..., batch_100 이렇게 각각의 배치들이 매번의 에폭마다  섞이지 않고 학습에 사용된다.
    반면 shuffle=True 일때는 batch50, batch23, batch1, ..., batch_75 이렇게 무작위로 들어가게 된다.
    왜 사용할까? 모델이 batch 간의 순서도 학습할 수 있다. 예를 들어 시계열 데이터를 학습한다고 하자. 시계열 데이터에서는 시간에 따른 순서가 상당히 중요한데 shuffle=True로 한다면 시간적 의미가 다 파괴될것이다. 그러므로 일반적으로 shuffle=False로 한다. 반면 이미지 분류 문제를 수행하고 있다면 shuffle=False로 하는것이 일반적이다. 그래야만 다양한 데이터에 대해 편향이 생기지 않고 처리가 가능하기 때문이다. (시계열 데이터의 반대 이유라고 생각하면 이해가 편할것이다)
  • drop_last : 데이터를 배치크기로 나누었을 때 나머지 데이터의 사용 유무.
    예를 들어 데이터의 개수가 1280개가 있다면 batch_size=64로 한다면 1280/64 의 나머지는 0 임으로 문제가 없지만,
    데이터의 개수가 1200개 이고 batch_size=64 이면 1200/64의 나머지는 48 이다. 나머지 48개의 데이터셋을 모델의 학습에 사용 유무를 선택하겠다는 것이다. 
    왜 사용할까? 나머지의 데이터가 loss값에 영향을 상당히 미칠 수 도 있다(좋던, 안좋던). 시계열 데이터라면 Flase로 해서 하는것이 명확한 방법일 것이며, 데이터가 상당히 많다면 True를 해도 상관없을듯 하다. 어떤 문제를 푸느냐에 따라 선택해야 한다.
  • batch_size : 하나의 소그룹에 속하는 데이터의 개수. 즉, mini batch 내부의 데이터 개수를 말하는 것.
from torch.utils.data import DataLoader

dataset = CustomDataset_name()
dataloader = DataLoader(dataset, batch_size=64, shuffle=True, drop_last=False)

 

❏  CustomDataset 예시(1)

  • 이해를 돕기 위한 가장 기본적인 예시임
# Dataset 상속
class CustomDataset(Dataset): 
  def __init__(self):
    self.x_data = [[1, 58, 24],
                   [31, 90, 73],
                   [49, 61, 40],
                   [57, 79, 57],
                   [63, 76, 20]]
    self.y_data = [[862], [234], [230], [427], [624]]

  # 데이터의 길이 (총 샘플수) 작성.
  def __len__(self): 
    return len(self.x_data)

  # 전처리 된 데이터셋을 인덱스(idx)에 맞게, Pytorch Tensor 형태로 반환 함.
  def __getitem__(self, idx): 
    x = torch.FloatTensor(self.x_data[idx])
    y = torch.FloatTensor(self.y_data[idx])
    return x, y
    
    
# 참고: https://wikidocs.net/57165
dataset = CustomDataset()
dataloader = DataLoader(dataset, batch_size=2, shuffle=True, drop_last=False)

❏  CustomDataset 예시(2)

  • 실제로 머신러닝을 돌리다보면 본인이 수집한 데이터들을 load해서 전처리를 수행한다. 
  • 데이터를 다른 폴더에서 불러와서 sliding_window로 전처리를 수행한 코드이다.  
from torch.utils.data import Dataset,DataLoader
import torch
import os
import pandas as pd
import numpy as np

class CustomDataset(Dataset):
    
    # 데이터 전처리
    def __init__(self, window_size,Folder_dir):
        self.data_list=[]
        swindow_size=window_size
        Folder_path=Folder_dir

        for filename in os.listdir(Folder_path):

            data=pd.read_csv(Folder_path+filename)
            data=data.to_numpy()



            features=torch.FloatTensor(data[:,0:99])
            labels=torch.FloatTensor(data[:,99])
            labels=labels.reshape(len(labels),-1)
            
            
            for i in range(len(features)-window_size):
                features_subset=features[i:i+window_size]
                labels_subset=labels[i]
                
                # print(features_subset.shape)
                # print(labels_subset.shape)
                self.data_list.append([features_subset,labels_subset])

    # 데이터 길이
    def __len__(self):

        return len(self.data_list)


    # 데이터 셋을 전처리 후 반환하는 함수
    def __getitem__(self, idx):
        
        x,y=self.data_list[idx]

        x = torch.FloatTensor(x)
        y = torch.FloatTensor(y)

        return x, y
##
##   데이터셋 생성
##

from main_files.CustomDataset import CustomDataset  # main_files 폴더에서 CustomDataset class 호출하여 사용.
from torch.utils.data import Dataset,DataLoader ,random_split

dataset=CustomDataset(window_size=30, Folder_dir='./main_data/')

# train,test 분리
val_ratio=0.2
val_size=int(val_ratio*len(dataset))

train_size=len(dataset)-val_size
train_dataset, val_dataset=random_split(dataset,[train_size,val_size])


train_dataloader=DataLoader(train_dataset,batch_size=64,shuffle=False,drop_last=True)  # shuffle: 미니배치들이 에폭마다 섞이는 유무.
val_dataloader=DataLoader(val_dataset,batch_size=64,shuffle=False,drop_last=True)  # shuffle: 미니배치들이 에폭마다 섞이는 유무.

print("Dataset size:",len(dataset),'\n')
print("Traing data size:",len(train_dataset))
print("Traing data size (.dataset):",len(train_dataloader.dataset))
print("Validation data size:",len(val_dataset),'\n')   
print("Traing data # of batch:",len(train_dataloader))

print("Validation # of batch:",len(val_dataloader))

 

❏  CustomDataset 예시(3)

만약 각각 train_x, train_y 를 불러와서 붙여야 한다면?...

TensorDatset활용 : PyTorch에서 제공하는 데이터셋 클래스 중 하나로, 여러개의 텐서들을 간단하게 묶어서 사용할 수 있도록 도와주는 클래스입니다. 이 클래스를 사용하면 훈련 데이터와 타깃 데이터를 각각의 텐서로 구성하여 편리하게 다룰 수 있습니다.

from torch.utils.data import DataLoader, TensorDataset

train_dataset=TensorDataset(train_x, train_y)

train_dataloader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=4)

 

❏  기타 팁

  • 필자는 CustomDataset을 따로 .py 파일로 다른 파일에 만들어 놓고,
    train.py 또는 main.py파일을 만들어서 class형태로 호출하여 model을 학습시킨다. 
    경험적으로 이러한 방식이 코드가 깔끔하고, 가독성도 높으며, 수정하기 편한것 같다. 
Comments