본문 바로가기

Data_Analysis_Track_33/Python

Python_Deeplearning_pytorch_04(Dataset 과 DataLoader)

Dataset 과 DataLoader

  • 딥러닝 모델을 학습시키고 평가할때 사용할 데이터를 제공하기 위한 객체
  • torch.utils.data.Dataset
        - 원본 데이터셋(input/output dataset)을 저장하고 있으며 indexing을 통해 데이터를 하나씩 제공한다.
            - 제공시 data augmentation등 원본데이터를 변환해서 제공하도록 처리를 할 수 있다.
        - subscriptable, iterable 타입. 
            > subscriptable타입: indexing을 이용해 원소 조회가 가능한 타입)
       
  • torch.utils.data.DataLoader
        - Dataset의 데이터를 batch단위로 모델에 제공하기 위한 객체.
            - iterable 타입
        - Dataset이 가지고 있는 데이터를 어떻게 제공할 지 설정한다.
            - batch size, shuffle 등을 모델에 데이터제공을 어떻게 할지 방식을 설정한다.

 

Built-in Dataset

 

Image  Built-in dataset Loading

  • torchvision 모듈을 통해 다양한 오픈소스 이미지 데이터셋을 loading할 수 있는 Dataset 클래스를 제공한다.
  • 각 Dataset 클래스의 주요 매개변수
        - root : str
            - Raw data를 저장할 디렉토리 경로
        - train: bool
            - True일경우 Train set을 False일 경우 Test set을 load
        - download: bool
            - True이면 root에 지정된 경로에 raw 데이터를 인터셋에서 download할지 여부. 이미 저장되있는 경우                        download하지 않는다.
        - transform: function
            - Loading한 이미지를 변환하는 function.
                - Normalization이나 data Agumentation 처리를 한다.

 

import

import torch
import torch.nn as nn
from torchvision import datasets # torchvision(파이토치 영상처리 모듈).datasets (이미지 데이터셋 제공 클래스)

import numpy

 

디렉토리가 없다면 os.makedirs로 디렉토리 생성후 실행할 것

- MNIST Dataset(Train, Test) 생성

# 데이터들을 저장할 디렉토리.
DATASET_ROOT_PATH = "datasets" # 상대경로, 절대경로 상관없음.

# MNIST Dataset 생성
# Train dataset
mnist_trainset = datasets.MNIST(root=DATASET_ROOT_PATH # 원본데이터 파일 저장 위치
                                ,train=True # True : Train_set, False : Test set
                                ,download=True # True : 원본 파일이 없으면 다운로드, False : 다운받지 않는다.
                                # ,transform=이미지변환처리함수 # 이미지를 제공하기 전에 전처리할 함수                                
                               ) 
# Test dataset -> train=False
mnist_testset = datasets.MNIST(root=DATASET_ROOT_PATH
                               ,train=False
                               ,download=True
                              )

 

trainset과 testset의 type 확인

- isinstance(객체, 클래스) -> 객체가 클래스 타입의 객체인지 확인, 보통 상속관계 확인할 때 많이 사용한다.

# 타입확인
print(type(mnist_trainset), type(mnist_testset))
print(isinstance(mnist_trainset, torch.utils.data.Dataset)) # Dataset의 하위클래스의 객체인지(상속관계)
# isinstance(객체, 클래스) -> 객체가 클래스 타입의 객체인지 확인, 보통 상속관계 확인할 때 많이 사용한다.

 

Dataset 정보 확인

# Bulit-in Dataset 정보 확인
print(mnist_trainset)
print("="*20)
print(mnist_testset)

 

데이터 총 개수 확인

# 총데이터의 개수만 확인
print(len(mnist_trainset), len(mnist_testset))

 

indexing 기능

# Dataset은 subscriptable 타입(indexing이 가능)이다. => indexing으로 개별 데이터 조회가 가능. (slicing/fancy indexing은 불가 = 한개씩만 조회가능)
data1 = mnist_trainset[0]
print(type(data1)) #tuple : (input, output)
print(type(data1[0]), type(data1[1]))

 

input, output 확인

print(data1[1])

data1[0]

 

slicing/fancy indexing 불가 -> for문으로 한개씩 가져오기

# 이미지 여러개 확인
for i in range(15):
    plt.subplot(3, 5, i+1)
    img, label = mnist_trainset[i] # 튜플대입
    img = np.array(img) # PIL.Image -> ndarray
    plt.imshow(img, cmap="gray")
    plt.title(str(label)) # label: int -> 문자열로 변환.

