본문 바로가기
AI 컴퓨터 비전프로젝트/[ML,DL]머신러닝,딥러닝

[ML/DL] 전이학습, 케글 데이터셋 활용해서 에일리언vs프레데터 예측하기

by 바다의 공간 2024. 7. 30.

1. 전이 학습(Transfer Learning)

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


2. 케글 데이터셋

  • 케글에 로그인
  • 우측 상단의 계정을 클릭 -> Your Profile -> 우측의 Setting 클릭 -> Account 에서 하단 API의 Create New API Token을 클릭 -> Kaggle.json 파일이 다운로드 됨
  • kaggle.json 파일을 메모장으로 열기
  •  {"username":"아이디","key":"내 고유 키 번호"}
import os
os.environ['KAGGLE_USERNAME'] = '아이디'
os.environ['KAGGLE_KEY'] = '고유 키 번호'

이렇게 하게 되면 나의 케글의 아이디와 나의 고유 키 번호를 가져와서 연동같이 됩니다.

활용할 수 있는 데이터셋이 굉장히 많습니다.

오늘은 

에일리언 프레데터 데이터셋을 비교해려고 합니다.

 


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

 

리디렉션 알림

 

www.google.com

 

#케글데이터셋 다운로드 주소 가져오는방법.

!kaggle datasets download -d pmigdal/alien-vs-predator-images

1.주소복사

2. ! 써주고 점세개 -> copy API command 클릭 붙여넣

 

이렇게 해도 똑같이 코드가 작성되게 됩니다.

단점은 실행할때마다 다시 실행해야 한다는 단점이 있습니다.

주피터 노트북을 사용한다면 한번 다운로드받으면 바로 사용이 가능합니다.

 

 

#압축 풀기

!unzip -q 압축파일경로

!unzip -q /content/alien-vs-predator-images.zip

구글드라이브 기준 이렇게 압축 된 파일을 다운로드 받았고, 파일 경로를 복사해서 붙여넣으면 됩니다.


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

  • 원본 이미지(데이터)를 조작하여 원본과는 크고 작은 변화를 가진 이미지를 생성하여 학습하는 기법
  • 일반적으로 모델 성능이 좋아짐
  • 오버피팅을 방지하는 목적
  • 이미지 증강 기법             
  • 이미지를 더 많이 구할 수 없을때 사용하는 기법 입니다.

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

 

필요한 모듈들을 임포트해준다음에 함수를 구성하려고 합니다.

*shear을 하게되면 10도정도로 찌그러뜨린다 라는 뜻 입니다.

data_transforms = {
    'train': transforms.Compose([
        transforms.Resize((224, 224)),
        # 각도, 크기변경, 찌그러뜨림
        transforms.RandomAffine(30, scale=(0.8, 1.2), shear=10),
        # 수평으로 뒤집기
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor()
    ]),
    'validation': transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor()
    ])
}

 

*전이학습을 시킬 수 있는 모델을 사용하려면 Resize를 하려면 (224,224)를 하면 됩니다.

[레스넷50] 모델을 사용하려고 하면 이미지 사이즈 고정되어있어서 224,224로 했습니다.

즉 모델마다 넣을 수 있는 이미지의 크기가 정해져있기때문에 확인해야합니다.

 

### 코드해석

data_transforms을 들리게되면

** train으로 갈 때에는 

1. 이미지 224, 224크기로 변경해주고

2. 각도 , 크기변경 찌그러뜨림

3. 수평으로 뒤집기

4. 텐서형으로 변경

 

** validation(검증)으로 갈 때에는 

1. 사이즈 변경 224, 224

2. 텐서형 변경

 

을 하게 됩니다.

 

그 다음 이미지를 넣거나 특정 값을 넣어주면 토치텐서.플로트형으로 변경해주는 함수를 만들어주려고합니다.

def target_transforms(target):
    return torch.FloatTensor([target])

 

이제 데이터셋을 만들려고 합니다.

 

순서는 비슷합니다.

1. 전처리

2. 데이터셋 만들기

3. 데이터로더만들기

4. 모델넣어서 결과확인

 

지금 데이터셋은 2개로 나누려고합니다 (1.train. 2.validation)

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

 

###코드해석

train : 경로가 data/train 에서 이미지를 갈거올거고, 데이터를 가져와서 data_tranforms 함수 中 train을 거쳐서 고오 또 실수형으로 변경해줄 수 있는 함수에 들러서 오는겁니다(ex.디타입..)

validation도 마찬가지로 이미지를 가져오고 data_transforms의 validation 을 거쳐서오고 실수형(실수화)으로 변경해서 옵니다.

 

 

그다음 데이터 로더를 만들겠습니다. 이 부분도 train 과 valid를 나눠서 하겠습니다.

dataloaders = {
    'train': DataLoader(
        image_datasets['train'],
        batch_size=32,
        shuffle=True
    ),
    'validation': DataLoader(
        image_datasets['validation'],
        batch_size=32,
        shuffle=False
    )
}

