first end-to-end semantic segmentation
이전에는 여러 알고리즘의 결합으로 semantic segmentation을 수행했었기 때문에, 데이터가 많더라도 학습 가능한 부분이 굉장히 제한적이었다.
예를 들어, Alexnet에서 convolution layer 뒤쪽에 flatten을 통하여 벡터화를 시켜주는데 이렇게 되면 입력 해상도가 호환되지 않아 학습된 fc layer를 사용하지 못하는 한계가 존재한다.
이러한 한계는 FCN을 통해 입력으로 임의의 해상도를 넣을 수 있으며, 입력 해상도에 맞는 출력을 나타낼 수 있게 됨으로 한계를 뛰어 넘게 되었다.
fully connected layer vs fully convolutional layer
FCN with 1x1 convolutions
위치 정보를 담기 위해서 각 위치마다 channel 축으로 flattening하여 벡터 형식으로 쌓게 되면 그 위치에 대한 정보를 포함하는 벡터를 생성할 수 있다.
1x1 conv layer는 위의 방법과 동일한 연산으로 activation map에서 각 위치 별 channel 축으로 1x1 kernel 연산은 한 것은 fc layer의 한 weight columns으로 볼 수 있다.
convolution 연산은 sliding window 방식으로 weight를 공유하면서 적용되기 때문에 위치 정보가 유지될 수 있다.
따라서, FCN은 fc layer를 convolution layer로 대체함으로써 어떤 입력사이즈에도 대응가능한 네트워크를 만들 수 있다.
위의 방법으로 출력된 결과는 pooling과 stride에 의해 매우 작은 해상도의 출력이 나타나기 때문에, 이를 다시 원본 이미지의 해상도에 맞게끔 키워줌으로써 semantic segmentation을 진행한다.
이때 사용하는 방법이 Upsampling이다.
Upsampling
Transposed convolution
NN-resize convolution & Bilinear-resize convolution
다시 FCN..
두 특징을 모두 가져오기 위해 높은 layer 뿐만 아니라 중간 layer들도 Upsampling을 통해 해상도를 늘려서 가져온다.
이후 가져온 layer들을 concat하여 최종 출력을 나타내는데, 위의 사진과 같이 사용된 layer에 따라 모델이 달라진다
Architecture
Contracting path
Expanding path
skip connection
위의 과정을 거치면 낮은 layer서 전달되는 특징이 localized한 정보를 가지고, 높은 layer에 전달된다.
U-net에서 주의해야 할 점
Implementation
# contracting path
def double_conv(in_channels,out_channels):
return nn.Sequential(
nn.Conv2d(in_channels, out_channels, 3),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, 3),
nn.ReLU(inplace=True)
)
self.dconv_down1 = double_conv(3,64)
self.maxpool_2x2 = nn.MaxPool2d(kernel_size=2,stride=2)
self.dconv_down2 = double_conv(64,128)
self.maxpool_2x2 = nn.MaxPool2d(kernel_size=2,stride=2)
self.dconv_down3 = double_conv(128,256)
self.maxpool_2x2 = nn.MaxPool2d(kernel_size=2,stride=2)
self.dconv_down4 = double_conv(256,512)
self.maxpool_2x2 = nn.MaxPool2d(kernel_size=2,stride=2)
self.dconv_down5 = double.conv(512,1024)
# extracting path
self.up_trans_1 = nn.ConvTransposed2d(in_channels=1024,out_channels=512, kernel_size=2, stride=2)
self.up_conv_1 = double_conv(1024,512)
self.up_trans_2 = nn.ConvTransposed2d(in_channels=1024,out_channels=512, kernel_size=2, stride=2)
self.up_conv_2 = double_conv(1024,512)
self.up_trans_3 = nn.ConvTransposed2d(in_channels=1024,out_channels=512, kernel_size=2, stride=2)
self.up_conv_3 = double_conv(1024,512)
self.up_trans_4 = nn.ConvTransposed2d(in_channels=1024,out_channels=512, kernel_size=2, stride=2)
self.up_conv_4 = double_conv(1024,512)
self.out = nn.Conv2d(in_channels=64, out_channels=2, kernel_size=1)
Conditional Random Fields(CRF)
픽셀과 픽셀 사이의 관계를 모두 이어주어 regular 한 grid를 그래프로 나타내어 경계를 잘 찾을 수 있게 모델링
처음 결과를 뽑고 나면 처음 사진처럼 굉장히 흐릿한 출력이 나타나는데, 이를 해결하기 위해 러프하게 나온 출력 스코어맵이 이미지의 경계선에 잘 들어맞게 하기 위해 확산을 시켜준다.
반대로 배경에 대한 스코어맵은 물체의 안쪽과 바깥쪽 모두 확산이 일어나게 만들고, 물체의 마스크와 배경의 마스크를 타이트하게 만들어준다.
이를 반복하면 맨 오른쪽의 그림과 같이 물체를 잘 구분할 수 있는 결과를 얻을 수 있다.
Atrous convolution(Dilated convolution)
Depthwise separable convolution
Deeplab v3+