plt.tight_layout()
plt.show()

 

 

# output의 범주값(class)들 확인 -> 분류문제를 위한 데이터. 분류문제: y가 범주형(categorical type)
mnist_trainset.classes
# list: index-class, value: 정답의 의미 문자열.

0 : "0 - zero" # 정답: 0 => 0의 의미: 0 - zero

 

mnist_trainset.class_to_idx
# 정답의미 -> class : 딕셔너리.

 

pred_label = 3 # 모델이 추정한 값으로 가정.
mnist_trainset.classes[pred_label]

mnist_trainset.class_to_idx['3 - three'] # '3 - three' 을 모델이 어떤 값으로 추정하는지

transform 매개변수를 이용한 데이터전처리

  • Dataset 생성할 때 전달하는 함수로 원본데이터를 모델에 주입(feeding)하기 전 전처리 과정을 정의한다.
        - Data Pipeline을 구성하는 함수
  • 매개변수로 input data 한개를 입력받아 처리한 결과를 반환하도록 구현한다.

 

torchvision.transforms.ToTensor

  • PIL Image나 NumPy ndarray 를 FloatTensor(float32) 로 변환하고, 이미지의 픽셀의 크기(intensity) 값을 [0, 1] 범위로 비례하여 조정한다.
  • Image 의 shape을 (channel, height, width) 로 변경한다.
  • https://pytorch.org/vision/stable/transforms.html

 

transform=transforms.ToTensor() 추가

from torchvision import transforms # 영상데이터(이미지)를 전처리를 위한 transforms(변환함수)들을 제공하는 모듈

mnist_trainset2 = datasets.MNIST(root=DATASET_ROOT_PATH
                                 ,train=True
                                 ,download=True
                                 ,transform=transforms.ToTensor() # ToTensor 클래스 객체
                                )

# transform이 설정안된 경우 :  원본이미지파일 - 읽어서 -> Dataset - 반환 ->
# 설정된 경우 : 원본이미지파일 - 읽어서 -> Dataset-transform함수(이미지) - 반환 ->

 

print("data2의 타입: ", type(data2)) # PIL.Image -> Tensor                                    
print(data2.shape)                  # [channel, height, width]
print("pixcel의 최소, 최대:", data2.min(), data2.max()) # 0 ~ 1 사이로 정규화(normalize) ==> scaling
print("pixcel의 타입:", data2.dtype) # uint8 -> float32

 

transform.Normalize

  • 채널별로 지정한 평균을 뺀 뒤 지정한 표준편차로 나누어서 정규화를 진행한다.
  • ToTensor()로 변환된 데이터를 받아서 추가 변환
            - 여려 변환을 할 경우 torchvision.transforms.Compose 클래스를 이용한다.

 

# 여러개의 transform 함수들을 묶어서 순서대로 호출 => Compose()를 이용해 묶어준다.
# 리스트에 호출 될 순서대로 넣어서 전달.

# ToTensor() -> Normalize()
transform = transforms.Compose([transforms.ToTensor() # 첫번째 변환작업.
                               ,transforms.Normalize(mean=0.5, std=0.5) # 첫번째 변환작업결과를 받아서 두번째 변환.
                               ])

mnist_trainset3 = datasets.MNIST(root=DATASET_ROOT_PATH
                                 ,train=True
                                 ,download=True
                                 ,transform=transform
                                )

mnist_testset3 = datasets.MNIST(root=DATASET_ROOT_PATH
                                 ,train=False
                                 ,download=True
                                 ,transform=transform
                                )

 

 

img3, label3 = mnist_trainset3[0]
print(type(img3))
print(img3.shape)
print(img3.dtype)

print(img3.min(), img3.max())

 

 

# channel이 여러개인 경우(color-3): mean/std = 값 => 모든 채널에 공통적으로 사용할 평균/표준편차 설정
#                                 mean/std = 튜플 => 각 채널별로 적용할 값을 튜플에 각각 넣어준다.
# normalizer = transforms.Normalize(mean=0.5, std=0.5) # 모든 픽셀에 평균=0.5, 표준편차=0.5 로 계산.
normalizer = transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)) # 각각의 픽셀에 평균=0.485, 표준편차=0.229 ...로 계산
transform2 = transforms.Compose([transforms.ToTensor() # 첫번째 변환작업.
                                ,normalizer                                 
                               ])

