본문 바로가기
머신러닝 & 딥러닝

21. 전이 학습

by 곽정우 2024. 6. 17.

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

import os

os.environ['KAGGLE_USERNAME'] = 'kwak0318 '
os.environ['KAGGLE_KEY'] = 'dcb13dc724efcf65a915a5d5e8b1d44e'
!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'
print(device)

 

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

 

Transforming and augmenting images — Torchvision main documentation

Shortcuts

pytorch.org

# Compose(): 여러개의 함수를 한꺼번에 적용하는 함수(여러 이미지 변환 함수들을 연속적으로 적용)
data_transforms = {
    'train':transforms.Compose([
        transforms.Resize([224, 224]),

        # 이미지를 무작위로 변형하여 데이터 다양성을 증대시킵니다.
        # 각도 조정, 비율 변경, 크기 조정을 통해 이미지를 변환합니다.
        transforms.RandomAffine(0, shear=10, scale=(0.8 , 1.2)),

        # 이미지를 무작위로 좌우 반전하여 변형합니다.
        transforms.RandomHorizontalFlip(),  
        
        # 이미지를 텐서(tensor) 형식으로 변환합니다.
        transforms.ToTensor()  
    ]),
    # 검증용 이미지 변환
    'validation': transforms.Compose([
        # 이미지 크기를 224x224로 조정합니다.
        transforms.Resize([224, 224]),  
        
        # 이미지를 텐서(tensor) 형식으로 변환합니다.
        transforms.ToTensor()           
    ])
}

# 데이터 전처리를 위한 변환을 정의합니다.
# data_transforms 딕셔너리는 학습 및 검증 데이터를 일관된 형태로 맞추기 위해 크기 조정, 각도 조정, 기울임, 좌우 반전 등의 변환을 적용합니다.
# 타겟 데이터를 받아 텐서형(자료형)으로 변환하는 함수 정의
def target_transforms(target):
    return torch.FloatTensor([target])

# ImageFolder(): 특정 이미지 폴더를 데이터셋으로 변환합니다.
# 인자: (데이터 경로, 데이터셋에 대해 적용할 이미지 변환(transform) 함수, 'train' 데이터셋의 타겟(레이블)에 대해 적용할 변환 함수)
# 데이터셋 생성
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,  # 배치 사이즈를 32로 설정
        shuffle=True    # 데이터를 섞어서 로드
    ),
    'validation': DataLoader(
        image_datasets['validation'],
        batch_size=32,  # 배치 사이즈를 32로 설정
        shuffle=True    # 데이터를 섞어서 로드
    )
}

# 이미지 변환(transforms)을 데이터에 적용합니다.
# train과 validation 데이터를 딕셔너리로 분리하고 torch 텐서 형태로 변환합니다.
# 그 후, 분리된 데이터를 dataloader로 만들어 배치 크기를 설정하여 학습시킵니다.
print(len(image_datasets['train']), len(image_datasets['validation']))

imgs, labels = next(iter(dataloaders['train']))

fig, axes = plt.subplots(4, 8, figsize=(20, 10))

for img, label, ax in zip(imgs, labels, axes.flatten()):
  ax.set_title(label.item())
  
  # 텐서의 차원을 (채널, 높이, 너비)에서 (높이, 너비, 채널)로 변경하여 matplotlib에서 이미지를 표시합니다.
  ax.imshow(img.permute(1, 2, 0))  
  ax.axis('off')

 

3. 전이 학습(Transfer Learning)

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

머신러닝과 전이학습과의 비교

 

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

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

 

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

