Fully Convolutional Networks (FCN)

Fully Convolutional Networks (FCN)

Fully Convolutional Networks (FCN)은 2015년도 CVPR에 소개된 논문으로 End-To-End의 세그멘테이션의 포문을 연 논문입니다. 인용수가 약 20,000회 이상으로 나중 세그멘테이션 논문들에 많은 영향을 끼쳤습니다.

Abstract

  • AlexNet을 시작으로 하는 CNN 모델들의 발전을 Image Segmentation 영역에 접목 (Pretrained된 딥러닝 모델을 이미지 피쳐를 추출하는 백본 네트워크로 활용) 합니다.
  • VGG 네트워크의 FC Layer (nn.Linear)를 Convolution으로 대체하여 이미지의 위치정보를 기억하고, 임의의 입력 크기에 대해서도 일관성 있는 결과를 생성합니다.
  • Transposed Convolution을 이용해서 Pixel Wise Prediction (세그멘테이션)을 수행합니다.

BackBone Network

FCN 논문에서는 총 3가지의 백본 네트워크를 실험을 합니다. AlexNet, VGG16, GoogleLeNet인데, 결과를 비교해보면 IoU 평가지료가 VGG16이 압도적으로 높은 것을 확인할 수 있고, 논문에서도 해당 네트워크를 백본으로 사용했습니다.

|              | FCN-AlexNet | FCN-VGG16 | FCN-GoogLeNet |
|--------------|-------------|-----------|---------------|
| mean IU      | 39.8        | 56        | 42.5          |
| forward time | 50 ms       | 210 ms    | 59 ms         |

VGG16은 아래의 그림과 같이 구성되어있습니다. 3x3의 Convolution 네트워크만을 이용해서 피쳐를 추출하고, 이후 3개의 FC Layer를 이용해서 Classification을 수행하는 구조입니다. 기존 네트워크의 경우 7x7 Convolution과 같이 큰 Kernel을 사용한 반면, 작은 Convolution과 Maxpooling을 섞어서 적은 Layer로 높은 성능을 거둔 네트워크입니다.

figure1

Fully Convolutional Layer vs Fully Connected Layer

논문의 그림에 따르면, 아래와 같이 3개의 FC Layer를 모두 Convolution Layer로 대체를 합니다.

이는 크게 2가지의 장점이 있습니다. 첫째, 각 픽셀의 위치정보를 해치지 않은채로 특징을 추출할 수 있습니다. 둘째, 임의의 입력크기에 대해서도 일관성 있는 결과를 생성할 수 있습니다.


먼저, 첫번째 특징이 의미하는 바를 그림으로 살펴보겠습니다. 아래의 그림은 1x1 convolution과 Fully Connected Layer인 FC Layer를 적용했을때의 결과입니다. 두개의 결과를 보면 Convolution은 2D 피쳐맵에 회색부분이 제대로 나온 것을 볼 수 있고, FC Layer는 1자 형태로 펴진 것을 볼 수 있습니다. (자세히는 Fully Connected Layer를 적용할때, .view()의 함수를 통해서 1차원의 Layer로 펼치고 거기에 FC Layer를 적용해서 위치 정보가 해쳐지게 됩니다.) 그렇게 될 경우, 1x1 Convolution은 사각형 형태의 특징맵이 나오기에 흰색의 특징을 해당 위치에 보존할 수 있고, FC Layer는 못하게 됩니다.


둘째, Fully Convolution Layer가 임의의 입력크기에 대해서도 일관성 있는 결과를 생성합니다.

이는 위의 그림을 살펴보면 좋은 예가 될 것 같습니다. PyTorch의 두가지 인자에 대해서 입력변수를 살펴보면, Conv2d는 입력 채널을 Linear는 입력채널x높이x너비를 받습니다. 즉, 이전 입력 (or 특징맵) 높이와 너비의 크기가 512x512로 고정되면 그에 맞는 하이퍼파라미터가 512x512x채널로 설정되어야하고, 이때 입력이 256x256으로 바뀌면 계산이 되지가 않습니다. 하지만, Conv2d는 입력 이미지의 크기에 따른 인자가 없어서 512x512로 학습이 된 이미지에 256x256의 입력이 들어와도 계산이 됩니다. 즉, Convolution은 이미지 혹은 레이어의 크기에 대해서는 상관이 없게 됩니다.

Upsampling (Deconvolution, Transposed Convolution)

마지막으로 Transposed Convolution을 이용하여 Pixel wise Prediction을 하는 부분입니다. Transposed Convolution은 Pooling 단계에 의해서 감소한 이미지의 크기를 원본 이미지의 크기로 복원하는 방법입니다.

figure4

다음 그림과 같이 입력의 한칸이 Kernel과 곱해져서 퍼지는 형식으로 구성이 됩니다. 단, 이때 Output의 크기는 Kernel의 크기, Stride, Padding으로 조절이 가능합니다.

torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1, padding_mode='zeros', device=None, dtype=None)

H_out=(H_in−1)×stride[0]−2×padding[0]+dilation[0]×(kernel_size[0]−1)+output_padding[0]+1

PyTorch 공식 홈페이지의 Transposed Conv2d의 함수와 크기는 다음과 같이 계산이 됩니다. 보면 알겠지만, kernel_size, stride가 커짐에 따라서 H_out이 커지고 padding이 커짐에 따라서 크기는 줄어드는 것을 확인할 수 있습니다.


