전이학습(Transfer Learning) & 평가지표(model evaluation)

전이학습 기초

전이학습이란?

전이학습

  • 특정 데이터로 훈련된 모델을 다른 데이터 형태로 학습

전이학습의 목적
  • 훈련할 데이터가 충분치 않다.
    • 학습된 모델을 사용하자


전이학습 모델 구성

  • head: classification(분류)
  • backbone: Filtering(필터링)


전이학습 방법

제한사항

  • 입력이미지 크기와 출력레이블이 모델과 맞지 않는다.


전이학습 문제

  • 입력 이미지가 바뀔때, conv층은 영향 받지 않으나, 완전연결층의 첫번째 층은 영향 받음
  • 출력레이블이 바뀔 때, 나머지 층은 영향받지 않으나, 완전연결층의 마지막 층만 영향 받음.

전이학습 해결책
  • 입력이미지 크기와 레이블 수를 학습 데이터에 맞추던지
    • 입력이미지 크기는 맞출 수 있지만, 레이블 수 맞추기는 불가능

  • 모델의 완전연결층 부분을 재설계 하던지
    • 모델의 완전연결층(head, 분류) 부분 재설계

  • 입력이미지 크기를 바꾸면 backbone(필터링)은 그대로,
    head(분류)는 재설계 해야한다.

전이학습 과정
  1. head만: Mydata로 head만 학습
  2. fine-tuning: Mydata로 모델 전체 재학습

Pytorch CNN 전이학습

dir(torchvision.models) # 참고사항: 모델 리스트 조회
  • 모델: resnet50, googlenet

  • 데이터셋: CIFAR-10

전처리

# 데이터 전처리 및 증강
transform_train = transforms.Compose([
    transforms.RandomCrop(32, padding=4),
    transforms.RandomHorizontalFlip(), #증강
    transforms.Resize(224),  # ResNet과 GoogLeNet의 입력 크기에 맞춤
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])

# 전처리
transform_test = transforms.Compose([
    transforms.Resize(224), #레즈넷 사이즈에 맞춤
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])

데이터셋 로드

# CIFAR-10 데이터셋 로드 - [ (데이터, 레이블) * batch_size ]
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                       download=True, transform=transform_train)
trainloader = DataLoader(trainset, batch_size=64, shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                      download=True, transform=transform_test)
testloader = DataLoader(testset, batch_size=64, shuffle=False, num_workers=2)

데이터셋 레이블

classes = ('plane', 'car', 'bird', 'cat', 'deer',
           'dog', 'frog', 'horse', 'ship', 'truck')

DataLoader

trainloader = DataLoader(trainset, batch_size=64, shuffle=True, num_workers=2)
# image 64개, Label 64개 쌍으로 묶어준다.

모델의 특징

ResNet50 모델의 특징
  • 입력이미지: 224, 224, 3

  • head
    • in_features: 2048
      • avgpool 이 있으므로 바꿀 필요 없다.
    • out_features: 1000 

GoogLeNet 모델의 특징
  • 입력이미지: 224, 224, 3

  • head
    • in_features: 1024
      • avgpool 이 있으므로 바꿀 필요 없다.
    • out_features: 1000

모델 정의

# resnet50
resnet_model = resnet50(pretrained=True) # imagenet으로 사전학습된 상태 가져오기
num_ftrs = resnet_model.fc.in_features    # 2048 그대로 사용
resnet_model.fc = nn.Linear(num_ftrs, 10) # 2048, 10으로 선언 

# googlenet
googlenet_model = googlenet(pretrained=True)
num_ftrs = googlenet_model.fc.in_features     # 1024 그대로 사용
googlenet_model.fc = nn.Linear(num_ftrs, 10)  # 1024, 10으로 선언

학습 함수

def train_model(model, criterion, optimizer, num_epochs=5):
    # criterion(오차함수), optimizer(기울기 최적화)
    best_acc = 0.0
    train_losses = [] # 시각화를 그리기 위해 모아줌
    train_accuracies = []

    for epoch in range(num_epochs): # epoch 반복
        model.train() # 트레인모드 변경: 모드에 따라 모델에서 on/off 하는 부분이 있다.
        running_loss = 0.0

        for i, (inputs, labels) in enumerate(trainloader): # batch 반복
            inputs, labels = inputs.to(device), labels.to(device)

            optimizer.zero_grad()   # 기울기 초기화
            outputs = model(inputs) # 예측
            loss = criterion(outputs, labels) # 오차확인 예측값과 레이블비교 
            loss.backward() # 기울기 계산
            optimizer.step() # weight 수정

            running_loss += loss.item() 

            if i % 100 == 99: 
                # 1 epoch, 100 번째 batch 평균 오차...
                # 1 epoch, 800 번째 batch 평균 오차
                # 2 epoch....
                print(f'[{epoch + 1}, {i + 1}] loss: {running_loss / 100:.3f}')
                train_losses.append(running_loss / 100) # 훈련 평균 수집
                running_loss = 0.0

        # train 정확도 계산
        model.eval() #평가모드 변경
        correct = 0
        total = 0
        with torch.no_grad():
            for inputs, labels in trainloader:
                inputs, labels = inputs.to(device), labels.to(device)
                outputs = model(inputs)
                _, predicted = torch.max(outputs.data, 1) # value(=확률값), index(=추론한 label)
                total += labels.size(0) # 1개 batch의 갯수만큼 더함. 전체 데이터 갯수
                correct += (predicted == labels).sum().item() # 참이면 1 이므로, 맞춘 갯수를 더한다.

        acc = 100 * correct / total  # 정확도 = 맞춘데이터/전체데이터 비율
        train_accuracies.append(acc) # 훈련 정확도 수집
        print(f'Accuracy on test images: {acc}%')

    return train_losses, train_accuracies

