여러가지 Upsampling 방식들

2022.02.20 14:14 7,005 조회

안녕하세요.

데이크루 1기로 활동중인 '동화책'입니다. 📚🤓

오늘은 Segmentation이나 GAN의 generator에 사용되는 Upsampling 방식에 대해 정리해보도록 하겠습니다.

Upsampling에 대해서 지난번 UNet 구조를 설명할 때 언급했던 적이 있었는데요, 

설명이 부족했던 것 같아서 조금 더 덧붙여보도록 하겠습니다.


사전 참고 자료

Semantic Segmentation을 위한 U-Net 모델 [2탄.모델 구조] 



0. Upsampling의 목적

지난번 UNet 구조에서 살펴본 것과 같이 Upsampling은 Pooling 레이어를 거치면서 축소된 피처맵을 원본 이미지의 크기로 되돌리기 위해서 사용하는 방식입니다. Pooling과 반대되는 과정이라서 'Unpooling'이라고 불리기도 합니다. Conv와 Pooling을 통과시키면서 원본 이미지를 압축해나가는 과정을 Downsampling이라고 한다면 그 반대로 크기를 늘려나가는 방식을 Upsampling이라고 부릅니다. 이러한 네트워크 구조를 수축 경로(contracting path)-확장 경로(expansive path) 또는 인코더(Encoder)-디코더(Decoder)라고 불렀습니다. Downsampling은 Pooling의 필터 사이즈와 stride를 조정해가면서 출력 피처맵의 크기를 조절할 수 있었는데요, Upsampling은 어떻게 할까요?

그림 1. Upsampling [출처:CS231n]



1. Nearest Neighbor / Bed of Nails

가장 쉽게 생각할 수 있는 방법입니다. 기존에 가지고 있던 동일한 값을 근접한 픽셀값에 채워넣거나 한쪽 픽셀에만 채워넣고 나머지는 0으로 채우는 방식입니다. 

그림 2. Nearest Neighbor / Bed of Nails [출처 : CS231n]


2. Max Unpooling 

Bed of Nails 과 비슷하지만 MaxPooling을 했을 때 최댓값을 가지고 왔던 위치를 기억해서 그 자리에 값을 넣어주는 방식을 Max Unpooling이라고 합니다. 이것을 구현하기 위해서는 네트워크 앞단에서 MaxPooling을 수행할 때 최댓값의 자리를 기억해서 뒷단에서 Upsampling 시에 그 값을 넘겨주어야 합니다. 

그림 3. Max Unpooling [출처 : CS231n]

지금까지는 입력값을 그대로 사용하는 방식이었는데 성능면에서 한계가 있자 Conv 레이어처럼 필터 값의 학습이 가능한 형태의 Upsampling 방식을 고안하게 됩니다. 



3. Transpose Convolution 

Transpose Convolution은 Convolution 레이어와 마찬가지로 필터 값을 학습해서 Upsampling을 수행합니다. 여러 논문에서 Transpose Convolution은 Deconvolution, Upconvolution, backwards stride convolution 등 다양한 이름으로 불렸는데 최근에는 Transpose Convolution으로 이름을 굳히고 있습니다. 

기존에 convolution 과정을 살펴보면 그림 4와 같습니다. 빨간색과 파란색 필터값이 학습된 값으로 고정되어 있고 입력 이미지에 stride 수만큼 슬라이딩해가면서 곱하여 더한 값을 출력값으로 가집니다.


그림 4. Convolution [출처 : CS231n]

이 과정을 반대로 생각해보면 그림 5와 같습니다. 입력값이 있고 필터값만 학습하면 입력 이미지보다 더 큰 크기의 출력을 얻을 수 있습니다.

그림 5. Transpose Convoltuion [출처 : CS231n]

이를 1차원으로 생각해보면 그림 6과 같습니다.

그림 6. 1D Transpose Coonvolution [출처 : CS231n]

기본적인 아이디어는 이해되셨나요? 그렇다면 이러한 과정을 왜 Transpose라고 부를까요?

이러한 아이디어를 구현하는 과정에서 행렬의 Transpose를 사용하면 되기 때문입니다.

convolution의 과정을 행렬의 연산으로 다시 써보면 그림 7과 같습니다. 기존의 3x3 필터를 4x16 크기의 Sparse한 행렬(요소로 0을 많이 가지고 있는 행렬)로 바꾸고 입력 이미지와 출력 이미지를 1차원으로 바꿀 수 있습니다. 바꾼 행렬을 서로 내적하면 출력 값이 나오는 것을 확인할 수 있습니다. 


그림 7. Convolution [출처 : nrupatunga]

이를 반대 방향으로 출력을 입력으로 하기 위해서는 4x16의 sparse한 행렬을 16x4 행렬로 transpose해주고 출력값과 내적하면 됩니다. 이러한 이유로 transpose convolution이라는 이름이 붙여졌습니다. 결국 이러한 필터를 학습하기만 하면 저해상도의 이미지를 고해상도의 이미지로 Upsampling할 수 있게 됩니다. 

그림 8. Transpose Convolution [출처 : nrupatunga]

UNet 역시 이러한 방식의 Upsampling을 사용하고 있으며 tensorflow나 pytorch에서도 API로 구성되어 있어 간편하게 불러와서 사용할 수 있습니다. 

# tensorflow
tf.keras.layers.Conv2DTranspose(
    filters, kernel_size, strides=(1, 1), padding='valid',
    output_padding=None, data_format=None, dilation_rate=(1, 1), activation=None,
    use_bias=True, kernel_initializer='glorot_uniform',
    bias_initializer='zeros', kernel_regularizer=None,
    bias_regularizer=None, activity_regularizer=None, kernel_constraint=None,
    bias_constraint=None, **kwargs
)

# pytorch
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)


마무리

오늘은 여러가지 Upsampling 방식에 대해서 설명해보았습니다.

다음에 더 유익한 내용으로 찾아오겠습니다.

긴 글 읽어주셔서 감사합니다.


참고 문헌

http://cs231n.stanford.edu/slides/2017/cs231n_2017_lecture11.pdf