의료 AI(딥러닝) 공부 일기

CH 03-1. Covid CT image classification

ignuy 2024. 7. 7.

결과 지표는 뭘 어떻게 봐야할까?

의료 데이터가 imbalance 한것은 사실 빈번한 문제이다. 예를들어, 단백질에 임의의 한 효과에 대해서 이게 약으로 작용할 지, 아닐지는 최소 1:10,000 정도의 비율을 가진다. 따라서 이런것들을 판단할 때, 단순히 Accruacy만 바라보는 것은 위험하다. 신뢰하기 위해서는 다른 평가 지표들도 함께 볼 수 있어야 한다.

일단 기본적으로 Classification의 성능은 Threshold에 의해서 나온다.

Classification Threshold

Activation funcion으로 sigmoid나 softmax를 사용하는 이유를 생각해보자. 이 두 함수를 사용하며 output을 도출하는 model은 전부 최종 output은 0에서 1의 값을 가져야 한다. 0에 가까우면 negative이고 1에 가까우면 positive를 나타내는 것이 일반적이다.

여기서 보통 confusion matrix를 활용한다. 양성임을 맞추고, 음성임을 맞추고, 위양성, 위음성을 맞추는 경우를 모으게 된다. 이를 분포도로 바꿔 표현하면 두번째 그림과 같아진다.

이제 threshold를 움직이게 되면 위양성, 위음성률 등이 변화하게 되는데 그 때, ratio의 변화를 찍어서 그래프로 나타내는게 ROC Curve이다.

ROC Curve(receiver operating characteristic curve)

AUROC

ROC Curve를 적분하여 나타낸 Area of Under ROC 값이다. 이 값도 해석을 위한 중요한 지표로 활용된다.

AUROC 가 0.5면 최악의 경우, 1.0이면 가장 좋은 지표를 의미한다.

AUROC가 0.5보다 작다면 랜덤픽보다 성능이 좋지 않음을 의미한다.

AUROC가 0.7보다 낮다면 sub-optimal performance, 0.7-0.8은 good performance, 0.8 이상은 excellent perfomance, 1.0은 perfect classifier에 대응된다.

PR Curve(Precision Recall Curve)

PR Curve도 ROC와 마찬가지로 trade-off 관계를 나타내는 그래프이다. positive sample의 비율로 따지게 된다.

Precision은 모델이 positive라고 했는데 실제로 positive인 경우의 수, Recall은 positvie임을 맞춘 경우의 수가 된다.

PR Curve는 ‘사실 얼마를 나타낸다가 좋은 지표다.’는 해석이 존재하지 않다. 데이터의 특성, 수에 따라 다 해석이 달라지기 때문에 그때그때 확인하고 해석해보자.

PR Curve든 ROC 커브든 threshold가 변함에 따라 FPR, TPR 또는 Precision, Recall 지표가 변함을 보여주는 것이므로 사실상 3차원 그래프로 그려야 함이 맞지만, 해석의 용이상 비율화하여 2차원 그래프로 표현한다.

이를 잘 표현하고 있는 것이 F1 Score이다.

 

CoViD-19 classification

CoviD-19 환자와 정상인의 CT image 데이터를 가지고 CNN모델을 훈련시킬 것이다. 오늘 사용할 모델은 ResNet과 EfficientNet 두가지이다. 이 두 모델의 성능을 비교해보자. 

 

사용한 모델의 코드와 파라미터는 아래 github을 참고해주세요.

https://github.com/Yg-Hong/Deep-Learning-for-Biomedical-Image/tree/main/Covid%20CT%20image%20classification

 

Deep-Learning-for-Biomedical-Image/Covid CT image classification at main · Yg-Hong/Deep-Learning-for-Biomedical-Image

Contribute to Yg-Hong/Deep-Learning-for-Biomedical-Image development by creating an account on GitHub.

github.com

데이터

# Train data의 COVID 데이터 랜덤하게 5개 확인하기

covid_train_path = os.path.join(root_dir, 'Covid_Train', '1_CT_COVID')

covid_files      = [os.path.join(covid_train_path, x) for x in os.listdir(covid_train_path)]
covid_images    =  [cv2.imread(x) for x in random.sample(covid_files, 5)]

