이전, MobileNet v1에 대해서 알아보았습니다.
이번 포스트에서는 MobileNet v1을 코드로 구현하는 부분을 다룰 것입니다.
우선, MobileNet v1의 이론적인 부분에 대한 내용은 다음과 같습니다.
위 페이지에서도 언급되었지만, 코드 생성을 위해 다시 언급해 봅니다. ㅎㅎ
MobileNet v1은 다음과 같은 Architecture를 갖습니다.
위 표에서 몇가지 단어(?)들이 의미하는 것에 대해 짚고 넘어가도록 하겠습니다.
s는 Stride Size를 의미합니다. 예를 들면, s2는 Stride Size가 2 임을 의미합니다. Conv dw는 Depthwise Convolution을 의미하고 Conv / s1은 Pointwise Convolution을 의미합니다.
그리고 MobileNet v1의 핵심 구조인 Depthwise Separable Convolution은 다음과 같습니다.
3x3 Depthwise Convolution을 통과한 후에 Batch Normalization을 수행하고 ReLU를 통과시킵니다. 그리고 Pointwise Convolution 연산을 수행하게 되는데, 이 Pointwise Convolution은 1x1 Convolution을 통과하고 Batch Normalization, ReLU를 차례로 통과하는 구조입니다.
각각의 요소에 따른 Code 부분은 다음과 같습니다.
_Standard_Conv와 _Depthwise_Separable_Conv, GlobalAveragePooling2D, Dropout, Dense로 간단하게 구현할 수 있습니다. 각 Layer마다의 Stride, Padding, Filter Size 등은 왼쪽 표에 맞게 넣어주면 됩니다.
앞서 언급한 _Standard_Conv와 _Depthwise_Separable, Pointwise 부분에 대한 코드는 다음과 같습니다.
TensorFlow에서 제공하는 Conv2D, DepthwiseConv2D, BatchNormalization, ReLU를 통해 간단하게 구현할 수 있습니다. 참고로 앞서 언급하였듯이 Depthwise Separable Convolution은 Depthwise→Pointwise의 순으로 수행되는 것을 의미합니다.
이제 모두 구현하였다고 생각해도 무방합니다!
다음은 위 내용을 토대로 구현한 Model의 전반적인 코드입니다.
- Main 부분
# Initializing models
MobilenetV1 = Mobilenet_V1(classes= 5, alpha= 1.0, rho= 1.0, droppout= 0.3, img_size= (150, 150)).build()
# Complie optimizer and loss function into model
MobilenetV1.compile(optimizer='adam', loss= CategoricalCrossentropy(label_smoothing=0.01, metrics= ['acc'])
# Callback
checkpoint = callbacks.ModelCheckpoint(args.Mobilenetv1_folder, monitor= 'val_acc', save_best_only= True, verbose = 1)
lr_R = callbacks.ReduceLROnPlateau(monitor= 'acc', patience= 4, verbose= 1 , factor= 0.5, min_lr= 0.00001)
# Training model
MobilenetV1.fit(train_data, validation_data= val_data, epochs= 100, verbose= 1, callbacks= [checkpoint, lr_R])
- Model 부분
from tensorflow.keras.layers import *
from tensorflow.keras import *
class Mobilenet_V1():
def __init__(self,*, classes, alpha = 1.0, rho = 1.0, droppout = 0.001, img_size = (224,224)):
self._alpha = alpha
self._rho = rho
self._num_classes = classes
self.model = None
self._droppout = droppout
self.img_size = img_size
def __Standard_Conv(self):
return models.Sequential([
Conv2D(filters= 32, kernel_size=(3,3), strides= (2,2), padding= 'valid'),
BatchNormalization(),
ReLU(max_value= 6)
])
def __Depthwise_Conv(self, strides, padding):
return models.Sequential([
DepthwiseConv2D(kernel_size= (3,3), strides= strides, padding= padding),
BatchNormalization(),
ReLU(max_value= 6)
])
def __Pointwise_Conv(self, filters):
return models.Sequential([
Conv2D(filters= int(filters * self._alpha), kernel_size= (1,1), strides= 1),
BatchNormalization(),
ReLU(max_value= 6)
])
def __Depthwise_Separable_Conv(self, *, strides_depthwise, padding_depthwise, filters_pointwise):
return models.Sequential([
self.__Depthwise_Conv(strides= strides_depthwise, padding= padding_depthwise),
self.__Pointwise_Conv(filters= filters_pointwise)
])
def build(self):
featur_map_size = int(self.img_size[0] * self._rho)
self.model = Sequential([
InputLayer(input_shape= (featur_map_size,featur_map_size,3)),
self.__Standard_Conv(),
# Depth_Separable_Conv 1
self.__Depthwise_Separable_Conv(strides_depthwise= (1,1), padding_depthwise= 'same',filters_pointwise= 64),
# Depth_Separable_Conv 2
self.__Depthwise_Separable_Conv(strides_depthwise= (2,2), padding_depthwise= 'valid', filters_pointwise= 128),
# Depth_Separable_Conv 3
self.__Depthwise_Separable_Conv(strides_depthwise= (1,1), padding_depthwise= 'same', filters_pointwise= 128),
# Depth_Separable_Conv 4
self.__Depthwise_Separable_Conv(strides_depthwise= (2,2), padding_depthwise= 'valid', filters_pointwise= 256),
# Depth_Separable_Conv 5
self.__Depthwise_Separable_Conv(strides_depthwise= (1,1), padding_depthwise= 'same', filters_pointwise= 256),
# Depth_Separable_Conv 6
self.__Depthwise_Separable_Conv(strides_depthwise= (2,2), padding_depthwise= 'valid', filters_pointwise= 512),
# Depth_Separable_Conv 7 - > 11
self.__Depthwise_Separable_Conv(strides_depthwise= (1,1), padding_depthwise= 'same', filters_pointwise= 512),
self.__Depthwise_Separable_Conv(strides_depthwise= (1,1), padding_depthwise= 'same', filters_pointwise= 512),
self.__Depthwise_Separable_Conv(strides_depthwise= (1,1), padding_depthwise= 'same', filters_pointwise= 512),
self.__Depthwise_Separable_Conv(strides_depthwise= (1,1), padding_depthwise= 'same', filters_pointwise= 512),
self.__Depthwise_Separable_Conv(strides_depthwise= (1,1), padding_depthwise= 'same', filters_pointwise= 512),
# Depth_Separable_Conv 12
self.__Depthwise_Separable_Conv(strides_depthwise= (2,2), padding_depthwise= 'valid', filters_pointwise= 1024),
# Depth_Separable_Conv 13
self.__Depthwise_Separable_Conv(strides_depthwise= (2,2), padding_depthwise= 'same', filters_pointwise= 1024),
GlobalAveragePooling2D(),
# FC
Dropout(self._droppout),
Dense(self._num_classes, activation= 'softmax')
])
return self.model
생각보다 정말 간단하게 구현할 수 있었습니다.
물론, TensorFlow를 사용하여 기본적인 연산(ex. Conv2D, Batch Normalization 등)은 구현할 필요가 없었기에 간단할 수도 있겠지만, 그래도 기본 요소들을 활용하여 어떠한 모델을 구현함에 있어 짧은 코드만으로 구현할 수 있다는 것은 의외라고 생각할 수도 있을 것입니다.
이번 포스트는 이렇게 간단하게 마무리하도록 하겠습니다!
'Programming > Deep Learning Network' 카테고리의 다른 글
[Code] VNect (0) | 2023.03.13 |
---|---|
[Model] VNect과 XNect (0) | 2023.03.13 |
[Model] RefineDet (0) | 2023.03.06 |
[Model] SSD (Single Shot Detector) (0) | 2023.03.02 |
[Model] YOLO v1 (0) | 2023.02.23 |
댓글