cifar10_train_set2 = datasets.CIFAR10(root=DATASET_ROOT_PATH
                                    ,train=True
                                    ,download=True
                                    ,transform=transform2
                                   )

 

img4, label4 = cifar10_train_set2[0]
print(img4.shape)
print("Red channel의 min/max:", img4[0].min(), img4[0].max())
print("Green channel의 min/max:", img4[1].min(), img4[1].max())
print("Blue channel의 min/max:", img4[2].min(), img4[2].max())

 

DataLoader 생성

  • DataLoader
        - 모델이 학습하거나 추론할 때 Dataset의 데이터를 모델에 제공해준다. (feeding)
        - initalizer속성
            - dataset: 값을 제공하는 Dataset 타입 객체
            - batch_size: 한번에 값을 제공할 batch 크기
            - shuffle: 에폭마다 데이터셋을 섞을 지 여부 (default: False)
            - drop_last: 마지막 배치의 데이터개수가 batch_size 설정보다 적을 경우 모델에 제공하지 않는다.

 

from torch.utils.data import DataLoader
# mnist_xxx_3 (tranform=ToTensor+Normalize)
mnist_train_loader = DataLoader(dataset=mnist_trainset3 # 데이터를 제공받을 Dataset
                                ,batch_size=200          # 한번에 제공할 데이터 개수
                                ,shuffle=True            # 처음제공전에 데이터들을 섞을지 여부 - Trainset: True, Testset: False
                                ,drop_last=True          # 제공할 데이터개수가 batch_szie보다 적을 경우 제공할지 여부(True: 제공안함) - Trainset: True, Testset: False
                               )

mnist_test_loader = DataLoader(dataset=mnist_testset3
                               ,batch_size=200
                              )

 

# DataLoader -> Iterator 타입
# 에폭당 step수 조회
# drop_last=True : floor(총데이터수/batch_size), Flase: ceil(총데이터수/batch_size)
print(len(mnist_train_loader), len(mnist_test_loader))

 

DataLoader에 설정된 Dataset 조회

# DataLoader에 설정된 Dataset을 조회.
mnist_test_loader.dataset

 

DataLoader에서 데이터를 조회

# DataLoader에서 데이터를 조회 -> Iterable 타입 ==> 한번: iter(): Iterator, next(iterator), 다: for in
batch1 = next(iter(mnist_train_loader))

 

print(type(batch1), len(batch1)) # [input들, output들]

X, y = batch1
print(type(X), type(y))

 

print(X.shape) # [200:batch size, 1:channel, 28:height, 28:width]
print(y.shape) # [200:batch size]

 

Custom Dataset 구현

  • 1. torch.utils.data.Dataset 클래스를 상속한 클래스를 정의한다.
  • 2. __init__(self, ...)  
        - DataSet객체 생성시 필요한 설정들을 초기화 한다. 
        - ex) Data저장 경로, transform 설정 여부 등
  • 3. __len__(self)
        - 총 데이터 수를 반환하도록 구현한다.
        - DataLoader가 Batch 생성할 때 사용한다.
  • 4. __getitem__(self, index)
        - index의 Data point를 반환한다.
        - input(X), output(y) 를 튜플로 반환한다.
        - transform이 있을 경우 변환처리한 input을 반환한다.

 

 

# subscriptable 타입 클래스 구현 -> indexing 가능객체
class MySub:

    def __init__(self):
        #  제공할 값들을 초기화
        self.one = "사자"
        self.two = "호랑이"
        self.three = "하마"

    def __len__(self):
        # 제공할 데이터의 개수를 반환.
        return 3

    def __getitem__(self, idx):
        # idx의 값을 반환.
        if idx == 0:
            return self.one
        elif idx == 1:
            return self.two
        elif idx == 2:
            return self.three
        else:
            raise IndexError(f"{idx} 번째 값이 없습니다.")

 

m = MySub()
len(m) # m.__len__()

 

print(m[2])
m[5]

 

OxfordPet Dataset 생성

  • https://www.robots.ox.ac.uk/~vgg/data/pets/
  • 개,고양이 37가지 품종
  • 품종별로 200장 정도씩 구성됨. (품종별로 이미지 개수는 다르다)
  • 목표
        - train: 70%, validation: 20%, test: 10%

 

