꺼내먹는지식 준

DeepLab v2, PSPNet, DeepLabv3 본문

카테고리 없음

DeepLab v2, PSPNet, DeepLabv3

알 수 없는 사용자 2022. 4. 28. 14:50

DeepLab, DilatedNet 모두 dilated conv를 사용하여 receptive field 를 효과적으로 키웠다. 

추가적으로 FCN에서는 5개의 max pooling으로 입력이미지를 1/32 로 줄이고 원본 이미지로 키운 반면, DilatedNet과 DeepLab은 원본이미지를 1/8만큼 줄이고 복원해서 아무래도 복원이 더 수월했다.  

 

DeepLab은 DilatedNet에서 나왔던 Basic Content Module 및(어디까지나 내 추측이다) 추가적 아이디어에 힘입어서 새로운 DeepLab2 를 제안한다. 

DeepLab2 

 

Backbone 구조는 동일하지만, Average Pooling이 사라지고 추가적으로 FC 6 ~ 8(score)이 하나만 적용되었다면, 이제는 4가지 서로 다른 dilated conv rate를 가지는 branch 형태로 4개가 뻗어나간다. 

이 형태를 ASPP(Atrous Special Pyramid Pooling) 이라고 부른다. 

다시 말하자면 다양한 dilated rate를 독립적으로 적용한다음에 sum을 취한 구조.

ASPP 를 적용하자 65 $\rightarrow$ 68로 큰 성능향상이 있었다. 

 

ASPP 가 어떤 의미를 가질까? 

conv5를 지난 feature map은 다양한 object에 대한 정보를 담고있다. FC6 부분에서 상대적으로 rate의 비율이 낮은 부분은 조금더 작은 object에 집중을 하고, 상대적으로 높은 부분은 큰 object에 집중을 했을 것이다. 

각기 잘 포착해내는 결과를 summation 한다. (마치 ensemble 과정과 비슷하다.) 

이를 통해 큰 성능 향상을 가져온 듯 하다. 

 

DeepLab v2 에서는 backbone도 resnet으로 수정한 결과도 공유를 한다. 

아래는 ResNet의 구조이다. 

첫번째 bloc은 다음과 같다. 

7X7 conv + batchnorm + ReLU

 

 

 

 

 

 

코드를 살펴보면

RGB channel 3 $\rightarrow$ 64

kernel_size 7, stride = 2, padding = 3으로 기존 입력 크기 512가 floor(255.5+1) $\rightarrow$ 256으로 작아진다. 

MaxPooling 적용으로 동일하게 입력 크기를 반으로 줄여준다. 두배 감소를 위해서 kernel = 3일 때, stride =2, padding =1로 설정하였다. 

 

 

 

 

 

 

conv2 를 살펴보면, sub-block들로 이루어져 있는데 해당 subblock은 conv + batchnorm + ReLU이다. 그리고 이 중 마지막 layer는 ReLU를 통과시키기 전 skip connection 을 먼저 더해준다. 전에 살펴본 논문에서 ReLU를 두번 해줘도 펼 차이 없다고 했던 것 같기도하다. 

 

 

코드는 기존 block들과 다르게 먼저 channel의 크기를 64 를 두번 유지시킨 후 마지막 레이어에서 channel 크기를 256으로 키운다. 

이에 따라 skip connection도 channel 64 + channel 256을 수행해야 한다. 

이를 더해주기 위해 실제로 구현을 살펴보면 1X1 conv 를 추가하여 64 $\rightarrow$256으로 맞춰준다. 

 

그 후 2,3번 sub block은 input output 크기가 256으로 동일하다. 이에 따라 1X1 conv 를 수행하여 크기를 맞춰 skip connection을 하는게 아니라 그냥 한다. 

3번째 Conv3 block는 좀 특이해서 잘 살펴봐야한다. 

첫번째 sub block에서 down sampling을 통해 128X128을 64X64로 이미지의 크기를 줄인다. 두번째 layer에서 kernel_size = 3, stride = 2, padding = 1을 주어 줄였다. 

