본문 바로가기

Python and Data

Pytorch로 모델 만들기 간단 정리(CNN-V)

캐글 하다가 시간 많이 남아서 기본적으로 이렇게 하면 모델 만들어진다 싶은 코드를 정리해두면

여기저기 가져다 쓸일이 많은것 같아서 한번 남긴다.

CNN를 위한 코드이다. 큰 틀은 별로 안바뀌고 여기서 모델, 전처리, 기타 하이퍼파라미터 정도만 바꾸면 꽤나 다용도로 사용할 수 있는 코드라고 생각한다.

 

기본적으로 이정도 라이브러리는 넣어놓고 시작한다. 자주 쓸 뿐더러 배포할 프로젝트가 아니기 때문에 없는것 보단 그냥 한번에 넣어 놓는게 좋은것 같다.

 

# Data handling
import pandas as pd
import numpy as np

# Data visualization
import matplotlib.pyplot as plt
import seaborn as sns
import cv2
from PIL import Image
Image.MAX_IMAGE_PIXELS = None

# Preprocessing
from sklearn.model_selection import train_test_split as tts
from sklearn.utils.class_weight import compute_class_weight

# Torch
import torch
from torch import nn, optim
from torch.utils.data import Dataset, DataLoader
from torchvision.models import vit_b_16, ViT_B_16_Weights
from torchinfo import summary
from torchvision.datasets import ImageFolder

# Metrics
from sklearn.metrics import balanced_accuracy_score
from sklearn.metrics import confusion_matrix

# os
import os

# Path
from pathlib import Path

# random
import random

# OrderedDict
from collections import OrderedDict

# tqdm
from tqdm.auto import tqdm

# warnings
import warnings
warnings.filterwarnings("ignore")

 

난 그다음에 바로 하이퍼 파라미터와 transform을 각각 넣어 놓는 편이다. 

 

hyperparam

train_val_split_ratio = 0.8
learning_rate = 0.0001
num_epoch = 100
batch_size = 8 # dataset이 고작 200몇개였다. 그러니까 이만한 배치사이즈도 이해해 주자
class_num = 7 #이때는 7종의 차 로고를 구분하는게 목적이여서 이렇게 해뒀다
loss_func = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

 

transform

가장 기본적인 것만 했다. 여기서 꼭 해줘야하는건 사실 ToTensor밖에 없다. 나머지(예를 들면 rotate나 flip 같은것들)은 성능에 영향을 주지만 ToTensor안하면 계산이 안된다.

trans = transforms.Compose([
    transforms.Resize((224, 224)),  # 이미지 크기 조정
    transforms.ToTensor(),  # 이미지를 텐서로 변환
])

 

그리고 이제 데이터를 받아온다. 받아오는 동시에 transform을 넣어주면 바뀐상태로 적용된다.

torch의 ImageFolder는 폴더의 이름을 자동으로 라벨로 연결하는 기능이 있다.

그다음 train_test_set을 분리해주면 일단 학습할 데이터는 모두 만들어졌다.

dataset_path = "car_logo_proj/data"
dataset = ImageFolder(root=dataset_path, transform=trans)
train_data, test_data = train_test_split(dataset, test_size = 0.2,random_state = 42)
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, drop_last=True)
test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, drop_last=True)

 

이제 해야할 일은 모델 넣고 돌리고. 학습 결과를 테스트하면 된다.

ResNet Pretrained model을 쓴다고 가정하면 이렇게 모델을 만들고 gpu가 있으면 올린다음 모델 객체를 생성해주면 된다.

class ResNet34(nn.Module):
    def __init__(self, pretrained):
        super(ResNet34, self).__init__()
        if pretrained is True:
            self.model = pretrainedmodels.__dict__['resnet34'](pretrained='imagenet')
        else:
            self.model = pretrainedmodels.__dict__['resnet34'](pretrained=None)

        # change the classification layer
        self.l0 = nn.Linear(512, class_num)
        self.dropout = nn.Dropout2d(0.6)
    def forward(self, x):
        # get the batch size only, ignore (c, h, w)
        batch, _, _, _ = x.shape
        x = self.model.features(x)
        x = F.adaptive_avg_pool2d(x, 1).reshape(batch, -1)
        x = self.dropout(x)
        l0 = self.l0(x)
        return l0
 
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

model = ResNet34(pretrained=True).to(device)

 

학습은 그냥 enum으로 순회하고

 

#train
for i in range(num_epoch):
    for j,[image,label] in enumerate(train_loader):
        x = image.to(device)
        y_= label.to(device)

        optimizer.zero_grad()
        output = model.forward(x)
        loss = loss_func(output,y_)
        loss.backward()
        optimizer.step()

    if i % 10 == 0:
        print(loss)

 

테스트도 거의 동일한 방식이지만 no_grad로

 

#test
correct = 0
total = 0

with torch.no_grad():
  for image,label in test_loader:
      x = image.to(device)
      y_= label.to(device)
      output = model.forward(x)
      _,output_index = torch.max(output,1)
      total += label.size(0)
      correct += (output_index == y_).sum().float()

  print("Accuracy of Test Data: {}".format(100*correct/total))

 

 

이후 모델이 괜찮다 싶으면 저장해서 쓰면 된다.

torch.save(model.state_dict(), 'model')

 

오차의 시각화 같은 부분은 상당부분 빠져있다.

케이스에 따라, 사람에 따라 보고자하는 방향이 다르기 때문에 그런 부분은 배제했다.

 

위의 코드를 따라서 세부적인 내용만 바꾼다면 기본적인 CNN Clasification에서 많이 사용할 수 있을것 같다.

오랜만에 나름 학술적인 내용이 아닌가 싶다가도 단순 코드리뷰라 딱히 안그런것 같기도 하다.

요즈음은 이론 공부하는게 겁이 난다. 이러다가 머리 굳는거 아닌가 싶다.