본문 바로가기
AI/컴퓨터 비전

12. VGG19 | 분류

by 사라리24 2024. 7. 24.



1. 분류 ( Classification )

* 분류는 기계 학습과 통계학에서 시스템이 일련의 특성을 기반으로 미리 정의된 여러 범주 또는 클래스 중 하나에 주어진 입력을 할당하도록 훈련되는 과정
* 입력 기능과 클래스 레이블 사이의 학습된 관계를 기반으로 샘플의 클래스 레이블을 예측하는 것

 

1. Binary Classification

    * 이진 분류: 데이터 요소를 두 클래스 중 하나로 분류
    * 질병 vs 질병이 아님

2. Multiclass Classification

    * 다중 클래스 분류: 데이터 요소를 여러 클래스 중 하나로 분류
    * 고양이, 강아지, 코끼리 ...

3. Multi-label Classification

    * 다중 레이블 분류 : 단일 데이터 요소가 여러 클래스에 속할 수 있음
      * 강아지- 포유동일, 길들여진 동물, 잡식

4. Classification 변천사



 

 

 설치


       
        ! pip install opencv-python
        ! pip install matplotlib
        ! pip install torch
        ! pip install torchvision
        ! pip install interact
   

 

 

import



           import numpy as np
           import os
           import os
           import cv2
           import copy
           import matplotlib.pyplot as plt
           import torch
           import warnings warnings.filterwarnings('ignore')
           from ipywidgets
import interact
           from torch
.utils.data import Dataset, DataLoader
           from torchvision
import transforms, models
           from torch
import nn


 

 

 

이미지 파일 목록을 생성하기


       
          # data_dir: Covid19-dataset/train
          # sub_dir: Normal, Covid, Viral Pneumonia

          def list_image_file(data_dir, sub_dir):
              image_format = ["jpeg", "jpg", "png"]
              image_files = []
              image_dir = os.path.join(data_dir, sub_dir) # Covid19-dataset/train/Normal
              for file_path in os.listdir(image_dir):
                  if file_path.split('.')[-1] in image_format: # Covid19-dataset/train/Normal/01.jpeg
                      image_files.append(os.path.join(sub_dir, file_path))
              return image_files
             
          data_dir = 'Covid19-dataset/train'
          normals_list = list_image_file(data_dir, "Normal")
          covids_list = list_image_file(data_dir, "Covid")
          pneumonias_list = list_image_file(data_dir, "Viral Pneumonia")
 

 

 

이미지 파일 개수


       

            print(len(normals_list))
            print(len(covids_list))
            print(len(pneumonias_list))


70
111
70

 

 

이미지를 읽어와 RGB 형식으로 변환하고,  'Normal', 'Covid', 'Viral Pneumonia' 이미지를 표시


       
        def get_RGB_image(data_dir, file_name):
            image_file = os.path.join(data_dir, file_name)
            image = cv2.imread(image_file)
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # BGR 형식을 RGB 형식으로 변환
            return image

        # 'Normal', 'Covid', 'Viral Pneumonia' 디렉토리의 이미지 파일 개수 중 최소값 계산
        min_num_files = min(len(normal_list), len(covids_list), len(pneumonia_list))

        # 인터랙티브 슬라이더를 사용하여 이미지 샘플을 보여주는 함수
        @interact(index=(0, min_num_files-1))
        def show_samples(index=0):
            # 선택된 인덱스에 해당하는 이미지를 각 클래스에서 읽어옴
            normal_image = get_RGB_image(data_dir, normals_list[index])
            covid_image = get_RGB_image(data_dir, covids_list[index])
            pneumonia_image = get_RGB_image(data_dir, pneumonias_list[index])
            plt.figure(figsize=(12, 8))
            plt.subplot(131)
            plt.title('Normal')
            plt.imshow(normal_image)
           
            plt.subplot(132)
            plt.title('Covid')
            plt.imshow(covid_image)
           
            plt.subplot(133)
            plt.title('Pneumonia')
            plt.imshow(pneumonia_image)
            plt.tight_layout() # 서브플롯 간의 간격을 자동으로 조정하여 레이아웃 정리

 


 

@ 실행이 안된다면 가상환경 만들기

더보기

day6_venv 가상환경 만들기python -m venv day6_venv
cd day6_venv
cd Scripts
activatepython -m ipykernel install --user --name day6_venv --display-name day6(주피터 노트북에서 표시할 이름)

 


커스텀 데이터셋 클래스 만들기(1)


       
 
          # 데이터셋의 디렉토리 경로와 클래스 목록 정의
          train_data_dir = 'Covid19-dataset/train/'  # 훈련 데이터가 저장된 디렉토리 경로
          class_list = ['Normal', 'Covid', 'Viral Pneumonia'] # 데이터셋의 클래스 목록
      class Chest_dataset(Dataset):
          def __init__(self, data_dir, transform=None):
              """
              데이터셋의 초기화 함수
              :param data_dir: 이미지 데이터가 저장된 디렉토리 경로
              :param transform: 이미지에 적용할 변환 (선택 사항)
              """
              self.data_dir = data_dir
              # 각 클래스의 이미지 파일 경로 리스트를 생성
              normals = list_image_file(data_dir, 'Normal')
              covids = list_image_file(data_dir, 'Covid')
              pneumonias = list_image_file(data_dir, 'Viral Pneumonia')
              # 모든 이미지 파일 경로를 하나의 리스트로 결합
              self.files_path = normals + covids + pneumonias
              self.transform = transform

          def __len__(self):
              """
              데이터셋의 전체 이미지 수를 반환
              :return: 데이터셋의 이미지 수
              """
              return len(self.files_path)

          def __getitem__(self, index):
              """
              주어진 인덱스에 해당하는 이미지와 레이블을 반환
              :param index: 데이터셋에서 이미지의 인덱스
              :return: {'image': 이미지 텐서, 'target': 레이블 텐서}
              """
              image_file = os.path.join(self.data_dir, self.files_path[index])
              image = cv2.imread(image_file)
              image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
              # Covid19-dataset/train/Normal/01.jpeg
              # ['Covid19-dataset', 'train', 'Normal', '01.jpeg']
              target = class_list.index(self.files_path[index].split(os.sep)[-2])
              return {'image':image, 'target':target}


