본문 바로가기

Machine Learning

딥러닝을 통한 Image Segmentation 입문!!

Keras를 사용해 딥러닝으로 이미지를 분석해보자!

A Beginner's guide to Deep Learning based Semantic Segmentation using Keras

원문 링크 : https://divamgupta.com/image-segmentation/2019/06/06/deep-learning-semantic-segmentation-keras.html
참조 : https://bskyvision.com/491

주의 사항 : 원문의 semantic segmentation, semantic image segmentation 등의 단어들을 통틀어 image segmentation(이미지 분석)로 번역하였습니다.


픽셀 기반의 이미지 분석(Image Segmentation) 문제는 컴퓨터 비전 (Computer Vision) 분야에서 널리 알려진 문제입니다. 이미지 분석의 목적은 이미지의 각각의 픽셀들을 특정 클래스로 분류하는 것입니다. 이번 글에서, 우리는 Deep CNN을 이미지 분석에 어떻게 사용할 지에 대해서 이야기해보도록 하겠습니다. 또한, 우리는 데이터를 준비하는 것에서부터 모델을 생성하는 것까지의 과정을 살펴볼 것입니다.

Github Code : https://github.com/divamgupta/image-segmentation-keras

딥러닝(Deep Learning)과 CNN(Convolutional neural networks)은 컴퓨터 비전분야에서 굉장히 널리 쓰이고 있습니다. CNN은 이미지 분류(Image Classification), 물체 감지(Object Detection), 이미지 생성(Image Generation)등의 분야에 쓰이고 있습니다. 딥러닝은 다른 분야에서도 그랬던 것처럼 이 이미지 분석 분야에 있어서도 다른 접근 방식들을 뛰어넘는 성과를 내고 있습니다.


Semantic Segmentation이란?

이미지 분석은 미리 정해진 클래스의 집합을 이용하여 주어진 이미지의 각 픽셀을 특정 클래스로 분류하는 작업입니다. 아래의 예제에서, 서로 다른 개체들이 분류되어 있는 것을 확인할 수 있습니다.

위의 예제에서, 침대에 해당하는 픽셀들은 '침대' 클래스로 분류되며, 벽에 해당하는 픽셀들은 '벽' 클래스라는 라벨이 붙게 됩니다.

좀더 자세히 말하자면, 우리의 목표는 W*H*3 크기의 이미지를 받아서 전체 픽셀의 예측 클래스를 담고 있는 W*H 배열을 생성하는 것입니다.

보통, 여러 개체들을 담고 있는 이미지에서, 우리는 특정 픽셀이 어느 개체 종류에 속하는지를 알고싶어합니다. 예를 들어, 야외 이미지에서 우리는 하늘, 땅, 나무들, 사람들 등을 분석해낼 수 있습니다.

이미지 분석은 이미지 주변에 감싸는 박스를 생성하는 이미지 인식과는 다른 문제입니다. 다시 말해, 우리는 같은 클래스에 속하는 서로다른 개체들을 분류할 필요가 없습니다. 예를 들어, 같은 '차'라는 라벨을 가진 픽셀 안에도 여러개의 차들이 존재할 수 있습니다.

이미지를 분석하기 위해서는, 이미지에 대한 더욱 높은 수준의 이해가 필요합니다. 우리가 만들어낼 알고리즘은 개체가 존재한다는 것 말고도, 어느 픽셀들이 그 개체를 나타내는지를 파악할 수 있어야 합니다.
이미지 분석은 전체적인 이미지를 해석하는데에 있어 가장 중요한 작업 중 하나라고 할 수 있습니다.


적용 분야

이미지 분야가 매우 유용하게 쓰일 수 있는 다양한 분야들이 있습니다.

의학 사진

몸을 스캔한 사진을 자동으로 분석할 수 있게 된다면 의사들이 진단을 하는데에 있어 매우 큰 도움이 될 수 있습니다. 예를 들어, 암을 감지하는데에 훈련될 수 있습니다.

자동주행차

자기 스스로 움직이는 자동주행차나 드론들은 이런 이미지 분석의 도움을 받을 수 있습니다. 예를 들어, 자동주행차들은 자기가 운전할 수 있는 영역을 인식할 수 있습니다.

위성 이미지 분석

위성 이미지들은 지형을 분석하는데 사용될 수 있습니다.


딥러닝을 통한 이미지 분석

