본문 바로가기

Programming/(Python)(Ubuntu)

PyTorch Dataset Class, transform 설명

PyTorch는 데이터를 불러오는 과정을 쉽게해주고, 또 잘 사용한다면 코드의 가독성도 보다 높여줄 수 있는 도구들을 제공합니다.

 

  • scikit-image : 이미지 Input / Output 와 변형을 위해 필요합니다.
  • pandas : CSV 파일 피싱을 보다 쉽게 해줍니다.

 

Dataset Class

 

torch.utils.data.Dataset 은 데이터셋을 나타내는 추상클래스입니다. 데이터넷은 Dataset에 상속하고 아래와 같이 오버라이드해야합니다.

  • len(dataset) 에서 호출되는 __len__ 은 데이터셋의 크기를 리턴해야합니다.
  • dataset[i] 에서 호출되는 __getitem__ 은 i 번째 샘플을 찾는데 사용됩니다.

 

데이터셋 클래스를 만들어보도록 하겠습니다. __init__을 사용해서 CSV 파일 안에 있는 데이터를 읽지만, __getitem__을 이용해서 이미지를 판독합니다. 이 방법은 모든 이미지를 메모리에 저장하지 않고 필요할때마다 읽기 때문에 메모리를 효율적으로 사용합니다.

 

데이터셋의 샘플은 { 'image' : image, 'label' : label } 의 사전 형태를 갖습니다. 선택적 인자인 transform 을 통해 필요한 전처리 과정을 샘플에 적용할 수 있습니다. 

 

''' mnistDataset의 예제를 통한 Dataset 클래스 구현방법'''

import torch
from torch.utils.data import Dataset, DataLoader
import pandas as pd

class mnistDataset(Dataset):
	"""mnist dataset."""
    
    def __init__(self, file_path, transform=None):
     """
     
     csv파일 데이터 및 라벨일 경우
        file_path = 경로 + 파일
        transform (callable, optional): 샘플에 적용될 Optional transform (전처리)
     이미지일 경우 추가
     	csv_file (string): csv 파일의 경로
     	root_dir (string): 모든 이미지가 존재하는 디렉토리 경로
        """
        self.data = [] # 데이터를 정제해 담을 행렬 생성
        df_mnist = pd.read_csv(file_path) # pandas를 이용한 csv 파일 읽기
        for idx in df_mnist.index: 
       		temp_target = df_mnist.values[idx][0] # 타겟 행 받아오기
            temp_input = df_mnist.values[idx][1:] # 행렬 행 받아오기
            self.data.append((temp_input, temp_target)) # 타겟 / 행렬 구분된 데이터를 data에 첨부 
        self.transform = transform # 데이터 받아오기 이후 데이터 전처리
            
    def __len__(self):
        return len(self.data)
    
    def __getitem__(self, idx):
        x, y = self.data[idx]
        sample = {"x" : x, "y" : int(y)}
               
        '''
        이미지일 경우 이미지 파일 읽어오기 추가
        if torch.is_tensor(idx):
            idx = idx.tolist()

        img_name = os.path.join(self.root_dir,
                                self.landmarks_frame.iloc[idx, 0])
        image = io.imread(img_name)
        landmarks = self.landmarks_frame.iloc[idx, 1:]
        landmarks = np.array([landmarks])
        landmarks = landmarks.astype('float').reshape(-1, 2)
        sample = {'image': image, 'landmarks': landmarks}
        '''
        
         if self.transform:
            sample = self.transform(sample)
        return sample
        
# 사용 방법
train_dataset = mnistDataset("파일경로/파일.csv")
test_dataset = mnistDataset("파일경로/파일.csv")

 

Transform

 

샘플들이 다 같은 사이즈가 아닐 때 혹은 다른 상황에 사용할 수 있습니다. 대부분의 신경망(neural networks)은 고정된 크기의 이미지라고 가정합니다. 그러므로 우리는 신경망에 주기 전에 처리할 과정을 작성해야합니다. ( Preprocessing )

 

  • Rescale : 이미지의 크기를 조절합니다.
  • RandomCrop : 이미지를 무작위로 자릅니다.
  • ToTensor : numpy 이미지에서 torch 이미지로 변경합니다. (축변환 필요합니다.)

간단한 함수대신에 호출 할 수 있는 클래스로 작성합니다. 이렇게 한다면, 클래스가 호출 될 때마다 Transform의 매개변수가 전달 되지 않아도 됩니다. 이와 같이, __call__ 함수를 구현해야 합니다. 필요하다면, __init__ 함수도 구현해야 합니다. 다음과 같이 transform을 사용할 수 있습니다.

 

ex)))) 이미지
class Rescale(object):
    """
    주어진 사이즈로 샘플크기를 조정합니다.
        output_size(tuple or int) : 원하는 사이즈 값
            tuple인 경우 해당 tuple(output_size)이 결과물(output)의 크기가 되고,
            int라면 비율을 유지하면서, 길이가 작은 쪽이 output_size가 됩니다.
    """

    def __init__(self, output_size):
        assert isinstance(output_size, (int, tuple))
        self.output_size = output_size

    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']

        h, w = image.shape[:2]
        if isinstance(self.output_size, int):
            if h > w:
                new_h, new_w = self.output_size * h / w, self.output_size
            else:
                new_h, new_w = self.output_size, self.output_size * w / h
        else:
            new_h, new_w = self.output_size

        new_h, new_w = int(new_h), int(new_w)

        img = transform.resize(image, (new_h, new_w))

        landmarks = landmarks * [new_w / w, new_h / h]

        return {'image': img, 'landmarks': landmarks}


class ToTensor(object):
    """numpy array를 tensor(torch)로 변환 시켜줍니다."""

    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']

        # swap color axis because
        # numpy image: H x W x C
        # torch image: C X H X W
        image = image.transpose((2, 0, 1))
        return {'image': torch.from_numpy(image),
                'landmarks': torch.from_numpy(landmarks)}

 

저번에 올렸던 Fashion MNIST 의 행렬 사이즈를 변경할 수 있습니다.

data = []
df_mnist = pd.read_csv('파일경로/파일.csv')
for idx in df_mnist.index:
    temp_input = df_mnist.values[idx][1:]
    #>>> '''전처리 과정''' temp_input_reshape = temp_input.reshape([28, 28])
    temp_target = df_mnist.values[idx][0]
    data.append((temp_input_reshape, temp_target))

 

Without Resize
Resize

 

 

 

~ing

 

 

 

 

 

 

 

참고자료:https://tutorials.pytorch.kr/beginner/data_loading_tutorial.html

반응형