학습

# ResNet 학습
criterion = nn.CrossEntropyLoss() # 오차(cost)함수
optimizer = optim.SGD(resnet_model.parameters(), lr=0.001, momentum=0.9) #최적화 알고리즘, momentum(이전기울기 누적)
resnet_losses, resnet_accuracies = train_model(resnet_model.to(device), criterion, optimizer)

# GoogLeNet 학습
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(googlenet_model.parameters(), lr=0.001, momentum=0.9)
googlenet_losses, googlenet_accuracies = train_model(googlenet_model.to(device), criterion, optimizer)

결과 시각화

plt.figure(figsize=(12, 4))

plt.subplot(1, 2, 1)
plt.plot(resnet_losses, label='ResNet')
plt.plot(googlenet_losses, label='GoogLeNet')
plt.title('Training Loss')
plt.xlabel('Iterations (x100)')
plt.ylabel('Loss')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(resnet_accuracies, label='ResNet')
plt.plot(googlenet_accuracies, label='GoogLeNet')
plt.title('Train Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy (%)')
plt.legend()

plt.tight_layout()
plt.show()

기본 평가지표


Accuracy (정확도)

  • = ( 맞춘갯수 / 전체갯수 ) * 100

Accuracy가 높다고 성능이 좋은걸까?

  • 데이터의 레이블이 불균형(embalanced) 하다면, Accuracy의 신뢰도가 떨어진다.
    • 암환자 1, 정상 99명일 경우, 모두 정상이라고 판정하면 accuracy는 99점이기 때문이다.

  • 데이터 레이블이 불균형(embalanced)하면, 다른 평가지표를 사용하자.

Pasitive 와 Negative

  • Pasitive: 밝혀야 하는 것, 찾아야 하는 것.
  • Negative: 나머지

  • 채점 - 예측
    • TP: True - Pasitive
    • TN: True - Negative
    • FP: False - Pasitive
    • FN: False - Negative

Precision (예측 중에 맞춘 것)

  • Predict Pasitive 중에 맞춘 갯수
  • P = TP / (TP + FP)
  • 예측을 틀리면 안될 때, P를 조절한다면?
    • FP를 줄이기 위해 P를 줄인다.
    • FP가 줄어들지만, FN이 많아진다. 

Recall (실제 중에 맞춘 것)

  • Real Pasitive 중에 맞춘 갯수
  • R = TP / ( TP + FN )
  • 실제를 틀리면 안될 때, P를 조절한다면?
    • FN을 줄이기 위해 P를 늘린다.
    • FN은 줄어들지만, FP가 많아진다.
  • 예시1)
    • 암판정 봇: 의심환자 명단 의사에게 전달
    • 의사는 100명 다 확인 -> 의심환자 50명만 확인 하고 싶다.
    • 의심P 50명(P54/N1), 정상N(P1/N49) 
    • 의심하지 않은 것(정상N) 중에서 암환자가 나오면 큰 일이다.

F1 Score

  • Precision 과 Recall의 조화평균
  • 2 * (서로의 곱) / (서로의 합)
  • F1 = 2 * ( Precision * Recall ) / ( Precision + Recall )

합리적인 모델은 무엇일까?

  • 인간이 사용해서 합리적이라고 느끼는 것.
  • 인간의 정성적인 평가.
  • 인간의 정성적인 평가에 가까운 정량적인 평가지표를 바꾸는 것이 해결 과제다.
    • gpt의 평가지표(합리적이라 느끼는 정성적 평가)는 무엇일까?

ROC_AUC ( = TPR / FPR )

  • TPR: 실제 Pasitive   대비 TP의 비율 (=Recall, Sensitibity)
  • FPR: 실제 Negative 대비 FP의 비율 (= 1-특이도)

  • ROC_AUC 차트
    • FPR은 낮을 수록 좋고, TPR은 높을 수록 좋다.
    • X=FPR, Y=TPR




  • TNR (특이도)
    • TNR: 실제 Negative 대비 TN의 비율 (= 1-FPR)


댓글 쓰기

다음 이전