다른 적용분야에서 그런 것처럼, CNN은 이미지 분석에서도 중요한 요소입니다. CNN을 이미지 분석에서 사용할 경우, 출력은 고정길이벡터 ( [1,2,3,4,5]등을 말하는 것 같음) 보다는 이미지 (이차원 배열)가 됩니다.

이미지 분석을 위한 CNN, FCN (Fully Convolutional Network)

그렇다면 이제 이미지 분석의 가장 대표적인 모델 중 하나라고 할 수 있는 FCN 에 대해서 알아봅시다.

FCN은 버클리대학의 Jonathan Long 등이 2015년 CVPR에 발표한 Semantic Segmentation 알고리즘으로, 이후에 출시된 알고리즘들은 사실상 FCN을 개선한 아류작이라고 할 수 있을 정도로 선두적인 역할을 한 알고리즘이다.

AlexNet, VGGNet 등 이미지 분류용 CNN 알고리즘들은 일반적으로 Convolution Layer와 Fully Connected 층들로 이루어져 있다. 항상 입력이미지를 네트워크에 맞는 고정된 사이즈로 작게 만들어서 입력해준다. 그러면 네트워크는 그 이미지가 속할 클래스를 예측해서 알려준다. 아래 그림에서 네트워크는 입력된 이미지의 클래스를 얼룩무늬 고양이(tabby cat)이라고 예측하였다.

이 분류용 CNN 알고리즘들은 이미지에 있는 물체가 어떤 클래스에 속하는지는 예측해낼 수 있지만, 그 물체가 어디에 존재하는지는 예측해낼 수 없다. 왜냐하면 네트워크 후반부의 fully connected 층에 들어서면서 위치정보가 소실되었기 때문이다. 따라서 AlexNet, VGGNet 등과 같은 알고리즘들을 수정함 없이 이미지 분석 과제에 그대로 사용하는 것은 불가능하다.

FCN 개발자들은 위치정보가 소실되지 않게 하기 위해서, 또한 어떠한 크기의 입력이미지도 허용하기 위해서 다음과 같이 알고리즘을 발전시켜나간다. 먼저 고정된 크기의 인풋만을 허용하는 fully connected 층을 1*1 convolution layer 로 바꿔준다.

결과적으로 네트워크 전체가 Convolution Layer들로 이루어지게 된다. Fully Connected 층이 없어졌으므로 이제 더이상 입력 이미지의 크기에 제한을 받지 않게 되었다.

이제 어떠한 사이즈 H * W 의 이미지도 이 네트워크에 입력될 수 있다. 여러 층의 Convolution Layer들을 거치고 나면 특성 맵(Feature Map)의 크기가 H/32 * W/32가 되는데, 그 특성맵의 한 픽셀이 입력이미지의 32 * 32크기를 대표한다. 즉, 입력이미지의 위치 정보를 '대락적으로' 유지하고 있는 것이다.

여기서 중요한 것은 이 Convolution Layer들을 거치고 나서 얻게 된 마지막 특성맵의 갯수는 훈련된 클래수의 갯수와 동일하다는 것이다 21개의 클래스로 훈련된 네트워크라면 21개의 특성맵을 산출해낸다. 각 특성맵은 하나의 클래스를 대표한다. 만약 고양이 클래스에 대한 특성맵이라면 고양이가 있는 위치의 픽셀값들이 높고, 강아지 클래스에 대한 특성맵이라면 강아지 위치의 픽셀값들이 높다.

이 대략적인 특성맵들의 크기를 원래 이미지의 크기로 다시 복원해줄 필요가 있다. 이미지의 모든 픽셀에 대해서 클래스를 예측하는 dense prediction을 해주는 것이 이미지 분석의 목적이기 때문이다. 이 원래 이미지 크기로 복원하는 과정을 Upsampling 이라고 부른다. Upsampling을 통해서 각 클래스에 해당하는 대략적인 특성맵들을 원래 사이즈로 크기를 키워준다. Upsampling된 특성맵들을 종합해서 최종적인 segmentation map을 만들게 된다. 간단히 말해서 각 픽셀당 확률이 가장 높은 클래스를 선정해주는 것이다. 만약 (1,1) 픽셀에 해당하는 클래스당 확률값들이 강아지 0.45, 고양이 0.94, 나무 0.02, 컴퓨터 0.05, 호랑이 0.21 이런 식이라면, 0.94로 가장 높은 확률을 산출한 고양이 클래스를 (1,1) 픽셀의 클래스로 예측하는 것이다. 이런 식으로 모든 픽셀이 어느 클래스에 속하는지 판단한다.

