AI/활용

Pytorch로 Fashion MNIST 구현하기

모딩 2021. 3. 3. 17:21
반응형

파이토치 사용법을 익히기 위해 간단한 이미지 분류 과정을 학습해보았다. 파이토치에 내장되어 있는 Fashion MNIST 데이터를 사용하여 학습을 진행했다. 

학습에 사용한 패키지들 

import torch
import torchvision
from torchvision import models
import torchvision.datasets as dsets
import torchvision.transforms as transforms 
from torch.utils.data import Dataset, DataLoader
import torch.nn.init
import torch.optim as optim
import torch.nn.functional as F
import torch.nn as nn

import matplotlib.pyplot as plt
import time

 

패키지 사용법에 대해서 여기에 정리해두었다. 

 

하드웨어 설정

device = 'cuda' if torch.cuda.is_available() else 'cpu'

torch.manual_seed(777)  
if device == 'cuda':
  torch.cuda.manual_seed_all(777)

**torch.manual_seed(777)와 torch.cuda.manual_seed_all(777)을 쓰는 이유는 무엇일까?

 

학습을 위한 실험을 할 때 무작위성을 컨트롤하기 위해 사용한다. 시드가 무작위로 변환다면 학습 모델의 결과를 확인할 때 무엇이 문제인지 파악하기 어려워진다. reproducible한 실험을 하기 위해 시드를 고정해둘 필요가 있다. Pytorch에서 무작위성을 배제하고 일관된 학습 결과를 얻으려면 다음과 같은 코드를 사용하면 된다. 

torch.manual_seed(seed) # cpu 연산 무작위 고정 
torch.cuda.manual_seed(seed) # gpu 연산 무작위 고정 
torch.cuda.manual_seed_all(seed) # 멀티 gpu 연산 무작위 고정 
torch.backends.cudnn.enabled = False # cudnn library를 사용하지 않게 만듬
numpy.random.seed(~~) # numpy 관련 연산 무작위 고정

 

예를 들어 아래와 같이 시드를 고정하여 사용하면 학습시에 몇 번을 돌려도 같은 결과가 나오는 것을 알 수 있다. 

torch.manual_seed(777)
torch.cuda.manual_seed(777)
torch.backends.cudnn.enabled = False 

 

파라미터 설정 

최적화된 학습 결과를 얻기 위해 파라미터를 설정해야한다. 기본적으로 필요한 lr, epoch, batch_size를 설정했다. 

learning_rate = 0.001 
training_epochs = 50 
batch_size = 200

**lr, epoch, batch_size의 개념은 여기에 따로 정리해두었다. 

 

 

데이터 불러오기 (data loading)

(1) FashionMNIST 다운로드

파이토치에 내장되어 있는 FashionMNIST를 다운받는다. 학습을 위해 mnist_train, mnist_test 로 나누어 불러온다.

 

(2) Image resize

Lenet 신경망에 맞는 입력 크기는 32*32이다. MNIST 데이터 셋을 모델에 사용하기 위해서 image resize를 진행한다. 그리고 모델학습을 위해 이미지를 텐서로 바꿔준다. 

transforms=transforms.Compose([
                               transforms.Resize((35,35)),
                               transforms.ToTensor(),
                               ])

mnist_train = dsets.FashionMNIST(root='MNIST_data/',
                          train=True,
                          transform=transforms,
                          download=True)

mnist_test = dsets.FashionMNIST(root='MNIST_data/',
                         train=False,
                         transform=transforms,
                         download=True)

**Compose와 dset 사용법은 여기에 설명해두었다.

 

(3) Mini-batch 로 데이터 정의

학습을 위해 DataLoader를 사용하여 데이터를 작은 단위로 나눈다. 

train_loader = DataLoader(dataset=mnist_train,
                          batch_size=batch_size,
                          shuffle=True,
                          drop_last=True)

test_loader = DataLoader(dataset=mnist_test,
                         batch_size=batch_size,
                         shuffle=True,
                         drop_last=True)

 

모델 정의 

(1) 간단한 순전파 네트워크(Feed-forward network)인 Lenet5 신경망을 정의한다. 

class Lenet5(torch.nn.Module):
  def __init__(self):
    super(Lenet5,self).__init__()

    self.l1=torch.nn.Conv2d(1,6,kernel_size=5,padding=0,stride=1)
    self.x1=torch.nn.Tanh()
    self.l2=torch.nn.AvgPool2d(kernel_size=2,padding=0,stride=2)

    self.l3=torch.nn.Conv2d(6,16,kernel_size=5,padding=0,stride=1)
    self.x2=torch.nn.Tanh()
    self.l4=torch.nn.AvgPool2d(kernel_size=2,padding=0,stride=2)

    self.l5=torch.nn.Flatten()

    self.l6=torch.nn.Linear(16*5*5,120,bias=True)
    self.x3=torch.nn.Tanh()

    self.l7=torch.nn.Linear(120,84,bias=True)
    self.x4=torch.nn.Tanh()

    self.l8=torch.nn.Linear(84,10,bias=True)
  


  def forward(self,x):
    out=self.l1(x)
    out=self.x1(out)
    out=self.l2(out)
    out=self.l3(out)
    out=self.x2(out)
    out=self.l4(out)

    out=out.view(out.size(0),-1)

    out=self.l6(out)
    out=self.x3(out)
    out=self.l7(out)
    out=self.x4(out)
    out=self.l8(out)
    return out

 

