딥러닝 Deeplearning 6

Bleu·2023년 10월 23일
0

python

목록 보기
22/22

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 torch
import torch.nn as nn
from torchvision import datasets  #torchvision(파이토치 영상처리 모율).datasets(이미지 데이터셋 제공 class

import numpy as np
# 데이터들을 저장할 메모리
DATASET_ROOT_PATH = "datasets"  # 상대 경로, 절대경로 상관 없ㅇㅁ

# MNIST Dataset 생성
## Train dataset
mnist_trainset = datasets.MNIST(root = DATASET_ROOT_PATH,  # 원본 데이터 파일을 저장 위치
                               train =False,  #)True: Train set, False:Test set
                                download = True,  # True : 원본파일이 없으면 다운로드,, False: 다운받지 않는다
                                # Transform = 이미지변환처리함수  # 이미지를 제공하기 전체 전처리 필요
                               )
# Test dataset -> train = Flase
mnist_testset = datasets.MNIST(root=DATASET_ROOT_PATH, train=False, download = True)
### 타입 확인
print(type(mnist_trainset), type(mnist_testset))
print(isinstance(mnist_trainset, torch.utils.data.Dataset))  # Dataset의 하위 객체인지
# isinstance(객체, 클래스)  -> 객체가 클래스 타입의 객체인지 확인, 보통 상속관계 확인 많이 씀
print(mnist_testset)
## 총 데이터의 개수만 확인
print(len(mnist_trainset), len(mnist_testset))

##6만과 만이라는 int값이 필요한 경우 사용
## Dataste은 subscriptable 타입(indexing이 기능)이다. 
### => indexing으로 개별 데이터 조회가 가능 (Dataset은 slicing/facy indexing 은 안됨 = 한개씬만 조회 가능ㅇ)
data1 = mnist_trainset[0]
print(type(data1))  # tuple: (input, output)
print(type(data1[0]), type(data1[1]))
print(data1[1])

data1[0]
# data1의 image를 ndarray 변환
img1 = np.array(data1[0])
print(img1.shape, img1.min(), img1.max())
import matplotlib.pyplot as plt
plt.imshow(img1, cmap='gray')
plt.show()
# 이미지 여러개 확인  - 반복문 사용
for i in range(15):
    plt.subplot(3, 5, i + 1)
    img, label = mnist_trainset[i]  #튜플 대입
    img = np.array(img)  #PLT.Image-> ndarray
    plt.imshow(img, cmap = "gray")
    plt.title(str(label))  # Label: int -> 문자열로 변환
    
plt.tight_layout()
plt.show()
mnist_trainset.class_to_idx
# 정답의 의미 -> class : 딕셔너리
pred_label = 3  # 모델이 추정한 값으로 가정
mnist_trainset.classes[pred_label]

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

  • Dataset 생성할 때 전달하는 함수로 원본데이터를 모델에 주입(feeding)하기 전 전처리 과정을 정의한다.
    • Data Pipeline을 구성하는 함수
  • 매개변수로 input data 한개를 입력받아 처리한 결과를 반환하도록 구현한다.
# 위에서 읽은 mnist 이미지 정보
data = mnist_trainset[0][0]   # 이미지만 가져온 것
img = np.array(data)
print("data의 type:", type(data))
print(img.shape)
print("pixcel 최소, 최대값 - ", img.min(), img.max())
print("dtype:", img.dtype)

torchvision.transforms.ToTensor

  • PIL Image나 NumPy ndarray 를 FloatTensor(float32) 로 변환하고, 이미지의 픽셀의 크기(intensity - 보통 0~ 25) 값을 [0., 1.] 범위로 비례하여 조정한다.
  • Image 의 shape을 (channel, height, width) 로 변경한다.
  • https://pytorch.org/vision/stable/transforms.html
from torchvision import transforms
# 영상데이처(이미지)를 전처리 하기 위한 transforms(변환합수)들을 제공하는 모듈

mnist_trainset2 = datasets.MNIST(root=DATASET_ROOT_PATH, train=True, download=True, 
                                 transform = transforms.ToTensor()  # ToTensor 클래스 객체 -객체를 함수처럼 사용 가능
                                )