그런데 단순히 upsampling을 시행하면, 특성맵의 크기는 원래 이미지 크기로 복원되고, 그것들로부터 원래 이미지 크기의 segmentation map을 얻게 되지만 여전히 대략적인, 즉 디테일하지 못한 segmentation map을 얻게 된다. 1/32 만큼 줄어든 특성맵들을 단숨에 32배만큼 upsampling 했기 때문에 당연히 대략적일 수 밖에 없다. 이렇게 단숨에 32배 upsampling 하는 방법을 논문에서는 FCN-32s라고 소개하고 있다.
아래 그림을 보자. 실제 상황과 비교해서 FCN-32s로 얻은 segmentation map은 많이 뭉뚱그려져 있고 디테일하지 못함을 알 수 있다.

FCN의 개발자들은 좀더 디테일한 segmentation map을 얻기 위해 skip combining 이라는 기법을 제안한다. 기본적인 생각은 다음과 가은데, 컨볼루션과 풀링 단계로 이뤄진 이전 단계의 컨볼루션층들의 특성맵을 참고하여 upsampling을 해주면 좀 더 정확도를 높일 수 있지 않겠냐는 것이다. 왜냐하면 이전 컨볼루션층들의 특성맵들이 해상도 면에서는 더 낫기 때문이다. 이렇게 바로 전 convolution layer들의 특성맵(pool4)와 현재 층의 특성맵(conv7)을 2배 upsampling한 것을 더한다. 그 다음 그것(pool + 2x conv7)을 16배 upsampling으로 얻은 특성맵들로 segmentation map을 얻는 방법을 FCN-16s라고 부른다. 아래 그림을 참고하자.

또 더 나아가서 전전 convolution layer의 결과도 참고해서 특성맵들을 얻고, 또 그 특성맵들로 segmentation map을 구할 수도 있다. 이 방법은 FCN-8s라고 부른다. 좀 더 구체적으로 이야기하면, 먼저 전전 단계의 특성맵(pool3)과 전 단계의 특성맵(pool4)을 2배 upsampling한 것과 현 단계의 특성맵(conv7)을 4배 upsampling한 것을 모두 더한 다음에 8배 upsampling 을 수행하므로 특성맵들을 얻는다. 이것을 모두 종합해서 최종 segmentation map을 만들게 된다.

아래 그림을 보면 FCN-8s가 FCN-16s 보다 좀 더 세밀하고, FCN-32s 보다는 훨씬 더 정교해졌음을 알 수 있다. 다른 논문에서, 또는 웹상에서 누군가 FCN을 말할 때는 보통 이 FCN-8s 를 의미한다고 봐도 무방하다.

지금까지 살펴본 모델인 FCN은 추상화된 정보를 다시 원래 이미지의 segmentation으로 풀어나갈때 별도의 복잡한 수학적 계산이 들어가 있지 않습니다. 그러나 다른 대부분의 이미지 분석 모델에서는 별도의 수학적인 연산들을 이용하여 결과를 내게 되는데 이러한 구조를 다른 말로 Encoder-Decoder 구조라고도 하며, 아래 그림에서 확인할 수 있습니다.


Transfer Learning

이미지 분류를 위한 CNN 모델들은 분석에도 사용될 수 있는 유용한 정보들을 가지고 있습니다. 우리는 미리 학습된 모델들의 convolution layers 를 분석용 모델의 encoder (upsampling 전) 부분에 재사용할 수 있습니다. Resnet이나 VGG등을 사용하는 것이 가장 널리 알려진 선택입니다. Transfer Learning에 대해 더 궁금하다면 아래 링크를 참조하세요!
http://cs231n.github.io/transfer-learning/


손실 함수 (Loss Function)

모델의 출력의 각각의 픽셀들은 실제 입력 이미지의 각 픽셀과 비교되고, 우리는 이에 standard cross-entropy 손실 함수를 적용하게 됩니다.


실제 구현

실제 구현 부분은 코드 위주이므로 원문을 참고하시면 감사하겠습니다.
향후 추가할 수 있다면 추가하도록 하겠습니다.
본 글에서는 원문의 모델을 만들고 고르는 방법에 대한 해석만 넣도록 하겠습니다.