import 코드

import os
import re
from glob import glob
import tarfile
from PIL import Image

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms

DATA_ROOT_PATH = 'datasets'
tarfile_path =  os.path.join(DATA_ROOT_PATH, 'images.tar.gz')
PET_DATA_PATH = os.path.join(DATA_ROOT_PATH, 'oxfordpet') # 압축풀 디렉토리 경로
PET_IMAGE_PATH = os.path.join(PET_DATA_PATH, "images")    # 압축푼 이미지들 디렉토리 경로

os.makedirs(PET_DATA_PATH, exist_ok=True)

 

datasets 디렉토리에 위 링크에서 다운받은 tar.gz 파일 두개를 넣고 아래 코드 실행

# 압축 풀기: zip: zipfile 모듈, tar: tarfile 모듈
with tarfile.open(tarfile_path, "r:gz") as tar: # 압축파일과 연결
    tar.extractall(PET_DATA_PATH) # 압축풀 디렉토리 경로를 지정해서 압축푼다.

 

**/*.확장자 -> *이전의 디렉토리안의 모든 확장자파일을 가져오기

# 참조코드
# 모든 이미지파일의 경로를 조회 => glob
file_list = glob(r"datasets/oxfordpet/**/*.jpg")
print(len(file_list))
file_list[:10]

 

os.path.~~ 명령어

# 참조 코드
f = file_list[0]
print(f)
print("파일경로에서 파일명과 확장자를 분리:", os.path.splitext(f)) # 확장자와 나머지 경로를 분리
print("파일경로에서 파일명(확장자포함)을 분리:", os.path.basename(f))
print("파일경로에서 디렉토리만 분리:", os.path.dirname(f))

 

RGB모드의 이미지를 제외한 이미지들 삭

# 이미지들 중 RGB 모드의 이미지만 남기고 삭제
remove_cnt = 0 # 몇장 삭제했는지 저장
for idx, image_path in enumerate(file_list):
    # print(idx, image_path)
    # 이미지 읽기 -> PIL.Image.open()
    with Image.open(image_path) as img:
        image_mode = img.mode # str: 'L' - grayscale, 'RGB': rgb

    # RGB가 아니면 삭제
    if image_mode != "RGB":
        os.remove(image_path)
        remove_cnt += 1
        print(f"{idx+1}번째 파일 삭제. {os.path.basename(image_path)}, mode: {image_mode}")

 

몇 장 삭제했는지 확인

- file_list의 개수가 7390에서 12장 사라진 7378로 바뀌었다.

remove_cnt

# 삭제결과를 적용해서 file_list를 새로 생성
file_list = glob(r"datasets/oxfordpet/**/*.jpg")
len(file_list)

 

index_to_class, class_to_index 생성

  • index_to_class : class들을 가지는 리스트. index(0, 1, ..)로 class 조회
  • class_to_index : key: 클래스이름, value: index -> class이름 넣으면 index 반환
  • 파일명이 class

 

index_to_class, class_to_index 생성

- os.path.~~ 활용

class_name_set = set() # 중복된 것은 하나만 저장하기 위해 set을 생성. <- 파일명: 품종_번호.jpg 품종만 set에 추가

for file in file_list:
    # file_list에서 파일명안의 품종을 추출한 뒤 class_name_set에 추가.
    filename = os.path.basename(file)
    filename = os.path.splitext(filename)[0]
    class_name = re.sub(r"_\d+", "", filename)
    # print(filename, class_name)
    class_name_set.add(class_name)

 

결과

index_to_class = list(class_name_set)

# index -> 클래스이름
index_to_class.sort()
print(len(index_to_class))
index_to_class

 

# 클래스이름 -> index : dic
class_to_index = {name:idx for idx, name in enumerate(index_to_class)}
class_to_index

 

index로 class 찾기, class로 index 찾기

pred = 5 # 모델 추정값
index_to_class[pred], class_to_index['Egyptian_Mau']

Train set(모델 훈련용), Test set(모델 평가/검증용) 분리 - 8:2, 7.5:2.5, 7:3, 6:4

 

# 개수를 확인
# 200장 기준으로(모든 label이 대략 200장 내외로 있음) 7:3 으로 나눴을때 각각 분리기준점을 계산
train_idx = int(200*0.7)
print(train_idx)
print(f"trainset: [:{train_idx}]")
print(f"testset: [{train_idx}:]")

 