PyTorch의 Dataset 클래스를 상속하여 Chest_dataset이라는 커스텀 데이터셋 클래스를 정의하고
이미지 데이터를 로드하고, 클래스 레이블을 추출하며, 선택적으로 전처리 변환을 적용할 수 있도록 설계하기

 


 Chest_dataset 클래스를 사용하여 데이터셋 객체를 생성


       
        dset = Chest_dataset(train_data_dir)

 
 

 

 


 데이터셋의 이미지수


       
            len(dset)


251

 

 

 데이터셋에서 인덱스 100에 해당하는 데이터 샘플을 반환


       
      dset[100]




{'image': array([[[  4,   4,   4],
         [  4,   4,   4],
         [  5,   5,   5],
         ...,
         [  1,   1,   1],
         [  1,   1,   1],
         [  1,   1,   1]],
 
        [[  4,   4,   4],
         [  4,   4,   4],
         [  5,   5,   5],
         ...,
         [  1,   1,   1],
         [  1,   1,   1],
         [  1,   1,   1]],
 
        [[  4,   4,   4],
         [  4,   4,   4],
         [  4,   4,   4],
         ...,
         [  1,   1,   1],
         [  1,   1,   1],
         [  1,   1,   1]],
 
        ...,
 
        [[230, 230, 230],
         [230, 230, 230],
         [230, 230, 230],
         ...,
         [121, 121, 121],
         [124, 124, 124],
         [127, 127, 127]],
 
        [[230, 230, 230],
         [230, 230, 230],
         [230, 230, 230],
         ...,
         [121, 121, 121],
         [122, 122, 122],
         [125, 125, 125]],
 
        [[232, 232, 232],
         [232, 232, 232],
         [232, 232, 232],
         ...,
         [121, 121, 121],
         [122, 122, 122],
         [123, 123, 123]]], dtype=uint8),
 'target': 1}

 

 


index = 100으로 지정된 인덱스의 이미지를 표시


       
        index = 100
        plt.title(class_list[dset[index]['target']])
        plt.imshow(dset[index]['image'])

 

 

 


 이미지 데이터를 전처리하는 
transforms 모듈


       

        # 이미지 전처리를 위한 변환 함수 정의
        transformer = transforms.Compose([
            transforms.ToTensor(), # 이미지를 텐서로 변환 (값 범위: [0, 1])
            transforms.Resize((224, 224)), # 이미지를 224x224 크기로 리사이즈
            transforms.Normalize(mean = [0.5, 0.5, 0.5], std = [0.5, 0.5, 0.5]) # 텐서를 정규화
        ])


 

 


커스텀 데이터셋 클래스 만들기(2)_transformer 적용


       
   
        class Chest_dataset(Dataset):
            def __init__(self, data_dir, transform=None):
                """
                Chest_dataset 클래스의 초기화 함수
                :param data_dir: 이미지 데이터가 저장된 디렉토리 경로
                :param transform: 이미지에 적용할 변환 (선택 사항)
                """
                self.data_dir = data_dir
                normals = list_image_file(data_dir, 'Normal')
                covids = list_image_file(data_dir, 'Covid')
                pneumonias = list_image_file(data_dir, 'Viral Pneumonia')
                self.files_path = normals + covids + pneumonias # 모든 클래스의 이미지 파일 목록을 하나의 리스트로 결합
                self.transform = transform # 이미지에 적용할 변환을 저장

            def __len__(self):
                return len(self.files_path)

            def __getitem__(self, index):
                """
                주어진 인덱스에 해당하는 이미지와 레이블을 반환하는 함수
                :param index: 데이터셋에서 이미지의 인덱스
                :return: {'image': 이미지 텐서, 'target': 레이블 텐서}
                """
                image_file = os.path.join(self.data_dir, self.files_path[index])
                image = cv2.imread(image_file)
                image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
                # 예: 'Covid19-dataset/train/Normal/01.jpeg'
                # ['Covid19-dataset', 'train', 'Normal', '01.jpeg']
                # 이미지의 클래스 레이블을 추출 (디렉토리 이름으로부터 레이블 추출)
                target = class_list.index(self.files_path[index].split(os.sep)[-2])
               
                # 변환이 지정된 경우 적용
                if self.transform:
                    image = self.transform(image) # 이미지에 전처리 변환을 적용
                    target = torch.Tensor([target]).long() # 레이블을 텐서로 변환 (long 타입)
                   
                return {'image':image, 'target':target}


 

 

 

 

 Chest_dataset 클래스를 사용하여 데이터셋 객체를 생성


       
        train_dset = Chest_dataset(train_data_dir, transformer)

 
 

 

 

인덱스 100에 해당하는 데이터 샘플을 가져와서, 이미지와 레이블을 출력하기


       
        index = 100
        image = train_ dset[index]['image']
        label = train_ dset[index]['target']
        print(image.shape, label)


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

 

 

