인공지능/파이토치

파이토치 - CNN을 사용한 이미지 분류 (Fashion-MNIST)

해피밀세트 2020. 7. 21. 00:16
반응형

 

 

 

 

1. Fashion-MNIST

 

 

  • 10가지 분류의 옷 및 액세서리(신발, 구두 등)를 이미지 데이터
  • 28 X 28 픽셀 크기의 흑백 이미지

 

1) Fashion-MNIST 데이터로부터 DataLoader 작성

 

# 라이브러리 불러오기

import torch

from torch import nn, optim

from torch.utils.data import (Dataset, DataLoader, TensorDataset)

import tqdm

from torchvision.datasets import FashionMNIST

from torchvision import transforms

# 구글 드라이브와 구글 코랩을 연동

from google.colab import drive

drive.mount('/content/gdrive')

# 훈련용 데이터 가져오기
# 초기 상태에선 PIL(Python Imaging Library) 이미지 형식으로 Dataset를 만들어 버린다.
# 따라서 transforms.ToTensor를 사용해 텐서로 변환한다.

fashion_mnist_train = FashionMNIST("/content/gdrive/My Drive/FashionMNIST",

                                   train=True, download=True,

                                   transform=transforms.ToTensor())

# 검증용 데이터 가져오기

fashion_mnist_test = FashionMNIST("/content/gdrive/My Drive/FashionMNIST",

                                  train=False, download=True,

                                  transform=transforms.ToTensor())

# 배치 크기가 128인 DataLoader를 각각 작성

batch_size = 128

train_loader = DataLoader(fashion_mnist_train,

                          batch_size=batch_size, shuffle=True)

test_loader = DataLoader(fashion_mnist_train,

                         batch_size=batch_size, shuffle=False)

 

 

 

2. CNN 구축과 학습

 

1) 2층 합성곱과 2층 MLP를 연결한 CNN 작성

 

# (N, C, H, W)형식의 텐서를(N, C*H*W)로 늘리는 계층
# 합성곱 출력을 MLP에 전달할 때 필요

class FlattenLayer(nn.Module):

  def forward(selfx):

    sizes = x.size()

    return x.view(sizes[0], -1)

# 5x5의 커널을 사용해서 처음에 32개, 다음에 64개의 채널 작성
# BatchNorm2d는 이미지용 Batch Normalization
# Dropout2d는 이미지용 Dropout
# 마지막으로 FlasttenLayer 적용

conv_net = nn.Sequential(

    nn.Conv2d(1325),

    nn.MaxPool2d(2),

    nn.ReLU(),

    nn.BatchNorm2d(32),

    nn.Dropout2d(0.25),

    nn.Conv2d(32645),

    nn.MaxPool2d(2),

    nn.ReLU(),

    nn.BatchNorm2d(64),

    nn.Dropout2d(0.25),

    FlattenLayer()

)

# 합성곱에 의해 최종적으로 이미지 크기가 어떤지를 더미 데이터를 넣어서 확인한다.

test_input = torch.ones(112828)

conv_output_size = conv_net(test_input).size()[-1]

# 2층 MLP

mlp = nn.Sequential(

    nn.Linear(conv_output_size, 200),

    nn.ReLU(),

    nn.BatchNorm1d(200),

    nn.Dropout(0.25),

    nn.Linear(20010)

)

# 최종 CNN

net = nn.Sequential(

    conv_net,

    mlp

)

 

 

 

2) 평가와 훈련용 헬퍼 함수 작성

 

# 평가용 헬퍼 함수

def eval_net(netdata_loaderdevice="cpu"):

  # Dropout 및 BatchNorm을 무효화

  net.eval()

  ys = []

  ypreds = []

  for x, y in data_loader:

    # to 메서드로 계산을 실행할 디바이스로 전송

    x = x.to(device)

    y = y.to(device)

    # 확률이 가장 큰 분류를 예측

    # 여기선 forward(추론) 계산이 전부이므로 자동 미분에 필요한 처리는 off로 설정해서 불필요한 계산을 제한다

    with torch.no_grad() :

      _, y_pred = net(x).max(1)

    ys.append(y)

    ypreds.append(y_pred)

 

  # 미니 배치 단위의 예측 결과 등을 하나로 묶는다.

  ys = torch.cat(ys)

  ypreds = torch.cat(ypreds)

  # 예측 정확도 계산

  acc = (ys == ypreds).float().sum() / len(ys)

  return acc.item()

# 훈련용 헬퍼 함수

def train_net(nettrain_loadertest_loader,

              optimizer_cls=optim.Adam,

              loss_fn=nn.CrossEntropyLoss(),

              n_iter=10, device="cpu"):

  train_losses = []

  train_acc = []

  val_acc = []

  optimizer = optimizer_cls(net.parameters())

  for epoch in range(n_iter):

    running_loss = 0.0

    # 신경망을 훈련 모드로 설정

    net.train()

    n = 0

    n_acc = 0

    # 시간이 많이 걸리므로 tqdm을 사용해서 진행바를 표시

    for i, (xx, yy) in tqdm.tqdm(enumerate(train_loader),

                                 total = len(train_loader)):

      xx = xx.to(device)

      yy = yy.to(device)

      h = net(xx)

      loss = loss_fn(h, yy)

      optimizer.zero_grad()

      loss.backward()

      optimizer.step()

      running_loss += loss.item()

      n += len(xx)

      _, y_pred = h.max(1)

      n_acc += (yy == y_pred).float().sum().item()

    train_losses.append(running_loss / i)

    # 훈련 데이터의 예측 정확도

    train_acc.append(n_acc / n)

 

    # 검증 데이터의 예측 정확도

    val_acc.append(eval_net(net, test_loader, device))

    # epoch의 결과 표시

    print(epoch, train_losses[-1], train_acc[-1],

          val_acc[-1], flush=True)

 

 

 

3) 모든 파라미터를 GPU로 전송해서 훈련 실행

 

# 신경망의 모든 파라미터를 GPU로 전송

net.to("cuda:0")

# 훈련 실행

train_net(net, train_loader, test_loader, n_iter=20, device="cuda:0")

 

반응형