합성곱 신경망(CNN)은 대뇌의 시각 피질 연구에서 시작되었고 1980년대부터 이미지 인식 분야에 사용됐습니다.
CNN은 이미지 검색 서비스, 자율주행 자동차, 영상 자동 분류 시스템 등에 큰 기여를 했습니다.
또한 CNN은 시각 분야에 국한되지 않고 음성 인식이나 자연어 처리(NLP) 같은 다른 작업에도 많이 사용됩니다.
그러나 이번 포스트에서는 시각적 애플리케이션에 초점을 맞추겠습니다.
합성곱 층과 풀링 층부터 시작해서 하나하나 알아보겠습니다.
합성곱 층
합성곱 층은 CNN에서 가장 중요한 요소입니다.
첫 번째 합성곱 층의 뉴런은 입력 이미지의 모든 픽셀에 연결되는 것이 아니라 합성곱 층 뉴런의 수용장 안에 있는 픽셀에만 연결됩니다. 두 번째 합성곱 층에 있는 각 뉴런은 첫 번째 층의 작은 사각 영역 안에 위치한 뉴런에 연결됩니다. 이런 구조는 네트워크가 첫 번째 은닉층에서는 작은 저수준 특성에 집중하고, 그다음 은닉층에서는 더 큰 고수준 특성으로 조합해나도록 도와줍니다.
어떤 층의 한 뉴런은 수용장에 해당하는 만큼의 이전 층의 뉴런의 출력과 연결됩니다.
아래 그림처럼 높이와 너비를 이전 층과 같게 하기 위해 입력의 주위에 0을 추가하는 것이 일반적입니다.
이를 제로 패딩이라고 합니다.
그리고 수영장 사이에 간격을 둬서 큰 입력층을 훨씬 작은 층에 연결하는 것도 가능합니다.
이렇게 하면 모델의 계산 복잡도를 크게 낮춰줍니다.
한 수용장과 다음 수용장 사이의 간격을 스트라이드(stride)라고 합니다.
필터
아래 그림에서는 필터라 부르는 두 개의 가중치 세트를 보여줍니다.
첫 번째 필터는 수직 필터로, 수직선 부분을 제외하고는 수용장에 있는 모든 것을 무시합니다.
두 번째 필터는 수평 필터로, 수평선 부분을 제외하고는 수용장에 있는 모든 것을 무시합니다.
각 필터는 층의 전체 뉴런에 적용되어 각각 하나의 특성 맵을 만듭니다.
이 맵은 필터를 가장 크게 활성화시키는 이미지의 영역을 강조합니다.
물론 필터를 직접 정의할 필요는 없고, 훈련하는 동안 합성곱 층이 알아서 가장 유용한 필터를 찾습니다.
여러 가지 특성 맵 쌓기
단순함을 유지하기 위해 합성곱 층의 출력을 2D로 봤지만,
실제로는 여러 필터를 가지고 필터마다 하나의 특성 맵을 출력하므로 3D로 표현하는 게 더 정확합니다.
텐서플로 구현
풀링 층
이 층의 목적은 계산량과 메모리 사용량, 파라미터 수를 줄이기 위해 입력 이미지의 부표본을 만드는 것입니다.
합성곱 층과 마찬가지로 풀링 층의 각 뉴런은 이전 층의 작은 사각 영역의 수용장 안에 있는 뉴런의 출력과 연결되어 있습니다. 이전처럼 크기, 스트라이드, 패딩 유형을 지정해야 합니다. 하지만 풀링 뉴런은 가중치가 없고, 최대나 평균 같은 합산 함수를 사용해 입력값을 더하는 것이 전부입니다. 아래는 최대 풀링 층의 예시입니다.
계산량, 메모리 사용량, 파라미터 수를 감소하는 것 외에도 최대 풀링은 작은 변화에도 일정 수준의 불변성을 만들어줍니다. CNN에서 몇 개 층마다 최대 풀링 층을 추가하면 전체적으로 일정 수준의 이동 불변성을 얻을 수 있습니다. 또한 최대 풀링은 회전과 확대, 축소에 대해 약간의 불변성을 제공합니다. 이런 불변성은 분류 작업처럼 예측이 이런 작은 부분에서 영향을 받지 않는 경우 유용합니다. 그렇다고 해서 최대 풀링이 항상 좋은 것만은 아닙니다. 이 층은 매우 파괴적이라는 단점도 가지고 있습니다. 어떤 애플리케이션에서는 불변성이 아니라 등변성이 목표가 될 수도 있습니다. 따라서 각 상황에 맞게 선택을 하는 것이 가장 좋은 방법입니다.
텐서플로 구현
CNN 구조
전형적 CNN 구조는 합성곱 층을 몇 개 쌓고 그 다음 풀링 층을 쌓고, 다시 합성곱 층을 몇 개 쌓고, 그 다음 다시 풀링 층을 쌓는 식입니다. 네트워크를 통과할수록 이미지는 점점 작아지지만 합성곱 층 때문에 일반적으로 점점 깊어집니다. 맨 위층에는 몇 개의 완전 연결 층(+ReLU)으로 구성된 일반적 피드포워드 신경망이 추가되고 마지막 층에서 예측을 출력합니다. 아래 그림은 전형적인 CNN 구조를 나타낸 그림입니다.
이러한 기본 구조에서 여러 변종이 개발되었고 놀라운 성과를 이끌어냈습니다.
LeNet-5, AlexNet, GoogLeNet, ResNet등은 이후 공부를 더 한 후에 다른 글에 따로 포스팅하겠습니다.
케라스를 사용해 ResNet-34 CNN 구현하기
대부분의 CNN 구조는 구현하기 매우 쉽습니다. (일반적으로 사전훈련된 네트워크를 사용합니다.)
이 과정을 설명하기 위해 케라스를 사용해 직접 ResNet-34 모델을 구현해보겠습니다.
케라스에서 제공되는 사전훈련된 모델 사용하기
일반적으로 GoogLeNet이나 ResNet 같은 표준 모델을 직접 구현할 필요가 없습니다.
keras.applications 패키지에 준비되어 있는 사전훈련된 모델을 코드 한 줄로 불러올 수 있습니다.
ResNet-50 모델을 사용하는 예시를 한 번 보겠습니다.
이렇게 사전훈련된 모델을 로드해 좋은 이미지 분류기를 만드는 것은 아주 쉽습니다.
keras.applications 패키지에 다른 비전 모델들도 있습니다.
여러 종류의 ResNet 모델, Inception-v3와 Xception같은 GoogLeNet 변종 모델, 여러 가지 VGGNet 모델, MobileNet과 MobileNetV2 모델이 있습니다.
이미지넷에 없는 이미지 클래스를 감지하는 이미지 분류기가 필요하다면 사전훈련된 모델을 사용해 전이 학습을 수행할 수 있습니다.
사전훈련된 모델을 사용한 전이 학습
훈련 데이터가 충분하지 않다면 사전훈련된 모델의 하위층을 사용하는 것이 좋습니다.
예를 들어 Xception 모델을 사용해 꽃 이미지를 분류하는 모델을 훈련해보겠습니다.
넣어
분류와 위치 추정
물체의 위치를 추정하는 것인 회귀 작업으로 나타낼 수 있습니다.
물체 주위의 바운딩 박스를 예측하는 일반적 방법은 물체 중심의 수평, 수직 좌표와 높이, 너비를 예측하는 것입니다.
즉 네 개의 숫자를 예측해야 합니다. 따라서 모델을 크게 바꿀 필요가 없이 네 개의 유닛을 가진 두 번째 밀집 출력 층을 추가하고 MSE 손실을 사용해 훈련합니다.
base_model = keras.applications.xception.Xception(weights="imagenet",
include_top=False)
avg = keras.layers.GlobalAveragePooling2D()(base_model.output)
class_output = keras.layers.Dense(n_classes, activation="softmax")(avg)
loc_output = keras.layers.Dense(4)(avg)
model = keras.models.Model(inputs=base_model.input,
outputs=[class_output, loc_output])
model.compile(loss=["sparse_categorical_crossentropy", "mse"],
loss_weights=[0.8, 0.2], # 어떤 것을 중요하게 생각하느냐에 따라
optimizer=optimizer, metrics=["accuracy"])
여기서 한 가지 문제가 있는데, 꽃 데이터셋은 꽃 주위에 바운딩 박스를 가지고 있지 않습니다.
따라서 직접 만들어 추가해야 하는데, 이는 머신러닝 프로젝트에서 가장 어렵고 비용이 많이 드는 작업 중 하나입니다.
이를 위한 오픈 소스 이미지 레이블 도구로 VGG Image Annotator, LabelImg, OpenLabeler, ImgLab이 있고 유료 제품으로는 LabelBox, Supervisely가 있습니다. 처리해야 할 이미지가 아주 많다면 아마존 매커니컬 터크와 같은 크라우드소싱 플랫폼을 고려해볼 수도 있습니다.
어쨋든, 꽃 데이터셋의 모든 이미지에 대해 바운딩 박스가 준비되었다고 하겠습니다.
클래스 레이블, 바운딩 박스와 함께 전처리된 이미지의 배치가 하나의 원소인 데이터셋을 만들어야 합니다.
각 원소는 (images, (class_labels, bounding_boxes)) 형태의 튜플이 됩니다.
Tip: 바운딩 박스의 높이와 너비는 물론 수평과 수직 좌표의 범위를 0 ~ 1로 정규화해야 합니다. 또한 일반적으로 높이와 너비를 직접 예측하지 않고 높이와 너비의 제곱근을 예측합니다. 따라서 작은 바운딩 박스에서 10픽셀 오차가 큰 바운딩 박스의 10픽셀 오차보다 많은 벌칙을 받습니다.
MSE는 모델을 훈련하기 위한 손실 함수로 쓸 수 있지만 바운딩 박스에 널리 사용되는 지표는 IoU(intersection over union)입니다. 이 값은 예측한 바운딩 박스와 타깃 바운딩 박스 사이에 중첩되는 영역을 전체 영역으로 나눈 값입니다. 이는 tf.keras.metrics.MeanIoU에 구현돼있습니다.
객체 탐지
하나의 이미지에서 여러 물체를 분류하고 위치를 추정하는 작업을 객체 탐지라고 합니다.
몇 년 전까지 널리 사용되던 방법은 하나의 물체를 분류하고 위치를 찾는 분류기를 훈련한 다음 아래 그림처럼 이미지를 모두 훑는 것입니다.
이런 간단한 방법은 꽤 잘 작동하지만 CNN을 여러 번 실행시켜야 해서 많이 느립니다.
다행히 완전 합성곱 신경망(fully convolutional network, FCN)을 사용하여 CNN을 훨씬 빠르게 이미지에 슬라이딩시킬 수 있습니다.
완전 합성곱 신경망
CNN의 맨 위 밀집 층을 합성곱 층으로 바꾼 것이 FCN(Fully Convolutional Network)입니다.
이해를 위해 예를 들어보면, 7 \(\times\) 7 크기 100개의 특성 맵을 출력하는 합성곱 층 위에 뉴런이 200개 있는 밀집 층이 있다고 하겠습니다. 각 뉴런은 합성곱 층에서 출력된 100 \(\times\) 7 \(\times\) 7 크기의 활성화 값과 편향에 대한 가중치 합을 계산합니다. 이 밀집 층을 7 \(\times\) 7 크기의 필터 200개와 'valid' 패딩을 사용하는 합성곱 층으로 바꾼다고 해보겠습니다. 이 층은 1 \(\times\) 1 크기의 특성 맵 200개를 출력할 것입니다. 다시 말하면 밀집 층과 마찬가지로 200개의 숫자가 출력되는 것입니다. 이 합성곱 층이 수행하는 계산을 살펴보면 밀집 층이 만드는 숫자와 동일합니다. 유일한 차이는 [배치 크기, 200] 크기의 텐서이고 합성곱 층이 [배치 크기, 1, 1, 200] 크기의 텐서를 출력합니다.
Tip: 밀집 층을 합성곱 층으로 바꿀 때 합성곱 층의 필터 개수와 밀집 층의 유닛 개수가 동일해야 하고 필터의 크기와 입력 특성 맵의 크기가 같아야 합니다. 그리고 'valid' 패딩도 사용해야 하고 스트라이드는 1 이상 지정할 수 없습니다.
이 네트워크의 역할을 살펴보겠습니다.
밀집 층은 특정 입력 크기를 기대하지만 합성곱 층은 어떤 크기의 이미지도 처리할 수 있습니다.
합성곱 층은 입력 채널마다 커널 가중치가 달라서 특정 개수의 채널을 기대합니다.
FCN은 합성곱 층만 가지므로 어떤 크기의 이미지에서도 훈련하고 실행할 수 있습니다.
YOLO
YOLO는 조지프 레드먼이 2015년 논문에서 제안한 객체 탐지 구조이고 이어서 YOLOv2와 YOLOv3가 나오면서 향상되었습니다. 이 알고리즘은 매우 빨라서 실시간으로 비디오에 적용할 수 있습니다. YOLOv3 구조는 앞서 설명한 구조와 매우 비슷하지만 몇 가지 차이점이 있습니다.
- 각 격자 셀마다 5개의 바운딩 박스를 출력합니다. 바운딩 박스마다 하나의 존재여부 점수가 부여됩니다. 20개의 클래스가 있는 PASCAL VOC 데이터셋에서 훈련되었기 때문에 20개의 클래스 확률을 출력합니다. 각각 4개의 좌표를 가진 5개의 바운딩 박스, 5개의 존재여부 점수, 20개의 클래스 확률로 총 45개의 숫자가 출력됩니다.
- 바운딩 박스 중심의 절대 좌표 대신 격자 셀에 대한 상대 좌표를 예측합니다. 각 격자 셀에 대해 YOLOv3는 바운딩 박스의 중심이 격자 셀 안에 놓인 것만을 예측하도록 훈련됩니다. YOLOv3는 로지스틱 활성화 함수를 적용해 바운딩 박스 좌표가 0과 1 사이가 되도록 만듭니다.
- 신경망을 훈련하기 전에 YOLOv3는 앵커 박스라 부르는 5개의 대표 바운딩 박스 크기를 찾습니다. 이를 위해 k-평균 알고리즘을 훈련 세트 바운딩 박스의 높이와 너비에 적용합니다. 예를 들어 훈련 이미지에 많은 보행자가 있다면 앵커 박스 중 하나는 전형적인 보행자의 크기가 될 것입니다. 그 다음 신경망이 격자 셀마다 5개의 바운딩 박스를 예측할 때 각 앵커 박스의 스케일을 얼마나 다시 조정할 것인지 예측합니다. 이런 앵커 박스를 통해 네트워크는 적절한 차원의 바운딩 박스를 예측할 가능성이 높고, 이는 훈련 속도를 높여줍니다.
- 네트워크가 다른 스케일을 가진 이미지를 사용해 훈련됩니다. 훈련하는 동안 몇 번의 배치마다 랜덤하게 새로운 이미지 차원을 선택합니다. 이를 통해 네트워크가 다른 스케일의 객체를 감지하는 방법을 학습합니다.
이 외에도 다른 객체 탐지 모델이 텐서플로 모델 프로젝트 깃허브에 사전훈련된 가중치와 함께 제공됩니다. 인기가 많은 SSD와 Faster R-CNN 같은 모델은 TF 허브에 포팅되어 있습니다.
시맨틱 분할
시맨틱 분할을 쓰면 각 픽셀은 아래 그림에서처럼 픽셀이 속한 객체의 클래스로 분류됩니다. 클래스가 같은 물체는 구별되지 않습니다.
이 작업에서 가장 어려운 점은 이미지가 일반적인 CNN을 통과할 때 점진적으로 위치 정보를 잃는 것입니다. 따라서 보통의 CNN은 왼쪽 아래 어딘가에 사람이 있다고 알 수 있지만 그보다 더 정확히 알지 못합니다. 그런데 조너선 롱 등이 2015년 논문에서 매우 단순한 해결책을 제시했습니다. 저자들은 먼저 훈련된 CNN을 FCN으로 변환합니다. 이 CNN이 입력 이미지에 적용하는 전체 스트라이드는 32입니다.(1보다 큰 스트라이드를 모두 더한 값입니다.) 이는 마지막 층이 입력 이미지보다 32배나 작은 특성 맵을 출력한다는 의미입니다. 이는 너무 듬성듬성하므로 해상도를 32배로 늘리는 업샘플링 층을 하나 추가합니다. 여러 가지 이미지 사이즈를 늘리는 업샘플링 방법이 있지만 저자들은 전치 합성곱 층을 사용했습니다. 이 층은 0으로 채워진 빈 행과 열을 삽입해 늘린 다음 일반적 합성곱을 수행합니다. tf.keras에서는 Conv2DTranspose 층을 사용합니다.
좋은 방법이지만 여전히 정확도가 떨어집니다. 따라서 저자들은 아래쪽 층에서부터 스킵 연결을 추가했습니다. 예를 들어 2배로 출력 이미지를 업샘플링하고 아래쪽 층의 출력을 더하여 해상도를 두 배로 키웁니다. 그 다음 이 결과를 16배로 늘려서 업샘플링하여 최종적으로 32배의 업샘플링을 달성합니다. 비슷하게 두 번째 스킵 연결을 사용해 더 낮은 층의 세부 정보를 복원해 최상의 구조를 만들었습니다. 요약하면 다음과 같은 과정을 거칩니다. 2배 업샘플링, 동일한 크기의 아래쪽 층 출력을 도함, 2배 업샘플링, 더 아래쪽 층의 출력을 더함, 마지막으로 8배 업샘플링입니다.
시맨틱 분할의 텐서플로 구현을 제공하는 많은 깃허브 저장소가 있습니다. 텐서플로 모델 프로젝트에서 사전훈련된 인스턴스 분할 모델을 찾을 수 있습니다. 인스턴스 분할은 시맨틱 분할과 비슷하지만 동일한 클래스의 물체여도 각 물체를 구분하여 표시합니다. (이 모델은 2017년 한 논문에 제안된 Mask R-CNN 모델입니다.)
'DATA > 머신 러닝' 카테고리의 다른 글
[머신 러닝] RNN과 CNN을 사용해 시퀀스 처리하기 (0) | 2022.02.15 |
---|---|
[머신 러닝] 텐서플로를 사용한 사용자 정의 모델과 훈련 (0) | 2022.02.04 |
[머신 러닝] 머신러닝을 위한 텐서플로 (0) | 2022.02.02 |
[머신 러닝] 심층 신경망 훈련하기 (0) | 2022.02.01 |
[머신 러닝] 신경망 하이퍼파라미터 튜닝하기 (0) | 2022.01.23 |