김복칠 2024. 3. 26. 21:37
optimizer = optim.Adam(model.fc.parameters(), lr = 0.001)

epochs = 10

for epoch in range(epochs + 1):
  sum_loss = 0
  sum_acc = 0
  for x_batch, y_batch in dataloaders['train']:
    x_batch, y_batch = x_batch.to(device), y_batch.to(device)
    y_pred = model(x_batch)
    loss = nn.BCELoss()(y_pred, y_batch)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    sum_loss = sum_loss + loss.item()

    y_bool = (y_pred >= 0.5).float()
    acc = (y_batch == y_bool).float().sum() / len(y_batch) * 100

    sum_acc = sum_acc + acc.item()

  avg_loss = sum_loss / len(dataloaders['train'])
  avg_acc = sum_acc / len(dataloaders['train'])

  print(f'Epoch {epoch:4d}/{epochs} Loss: {avg_loss:.6f} Accuracy: {avg_acc:.2f}%')
  
# 결과
Epoch    0/10 Loss: 0.461126 Accuracy: 76.54%
Epoch    1/10 Loss: 0.289575 Accuracy: 86.23%
Epoch    2/10 Loss: 0.243776 Accuracy: 90.22%
Epoch    3/10 Loss: 0.192780 Accuracy: 92.20%
Epoch    4/10 Loss: 0.184669 Accuracy: 93.04%
Epoch    5/10 Loss: 0.145799 Accuracy: 95.03%
Epoch    6/10 Loss: 0.184113 Accuracy: 92.57%
Epoch    7/10 Loss: 0.168040 Accuracy: 92.90%
Epoch    8/10 Loss: 0.135855 Accuracy: 95.03%
Epoch    9/10 Loss: 0.147568 Accuracy: 94.25%
Epoch   10/10 Loss: 0.121778 Accuracy: 95.74%

1. 에일리언 vs 프레데터 데이터셋

 

Alien vs. Predator images

Small image classification - for transfer learning

www.kaggle.com

  • 케글 로그인 -> 우측 상단의 계정을 클릭 -> Your Profile -> 중앙에 Account를 클릭 -> API 항목에 Create New API Token -> kaggle.json이 다운로드 됨
  • {"username":"sakongmyoungheun","key":"eebb8c6baa890a589e08e51889cc3832"}
  • 이번에는 기존에 존재하는 모델에 전이 학습을 진행하기 위해 kaggle에서 데이터를 불러와서 진행해보겠습니다
import os

os.environ['KAGGLE_USERNAME'] = 'sakongmyoungheun'
os.environ['KAGGLE_KEY'] = 'eebb8c6baa890a589e08e51889cc3832'
!kaggle datasets download -d pmigdal/alien-vs-predator-images
!unzip -q alien-vs-predator-images.zip

import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
from torchvision import datasets, models,transforms
from torch.utils.data import DataLoader
device = 'cuda' if torch.cuda.is_available() else 'cpu'
  • kaggle에서 데이터를 불러오기 위해 kaggle_key를 발급받아 명령어로 데이터를 불러오기 위한 호출을 해줍니다
  • 이후 학습에 필요한 모듈(?)을 import 해줍니다

2. 이미지 증강 기법(Image Augmentation)

# 이미지 긍강 기법
data_transforms = {
    'train': transforms.Compose([
        # 크기 재조정
        transforms.Resize((224, 224)),
        # 각도, 찌그러뜨림, 크기,
        transforms.RandomAffine(0, shear = 10, scale = (0.8, 1.2)),
        # RandomHorizontalFlip: 수평으로 뒤집기
        transforms.RandomHorizontalFlip(),
        # tensor로 전환
        transforms.ToTensor()
    ]),
    'validation': transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor()
    ])
}
  • 먼저 데이터를 동일한 조건으로 변경하는 transforms를 만들어 줍니다
  • 이 data_transforms 딕셔너리는 데이터의 크기, 각도, 기울어짐 정도를 동일하게 하는 역할을 합니다
def target_transforms(target):
    return torch.FloatTensor([target])
    
image_datasets = {
    'train': datasets.ImageFolder('data/train', data_transforms['train'], target_transform=target_transforms),
    'validation': datasets.ImageFolder('data/validation', data_transforms['validation'], target_transform=target_transforms)
}

dataloaders = {
    'train':DataLoader(
        image_datasets['train'],
        batch_size=32,
        shuffle=True
    ),
    'validation':DataLoader(
        image_datasets['validation'],
        batch_size=32,
        shuffle=True
    )
}
  • 그리고 transforms에 맞는 데이터를 적용시켜줍니다
  • 마찬가지로 딕셔너리로 구성을 해서 train과 test를 나누어 torch 형태로 바꾸어 줍니다
  • 그리고 나누어진 데이터를 dataloader를 만들어서 학습시킬 batch_size를 설정해 줍니다