Dataloader 만들기


     
        def build_dataloader(train_data_dir, val_data_dir):
            """
            훈련 및 검증 데이터셋에 대한 DataLoader 객체를 생성하는 함수.
           
            :param train_data_dir: 훈련 데이터가 저장된 디렉토리의 경로
            :param val_data_dir: 검증 데이터가 저장된 디렉토리의 경로
            :return: 'train' 및 'val' 키를 포함하는 딕셔너리 형태의 데이터로더
            """
            # 데이터로더를 저장할 빈 딕셔너리 초기화
            dataloaders = {}

            # 훈련 데이터셋 생성
            train_dset = Chest_dataset(train_data_dir, transformer)
            # 훈련 데이터로더 생성
            dataloaders['train'] = DataLoader(train_dset, batch_size = 4, shuffle = True, drop_last = True)

            # 검증 데이터셋 생성
            val_dset = Chest_dataset(val_data_dir, transformer)
            # 검증 데이터로더 생성
            dataloaders['val'] = DataLoader(val_dset, batch_size = 1, shuffle = False, drop_last = False)
            # 훈련과 검증 데이터로더를 포함하는 딕셔너리 반환
            return dataloaders

        train_data_dir = 'Covid19-dataset/train/'
        val_data_dir = 'Covid19-dataset/test/'
        dataloader = build_dataloader(train_data_dir, val_data_dir)
 
 

 

 

데이터 배치를 반복적으로 처리하고, 첫 번째 배치에 대해 정보를 출력


     
        for i, d in enumerate(dataloader['train']):
            print(i, d)
            if i == 0:
                break


0 {'image': tensor([[[[-0.9508, -0.9305, -0.9012,  ..., -0.3886, -0.2740, -0.7891],
          [-0.9508, -0.9321, -0.9042,  ..., -0.4202, -0.3388, -0.7640],
          [-0.9514, -0.9333, -0.9059,  ..., -0.7780, -0.7377, -0.9323],
          ...,
          [-0.9253, -0.9147, -0.8738,  ..., -1.0000, -0.9965, -0.9062],
          [-0.8917, -0.4111, -0.4916,  ..., -1.0000, -0.9976, -0.9518],
          [-0.5959, -0.1704, -0.4112,  ..., -1.0000, -1.0000, -0.9999]],

         [[-0.9508, -0.9305, -0.9012,  ..., -0.3886, -0.2740, -0.7891],
          [-0.9508, -0.9321, -0.9042,  ..., -0.4202, -0.3388, -0.7640],
          [-0.9514, -0.9333, -0.9059,  ..., -0.7780, -0.7377, -0.9323],
          ...,
          [-0.9253, -0.9147, -0.8738,  ..., -1.0000, -0.9965, -0.9062],
          [-0.8917, -0.4111, -0.4916,  ..., -1.0000, -0.9976, -0.9518],
          [-0.5959, -0.1704, -0.4112,  ..., -1.0000, -1.0000, -0.9999]],

         [[-0.9508, -0.9305, -0.9012,  ..., -0.3886, -0.2740, -0.7891],
          [-0.9508, -0.9321, -0.9042,  ..., -0.4202, -0.3388, -0.7640],
          [-0.9514, -0.9333, -0.9059,  ..., -0.7780, -0.7377, -0.9323],
          ...,
          [-0.9253, -0.9147, -0.8738,  ..., -1.0000, -0.9965, -0.9062],
          [-0.8917, -0.4111, -0.4916,  ..., -1.0000, -0.9976, -0.9518],
          [-0.5959, -0.1704, -0.4112,  ..., -1.0000, -1.0000, -0.9999]]],


        [[[-0.8873, -0.8757, -0.8885,  ..., -0.8007, -0.7912, -0.8016],
          [-0.8786, -0.8818, -0.8937,  ..., -0.8057, -0.7991, -0.8034],
          [-0.8789, -0.8864, -0.8887,  ..., -0.8077, -0.8040, -0.8037],
          ...,
          [-0.8967, -0.8926, -0.9065,  ..., -0.7752, -0.7772, -0.7866],
          [-0.8993, -0.8948, -0.9038,  ..., -0.7771, -0.7815, -0.7833],
          [-0.8855, -0.8915, -0.8956,  ..., -0.7825, -0.7699, -0.7819]],

         [[-0.8873, -0.8757, -0.8885,  ..., -0.8007, -0.7912, -0.8016],
          [-0.8786, -0.8818, -0.8937,  ..., -0.8057, -0.7991, -0.8034],
          [-0.8789, -0.8864, -0.8887,  ..., -0.8077, -0.8040, -0.8037],
          ...,
          [-0.8967, -0.8926, -0.9065,  ..., -0.7752, -0.7772, -0.7866],
          [-0.8993, -0.8948, -0.9038,  ..., -0.7771, -0.7815, -0.7833],
          [-0.8855, -0.8915, -0.8956,  ..., -0.7825, -0.7699, -0.7819]],

         [[-0.8873, -0.8757, -0.8885,  ..., -0.8007, -0.7912, -0.8016],
          [-0.8786, -0.8818, -0.8937,  ..., -0.8057, -0.7991, -0.8034],
          [-0.8789, -0.8864, -0.8887,  ..., -0.8077, -0.8040, -0.8037],
          ...,
          [-0.8967, -0.8926, -0.9065,  ..., -0.7752, -0.7772, -0.7866],
          [-0.8993, -0.8948, -0.9038,  ..., -0.7771, -0.7815, -0.7833],
          [-0.8855, -0.8915, -0.8956,  ..., -0.7825, -0.7699, -0.7819]]],


        [[[-1.0000, -1.0000, -1.0000,  ..., -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000,  ..., -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000,  ..., -1.0000, -1.0000, -1.0000],
          ...,
          [-0.6367, -0.6177, -0.5638,  ..., -0.4194, -0.5029, -0.6054],
          [-0.6391, -0.6333, -0.5873,  ..., -0.4250, -0.5218, -0.6094],
          [-0.6641, -0.6473, -0.5882,  ..., -0.4429, -0.5267, -0.6128]],

         [[-1.0000, -1.0000, -1.0000,  ..., -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000,  ..., -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000,  ..., -1.0000, -1.0000, -1.0000],
          ...,
          [-0.6367, -0.6177, -0.5638,  ..., -0.4194, -0.5029, -0.6054],
          [-0.6391, -0.6333, -0.5873,  ..., -0.4250, -0.5218, -0.6094],
          [-0.6641, -0.6473, -0.5882,  ..., -0.4429, -0.5267, -0.6128]],

         [[-1.0000, -1.0000, -1.0000,  ..., -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000,  ..., -1.0000, -1.0000, -1.0000],
          [-1.0000, -1.0000, -1.0000,  ..., -1.0000, -1.0000, -1.0000],
          ...,
          [-0.6367, -0.6177, -0.5638,  ..., -0.4194, -0.5029, -0.6054],
          [-0.6391, -0.6333, -0.5873,  ..., -0.4250, -0.5218, -0.6094],
          [-0.6641, -0.6473, -0.5882,  ..., -0.4429, -0.5267, -0.6128]]],


        [[[-0.9922, -0.9922, -0.9922,  ..., -0.9922, -0.9922, -0.9922],
          [-0.9922, -0.9922, -0.9922,  ..., -0.9922, -0.9921, -0.9922],
          [-0.9922, -0.9922, -0.9922,  ..., -0.9920, -0.9899, -0.9832],
          ...,
          [-0.9922, -0.9921, -0.9921,  ..., -0.3735, -0.4247, -0.4114],
          [-0.9922, -0.9922, -0.9922,  ..., -0.3650, -0.4169, -0.4075],
          [-0.9929, -0.9929, -0.9929,  ..., -0.4184, -0.4691, -0.4698]],

         [[-0.9922, -0.9922, -0.9922,  ..., -0.9922, -0.9922, -0.9922],
          [-0.9922, -0.9922, -0.9922,  ..., -0.9922, -0.9921, -0.9922],
          [-0.9922, -0.9922, -0.9922,  ..., -0.9920, -0.9899, -0.9832],
          ...,
          [-0.9922, -0.9921, -0.9921,  ..., -0.3735, -0.4247, -0.4114],
          [-0.9922, -0.9922, -0.9922,  ..., -0.3650, -0.4169, -0.4075],
          [-0.9929, -0.9929, -0.9929,  ..., -0.4184, -0.4691, -0.4698]],

         [[-0.9922, -0.9922, -0.9922,  ..., -0.9922, -0.9922, -0.9922],
          [-0.9922, -0.9922, -0.9922,  ..., -0.9922, -0.9921, -0.9922],
          [-0.9922, -0.9922, -0.9922,  ..., -0.9920, -0.9899, -0.9832],
          ...,
          [-0.9922, -0.9921, -0.9921,  ..., -0.3735, -0.4247, -0.4114],
          [-0.9922, -0.9922, -0.9922,  ..., -0.3650, -0.4169, -0.4075],
          [-0.9929, -0.9929, -0.9929,  ..., -0.4184, -0.4691, -0.4698]]]]), 'target': tensor([[2],
        [0],
        [0],
        [1]])}

 

 

