GhostNet: More Features from Cheap Operations


본 글은 CVPR 2020에서 발표한 GhostNet에 대한 코드와 함께 보는 논문 리뷰입니다

간단 요약

  • Ghostnet에서는 전통적인 CNN에서 나타나는 특징인 중복된 Feature(중요한 Feature)에 중점을 두고 이를 효율적으로 만들어 내는 방법에 대하여 작성된 논문입니다.
  • 기존에 뽑아둔 Feature를 일반적인 Convolution 연산보다 적은 resource를 가지는 Linear Transfer연산을 통하여 조금씩 변형하여 중복된 Feature를 만드는 것에 초점이 잡혀있습니다

GhostNet : Idea

아래의 그림은 Resnet 50의 일부 Feature 맵을 시각화 한 사진인데, 여기서 보면 일부 비슷한 모습을 가지는 Feature Map을 볼 수 있습니다. 이를 본 논문에서는 이러한 겹치는 Feature를 Ghost Feature라고 서술하였습니다.

Ghostnet에서는 이러한 Ghost Feature map을 convolution 연산을 통해 자연스럽게 나오는게 아닌 Linear Transfer를 이용하여 기존의 뽑아낸 Feature를 조금씩 변형하여 만든 Feature맵으로 유도하여 적은 Parameter와 Flops를 가진 모델을 만드는 것을 목표로 합니다.

Ghost Module

기존의 CNN Block Module은 아래의 첫번째 그림과 같이 연산이 됩니다. 이 경우 필요한 Parameter의 수는 다음과 같습니다.

$c$ : channel수
$kernel$ : kernel size

$$
parameters = c_{in} \times c_{out} \times kernel
$$

두번째 그림에서 보여주는 Ghost Module은 다음과 같은 연산과정을 통하여 이루어집니다

  • Input을 Convolution연산을 통하여 Feature를 추출
  • 추출된 Feature에 Linear transformation을 통하여 기존의 Feature들과 유사한 Ghost Feature를 생성
  • Feature와 Ghost Feature를 Concat을 통하여 하나로 합침

실제 구현에서는 Linear Transfer 연산으로 Mobilenet에서 사용되었던 Depthwise Convolution 연산을 사용하였습니다.
input이 80이고 output이 100인 연산을 convolution으로 만 진행할 경우 필요한 Parameters는 아래와 같지만
$$ 80 \times 100 \times kernel_{conv}$$
이를 Ghost Block으로 진행 할 경우 다음과 같이 Parameter가 줄게 됩니다.

  • 기존 Feature와 Ghost Feature의 1:1 인 경우
    $$
    80 \times 50 \times kernel_{conv}+ 50 \times kernel_{dwconv}
    $$
  • torch의 Conv2d Layer는 group을 이용하여 depthwise Convolution 연산을 진행 할 수 있습니다.
  • group이 output사이즈와 같을경우 depthwise 연산을 진행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class GhostModule(nn.Module):
def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True):
super(GhostModule, self).__init__()
self.oup = oup
init_channels = math.ceil(oup / ratio)
new_channels = init_channels*(ratio-1)

self.primary_conv = nn.Sequential(
nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False),
nn.BatchNorm2d(init_channels),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)

self.cheap_operation = nn.Sequential(
nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False),
nn.BatchNorm2d(new_channels),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)

def forward(self, x):
x1 = self.primary_conv(x)
x2 = self.cheap_operation(x1) # depthwise convolution 연산
out = torch.cat([x1,x2], dim=1) # identity 결합
return out[:,:self.oup,:,:]

코드 출처 : huawei-noah/Efficient-AI-Backbones

Ghostnet은 CNN 중복된 Feature 특징 통하여 딥러닝 모델에서의 Feature가 서로 상관관계가 있고, 이를 기존 Feature를 통해 생성하면서 좋은 결과를 보여주었습니다.

데모

Colab 데모 페이지

GhostNet은 pytorch hub를 통하여 손쉽게 Classification 모델로 사용할 수 있습니다.

  1. 모델 Load
1
2
3
import torch
model = torch.hub.load('huawei-noah/ghostnet', 'ghostnet_1x', pretrained=True)
model.eval()
  1. Classification
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from PIL import Image
from torchvision import transforms
input_image = Image.open(filename)
preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
input_tensor = preprocess(input_image)
input_batch = input_tensor.unsqueeze(0) # create a mini-batch as expected by the model