#  transform 이 설정 안된 경우: 원본이미지 파일 -읽어서-> Dataset -반환 ->
#  transform 이 설정된 경우: 원본이미지 파일 - 읽어서 -> Dataset - transform함수(이미지) - 반환->
data2, label = mnist_trainset2[0]
print(label)
print("data2의 타입: ", type(data2))  # PLI.Image -> Tensor           #  데이터 타입이 tensor type으로 바뀜   
print(data2.shape)                   # [channel, height, width]
print("pixcel의 최소, 최대:", data2.min(), data2.max())  # 0~1 사이로 정규화(normalize) ==> scaling
print("pixcel의 타입:",data2.dtype)    # unit8 -> float32 
   

###### ToTensor가 하는 일

transform.Normalize

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

Compose를 이용해서 변환기를 묶어준다.
-> compose도 하나의 함수

예시 추가

DataLoader 생성

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


OxfordPet Dataset 생성

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, "imafes")  # 압축 푼 이미지들 디렉토리 경로 

os.makedirs(PET_DATA_PATH, exist_ok=True)

압축 해제

# 압축 풀기: zip: z|ipfile 모듈, 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

파일 경로를 분리 (사용할 범위에 따라서)

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

이미지들 중 RGB 모드의 이미지만 남기고 삭제한다
(이때는 open cv 보다 확장자가 더 편하다)

# 이미지들 중 RGB 모드의 이미지만 남기고 삭제  - 이때는 open cv 보다 확장자가 더 편하다
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}")

총 몇개의 파일이 삭제되었는지 확인

remove_cnt

삭제결과를 적용해서 file_list르 새로 생성

file_list = glob(r"datasets/ozfordpet/**/*.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

위에서 만든 file_list를 사용

file_list[0]
# 0-> american bulldog: index_to _class
# american bulldog -> 0: class_to_index

class_name_set = set()  # 중복된것은 하나만 저장하기 위해 set을 생성 <- vkdlfaud: 품종_번호.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, classname)
    class_name_set.add(class_name)
    #break

제대로된 결과 도출

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

새로운 예시

(이 부분에서 코드 점검 필요)


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

import

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

헤더 없음

수정 필요 - 고치자

iris = pd.read_csv('data/iris.data', header=None,
                  names=["꽃받침길이", "꽃맏침너비", "꽃잎길이", "꽃잎너비", "정답"])  # 정답- 품종
iris.shape
iris['정답'].unique()
index_to_class = list(iris['정답'].unique())
# index_to_class
class_to_index = {class_name: idx for idx, classs_name in enumerate(index_to_class)}
class_to_index

torch에서는 제공 안하는 샐ㅗㅜ 맛집을 ㅁ크
!pip install scikit-learn

이 위는 전체적인 수정 필요



torchvision.datasets.ImageFolder 이용

  • 저장장치에 파일로 저장된 image들을 쉽게 로딩할 수 있도록 한다.
  • train/validation/test 데이터셋을 저장하는 디렉토리에 class 별로 디렉토리를 만들고 이미지를 저장한다.

gdown library 새로 설치 - google drive의 공유파일 다운 가능하게 하는 library
(공유 파일의 ID 만 알고 있다면 다운로드 가능)

그러나 한번씩 upgrade를 해주어야 한다

!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' # 파일 링크 중간에 보면 id가 있다
    fname = 'data/cats_and_dogs_small.zip'   #파일을 어디에 넣을지 지정

    gdown.download(url, fname, quiet=False)  #quiet를 true 로 하면 다운로드 되는것이 안보인다
    
    #zipfile모듈: Zip 압축파일을 다루는 모듈(압축하기, 풀기)
    from zipfile import ZipFile  #zip 파일 안에 zip파일 존재
    # 압축풀기: ZipFile(압축파일경로).extractall(풀경로) # 디렉토리 없으면 생성해 준다.
    with ZipFile(fname) as zipFile:
        zipFile.extractall(os.path.join('datasets','cats_and_dogs_small'))
        
down_extract()   


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

  • 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만 추출하는 경우)

예시 추가

torchvision.datasets.ImageFolder 이용

  • 저장장치에 파일로 저장된 image들을 쉽게 로딩할 수 있도록 한다.
  • train/validation/test 데이터셋을 저장하는 디렉토리에 class 별로 디렉토리를 만들고 이미지를 저장한다.

random_split() 함수 이용

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

0개의 댓글