본문 바로가기
Project/Image Classification Pipeline Project

6. ResNet50 Transfer learning & fine tuning 적용하기(이미지 수집부터 분류 모델까지)

by inhovation97 2021. 8. 2.

이 포스팅은 아래 흐름대로 진행되는 포스팅입니다.

데이터 수집 - 전처리 - 모델링 - 성능 개선

 

이 포스팅에서 저는 직접 이미지를 크롤링하여 수집하고(링크1), 수집한 이미지를 정리하고(링크2), 직접 전처리하며(링크3), 모델을 설계했고(링크4)

이번 포스팅을 마지막으로 finetuning으로 성능 개선을 합니다.

 

처음엔 Accuracy가 절반도 안됐지만, 성능이 얼마나 개선되었을까요?이 글을 통해 제 모델링 결과를 확인하시면, 아마 finetuning에 관한 감이 제대로 잡히실 것 같습니다.

 

먼저 fine tuning과 transfer learning의 개념이 헷갈리시는 분들은 이 글(fine tuning 설명)을 읽고 오시길 추천드립니다.

 

이전 포스팅 링크

 

5. Pytorch 텐서보드 활용 & image augmentation 중요성-실험 결과(이미지 수집부터 분류 모델까지)

포스팅 순서

< fine tuning으로 성능을 높여보자! >

1. fine tuning 설계에 대한 설명

2. fine tuning 적용하기
   - 중간 과정 모델fc layer 선행학습 시켜주기
   - 중간 과정 모델model 저장하기
   - 중간 과정 모델 성능

3. 최종 모델 학습하기
   - 최종 모델 정의하기
   - 중간 과정 모델의 파라미터를 transfer하기
   - fine tuning 최종 성능

4. fine tuning 최종 인사이트

티스토리 코드 가독성 때문에 먼저 코드 파일을 공유합니다. (주석도 잘 달아놓음)

코랩으로 열기를 권장합니다.

https://github.com/inhovation97/personal_project/blob/main/pytorch/pytorch_project_fine_tuning.ipynb

 

GitHub - inhovation97/personal_project: 개인 프로젝트

개인 프로젝트. Contribute to inhovation97/personal_project development by creating an account on GitHub.

github.com

 

 

 

<fine tuning 설계에 대한 설명>

 

 

 

Data 양이 적을 때는 transfer learning이 최선의 방법이겠죠?

먼저 제가 fine tuning을 어떻게 설계했는지 설명하기 이전 제 포스팅을 시퀀스대로 안읽으셨을 수 있으니 제 데이터셋부터 설명드립니다.

 

직접 크롤링한 데이터셋이며, class는 shark, whale, dolphin 3개입니다. 

dataset 수가 너무 작아 augmentation을 했고, 이는 성능 gain의 효과를 봤습니다.(이 결과도 흥미롭습니다.)

사전학습된 imagenet 데이터셋에는 shark와 whale 클래스가 존재하여 어느정도 유사한 데이터셋입니다.

Trainset : 1,049장

 Testset : 2~300장 

 

 

 

 

 

그럼 이제 제가 finetuning을 어떻게 설계했는지 봅시다. 

제가 위에 달았던 링크에서 제가 설명한 fine tuning글을 보고오시면, 4가지의 경우로 전략을 나눴었습니다.

사전학습된 데이터셋과 유사하고, 현 데이터셋의 양은 적은 경우의 fine tuning

제 데이터셋과 imagenet과의 데이터셋은 꽤나 유사하다고 볼 수 있습니다. 클래스가 3개중에 2개나 겹치니까요. (이미지넷으로 학습한 모델은 그냥 feature extractor로 좋긴합니다.)

 

fc layer는 output feature가 3개이므로 당연히 수정을 해주어야겠죠?

이제 이 부분에서 많은 고민을 해야합니다.

제가 올린 링크에서 저는 모든 layer는 꼭 1번 이상 학습이 완료된 상태에서 finetuning을 진행한다고 했습니다. 왜냐하면 Conv layer의 가중치는 이미 선행 학습으로 weight, bias들이 안정적인 값을 가지고 있습니다. 나머지 layer를 그냥 finetuning으로 학습을 진행하면, optimizer가 갑자기 새롭게 만든 가중치가 없는 fc layer를 만나게됩니다. 