채널 수는 첫번째 layer에서 256 에서 128로 줄였다가 2번째 layer에서 유지하고 마지막 layer에서 512로 키운다. 

skip connection에서는 channel과 resoultion (img, featuremap 크기)를 맞춰줘야한다. 

다음과 같이 1X1 conv 에 stride=2를 통해 channel과 img 사이즈 모두 맞춰주었다. 

2번째 sub block 부터는 downsampling을 제거하고, 채널도 맞으니까 별도의 처리 없이 skip connection을 진행한다. 나머지 sub block 3,4도 이를 반복하는 과정이다. 

 

4번째 conv block

3번째 conv block과 거의 유사하다. 

다만 반복횟수가 23회이다. 아마 첫번째 sub block에서 img 크기를 줄이면서 동시에 첫 layer에서 256으로 channel을 줄였다가 3번째 layer에서 1024로 키운 것으로 보인다. 

마지막 layer도 이와 같다.  

 

5번째 conv block

 

5번째도 동일하다. 

 

ResNet을 잘 아시는 분들이라면 눈치 챘겠지만, 기존의 ResNet과는 약간 다른 형태이다. 

기존 ResNet과 다르게 DeepLabv2에서는 conv4와 5에서는 첫번째 subblock의 2번째 layer의 3X3 conv에서 stride=1로 적용하여 downsampling을 진행하지 않았고, 모든 sub block의 2번째 layer 3X3 conv 에서 dilated convolution을 사용하였다.

 

 

 

 

성능 차이는 의외로 ASPP 를 쓴건 1.2 의 향상이 다이다. 오히려 ResNet을 쓴 것이 성능의 대폭 향상이 있었다. 

최종 CRF 를 적용해서 0.4 만큼 더 큰 향상을 얻었다. 

 

PSPNet

비슷하지만 조금 다르게 receptive field 를 키우는 아이디어

PSPNet은 궁극적으로 주변의 특징, category간의 관계, 작은 객체들을 다 잡아내려고 한 것이 골자이다. 

FCN도 MaxPool을 통해서 큰 Receptive field 를 가졌는데, 왜 global contextual information을 찾을까? 

해당 논문에 따르면 이론적 Pooling을 통해 얻는 receptive field와 실제 receptive field의 크기가 많이 다르다. 이는 pooling이 진행 될 수록 그 정도가 심해진다. 

이에 따라 Average Pooling을 각기 다른 크기를 갖는 sub region을 얻는다. 

 

※Global Average Pooling? 

전체 feature map을 1X1크기로 줄이는 방법. (모든 정보를 요약 feature map전체를 대표하는 값이라 global 한 context catch)

 

이렇게 넓은 영역에 average pooling 적용 결과 convolution 과 차이를 보인다. 

conv는 여러 특징 적인 부분들을 반영하여 final feature map을 구성하고, 

Average pooling은 평균을 내서 대표값을 추출했다. 

conv가 외관의 local 정보를 잘 추출해내고 

average pooling은 global한 영역에 대해서 context 를 추출할 수 있다. 

이에 따라 PSP 에서는 1X1 , 2X2, 3X3, 6X6 의 크기를 갖도록 Average Pooling을 다르게 적용한 후, 1X1으로 채널 수 맞춰주고(img 크기 맞춰주기 위해서는 upsampling을 진행한다.) 마지막으로 concat하고 feature map의 결과를 skip connection으로 더해준다. 

 

위 오른쪽 사진에서 볼 수 있듯이 결과는 baseline 대비 훨씬 정보가 더 잘 살아나는 것을 볼 수 있다. 

당시 SOTA 를 달성!
딥랩과 비교해도 더 잘잡고 디테일 복원을 잘했다.

DeepLab v3

DeepLabv3 는 ASPP에 feature map을 전부 평균내서 1X1의 크기를 만들어주는 global average pooling 을 추가하여 global 한 context를 더 잘 catch하도록 만들었다. 

기존 ASPP 와 DeepLab v3 ASPP의 차이

6,12,18,24 $\rightarrow$ 6,12,18 + global average pooling + 1X1 Cov

기존에는 summation이었는데, 여기서는 concat 한 후에 1X1 conv 를 수행했다. 

 