3. 전이 학습(Transfer Learning)

  • 하나의 작업을 위해 훈련된 모델을 유사 작업 수행 모델의 시작점으로 활용하는 딥러닝 접근법
  • 신경망은 처음부터 새로 학습하는 것보다 전이 학습을 통해 업데이트하고 재학습하는 편이 더 빠르고 간편함
  • 전이 학습은 여러 응용 분야(검출, 영상 인식, 음성 인식, 검색 분야)에서 많이 사용됨

3-1. 전이 학습의 고려할 점

  • 크기 : 모델 크기는 배포할 위치와 방법에 따라 달라짐
  • 속도 및 정확도 : 하드웨어, 배치 크기와 같은 요소를 고려

3-2. 사전 학습된 ResNet50 모델 사용하기

4. Freeze Layers

    • 특징을 뽑아내는 CNN의 앞쪽 컨볼루션 레이어들은 학습을 하지 않도록 설정
    • 출력 부분의 레이어(fc)를 다시 설정하여 분류에 맞게 변경
model = models.resnet50(weights='IMAGENET1K_V1').to(device)

for param in model.parameters():
    param.requires_grad = False # 가져온 파라미터 (W,b)를 업데이트하지 않음

# 기존의 모델을 우리의 입맞에 맞게 바꾸는 과정
model.fc = nn.Sequential(
    nn.Linear(2048, 128),
    nn.ReLU(),
    nn.Linear(128,1),
    nn.Sigmoid()
).to(device)
  • 먼저 resent50을 불러옵니다
  • 이후 학습하는 과정에서 모델이 최신화 될 수 있기 때문에 파라미터는 업데이트 되지 않도록 해줍니다
  • 그리고 출력되는 과정을 ReLU와 Sigmoid 함수를 이용해 레이어를 추가해서 완성해 줍니다
# train
optimizer = optim.Adam(model.fc.parameters(), lr = 0.001)

epochs = 10

for epoch in range(epochs + 1):
  sum_loss = 0
  sum_acc = 0
  for x_batch, y_batch in dataloaders['train']:
    x_batch, y_batch = x_batch.to(device), y_batch.to(device)
    y_pred = model(x_batch)
    loss = nn.BCELoss()(y_pred, y_batch)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    sum_loss = sum_loss + loss.item()

    y_bool = (y_pred >= 0.5).float()
    acc = (y_batch == y_bool).float().sum() / len(y_batch) * 100

    sum_acc = sum_acc + acc.item()

  avg_loss = sum_loss / len(dataloaders['train'])
  avg_acc = sum_acc / len(dataloaders['train'])

# test
model.eval()

optimizer = optim.Adam(model.fc.parameters(), lr = 0.001)

epochs = 10

for epoch in range(epochs + 1):
    sum_loss = 0
    sum_acc = 0
    for x,y in dataloaders['validation']:
        x = x.to(device)
        y = y.to(device)
        y_pred = model(x)
        loss = nn.BCELoss()(y_pred, y)
        sum_loss = sum_loss + loss.item()
        y_bool = (y_pred >= 0.5).float()
        acc = (y_bool == y).float().sum() / len(y) * 100
        sum_acc = sum_acc + acc.item()

    avg_loss = sum_loss / len(dataloaders['validation'])
    avg_acc = sum_acc / len(dataloaders['validation'])

 

  • 이제 만들어진 모델을 가지고 train과 test를 진행해 줍니다
  • loss와 accuracy 수치를 확인하고자 optimizer와 loss를 설정해 줍니다
  • 이렇게 학습까지 완료한 모델을 예측 분류작업을 해줍니다
# 테스트할 이미지
img1 = Image.open('./data/validation/alien/19.jpg')
img2 = Image.open('./data/validation/predator/20.jpg')

# transforms 진행
img1_input = data_transforms['validation'](img1)
img2_input = data_transforms['validation'](img2)

# 이미지를 분류하기 위한 설정 및 모델에 주입
test_batch = torch.stack([img1_input, img2_input])
test_batch = test_batch.to(device)
y_pred = model(test_batch)
y_pred

# 이미지 결과 출력
fig ,axes = plt.subplots(1,2,figsize=(12,6))
axes[0].set_title(f'{(1-y_pred[0,0])*100:.2f}% Alien, {(y_pred[0,0])*100:.2f}% Predator')
axes[0].imshow(img1)
axes[0].axis('off')

axes[1].set_title(f'{(1-y_pred[1,0])*100:.2f}% Alien, {(y_pred[1,0])*100:.2f}% Predator')
axes[1].imshow(img2)
axes[1].axis('off')

plt.show()

분류 예측 결과