그렇게되면 학습이 안정적이지 않고, backprop이 잘 안될 수 있습니다.

하지만 제가 쓰기로한 resnet50은 마지막 Conv layer에서 2048의 output feature를 요구합니다. 이 말은

즉, 제가 새로 만들어야하는 fc layer가 입력 dimension이 2048 출력 dimension이 3인 fc layer를 만들어야하는 겁니다.

 

resnet이든 제가 직쩝 짜든 2048 channel의 feature map을 출력하는 conv 모델을 짜고, fc layer를 선행 학습해와서 이걸 resnet50 fc layer로 써야하는 상황인데, 아무래도 학습 데이터가 약 1,000장이니 과적합이 될 것 같고, 시간도 엄청 오래걸릴 것이 예상됩니다.

그래서 이렇게 전략을 짰습니다.

 

1. pretrained resnet50 모델에 파라미터가 학습되지 않은 fc layer를 붙여주어 아키텍쳐를 설계합니다.

 

2. 설계한 아키텍쳐에서 Conv layer의 가중치는 전부 freeze시킨 뒤, fc layer만을 학습시킵니다. (약간 값이 튈 수 있습니다.) (이 모델을 '중간 과정 모델'라고 하겠습니다.)

 

3. 이제 전부 1차 학습이 완료된 이 아키텍쳐의 가중치를 전부 trainable로 설정하고, 전체 학습시킵니다.

(이 모델을 '최종 단계 모델'라고 하겠습니다.)

 

경험이 조금씩 쌓이다보니, 드는 생각은 딱 정답이 있는 것 같지는 않습니다. 딥러닝 모델은 그 내부를 제대로 확인할 수 없으니 가설을 세우고 실험해가는 것이 중요한 것 같습니다.

이 가설도 정답은 아닙니다! 생각하는대로 학습되지는 않더군요. 

 

 

 

<finetuning 적용하기>

 

 

 

1. 중간 과정 모델 fc layer를 선행학습 시켜주기

위에서 말한대로 2048 dim을 받아 3 dim을 출력해주는 fc layer의 가중치를 선행 학습 시켜줍시다.

불러온 pretrained resnet50의 가중치르 전부 freeze시킨 뒤, fc layer를 새로 정의해주면 fc layer만 학습이 됩니다.

optimizer를 선언할때 fc layer의 파라미터만 가져와도 똑같이 fc layer만 학습시킬 수 있습니다.

 

 

모델 저장 함수를 정의합니다. 

파이토치는 state_dict 함수를 통해서 여러가지 파라미터들을 사전형태로 save가 가능한 것이 장점입니다.

모델의 가중치만 'net'으로 저장해줍시다.

torch.save 함수에는 딕셔너리형으로 정의한 파라미터와 저장경로+파일명을 인자로 넣어줍시다.

저는 model.eval에서 if문을 걸어 저장해주었습니다.

 

 

 

2. 중간 단계 모델 성능

현재 fc layer는 아예 학습되지 않은 파라미터라서 학습 값이 많이 튈 줄 알았는데, 안정적으로 잘 학습 되더군요!

절반도 나오지 않던 성능이 Accuracy 82% 까지 성능 향상을 했습니다!

이정도만해도 정말 엄청난 성능 gain입니다.

 

제가 설정한 하이퍼 파라미터를 보면, learning rate의 값이 굉장히 작죠? fine tuning은 이렇게 작은 lr로 학습시켜야 안정적으로 진행됩니다.

 

 

 

<최종 모델 학습하기>

 

 

 

1. 최종 모델 정의하기

torchvision으로 resnet50을 가져옵니다.

어차피 모델 파라미터는 중간 과정 모델을 쓸 것이니 none pretrained model을 불러옵니다.

fc layer도 새로 달아줍시다.

 

pytorch도 summary함수가 있더군요!

summary로 한번 모델을 확인해봅니다. fc layer가 2048 feature를 받아 3 output을 출력합니다.

 

첫 Conv layer의 필터를 시각화해봅니다. 당연히 none pretrained model이라서 필터가 깔끔합니다. 이제 이 깡통 모델에 파라미터를 transfer해주고, 다시 필터를 시각화하여 차이를 확인해봅시다.

 

 

 

