본문 바로가기

AI & Big Data/AI

[AI 이론] Layer, 레이어의 종류와 역할, 그리고 그 이론 - 5 (DepthwiseConv, PointwiseConv, Depthwise Separable Conv)

반응형

Depthwise Convolutional Layer

Depthwise Conv 레이어는 우선 Tensorflow 2.X 에서는 기본 레이어 함수로 지원하나, PyTorch에서는 지원하지 않는다.

그렇다고 만들기 어렵지는 않다.

 

우선은 Depthwise Conv 레이어를 왜 쓰는지, 그 역할은 무엇인지에 대해 알아보도록 하자.

위 그림처럼, H × W × C(차례대로 Height, Width, Channels)의 Convolutional Ouput을 채널 단위로 분리하여,

각각 Conv Filter를 적용하여 Output을 만들고,

그 결과를 다시 합치면 Conv Filter가 훨씬 적은 파라메터를 갖고 동일한 크기의 Output을 낼 수 있다.

(각 Filter에 대한 연산 결과가 다른 Filter로부터 독립적일 필요가 있을 경우에는 특히나 장점이 된다.)

 

이때, 동일한 채널 내에서만 컨벌루션 곱을 진행하게 되는데 (채널 사이는 독립적),이는 in_channel의 개수 == out_channel의 개수의 관계가 성립되어야 한다.

 

Depthwise Conv는 기존의 Conv2D가 각 채널만의 Spatial Feature(공간적 특징)을 추출하는 것이 불가능하기 때문에 고안해낸 방법이다.

그러니 Depthwise Conv의 역할은 각 채널마다 Spatial Feature를 추출하는 것이라는 것을 알 수 있다.

즉, 각 채널마다 독립적으로 컨벌루션 곱을 실행한다.이렇게 되면 기존의 Conv2D에 비해 파라메터의 수는 줄어들고, 연산량은 빨라지는 효과가 있다.

 

Depthwise Conv 수학적 표현식

Depthwise Conv는 특성상 Input과 Output의 채널 개수가 같으므로, M = C가 된다.그러므로 총 파라메터 수는 K2C가 된다.

(이때, K는 Filter의 크기이며 Input의 채널의 수는 C이며, Output의 채널의 수는 M이다.)

 

Computational Cost, 즉 연산량은 여기에 Input의 Height과 Width를 곱하여, K2CHW가 된다.

 

Tensorflow 2.X와 PyTorch에서의 용례

 

# Tensorflow 2.X

tf.keras.layers.DepthwiseConv2D(
    kernel_size, strides=(1, 1), padding='valid', depth_multiplier=1,
    data_format=None, activation=None, use_bias=True,
    depthwise_initializer='glorot_uniform', bias_initializer='zeros',
    depthwise_regularizer=None, bias_regularizer=None, activity_regularizer=None,
    depthwise_constraint=None, bias_constraint=None, **kwargs
)

 

Tensorflow 2.X 버전에서는 위와 같이 함수를 제공하고 있다.

단, PyTorch에서는 제공하고 있진 않고, torch.nn.Conv2d 함수 항목에서 아래와 같은 문구를 삽입해놓았다.

 

즉, torch.nn.Conv2d 함수를 group==in_channels && out_channels == K * in_channels 이고, K >= 0일때, torch.nn.Conv2d 함수는 Depthwise Convolution Layer로 작동할 수 있다는 뜻이다.

 

그렇기 때문에, PyTorch로 DepthwiseConv2D를 구성해보면 아래와 같다.

 

# PyTorch
# DepthwiseConv2D Function

class depthwise_conv(nn.Module):
    def __init__(self, in_channel, kernels_per_layer):
        super().__init__()
        self.depthwise = nn.Conv2d(in_channel, in_channel * kernels_per_layer,
                                   kernel_size=3, padding=1, groups=in_channel)

    def forward(self, x):
        out = self.depthwise(x)
        return out

Pointwise Convolutional Layer

Pointwise Convolutional Layer부터는, Tensorflow에서도 기본적으로 지원하지 않는 레이어 영역이다.

 

Pointwise Convolutional Layer는, 1-D Convolution으로 여러 개의 채널을 하나의 새로운 채널로 합치는 역할을 한다.

흔히 1×1 Conv라고 불리는 필터이며, 주로 기존의 텐서의 결과를 논리적으로 다시 Shuffle해서 뽑아내는 것을 목적으로 한다.

 

Pointwise Convolution의 Filter 크기는 1×1으로 고정되어 있다.

그렇기 때문에 Depthwise Conv가 공간적 특징을 다루면 반면, Pointwise Conv는 채널들에 대한 연산만 수행하게 된다.따라서 Output의 크기는 불변이며, 채널의 수를 조절할 수 있는 역할을 하게 된다.

 

이는 Dimensional Reduction, 즉 차원 감소를 위해 사용되는 레이어이다.

(채널을 줄이는 것이 Dimensional Reduction의 의의이다.)

 

이렇게 차원을 감소시키면, 추후 연산량을 많이 감소시킬 수 있기 때문에 중요한 역할을 하게 된다.

 

