computer vision 분야의 이미지 분류 문제를 해결하기 위해 모델을 학습시키는 프로세스에 대해서 배워보자.
크게 dataset을 DataLoader에 로드 → model 정의 → 학습 → 손실 함수 계산 → optimizer로 손실 함수가 최소가 되도록 모델 파라미터 최적화 의 과정을 거치게 된다.
__len__ : 데이터 셋의 전체 아이템 수 반환__getitem__ : 주어진 인덱스에 해당하는 데이터를 학습에 적합한 형태로 변환하고 제공import os
import pandas as pd
from torchvision.io import read_image
class CustomImageDataset(Dataset):
def __init__(self, annotations_file, img_dir, transform=None, target_transform=None):
self.img_labels = pd.read_csv(annotations_file)
self.img_dir = img_dir
self.transform = transform
self.target_transform = target_transform
def __len__(self):
return len(self.img_labels)
def __getitem__(self, idx):
img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0])
image = read_image(img_path)
label = self.img_labels.iloc[idx, 1]
if self.transform:
image = self.transform(image)
if self.target_transform:
label = self.target_transform(label)
return image, label
from torch.utils.data import DataLoader
train_dataset = CustomDataset(train_data, train=True)
test_dataset = CustomDataset(test_data, train=False)
train_dataloader = DataLoader(
train_dataset,
batch_size=64,
shuffle=True,
num_workers=0,
drop_last=True)
test_dataloader = DataLoader(
test_dataset,
batch_size=64,
shuffle=False,
num_workers=0,
drop_last=False)
아래 코드와 같이 직접 neural network를 구성해서 model을 정의할 수도 있고, torchvision, timm 등의 라이브러리를 이용해서도 model을 정의할 수 있다.
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = torch.flatten(x, 1) # flatten all dimensions except batch
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
import torch.nn as nn
loss_fn = nn.CrossEntropyLoss(
weight = None, # 각 클래스에 대한 가중치를 조절하여 클래스 불균형을 다루는데 도움
ignore_index = -100, # 지정한 인덱스에 해당하는 label은 손실 계산에서 제외
reduction = 'mean', # batch 크기로 계산된 손실값을 어떻게 처리할지 결정. mean으로 되어있으면 모든 batch의 손실값들의 평균하여 하나의 값으로 반환.
label_smoothing=0.0) # label의 값을 0 -> 0.1, 1 -> 0.9 와 같이 완화하여 모델이 너무 확신하지 않도록 조정. 과적합 방지에 도움됨.
loss = loss_fn(predicts, labels)
import torch.optim as optim
optimizer = optim.Adam(
model.parameters(),
lr=0.001,
betas=(0.9, 0.999),
weight_decay=0.01)
# train for one epoch
def train_epoch(self) -> float:
self.model.train()
total_loss = 0.0
progress_bar = tqdm(self.train_loader, desc="Training", leave=False)
for images, targets in proegress_bar:
images, targets = images.to(self.device), targets.to(self.device)
self.optimizer.zero_grad()
outputs = self.model(images)
loss = self.loss_fn(outputs, targets)
loss.backward()
self.optimizer.step()
self.scheduler.step()
total_loss += loss.item()
progress_bar.set_postfix(loss=loss.item())
return total_loss / len(self.train_loader)
위의 코드는 한 epoch의 training이고, 이 training을 적절히 반복하여 모델을 학습시켜야 한다. epoch 수에 따라 training과 validation을 반복적으로 수행한다.
def train(self) -> None:
for epoch in range(self.epochs):
train_loss = self.train_epoch()
val_loss = self.validate()
self.save_model(epoch, val_loss)
self.scheduler.step()
✯ step과 epoch
def validate(self) -> float:
self.model.eval()
total_loss = 0.0
progress_bar = tqdm(self.val_loader, desc='Validating', leave=False)
with torch.no_grad():
for images, targets in progress_bar:
images, targets = images.to(self.device), targets.to(self.device)
outputs = self.model(images)
loss = self.loss_fn(outputs, targets)
total_loss += loss.item()
progress_bar.set_postfix(loss=loss.item())
return total_loss / len(self.val_loader)
https://pytorch.org/tutorials/beginner/basics/data_tutorial.html
https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html
https://gaussian37.github.io/dl-pytorch-lr_scheduler/#steplr-1
https://sanghyu.tistory.com/113