ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Transfer Learning Tutorial(전이학습)
    AI Basic 2023. 3. 12. 01:04

    전이학습이 어떤 방식으로 이루어 지는지 궁금해서 

     

    아래 예제로 공부하면서 코드를 작성해봤다. 재사용을 위해서 리팩토링을 했므으로 같은 함수이다.

     

    출저 : https://9bow.github.io/PyTorch-tutorials-kr-0.3.1/beginner/transfer_learning_tutorial.html

     

    전이학습(Transfer Learning) 튜토리얼 — PyTorch Tutorials 0.3.1 documentation

    이 튜토리얼에서는 전이학습(Transfer Learning)을 이용하여 신경망을 어떻게 학습시키는지 배워보겠습니다. 전이학습에 대해서 더 알아보시려면 CS231n 노트 를 읽어보시면 좋습니다. 실제로 충분한

    9bow.github.io

    나는 위 예제가 가장 좋은것 같다. 그래서 위 예제를 따라하면 전의 학습이 어떻게 이루어 지는지 알아보자!

     

    사전 준비 지식

    위 예제에서는 resnet 이라는  network를 사용한다. 자세한것은 인터넷에 찾아보면 되고 핵심적인 내용은

    이미지를 분류하는 네트워크이고 1000개로 이미지를 분류한다.

    네트워크 복잡도에 따라서 resnet18, resnet50,resnet101,resnet152로 나뉜다.

    여기서 내 경우는 resnet18, resent152를 사용해서 얼마나 성능향상이 있는지 알아본다.

    아래 글에  resnet에대해서 간단하게 잘 적어져 있으니 참조하면 좋을것 같다.

     

    출처 : https://bskyvision.com/entry/CNN-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EB%93%A4-ResNet%EC%9D%98-%EA%B5%AC%EC%A1%B0

     

    [CNN 알고리즘들] ResNet의 구조

    LeNet-5 => https://bskyvision.com/418 AlexNet => https://bskyvision.com/421 VGG-F, VGG-M, VGG-S => https://bskyvision.com/420 VGG-16, VGG-19 => https://bskyvision.com/504 GoogLeNet(inception v1) => https://bskyvision.com/539 ResNet => https://bskyvision.co

    bskyvision.com

     

    1. 사전 준비

    데이터를 다운 받는다.  pytorch tutorial에 

    ~~~

    메모

    데이터를 여기 에서 다운로드 받아 현재 받고 압축을 해주세요.

    ~~~

    내 경우는 코드 기준 ../data/hymenoptera_data 위치에 데이터를 저장했음.

    그러면 아래와 같은 구조를 가짐.

    data

    |-- hymenoptera_data

            |-- train

            |-- val

     

    |-- learning-ai-knowledge

     

    https://github.com/freddiekimN/learning-ai-knowledge/tree/study/transfer_learning_tutorial

     

    GitHub - freddiekimN/learning-ai-knowledge

    Contribute to freddiekimN/learning-ai-knowledge development by creating an account on GitHub.

    github.com

    코드는 여기 있다.

    브랜치를 transfer_learning_tutorial로 수정후 다운 받으면됨.

     

    그리고 네트워크의 입출력에대해서 알아한다.

     

    기존 resnet 의 입력은 3x244x244 이고 출력은 1000개 이다.

     

    그러나 예제에서는 3x244x244  을 받아서 출력은 2개(벌, 개미) 만 분류하는 예이다.

     

    그리고 샘플 데이터는 

     

    train  데이터의 크기는 개미는 124개 이미지 이고 벌은 121개 이미지 이다.

     

    validation  데이터 크기는 개미는 70개 벌은 83개이다.

     

    batch 사이즈는 4개단위로 학습한다.

     

    위 정도 사전지식을 알면 코드를 분석하기 훨씬 편하다.

     

    우선 내가 볼때 transfer learning은 3가지 방법이 있는것 같다.

     

    1. 모든 파라미터를 학습시킨다. 2. 가져온 파라미터는 고정시키고 뒷단만 학습시킨다.3. 둘다 한번씩한다.

     

    즉 이번 전이 학습은 1000개의 클래스로 학습된 모델을 2개로 다시 학습하는 예이다.

     

    from __future__ import print_function, division
    
    import torch
    import torchvision
    from torchvision import datasets, models, transforms
    import matplotlib.pyplot as plt
    import os
    from torchsummary import summary as summary
    from imshow import imshow
    from visualize_model import visualize_model
    from train_model import train_model
    from train_model_main import train_model_main
    
    plt.ion()   # interactive mode
    
    # 학습을 위한 데이터 증가(Augmentation)와 일반화하기
    # 단지 검증을 위한 일반화하기
    data_transforms = {
        'train': transforms.Compose([
            transforms.RandomResizedCrop(224),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ]),
        'val': transforms.Compose([
            transforms.Resize(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ]),
    }
    
    data_dir = '../data/hymenoptera_data'
    image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                              data_transforms[x])
                      for x in ['train', 'val']}
    dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,
                                                 shuffle=True, num_workers=4)
                  for x in ['train', 'val']}
    dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
    class_names = image_datasets['train'].classes
    
    use_gpu = torch.cuda.is_available()
    
    # Get a batch of training data
    inputs, classes = next(iter(dataloaders['train']))
    
    # Make a grid from batch
    out = torchvision.utils.make_grid(inputs)
    
    imshow(out, title=[class_names[x] for x in classes])
    
    # 모델 학습하기
    model_ft = models.resnet18(pretrained=True)
    model_ft = train_model_main(model_ft,True,use_gpu,dataloaders,class_names,dataset_sizes)
    
    model_conv = models.resnet18(pretrained=True)
    model_conv = train_model_main(model_conv,False,use_gpu,dataloaders,class_names,dataset_sizes)
    
    model_152_ft = models.resnet152(pretrained=True)
    model_152_ft = train_model_main(model_152_ft,True,use_gpu,dataloaders,class_names,dataset_sizes)
    
    model_152_conv = models.resnet152(pretrained=True)
    model_152_conv = train_model_main(model_152_conv,False,use_gpu,dataloaders,class_names,dataset_sizes)

     

    #3번 을 했을때 결과이다.## resnet18 #모든 파라미터를 학습하므로 시간이 많이 걸린다.
    Training complete in 1m 26s
    Best epoch 5 and val Acc: 0.954248

     

    #뒷단만 하므로 시간이 적게 걸린다.
    Training complete in 0m 49s
    Best epoch 1 and val Acc: 0.947712

     

    ## resnet152 
    Training complete in 8m 14s
    Best epoch 19 and val Acc: 0.967320

    Training complete in 3m 47s
    Best epoch 4 and val Acc: 0.973856

    # 1번 각각 다 학습함
    # resnet18 
    Training complete in 1m 19s
    Best epoch 3 and val Acc: 0.967320

     

    # 2번 뒷단만 학습함

    # resnet18

    Training complete in 0m 44s
    Best epoch 4 and val Acc: 0.960784

     

    # 1번 각각 다 학습함

    # resnet152

    Training complete in 7m 59s
    Best epoch 24 and val Acc: 0.980392

     

    # 2번 뒷단만 학습함

    # resnet152

    Training complete in 4m 2s
    Best epoch 2 and val Acc: 0.954248

    근데 위 결과를 다 믿을 수는 없다. 이유는 suffle=True이기때문에 어떻게 잘 썪이는지가 약간 중요했다.

     

    횟수 늘리고 데이터 늘리면 되겠지만 시간도 없고 의지도 없다. 

     

    내가 볼때 여기서 주목할 것은 

    # 2번 뒷단만 학습함

    # resnet18

    Training complete in 0m 44s
    Best epoch 4 and val Acc: 0.960784

    이 데이터 이다. 

     

    당연히 

    # 1번 각각 다 학습함

    # resnet152

    Training complete in 7m 59s
    Best epoch 24 and val Acc: 0.980392

    이 조건이 결과가 제일 좋았지만 

     

    처음한것이 학습시간이 1/8이 었다. 즉 간단한 모델과 적은 데이타가 있어도 전이학습을 잘만하면 최상은 아니지만 괜찮은 결과를 얻을 수 있다. 반증 같다.

     

    참고로 3번 처럼 시험하기 위해서는 아래 처럼 코드를 수정하면 된다.

    이유는 train_model.py 를 보면 가장 좋은 모델을 저장하는 코드가 내장되어 있다.

    그래서 생성부분을 주석 처리하고 출력을 다시 입력으로 넣어 주면된다.

    best_model_wts = copy.deepcopy(model.state_dict())
    model.load_state_dict(best_model_wts)
    # 모델 학습하기
    model_ft = models.resnet18(pretrained=True)
    model_ft = train_model_main(model_ft,True,use_gpu,dataloaders,class_names,dataset_sizes)
    
    #model_ft = models.resnet18(pretrained=True)
    model_ft = train_model_main(model_ft,False,use_gpu,dataloaders,class_names,dataset_sizes)
    
    model_152_ft = models.resnet152(pretrained=True)
    model_152_ft = train_model_main(model_152_ft,True,use_gpu,dataloaders,class_names,dataset_sizes)
    
    #model_152_ft = models.resnet152(pretrained=True)
    model_152_ft = train_model_main(model_152_ft,False,use_gpu,dataloaders,class_names,dataset_sizes)

    구조 파악하기 

    1. 모델 가져오기.

    model_ft = models.resnet18(pretrained=True)

    맨 마지막에 있는 fully connect layer를 변경함. 입력개수는 같고 출력개수가 1000개 에서 2개로 변경함.

    num_ft = model_ft.fc.in_features
    model_ft.fc = nn.Linear(num_ft, 2)

    학습과 검증을 번갈아가변서 실행됨.

    num_epochs = 25번 이므로 25번 실행되고

    batch_size=4

    이므로 한번에 4개의 이미지를 비교함.

    for epoch in range(num_epochs):
    print('Epoch {}/{}'.format(epoch, num_epochs - 1))
    print('-' * 10)

    # Each epoch has a training and validation phase
    for phase in ['train', 'val']:
    if phase == 'train':
    scheduler.step()
    model.train(True) # Set model to training mode
    else:
    model.train(False) # Set model to evaluate mode

    모델을 실행하고 결과가 나옴.

    batch_size = 4 이고 출력 개수는 2개이므로

    output.data 는 4 x 2 의 행렬 크기를 가짐. 

    dim = 1이라는 의미으로  max 를 하면 4x1이 되고 

    preds  는 index 를 저장함.

    outputs = model(inputs)
    _, preds = torch.max(outputs.data, 1)

     

    running_loss += loss.data.item() * inputs.size(0)
    running_corrects += torch.sum(preds == labels.data)

    검증 개수는 153개

    훈련개수는 244개임.

    epoch_loss = running_loss / dataset_sizes[phase]
    epoch_acc = running_corrects / dataset_sizes[phase]

    인덱스를 이용해서 같은 개수 누적해서 확률을 개산함.

     

    훈련은 61번 실행되고 

    검증은 39번 실행됨 

    batch size 4  이므로

    전체 개수에서 정수로 떨어지지 않을 경우 더 많이 실행되는것을 알 수있음.

    39x4=156

    61x4=244

    train Loss: 0.4669 Acc: 0.8074 Cnt:61
    val Loss: 0.2914 Acc: 0.8954 Cnt:39

     

    질문1.

    위 코드에서 batch size는 성능에 영향을 미칠까?

    YES

    이유는 아래 코드 때문임.

    4개에서 loss 를 계산하고 *4를함.

    즉 너무 크거나 너무 작으면 학습이 잘 안 될것 같음.

    loss = criterion(outputs, labels)
    loss.data.item() * inputs.size(0)

     

     

     

     

     

     

    반응형
Designed by Tistory.