이전에 사용했던 file_list의 파일경로들을 train, test로 분

# file_list의 파일경로들을 trainset, testset으로 분리
file_list.sort() # 같은 품종의 파일들끼리 모이도록 정렬
train_path_list, test_path_list = [], []   # train set과 test set에 넣을 파일들의 경로를 저장할 리스트.
cnt = 0 # class별(품종)로 몇번째 파일인지를 저장할 변수
previous_class = "" # 이전에 처리한 파일이 어떤 class(품종)인지를 저장할 변수

for path in file_list:
    # 경로에서 파일명만 조회
    file_name = os.path.splitext(os.path.basename(path))[0]
    class_name = re.sub(r"_\d+", "", file_name) # Abyssinian_1 => Abyssinian

    # 현재 반복에서 처리할 class가 이전에 처리한 것과 같은지 비교
    if class_name == previous_class: # 같은 class에 대한 처리
        cnt += 1
    else: # 새로운 클래스에 대한 처리
        cnt = 1

    # 현재 반복에서 처리하는 경로를 train_path.list 또는 test_path_list 로 이동
    if cnt <= train_idx:
        train_path_list.append(path)
    else:
        test_path_list.append(path)

    previous_class = class_name # 현재 처리한 파일의 클래스를 이전 클래스에 등록

 

개수 확인

print(len(file_list))

print(len(file_list), len(train_path_list), len(test_path_list))

 

 

Dataset 클래스를 정의 (최소 요건)

  • 1. Dataset을 상속
  • 2. __init__() : 필요한 속성들을 초기화
  • 3. __getitem__() : 1개의 데이터를 반환. ==> (input, output) 튜플로 반환.
  • 4. __len__() : 총 제공할 데이터개수
  • 5. +...
class OxfordPetDataset(Dataset):
    
    def __init__(self, path_list, transform=None):
        # path_list: 파일경로들을 가진 LIST
        self.path_list = path_list
        self.transform = transform
    
    def __len__(self):
        return len(self.path_list)
    
    def __getitem__(self, index):
        # index의 이미지와 그 label을 반환
        # 이미지: PIL.Image, ndarray
        path = self.path_list[index]
        img = Image.open(path)  # cv2.imread(path)  
        # transform 함수로 전처리
        if self.transform != None:
            img = self.transform(img)        
        
        file_name = os.path.splitext(os.path.basename(path))[0]
        class_name = re.sub(r"_\d+", "", file_name)
        # class name을 index 변환
        class_idx = class_to_index[class_name]
        return img, class_idx

 

train, test -> Dataset 클래스 정의

ofp_trainset = OxfordPetDataset(train_path_list)  # trainset
ofp_testset = OxfordPetDataset(test_path_list, transform=transforms.ToTensor())

 

trainset과 testset의 데이터값 확인.

print(len(ofp_trainset), len(ofp_testset))

x, y = ofp_trainset[2100]
print(type(x))
pirnt(y, index_to_class[y])
x

 

x1, y1 = ofp_testset[0]
print(y1)

# ToTensor가 적용됨.
type(x1), x1.min(), x1.max(), x1.shape

 

Dataset 생성 + DataLoader

### Dataset 생성 + DataLoader
transform = transforms.Compose([
    transforms.Resize((224, 224), antialias=True),  # 이미지 resize -h:224, w:224
    transforms.ToTensor()
])

train_set = OxfordPetDataset(train_path_list, transform=transform)
test_set = OxfordPetDataset(test_path_list, transform=transform)

train_loader = DataLoader(train_set, batch_size=100, shuffle=True, drop_last=True)
test_loader = DataLoader(test_set, batch_size=100)

 

x, y = next(iter(train_loader))
x.shape

Dataset을 이용해 CSV파일에 저장된 데이터셋 로딩

 

import

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

 

iris.data 불러오기

iris = pd.read_csv('data/iris.data', header=None, 
                   names=["꽃받침길이", "꽃받침너비", "꽃잎길이", "꽃잎너비", "정답"]) #정답-품종
iris.shape

 

정답 column의 값 종류 확인

iris['정답'].unique()

 

index_to_class, class_to_index

index_to_class = list(iris['정답'].unique())
print(index_to_class)
class_to_index = {class_name:idx for idx, class_name in enumerate(index_to_class)}
class_to_index

 

DataFrame 분리