plt.figure(figsize=(20,10))
columns = 5
for i, image in enumerate(covid_images):
    plt.subplot((len(covid_images) + columns - 1) // columns, columns, i + 1)
    plt.imshow(image)

정상인과 CoViD-19 환자의 폐 CT 사진이다. 데이터 수는 아래와 같다.

Train 데이터의 COVID 수: 191
Train 데이터의 Non-COVID 수: 234

Valid 데이터의 COVID 수: 60
Valid 데이터의 Non-COVID 수: 58

Test 데이터의 COVID 수: 98
Test 데이터의 Non-COVID 수: 105

 

CH 03-0에서 학습했던 overfiitting을 방지하기 위한 전략 중 두가지가 데이터 transform을 적용하는 코드블럭에서 보인다.

# 데이터 transform 적용하기

normalize = transforms.Normalize(mean=[0,0,0], std=[1,1,1])
image_transforms = {
    'train': transforms.Compose([
    transforms.Resize(256),

    # data augmentation
    # Overfitting을 방지하기 위한 step 1 ~ training set에만 적용
    transforms.RandomResizedCrop((224),scale=(0.5,1.0)),
    transforms.RandomHorizontalFlip(),

    transforms.ToTensor(),
    # Overfitting을 방지하기 위한 step 2
    normalize
    ]),

    'valid': transforms.Compose([
    # 224 X 224는 CNN 모델에서 자주 받는 input size
    transforms.Resize((224,224)),
    transforms.ToTensor(),
    normalize
    ])
}

 

모델 만들기

ResNet

class CovidResNet(nn.Module):
    """pretrain 된 ResNet을 이용해 CT image embedding
    """

    def __init__(self):
		"""
		Args:
			base_model : resnet18 / resnet50
			config: 모델 설정 값
		"""
        super(CovidResNet, self).__init__()

        model = models.resnet50(pretrained=True)
        num_ftrs = model.fc.in_features
        self.num_ftrs = num_ftrs

        """
        param.requires_grad = False 는 모델에 대해서 fine-tuning하지 않겠다는 것을 의미
        이 블럭을 주석처리하거나 False로 만들어야 한다.
        현재같이 데이터가 적은 상황에서는 fine-tuning이 불리하므로 사용하지 않음
        """
        for name, param in model.named_parameters():
            if 'layer2' in name:
                break
            param.requires_grad = False

        self.features = nn.Sequential(*list(model.children())[:-1])

    def forward(self, x):
        x = self.features(x)
        b = x.size(0)
        x = x.view(b, -1)

        return x

EfficientNet

class CovidEfficientNet(nn.Module):
    """pretrain 된 ResNet을 이용해 CT image embedding
    """

    def __init__(self):
		"""
		Args:
			base_model : efficientnet_b0
			config: 모델 설정 값
		"""
        super(CovidEfficientNet, self).__init__()

        # model = models.resnet50(pretrained=True)
        model = models.efficientnet_b0(pretrained=True)
        num_ftrs = model.classifier[-1].in_features
        self.num_ftrs = num_ftrs

        # for name, param in model.named_parameters():
        #     if 'layer2' in name:
        #         break
            # param.requires_grad = False

        self.features = nn.Sequential(*list(model.children())[:-1])

    def forward(self, x):
        x = self.features(x)
        b = x.size(0)
        x = x.view(b, -1)

        return x

이 밖에도 다른 모델을 선택할 수 있으니 torchvision의 docs를 참고하여 활용해보도록 하자.

모델 학습에 사용한 hyperparameter는 gihub의 소스코드에 있다.

 

결과 해석

ResNet: [Validation at Epoch 20 , Accuracy: 0.83743 , sensitivity: 0.77551 specificity: 0.8952380952380953 , roc_score: 0.89173]

EfficientNet: [Validation at Epoch 20 , Accuracy: 0.78325 , sensitivity: 0.75510 specificity: 0.8095238095238095 , roc_score: 0.85034]

좌 - ResNet, 우 - EfficientNet
좌 - ResNet, 우 - EfficientNet

EfficientNet 성능 지표:

  • Accuracy (정확도): 0.78325
  • Sensitivity (민감도, True Positive Rate): 0.75510
  • Specificity (특이도, True Negative Rate): 0.80952
  • ROC AUC Score : 0.85034

ResNet 성능 지표:

  • Accuracy (정확도): 0.83743
  • Sensitivity (민감도, True Positive Rate): 0.77551
  • Specificity (특이도, True Negative Rate): 0.89524
  • ROC AUC Score : 0.89173

성능 지표 비교:

  1. 정확도 (Accuracy):
    • EfficientNet: 0.78325
    • ResNet: 0.83743
    • 비교: ResNet이 더 높은 정확도를 보인다.
  2. 민감도 (Sensitivity):
    • EfficientNet: 0.75510
    • ResNet: 0.77551
    • 비교: ResNet이 조금 더 높은 민감도를 보인다.
  3. 특이도 (Specificity):
    • EfficientNet: 0.80952
    • ResNet: 0.89524
    • 비교: ResNet이 더 높은 특이도를 보인다.
  4. ROC AUC Score:
    • EfficientNet: 0.85034
    • ResNet: 0.89173
    • 비교: ResNet이 더 높은 ROC AUC Score를 보인다.

종합 평가

ResNet 모델이 EfficientNet 모델보다 전반적으로 더 나은 성능을 보이고 있다. ResNet은 정확도, 민감도, 특이도, 그리고 ROC AUC Score 모든 지표에서 EfficientNet을 앞서고 있다. 이는 ResNet이 CoViD-19 환자와 정상인의 CT 데이터를 분류하는 데 더 효율적이라는 것을 의미한다.

댓글