파이토치에서 제공하는 사전학습 모델들  (https://pytorch.org/vision/stable/models.html)

ResNet50 모델의 Layer 구성


3-3. ResNet50의 특징

  • 네트워크가 이전 레이어의 출력을 다음 레이어로 직접 전달하는 '스킵 연결'을 통해 학습을 진행하는 방식
  • 기울기 소실 문제를 해결하고 훨씬 더 깊은 네트워크를 효과적으로 학습시킬 수 있음
  • 50개의 레이어로 구성되어 있으며 주로 Conv(컨볼루션) 레이어와 Batch Normalization, ReLU 활성화 함수, 풀링 레이어 등으로 이루어져 있음
  • 이미지 분류, 객체 탐지 등 다양한 컴퓨터 비전 작업에서 높은 성능을 보임

 

4. 이미지넷(ImageNet)

  • 이미지 데이터베이스
  • 1000개의 클래스로 동물과 사물 이미지를 포함

# 파이토치에서 제공하는 model가져와 미리 학습된 모델을 사용하기

model = models.resnet50(weights='IMAGENET1K_V1').to(device)
print(model)

 

5. Freeze Layers

  • 특징을 뽑아내는 CNN의 앞쪽 컨볼루션 레이어들은 수정하지 않도록 설정
  • 출력 부분의 레이어(fc 레이어)를 다시 설정하여 분류에 맞게 변경
for param in model.parameters():
  param.requires_grad = False  # 가져온 파라미터(W, b)를 업데이트 하지 않음

# 모델의 FC 레이어
model.fc = nn.Sequential(
    nn.Linear(2048, 128),  # 입력 크기가 2048이고 출력 크기가 128인 선형 레이어
    nn.ReLU(),
    nn.Linear(128,1),  # 128차원의 특성을 1차원으로 줄이고, 최종 예측 값을 출력
    nn.Sigmoid()       # 출력을 0과 1 사이로 제한하여 이진 분류 문제에서 확률 값
).to(device)

print(model)

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

epochs = 10

for epoch in range(epochs+1):
  for phase in ['train', 'validation']:  # train과  validation 따로 반복문을 돌아
    if phase == 'train':
      model.train()
    else:
      model.eval()   # 학습 모드에 있던 메모리를 지우고 바로 Test모드(훨씬 빠름)

    sum_losses = 0
    sum_accs = 0

    for x_batch, y_batch in dataloaders[phase]:  # train이라면 train에 대한 데이터로더, validataion이라면 validation에 대한 데이터로더 (따로 쓰지 않고 합쳐서 씀)
      x_batch = x_batch.to(device)
      y_batch = y_batch.to(device)

      y_pred = model(x_batch)

      loss = nn.BCELoss()(y_pred, y_batch)

      if phase == 'train':
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

      sum_losses = sum_losses + loss

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

    avg_loss = sum_losses / len(dataloaders[phase])
    avg_acc = sum_accs / len(dataloaders[phase])

    print(f'{phase:10s}: Epoch {epoch+1:4d}/{epochs}, Loss: {avg_loss:.4f}, Accuracy: {avg_acc:.2f}%')

from PIL import Image
img1 = Image.open('/content/data/validation/alien/21.jpg')
img2 = Image.open('/content/data/validation/predator/30.jpg')

fig, axes = plt.subplots(1, 2, figsize=(12, 6))

axes[0].imshow(img1)
axes[0].axis('off')
axes[1].imshow(img2)
axes[1].axis('off')
plt.show()

# data_transforms의 validation 키의 value 값에 img1을 통과시킴 ➡️ Resize되고 Tensor로 변환

img1_input = data_transforms['validation'](img1)  
img2_input = data_transforms['validation'](img2)
print(img1_input.shape)
print(img2_input.shape)

# 두 이미지를 하나의 이미지 텐서로 결합

test_batch = torch.stack([img1_input, img2_input])  # 두 이미지를 첫 번째 차원(배치 차원)으로 결합
test_batch = test_batch.to(device)
test_batch.shape   # 두 개 이미지가 붙음, torch.Size([2, 3, 224, 224]): (2(배치크기), 컬러채널, 세로, 가로)

# 모델을 통해 이진 분류 예측하기

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()

'머신러닝 & 딥러닝' 카테고리의 다른 글

22. 포켓몬 분류  (2) 2024.06.17
20. 간단한 CNN 모델 만들기  (0) 2024.06.17
19. CNN 기초  (0) 2024.06.13
18. 비선형 활성화 함수  (0) 2024.06.13
17. 딥러닝  (0) 2024.06.13