## DataFrame -> X, y 분리
X = iris.drop(columns="정답").values # DataFrame/Series.values ==> ndarray


y = iris['정답']  
y = y.apply(lambda x: class_to_index[x]).to_frame().values #y 를 index로 변환.
# y
X.shape, y.shape

 

Train/Test set 분리

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, # 나눌 대상 input, output dataset
                                                    test_size=0.2, # test 데이터셋의 비율
                                                    stratify=y     # 분류: 원본데이터셋의 클래스별 데이터 비율에 맞춰서 나눈다.
                                                   )

 

validation

X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, stratify=y_train)

 

분리된 train, test set 형태 확인

X.shape, X_train.shape, X_test.shape

y.shape, y_train.shape, y_test.shape

np.unique(y, return_counts=True)[1]/150

np.unique(y_train, return_counts=True)[1]/120

 

Dataset 생성

- 원본 데이터가 메모리에 Tensor 객체(ndarray) 로 있을때 => TensorDataset 을 이용해서 생성.

## Dataset 생성
#### 원본 데이터가 메모리에 Tensor 객체(ndarray) 로 있을때 => TensorDataset 을 이용해서 생성.
# TensorDataset(Input:torch.Tensor, Output:torch.Tensor)

iris_trainset = TensorDataset(torch.tensor(X_train, dtype=torch.float32),  # X/input
                              torch.tensor(y_train, dtype=torch.float32)   # y/output
                             )
iris_testset = TensorDataset(torch.tensor(X_test, dtype=torch.float32), 
                             torch.tensor(y_test, dtype=torch.float32))

### DataLoader 생성

 

값 확인

x, y = iris_trainset[0] 
print(x)
print(y)

torchvision.datasets.ImageFolder 이용

  • 저장장치에 파일로 저장된 image들을 쉽게 로딩할 수 있도록 한다.
  • train/validation/test 데이터셋을 저장하는 디렉토리에 class 별로 디렉토리를 만들고 이미지를 저장한다.
  • google drive의 공유파일을 다운로드 하는 라이브러리.
        - !pip install gdown --upgrade
import os
from zipfile import ZipFile
import gdown
def down_extract():
    os.makedirs('data', exist_ok=True)
    url = 'https://drive.google.com/uc?id=1YIxDL0XJhhAMdScdRUfDgccAqyCw5-ZV'
    fname = 'data/cats_and_dogs_small.zip'

    gdown.download(url, fname, quiet=False)
    
    #zipfile모듈: Zip 압축파일을 다루는 모듈(압축하기, 풀기)
    from zipfile import ZipFile
    # 압축풀기: ZipFile(압축파일경로).extractall(풀경로) # 디렉토리 없으면 생성해 준다.
    with ZipFile(fname) as zipFile:
        zipFile.extractall(os.path.join('datasets','cats_and_dogs_small'))
        
down_extract()

 

ImageFolder를 이용한 Dataset 생성

- 클래스별 폴더가 있는 디렉토리를 설정한다.

from torchvision.datasets import ImageFolder

# ImageFolder를 이용해서 Dataset을 생성.
cd_train_set = ImageFolder(root="datasets/cats_and_dogs_small/train", # 클래스별 폴더가 있는 디렉토리 설정
                          )
cd_test_set = ImageFolder(root="datasets/cats_and_dogs_small/test", transform=transforms.ToTensor())
cd_valid_set = ImageFolder(root="datasets/cats_and_dogs_small/validation")

 

데이터셋 정보 및 데이터 확인(index to class, class to index 활용)

isinstance(cd_train_set, Dataset)

# 데이터수
len(cd_train_set), len(cd_test_set)

# 데이터셋 정보
cd_train_set

# index to class
cd_train_set.classes

# class to index
cd_train_set.class_to_idx

x, y = cd_train_set[0]
print(y, cd_train_set.classes[y])

print(type(x))

x

 

permute(height, width, channel)

x2, y2 = cd_test_set[0]
print(type(x2), type(x))

print(x2.min(), x2.max(), x2.shape)

import matplotlib.pyplot as plt
plt.imshow(x2.permute(1, 2, 0)) # [c, h, w] => [h, w, c]