Model 만들기

이제, Keras API를 사용해서 skip combining이 사용된 우리의 분석 모델을 만들어봅시다!

먼저 Encoder 층을 만들어봅시다. 여기서, 각각의 블록들은 두개의 convolution 층과 하나의 max pooling 층을 가지고 있어 downsampling의 효과를 지니게 됩니다.

img_input = Input(shape=(input_height,input_width , 3 ))

conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(img_input)
conv1 = Dropout(0.2)(conv1)
conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv1)
pool1 = MaxPooling2D((2, 2))(conv1)

conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1)
conv2 = Dropout(0.2)(conv2)
conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv2)
pool2 = MaxPooling2D((2, 2))(conv2)

conv1conv2 은 향후 decoder layer에서 skip combining 용도로 쓰일 중간 결과를 지니고 있습니다. pool2 에서는 encoder 층의 최종 결과가 나오죠.

이번에는 Decoder 층을 만들어봅시다. 우리는 encoder 층의 중간 결과들과 decoder층의 중간 결과들을 연결짓게 되는데, 바로 앞에서 살펴봤던 skip combining입니다.

conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2)
conv3 = Dropout(0.2)(conv3)
conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3)

up1 = concatenate([UpSampling2D((2, 2))(conv3), conv2], axis=-1)
conv4 = Conv2D(64, (3, 3), activation='relu', padding='same')(up1)
conv4 = Dropout(0.2)(conv4)
conv4 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv4)

up2 = concatenate([UpSampling2D((2, 2))(conv4), conv1], axis=-1)
conv5 = Conv2D(32, (3, 3), activation='relu', padding='same')(up2)
conv5 = Dropout(0.2)(conv5)
conv5 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv5)

여기서 conv1conv4 와 skip combining 되며 conv2conv3과 그렇게 됩니다. 최종 결과를 얻기 위해서, 클래스의 개수와 같은 개수의 필터를 지닌 convolution 층을 더합니다. (분류에서와 비슷합니다)

out = Conv2D( n_classes, (1, 1) , padding='same')(conv5)


# Keras_segmentation은 여러 준비된 모델들을 가지고 있어 우리의 모델을 만들지 않고 기존의 것을 사용할 수도 있습니다.

from keras_segmentation.models.model_utils import get_segmentation_model

model = get_segmentation_model(img_input ,  out ) # this would build the segmentation model

Model 고르기

이미지 분석을 위해 쓸 수 있는 여러 모델들이 존재합니다. 우리는 우리가 사용할 방식, 그리고 분야에 따라 모델을 달리 골라야 합니다. 우리가 고려해야 할 점은 다음과 같습니다

- 훈련 이미지의 개수
- 이미지의 사이즈
- The domain of the images (무슨 뜻일까요..?)

보통, 딥러닝에 기반한 분석 모델들은 CNN 네트워크를 기반으로 만들어집니다. ResNet, VGG, MobileNet과 같은 모델들이 보통 CNN 네트워크를 사용한 기반 모델로 많이 사용됩니다. 이러한 기반 모델들은 최종적인 분석 모델의 encoder 부분의 초기 층들로 사용되며, 그 위에 나머지 분석 모델이 만들어지게 됩니다.

기반 모델 고르기

분석 모델을 만드는데 있어, 우리가 처음으로 할 일은 적절한 기반 모델을 선정하는 것입니다. 많은 경우, ImageNet 데이터들로 미리 훈련된 모델을 고르는 것이 좋은 선택이죠.

ResNet

Microsoft에 의해 제안된 모델로, 2016년 ImageNet 경연에서 96.4%의 정확도를 달성하였습니다. ResNet은 여러 분야에서 기반 모델로 선택되고 있습니다. ResNet은 매우 많은 개수의 층을 가지고 있습니다.

VGG-16

Oxford에 의해 제안된 모델로, 2013년 ImageNet 경연에서 92.7%의 정확도를 달성하였습니다. ResNet과 비교하자면, 이 모델은 훨씬 적은 수의 층을 가지고 있어 훈련에 소요되는 시간이 훨씬 적습니다. 실제로 현장에서의 적용결과를 보자면 VGG는 ResNet만큼의 정확도를 보여주지는 못합니다. ResNet이 등장하기 전에는 VGG가 가장 널리 쓰이는 모델이였다고 합니다.

MobileNet

