1. 모바일 넷 (MobileNet)이란?
모바일 넷은 스마트폰 및 기타 모바일 장치와 같이 리소스가 제한된 환경에서 효율적인 계산을 위해 설계된 경량 심층신경망으로 2017년 구글에서 개발했다. 모바일 넷은 depth-wise convolutions을 기반으로 하는 새로운 아키텍처를 사용하여 기존의 CNN에 비해 매개변수 수와 계산 요구 사항을 크게 줄였습니다. 따라서 제한된 장치에 배포가 가능해졌습니다. 모바일 넷은 image classification, object detection, pose estimation, and semantic segmentation을 비롯한 다양한 컴퓨터 비전 작업에 널리 사용되고 있습니다.
2. Depth-wise Separable Convolutions
Depth-wise Separable Convolutions은 필터의 채널과 공간 차원을 분리할 수 있다는 생각에서 비롯됐습니다. 일반적인 컨볼루션에서는 채널 방향과 공간 방향의 컨볼루션을 동시에 수행합니다. 반면 Depth-wise Separable Convolutions은 공간 방향의 depth-wise 컨볼루션과 채널 방향의 pointwise convolution을 따로 수행하여 합치는 방식을 사용합니다.
2.1 일반적인 CNN 연산
기존 CNN 네트워크에서 컨볼루션 연산은 Input feature map의 각 채널에 필터를 적용하여 output feature map을 생성한다. 이 작업에 사용되는 필터 수는 일반적으로 input feature map의 채널 수와 동일하다.
이미지는 일반적인 컨볼루션 연산의 모습입니다. 그리고 아래의 수식은 CNN의 연산량을 나타낸다. (이미지에서는 width, height을 F로 통일해지만 수식에서는 W, H으로 분리하여 작성함)
$$O_{2D_CNN} = K^2 \cdot H \cdot W \cdot C_{in} \cdot C_{out} \cdot F$$
\(K\): Convolution kernel size
\(H\): Input feature map height
\(W\): Input feature map width
\(C_{in}\): Number of input channels
\(C_{out}\): Number of output channels
2.2 Depth wise separable convolutions 연산
대조적으로, depth wise separable convolutions 은 input feature map 의 각 채널에 독립적으로 필터를 적용하여 여러 output feature map 을 생성합니다. 채널마다 다른 필터가 적용되기 때문에 출력 채널의 수는 입력 채널의 수와 동일하다. (필터 수 = 출력 채널 수) 일반적으로 컨볼루션보다 연산량이 적어지는 이유는 채널 axis로 연산이 이루어지지 않기 때문이다.
이미지는 depth wise separable convolutions의 모습이다. 그리고 아래의 수식은 연산량을 나타낸다. (이미지에서는 width, height을 F로 통일해지만 수식에서는 W, H으로 분리하여 작성함)
$$O_{depthwise} = K^2 \cdot H \cdot W \cdot C_{in}$$
\(K\) : Convolution kernel size
\(H\) : Input feature map height
\(W\) : Input feature map width
\(C_{in}\) : Number of input channels
2.3 Pointwise Convolution 연산
1x1 conv는 GoogleNet에서 다루었듯이 연산량을 줄이는데 효과적이며, 필터 수를 줄여서 차원 축소를 할 수 있습니다. Pointwise convolution에는 depthwise convolution과 반대로 채널 방향으로 연산을 수행하고, 공간 방향으로는 연산을 하지 않는다.
이미지는 Pointwise convolution의 모습이다. 그리고 아래의 수식은 연산량을 나타낸다. (이미지에서는 width, height을 F로 통일해지만 수식에서는 W, H으로 분리하여 작성함)
$O_{pointwise} = K^2 \cdot H \cdot W \cdot C_{in} \cdot C_{out}$
$K$: Convolution kernel size
$H$: Input feature map height
$W$: Input feature map width
$C_{in}$: Number of input channels
$C_{out}$: Number of output channels
2.4 일반 CNN 연산과 모바일넷 연산 비교
커널 사이즈가 2이고 입력 채널수가 64인 2D CNN의 연산량은 다음과 같이 계산될 수 있다.
$O_{2D_CNN} = 2^2 \cdot H \cdot W \cdot 64 \cdot C_{out}$
반면, 모바일 넷은 다음과 같이 계산될 수 있습니다. 이에 기존 2D CNN의 연산량에 비해 모바일넷의 연산량이 적다고 할 수 있다.
Depthwise Convolution: $O_{depthwise} = 2^2 \cdot H \cdot W \cdot 64$
Pointwise Convolution: $O_{pointwise} = 2^2 \cdot H \cdot W \cdot 64 \cdot C_{out}$
$O_{MobileNet} = O_{depthwise} + O_{pointwise} = 2^2 \cdot H \cdot W \cdot 64 + 2^2 \cdot H \cdot W \cdot 64 \cdot C_{out}$
3. 모바일 넷 Architecture
모바일 넷의 아키텍처(Architecture)는 아래와 같다. Deptwise separble convolution을 제외하고는 일반적인 CNN 네트워크와 비슷하다.
모바일 넷에서는 아래 그림의 오른쪽과 같은 형태로 depthwise separable convolution layer를 구성한다. (왼쪽은 일반적인 Conv + BN + ReLU)
아래 이미지는 모바일 넷의 전체 아키텍쳐를 보여준다. “Conv dw”은 depth separable convolution을 의미하며 “s1”은 stride = 1, “s2”는 stride =2를 의미한다. 모바일 넷에서 특이한점은 Pooling layer를 따로 사용하지 않고 stride를 2로 잡아 차원 축소와 정규화 효과를 낸다는 점이다. 여기서 별도의 풀링 레이어를 사용하지 않는 이유는 다음과 같다. 요약하면 계산 비용을 줄이고 공간 해상도를 유지하며 네트워크를 정규화 하기 위해 풀링 레이어 대신 stride 2를 사용하는 방법을 사용합니다.
- 첫 번째로 계산 효율성이다. 풀링 레이어는 추가 계산으로 네트워크의 매개변수 수를 늘립니다. 모바일 넷은 리소스가 제한된 장치에 배포해야 매개변수 수가 늘어나지 않는 stride 2를 사용하는 방법이 더 적합합니다.
- 두 번째로 풀링 레이어는 피처 맵의 공간 해상도를 감소시켜 세분화된 공간 정보의 손실을 불러올 수 있습니다.
- 세 번째로 stride를 통해 연산을 처리하는 것이 과적합을 방지하는 데 도움이 되기 때문입니다.
4. 구현 코드
import tensorflow as tf
from tensorflow.keras import layers, models
def add_block(model, num_filters):
model.add(layers.Conv2D(num_filters, (1, 1), padding='same', activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.DepthwiseConv2D((3, 3), padding='same', activation='relu'))
model.add(layers.BatchNormalization())
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = x_train.reshape(-1, 28, 28, 1)
x_test = x_test.reshape(-1, 28, 28, 1)
x_train, x_test = x_train / 255.0, x_test / 255.0
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(28, 28, 1)))
model.add(layers.BatchNormalization())
filters = [64, 128, 256, 512, 1024]
for num_filters in filters:
add_block(model, num_filters)
model.add(layers.Flatten())
model.add(layers.Dense(10, activation='softmax'))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=10, batch_size=32, validation_data=(x_test, y_test))
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
print('Test Accuracy:', test_acc)
5. 참고 문헌
☞ 문서의 내용은 가장 참고문헌 및 사이트를 참고하여 필자가 보기 쉽도록 정리한 내용입니다.
☞ 틀린 내용 및 저작권 관련 문의가 있는 경우 문의하시면 수정 및 삭제 조치하겠습니다.