CNN의 배경과 구성요소, 구현방법과 뛰어난 성능을 보이는 CNN구조 몇 개를 알아보자.
Ojbect detection 과 Semantic segmentation 에 대한 작업에 대해서도 다룰 것이다!
원숭이 대상 실험에서,
처음엔 수평,수직,대각선으로 시작, 고수준으로 올라가며 정교화된 패턴으로 반응한다는 것!
이러한 시각피질 연구는 현재 합성곱신경망(convolutional Neural Network)라 부르는 것으로 발전되었다. 1998년 LeNet-5 구조를 소개한 논문에서 기존 완전연결층, 시그모이드 활성화 함수 이외에도 합성곱 층, 풀링층 구성요소가 소개되었다. 본격적으로 알아보자!
CNN의 가장 중요한 요소는 convolutional layer 라 불리는 합성곱 층이다.
스트라이드란 위의 그림에서 어떤 층이 이전층과 연결되었을 때 매핑되는 이전 층 픽셀 사이의 간격을 말한다.(커널이 움직이며 스캔하는 보폭이다)
패딩이란 커널로 스캔하는 과정에서 스캔, 보폭만큼 이동, 스캔이 반복되고 남은 픽셀을 무시할까를 결정하는 여부이다. padding = 'same'은 0인 픽셀을 추가하는 제로패딩 사용, padding = 'valid' 는 패딩 없음을 뜻한다.
CNN에서 뉴런의 가중치 = 커널이 가진다.
예컨대 커널 2X2 라 하면, 2X2 짜리 도장을 온 사진에 찍는 것과 같다!
왼쪽은 흰 수직선이 잇는 검은 사각형이다.(가중치=하나의 수용장에 가운데 열이 1로 채워짐), 오른쪽은 흰 수평선이 있는 검은 사각형이다.(하나의 수용장에 가운데 행이 1로 채워짐)
이렇게 입력에 필터(=커널)이 적용된 각각의 결과를 특성맵이라 한다. (합성곱 층은 자동으로 가장 유용한 필터를 찾고 맵을 반환하며 상위 층이 이들을 결합하여 복잡한 패턴을 학습하는 것)
정리하자면, 합성 곱 층 = 하나의 맵은 작은 커널을 입력에 도장처럼 찍어서 만듦 = 커널에 따라 맵의 결과가 달라짐.
합성곱 층에 있는 뉴런의 출력을 계산하는 식을 보자. 위 이미지를 아래의 식으로 나타낸 것이다.
import numpy as np
from sklearn.datasets import load_sample_image
# 샘플 이미지를 로드합니다.
china = load_sample_image("china.jpg") / 255
flower = load_sample_image("flower.jpg") / 255
images = np.array([china, flower])
batch_size, height, width, channels = images.shape
# 2개의 필터를 만듭니다.
filters = np.zeros(shape=(7, 7, channels, 2), dtype=np.float32)
filters[:, 3, :, 0] = 1 # 수직선 (너비 3에만 일정하게 1을 채우니 수직선)
filters[3, :, :, 1] = 1 # 수평선 (높이 3에만 일정하게 1을 채우니 수평선)
outputs = tf.nn.conv2d(images, filters, strides=1, padding="SAME")
plt.imshow(outputs[0, :, :, 1], cmap="gray") # 첫 번째 이미지의 두 번째 특성맵을 그립니다.
plt.axis("off")
plt.show()
tf.nn.conv2d()
를 사용한다. 스트라이드 1과 제로패딩(SAME, 사용하지 않으려면 VALID로 지정)을 사용했다.사실 실제 CNN에서 훈련 가능한 변수로 필터를 정의하므로 코드가 훨씬 간단해진다! 아래의 코드를 사용하자.
conv_same = keras.layers.Conv2D(filters=32, kernel_size=3, strides=1, padding="SAME", activation = 'relu')
CNN은 합성곱 층이 많은 양의 RAM 을 필요로 하므로 주의하자.
풀링층 또한 층의 일종으로 계산량 / 메모리 사용량 / 파라미터 수를 줄이기 위한 목적을 가진다. 이를 위해 풀링층에선 입력이미지의 부표본을 만든다. 말이 어렵지만 특정 크기와 스트라이드에 대하여 평균 또는 최댓값을 취하여 사진을 줄이겠다는 것이다.
합성곱 층과 마찬가지로 풀링층에서는 이전 층의 수용장이 하나의 뉴런과 연결되며,
아래의 예를 보자.
해당 풀링층은
이동 불변성의 반대는 등변성equivariance가 필요한 상황이며 대표적으로 Semantic Segmentation 에선 등변성이 필요하다.
풀링층을 구현하는 것은 다음과 같으며 보통 어떤 패딩도 하지 않는다. 다음은 2 X 2 커널을 이용해 최대 풀링층을 만드는 코드이다.
max_pool = keras.layers.MaxPool2D(pool_size=2)
(평균 풀링층은 MaxPool2D 의 자리에 AvgPool2D를 사용하자.)
현재까지는 특정 공간(2X2와 같은) 차원에 대해 최대, 평균 풀링을 사용했지만 깊이 차원으로 풀링을 진행할 수 있다. 일단 같은 사진이 회전된 여러 가지 필터를 준비한다. 이에 대해 깊이 방향 최대 풀링층을 적용하면, 회전에 상관없이 동일한 출력을 만드는 것!
추가로 최대, 평균 풀링층 이외에도 전역 평균 풀링 층global average pooling layer을 사용할 수 있을 것이다. 이는 하나의 특성 맵이 주어지면 여러 값이 아닌 단 하나의 숫자를 출력하는 층이다. 보통 출력층에서 유용하다!
global_avg_pool = keras.layer.GlobalAvgPool2D()
합성곱 층, 풀링층, 완전 연결층을 배웠으니 이를 조합하면 CNN층이 되는 것을 이해할 수 있을 것이다. CNN의 전형적 구조는 다음과 같다.
간단한 CNN 코드를 보자.
from functools import partial
DefaultConv2D = partial(keras.layers.Conv2D,
kernel_size=3, activation='relu', padding="SAME")
model = keras.models.Sequential([
DefaultConv2D(filters=64, kernel_size=7, input_shape=[28, 28, 1]),
keras.layers.MaxPooling2D(pool_size=2),
DefaultConv2D(filters=128),
DefaultConv2D(filters=128),
keras.layers.MaxPooling2D(pool_size=2),
DefaultConv2D(filters=256),
DefaultConv2D(filters=256),
keras.layers.MaxPooling2D(pool_size=2),
keras.layers.Flatten(),
keras.layers.Dense(units=128, activation='relu'),
keras.layers.Dropout(0.5),
keras.layers.Dense(units=64, activation='relu'),
keras.layers.Dropout(0.5),
keras.layers.Dense(units=10, activation='softmax'),
])
이제 이러한 기본 구조에서 파생된 변종을 보자. 고전 구조 LeNet-5와, ILSVRC 이미지넷 대회에서 우승한 세 가지 모델(AlexNet, GoogleNet, ResNet)을 보자.
1988, 얀 르쿤, MNIST 데이터에 널리 사용
층의 구조를 보자.
MNIST 기본이미지 28 X 28 에 제로 패딩하여 32 X 32 가 되고, 정규화하였다.
최근에는 평균 폴링에 학습되는 계수, 편향이 없지만 여기서는 각 뉴런이 입력의 평균을 계산, 계숫값을 곱하고 편향을 더한 것에 활성화 함수를 적용했다.
C3의 뉴런은 맵 전체가 아닌 3,4개 맵의 뉴런에만 연결된다.
출력은 이미지가 얼마나 특정 숫자 클래스에 속하는지 측정하고, 일반적인 가중치 벡터와의 곱셈 대신 입력 벡터, 가중치 벡터 사이 유클리드 거리를 출력한다.
2012년 이미지넷 대회에서 2위 26% 에러율과 큰 차이인 17% 에러율을 기록한 CNN
합성곱 층 위에 폴링 층이 아닌 합성곱 층끼리 쌓은 구조가 특징
합성곱끼리 그대로 쌓았을 때의 문제는 과대적합 발생 가능성이며, 이를 줄이기 위해 두 가지 규제를 사용한다
인셉션 모듈에서 1X1 커널을 사용한 이유는 무엇일까?
스킵 연결로 시작하여 스킵 연결로 끝나는 한 단위를 잔차 유닛이라 볼 수 있으며(residual unit), 이러한 연결이 쌓이면 심층 잔차 네트워크가 된다.
왜 이런 연결을 추가할까?
중간에 깊은 잔차 유닛을 쌓은 것 외에 나머지는 GoogleNet 과 똑같다.
GoogleNet + ResNet 이지만 인셉션 모듈을 깊이별 분리 합성곱 층(분리 합성곱) 이라는 층으로 대체.
일반적인 합성곱 층이 공간상 패턴(타원) / 채널 사이의 패턴(입+코+눈 = 얼굴)을 동시에 잡기 위해 필터를 사용하나,
분리 합성곱 층은 공간과 채널 사이 패턴을 분리하여 모델링한다고 가정
따라서