Google에 의해 제안된 모델로 작은 사이즈로 최적화되어 빠른 수행시간을 자랑합니다. 이는 자원이 한정된 기기들이나 모바일 기기들에서 동작하기에 이상적입니다. 작은 사이즈로 인해서, 정확도에는 약간의 감점이 있을 수 있습니다.

Custom CNN

ImageNet 데이터들로 미리 훈련된 모델을 사용하지 않고, 자신이 직접 만든 모델을 기반 모델로 사용할 수도 있습니다. 만약 분석 모델을 적용할 분야가 간단하다면 ImageNet을 이용한 사전 훈련은 꼭 필요하지는 않습니다. 자신만의 모델을 사용하는 것의 또다른 이점은 적용할 분야에 따라 세부적으로 조정을 할 수 있다는 점입니다.


분석 모델 고르기

기반 모델을 선정했으면 다음은 분석 모델의 구조를 고를 차례입니다. 지금부터는 여러 유명 분석 모델들에 대해서 알아보겠습니다.

FCN

FCN은 가장 먼저 제안된 모델 중 하나입니다. 위에서 설명했으므로 넘어가겠습니다.


SegNet

SegNet 모델은 위에서 서술한 Encoder-Decoder 구조를 사용합니다. 두 파트는 서로 대칭적입니다. SegNet에서는 encoder 과정에서의 Max-Pooling 작업의 과정을 기억했다가 decoder에서 upsampling시에 사용하게 됩니다. 대신, Skip Combining 과정은 없습니다.


UNet

UNet 역시 Encoder-Decoder 구조를 사용하는 모델로 skip combining을 통해서 decoding 을 하게 됩니다.


PSPNet (Pyramid Scene Parsing Network)

PSPNet은 이미지의 전체적인 문맥 (전역 정보) 를 더 잘 학습하기 위해 최적화된 모델입니다. 먼저, 이미지는 기반 모델로 입력되어 Feature Map 을 얻게 되며, 이는 여러가지 서로 다른 scale 로 downsampling 됩니다. 그 후 Convolution 작업을 거친 Feature Map들은 다시 같은 크기로 upsample 된 후 합쳐지게 됩니다. 마지막으로, 또다른 convolution 층을 통해서 최종 이미지 분석 결과를 산출해냅니다.
여기서, 서로 다른 크기의 특성 맵들을 통해서 다양한 크기의 개체들을 더욱 잘 분석해낼 수 있게 됩니다.


정리

실내와 야외의 장면들을 포함하는 이미지에 대해서는, 이미지가 보통 서로 다른 크기로 나타나므로 PSPNet 이 선호됩니다. 보통 여기서 입력 이미지들은 상당히 큰데, 500*500 정도 됩니다.

의학 분야의 이미지에서는, UNet이 보편적인 선택입니다. skip combining 과정을 통해서 UNet은 섬세한 디테일을 놓치지 않습니다. UNet은 작은 개체들이 있는 실내, 야외 이미지에서도 유용하게 쓰일 수 있습니다.

간단한 데이터에서, 즉 큰 개체들이 조금 있는 이미지에서는 UNet 과 PSPNet은 너무 과도한 투자일 수 있습니다. 여기서는 FCN이나 SegNet과 같은 간단한 모델들만으로 충분합니다.

여러분은 다양한 분석모델과 다양한 입력 사이즈의 이미지들로 여러 실험을 해보면서 더 좋은 지식을 축적할 수 있습니다.

만약 당신의 custom 모델을 사용하고 싶지 않다면, 당신은 Keras_segmentation에 들어있는 미리 준비된 모델을 사용할 수 있습니다.

import keras_segmentation

model = keras_segmentation.models.unet.vgg_unet(n_classes=51 ,  input_height=416, input_width=608  )

마치며..

이번 포스트에서, 우리는 딥러닝을 통한 이미지 분석의 개념에 대해서 알아보았습니다. 그리고, 우리는 널리 쓰이는 유명한 모델들을 찾아보았습니다. 또한, 우리는 Keras를 통해서 전체적인 과정을 짜는 방법과, 적절한 모델을 선정하는 법을 배웠습니다. 혹시 궁금하신 점이 있다면 저에게 연락주세요!

역주 : 혹시 위에서 서술된 여러 모델들에 대한 자세한 설명이 궁금하다면 아래 사이트에 매우 정리가 잘 되어있으니 참고하세요!! https://laonple.blog.me/220958109081