본문 바로가기
AI/딥러닝

09. 전이학습

by 사라리24 2024. 6. 21.
SMALL



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

 

 

더보기

 

  • 직접 다운로드 하기

 

  • 캐글 api 가져오기
    : 케글 로그인 -> 계정 클릭 -> Your Profile -> Account -> API항목에 Create New Token -> kaggle.json 다운로드
     

 

 

 

 

 

  • Kaggle API를 위한 환경변수 설정

 
        import os   
        os.environ['KAGGLE_USERNAME'] = 'username'
        os.environ['KAGGLE_KEY'] = 'key'


 

 

  • 에일리언 데이터셋을 다운로드하고 압축을 해제

       
        !kaggle datasets download -d pmigdal/alien-vs-predator-images
        !unzip -q alien-vs-predator-images.zip

 
Dataset URL: https://www.kaggle.com/datasets/pmigdal/alien-vs-predator-images
License(s): ODbL-1.0
Downloading alien-vs-predator-images.zip to /content
 35% 5.00M/14.1M [00:00<00:00, 28.3MB/s]
100% 14.1M/14.1M [00:00<00:00, 66.3MB/s]

 

 

  • import / GPU

       
        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 = torch.device ('cuda' if torch.cuda.is_available() else 'cpu')
        print(device)

 
cpu 

[런타임 유형 변경]
[런타임 다시 실행]
cuda

 

 

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


* 원본 이미지 (데이터)를 조작하여 원본과는 크고 작은 변화를 가진 이미지를 생성
  -일반적으로 모델 성능이 좋아짐
  - over fitting 방지

 

 

Transforming and augmenting images — Torchvision main documentation

Shortcuts

pytorch.org

 

 

  • 이미지 데이터를 전처리하고 이미지 로드


       
          data_transforms ={
              'train': transforms.Compose([
                  transforms.Resize((224,224)), # 이미지를 224x224 크기로 조정
                  transforms.RandomAffine(0, shear=10 , scale =(0.8,12)),
                  # 랜덤으로 각도, 찌그러뜨림, 크기 변환
                  transforms.RandomHorizontalFlip(), # 랜덤으로 좌우반전
                  transforms.ToTensor(),# 이미지를 PyTorch 텐서로 변환
              ]),

              'validation':transforms.Compose([
                  transforms.Resize((224,224)),
                  transforms.ToTensor()
              ])
          }

          # 목표 값 변환: target ---> PyTorch의 FloatTensor로 변환
          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']))



데이터 전처리

data_transforms 딕셔너리는 학습 데이터(train)와 검증 데이터(validation)에 대해 서로 다른 전처리 과정을 정의합니다.
transforms.Compose는 여러 전처리 과정을 순차적으로 적용할 수 있도록 합니다.
  • data_transforms는 학습과 검증 데이터를 위한 전처리 과정을 정의합니다.
    • 학습 데이터: 이미지 크기 조정, 랜덤 어파인 변환, 랜덤 좌우 반전, 텐서 변환.
    • 검증 데이터: 이미지 크기 조정, 텐서 변환.
  • target_transforms는 주어진 목표 값을 FloatTensor로 변환합니다.
이 설정을 통해 이미지 데이터를 모델에 입력하기 전에 일관된 크기와 형식으로 전처리할 수 있으며,
목표 값도 적절한 형식으로 변환할 수 있습니다.


데이터셋 로드

  • datasets.ImageFolder: 주어진 디렉토리 구조에서 이미지를 불러오기
    • 이미지가 저장된 디렉토리 경로
    • 이미지를 전처리하는 변환(transform) 함수
    • target_transform: 라벨(목표 값)을 변환하는 함수

  • DataLoader: 데이터셋을 배치 단위로 나누어 모델에 입력할 수 있도록 하는 PyTorch 클래스입니다 .
    • image_datasets['train']과 image_datasets['validation']을 각각 데이터 로더에 입력합니다.
    • batch_size=32: 배치 크기를 32로 설정합니다.
    • shuffle=True: 학습 데이터셋을 셔플하여 랜덤하게 배치를 만듭니다.
    • shuffle=False: 검증 데이터셋은 셔플하지 않습니다.

  • 데이터셋 크기 출력:
    • len(image_datasets['train'])와 len(image_datasets['validation'])을 사용하여
      학습 데이터셋과 검증 데이터셋의 이미지 개수를 출력합니다.
