본문 바로가기

딥러닝과 머신러닝

Transfer Learning, Image Agumentation (2024-06-24)

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

* 캐글 로그인 -> 우측 상단의 계정을 클릭 -> Settings-> Account -> API Create New API Token 클릭 -> Kaggle.json 파일이 다운로드 됨
* {"username":"dumbo3720","key":"31176eaeb35c4ac2457896d156107c55"}
 
import os
 
os.environ['KAGGLE_USERNAME'] = '본인 캐글 이름'
os.environ['KAGGLE_KEY'] = '본인 키'
 
# kaggle 데이터셋 우측 상단 ...에서 copy api command
!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
 

 

 

 

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

* 원본 이미지(데이터)를 조작하여 원본과는 크고 작은 변화를 가진 이미지를 생성하여 학습하는 기법
* 일반적으로 모델 성능이 좋아짐
* 오버피팅을 방지하는 목적
 
data_transforms = {
    'train' : transforms.Compose([
        transforms.Resize((224, 224)),
        # 0  각도 / shear 찌그러뜨림/ scale 크기 변형
        transforms.RandomAffine(0, shear = 10, scale = (0.8, 1.2)),

        # 좌우반전  = 수평으로 뒤집기
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor()
        ]),
     'validation' : transforms.Compose([
            transforms.Resize((224, 224)),
            transforms.ToTensor()
        ])
}
 
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 = False
    )
}
 
print(len(image_datasets['train']), len(image_datasets['validation'])) # 694 200
 
# imgs = (텐서) 데이터 / labels (레이블) 데이터의 정답 또는 타겟
imgs, labels = next(iter(dataloaders['train']))

# 4행 8열 사이즈 부여
fig, axes = plt.subplots(4, 8, figsize = (16, 8))

# axes.flatten(): 4x8 배열의 서브플롯 객체를 1차원 배열로 평탄화합니다. for 루프에서 서브플롯을 순회할 수 있습니다.
for ax, img, label in zip(axes.flatten(), imgs, labels):
  ax.imshow(img.permute(1, 2, 0))  # permute   데이터 형태를 바꿔줌 (3, 224, 224) -> (224, 224, 3)
                                   # 둘 다 224,224가 3개 있다는 뜻인데 형식이 다름
  ax.set_title(label.item())
  ax.axis('off')
 
 

 

 

 3. 전이 학습(Transfer Learning)

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

* Dataset1을 학습 후 가중치를 저장해두고  Dataset2를 학습할 때 사용
 
 

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

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

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

 
 

 3-3. ResNet50의 특징

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

 

 

 4. 이미지넷(ImageNet)

* 이미지넷은 이미지 데이터베이스
* 1000개의 클래스로 동물과 사물 이미지를 포함하고 있다
 

 

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

 

 5. Freeze Layers

* 특징을 뽑아내는 CNN의 앞쪽 컨볼루션 레이어들은 수정하지 않도록 설정
* 출력 부분의 레이어(FC)를 다시 설정하여 분류에 맞게 변경
 
for param in model.parameters():
  param.requires_grad= False # 가져온 파라미터를 업데이트 하지 않겠다
 
model.fc = nn.Sequential(
    nn.Linear(2048, 128),
    nn.ReLU(),
    nn.Linear(128, 1),
    nn.Sigmoid()
).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'] :
    if phase == 'train' :
      model.train()
    else:
      model.eval() # validation 일때는 gradient가 실행되지 않게

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

 

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

 

# img1,2 를 data_transforms의 validation(사이즈 바꾸고, to tensor함)에 넣어서 img1,2를 가르키게 만듬
img1_input = data_transforms['validation'](img1)
img2_input = data_transforms['validation'](img2)

print(img1_input.shape) # torch.Size([3, 224, 224]) 3개의 채널 224 크기
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]) 3 224 224 가 2개 있다
                             # test가 아닌 기존 batch는 [32, 3, 224, 224] 였다.
 
 
y_pred = model(test_batch)
y_pred
 
fig, axes = plt.subplots(1, 2, figsize = (12, 6))
axes[0].set_title(f'Alien {(1-y_pred[0, 0])*100:.2f}%, Prediator {(y_pred[0,0])*100:.2f}%')
axes[0].imshow(img1)
axes[0].axis('off')

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