모델 성능 평가를 위한 데이터셋 분리

  • Train 데이터셋 (훈련/학습 데이터셋)
        - 모델을 학습시킬 때 사용할 데이터셋.
  • Validation 데이터셋 (검증 데이터셋)
        - 모델의 성능 중간 검증을 위한 데이터셋
  • Test 데이터셋 (평가 데이터셋)
        - 모델의 성능을 최종적으로 측정하기 위한 데이터셋
        - Test 데이터셋은 마지막에 모델의 성능을 측정하는 용도로 한번만 사용한다.

 

Validataion 과 Test datas 분리이유

  • 모델을 훈련하고 평가했을때 원하는 성능이 나오지 않으면 모델을 수정한 뒤에 다시 훈련시키고 검증 하게 된다. 원하는 성능이 나올때 까지 설정변경->훈련->검증을 반복하게 된다.
  • 위 사이클을 반복하게 되면 검증과 결과를 바탕으로 모델 설정을 변경하게 되므로 검증할 때 사용한 데이터셋에 모델이 맞춰서 훈련하는 , 즉 검증데이터셋으로 모델을 학습한 것과 같다. 그래서 Train dataset과 Test dataset 두 개의 데이터셋만 사용하게 되면 모델의 성능을 제대로 평가할 수 없게 된다. 그래서 데이터셋을 train 세트, validation 세트, test 세트로 나눠 train set 와 validation set으로 모델을 최적화 한 뒤 마지막 학습하는 과정에서 한번도 사용하지 않았던  test set으로 최종 평가를 한다

     - (Parameter)머신러닝 모델 파라미터
        - 성능에 영향을 주는 값으로 최적화 대상
           - 하이퍼파라미터(Hyper Parameter)
                - 사람이 직접 설정해야하는 파라미터 값
           - 파라미터(Parameter)
                - 데이터 학습을 통해 찾는 파라미터 값

 

파이토치 데이터셋 분리

  • torch.utils.data.Subset을 이용

    - Dataset의 일부를 가지는 부분집합 데이터셋을 생성
    - 주로 사용하는 곳
        1. 데이터 셋을 분리
        2. 전체 데이터 셋에서 일부 데이터를 추출 할 때
        3. 데이터셋에서 특정 데이터만 골라서 추출할 때 (ex: 특정 class만 추출하는 경우)

dataset 생성

import torch
from torch.utils.data import Subset

data = torch.tensor([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])
label = torch.tensor([[1],[2],[2], [0],[1]])
print(data.shape, label.shape)
dataset = TensorDataset(data, label)
len(dataset)

 

 

Subset 활용

 

d1 = Subset(dataset, [0, 1])    # data의  index 0, 1을 d1으로
# dataset의 input/output중 0, 1 index의 값들로 부분 집합 Dataset을 생성

d2 = Subset(data, [2, 3, 4])     # data의 index 2, 3, 4을 d2로 나누기.

 

d1, d2 정보 확인

print(type(d1), isinstance(d1, Dataset))
print(len(d1), len(d2))

d1[1]

 

mnist_trainset 생성

from torchvision import datasets
DATA_ROOT_PATH = "datasets"

mnist_trainset = datasets.MNIST(root=DATA_ROOT_PATH, 
                                train=True, 
                                download=True,
                                transform=transforms.ToTensor())

 

mnist_trainset을 trainset, validationset으로 분리

# mnist_trainset -> trainset, validation set
t_index = list(range(50000))
v_index = list(range(50000, 60000))
t_set = Subset(mnist_trainset, t_index)
v_set = Subset(mnist_trainset, v_index)
len(t_set), len(v_set)

 

random_split() 함수 이용

  • Dataset객체와 나눌 데이터셋들의 원소개수를 리스트로 묶어서 전달하면  Shuffle후 나눈뒤 그 결과를 Subset객체들을 리스트에 담아 반환한다.
from torch.utils.data import random_split # 함수

# dataset(5개) -> 2 (3개, 2개)
d = random_split(dataset, # 나눌대상 Dataset
             [2, 3]  # 개수를 지정.
            )
print(d)
len(d[0]), len(d[1])

 

for x, y in dataset:
    print(x)

for x, y in d[0]:
    print(x)

 

random_split()

 - 3개로 나눔

random_split(dataset, [1, 2, 2])  # 3개 Subset으로 나눔.

 

random_split()

- 2개로 나눔 -> 개수 입

##### mnist_trainset  -> 두개 dataset으로 나눌경우. => 개수
mnist_trainset, mnist_valset = random_split(mnist_trainset, [50000, 10000])
len(mnist_trainset), len(mnist_valset)