694 200

 

 



  • DataLoader를 사용하여 학습 데이터의 배치를 가져와서, 일부 이미지를 시각화

       
        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)
          ax.set_title(label.item())
          ax.axis('off')

 
  • 학습 데이터 배치 가져오기:
    • iter(dataloaders['train']): 학습 데이터 로더의 반복자를 생성합니다.
    • next(iter(dataloaders['train'])): 반복자에서 첫 번째 배치를 가져옵니다. 이 배치는 이미지와 라벨로 구성됩니다.
    • imgs: 배치에 포함된 이미지들입니다.
    • labels: 배치에 포함된 이미지들의 라벨입니다.
  • 플롯을 위한 서브플롯 설정:
    • plt.subplots(4, 8, figsize=(16, 8)): 4x8의 그리드(총 32개의 서브플롯)와 크기 16x8인 전체 플롯을 생성합니다.
    • fig: 전체 플롯을 나타내는 객체입니다.
    • axes: 개별 서브플롯을 나타내는 객체 배열입니다.
  • 이미지 시각화:
    • axes.flatten(): 4x8 그리드를 1차원 배열로 평탄화하여 반복문에서 순차적으로 접근할 수 있게 합니다.
    • img.permute(1, 2, 0): PyTorch 텐서는 (채널, 높이, 너비) 순서이므로, 이를 (높이, 너비, 채널) 순서로 변경하여 imshow 함수가 인식할 수 있도록 합니다.
    • ax.set_title(label.item()): 각 서브플롯에 해당 이미지의 라벨을 제목으로 설정합니다.
    • ax.axis('off'): 각 서브플롯의 축을 비활성화하여 표시하지 않도록 합니다.

 

 

3. 전이 학습 (Transfer Learning)


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

 

 

 

1.  전이학습의 고려할 점

- 크기 : 모델 크기의 중요성은 모델을 배포할 위치와 방법에 따라 달라짐
- 정확도: 재훈련 전의 모델 성능은 어느 정도인지 확인이 필요
- 예측 속도: 하드웨어 및 배치 크기와 같은 다른 딥러닝 요소는 물론 선택된 모델의 아키텍쳐와 모델 크기에 따라서도 달라짐


 

2.  사전 학습된 ResNet50 모델

- https://pytorch.org/vision/stable/models.html

 

 

 

4. 이미지넷(ImageNet)


* 이미지 데이터베이스
* 1000개의 동물과 사물 이미지를 포함

 

  • ResNet-50 모델을 불러와서 사용하기

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


  1. ResNet-50 모델 불러오기:
    • models.resnet50: PyTorch의 torchvision.models 모듈에서 제공하는 ResNet-50 모델을 불러옵니다.
    • weights='IMAGENET1K_V1': 불러올 가중치(weights)를 지정합니다. 여기서는 ImageNet 데이터셋에서 사전 학습된 가중치를 사용합니다.
    • .to(device): 모델을 GPU(device)로 이동합니다. device는 CUDA를 사용할 경우 GPU 디바이스를 지정하는 변수입니다.
  2. 모델 출력:
    • print(model): 불러온 ResNet-50 모델의 구조를 출력합니다.

출력된 모델 구조:

