Joonas' Note

Joonas' Note

[딥러닝 일지] 이진 분류를 위한 CNN 모델 작성 (개 vs 고양이) 본문

AI/딥러닝

[딥러닝 일지] 이진 분류를 위한 CNN 모델 작성 (개 vs 고양이)

2022. 3. 9. 15:12 joonas

    이전 글 - [딥러닝 기록] 시작하기 - 개 vs 고양이 분류

     

    [딥러닝 기록] 시작하기 - 개 vs 고양이 분류

    딥러닝을 공부하면서, 헷갈리는 내용이나 앞으로 알아봐야 할 내용들을 블로그에 정리하기로 했다. 까먹는 일이 부지기수고, 오래되면서 머릿 속에 있던 지식들이 섞이면서 점점 헷갈리고 있어

    blog.joonas.io


    모델 작성

    데이터 셋이 어떤 구성으로 되어있는 지 알아보았고, 이제 그 데이터 셋으로 학습을 할 때다.

    모델은 CNN 구조로, kaggle에서 돌아다니는 여러 노트북들을 보며 작성했다.
    기본적인 개념은 Convolution으로 어떤 window 단위로 특징을 추출하고, 추출한 값에서 최대만 다시 추려내는 Max pooling, 그리고 사이사이에 활성화 함수로 ReLU가 있다. 이렇게를 블록(block)으로 보고 여러 block을 쌓아가는 식이다.

    대표적인 CNN 설명 그림인데 출처를 모르겠다.

    위처럼 block들을 쌓아서 특징을 뽑아내는 네트워크를 feautre 레이어로 부르고, 뽑아낸 feature들을 분류하기 위한 Fully Connect 레이어가 붙는다. 여기는 분류기(classifier)라고 부른다.

    Sigmoid가 아니라 ReLU를 사용하는 이유는, Vanishing Gradient Problem을 참고. 요약하자면 Sigmoid 함수는 그래프가 부드러워서 역전파(back propagation)를 진행할 때 반영되는 값이 점점 작아져서 (0.5 * 0.4 * 0.45 * ...) 학습이 제대로 되지 못하는 그런 느낌.

    이 구조로 작성했고, Version 10의 [9]번 노트처럼 출력해보면 아래와 같다.

     

    Dogs vs. Cats Classification

    Explore and run machine learning code with Kaggle Notebooks | Using data from multiple data sources

    www.kaggle.com

    MLPModel(
      (layer): Sequential(
        (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1))
        (1): ReLU()
        (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
        (3): Conv2d(64, 192, kernel_size=(3, 3), stride=(1, 1))
        (4): ReLU()
        (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
        (6): Conv2d(192, 256, kernel_size=(3, 3), stride=(1, 1))
        (7): ReLU()
        (8): Conv2d(256, 384, kernel_size=(3, 3), stride=(1, 1))
        (9): ReLU()
        (10): Conv2d(384, 128, kernel_size=(3, 3), stride=(1, 1))
        (11): ReLU()
        (12): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
      )
      (classifier): Sequential(
        (0): Dropout(p=0.5, inplace=False)
        (1): Linear(in_features=100352, out_features=1024, bias=True)
        (2): ReLU()
        (3): Dropout(p=0.5, inplace=False)
        (4): Linear(in_features=1024, out_features=512, bias=True)
        (5): ReLU()
        (6): Linear(in_features=512, out_features=2, bias=True)
        (7): ReLU()
        (8): Dropout(p=0.5, inplace=False)
        (9): LogSoftmax(dim=1)
      )
    )

    각 레이어마다 앞 뒤 행렬 크기를 맞춰주었고, 마지막의 LogSoftmax(dim=1)은 classifier 레이어의 (6)번 레이어에서 나온 2개의 값을 전체 합이 1로 되도록 만들면서 1차원 행렬로 쭉 펴준다.

     

    모델 학습

    에포크(epoch)마다 train set의 배치를 하나씩 넣어 학습시켰다.

    for epoch in range(EPOCHS):
        running_loss = 0.0
        for inputs, labels in tqdm(train_loader, desc=f'train model ({epoch}/{EPOCHS} epoch)'):
            inputs, labels = inputs.to(device), labels.to(device)
            
            optimizer.zero_grad()
            
            outputs = model.forward(inputs) # 예측값 산출 
            loss = criterion(outputs, labels) # 손실함수 계산
            loss.backward() # 손실함수 기준으로 역전파 선언
            optimizer.step() # 가중치 최적화
    
            running_loss += loss.item()

     

    어떤 일이 일어나고 있는 지는 모르겠지만, 분명 학습이 잘 되고 있다!

    라고 생각했는데, test set으로 확인한 정확도는 다음과 같았고, 처참했다.

    train acc: 50.05438995361328%
    test acc: 49.873111724853516%

     

    무슨 일이 일어난건지 확인해보기 위해서 눈으로 예측 결과와 이미지를 출력하는 스크립트를 작성했다. 인간지능 출동..

    [15]번 노트에서, test1.zip의 이미지들을 읽어서 모델에 넣어 측정(predict)한 결과를 적을 때, 샘플로 몇 개의 이미지와 측정 결과를 저장했다.
    그리고 [16]번 노트에서 이미지를 뽑아봤는 데, 아래와 같았다.

    weight가 어떻게 학습됐는지는 모르겠지만, 전부 cat 으로 예측하고 있는 것으로 보아 학습이 전혀 안되고 있는 것이 분명하다.
    실제로 학습 결과 Tensor를 찍어보면, [0, 0, 0, 0, ..., 0] 으로 전부 0(고양이) 값이 나오고 있다. accuracy에서 50%가 나온 것은 아마도 전체 데이터의 분포가 개와 고양이가 50% 씩이라서 그런 것 같다.

     

    모델 수정

    손실 함수는 CrossEntropyLoss, 최적화는 Adam (learing rate=0.001) 를 사용했는데, 여기에 문제가 있었다.

    CrossEntropyLoss에는 이미 LogSoftmax가 포함되어있기 때문에 작성한 모델 레이어 끝에 있는 LogSoftmax가 문제였다.

    Note that this case is equivalent to the combination of LogSoftmax and NLLLoss.
    from CrossEntropyLoss — PyTorch 1.11.0 documentation

    따라서, 모델 내부의 LogSoftmax를 빼든지 손실 함수를 NLLLoss로 바꾸면 된다.

     

    바꾸고 학습한 결과

    [Version 14]

     

    Dogs vs. Cats Classification

    Explore and run machine learning code with Kaggle Notebooks | Using data from multiple data sources

    www.kaggle.com

    [12]번 노트

    학습 후에 accuracy를 측정해보면 정상적으로 학습된 것을 볼 수 있다. (label과 예측값을 비교하여 맞춘 개수를 계산)

    train acc: 99.4160385131836%
    test acc: 81.78175354003906%

    [17]번 노트 - test1.zip 데이터로 확인

    이제 모델도 학습이 될만한 구조를 갖추었으니, 정확도를 높이기 위해 이것 저것을 해볼 것이다.

    Comments