2. 중간 과정 모델의 파라미터를 transfer하기

torch.load 함수로 저장했던 파라미터를 불러옵니다.

load_state_dict 함수로 위에서 정의한 모델에 가중치를 transfer해줍니다.

가중치를 사전형태로 저장했기 때문에 key 값을 지정해주어야 원하는 파라미터를 전이받을 수 있습니다. 이게 파이토치의 큰 장점같아요. 파라미터만 가볍게 저장해서 원하는 파라미터만 전이받을 수 있죠.

 

파라미터를 전이받은 모델의 필터를 시각화해보니, 필터가 예사롭지 않습니다. 저 필터들이 이미지를 훑으면서 특징을 추출할겁니다.

 

이제 이 모델은 모든 layer의 가중치가 전부 학습돼있고, trainable합니다.

 

 

 

3. fine tuning 최종 성능

최종 Accuracy가  92.4%까지 나왔습니다!

맨 처음엔 정확도 40%대 였는데, 최종적으로 92% 성능으로 분류를 완료했습니다. 직접 구글에서 긁어온 사진들로 이렇게 분류가 잘 되니 신기합니다. epoch는 100이 기준입니다.

 

 

 

<fine tuning 최종 인사이트>

 

뭔가 아쉬운 점이 좀 더 많은데...

92%의 성능이 나왔지만 위에 최종 학습 그래프를 보시면, 성능 값이 너무 많이 튀는게 보이시죠?

 

다짜고짜 학습되지 않은 fc layer를 끼우고 학습하는 것이 안좋을 것 같아서, 이렇게 설계했던건데도 불구하고 많이 튀는 모습입니다. 그래서 저는 또 비교하기위해 실제로 위 최종 모델에서 Conv layer는 resnet50의 가중치를 transfer하고 학습되지 않은 fc layer를 끼워 학습해봤습니다.

 

왼쪽이 학습한 fc layer 오른쪽이 학습하지않은 fc layer를 finetuning한 결과입니다.

제가 설계한 fine tuning의 loss가 조금 더 편차가 작은 것 같긴하지만, 최대 accuracy는 1%미만으로 밖에 차이가 나지 않습니다...

개인적으로는 전체 layer보다는 conv layer 상단 일부와 fc layer만 trainable로 열어두면 훨씬 더 안정적으로 학습이 되지 않았을까 싶습니다...

괜히 요란하게 헛고생한 느낌...

 

아 그리고 통상 transfer learning시에는 SGD 모멘텀을 쓰는 것이 더 안정적이다라는 시선이 있습니다.

( Learning rate & batch size best 조합 찾기 (feat.논문리뷰와 실험결과) <-제 포스팅 중에 실험했던 이력이 있습니다)

이번 실험에 그것도 확인해봤는데,

역시 Adam이 더 빨리 잘 수렴하더군요. (epoch를 천 단위로 가져가면 모르겠습니다!)

 

 

 

이렇게 fine tuning을 마지막으로 최종 Accuracy 92%로 제 프로젝트가 끝이 났습니다.

저도 직접 수집한 데이터를 분류하는 것은 처음이라 크롤링 할 때만해도, 잘 마무리할 수 있을지 걱정 됐습니다. 포스팅을 질러놓고 시작한 프로젝트였습니다. 그렇게 중간에 아예 학습되지 않아 절반도 분류하지 못했을 때는 정말로 난감했는데 예상보다도 훨씬 더 높은 성능의 모델을 설계해서 재밌었습니다! 

 

이미지 수집부터 모델 설계까지 있었던 흥미로운 인사이트를 공유하는 다음 포스팅을 마지막으로 이번 프로젝트 포스팅을 마무리하겠습니다. 

https://inhovation97.tistory.com/42

 

7. [최종] 이미지 분류 모델 인사이트 (이미지 수집부터 분류 모델까지)

이 포스팅은 아래 흐름대로 진행되는 포스팅입니다. 데이터 수집 - 전처리 - 모델링 - 성능 개선 이번 마지막 포스팅은 직접 수집한 이미지를 분류하면서 얻은 인사이트를 공유합니다. 1. 파

inhovation97.tistory.com

댓글