Cnn 11 (데이터 준비하기)
파이토치에서 데이터 준비하기
import torch # 파이토치 기본 라이브러리
# torchvision : 데이터셋, 모델 아키텍처, 컴퓨터 비전의 이미지 변환 기능 제공
import torchvision
from torchvision import datasets # torchvision에서 제공하는 데이터셋
from torchvision import transforms # 이미지 변환기능을 제공하는 패키지
# torch.utils.data : 파이토치 데이터 로딩 유틸리티
from torch.utils.data import DataLoader # 모델 훈련에 사용할 수 있는 미니 배치 구성하고
# 매 epoch마다 데이터를 샘플링, 병렬처리 등의 일을 해주는 함수
import numpy as np
import matplotlib.pyplot as plt
import cv2
1. 파이토치 제공 데이터 사용하기
transform = transforms.Compose([transforms.RandomHorizontalFlip(p=0.3),
transforms.ToTensor()])
trainset = datasets.CIFAR10('./dataset/', download=True, train=True, transform=transform)
testset = datasets.CIFAR10('./dataset/', download=True, train=False, transform=transform)
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./dataset/cifar-10-python.tar.gz
100%|██████████| 170498071/170498071 [00:02<00:00, 59091013.15it/s]
Extracting ./dataset/cifar-10-python.tar.gz to ./dataset/
Files already downloaded and verified
batch_size= 16
trainloader = DataLoader(trainset, batch_size=batch_size, shuffle=True)
testloader = DataLoader(trainset, batch_size=batch_size, shuffle=False)
2. 같은 클래스별로 폴더가 정리된 데이터 사용하기
!pip install kaggle
from google.colab import files
files.upload()
Saving kaggle.json to kaggle.json
{'kaggle.json': b'{"username":"ghyungheejang","key":"6b01278c937e20ac3033e43c7f98a078"}'}
# permmision warning 방지
!mkdir -p ~/.kaggle
!cp kaggle.json ~/.kaggle/
!chmod 600 ~/.kaggle/kaggle.json
# download
!kaggle datasets download -d tongpython/cat-and-dog
# unzip(압축풀기)
!unzip -q cat-and-dog.zip -d catanddog/
Downloading cat-and-dog.zip to /content
100% 217M/218M [00:07<00:00, 28.5MB/s]
100% 218M/218M [00:07<00:00, 28.7MB/s]
data_dir = './catanddog/'
transform = transforms.Compose([transforms.Resize([256, 256]),
transforms.ToTensor()])
trainset = datasets.ImageFolder(root = data_dir + 'training_set/training_set/', transform=transform)
testset = datasets.ImageFolder(root = data_dir + 'test_set/test_set/', transform=transform)
trainset[0] # __getitem__(0)
len(trainset) # __len__()
8005
type(trainset[0])
tuple
trainset[0][0].size(), trainset[0][1]
(torch.Size([3, 256, 256]), 0)
batch_size= 16
trainloader = DataLoader(trainset, batch_size=batch_size, shuffle=True)
testloader = DataLoader(testset, batch_size=batch_size, shuffle=False)
train_iter = iter(trainloader)
images, labels = next(train_iter)
images.shape, labels.shape
(torch.Size([16, 3, 256, 256]), torch.Size([16]))
3. 나만의 데이터셋 만들기
# 파이토치에서 제공하는 Dataset 클래스를 활용해서 데이터셋 객체를 만들 수 있음
# 나만의 데이터셋은 Dataset 클래스를 상속받은 다음
# 특수 메서드인 __len__(), __getitem__()을 재정의해야 함
# 아래 패턴처럼 작성하기
"""
from torch.utils.data import Dataset
class MyDataset(Dataset):
def __init__(self):
# todo
pass
def __len__(self): # len(MyDataset)
# todo
pass
def __getitem__(self, index): # MyDataset[index]
# todo
pass
"""
# __len__() : 데이터셋의 크기를 반환, len(MyDataset) 형식으로 호출
# __getitem__() : index를 전달 받아 인덱스에 해당하는 데이터를 반환, MyDataset[index] 형식으로 호출
# 이런 방식으로 만든 데이터셋을 'map-style dataset'이라고 함
'\nfrom torch.utils.data import Dataset\n\nclass MyDataset(Dataset):\n def __init__(self):\n # todo\n pass\n\n def __len__(self): # len(MyDataset)\n # todo\n pass\n def __getitem__(self, index): # MyDataset[index]\n # todo\n pass\n'
torchvsion.transforms 변환기를 적용할 때 사용할 데이터셋
from torch.utils.data import Dataset
import glob
from PIL import Image # Image.open(path)
class CatandDogDataset(Dataset):
def __init__(self, root, transform):
self.filepaths = glob.glob(root + '*/*.jpg')
self.transform = transform
def __len__(self): # len(MyDataset)
return len(self.filepaths)
def __getitem__(self, index): # MyDataset[index]
# (1) image 준비
image_filepath = self.filepaths[index]
image = Image.open(image_filepath)
# https://pytorch.org/vision/stable/transforms.html
# 이미지 변환 (파이토치 transforms 에서 제공하는 변환기들은 PIL, tensor 타입을 기대)
transformed_image = self.transform(image) # Resize -> To Tensor
# (2) label 준비
dir_label = image_filepath.split('/')[-2]
if dir_label == 'cats':
label = 0
else: label = 1
return transformed_image, label
trainset = CatandDogDataset(root = data_dir + 'training_set/training_set/', transform=transform)
testset = CatandDogDataset(root = data_dir + 'test_set/test_set/', transform=transform)
len(trainset), len(testset)
(8005, 2023)
trainset.__len__(), testset.__len__()
(8005, 2023)
trainset[5000] # trainset.__getitem__(5000)
trainset[5000][0].size(), trainset[5000][1]
(torch.Size([3, 256, 256]), 1)
trainset.__getitem__(5000)[0].size()
torch.Size([3, 256, 256])
batch_size = 16
trainloader = DataLoader(trainset, batch_size=batch_size, shuffle=True)
train_iter = iter(trainloader)
images, labels = next(train_iter)
images.shape, labels.shape
(torch.Size([16, 3, 256, 256]), torch.Size([16]))
grid_image = torchvision.utils.make_grid(images)
plt.figure(figsize=(20, 200))
plt.imshow(grid_image.permute(1, 2, 0))
<matplotlib.image.AxesImage at 0x7fd0e686afa0>
4. 내부 전처리기 사용 예시(torchvision.transforms)
-
https://pytorch.org/vision/stable/transforms.html
-
Resize 해주는 변환기
# https://pytorch.org/vision/stable/transforms.html
# 이미지 변환 (파이토치 transforms 에서 제공하는 변환기들은 PIL, tensor 타입을 기대)
trans = transforms.Resize((400, 224))
image = Image.open('cactus.png')
resized_image = trans(image)
resized_image # type이 PIL 포맷
- Grayscale 바꾸는 변환기
trans = transforms.Grayscale()
image = Image.open('cactus.png')
gray_image = trans(image)
gray_image
- Random하게 Rotation 해주는 변환기
trans = transforms.RandomRotation(degrees=(0, 180))
image = Image.open('cactus.png')
rotated_image = trans(image)
rotated_image
- Random하게 Crop 시켜주는 변환기
trans = transforms.RandomCrop(size=(128, 128))
image = Image.open('cactus.png')
cropped_image = trans(image)
cropped_image
transform = transforms.Compose([transforms.RandomCrop(size=(128, 128)),
transforms.RandomRotation(degrees=(0, 180)),
transforms.Resize((400, 224)),
transforms.ToTensor()])
trainset = CatandDogDataset(root = data_dir + 'training_set/training_set/', transform=transform)
testset = CatandDogDataset(root = data_dir + 'test_set/test_set/', transform=transform)
batch_size = 16
trainloader = DataLoader(trainset, batch_size=batch_size, shuffle=True)
train_iter = iter(trainloader)
images, labels = next(train_iter)
grid_image = torchvision.utils.make_grid(images)
plt.figure(figsize=(20, 200))
plt.imshow(grid_image.permute(1, 2, 0))
<matplotlib.image.AxesImage at 0x7fd0ead78160>
5. 외부 전처리기 사용 예시(Albumentation)
- torchvision 변환기와 비교했을 때 처리 속도가 빠르고 더 다양한 이미지 변환을 제공
- Detection, Segmentation, Keypoints 에서도 사용 가능(Label까지 같이 변환해줌)
공식문서
- https://albumentations.ai/docs/getting_started/image_augmentation/
- https://albumentations.ai/docs/examples/example/
image = cv2.imread('dog.png')
orig_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(orig_image)
print(orig_image.shape)
(558, 557, 3)
import albumentations as A
- Resize : 이미지 크기를 조절하는 변환기 (높이, 너비)
augmentor = A.Resize(450, 650)
# Albumentation에서는 image 뿐만 아니라 label도 전처리 가능
# augmentor(image=image, label=label) 형식으로 사용
result = augmentor(image=orig_image)
augmented_image = result['image']
plt.imshow(augmented_image)
print(augmented_image.shape)
(450, 650, 3)
- RandomBrightnessContrast : 이미지 밝기과 대비를 조절하는 변환기
augmentor = A.RandomBrightnessContrast(brightness_limit=0.2, # -0.2~0.2 범위에서 임의로 밝기 조절. -1:검은색, 1:흰색
contrast_limit=0.2, # brightness_limit와 작동방식 동일
p=0.3 ) # 적용확률. 0.3이면 30%의 확률로 변환기에 적용
augmented_image = augmentor(image=orig_image)['image']
plt.imshow(augmented_image)
<matplotlib.image.AxesImage at 0x7fd675511af0>
def repeat_aug(count=4, orig_image=None, label=None, augmentor=None):
image_list = [orig_image]
label_list = ['Original']
for i in range(count):
augmented_image = augmentor(image=orig_image)["image"]
image_list.append(augmented_image)
label_list.append(label)
figure, axes = plt.subplots(nrows=1, ncols=count+1, figsize=(22, 4))
for i in range(count+1):
axes[i].imshow(image_list[i])
axes[i].set_title(label_list[i])
augmentor = A.RandomBrightnessContrast(brightness_limit=0.2, # -0.2~0.2 범위에서 임의로 밝기 조절. -1:검은색, 1:흰색
contrast_limit=0.2, # brightness_limit와 작동방식 동일
p=0.3 ) # 적용확률. 0.3이면 30%의 확률로 변환기에 적용
repeat_aug(count=4, orig_image=orig_image, label='RandomBrightnessContrast', augmentor=augmentor)
- VerticalFlip : 상하 대칭 변환
augmentor = A.VerticalFlip(p=0.2)
repeat_aug(count=4, orig_image=orig_image, label='VerticalFlip', augmentor=augmentor)
- HorizontalFlip : 좌우 대칭 변환환
augmentor = A.HorizontalFlip(p=0.5)
repeat_aug(count=4, orig_image=orig_image, label='HorizontalFlip', augmentor=augmentor)
- ShiftScaleRotate : 이동, 스케일링, 회전 변환
augmentor = A.ShiftScaleRotate(shift_limit=0.1, # -0.1~0.1 범위에서 무작위로 이동
scale_limit=0.2, # -0.2~0.2 범위에서 무작위로 스케일
rotate_limit=30, # -30~30도 범위에서 무작위로 회전
p=0.5)
repeat_aug(count=4, orig_image=orig_image, label='ShiftScaleRotate', augmentor=augmentor)
- Blur : 블러 효과, AdvancedBlur : 가우시안 플러 효과
augmentor = A.Blur(blur_limit=(7, 10), # blur_limit 는 kernel size로 값이 클수록 더 블러링됨
p = 1)
repeat_aug(count=4, orig_image=orig_image, label='Blur', augmentor=augmentor)
- OneOf : 한가지만 선택
augmentor = A.OneOf([
A.VerticalFlip(p=1),
A.HorizontalFlip(p=1),
A.Rotate(limit=(45, 90), p=1, border_mode=cv2.BORDER_CONSTANT)], p=0.8)
repeat_aug(count=4, orig_image=orig_image, label='OneOf', augmentor=augmentor)
- Normalize : 평균(0), 표준편차(1)
def repeat_aug2(count=4, orig_image=None, label=None, augmentor=None):
image_list = [orig_image]
label_list = ['Original']
mean=(0.485, 0.456, 0.406)
std=(0.229, 0.224, 0.225)
for i in range(count):
# transfrom
# (image - mean) / std
augmented_image = augmentor(image=orig_image)["image"]
# untransform
# (image * std) + mean
# augmented_image = augmented_image * (0.229, 0.224, 0.225) + (0.485, 0.456, 0.406)
image_list.append(augmented_image)
label_list.append(label)
figure, axes = plt.subplots(nrows=1, ncols=count+1, figsize=(22, 4))
for i in range(count+1):
axes[i].imshow(image_list[i])
axes[i].set_title(label_list[i])
augmentor = A.Normalize()
# Normalize 변환기에서는 imagenet 데이터의 평균과 표준편차가 기본값으로 설정
# mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)
repeat_aug2(count=4, orig_image=orig_image, label='Normalize', augmentor=augmentor)
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Albumentation 변환기를 적용한 데이터셋 만들기
from albumentations.pytorch import ToTensorV2
# torchvision 에서는 ToTensor 변환기를 사용하면 tensor로 변환함과 동시에 정규화까지 해줌
# albumentatioin 에서 제공하는 ToTensorV2는 정규화는 해주지 않음음
transform = A.Compose([A.Resize(224, 224),
ToTensorV2()])
albumentation 변환기를 적용할 때 사용할 데이터셋
from torch.utils.data import Dataset
import glob
from PIL import Image # Image.open(path)
class CatandDogDataset(Dataset):
def __init__(self, root, transform):
self.filepaths = glob.glob(root + '*/*.jpg')
self.transform = transform
def __len__(self): # len(MyDataset)
return len(self.filepaths)
def __getitem__(self, index): # MyDataset[index]
# (1) image 준비
image_filepath = self.filepaths[index]
# albumentation 변환기에서는 numpy ndarrary를 기대
image = cv2.imread(image_filepath)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
transformed_image = self.transform(image=image)['image'] # Resize -> To Tensor
# (2) label 준비
dir_label = image_filepath.split('/')[-2]
if dir_label == 'cats':
label = 0
else: label = 1
return transformed_image, label
trainset = CatandDogDataset(root = data_dir + 'training_set/training_set/', transform=transform)
testset = CatandDogDataset(root = data_dir + 'test_set/test_set/', transform=transform)
trainset[0][0].shape, trainset[0][1]
(torch.Size([3, 224, 224]), 0)
batch_size = 16
trainloader = DataLoader(trainset, batch_size=batch_size, shuffle=True)
train_iter = iter(trainloader)
images, labels = next(train_iter)
grid_image = torchvision.utils.make_grid(images)
plt.figure(figsize=(20, 200))
plt.imshow(grid_image.permute(1, 2, 0))
<matplotlib.image.AxesImage at 0x7fd674cf3f40>
6. 나만의 전처리기 만들기
나만의 변환기를 적용할 때 사용할 데이터셋
- torchvision 변환기 적용할 때 사용한 데이터셋과 동일
from torch.utils.data import Dataset
import glob
from PIL import Image # Image.open(path)
class CatandDogDataset(Dataset):
def __init__(self, root, transform):
self.filepaths = glob.glob(root + '*/*.jpg')
self.transform = transform
def __len__(self): # len(MyDataset)
return len(self.filepaths)
def __getitem__(self, index): # MyDataset[index]
# (1) image 준비
image_filepath = self.filepaths[index]
image = Image.open(image_filepath)
# https://pytorch.org/vision/stable/transforms.html
# 이미지 변환 (파이토치 transforms 에서 제공하는 변환기들은 PIL, tensor 타입을 기대)
transformed_image = self.transform(image) # Resize -> To Tensor
# (2) label 준비
dir_label = image_filepath.split('/')[-2]
if dir_label == 'cats':
label = 0
else: label = 1
return transformed_image, label
# 나만의 변환기 1 : Resize
# 입력 이미지를 Resize를 해줌
# transforms.Resize((224, 224)) 형태의 변환기
# 변환기를 class로 만들어도 __call__() 메서드를 채워넣으면 함수처럼 호출해서 사용할 수 있음음
class MyResize:
def __init__(self, output_size):
self.output_size = output_size
def __call__(self, input):
# 입력 이미지가 PIL 형태로 들어옴
np_input = np.array(input)
output_height, output_width = self.output_size
resized_image = cv2.resize(np_input, (output_width, output_height))
return resized_image
# 나만의 변환기 2 : ToTensor
# (1) 입력 이미지를 0~1 사이로 정규화하고, (2) Tensor 형태로 바꿈 (channel x height x width)
# transforms.ToTensor()
class MyToTensor:
def __call__(self, input):
# 입력 이미지가 PIL 형태로 들어옴
np_input = np.array(input)
# (1) 정규화
norm_input = np_input/255
# (2) ToTensor
tensor_input = torch.FloatTensor(norm_input) # float32 type tensor로 변환
tensor_input = tensor_input.permute(2, 0, 1) # h, w, c -> c, h, w
return tensor_input
image = Image.open('cactus.png')
# 나만의 변환기 1
trans = MyResize((224, 224)) # 세로, 가로
resized_image = trans(image) # trans.__call__(image)
resized_image.shape
(224, 224, 4)
# 나만의 변환기 2
trans = MyToTensor()
ts_image = trans(image) # trans.__call__(image)
ts_image.size()
torch.Size([4, 333, 250])
# 나만의 변환기 1 + 나만의 변환기 2
transform = transforms.Compose([MyResize((224, 224)), # Resize
MyToTensor()]) # ToTensor
transoformed_image = transform(image)
transoformed_image.size()
torch.Size([4, 224, 224])
전체 데이터셋에 나만의 변환기 파이프라인 적용
trainset = CatandDogDataset(root = data_dir + 'training_set/training_set/', transform=transform)
testset = CatandDogDataset(root = data_dir + 'test_set/test_set/', transform=transform)
batch_size = 16
trainloader = DataLoader(trainset, batch_size=batch_size, shuffle=True)
train_iter = iter(trainloader)
images, labels = next(train_iter)
grid_image = torchvision.utils.make_grid(images)
plt.figure(figsize=(20, 200))
plt.imshow(grid_image.permute(1, 2, 0))
<matplotlib.image.AxesImage at 0x7fd6751de6a0>
댓글남기기