global average pooling은 1X1으로 concat하기에 사이즈 차이가 많이 난다. 이에 따라 크기를 맞춰주기 위하여 bilinear interpolation을 적용해준다. 나머지 featuremap에 적용하는 conv들은 크기가 변하지 않도록 적절하게 padding의 크기를 지정해준다. 

(구현체마다 dilated conv의 rate의 비율은 조금씩 다르다.)

 

DeepLab v3+ 

아주 유명한 DeepLab v3+

DeepLab v3와 비교하여 어떤 것이 달라졌을까? 

사실 Encoder에서 spatial dimension 의 축소로 인해 손실된 정보를 Decoder에서 점진적으로 복원하는 건 항상 하는 방법이다. 

그에 대한 예시로 ASPP 도 있었고, Dilated(Atrous) Conv도 있었고, Skip connection도 있었다.  

 

맨 오른쪽 그림을 보자. 

결론만 말하자면 구조적으로 좀 더 개선이 되었다.

img 크기를 반으로 줄이는 과정이 한번 더 추가되었고, 동일하게 ASPP 는 하되, 크기를 확대시킬 때 한번에 확대시키는 것이 아니라 두번에 나눠 4배씩 확대 시키고, 중간에 skip connection으로 한번 low level information을 받아온다. 

Backbone

  • ResNet $\rightarrow$ Xception을 수정해서 사용 
  • Astrous separable convolution을 적용한 ASPP 모듈 사용 
  • Backbone 내 low-level feature와 ASPP 모듈 출력을 모두 decoder에 전달 

Decoder 

  •  ASPP모듈의 출력을 up-smapling(bilinear) 하여 low level feature 와 결합

low level feature는 1X1 conv, ASPP output은 upsampling by 4(linear interpolation)하여 Concat

  • 결합된 정보는 convolution 연산 및 up sampling되어 최종 결과 도출 
  • 기존의 단순한 up sampling을 개선시켜 detail을 유지하도록 함 

 

BackBone Details

효율적 연산을 위해서 Depthwise saparable Convolution을 사용 

 Depthwise separable Convolution: 일반적인 Conv가 spatial 및 channel dimension의 방향에 대해서 한번에 연산을 진행했다면, depthwise separable convolution은 channel과 spatial 에 대한 연산을 따로 따로 진행한다. 

 

Depthwise separable Convolution = Depthwise convolution + Pointwise convolution 

 

Depthwise convolution

위 그림과 같이 서로 다른 채널에 대해서 convolution 연산을 각기 다른 것을 수행한 후에 conv 결과를 합치는 것depthwise convolution (직관적으로 봤을 때는 channel별로 동일한 연산이 아닌 다른 연산 수행 후 concat으로 보인다.)

코드단에서 입력 채널과 groups 에 동일한 값을 설정함으로써 사용 가능하다. 

 

Pointwise convolution

다를게 없다. 그냥 우리가 아는 1X1 convolution 연산이다. in_channel 이 3 , out_channel이 1 (in_channel: conv의 channel수 out_channel: 몇장을 곱하는가)

둘을 합치면 다음과 같다. 

먼저 depthwise를 수행하고, 그 후 1X1 conv 를 수행한다. 

코드를 살펴보자. 

이 depthwise separable conv 를 사용해서 만든 network 가 Xception이다. 

DeepLab v3 에서는 이 backbone을 수정하여 사용하였다. 

각 Entry flow, Middle flow, Exit flow 별로 어떤 부분이 변했는지 한번 살펴보자. 

 

Entry flow 

max pooling $\rightarrow$ Depthwise Separable Convolution 

(내가 이해한 Depthwise separable conv 는 각 채널별 정보를 좀더 풍부하게(학습을 통해) 보고, 체널 수를 줄이는 역할, 여기에 stride = 2를 주어서 크기도 줄인다.) 

Max pooling은 대표적 특징만 추출했다면 Depthwise Separable Convolution는 학습 가능한 parameter로 어느 부분이 중요하고, 어떻게 추출하는게 추출하는 것이 좀 더 정보의 손실을 방지할 수 있는지를 만들어주는 역할을 수행한다. 

 

