accuracy 를 유지하면서 효율적인 네트워크를 design 하기 위해 최근 연구에서는
conv 와 transformer design 을 결합한 hybrid architecture 를 도입하여 local, global 정보들을 효과적으로 capture 한다.
hybrid transformer = combine convolutional and transformer design
hybrid architecture 들의 대부분에서 token mixer 는 self-attention 기반이 우세하다. 최근 MetaFormer 에서는 token mixing 의 후보로 간단하고 효율적인 Pooling 방식을 소개하였다.
최근 연구에서 작은 메모리 access 비용으로 reparameterizating skip connections 의 장점을 보여준다.
Reparameterization trick? 아니… RepVGG 에서 아이디어 가져왔다.
inference 시 RepMix component 에서 reparameterizable 한 새로운 architecture 를 채택한다.
좀 더 효율적인 works 를 위해 depthwise 또는 pointwise conv 에 의해 그룹화 된 conv 를 사용하여 factorized 된 conv 를 채택한다.
우리가 아는 한 (To the best of our knowledge)
skip connections 를 제거하기 위한 구조적인 reparameterization 과 선형 overparameterization 은 어떤 사전적인 hybrid transformer architecture 에서도 시도되지 않았다.
FastViT is a hybrid transformer and has four distinct stages which operate at different scales as shown in Figure 2.
FastViT uses RepMixer, a token mixer that reparameterizes a skip connection, which helps in alleviating (완화하다) memory access cost (see Figure 2d)
: non-linear activation function
: Batch Normalization layer
: depthwise convolution layer.
연산이 복잡하므로, 간단하게 non-linear activation function 을 지우고, input 값에 대한 bias 를 구하고 를 구하는 것이..
it can be reparameterized at inference time to a simgle depthwise convolutional layer
def reparameterize_model(model: torch.nn.Module) -> nn.Module:
...
# Avoid editing original graph
model = copy.deepcopy(model)
for module in model.modules():
if hasattr(module, "reparameterize"):
module.reparameterize()
return model
Green : conv + BN layer → conv + Bias
Orange : conv + BN layer → conv + Bias
Identity + BN : conv + BN layer → conv + Bias
def reparameterize(self):
...
if self.inference_mode:
return
kernel, bias = self._get_kernel_bias()
self.reparam_conv = nn.Conv2d(
in_channels=self.in_channels,
out_channels=self.out_channels,
kernel_size=self.kernel_size,
stride=self.stride,
padding=self.padding,
dilation=self.dilation,
groups=self.groups,
bias=True,
)
self.reparam_conv.weight.data = kernel
self.reparam_conv.bias.data = bias
# Delete un-used branches
for para in self.parameters():
para.detach_()
self.__delattr__("rbr_conv")
self.__delattr__("rbr_scale")
if hasattr(self, "rbr_skip"):
self.__delattr__("rbr_skip")
self.inference_mode = True
def validate(args):
...
# create model
model = create_model(
args.model,
pretrained=args.pretrained,
num_classes=args.num_classes,
in_chans=3,
global_pool=args.gp,
scriptable=args.torchscript,
inference_mode=args.use_inference_mode,
)
...
# Reparameterize model
model.eval()
if not args.use_inference_mode:
_logger.info("Reparameterizing Model %s" % (args.model))
model = reparameterize_model(model)
conditional positional encodings
pos_embs = [None, None, None, partial(RepCPE, spatial_shape=(7, 7))]
depth-wise conv operator 과 patch embedding 을 더한 것의 결과로써 이 encoding 들이 만들어 졌다.
class RepCPE(nn.Module):
...
def forward(self, x: torch.Tensor) -> torch.Tensor:
if hasattr(self, "reparam_conv"):
x = self.reparam_conv(x)
return x
else:
x = self.pe(x) + x
return x
if inference_mode:
self.reparam_conv = nn.Conv2d(
in_channels=self.in_channels,
out_channels=self.embed_dim,
kernel_size=self.spatial_shape,
stride=1,
padding=int(self.spatial_shape[0] // 2),
groups=self.embed_dim,
bias=True,
)
else:
self.pe = nn.Conv2d(
in_channels,
embed_dim,
spatial_shape,
1,
int(spatial_shape[0] // 2),
bias=True,
groups=embed_dim,
)
reparameterizing skip connections ( when inference ) 의 이점 을 좀 더 효과적 ( in terms of latency : 응답 대기 시간 ) token mixer ( MetaFormer S12 architecture ) 의 Pooling 과 RepMixer 사용시 비교.
@register_model
def fastvit_t8(pretrained=False, **kwargs):
"""Instantiate FastViT-T8 model variant."""
layers = [2, 2, 4, 2]
embed_dims = [48, 96, 192, 384]
mlp_ratios = [3, 3, 3, 3]
downsamples = [True, True, True, True]
token_mixers = ("repmixer", "repmixer", "repmixer", "repmixer")
@register_model
def fastvit_t12(pretrained=False, **kwargs):
"""Instantiate FastViT-T12 model variant."""
layers = [2, 2, 6, 2]
embed_dims = [64, 128, 256, 512]
mlp_ratios = [3, 3, 3, 3]
downsamples = [True, True, True, True]
token_mixers = ("repmixer", "repmixer", "repmixer", "repmixer")
@register_model
def fastvit_s12(pretrained=False, **kwargs):
"""Instantiate FastViT-S12 model variant."""
layers = [2, 2, 6, 2]
embed_dims = [64, 128, 256, 512]
mlp_ratios = [4, 4, 4, 4]
downsamples = [True, True, True, True]
token_mixers = ("repmixer", "repmixer", "repmixer", "repmixer")
@register_model
def fastvit_sa12(pretrained=False, **kwargs):
"""Instantiate FastViT-SA12 model variant."""
layers = [2, 2, 6, 2]
embed_dims = [64, 128, 256, 512]
mlp_ratios = [4, 4, 4, 4]
downsamples = [True, True, True, True]
pos_embs = [None, None, None, partial(RepCPE, spatial_shape=(7, 7))]
token_mixers = ("repmixer", "repmixer", "repmixer", "attention")
@register_model
def fastvit_sa24(pretrained=False, **kwargs):
"""Instantiate FastViT-SA24 model variant."""
layers = [4, 4, 12, 4]
embed_dims = [64, 128, 256, 512]
mlp_ratios = [4, 4, 4, 4]
downsamples = [True, True, True, True]
pos_embs = [None, None, None, partial(RepCPE, spatial_shape=(7, 7))]
token_mixers = ("repmixer", "repmixer", "repmixer", "attention")
@register_model
def fastvit_sa36(pretrained=False, **kwargs):
"""Instantiate FastViT-SA36 model variant."""
layers = [6, 6, 18, 6]
embed_dims = [64, 128, 256, 512]
mlp_ratios = [4, 4, 4, 4]
downsamples = [True, True, True, True]
pos_embs = [None, None, None, partial(RepCPE, spatial_shape=(7, 7))]
token_mixers = ("repmixer", "repmixer", "repmixer", "attention")
@register_model
def fastvit_ma36(pretrained=False, **kwargs):
"""Instantiate FastViT-MA36 model variant."""
layers = [6, 6, 18, 6]
embed_dims = [76, 152, 304, 608]
mlp_ratios = [4, 4, 4, 4]
downsamples = [True, True, True, True]
pos_embs = [None, None, None, partial(RepCPE, spatial_shape=(7, 7))]
token_mixers = ("repmixer", "repmixer", "repmixer", "attention")
resolutions starting from to
At , using RepMixer will lower the latency by
At larger resolutions , latency is lowered significantly by
@register_model
def fastvit_sa12(pretrained=False, **kwargs):
"""Instantiate FastViT-SA12 model variant."""
layers = [2, 2, 6, 2]
embed_dims = [64, 128, 256, 512]
mlp_ratios = [4, 4, 4, 4]
downsamples = [True, True, True, True]
pos_embs = [None, None, None, partial(RepCPE, spatial_shape=(7, 7))]
token_mixers = ("repmixer", "repmixer", "repmixer", "attention")
self.token_mixer = RepMixer(
dim,
kernel_size=kernel_size,
use_layer_scale=use_layer_scale,
layer_scale_init_value=layer_scale_init_value,
inference_mode=inference_mode,
)
assert mlp_ratio > 0, "MLP ratio should be greater than 0, found: {}".format(
mlp_ratio
)
mlp_hidden_dim = int(dim * mlp_ratio)
self.convffn = ConvFFN(
in_channels=dim,
hidden_channels=mlp_hidden_dim,
act_layer=act_layer,
drop=drop,
)
pos_embs = [None, None, None, partial(RepCPE, spatial_shape=(7, 7))]
self.norm = norm_layer(dim)
self.token_mixer = MHSA(dim=dim)
assert mlp_ratio > 0, "MLP ratio should be greater than 0, found: {}".format(
mlp_ratio
)
mlp_hidden_dim = int(dim * mlp_ratio)
self.convffn = ConvFFN(
in_channels=dim,
hidden_channels=mlp_hidden_dim,
act_layer=act_layer,
drop=drop,
)
# Drop path
self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
# Layer Scale
self.use_layer_scale = use_layer_scale
if use_layer_scale:
self.layer_scale_1 = nn.Parameter(
layer_scale_init_value * torch.ones((dim, 1, 1)), requires_grad=True
)
self.layer_scale_2 = nn.Parameter(
layer_scale_init_value * torch.ones((dim, 1, 1)), requires_grad=True
)
@register_model
def fastvit_sa36(pretrained=False, **kwargs):
"""Instantiate FastViT-SA36 model variant."""
layers = [6, 6, 18, 6]
embed_dims = [64, 128, 256, 512]
mlp_ratios = [4, 4, 4, 4]
downsamples = [True, True, True, True]
pos_embs = [None, None, None, partial(RepCPE, spatial_shape=(7, 7))]
token_mixers = ("repmixer", "repmixer", "repmixer", "attention")
RepMixer 의 receptive field 는 self-attention token mixer 와 비교할 때 local 이다.
token_mixers = ("repmixer", "repmixer", "repmixer", "attention")
self-attention 을 기본으로하는 token mixers 은 계산 비용이 굉장히 비싸다.
self-attention 을 사용하지 않은 early stage 의 receptive field 를 향상시키기 위해 depthwise large kernel conv 와 결합하는 것이 효율적인 접근법이다.
class RepMixerBlock(nn.Module):
...
self.token_mixer = RepMixer(
dim,
kernel_size=kernel_size,
use_layer_scale=use_layer_scale,
layer_scale_init_value=layer_scale_init_value,
inference_mode=inference_mode,
)
assert mlp_ratio > 0, "MLP ratio should be greater than 0, found: {}".format(
mlp_ratio
)
mlp_hidden_dim = int(dim * mlp_ratio)
self.convffn = ConvFFN(
in_channels=dim,
hidden_channels=mlp_hidden_dim,
act_layer=act_layer,
drop=drop,
)
class RepMixer(nn.Module):
def __init__(...):
....
if inference_mode:
self.reparam_conv = nn.Conv2d(
...
)
else:
# BatchNorm2d 이거 왜 빼는지?
self.norm = MobileOneBlock(
...
use_scale_branch=False,
num_conv_branches=0,
)
# DWConv(BN(X)) + X
self.mixer = MobileOneBlock(
dim,
dim,
kernel_size,
padding=kernel_size // 2,
groups=dim,
use_act=False,
)
self.use_layer_scale = use_layer_scale
if use_layer_scale:
self.layer_scale = nn.Parameter(
layer_scale_init_value * torch.ones((dim, 1, 1)), requires_grad=True
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
if hasattr(self, "reparam_conv"):
x = self.reparam_conv(x)
return x
else:
if self.use_layer_scale:
x = x + self.layer_scale * (self.mixer(x) - self.norm(x))
else:
x = x + self.mixer(x) - self.norm(x)
return x
FFN 에 DW large kernel conv 를 채택한다.
class ConvFFN(nn.Module):
...
out_channels = out_channels or in_channels
hidden_channels = hidden_channels or in_channels
self.conv = nn.Sequential()
self.conv.add_module(
"conv",
nn.Conv2d(
in_channels=in_channels,
out_channels=out_channels,
kernel_size=7,
padding=3,
groups=in_channels,
bias=False,
),
)
self.conv.add_module("bn", nn.BatchNorm2d(num_features=out_channels))
self.fc1 = nn.Conv2d(in_channels, hidden_channels, kernel_size=1)
self.act = act_layer()
self.fc2 = nn.Conv2d(hidden_channels, out_channels, kernel_size=1)
self.drop = nn.Dropout(drop)
self.apply(self._init_weights)
and patch embedding layers.
# Patch merging/downsampling between stages.
if downsamples[i] or embed_dims[i] != embed_dims[i + 1]:
network.append(
PatchEmbed(
patch_size=down_patch_size,
stride=down_stride,
in_channels=embed_dims[i],
embed_dim=embed_dims[i + 1],
inference_mode=inference_mode,
)
)
FastVit : Full Model
Training data
Hyper parameter setting.
Fine-tune models for 30 epochs
latency 를 측정하기 위해, respective (각각의) 방법에 대응하는 input size 를 사용 한다.
iPhone latency 측정을 위해서 Core ML Tools (v6.0) 를 사용하는 모델을 export 하고, iPhone 12 Pro Max 에 있는 iOS 16 에서 run 하고 batch size 는 모든 모델에 대해서 1 로 set 한다.
ImageNet-1k dataset 으로 최근의 SOTA model 과 비교 해보자. 공정한 비교를 위해서 비용이 많이 드는 reshape operation 들을 피하여 official 한 implementation 로부터 ConvNeXt 를 수정한다.
두 가지 다른 compute fabrics ( 데스크탑 gpu 와 mobile device ) 에서 최근 SOTA 를 비교할 때 Fast-ViT 이 best accuracy-latency trade-off 이 가장 좋다는 이야기…
RegNet 16GF 를 가지고 DeiT 에서 설명된 셋팅으로 follow 한다. Fast-ViT 을 300 epoch 학습하며 true label 로써 teacher 의 hard decision 으로 나온 hard distillation 을 사용한다.
distillation 을 위해 추가된 classification head 는 알아서 하시길..
Model | Image size | Param | FLOPs | GPU Latency | Mobile Latency | Top-1 Acc |
---|---|---|---|---|---|---|
FastViT-SA24 | 256 | 20.6 | 3.8 | 3.8 | 2.6 | 83.4 |
EfficientFormer-L7 | 224 | 82.1 | 10.2 | 7.5 | 7.0 | 83.3 |
parameter less, FLOPs less, lower latency
a dataset that contains naturally occurring examples that are misclassified by ResNets
https://paperswithcode.com/dataset/imagenet-a
a dataset that contains natural renditions of ImageNet object classes with different textures and local image statistics
https://paperswithcode.com/dataset/imagenet-r
a dataset that contains black and white sketches of all ImageNet classes, obtained using google image queries.
https://paperswithcode.com/dataset/imagenet-sketch
a dataset that consists of algorithmically generated corruptions (blur, noise) applied to the ImageNet test-set. corruption error is reported (lower is better) and for other datasets Top-1 accuracy is reported (higher is better).
https://paperswithcode.com/dataset/imagenet-c
Section 3.2.3 에서 논의된 robustness 향상에 도움이 되는 self-attention layers 가 들어간 조합에서 patch embedding layer 들과 FFN 에서 large kernel convolution 들을 사용하는 Architecture 를 선택한다.
최근 실시간 3D hand mesh 추정에 CNN based 백본 으로 소개된다. 백본으로는 feature extraction 을 위하여 HRNets 을 사용하는 METRO 와 MeshGraphormer 를 제외한 ResNET 또는 MoibleNet 군을 사용한다.
https://lmb.informatik.uni-freiburg.de/projects/freihand/
공정하게 비교하기 위해서 훈련을 위한 데이터 셋는 FreiHand 데이터 셋만 사용하고, 그 결과를 pre-train, train 또는 더 추가된 pose datasets 의 fine-tune (미세 조정) 에 인용(cite) 한다.
ImageNet-1k 로 pre-training 을 하고, “End-to-end human pose and mesh reconstruction with transformers” 에서 설명된 실험을 설정하여 FreiHand 데이터 셋으로만 train 한다.
For semantic segmentation ( 영역 분할 )
Semantic segmenation
→ 이미지 내에서 객체가 속한 Class가 무엇인지에 대해서만 판단. ( 의미론적 분할 )
Instance segmentation ( 개별적 분할 )
→ Class 안의 Instance 들 간의 구분 가능.
Panoptic Segmentation
→ Sementic & Panoptic Segmentation 조합.
20K training images and 2K validation images with 150 semantic categories.
https://groups.csail.mit.edu/vision/datasets/ADE20K/
For object detection ( 개체 검출 )
with 80 classes containing 118K training and 5K validation images