본문 바로가기
대학원 시험 공부

데이터분석 프로젝트 (파이썬 Covid-19 사진 학습(분류) 3)

by code2772 2023. 4. 5.

[ 목차 ]

    728x90
    반응형

    11. 모델 검증을 위한 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

    12. 모델 학습을 위한 함수 구현하기

    # 모델을 학습하기 위한 함수를 만들어보자
    device = torch.device('cpu')
    
    
    # epcoh: 데이터 전체를 한바퀴를 돌리는것?
    # model: 우리가 학습한 모델
    def train_one_epoch(dataloaders, model, optimizer, loss_func, device):
        losses = {} # 딕셔너리 타입으로 만들어준다...
        accuracies = {} 
        
        for tv in ['train', 'val']:
            running_loss = 0.0 # 얼마만큼 틀렸니를 누적...
            running_correct = 0 # 얼마만큼 맞았니를 누적...
            
            if tv == 'train':
                model.train() # 학습을 시킨다...
            else:
                model.eval() # 학습에 대한 메모리는 날리고 검증에 대한 메모리만 남긴다... no_grad()와 비슷...
                
            for index, batch in enumerate(dataloaders[tv]): # dataloaders 라는 객체에 train or val를 넣어준다...
                image = batch['image'].to(device)
                target = batch['target'].squeeze(dim=1).to(device) # squeeze: 차원축소, dim=1로 하면 1차원으롤 줄여주고, 값이 없으면 한차원 줄여줌
                
                
      # with : 파일 입출력에서 open을 사용하면 이 메소드들에 의해 사용되는 경우가 많은데, 마지막에 파일같은 경우에는
            # close를 해줘야 하는데 with를 하면 open하고 closs 할 필요가 없고 with내에 들여쓰기하여 사용을 하면 된다.
           # set_grad_enabled : 함수의미는 역전파 시키는 객체들을 메모리에 올려준다(학습에 필요한 데이터)
        # with문은 if문이 아니고 train일 때 이 함수를 사용하라는 거지 다른 경우도 들어올 수 있다.
                with torch.set_grad_enabled(tv == 'train'): # with: with문 밖으로 나가면 자원이 반납된다... val이어도 안으로 들어간다...
                    prediction = model(image) # 모델에 이미지를 넣어서 예측값을 구한다...
                    loss = loss_func(prediction, target)
                    
                    if tv == 'train':
                        optimizer.zero_grad() # zero_grad(): 기울기 초기화 시키라는...안해주면 틀린 loss값을 누적하게 된다.
                                 # 옵티마이저 : SGD를 사용, zero_grad() : 초기화 시켜아
                                            # 이 옵티마이저는 실제 예측값을 구하고 차를 구해 
                            # 직선의 방정식에 의한 예측값을 뽑아 후에 
                            # 미분을 구해 어느 쪽으로 이동해야 하는지 학습률만큼 이동을 하고 기울기가 같은 것으로
                            # 초기화를 안하면 기울기 값을 구해야 하는데 계속 값이 추가되어   
                        loss.backward() # 역전파 계산, 미분
                        optimizer.step() # 새로운 기울기 적용
                        
                running_loss += loss.item()
                running_correct += get_accuracy(image, target, model)
                
                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

     

    # 예측할때마다 학습을 시킨다면 오래걸리니 학습시킨 파일로 만들어준다
    # 모델을 저장한 파일을 생기게 만든다. 학습하면서 저장위해 계속 학습보단 있는 내용을 파일로 저장해서 활용하자
    def save_best_model(model_state, model_name, save_dir='./trained_model'):
        os.makedirs(save_dir, exist_ok=True) # 존재하면 덮어쓰겠다...
        torch.save(model_state, os.path.join(save_dir, model_name)) # 좋을때마다 저장해준다...
        
         # 계속 저장을 하면 너무 쓰잘데 없는 파일이 많이 생기기 때문에 로스 값이 적을 때만 저장을 할 조건을 정의하자

    13. 모델 학습 수행하기

    device = torch.device('cpu')
    
    train_data_dir = './Covid19-dataset/train'
    val_data_dir = './Covid19-dataset/test'
    
    dataloaders = build_dataloader(train_data_dir, val_data_dir)
    model = build_vgg19_based_model(device_name='cpu')
    loss_func = nn.CrossEntropyLoss(reduction='mean')
    optimizer = torch.optim.SGD(model.parameters(), lr=1E-3, momentum=0.9)

     

    # num_epochs = 10 : 학습할 epoch 수를 설정합니다. 즉, 데이터셋 전체를 10번 반복하여 학습합니다.
    # best_acc = 0.0 : 현재까지 최고 정확도를 저장하는 변수를 0.0으로 초기화합니다.
    # train_loss, train_accuracy = [], [] : 학습 데이터셋의 손실과 정확도를 저장하기 위한 리스트를 생성합니다.
    # val_loss, val_accuracy = [], [] : 검증 데이터셋의 손실과 정확도를 저장하기 위한 리스트를 생성합니다.
    # 그리고 for loop을 통해 epoch 수만큼 반복하면서 다음 작업을 수행합니다.
    
    # train_one_epoch 함수를 호출하여 학습을 수행합니다. 이 함수는 dataloaders에서 가져온 배치 데이터를 사용하여 모델을 학습하고, 학습 데이터셋과 검증 데이터셋의 손실과 정확도를 계산합니다.
    # 각 epoch 마다 학습 데이터셋과 검증 데이터셋의 손실과 정확도를 각각의 리스트에 추가합니다.
    # 학습 정보를 출력합니다.
    # 만약 epoch 수가 3보다 크고 검증 데이터셋의 정확도가 현재까지의 최고 정확도보다 높은 경우, 모델의 상태를 best_model에 저장하고 최고 정확도를 best_acc에 저장합니다. 그리고 save_best_model 함수를 호출하여 최고 모델을 파일로 저장합니다.
    # 최종적으로 최고 정확도를 출력합니다.
    # 위 코드는 학습 과정에서 모델의 성능을 모니터링하고, 최고 성능을 달성한 모델의 상태를 저장하는 등의 다양한 기능을 포함하고 있습니다.
    
    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 Loss:{accuracies['train']}, val_Loss:{accuracies['val']}")
              
        if(epoch > 3) 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 Accracy: {best_acc}')

    # num_epochs = 10 : 학습할 epoch 수를 설정합니다. 즉, 데이터셋 전체를 10번 반복하여 학습합니다.
    # best_acc = 0.0 : 현재까지 최고 정확도를 저장하는 변수를 0.0으로 초기화합니다.
    # train_loss, train_accuracy = [], [] : 학습 데이터셋의 손실과 정확도를 저장하기 위한 리스트를 생성합니다.
    # val_loss, val_accuracy = [], [] : 검증 데이터셋의 손실과 정확도를 저장하기 위한 리스트를 생성합니다.
    # 그리고 for loop을 통해 epoch 수만큼 반복하면서 다음 작업을 수행합니다.

    # train_one_epoch 함수를 호출하여 학습을 수행합니다. 이 함수는 dataloaders에서 가져온 배치 데이터를 사용하여 모델을 학습하고, 학습 데이터셋과 검증 데이터셋의 손실과 정확도를 계산합니다.
    # 각 epoch 마다 학습 데이터셋과 검증 데이터셋의 손실과 정확도를 각각의 리스트에 추가합니다.
    # 학습 정보를 출력합니다.
    # 만약 epoch 수가 3보다 크고 검증 데이터셋의 정확도가 현재까지의 최고 정확도보다 높은 경우, 모델의 상태를 best_model에 저장하고 최고 정확도를 best_acc에 저장합니다. 그리고 save_best_model 함수를 호출하여 최고 모델을 파일로 저장합니다.
    # 최종적으로 최고 정확도를 출력합니다.
    # 위 코드는 학습 과정에서 모델의 성능을 모니터링하고, 최고 성능을 달성한 모델의 상태를 저장하는 등의 다양한 기능을 포함하고 있습니다.

     

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

     

     

    14. 테스트 이미지를 통한 학습모델 분류 성능 검증하기

    data_dir = './Covid19-dataset/test/'
    class_list = ['Normal', 'Covid', 'Viral Pneumonia']
    
    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')
    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(batch), C, H, W
        return tensor_image
    def model_predict(image, model):
        tensor_image = preprocess_image(image) # 전처리
        prediction = model(tensor_image) # 예측
        
        _, pred_label = torch.max(prediction.detach(), dim=1) # 라벨을 떼서 최고값을 구함
        print('prediction.detach: ', prediction.detach())
        print('pred_label: ', pred_label)
        pred_label = pred_label.squeeze(0)
        print('pred_label2: ', pred_label)
        return pred_label.item()
        
    ckpt = torch.load('./trained_model/model_07.pth')
    
    model = build_vgg19_based_model()
    model.load_state_dict(ckpt) # 학습된 모델을 넣어줌
    model.eval()
    min_num_files = min(len(normals_list), len(covids_list), len(pneumonias_list))
    
    @interact(index=(0, min_num_files-1))
    def show_result(index=0):
        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'Pred:{class_list[prediction_1]} | GT:Normal')
        plt.imshow(normal_image)
        
        plt.subplot(132)
        plt.title(f'Pred:{class_list[prediction_2]} | GT:Covid')
        plt.imshow(covid_image)
        
        plt.subplot(133)
        plt.title(f'Pred:{class_list[prediction_3]} | GT:Pneumonia')
        plt.imshow(pneumonia_image)
        plt.tight_layout()

    15. 결론

    • CNN에 대한 학습
    • 이미지 데이터셋 구축
    • Torchvision transforms 라이브러리를 활용한 텐서형 데이터 변환
    • VGG 19 모델을 불러와 Head 부분을 수정
    • Cross entropy Loss Function, SGDM 적용
    • 인간 추론원리와 닮은 딥러닝 결과 출력
    반응형