Pointwise Conv 수학적 표현식

Pointwise Conv는 일반 Conv2d에서 K = 1인 경우이기 때문에,총 파라메터 수는 CM이 된다.

 

Computational Cost, 즉 연산량 또한 일반 Conv2d에서 K = 1인 경우이기 때문에, CMHW가 된다.

 

Pointwise Conv는 기본 제공되지 않는 레이어 함수이기 때문에 직접 구현해야 하는데, Tensorflow로 구현한 코드는 아래와 같다.

 

Tensorflow 2.X와 PyTorch에서의 용례

 

# Tensorflow 2.X
# Pointwise Conv

class PointwiseConv2D(tf.keras.layers.Layer):
    def __init__(self, in_channel, out_channel):
        super().__init__()
        self.pointwise = tf.keras.layers.Conv2D(in_channel, out_channel, kernel_size=1)
    def call(self, x):
        out = self.pointwise(x)
        return out

 

PyTorch도 위와 같이 구현하면 된다.

 

# PyTorch
# Pointwise Conv

class PointwiseConv2d(nn.Module):
    def __init__(self, in_channel, out_channel):
        super().__init__()
        self.pointwise = nn.Conv2d(in_channel, out_channel, kernel_size=1)
        
    def forward(self, x):
        out = self.pointwise(x)
        return out

Depthwise Separable Convolution Layer

Depthwise Separable Convolution Layer는 기본적으로 위 Depthwise Conv와 Pointwise Conv를 결합시켜놓은 것이다.

 

그렇기 때문에 수학적 표현식은 위 두 수학적 표현식을 합쳐놓은 것이다.

Depthwise Separable Conv는 CNN에서 연산량을 줄이기 위해서,

특히 MobileNet에서 사용하기 위해 고안된 방법이다.2017년 MobileNet v1 논문에서 찾아볼 수 있고,해당 논문에서는 아래와 같은 실험 결과를 덧붙였다.

즉, 일반 Conv로만 이루어진 MobileNet(위) 대비 Depthwise Separable Convolution Layer를 덧붙인 MobileNet(아래)은,

1.1%의 Accuracy의 희생(감소)으로 필요한 Mult-Adds는 4866 → 569, 파라메터는 29.3M에서 4.2M으로 약 88.3%, 85.7% 감소시켰다.

 

위에서 Depthwise Conv는 각 채널만의 Spatial Feature, 즉 공간적 특징만을 보고,Pointwise Conv는 각 채널들에 대한 연산만 진행한다고 했다.

 

이 둘을 합치게 된다면,

Channel과 Spatial한 특징 모두를 보기 때문에, 기존의 CNN과 유사하게 작동하지만, 파라메터의 수와 연산량이 훨씬 적어지게 되어 빠른 동작을 할 수 있게 된다.

 

파라메터의 수는,K2C + CM = C(K2 + M) 이 되고,

 

Computational Cost, 즉 연산량은K2CHW + CMHW = CHW(K2 + M) 이 되게 된다.

 

Conv2D의 연산량은 지난 시간에 K2CMHW라고 정리를 했고,Depthwise Separable Conv가 CHW(K2 + M)이 된다고 정리를 했으니,

 

(Depthwise Separable Conv) / (Conv2D) = (K2 + M) / KM 이 되며,이는 최종적으로 1/K2 + 1/M 이 된다.일반적으로 K(필터/커널의 크기)보다는 M(output_channel의 수)가 훨씬 크기 때문에,M은 0에 한없이 가까워진다고 가정하고,

 

연산량은 기존 Conv2D 대비 Depthwise Separable Conv가 약 K2 더 빠르다고 볼 수 있다.

 

Tensorflow 2.X와 PyTorch에서의 용례

# Tensorflow 2.X
# DepthwiseSeparableConv2D

class DepthwiseSeparableConv2D(tf.keras.layers.Layer):
    def __init__(self, in_channel, out_channel, kernels_per_layer):
        super().__init__()
        self.depthwise = DepthwiseConv2D(in_channel, out_channel, kernels_per_layer)
        self.pointwise = Conv2D(in_channel * kernels_per_layer, out_channel, kernel_size=1)
    def call(self, x):
        out = self.depthwise(x)
        out = self.pointwise(out)
        return out

 

위는 Tensorflow, 아래는 PyTorch이다.

 

# PyTorch
# DepthwiseSeparableConv2D

class DepthwiseSeparableConv2d(nn.Module):
    def __init__(self, in_channel, out_channel, kernels_per_layer):
        super().__init__()
        self.depthwise = nn.Conv2d(in_channel, in_channel * kernels_per_layer,
                                   kernel_size=3, padding=1, groups=in_channel)
        self.pointwise = nn.Conv2d(in_channel * kernels_per_layer, out_channel,
                                   kernel_size=1)
    def forward(self, x):
        out = self.depthwise(x)
        out = self.pointwise(out)
        return out

오늘은 3가지 레이어에 대해 알아보았다.

다음 포스팅에 또 새로운 것을 정리해보도록 하겠다.


 

반응형