데이터 로더(DataLoader)에서 가져온 배치(batch) 딕셔너리 d의 'image' 키에 해당하는 값의 모양(shape)


       
      d['image'].shape

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

 

 

데이터 로더에서 가져온 배치 딕셔너리 d의 'target' 키에 해당하는 값의 모양(shape)

      

      d['target'].shape


torch.Size([4, 1])

 

 

 

2. VGG19(Classification) 모델 

 
  • VGG는 Visual Geometry Group의 약자
  • 다중 레이어가 있는 표준 심층 CNN 아키텍쳐

 

 

VGG19 모델을 로드


       
        model = models.vgg19(pretrained = True)
        model


VGG(
  (features): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU(inplace=True)
    (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (3): ReLU(inplace=True)
    (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (6): ReLU(inplace=True)
    (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (8): ReLU(inplace=True)
    (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): ReLU(inplace=True)
    (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (13): ReLU(inplace=True)
    (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (15): ReLU(inplace=True)
    (16): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (17): ReLU(inplace=True)
    (18): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (19): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (20): ReLU(inplace=True)
    (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (22): ReLU(inplace=True)
    (23): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (24): ReLU(inplace=True)
    (25): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (26): ReLU(inplace=True)
    (27): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (29): ReLU(inplace=True)
    (30): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (31): ReLU(inplace=True)
    (32): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (33): ReLU(inplace=True)
    (34): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (35): ReLU(inplace=True)
    (36): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (avgpool): AdaptiveAvgPool2d(output_size=(7, 7))
  (classifier): Sequential(
    (0): Linear(in_features=25088, out_features=4096, bias=True)
    (1): ReLU(inplace=True)
    (2): Dropout(p=0.5, inplace=False)
    (3): Linear(in_features=4096, out_features=4096, bias=True)
    (4): ReLU(inplace=True)
    (5): Dropout(p=0.5, inplace=False)
    (6): Linear(in_features=4096, out_features=1000, bias=True)
  )
)

 

 

모델 만들기


       
          def build_vgg19_based_model(device_name='cpu'):
              """
              VGG19 모델을 기반으로 하여 커스텀 모델을 정의하고, 주어진 장치로 이동합니다.
             
              :param device_name: 모델을 배치할 장치 ('cpu' 또는 'cuda' 등). 기본값은 'cpu'.
              :return: 정의된 모델을 주어진 장치에 배치한 상태로 반환.
              """
              # 주어진 장치 이름에 해당하는 장치 객체를 생성합니다.
              device = torch.device(device_name)
              # 사전 학습된 VGG19 모델을 로드합니다.
              model = models.vgg19(pretrained=True)
              # VGG19의 기본 평균 풀링 레이어를 AdaptiveAvgPool2d로 교체하여 출력 크기를 (1, 1)로 조정합니다.
              model.avgpool = nn.AdaptiveAvgPool2d(output_size=(1, 1))
              # 기존의 완전 연결(FC) 레이어를 사용자 정의 레이어로 교체합니다.
              model.classifier = nn.Sequential(
                  nn.Flatten(), # 4D 텐서를 2D 텐서로 변환합니다. (배치 크기, 채널, 높이, 너비 -> 배치 크기, 채널 * 높이 * 너비)
                  nn.Linear(512, 256),  # 512개의 입력 노드를 256개의 출력 노드로 연결하는 선형 변환 레이어입니다.
                  nn.ReLU(), # 비선형 활성화 함수입니다. 입력 값을 비선형적으로 변환합니다.
                  nn.Linear(256, len(class_list)),# 256개의 입력 노드를 데이터셋의 클래스 수(len(class_list))에 맞게 출력하는 선형 변환 레이어입니다.
                  nn.Softmax(dim=1)  # 출력값을 확률로 변환하는 소프트맥스 함수입니다. 클래스 차원(dim=1)에서 적용됩니다.
              )
              # 모델을 주어진 장치로 이동시킵니다 (CPU 또는 GPU).
              return model.to(device)
             
          # 모델 정의 및 CPU 장치에 배치
          model = build_vgg19_based_model(device_name = 'cpu')
          model

 

 

 

손실 함수 정의


      
      loss_func = nn.CrossEntropyLoss(reduction='mean')


 

 

 

SGD 최적화 알고리즘을 사용하여 모델의 파라미터를 업데이트하도록 설정


       

      # 옵티마이저 정의
      optimizer = torch.optim.SGD(model.parameters(),lr=1E-3, momentum=0.9)


lr(학습률)과 momentum(모멘텀)은 모델 학습의 효율성과 안정성을 조절하는 중요한 하이퍼파라미터

 

 

모델의 예측 결과와 실제 레이블을 비교하여 정확도 반환하기
@torch.no_grad() 데코레이터와 함께 제공된 get_accuracy 함수는 모델의 예측 정확도를 계산하는 데 사용됩니다.


     
        @torch.no_grad()
        def get_accuracy(image, target, model):
            batch_size = image.shape[0]
            prediction = model(image)
            _, pred_label = torch.max(prediction, dim = 1)

            is_correct = (pred_label == target)
            return is_correct.cpu().numpy().sum() / batch_size


  • @torch.no_grad()
    • @torch.no_grad(): 이 데코레이터는 함수가 호출될 때 기울기 계산을 비활성화합니다. 이로 인해 메모리 사용량이 줄어들고 계산 속도가 빨라지며, 모델의 추론(inference) 중에는 기울기 계산이 필요 없으므로 계산 효율성이 향상됩니다.
    • 목적: 모델의 학습 단계가 아니라 예측 단계에서 기울기를 계산하지 않도록 하여, 불필요한 메모리 사용을 줄이고 성능을 개선합니다.
  • def get_accuracy(image, target, model)
    • image: 입력 이미지 배치. 이 배치는 모델에 입력될 이미지들의 텐서입니다.
    • target: 실제 레이블. 각 이미지에 대한 실제 정답 레이블이 포함된 텐서입니다.
    • model: 평가할 모델. 입력 이미지를 예측하기 위해 사용됩니다.
  • batch_size = image.shape[0]
    • batch_size: 입력 이미지 배치의 크기를 얻습니다. image.shape[0]은 배치의 첫 번째 차원으로, 배치에 포함된 이미지의 수를 나타냅니다.
  • prediction = model(image)
    • model(image): 입력 이미지를 모델에 통과시켜 예측값을 얻습니다. prediction은 모델의 출력값으로, 각 클래스에 대한 점수를 포함하는 텐서입니다.
  • _, pred_label = torch.max(prediction, dim=1)
    • torch.max(prediction, dim=1): 예측값 중에서 가장 높은 값을 가진 클래스의 인덱스를 반환합니다.
      • prediction: 모델의 출력값 텐서. 각 클래스에 대한 점수를 포함합니다.
      • dim=1: 각 이미지의 클래스 점수에 대해 가장 높은 값을 찾습니다.
      • _: 최대 값(여기서는 필요 없으므로 무시합니다).
      • pred_label: 예측된 클래스 레이블. 각 이미지에 대해 가장 높은 점수를 가진 클래스의 인덱스입니다.
  • is_correct = (pred_label == target)
    • pred_label == target: 예측 레이블과 실제 레이블이 일치하는지 여부를 비교하여 불리언 텐서를 생성합니다. True는 예측이 올바른 경우를 의미하고, False는 잘못된 경우를 의미합니다.
  • return is_correct.cpu().numpy().sum() / batch_size
    • is_correct.cpu(): GPU에서 CPU로 데이터를 이동시킵니다. 이는 NumPy 변환을 위해 필요합니다.
    • is_correct.numpy(): PyTorch 텐서를 NumPy 배열로 변환합니다.
    • is_correct.numpy().sum(): 올바른 예측의 총 개수를 계산합니다. True는 1로, False는 0으로 변환되어 합산됩니다.
    • / batch_size: 정확도는 총 올바른 예측 수를 배치 크기로 나눈 값입니다. 이는 정확도 비율(정확도 퍼센트)을 계산합니다.

 

 

모델의 손실(loss)과 정확도(accuracy)를 계산하여 반환하기
train_one_epoch 함수는 주어진 데이터 로더(dataloaders)를 사용하여
모델을 학습하거나 평가하는 단일 에폭(epoch)을 수행합니다.


       
        def train_one_epoch(dataloaders, model, optimizer, loss_func, device):
            """
            주어진 데이터 로더를 사용하여 모델의 훈련 또는 검증 단일 에폭을 수행합니다.

            :param dataloaders: 훈련 및 검증 데이터 로더를 포함하는 딕셔너리.
            :param model: 학습할 모델.
            :param optimizer: 모델의 파라미터를 업데이트할 최적화 알고리즘.
            :param loss_func: 손실 함수.
            :param device: 모델과 데이터를 이동할 장치(CPU 또는 GPU).
            :return: 손실과 정확도를 포함하는 딕셔너리.
            """
            losses = {} # 각 단계('train', 'val')에 대한 손실 값을 저장할 딕셔너리
            accuracies = {} # 각 단계('train', 'val')에 대한 정확도 값을 저장할 딕셔너리
            # 'train'과 'val' 두 단계 모두에 대해 반복합니다.
            for tv in ['train', 'val']:
                running_loss = 0.0 # 현재 에폭의 손실 값을 누적하기 위한 변수
                running_correct = 0 # 현재 에폭의 정확도 값을 누적하기 위한 변수

                # 훈련 모드 또는 평가 모드 설정
                if tv == 'train':
                    model.train() # 훈련 모드: 드롭아웃(dropout)과 배치 정규화(batch normalization) 등이 활성화됩니다.
                else:
                    model.eval() # 평가 모드: 드롭아웃과 배치 정규화가 비활성화됩니다.

                # 데이터 로더에서 배치 단위로 반복합니다.
                for index, batch in enumerate(dataloaders[tv]):
                    image = batch['image'].to(device) # 이미지 텐서를 장치로 이동합니다.
                    target = batch['target'].squeeze(dim=1).to(device)  # 레이블 텐서를 장치로 이동하고 필요에 따라 차원을 축소합니다.
                    # 훈련 모드일 때만 기울기 계산을 활성화합니다.
                    with torch.set_grad_enabled(tv == 'train'):
                        prediction = model(image) # 모델을 사용하여 예측값을 계산합니다.
                        loss = loss_func(prediction, target) # 예측값과 실제 레이블을 비교하여 손실 값을 계산합니다.

                        # 훈련 모드일 때만 역전파와 파라미터 업데이트를 수행합니다.
                        if tv == 'train':
                            optimizer.zero_grad() # 이전 배치의 기울기를 초기화합니다.
                            loss.backward() # 손실에 대해 기울기를 계산합니다.
                            optimizer.step() # 기울기를 사용하여 모델의 파라미터를 업데이트합니다.

                    # 현재 배치의 손실 값과 정확도를 누적합니다..
                    running_loss += loss.item() # 손실 값을 누적합니다.
                    running_correct += get_accuracy(image, target, model) # 정확도를 계산하여 누적합니다.

                    # 훈련 모드일 때, 10 배치마다 손실 값을 출력합니다.
                    if tv == 'train':
                        if index % 10 == 0:
                            print(f"{index}/{len(dataloaders['train'])} - Running loss: {loss.item()}")
                           
                # 전체 에폭 동안의 평균 손실과 정확도를 계산합니다.            
                losses[tv] = running_loss / len(dataloaders[tv])  # 평균 손실 값
                accuracies[tv] = running_correct / len(dataloaders[tv]) # 평균 정확도 값
               
            return losses, accuracies# 각 단계('train', 'val')의 손실과 정확도 딕셔너리를 반환합니다.


 

 

 

 모델의 상태를 저장하는 함수


       
        def save_best_model(model_state, model_name, save_dir = './trained_model'):
            """
            주어진 모델 상태를 파일로 저장합니다.

            :param model_state: 저장할 모델의 상태 딕셔너리 (예: `model.state_dict()`).
            :param model_name: 저장할 파일의 이름 (예: 'best_model.pth').
            :param save_dir: 모델 파일을 저장할 디렉토리 경로. 기본값은 './trained_model'.
            """

            # 저장할 디렉토리가 존재하지 않으면 생성합니다.
            os.makedirs(save_dir, exist_ok = True)
            # 모델 상태를 지정된 디렉토리에 저장합니다.
            torch.save(model_state, os.path.join(save_dir, model_name))

 
 

 

 

 

딥러닝 모델을 훈련하기 위한 환경을 설정


       
        device = torch.device('cpu')
        train_data_dir = 'Covid19-dataset/train/'
        val_data_dir = 'Covid19-dataset/test/'
       
        # `train_data_dir`과 `val_data_dir`을 사용하여 훈련 및 검증 데이터에 대한 데이터 로더를 만듭니다.
        dataloaders = build_dataloader(train_data_dir, val_data_dir)
       
        # VGG19 기반의 모델을 생성하는 함수를 호출합니다
        model = build_vgg19_based_model(device_name = device)
       
        # 손실 함수를 정의합니다.
        loss_func = nn.CrossEntropyLoss(reduction = 'mean')
       
        # 최적화 기법을 설정합니다.
        # Stochastic Gradient Descent(SGD) 알고리즘을 사용하여 모델의 파라미터를 업데이트합니다.
        optimizer = torch.optim.SGD(model.parameters(), lr = 1E-3, momentum=0.9)

 
 

 

 

 학습하기


       
          num_epochs = 10

          # 최고 정확도 및 손실을 추적하기 위한 초기값 설정
          best_acc = 0.0
          train_loss, train_accuracy = [], []
          val_loss, val_accuracy = [], []
          for epoch in range(num_epochs):
              # 한 에포크 동안 학습과 검증을 수행하고 손실과 정확도를 반환받습니다.
              losses, accuracies = train_one_epoch(dataloaders, model, optimizer, loss_func, device)
              # 훈련 및 검증 손실과 정확도를 리스트에 저장합니다.
              train_loss.append(losses['train'])
              val_loss.append(losses['val'])
              train_accuracy.append(accuracies['train'])
              val_accuracy.append(accuracies['val'])
              print(f"{epoch+1}/{num_epochs} - Train_Loss:{losses['train']}, Val_Loss:{losses['val']}")
              print(f"{epoch+1}/{num_epochs} - Train_Accuracies:{accuracies['train']}, Val_Loss:{accuracies['val']}")

              # epoch 2번째 까진 정확도가 높아도 신빙성 없다는 판단.
              # 에포크가 2 이상일 때만 정확도를 기준으로 모델을 저장합니다.
              # 검증 정확도가 현재까지의 최고 정확도보다 높은 경우, 모델을 저장합니다
              if(epoch > 2) and (accuracies['val'] > best_acc):
                  best_acc = accuracies['val']
                  best_model = copy.deepcopy(model.state_dict())
                  save_best_model(best_model, f'model_{epoch+1:02d}.pth')

          print(f'Best Accuracy: {best_acc}')

 
0/62 - Running loss: 1.093900442123413
10/62 - Running loss: 1.0953799486160278
20/62 - Running loss: 1.0953714847564697
30/62 - Running loss: 1.0796129703521729
40/62 - Running loss: 1.1127455234527588
50/62 - Running loss: 1.1119818687438965
60/62 - Running loss: 1.0700511932373047
1/10 - Train_Loss:1.0962051114728373, Val_Loss:1.0794648365540938
1/10 - Train_Accuracies:0.4032258064516129, Val_Loss:0.6818181818181818
0/62 - Running loss: 1.0641858577728271
10/62 - Running loss: 1.0683239698410034
20/62 - Running loss: 1.1290593147277832
30/62 - Running loss: 0.9145467877388
40/62 - Running loss: 1.0068436861038208
50/62 - Running loss: 0.7750745415687561
60/62 - Running loss: 0.7086907625198364
2/10 - Train_Loss:0.9750767061787267, Val_Loss:0.8264774129246221
2/10 - Train_Accuracies:0.6451612903225806, Val_Loss:0.7272727272727273
0/62 - Running loss: 0.7580134868621826
10/62 - Running loss: 0.8338483572006226
20/62 - Running loss: 0.5556808710098267
30/62 - Running loss: 0.835498571395874
40/62 - Running loss: 0.8826307058334351
50/62 - Running loss: 0.794944167137146
60/62 - Running loss: 0.6320281624794006
3/10 - Train_Loss:0.78758694567988, Val_Loss:1.0869710969202446
3/10 - Train_Accuracies:0.8145161290322581, Val_Loss:0.42424242424242425
0/62 - Running loss: 1.1429064273834229
10/62 - Running loss: 0.5522404909133911
20/62 - Running loss: 0.804008960723877
30/62 - Running loss: 0.8015300035476685
40/62 - Running loss: 0.7871673703193665
50/62 - Running loss: 0.5652523040771484
60/62 - Running loss: 1.012768030166626
4/10 - Train_Loss:0.8541934249862548, Val_Loss:0.9109374718232588
4/10 - Train_Accuracies:0.7258064516129032, Val_Loss:0.6515151515151515
0/62 - Running loss: 0.7917495965957642
10/62 - Running loss: 0.5842599868774414
20/62 - Running loss: 0.8373899459838867
30/62 - Running loss: 0.5514838695526123
40/62 - Running loss: 0.5688213109970093
50/62 - Running loss: 0.6073175668716431
60/62 - Running loss: 0.7852078676223755
5/10 - Train_Loss:0.728634137299753, Val_Loss:0.6813537595850049
5/10 - Train_Accuracies:0.8669354838709677, Val_Loss:0.9090909090909091
0/62 - Running loss: 0.5629498958587646
10/62 - Running loss: 0.5740698575973511
20/62 - Running loss: 0.8823028802871704
30/62 - Running loss: 0.741658091545105
40/62 - Running loss: 0.5614508390426636
50/62 - Running loss: 0.8062911629676819
60/62 - Running loss: 0.6302551627159119
6/10 - Train_Loss:0.6409576785179877, Val_Loss:0.7088807613560648
6/10 - Train_Accuracies:0.9354838709677419, Val_Loss:0.8333333333333334
0/62 - Running loss: 0.6321278810501099
10/62 - Running loss: 0.6091440916061401
20/62 - Running loss: 0.8005079030990601
30/62 - Running loss: 0.785637617111206
40/62 - Running loss: 0.6309155225753784
50/62 - Running loss: 0.7838208675384521
60/62 - Running loss: 0.5551669597625732
7/10 - Train_Loss:0.6466519044291589, Val_Loss:0.6311718956990675
7/10 - Train_Accuracies:0.9596774193548387, Val_Loss:0.9090909090909091
0/62 - Running loss: 0.7726355791091919
10/62 - Running loss: 0.5523151159286499
20/62 - Running loss: 0.5582350492477417
30/62 - Running loss: 0.5521222352981567
40/62 - Running loss: 0.6644344329833984
50/62 - Running loss: 0.5515321493148804
60/62 - Running loss: 0.8015098571777344
8/10 - Train_Loss:0.6385487750653298, Val_Loss:0.7524168328805403
8/10 - Train_Accuracies:0.9516129032258065, Val_Loss:0.7878787878787878
0/62 - Running loss: 0.5660336017608643
10/62 - Running loss: 0.5527616143226624
20/62 - Running loss: 0.5515630841255188
30/62 - Running loss: 0.5783404111862183
40/62 - Running loss: 0.5528625249862671
50/62 - Running loss: 0.5530462265014648
60/62 - Running loss: 0.5514616966247559
9/10 - Train_Loss:0.6024611025087295, Val_Loss:0.5913251114614082
9/10 - Train_Accuracies:0.9637096774193549, Val_Loss:0.9545454545454546
0/62 - Running loss: 0.5514703989028931
10/62 - Running loss: 0.5519660711288452
20/62 - Running loss: 0.5795284509658813
30/62 - Running loss: 0.5515310168266296
40/62 - Running loss: 0.551509439945221
50/62 - Running loss: 0.5527869462966919
60/62 - Running loss: 0.5527551770210266
10/10 - Train_Loss:0.5850859534355902, Val_Loss:0.805530337673245
10/10 - Train_Accuracies:0.9879032258064516, Val_Loss:0.7424242424242424
Best Accuracy: 0.9545454545454546



 

학습 및 검증 과정에서의 손실(loss)과 정확도(accuracy)의 변화를 시각화


     
        plt.figure(figsize=(6, 5))
        plt.subplot(211)
        plt.plot(train_loss, label='train')
        plt.plot(val_loss, label='val')
        plt.xlabel('epoch')
        plt.ylabel('loss')
        plt.grid('on')
        plt.legend()
        plt.subplot(212)
        plt.plot(train_accuracy, label='train')
        plt.plot(val_accuracy, label='val')
        plt.xlabel('epoch')
        plt.ylabel('accuracy')
        plt.grid('on')
        plt.legend()
        plt.tight_layout()


 



 

저장된 모델의 체크포인트를 로드하고, 이를 사용하여 모델을 평가 모드로 전환


     
        ckpt = torch.load('./trained_model/model_04.pth') # 가장 좋은 모델 path
        model = build_vgg19_based_model()
        model.load_state_dict(ckpt)
        model.eval()



 
 

VGG(
  (features): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU(inplace=True)
    (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (3): ReLU(inplace=True)
    (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (6): ReLU(inplace=True)
    (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (8): ReLU(inplace=True)
    (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): ReLU(inplace=True)
    (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (13): ReLU(inplace=True)
    (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (15): ReLU(inplace=True)
    (16): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (17): ReLU(inplace=True)
    (18): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (19): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (20): ReLU(inplace=True)
    (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (22): ReLU(inplace=True)
    (23): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (24): ReLU(inplace=True)
    (25): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (26): ReLU(inplace=True)
    (27): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (29): ReLU(inplace=True)
    (30): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (31): ReLU(inplace=True)
    (32): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (33): ReLU(inplace=True)
    (34): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (35): ReLU(inplace=True)
    (36): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
  (classifier): Sequential(
    (0): Flatten(start_dim=1, end_dim=-1)
    (1): Linear(in_features=512, out_features=256, bias=True)
    (2): ReLU()
    (3): Linear(in_features=256, out_features=3, bias=True)
    (4): Softmax(dim=1)
  )
)

 

 

 

테스트 데이터셋을 준비하고 클래스 목록을 정의하기


       
      data_dir = 'Covid19-dataset/test/'
      test_normals_list = list_image_file(data_dir, 'Normal')
      test_covids_list = list_image_file(data_dir, 'Covid')
      test_pneumonias_list = list_image_file(data_dir, 'Viral Pneumonia')

      class_list = ['Normal', 'Covid', 'Viral Pneumonia']

 
 

 

 

테스트 데이터셋의 각 클래스별 이미지 파일 수를 계산하고, 그 중 가장 작은 값을 찾기


       
      min_num_files = min(len(test_normals_list), len(test_covids_list), len(test_pneumonias_list))
      min_num_files

 
20

 

 

 

이미지를 전처리하는 함수 preprocess_image를 정의하기


       
        def preprocess_image(image):
            transformer = transforms.Compose([
                transforms.ToTensor(),
                transforms.Resize((224, 224)),
                transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
            ])
            tensor_image = transformer(image) # C, H, W
            tensor_image = tensor_image.unsqueeze(0) # B, C, H, W
            return tensor_image

 
 

 

 

주어진 이미지를 모델에 입력하고 예측된 레이블을 반환하는 함수  'model_predict' 정의하기


     
      def model_predict(image, model):
          tensor_image = preprocess_image(image)
          prediction = model(tensor_image)
          _, pred_label = torch.max(prediction.detach(), dim=1)
          pred_label = pred_label.squeeze(0)
          return pred_label.item()


 

 

 

이미지 샘플을 모델을 사용해 예측하고, 결과를 시각화하


       

        @interact(index=(0, min_num_files-1))

        def show_samples(index=0):
            """
            선택한 인덱스에 해당하는 이미지 샘플을 로드하고, 모델의 예측 결과를 시각화하는 함수입니다.

            :param index: 슬라이더를 통해 선택한 이미지 샘플의 인덱스
            """
            # 선택한 인덱스에 해당하는 이미지를 RGB 형식으로 로드합니다.
            normal_image = get_RGB_image(data_dir, test_normals_list[index])
            covid_image = get_RGB_image(data_dir, test_covids_list[index])
            pneumonia_image = get_RGB_image(data_dir, test_pneumonias_list[index])
           
            # 각 이미지를 모델에 입력하여 예측 결과를 얻습니다.
            prediction_1 = model_predict(normal_image, model)
            prediction_2 = model_predict(covid_image, model)
            prediction_3 = model_predict(pneumonia_image, model)
           
            plt.figure(figsize=(12, 8))
            plt.subplot(131)
            plt.title(f'Normal, Pred:{class_list[prediction_1]}')
            plt.imshow(normal_image)
           
            plt.subplot(132)
            plt.title(f'Covid, Pred:{class_list[prediction_2]}')
            plt.imshow(covid_image)
           
            plt.subplot(133)
            plt.title(f'Pneumonia, Pred:{class_list[prediction_3]}')
            plt.imshow(pneumonia_image)
            plt.tight_layout()

 




 

 

'AI > 컴퓨터 비전' 카테고리의 다른 글

@. 과제  (0) 2024.08.05
13. Faster R-CNN | 객체 탐지  (0) 2024.07.25
11. OCR  (1) 2024.07.23
10. 레이블링  (2) 2024.07.23
09. 모폴로지 변환  (0) 2024.07.23