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

4. Pytroch resnet50 구현하기 (이미지 수집부터 분류 모델까지)

by inhovation97 2021. 7. 2.

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

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

 

이번 포스팅은 전처리한 이미지들을 분류하기위한 모델의 아키텍쳐를 구현하여 학습을 진행합니다.

 

모델은 resnet50을 가져와 데이터에 맞게 아키텍쳐를 새로 구성하고 학습을 진행합니다.

https://inhovation97.tistory.com/37<< 이전 포스팅 [3. 이미지 전처리 - augmentation, normalization]

 

포스팅 순서

< resnet50을 구현해보자! >

1. resnet50 구조 알아보기
2. resnet50 아키텍쳐 세우기
3. resnet50 train/test 진행

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

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

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

 

 

 

< resnet50을 구현해보자!>

 

 

1. resnet50 구조 알아보기

resnet50을 자신의 데이터에 맞게 구현하려면, 먼저 구성을 알아야겠죠?https://inhovation97.tistory.com/25 << resnet에 대해서 잘 모르시거나 까먹으신 분들은 링크에서 한 번 살펴보고 오시면 좋습니다.

 

resnet은 imagenet데이터 학습에 맞춰져있기 때문에 제 데이터에 맞게 아키텍쳐를 구성해봅시다.

bottleneck

resnet은 basic neck과 bottle neck이 있는데, 50부터는 layer를 더 많이 쌓아야하기 때문에 conv가 3개 있는 bottle neck을 씁니다.

input부터 차례대로 내려가봅시다.

Input

Imagenet데이터를 학습했던 아키텍쳐라서 224x224 이미지를 input으로 받습니다.

 

conv1(1번)

stride가 2인 conv와 maxpooling을 거치면서 [64x56x56](C,H,W)가 됩니다.

 

conv2(3번)

stride가 1이기 때문에 feature map사이즈가 줄어들지 않습니다. [256x56x56]이 됩니다.

 

conv3(4번)

bottleneck에서는 채널이 2의 제곱으로 커지기 때문에 conv2에서 256채널이 되었는데, input으로 128채널을 받습니다. 이게 어떻게 된 걸까요? bottleneck*에서 bottleneck으로 넘어갈 때 down samaple을 추가해주어 채널 사이즈를 맞춰주기 때문입니다. 내부에서도 size가 안맞으면 이는 여기에 가서 downsample부분 코드를 뜯어보면, 이해가 쉽습니다.

conv3은 2stride이기 때문에 [512x28x28]이 됩니다.

 

conv4(6번)

마찬가지로 downsmapling을 거치고, 2stride이기 때문에 [1024x14x14]이 됩니다.

 

conv5(3번)

downsmapling을 거치고, 2stride이기 때문에 [2048x7x7]이 됩니다.

 

FClayer(1번)

average pooling후에 들어갑니다.

 

1 + 3개(3+4+6+3) + 1 = resnet50

 

 

 

2. resnet50 아키텍쳐 세우기

resnet50을 파이토치에서 불러오려면, 아래 2가지 방법을 생각할 수 있습니다. 여기에서 py파일을 다운받아오기.torchvision으로 resnet을 불러오기.전 후자를 선택했습니다.

 

제가 전처리한 이미지는 128x128이었죠? 224x224를 받는 resnet50과는 맞지않기 때문에 자신의 데이터셋에 맞게 아키텍쳐를 구성해봅시다. 

 

아 제가 128x128로 resize한 것은 큰 이유는 없습니다. 그냥 최대한 손실을 줄이는 방향으로 선택해봤습니다.

 

resnet을 임포트한 뒤, 위 링크에서 Resnet클래스를 복사해오면됩니다.

저는 conv1을 빼고는 stride를 전부 그대로 두었습니다.

수정한 맨 처음 conv1만 봅시다.224보다는 작은 128사이즈이기 때문에 최종featuremap 수도 조정했습니다.

Conv2d(3, 64, kernelsize=7, padding=3, stride=2) -> (3, 32, kernelsize=3, padding=1, stride=1)로 바꾸어 주었습니다.

 

Input

전처리한 128x128 이미지를 input으로 받습니다.

 

conv1(1번)

maxpooling을 거치면서 [32x64x64](C,H,W)가 됩니다.

 

conv2(3번)

stride가 1이기 때문에 feature map사이즈가 줄어들지 않습니다. [128x64x64]이 됩니다.

 

conv3(4번)

conv3은 2stride이기 때문에 [256x32x32]이 됩니다.

 

conv4(6번)

마찬가지로 downsmapling을 거치고, 2stride이기 때문에 [512x16x16]이 됩니다.

 

conv5(3번)

downsmapling을 거치고, 2stride이기 때문에 [1024x8x8]이 됩니다.

 

FClayer(1번)

average pooling후에 들어갑니다.

 

최후에는 256개의 8x8 featuremap이 남게됩니다.

 

summary를 이용하면, 계산한 featuremap 사이즈가 맞는지 확인해볼 수 있습니다.

 

 

저도 이번에 알았는데, 레이어의 filter도 이렇게 시각화를 할 수 있더군요.

cs231n에서 봤을 때 이해가 잘 안되는 부분이었는데 직접 설계하니 이해가 됩니다. 나중에 학습된 모델의 filter를 뽑으면 아마 여기에 edge나 color같은게 잡힐겁니다. 

학습을 완료하고 다시 살펴봅시다.

 

 

 

3. resnet50 train/test 진행

train부터 봅니다.

우리는 dataloader를 정의할 때 bathsize도 같이 정의했습니다. 

data가 배치사이즈 단위로 data, label이 나오기 때문에 epoch 와 batch 이중 for문을 만들어줍니다.

 

optimizer는 Adam으로 하고, multi classification이므로 cross entropy를 loss식으로 설정해줍니다.

 

optimizer.zero_grad를 꼭 넣어주셔야합니다.

 

-> 개념적인 부분이라 다들 아시겠지만!  배치 사이즈 만큼의 데이터가 들어가서 forward train을 하고 backward로 optimize를 진행합니다. 이게 1step입니다. step마다 계산된 가중치를 다시 제로로 만들고 다음 배치 데이터를 forward/backward해야 하므로 optimizer.zero_grad를 꼭 넣어줍시다.

 

test시에는 torch.no_grad를 넣어 학습된 모델로 predict만 하도록 합니다.

제 코드를 보시면, 또 뭐가 잔뜩 있습니다. 

 

batch size가 전체 이미지를 한 바퀴 다 돌면, 1epoch입니다.

전체 데이터/batch size = iteration입니다.

배치 사이즈가 에포크를 다도는 단위가 iteration인 거죠. 저는 1에포크 동안 3번 정도 loss와 accuracy를 출력하도록 코딩한 것입니다.

오른쪽 처럼 출력되는데 솔직히 시각화하는 것이 가장 좋을 겁니다.

 

다음 포스팅은 '텐서보드'시각화 툴을 이용해서 학습 과정을 비교해보겠습니다!

다음 포스팅도 재밌습니다! 꽤나 유의미한 결과가 나와서 저도 깜짝 놀랐습니다!

다음 포스팅도 고고

 

※ 이게 저도 시퀀셜한 포스팅을 구성만해놓고 다시 공부하면서 코드를 짜가면서 포스팅을 했는데, 드디어 야심차게 학습을 진행했더니...

데이터가 크기도 작고 이미지 특징이 비슷한지(?)  파라미터 조정해도 acc가 50이상을 넘기질 못하네요... 

그래도 아직 성능개선에 시도해 볼 만한 것이 있습니다.

그마저도 성능 안나오면... :(

댓글