① 학습 Task 선언
② 학습 방법 선정
③ 학습 설계
Dataset과 DataLoader
기본적인 torch Dataset 클래스를 상속받아 다양한 soruce로부터 데이터를 로드하고 전처리하느 방법을 정의
-> 필요에 따라 Custom
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): ## 주어진 인덱스에 해당하는 데이터를 학습에 적합한 형태로 변환하고 제공
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, #데이터를 불러오는데 사용하하는 작업자수(Cpu 코어)
drop_last = True #마지막 배치의 크기가 batch_size에서 정한 것보다 작을경우 버릴지 말지 결정.
)
test_dataloader = DataLoader(
test_dataset,
batch_size = 64,
shuffle =False,
num_workers = 0,
drop_last = False
)
class SimpleCNN(nn.Module):
def __init__(self, num_classes : int):
super(SimpleCNN, self).__init__() #nn.Module 상속받음 ->
self.conv1 = nn.Conv2d(3,32,kernel_size = 3, padding = 1)
self.conv2 = nn.Conv2d(32,64,kernel_size = 3, padding = 1)
self.conv3 = nn.Conv2d(64,128,kernel_size = 3, padding = 1)
self.pool = nn.MaxPool2d(2,2)
self.fc1 = nn.Linear(128*4*4,512)
self.fc2 = nn.Linear(512, num_classes)
self.relu = nn.ReLU()
def forward(self, x:torch.Tensor) -> torch.Tensor:
x = self.pool(self.relu(self.conv1(x))
x = self.pool(self.relu(self.conv2(x))
x = self.pool(self.relu(self.conv3(x))
x = torch.flatten(x,1)
x = self.relu(self.fc1(x))
x = self.fc2(x)
return x
손실함수(loss function)
모델의 현재 상태에서의 예측값과 실제 값 사이의 차이를 수치적으로 나타내는 함수, 이 손실값을 최소화 하는 방향으로 모델 파라미터 조정.
손실함수를 선택할 때는, 문제의 유형과 특성에 맞는 함수 선택해야함.
CrossEntropyLoss
- 다중 클래스 분류문제에 적합
import torch.nn as nn
loss_fn = nn.CrossEntopyLoss(
weight = None, # 각 클래스에 대한 가중치를 조절
ignore_index = -100, #label중 특정 인덱스를 무시하고 싶을때 사용(ex)패딩값 무시.)
reduction = "mean", #배치크기로 계산되어진 손실값 어떻게 다룰지
label_smoothing = 0.0 #label의 0,1 값을 완화아여 모델이 너무 확신하지 않도록 만듦.
)
loss = loss_fn(predicts, labels)
Custom Loss
- CrossEntropy Loss 외에도, 어떻게 학습을 하고 싶은지에 따라 Custom 한 Loss 구성
- 내가 하고자 하는 Task 에 맞는 데이터와 모델이 있을때, 어떻게ㅐ 학습할지에 따라 Loss 구성하고 선택.
Optimizer
- 모델의 학습 과정에서 손실함수를 최소화 하는 모델 파라미터를 찾는 과정을 자동화
import torch.optim as optim
model = CustomModel()
optimizer = optim.Adam(
model.parameter(), #학습가능한 모든 파라키터들을 리스트 형태로 반환
lr = 0.001,
betas = (0.9,0.999), #모멘텀으로 0.9는 1차 모멘텀 0.999는 2차 모멘텀
weight_decay = 0.01 #가중치 감소_L2규제.
)
🤔Adam이 optimizer 최고 일까?
SGD가 더 낫다고 하는 논문들이 있다.
① Adam vs. SGD: Closing the generalization gap on image classification
https://www.opt-ml.org/papers/2021/paper53.pdf
② SGD better
https://ar5iv.labs.arxiv.org/html/2010.05627
Learning Rate Scheduler
학습률은 모델의 파라미터 업데이트 크기 결정
너무 높은 학습률은 불안정하게 만들고 , 너무 낮은 학습률은 수렴속도를 느리게함.
스케쥴러(scheduler)는 초기에 높은 학습률을 설정하여 빠르게 학습하고, 이후 학습률을 낮춰 수렴과정을 안정화.
torch는 다양한 스케쥴러를 가지고 있음.
from torch.optim.lr_scheduler import (
StepLR,
ExponentialLR,
CosineAnnealingLR,
ReduceLROnPlateau
)
가장 많이 쓰는 위 4가지에 대해서 알아보자.
① StepLR
② ExponentialLR
③ CosineAnnealingLR
학습률을 Cosine 형태로 하고 초기에 높은 학습률 -> 낮은 학습률 ->다시 높은 학습률 로 loop를 돎.
여러 주기에 걸쳐 학습률 조정하면서 장기간에 걸쳐서 하고자 할대 사용
④ ReduceLROnPlateau
✍️ 학습률에 대해서 코드로 보고 싶으면 여기 들어가셈.
https://www.kaggle.com/code/isbhargav/guide-to-pytorch-learning-rate-scheduling
Training Loop
훈련 에폭 함수
def train_epoch(self)-> float:
self.model.train()
total_loss = 0.0
progress_bar = tqdm(self.train_loader , desc = "Training",leave =False) #desc(description) :진행바 앞에 설명 추가하는 옵션
#leave : 진행바가 완료된 후에도 화면에 남을지 여부,
for images, targets in progress_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()) #set_postfix는 진행바 끝에 추가적인 정보 보여줌. , .item()하면 tensor값을 파이썬 float 값으로 바꿔줌.
return total_loss / len(self.train_loader)
검증함수(Validate)
def validate(self) ->float:
self.model.eval() #모델을 평가모드로 설정
total_loss = 0.0
progress_bar = tqdm(self.val_loader, desc ="Validation", leave = False)
with torch.no_grad(): #model forward간에 발생하는 gradient 계산 비활성화.
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)
전체 훈련 과정 관리 함수
def train(self) -> None:
for epoch in range(self.epochs):
print(f"Epoch {epoch+1}/{self.epochs}")
train_loss =self.train_epoch()
val_loss = self.validate()
print(
f"Epoch {epoch+1}",
f"Train_loss : {train_loss:.4f}",
f"Validate_loss : {val_loss:.4f}
)
self.save_model(epoch, val_loss)
self.scheduler.step()
코드 같은 경우는 어떠한 기능을 하는 코드인지, 어떠한 flow 인지 이해하고 넘어가면 됨. 굳이 하나하나 외우지 마셈..!
Training with pytorch
https://pytorch.org/tutorials/beginner/introyt/trainingyt.html