1. 전이 학습(Transfer Learning)이란?
일반적으로 CNN 기반의 딥러닝 모델을 제대로 훈련시키려면 많은 양의 데이터가 필요하다. 하지만 큰 데이터셋을 얻는 것은 현실적으로 어렵기 때문에 아주 큰 데이터 셋을 써서 훈련된 모델의 Parameter를 가져와 해결하려는 과제에 맞게 보정해서 사용하는 *Transfer Learning*을 진행하여 현실적인 어려움을 해결한다.
이 때 아주 큰 데이터셋을 사용하여 훈련된 모델을 Pre-trained model이라 부른다.
전이 학습을 위한 방법으로는 특성 추출(Feature Extraction)과 미세 조정(Fine - Tuning) 기법이 있다.
2. 특성 추출 기법(Feature Extraction)이란?
ImageNet 데이터셋으로 Pre-trained model을 가져온 후 마지막에 완전연결층 부분만 새로 만든다.
즉, 학습할 때는 마지막 Fully Connected Layer(이미지의 Class를 결정하는 부분)만 학습하고 나머지 계층(Convolution Layer)들은 학습되지 않도록 한다(Freeze).
즉, Pre-trained Model의 합성곱층(가중치 고정, Freeze)에 새로운 데이터를 통과시키고, 그 출력을 데이터 분류기에서 훈련시킨다.
- Convolution Layer : 합성곱층과 풀링층으로 구성
- Fully Connected Layer : 추출된 특성을 입력받아 최종적으로 이미지에 대한 클래스를 분류하는 부분
사용 가능한 이미지 분류 모델은 아래와 같습니다.
- Xception
- Inception V3
- ResNet 50
- VGG16
- VGG19
- MobileNet
- Resnet18, Alexnet 등등
3. ResNet18 PyTorch 예제
사용 데이터 : https://www.kaggle.com/c/dogs-vs-cats/data
1) 필요한 라이브러리 호출
import numpy as np
import os
import time
import copy
import glob
# OpenCV 라이브러리
# Open Sourch Computer Vision Library
import cv2
import shutil
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
import torchvision
import torchvision.transforms as transforms
import torchvision.models as models
import matplotlib.pyplot as plt
2) 이미지 데이터 전처리 방법 정의
data_path = '../data/catanddog/train'
## Resize, RandomResizeCrop
# Resize : 합성곱층을 통과하기 위해 이미지 크기를 조정하는 전처리 과정(합성곱층을 통과하기 위해 이미지 크기 조정하는 용도
# RandomResizeCrop : 이미지를 랜덤한 크기 및 비율로 자른다.(데이터 확장 용도)
# RandomHorizontalFilp : 이미지를 수평으로 뒤집는다.
transform = transforms.Compose([
transforms.Resize([256, 256]),
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor()
])
# DataLoader가 데이터를 불러온 대상(혹은 경로)과 방법(혹은 전처리)를 정의
train_dataset = torchvision.datasets.ImageFolder(
data_path,
transform=transform
)
train_loader = DataLoader(
train_dataset,
batch_size=32,
num_workers=8,
shuffle=True
)
3) 학습에 사용될 이미지 출력
train_iter = iter(train_loader)
samples, labels = next(train_iter)
classes = {0:'cat', 1:'dog'}
fig = plt.figure(figsize=(16, 24))
for i in range(24):
a = fig.add_subplot(4, 6, i+1)
a.set_title(classes[labels[i].item()])
a.axis('off')
a.imshow(np.transpose(samples[i].numpy(), (1,2,0)))
plt.subplots_adjust(bottom=0.2, top=0.6, hspace=0)
4) Pre - Trained Model 내려 받기 & Parameter 학습 유무 지정 & Fully Connected Layer 수정
# 사전 훈련된 Model로 사용하기 위한 Hyper Parameter 주기
resnet18 = models.resnet18(pretrained=True)
# 역전파 중 파라미터들에 대한 변화를 계산할 필요가 없음을 정의하는 함수
# 이때, 모델의 일부(합성곱층과 풀링층)를 고정하고 나머지를 학습
for param in resnet18.parameters():
param.requires_grad = False
# Fully Connected Layer 재정의
resnet18.fc = nn.Linear(512, 2)
# Fully Connected Layer는 학습
for param in resnet18.fc.parameters():
param.requires_grad = True
※ ResNet 18
50개 계층으로 구성된 합성곱 신경망이다.
ImageNet 데이터베이스의 100만개가 넘는 영상을 이용하여 훈련된 신경망으로 전이 학습에 사용되도록 Pre-trained model을 제공한다.
하지만 ResNet18은 입력 제약이 매우 크고, 충분한 메모리(RAM)이 없으면 학습 속도가 느리다.
5) Opimizer 및 Loss Function 정의
optimizer = optim.Adam(resnet18.fc.parameters())
criterion = nn.CrossEntropyLoss()
6) Model 학습 함수 정의
def train_model(model, dataloaders, criterion, optimizer, device, num_epochs=13, is_trained=True):
since = time.time()
acc_history = []
loss_history = []
best_acc = 0.0
for epoch in range(num_epochs):
print("Epoch {}/{}".format(epoch, num_epochs-1))
print('-'*10)
running_loss = 0.0
running_corrects = 0
for inputs, labels in dataloaders:
inputs, labels, model = inputs.to(device), labels.to(device), model.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
_, preds = torch.max(outputs, dim=1)
loss.backward()
optimizer.step()
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(labels.data == preds)
epoch_loss = running_loss / len(dataloaders.dataset)
epoch_acc = running_corrects.double()/ len(dataloaders.dataset)
print("Loss : {:.4f} Acc : {:.4f}".format(epoch_loss, epoch_acc))
if epoch_acc > best_acc:
best_acc = epoch_acc
loss_history.append(epoch_loss)
acc_history.append(epoch_acc)
torch.save(model.state_dict(), os.path.join('../data/catanddog', '{0:0=2d}.pth'.format(epoch))) # 모델 재사용을 위해 저장
time_elapsed = time.time() - since
print("Training complete in {:.0f}m {:.0f}s".format(time_elapsed//60, time_elapsed%60))
print("Best Acc : {:.4f}".format(best_acc))
return acc_history, loss_history
7) Model 학습
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
train_acc_hist, train_loss_hist = train_model(resnet18, train_loader, criterion, optimizer, device)
아래는 Train 과정에서의 결과입니다.
Accuracy 약 93%로 높은 성능을 보임을 알수 있습니다.
Epoch 0/12
----------
Loss : 0.6881 Acc : 0.6000
Epoch 1/12
----------
Loss : 0.4499 Acc : 0.8052
Epoch 2/12
----------
Loss : 0.3570 Acc : 0.8494
Epoch 3/12
----------
Loss : 0.3410 Acc : 0.8623
Epoch 4/12
----------
Loss : 0.2660 Acc : 0.8753
Epoch 5/12
----------
Loss : 0.2472 Acc : 0.9091
Epoch 6/12
----------
Loss : 0.2754 Acc : 0.8753
Epoch 7/12
----------
Loss : 0.2218 Acc : 0.9195
Epoch 8/12
----------
Loss : 0.2224 Acc : 0.8961
Epoch 9/12
----------
Loss : 0.2130 Acc : 0.9247
Epoch 10/12
----------
Loss : 0.2479 Acc : 0.8805
Epoch 11/12
----------
Loss : 0.2562 Acc : 0.8857
Epoch 12/12
----------
Loss : 0.1770 Acc : 0.9273
Training complete in 7m 13s
Best Acc : 0.9273
8) 테스트 데이터 호출 및 전처리
import torch.utils
import torch.utils.data
test_path = '../data/catanddog/test'
transform = transforms.Compose([
transforms.Resize(224),
transforms.CenterCrop(224),
transforms.ToTensor()
])
test_dataset = torchvision.datasets.ImageFolder(
root=test_path,
transform=transform
)
test_loader = torch.utils.data.DataLoader(
test_dataset,
batch_size=32,
num_workers=1,
shuffle=True
)
9) 테스트 함수 정의
def eval_model(model, dataloader, device):
since = time.time()
acc_history = []
best_acc = 0.0
saved_models = glob.glob('../data/catanddog/'+'*.pth')
saved_models.sort()
print('Saved Model', saved_models)
for model_path in saved_models:
print("Loading Model", model_path)
model.load_state_dict(torch.load(model_path))
model.eval()
model.to(device)
running_corrects = 0
for inputs, labels in dataloader:
inputs, labels = inputs.to(device), labels.to(device)
with torch.no_grad():
outputs = model(inputs)
_, preds = torch.max(outputs.data, 1)
preds[preds>=0.5] = 1
preds[preds<0.5] = 0
running_corrects += preds.eq(labels.cpu()).int().sum()
epoch_acc = running_corrects.double() / len(dataloader.dataset)
acc_history.append(epoch_acc.item())
print("Acc : {:.4f}".format(epoch_acc))
if epoch_acc > best_acc:
best_acc = epoch_acc
time_elapsed = time.time() - since
print("Validation complete in {:.0f}m {:.0f}s".format(time_elapsed//60, time_elapsed%60))
print("Best Acc {:.4f}".format(best_acc))
return acc_history
10) 모델 Test
val_acc_hist = eval_model(resnet18, test_loader, device)
Test 결과 확인해보시면 Accuracy 약 94% 정도로 높은 정확도를 보인다.
결론적으로 모델의 네트워크를 일일이 개발하고 최적의 파라미터 값을 찾는다면 꽤 오랜 시간이 소요되겠지만 Pre Trained Model을 사용하면 쉽게 model을 학습시킬 수 있다.
'DL > CNN' 카테고리의 다른 글
[DL][CNN] LeNet-5 (1) | 2025.01.15 |
---|---|
[DL][CNN] 설명 가능한 AI (Explainable Artificial Intelligence, XAI)와 Feature Map 시각화, PyTorch 예제 (1) | 2025.01.15 |
[DL][CNN]전이학습의 미세조정 기법(Fine - Tuning) 개념 및 PyTorch 예제 (0) | 2025.01.14 |
[DL][CNN] CNN(Convolution Neural Network) PyTorch 예제 (0) | 2024.08.15 |
[DL][CNN] Convolution Neural Network(CNN) (1) | 2024.08.01 |