실제 해당 부분 구현을 살펴보면, channel 32, kernel_size = 3, stride = 2로 크기를 1/2로 줄이고, batch norm ReLU가 적용하고, 똫또다른 conv 도 수행한 후

in_channel과 groups 의 크기를 맞춰주고, pointwise 적용. 구현 코드를 살펴보니 채널은 줄이지 않고 단순 img 사이즈를 1/2 로 줄이는데 그 과정 속에서 dephwise 에서 학습 가능한 parameter로 각 채널로부터 정보를 뽑아낸다. 그 후 Batchnorm, ReLU 수행한다. 

Skip connection도 더해준다. 

 

Middle flow 

middle flow에서 repeated 횟수를 8 $\rightarrow$ 16으로 증가시키면서, 더 깊은 layer를 사용했다. 

Exit flow

Maxpooling연산을 Depthwise separable conv 에 stride=2 로 대체 

최종 부분은 2개의 Depthwise separable conv 를 3개로 바꾸고 채널 구조도 1536 2048구조를 1536 1536 2048 구조로 수정하였다. 

코드를 살펴보자.

 

classification 문제의 경우 5번의 strided convolution으로 입력 크기를 1/32로 줄였으나, 

segmentation에서는 크기를 너무 줄이는 면 pixel별 복원이 어려우므로 4번의 stride conv 를 진행 한 후 마지막 5번째에서 stride = 1로 적용하여 크기를 줄이지 않음으로서 입력을 1/16로 줄인다. 

segmentation의 경우 모델 설정에 따라 4번째 strided convolution에 stride=1을 적용해서 입력을 1/8로 줄이기도 한다. 

 

DeepLab v3 전체 구조 요약

DCNN 부분에서 modifier Xception 으로 512X512를 총 4개의 strided convolution을 거쳐 32X32로 즉 1/16 의 크기로 만든다. 

※strided convolution: 그냥 stride 가 1 이 아닌 모든 convolution을 뜻하는 것 같다. 

첫번째 block을 지난 결과에 ReLU를 거치게 한 후, Decoder로 low level feature를 전달 

modified Xception의 출력 map 은 ASPP 를 적용해서 출력을 만든다. 

이때 ASPP의 코드는 아래와 같다. 

Kernel size는 [1,3,3,3] padding [0,6,12,18] dilation [1,6,12,18]로 하여 img 크기가 변동을 방지한다. 

Global Average Pooling의 경우 adaptive average pooling(어떤 상황에서도 주어진 크기로 average pooling)을 이용하여 output size가 1,1 이 나오도록 하고, 맞지 않는 크기를 1X1로 channel 을 맞춰주고, bilinear interpolation으로 resolution크기를 맞춰준다. 

 

이렇게 나온 5개의 결과를 concat 해주고, 그 이후 1X1 conv 를 적용해 채널을 (256X5)  $\rightarrow$ (256)으로 감소시켜준다. 

해당 결과를 Decoder로 넘겨줌으로써 Encoder 결과가 끝난다. 

 

Decoder 

skip connection으로 뽑아낸 1/4크기의 low level feature 128X128 결과와 1/16 결과인 32X32 의 ASPP 가 주어진다. 

 

low level feature는 1X1 conv를 적용해서 128에서 48로 줄이고, ASPP 결과는 bilinear interpolation을 통해 4배만큼 키워 128X128 을 맞춰서 둘을 concat 한다.  (왜 굳이 크기가 안맞게 처리를 해줄까?)

 

그 후 concat결과에 3X3 conv를 이용해서 segmentation의 probabilty map을 만들고, 다음에 bilinear interpolation을 이용해서 4배만큼 키워서 512 X 512의 최종 결과를 도출한다. 

전체 구조 한줄 정리 

Modifier Xception (중간에 featuremap 뽑아서 skip connection) $\rightarrow$ ASPP

$\rightarrow$ 중간의 featuremap + ASPP 결과 

$\rightarrow$ 크기 키우기 512 X 512 출력 

 

Comments