(2) 학습을 위한 모델을 정의한다. 미리 설정한 하드웨어 가속기를 사용한다. 

model = Lenet5().to(device) # device = 'cuda' if torch.cuda.is_available() else 'cpu'

 

 

모델 최적화 (optimization)

 

 

Adam optimizer를 사용하여 모델 최적화를 진행한다. 미리 설정한 lr을 사용하였다. 

optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
# optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

 

모델 학습 (model training) 

1번의 에폭씩 50번 반복하여 모델 학습을 진행한다. 

 

(1) training_loader에서 X, Y값을 받고 예측을 위한 선형 회귀 함수를 생성한다. 

total_batch = len(train_loader)
print('Learning stared. It takes sometime.')
for epoch in range(training_epochs):
  avg_cost = 0

  for X, Y in train_loader:
      X = X.to(device)
      Y = Y.to(device)
      hypothesis = model(X) 

(2) 모델 학습 진행  

학습은 [순전파 - 오차값 계산 - 역전파 - 최적화] 의 과정으로 진행된다.

새로운 가중치를 구하기 위해 파이토치에서는 먼저 기울기(gradient)를 0으로 초기화하고 학습을 진행한다. 그 다음 추정값과 정답값 사이의 오차값을 계산하고 backward pass과정을 통해 가중치를 업데이트 한다. 최적의 결과값을 도출하기 위해 가중치, 편향값을 계속 보정하는 과정을 거친다. 

      optimizer.zero_grad() 
      cost = loss(hypothesis, Y) 
      cost.backward() 
      optimizer.step() 

(3) 각 배치마다 계산된 손실값의 평균 구하기 

1 에폭마다 손실값이 얼마나 나오는지 누적하고 전체 배치 수로 나누어 평균을 구한다.

 avg_cost += cost / total_batch 
  
  print('[Epoch: {:>2}] cost = {:>.9}'.format(epoch + 1, avg_cost)) 
print('Learning Finished!')

 

 

모델 테스트 (model evaluation)

(1) 테스트를 진행할 때는 Module 클래스의 훈련 상태 여부를 바꾸기 위해 model.eval() 함수를 호출한다. 이는 dropout 같은 함수가 드롭아웃을 적용할지 안할지를 결정하게 만드는 효과를 갖는다. 

accuracy = 0
total_batch = len(test_loader)

with torch.no_grad(): # 학습을 진행하지 않을 것이므로 torch.no_grad()
  model.eval() # model을 evaluation mode로 설정

**with torch.no_grad(): 를 왜 사용할까? 

가중치 연산을 끄기 위해 사용한다. 일종의 파이썬 컨텍스트 매니저(context manager)이다. 이 컨텍스트 내부에서 새로 생성된 텐서들은 requires_grad=False 상태가 되어, 메모리 사용량을 아껴준다.  

 

(2) test_loader에서 X, Y값을 받고 예측을 위한 선형 회귀 함수를 생성한다. 

  for X, Y in test_loader:
      X = X.to(device)
      Y = Y.to(device)

      prediction = model(X)

 

(3) 각 배치별로 가장 높은 가능성의 숫자 클래스를 뽑아준다. 

0번째 컬럼은 값이 아니기 때문에 1번째 컬럼부터 시작한다(first column has actual prob.) 그 값중에 가장 가능성이 높은 숫자 클래스를 뽑아준다. 그 값과 테스트 정답값과 비교하여 True/False 여부를 얻는다. 얻은 값 중에 True값만 평균내서 정확도를 추출하는 건가? 

correct_prediction = torch.argmax(prediction, 1) == Y 

**torch.argmax() 는 무엇일까? 

텐서의 최대값의 위치를 찾는 과정이다. torch.max를 사용하여 최대값을 찾고, torch.argmax를 사용하여 최대값의 위치를 찾을 수 있다. 

x = torch.rand(3,3)
max_x = torch.max(x)
max_x_location = torch.argmax(x)

 

(4) 전체 배치의 정확도를 누적시킨다.

나중에 전체 배치에 대한 정확도 평균을 구하기 위해 해당 과정을 진행한다. 

accuracy += correct_prediction.float().mean() 
# accuracy = correct_prediction.float().mean()을 하면 하나의 배치에 해당된 정확도만 알 수 있나? 

 

(5) 전체 배치에 대한 정확도 평균을 계산한다.

전체 배치의 정확도 합에서 전체 배치의 개수를 나누어준다.

print('Accuracy:' accuracy.item()/total_batch)

**accuracy.item() 에서 .item() 을 사용하는 이유는 무엇일까?

accuracy는 1개의 요소를 가지는 텐서 집합이다. item()을 사용하면 파이썬에서의 float 데이터처럼 값을 얻을 수 있다. 

x = torch.randn(1)
print(x) # tensor([ 0.9422])
print(x.item()) # 0.9422121644020081

 

 

참고자료

wikidocs.net/63618

http://www.gisdeveloper.co.kr/?p=7755

github.com/jinnyjinny/Torchvision

반응형

'AI > 활용' 카테고리의 다른 글

PyTorch 모델을 C++ 에 빌드 및 컴파일하기  (1) 2021.01.24