아롱이 탐험대

얼굴 나이 인식기 개발 - 3 data load check code (Using EfficientNet with Pytorch) 본문

Project/pytorch

얼굴 나이 인식기 개발 - 3 data load check code (Using EfficientNet with Pytorch)

ys_cs17 2020. 9. 15. 11:50
반응형

Review

이전 시간까지 데이터 수집 및 처리에 관하여 코드를 작성하였다.

모든 전처리 과정을 마친 데이터를 각 train, validation으로 나누었고, 오늘은 이 데이터를 가지고 본격적으로 Neuron Net을 학습시키는 코드를 작성할 것이다.

 

 

개발 환경

CPU: i7-9700F

GPU:NVIDIA GEFORCE RTX 2070 SUPER

OS: UBUNTU 18.04

RAM: DDR4 16G

LANGUAGE: python 3.6.8

CUDA: 10.1

LIBRARY

pytorch: torch 1.5.0+cu101, torchvision 0.6.0+cu101

opencv: 4.4.0.42

matplotlib: 3.3.1

numpy: 1.19,1

 

전체 코드

https://github.com/yunseokddi/pytorch_dev/tree/master/facial_age_classifier/EfficientNet_ver

 

pytorch를 사용한 CNN 개발의 학습 코드는 크게 데이터 정의, 모델 정의, optimizer, criterion, train 진행, val 진행, weight load 파트로 진행된다.

train.py 코드를 작성하기 전 추후 불참사가 일어나는 것을 방지하기 위해 중간중간 디버깅하는 습관을 기르는 것이 좋다.

우선 데이터에 대한 정의를 해야한다.

각 데이터를 transform을 통해 다양하게 변환된 pytorch를 사용해 불러온 후 이에 대해 라벨 값이 잘 맞고, 올바른 이미지를 가지고 오는지를 확인하는 코드를 작성해보자.

 

1. data_viewer.py

import os
import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np

from torchvision import datasets

필요한 library들을 import로 불러오자.

 

def imshow(inp, title=None):
    inp = inp.numpy().transpose((1, 2, 0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1)
    plt.imshow(inp)
    if title is not None:
        plt.title(title)
    plt.pause(0.001)

batch size만큼 이미지와 라벨을 matplot을 통해 시각화해주는 함수이다.

우선 inp로 들어온 image tensor값을 numpy로 변경해준 다음, pytorch와 matplot의 channel, width, height에 해당하는 순서가 다름으로 transpose를 통해 순서를 변경해준다.

그리고 mean과 std를 이후에 나오는 data_transoform과 일치하게 설정을 해준다. 그리고 std를 inp와 곱하고, mean값을 더해준다. 이렇게 해주는 이유는 data_transform 과정에서 image를 학습이 더 잘되게 mean과 std를 사용하여 정규화를 해주는데, 만약 정규화된 이미지를 그대로 보게 된다면 아래 이미지와 같이 원본 이미지가 아닌 컴퓨터 입장에서 편한 이미지를 보게 될 것이다.

np.clip함수를 통해 혹시나 음수인 값이 존재할 수도 있으므로 모든 음수를 0으로 대체한다. relu와 비슷하다.

이 setting이 모두 끝난 후 plt.imshow를 통해 image를 출력하고,  pause를 통해 이미지가 바로 사라지지 않게 설정한다. 이를 설정하지 않는다면 이미지는 잠깐 보이고 바로 없어지게 된다.

 

batch_size = 4
data_dir = 'data/past_data/face_age'

data_transforms = {'train': transforms.Compose([
    transforms.Resize((128, 128)),
    transforms.RandomRotation(30),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
    'val': transforms.Compose([
        transforms.Resize((128, 128)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])}

batch_size는 이 코드에서는 본인이 한 번에 이미지를 몇 장 출력할 것인지 결정하는 변수라고 생각하면 된다.

data_dir로 데이터에 경로를 설정한 후 data_transoform으로 data를 어떻게 변환할 것인지 preprocessing 과정이라고 생각하면 된다.

transform을 자세히 살펴보자면 우선 pytorch가 제공하는 transforms.Copose 함수를 통해 transform을 구상하게 된다.

 

transforms.Resize()

facial age dataset의 image size는 각각 다르고, 가장 작은 이미지의 사이즈는 200*200이다. 원래는 256, 512 size를 많이 사용하지만 우리의 minimum size에 맞춰 128*128로 설정을 했다. 참고로 이미지의 size가 클수록 학습 과정이 길어지고, 차지하는 gpu memory도 커지게 된다. 아마 당신이 titan급 gpu를 갖고 있지 않은 이상 큰 network를 학습할 경우 중간에 segment fault라는 에러를 보게 될 것이다.

또한 이미지가 클수록 network가 학습할 위치 정보가 커지고, 화질이 더욱 선명해짐으로 더욱 학습이 잘 될 수도 있다.

 

transforms.Rotation(), transforms.RandomHorizontalFlip()

우리의 data는 전체적인 양을 보았을 때 적지는 않은 데이터이지만 각 label의  data를 보았을 때는 부족한 감이 있다.

따라서 epoch과 batch가 증가하게 되면 같은 데이터를 학습할 확률이 높다. 따라서 해당 함수를 통해 데이터의 변환이 필요하다.

Rotation을 통해 이미지를 random으로 좌우 각도로 뒤틀린다. parameter는 degree를 의미하고 30이라고 하면 최대 각도가 30도 뒤틀린다고 생각하면 된다.

RandomHorizontalFlip는 이미지를 랜덤적으로 수평으로 뒤집는다. 글씨 같은 데이터는 사용하지 않는 것이 좋다. parameter값은 수평으로 뒤집히는 확률이다. default는 0.5이다.

위 2개뿐만 아니라 pytorch에서는 수많은 data transform을 제공한다. 본인의 데이터의 특성에 따라 선택하여 transform을 진행하는 것이 좋다.

또한 이런 transoform은 반드시 train에 해당하는 데이터에서만 적용시키도록 하자.

validation과 test시 원본과 다른 이미지는 필요가 없다.

 

transforms.ToTensor()

이 함수는 반드시 필요하다. 데이터 처리, 출력, 연산 등은 보통 numpy로 진행하지만 deep learning 학습 시에는 반드시 tensor로 진행한다. 우선 numpy는 보통 정수 값이 들어있음으로, 수만개의 데이터를 학습시 매우 큰 값을 발생시킬 수 있고, 오차율을 가늠하기가 힘들다. 또한 CUDA 연산도 지원하지 않는다.

따라서 ToTensor를 통해 numpy 배열을 torch.tensor로 변환시켜주자.

 

transforms.Normalize()

각 이미지에 따라 채도, 명도 등이 다르기 때문에 computer vision에서는 이미지를 동일한 환경으로 맞춰주는 image normalize를 수행한다.

normalize의 parameter 값인 std와 mean 값은 실제 여러 image들의 std와 mean값과 유사해서 그런 값을 쓴다고 한다.

 

image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                          data_transforms[x])
                  for x in ['train', 'val']}

dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=batch_size,
                                             shuffle=True, num_workers=4)
              for x in ['train', 'val']}

dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
class_names = image_datasets['train'].classes

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

inputs, classes = next(iter(dataloaders['train']))

out = torchvision.utils.make_grid(inputs)

image_datasets

이제는 image data를 불러오고, 해당 data를 사용한 data loader를 작성하자

image data는 우리의 data format이 directory 구조로 되어있기 때문에 dataset.ImageFolder를 사용하면 간단하게 폴더에 있는 데이터들을 불러올 수 있다.

또한 option으로 train, val dataset을 각각 나누었는데 이를 통해 train, val를 위에서 작성한 별도의 transform을 적용시켜준다.

 

dataloaders

그다음은 dataloaders이다. 이는 network의 data가 어떻게 동작하게 할 것인지 option을 주는 파트라고 생각하면 된다.

첫 번째 parameter로 위에서 정의한 image_datasets을 데이터로 넣어주고, batch_size는 global로 정의한 4를 넣어준다. batch_size는 이미지가 학습 시 한 번에 몇 개의 data가 network에 들어갈 것인지 결정해준다.

자신이 갖고 있는 data의 수와 hardware에 맞게 batch_size를 조절해주고, 보통은 2의 지수승의 값들이 들어간다.

train 신 위를 고려해야 하지만 지금의 코드에서는 한 번의 몇 개의 데이터를 출력할 것인지 결정하는 변수임으로 본인 마음대로 해도 된다.

 

data_sizes

dataset_sizes는 train, val이 올바르게 load 되었나 확인용으로 넣었다.

 

class_names

class_names는 dataset의 class를 담고 있는 변수이다.. classes로 image label 값에 해당하는 directory 이름을 호출한다.

 

device

device는 본인이 만약 cuda가 설정되어 있으면 연산을 gpu로 하고, 아니면 cpu로 한다.

해당 코드는 cpu로 해도 되지만 학습 시 cpu로 하면 아마 학습이 몇 개월~몇 년은 걸릴 것이다.

 

inputs, classes

이제 next와 iter를 사용해 batch_size만큼 데이터와 class를 뽑은 후 make_grid를 사용해 4개를 grid 형식으로 출력할 준비가 되었다.

inputs는 이미지, classes는 label에 대한 정보를 갖고 있다.

 

imshow(out, title=[class_names[x] for x in classes])

자 이제 위에서 만든 함수를 호출하자

output1
output2

결과물을 보면 batch size만큼의 image, label이 출력되었고, data transform을 적용시켜 사진이 뒤틀린 보습을 볼 수 있다. 또한 title을 통해 각 이미지에 해당하는 올바른 label을 확인할 수 있다.

이로써 data loader가 정확히 동작하는 모습을 확인할 수 있었다.

반응형
Comments