캐글 하다가 시간 많이 남아서 기본적으로 이렇게 하면 모델 만들어진다 싶은 코드를 정리해두면
여기저기 가져다 쓸일이 많은것 같아서 한번 남긴다.
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에서 많이 사용할 수 있을것 같다.
오랜만에 나름 학술적인 내용이 아닌가 싶다가도 단순 코드리뷰라 딱히 안그런것 같기도 하다.
요즈음은 이론 공부하는게 겁이 난다. 이러다가 머리 굳는거 아닌가 싶다.