Transposed Convolution의 용어로는 Upsampling, Deconvolution, Transposed Convolution 많은 용어로 불리지만 CS231에 따르면 Transposed Convolution이 정확한 표현입니다. 그 이유는 Deconvolution은 Convolution의 결과를 거꾸로해서 입력으로 다시 복원하는 작업인데, Transposed Convolution은 그렇게 하지 못하기때문입니다. 하지만, Convolution의 Matrix 연산을 Transposed 해서 나오기에 Tranposed Convolution이라고 하는데, 한번 어떤식으로 나오는지 아래의 그림을 살펴보겠습니다.

먼저, Convolution 연산을 Matrix으로 살펴보겠습니다. 3x3 Conv와 4x4 Input을 곱하는 과정인데, 위에처럼 Matrix로 바꿔도 값이 동일한 것을 볼 수 있습니다. Transposed Convolution은 정확히 이를 Transposed만 한 연산입니다.

위의 Convolution을 Transposed 취해서 ouput에 곱해줍니다. 그렇게 하면, 새로운 출력값을 얻을 수 있고 이는 input 값하고는 다른 결과를 가지게 됩니다. 참고로 이는 수식적으로 Convolution의 미분값과 동일해서 위의 수식처럼 계산이 됩니다.

Network Architecture

image-20210921124434716

기본적인 FCN32s의 구조는 위와 같습니다. Conv1 부터 Conv5까지는 Vgg16의 구조를 그대로 가져왔고, FC6 Score는 Vgg16의 FC Layer를 Convolution으로 바꾼 형태입니다, 그리고 마지막 Upscore는 Transposed Convolution을 적용했습니다. 이때 s는 Stride의 의미로 마지막 Transposed Convolution의 식이 다음과 같이 32를 가진다는 의미입니다.

nn.ConvTransposed2d(num_classes, num_classes, kernel_size=64, stride=32, padding=16)

Skip Connection

논문에서는 성능을 높이기 위해서 Skip Connection을 사용합니다. 이는, ResNet의 아이디어처럼 얕은 층의 특징과 깊은 층의 특징을 결합하려는 시도입니다. 이때 효과는 얕은 층은 일반적으로 복잡하지 않은 정보를 가지고, 깊은 층은 복잡한 정보를 가지기에 앙상블의 효과가 있습니다. 추가로, Maxpooling에 의해서 잃어버리는 정보의 손실이 일어나기 전의 특징을 가져온다는 효과도 있습니다.

figure12

논문에서는 다음과 같은 구조로 Skip Connection이 진행되었습니다. 그림이 보기가 안좋아서, 이를 수정하면 다음과 같이 볼 수 있습니다.

image-20210921124909456

단, 이때 FC6이 7X7이 아니라 1X1인 것을 확인할 수 있습니다. 원래는 7X7 Conv으로 해야 맞습니다. 하지만, 7x7 Conv를 적용할때에는 한가지의 문제점이 있습니다. 바로 7x7 Conv에 의해서 사이즈가 달라지는 문제입니다. FC6의 Convolution은 따로 padding을 적용하지 않아서 입력의 크기가 7x7이 들어오면 1x1으로 줄어드는 문제가 발생합니다.

image-20210921125140680

이에 대한 문제를 해결하기위해서, 구현체의 코드에서는 한가지 트릭을 사용합니다. 먼저, 첫번째의 Convolution에 padding을 100으로 주고 사이즈가 맞지 않는 부분에 Crop을 통해서 사이즈를 수정하는 작업을 진행합니다. Transposed Conv의 padding을 적용해서 외각을 자르기도하고, Feature map에서 중앙부분만을 추출하기도 합니다. (관련해서는 다음 글의 코드부분을 참고하시기 바랍니다.)

Results

figure13

결과적으로 위의 그림처럼, 다양한 Pooling의 결과를 쓰면 점수가 더 좋아지고 Backbone으로 VGG16을 사용했을 때 결과가 가장 좋았습니다. Image Segmentation에서 딥러닝을 활용한 초기의 논문이었고, Resnet의 기술이라든지 다양한 방향성을 제시해준 논문이어서 굉장히 가치가 있었습니다. 하지만, 위의 그림에서 보이듯이 아직 개선의 여지는 많습니다. 눈에 보이는 것처럼, 디테일한 모습이 살아나지 않은 문제, 오른쪽 라이더의 얼굴부분과 자전거 부분에 비는 픽셀이 존재 등 아직은 한계점이 많은 방법입니다. 위와 같이 디테일한 모습이 살아나지 않은 문제점의 이유를 생각해보면,

  • 첫째, 32배, 16배, 8배로 Transposed Convolution으로 바로 키우는게 너무 과하다는 생각이 듭니다. 비록 이에대한 문제를 해결하기 위해서, 2배씩 줄여가면서 Skip Connection을 진행하지만 이는 충분하지 않은 것 같습니다.
  • 둘째, 부분적인 특징들만을 가지고 예측하는게 옳은가에 대한 생각이 듭니다. Convolution은 이제 필터가 지나가면서 부분적인 특징을 추출하고, 이를 모아서 픽셀을 분류하는 형태입니다. 하지만, 하나의 Object를 인식하기 위해서 코끼리의 다리, 얼굴, 꼬리 등을 모아서 판단해야 하는게 아니라 전체를 보고 판단하는게 더 좋을 수 있습니다.
  • 셋째, 5개의 Maxpooling 및 FC6에 의해서 224x224의 입력이미지가 1x1까지 줄어드는데, 너무 과하게 정보가 손실되는 문제가 있습니다.

다음 글에서는 먼저 FCN의 코드 구현을 FCN32s, 16s, 8s 3가지 형태로 살펴보도록 하겠습니다.

댓글(0)

Designed by JB FACTORY