dataloader의 형식은 보통  DataLoader(데이터셋, 미니배치 크기, shuffle) 파라미터를 입력해서 사용합니다.

len(image_datasets['train']), len(image_datasets['validation'])

데이터로더를 다 만든 후 이미지 개수를 보면 

트레인은 694개 validation 은 200개가 됩니다.

 

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

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

for ax, img, label in zip(axes.flatten(), imgs, labels):
    ax.imshow(img.permute(1, 2, 0)) # (3, 224, 224) -> (224, 224, 3)
    ax.set_title(label.item())
    ax.axis('off')

matplotlib으로 그려보면 

원래는 3, 224,224인데 

permute함수를 사용해서 224,224,3 으로 순서를 변경해주었습니다.

 

 

이렇게 에일리언 데이터들이 변형되어서 나오게 됩니다.


5. 사전 학습된 ResNet50 모델

 

5-1. ResNet50의 특징

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


5-2. 이미지넷(ImageNet)

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

말 그대로 1천개의 클래스로 구성되어있고 물론 클래스가 1000가지니까 이미지는 훨씬 더 많다는것을 확인할 수 있죠.

직접 이미지넷을 50모델에다 담고 이어서 진행하려고합니다.

device = 'cuda' if torch.cuda.is_available() else 'cpu'

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

GPU를 사용할 수 있기때문에 일단 코드등록해놓겠습니다.

IMAGENET1K_V1을 사용하면 됩니다.

여기서 보면 인풋 아웃풋 사이즈가 맞지않는다는것을 확인할 수 있습니다.

그래서 사이즈를 맞춰야한다는것도 확인해야합니다.


5-3. Freeze Layers

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

cnn쪽은 아예 학습하지않고 전이된 상태도 그대로 납두는것입니다.

모델에 파라미터개수만큼 반복하면서 파라미터를 업데이트 하지 않음

 

 

model.fc = nn.Sequential(
    nn.Linear(2048, 128),
    nn.ReLU(),
    nn.Linear(128, 1),
    nn.Sigmoid()
).to(device)

일단 FC를 찾고 SEQUENTIAL 로 덮어씌웁니다.

리니어, 렐루, 다시 레이어 쌓으면서 linear 그다음 끝내기 위해서 sigmoid를 사용해서 끝내줍니다.

아까와 다르게 model을 찍어을때 아웃값이 1로 되는것을 확인할 수 있습니다. 이제 사이즈가 맞는거죠

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

전달하는 학습은 model 파라미터가 아니라 (프리즈 시켰기에) model.fc만 수정하면 됩니다.

Adam을 사용했습니다.

 

학습하도록 합니다.

epochs = 10

for epoch in range(epochs):
    for phase in ['train', 'validation']:
        if phase == 'train':
            model.train()
        else:
            model.eval()

        sum_losses = 0
        sum_accs = 0

        for x_batch, y_batch in dataloaders[phase]:
            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}%')

train, validation이 각각 phase에 들어가서 2바퀴를 돌고 2번째 for문은 20만큼돌게됩니다.

 

여기서 벨리데이션을 확인을 해야되는데 87%정도가 나오게 됩니다.

from PIL import Image

이미지를 가져오거나 볼 수 있는 모듈입니다.

img1 = Image.open('/content/data/validation/alien/19.jpg')
img2 = Image.open('/content/data/validation/predator/21.jpg')

img1 ,2 를 지정해 줍니다.

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

이미지를 보여줍니다.

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

3,224,224로 리사이징해주고 tensor으로 변경해줍니다. 그 값을 각각 img1_input, img2_input에 넣어줍니다.

 

차원이 224,224가 3가 있기때문에 

이미지를 2개로 묶어주고 싶으니 stack이라는 함수를 통해서 묶어줄 수 있습니다.

test_batch = torch.stack([img1_input, img2_input])
test_batch = test_batch.to(device)
test_batch.shape

 

이렇게 하면 이미지1,2가 하나로 묶입니다.

결과값은

torch.Size([2, 3, 224, 224])
으로 나오게 됩니다. 지금은 이미지 2개를 묶을 수 있는거예요.
 
y_pred = model(test_batch)
y_pred

묶어서 예측값을 뽑으면 

tensor([[0.0058],
        [0.9577]], device='cuda:0', grad_fn=<SigmoidBackward0>)

시그모이드를 통과한 확률이 나오게 됩니다.

1번째는 1번째이미지 확률 2번째 확률

0.5보다 작으면 에일리언 0.5보다 크면 프리데터입니다.

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}% Prediator')
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}% Prediator')
axes[1].imshow(img2)
axes[1].axis('off')
 

다시 그림으로 나타나면 프레데터랑 에일리언 잘 맞춘듯하죠~~

 

제얼굴 넣어서 해봤는데 Prediator이 나오네요