# move the input and model to GPU for speed if available
if torch.cuda.is_available():
input_batch = input_batch.to('cuda')
model.to('cuda')

with torch.no_grad():
output = model(input_batch)
# Tensor of shape 1000, with confidence scores over Imagenet's 1000 classes
print(output[0])
# The output has unnormalized scores. To get probabilities, you can run a softmax on it.
probabilities = torch.nn.functional.softmax(output[0], dim=0)
print(probabilities)

부스트 캠프 ai tech 4주 3일차 DL Basic (5)


Fully Convolutional Network

  • CNN에서 마지막을 담당하던 Dense Layer를 Dense Layer Feature와 동일한 Channel수를 가진 Convolution Layer로 대체한 Network
  • Dense Layer는 reshape을 통해서 input을 집어넣기 때문에 정해진 input size가 필요 했지만, convolution Layer의 경우 channel 이외에는 가변적이기 때문에 이미지의 크기와 상관없이 연산이 가능해졌다
  • 연산을 할 때 마다 차원이 축소되는 문제가 있다 -> upsampling기법을 사용
    • conv transpose -> checker board
    • unpooling

Object Detection

R-CNN 계열

  • R-CNN
    • Selective Search 로 BBox 2000개정도를 추출 한 뒤 각각 CNN을 돌린다
    • CNN 연산이 2000번 반복되기 때문에 매우 느린 속도이다
  • fast R-CNN
    • SPP Net을 이용하여 기존 2000번 반복된 연산을 1번으로 줄임
  • Faster R-CNN
    • Selective Search를 Region Proposal Network로 바꾼 R-CNN
    • Region Proposal Network : BBox도 네트워크 학습으로 뽑아내자
    • 기존에 존재하는 anchor Box(샘플한 여러 크기의 Bbox)와 이미지를 비교하여 물체가 있을법한 장소를 탐색하고, 대략적인 Bbox위치를 특정한다

Yolo

  • BBox와 Classfication이 동시에 이루어지는 1 stage 모델
  • R-CNN 계열에 비해서 속도가 매우 빠르지만, 정확도는 조금 떨어진다
  • 실시간 물체검출이나 추적에 용이한 모델이다

reference

부스트 캠프 ai tech 4주 2일차 DL Basic (3)


Convolution Neural Network (CNN)

  • 시각적 데이터르 분석하는데 사용되는 인공신경망
  • CNN은 크게 Convolution Layer, Pooling Layer, (Fully Cunnect Layer)로 구성된다

Convolution layer

  • 합성곱 연산이 진행되는 레이어
  • parameter의 수는 $kernel_size \times Channel_in \times Channel_out$으로 계산된다
  • 이미지 처리를 할 시에 Fully Connected Layer보다 parameter 수가 월등하게 적다
  • 실제 연산 이미지
  • input과 output의 크기는 아래와 같이 계산된다
  • input : $(N, C_{in}, H_{in}, W_{in})$
  • output : $(N, C_{out}, H_{out}, W_{out})$
  • stride : 연산이 이루어지는 간격
  • padding : 차원을 확장시켜 convolution 연산후에도 크기가 일정하게 유지되도록 한다
  • kernel : convolution 연산을 하는 주체, filter이기도 하다
  • N : batch size
  • C : Channel 수
  • W : 연산될 Matrix의 가로 길이
  • H : 연산될 Matrix의 세로 길이

Pooling Layer

  • 일정 범위안의 값들을 대표하는 하나의 값으로 압축하는 down sampling 기법
  • W와 H을 줄여서 차원을 축소하고, 연산에 필요한 parameter 수를 줄인다.
Max Pooling

1x1 Convolution

  • kernel size가 1,1인 convolution 연산을 하는것
  • input, output의 W, H는 일정하지만 channel수가 감소한다.
  • WHY?
    • channel 수를 줄여서 차원축소를 시키고, 추후에 연산에 필요한 parameter 수를 줄인다

$$
H_{out} = \left[ \frac{H_{in} + 2 \times \operatorname{padding[0]} - \operatorname{kernel_size[0]}}{\operatorname{stride}[0]} +1 \right]
$$

$$
W_{out} = \left[ \frac{W_{in} + 2 \times \operatorname{padding[1]} - \operatorname{kernel_size[1]}}{\operatorname{stride}[1]} +1 \right]
$$

reference