ResNet-50 모델은 다양한 계층(layer)으로 구성되어 있으며, 기본적으로는 합성곱(convolutional), 풀링(pooling), 완전 연결(fully connected) 레이어 등으로 구성됩니다. 
  • Convolutional Layers: 이미지 특징을 추출하는 합성곱 계층들.
  • Pooling Layers: 특성 맵의 크기를 줄이는 풀링 계층들.
  • Fully Connected Layers: 최종적으로 분류를 수행하기 위한 완전 연결 계층들.
  • Batch Normalization, ReLU Activation: 정규화와 활성화 함수 계층들.
  • Classifier: 최종 출력을 위한 분류기 계층.
ResNet-50은 50개의 계층을 가진 네트워크로, 매우 깊고 복잡한 구조를 가지고 있습니다. ImageNet 데이터셋에서 사전 학습된 가중치를 사용하므로, 이 모델은 이미지 분류나 전이 학습(transfer learning) 등에 사용될 수 있습니다.

 

 

 

5. Freeze Layers


* 특징을 뽑아내는 CNN의 앞쪽 컨볼루션 레이어들은 학습을 하지 않도록 설정
* 출력 부분의 레이어(fc)를 다시 설정하여 분류에 맞게 설정

 

  • ResNet-50 모델의 파라미터를 동결하고, 모델의 마지막 fully connected 레이어를 변경

       
          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)

          print(model)

 

 

 

  • ResNet-50 모델의 마지막 fully connected 레이어만을 학습하면서 이진 분류 작업을 수행

       
          # 학습: fc 부분만 학습하므로 속도가 빠름
          optimizer = optim.Adam(model.fc.parameters(), lr=0.001)

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


 
  1. 옵티마이저 설정:
    • optim.Adam: Adam 옵티마이저를 사용하여 모델의 파라미터를 최적화합니다.
    • model.fc.parameters(): 모델의 마지막 fully connected 레이어(model.fc)에 속한 파라미터들만을 최적화 대상으로 설정합니다.
    • lr=0.001: 학습률(learning rate)을 0.001로 설정합니다.
  2. 학습 반복 (Epoch):
    • epochs = 10: 총 10번의 에폭(epoch) 동안 학습을 반복합니다.
    • for epoch in range(epochs):: 각 에폭에 대해 반복합니다.
    • for phase in ['train', 'validation']:: 학습(train)과 검증(validation) 단계에 대해 반복합니다.
      • model.train(): 학습 모드로 모델을 설정합니다. 이는 드롭아웃(dropout)이나 배치 정규화(batch normalization)와 같은 학습 관련 기능들을 활성화합니다.
      • model.eval(): 평가 모드로 모델을 설정합니다. 이는 드롭아웃(dropout)이나 배치 정규화(batch normalization)와 같은 기능들을 평가 모드로 설정하여 평가할 때의 계산 결과를 일관되게 만듭니다.
    • for x_batch, y_batch in dataloaders[phase]:: 해당 단계(phase)의 데이터로더에서 배치를 반복하여 가져옵니다.
      • x_batch = x_batch.to(device), y_batch = y_batch.to(device): 데이터와 레이블을 GPU(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 += loss.item(), sum_accs += acc.item(): 배치의 손실과 정확도를 누적합니다.
    • avg_loss = sum_losses / len(dataloaders[phase]), avg_acc = sum_accs / len(dataloaders[phase]): 전체 단계(phase)에 대한 평균 손실과 정확도를 계산합니다.
    • print(...): 각 단계(phase)의 에폭, 평균 손실, 평균 정확도를 출력하여 모델의 학습 진행 상황을 모니터링합니다.

요약

이 코드는 ResNet-50 모델의 마지막 fully connected 레이어만을 학습하면서 이진 분류를 수행하는 과정을 보여줍니다. 학습과 검증 과정에서 손실과 정확도를 계산하고, Adam 옵티마이저를 사용하여 모델 파라미터를 업데이트합니다. 이를 통해 모델을 효율적으로 학습시키고 학습 진행 상황을 모니터링할 수 있습니다.

 

 

 

 

  • 테스트용 이미지 두 개를 불러와서 시각화

       
        # 테스트
        from PIL import Image
        img1 =Image.open('data/validation/alien/21.jpg')
        img2 =Image.open('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()


 

  • 이미지 데이터에 대해 정의된 데이터 변환을 적용
    변환된 이미지의 모양(shape)을 출력

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

        # 결과
        torch.Size([3, 224, 224])
        torch.Size([3, 224, 224])


torch.Size([3, 224, 224])
torch.Size([3, 224, 224])
torch.Size([3, 224, 224])

 

 

  • test_batch는 img1_input과 img2_input을 스택하여 하나의 텐서로 결합한 결과

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


torch.Size([2, 3, 224, 224])

torch.stack([img1_input, img2_input]): img1_input과 img2_input을 차원 0에 대해 스택하여 하나의 텐서로 결합합니다.
따라서 결과적으로 형상이 [2, 3, 224, 224]인 텐서가 생성됩니다.

  • 2: 배치 크기(batch size)가 2임을 의미합니다. 즉, 두 개의 이미지가 하나의 배치로 처리됩니다.
  • 3: 각 이미지는 RGB 채널을 가지고 있음을 나타냅니다.
  • 224, 224: 각 이미지의 높이와 너비가 224임을 나타냅니다.

 

  • test_batch를 입력으로 받아 모델이 예측한 출력값인 y_pred

       
          y_pred = model(test_batch)
          y_pred


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

  • tensor: PyTorch 텐서 객체입니다.
  • [[0.1966], [0.9433]]: 예측된 결과값을 나타내는 부분입니다.
    • 첫 번째 이미지에 대한 예측은 0.1966으로, 두 번째 이미지에 대한 예측은 0.9433으로 나타납니다.
    • 이 값은 각 이미지가 Predator일 확률을 나타내는 값입니다. 예를 들어, 첫 번째 이미지의 경우 Predator일 확률이 약 19.66%라는 의미입니다.
  • device='cuda:0': 텐서가 GPU(cuda) 메모리의 0번 장치에 있는 것을 나타냅니다. 즉, GPU 가속을 사용하여 연산이 수행되었습니다.
  • grad_fn=<SigmoidBackward0>: 이 텐서는 시그모이드 함수를 거쳐 생성된 결과임을 나타냅니다. 이는 출력에 대한 역전파(backpropagation)을 계산하는 데 사용됩니다.

 

 

  • 두 개의 이미지(img1과 img2)에 대해 모델이 예측한 결과를 시각화

       

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



  1. 서브플롯 설정:
    • plt.subplots(1, 2, figsize=(12, 6)): 1x2 격자 형태의 서브플롯을 생성합니다.
      각각의 서브플롯은 axes[0]과 axes[1]에 대응됩니다. 전체 그림의 크기는 가로 12인치, 세로 6인치로 설정됩니다.
  2. 제목 설정:
    • 각 서브플롯에 제목을 설정합니다.
    • f-string을 사용하여 각 이미지에 대해 Alien과 Predator일 확률을 백분율로 표시합니다.
    • 예를 들어, y_pred[0, 0]은 첫 번째 이미지가 Predator일 확률을 나타내며, 1 - y_pred[0, 0]은 Alien일 확률을 나타냅니다.
  3. 이미지 출력:
    • 각 서브플롯에 이미지 img1과 img2를 출력합니다.
  4. 축 설정:
    • 각 서브플롯의 축을 비활성화하여 이미지 주변의 축을 표시하지 않습니다.
  5. 그림 출력:
    • 설정한 모든 서브플롯을 하나의 그림으로 표시합니다.

 

 

 

'AI > 딥러닝' 카테고리의 다른 글

10. 포켓몬 분류  (0) 2024.06.21
08. 간단한 CNN 모델 만들기  (0) 2024.06.20
07. CNN 기초  (0) 2024.06.20
06. 비선형 활성화 함수  (0) 2024.06.20
05. 딥러닝  (0) 2024.06.20