Skip to content

Commit c5bfcc7

Browse files
committed
A few more tweaks to cpubone model. Cleaned up upsampling creation, use GroupNorm1, wire proj_drop_rate to the MLP dropout.
1 parent 98419df commit c5bfcc7

1 file changed

Lines changed: 71 additions & 35 deletions

File tree

timm/models/cpubone.py

Lines changed: 71 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import torch.nn.functional as F
1414

1515
from timm.data import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD
16-
from timm.layers import DropPath, LayerType, calculate_drop_path_rates, get_act_layer, get_norm_layer, use_fused_attn
16+
from timm.layers import DropPath, GroupNorm1, LayerType, calculate_drop_path_rates, get_act_layer, get_norm_layer, \
17+
use_fused_attn
1718
from ._builder import build_model_with_cfg
1819
from ._features import feature_take_indices
1920
from ._features_fx import register_notrace_module
@@ -31,6 +32,18 @@
3132
}
3233

3334

35+
def _check_local_mbconv_norm(local_mbconv_norm: str) -> None:
36+
if local_mbconv_norm not in _LOCAL_MBCONV_NORM_MODES:
37+
raise ValueError(
38+
f'Invalid local_mbconv_norm={local_mbconv_norm!r}; '
39+
f'expected one of {tuple(_LOCAL_MBCONV_NORM_MODES)}.'
40+
)
41+
42+
43+
def _check_global_pool(global_pool: str) -> None:
44+
assert global_pool in ("", "avg"), "CPUBone only supports average or disabled pooling"
45+
46+
3447
def remap_legacy_state_dict(state_dict: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
3548
"""Remap keys from original CPUBone checkpoints to the current model layout."""
3649
remapped = {}
@@ -307,26 +320,33 @@ def __init__(
307320
self.pwise = nn.Conv2d(input_dim, total_dim, kernel_size=1, stride=1, padding=0, bias=False)
308321

309322
self.o_proj_inpdim = self.head_dim * self.num_heads
310-
self.o_proj = nn.Conv2d(self.o_proj_inpdim, input_dim, kernel_size=1, stride=1, padding=0)
311-
312-
self.upsampling = nn.ConvTranspose2d(
313-
input_dim, input_dim, kernel_size=att_stride * 2, stride=att_stride, padding=att_stride // 2, groups=input_dim)
314-
if att_stride == 1:
315-
self.upsampling = nn.ConvTranspose2d(input_dim, input_dim, kernel_size=3, stride=1, padding=1, groups=input_dim)
316-
323+
# With fuse_out_proj the output projection is folded into the upsampling module below, which
324+
# then maps o_proj_inpdim -> input_dim instead of being a depthwise / parameter-free upsample.
317325
if fuse_out_proj:
318326
self.o_proj = nn.Identity()
319-
if att_stride == 1:
320-
self.upsampling = nn.ConvTranspose2d(self.o_proj_inpdim, input_dim, kernel_size=3, stride=1, padding=1)
321-
else:
322-
self.upsampling = nn.ConvTranspose2d(
323-
self.o_proj_inpdim, input_dim, kernel_size=att_stride * 2, stride=att_stride, padding=att_stride // 2)
327+
else:
328+
self.o_proj = nn.Conv2d(self.o_proj_inpdim, input_dim, kernel_size=1, stride=1, padding=0)
324329

325330
if upsample_mode == 'nearest':
326331
upsampling = [nn.Upsample(scale_factor=att_stride, mode="nearest") if att_stride > 1 else nn.Identity()]
327332
if fuse_out_proj:
328333
upsampling = [nn.Conv2d(self.o_proj_inpdim, input_dim, kernel_size=1, stride=1, padding=0)] + upsampling
329334
self.upsampling = nn.Sequential(*upsampling)
335+
elif fuse_out_proj:
336+
if att_stride == 1:
337+
self.upsampling = nn.ConvTranspose2d(self.o_proj_inpdim, input_dim, kernel_size=3, stride=1, padding=1)
338+
else:
339+
self.upsampling = nn.ConvTranspose2d(
340+
self.o_proj_inpdim, input_dim,
341+
kernel_size=att_stride * 2, stride=att_stride, padding=att_stride // 2)
342+
else:
343+
if att_stride == 1:
344+
self.upsampling = nn.ConvTranspose2d(
345+
input_dim, input_dim, kernel_size=3, stride=1, padding=1, groups=input_dim)
346+
else:
347+
self.upsampling = nn.ConvTranspose2d(
348+
input_dim, input_dim,
349+
kernel_size=att_stride * 2, stride=att_stride, padding=att_stride // 2, groups=input_dim)
330350

331351
def forward(self, x: torch.Tensor) -> torch.Tensor:
332352
H, W = x.shape[-2:]
@@ -376,15 +396,12 @@ def __init__(
376396
mlp_ratio: int = 4,
377397
small_kernels: bool = False,
378398
attn_upsample: str = 'transpose',
399+
proj_drop: float = 0.1,
379400
drop_path: float = 0.,
380401
local_mbconv_norm: str = 'proj',
381402
):
382403
super().__init__()
383-
if local_mbconv_norm not in _LOCAL_MBCONV_NORM_MODES:
384-
raise ValueError(
385-
f'Invalid local_mbconv_norm={local_mbconv_norm!r}; '
386-
f'expected one of {tuple(_LOCAL_MBCONV_NORM_MODES)}.'
387-
)
404+
_check_local_mbconv_norm(local_mbconv_norm)
388405
att_kernel = 5 if att_stride > 1 else 3
389406

390407
block = ConvAttention(
@@ -397,13 +414,13 @@ def __init__(
397414
upsample_mode=attn_upsample,
398415
)
399416

400-
context_module = ResidualBlock(nn.Sequential(nn.GroupNorm(1, in_channels), block), nn.Identity(), drop_path)
417+
context_module = ResidualBlock(nn.Sequential(GroupNorm1(in_channels), block), nn.Identity(), drop_path)
401418
mlp = nn.Sequential(
402-
nn.GroupNorm(1, in_channels),
419+
GroupNorm1(in_channels),
403420
nn.Conv2d(in_channels, in_channels * mlp_ratio, kernel_size=1),
404421
nn.GELU(),
405422
nn.Conv2d(in_channels * mlp_ratio, in_channels, kernel_size=1),
406-
nn.Dropout(p=0.1),
423+
nn.Dropout(p=proj_drop),
407424
)
408425
context_module = nn.Sequential(context_module, ResidualBlock(mlp, nn.Identity(), drop_path))
409426

@@ -452,7 +469,7 @@ def __init__(
452469
act_layer: Type[nn.Module] = nn.Hardswish,
453470
):
454471
super().__init__()
455-
assert global_pool in ("", "avg"), "CPUBone only supports average or disabled pooling"
472+
_check_global_pool(global_pool)
456473
self.num_features = width_list[-1]
457474
self.dropout = dropout
458475
self.pool_type = global_pool
@@ -468,7 +485,7 @@ def __init__(
468485

469486
def reset(self, num_classes: int, global_pool: Optional[str] = None):
470487
if global_pool is not None:
471-
assert global_pool in ("", "avg"), "CPUBone only supports average or disabled pooling"
488+
_check_global_pool(global_pool)
472489
self.pool_type = global_pool
473490
self.global_pool = nn.AdaptiveAvgPool2d(output_size=1) if global_pool else nn.Identity()
474491
self.flatten = nn.Flatten(1) if global_pool else nn.Identity()
@@ -502,6 +519,7 @@ def __init__(
502519
global_pool: str = "avg",
503520
head_widths: Tuple[int, int] = (1536, 1600),
504521
drop_rate: float = 0.0,
522+
proj_drop_rate: float = 0.1,
505523
drop_path_rate: float = 0.0,
506524
expand_ratio: float = 4,
507525
norm_layer: LayerType = nn.BatchNorm2d,
@@ -525,6 +543,8 @@ def __init__(
525543
global_pool: Global pooling type, either 'avg' or '' to disable pooling.
526544
head_widths: Hidden widths of the classification head (in_conv, pre_classifier).
527545
drop_rate: Classifier dropout rate.
546+
proj_drop_rate: Dropout rate at the end of the attention-stage (CPUBoneBlock) MLPs, the
547+
0.1 default matches the original implementation's fixed dropout.
528548
drop_path_rate: Stochastic depth rate.
529549
expand_ratio: Default expand ratio of MBConv / FusedMBConv blocks.
530550
norm_layer: Normalization layer.
@@ -553,13 +573,9 @@ def __init__(
553573
`smallk_only_lasts` → `small_kernels`; `lose_transpose=True` → `attn_upsample='nearest'`.
554574
"""
555575
super().__init__()
556-
assert global_pool in ("", "avg"), "CPUBone only supports average or disabled pooling"
576+
_check_global_pool(global_pool)
557577
assert attn_upsample in ('transpose', 'nearest')
558-
if local_mbconv_norm not in _LOCAL_MBCONV_NORM_MODES:
559-
raise ValueError(
560-
f'Invalid local_mbconv_norm={local_mbconv_norm!r}; '
561-
f'expected one of {tuple(_LOCAL_MBCONV_NORM_MODES)}.'
562-
)
578+
_check_local_mbconv_norm(local_mbconv_norm)
563579
num_stages = len(width_list) - 1
564580
if downsample_expand_ratios is None:
565581
downsample_expand_ratios = (expand_ratio,) * num_stages
@@ -576,6 +592,7 @@ def __init__(
576592
self.fused_conv = fused_conv
577593
self.fused_downsample = fused_downsample
578594
self.attn_mlp_ratio = attn_mlp_ratio
595+
self.proj_drop_rate = proj_drop_rate
579596
self.stem_expand_ratio = stem_expand_ratio
580597
self.downsample_expand_ratios = tuple(downsample_expand_ratios)
581598
self.expand_groups = expand_groups
@@ -708,6 +725,7 @@ def _build_attention_stage(
708725
mlp_ratio=self.attn_mlp_ratio,
709726
small_kernels=self.small_kernels,
710727
attn_upsample=self.attn_upsample,
728+
proj_drop=self.proj_drop_rate,
711729
drop_path=dpr[i],
712730
local_mbconv_norm=self.local_mbconv_norm,
713731
)
@@ -769,10 +787,9 @@ def get_classifier(self) -> nn.Module:
769787

770788
def reset_classifier(self, num_classes: int, global_pool: Optional[str] = None):
771789
if global_pool is not None:
772-
assert global_pool in ("", "avg"), "CPUBone only supports average or disabled pooling"
773-
self.num_classes = num_classes
774-
if global_pool is not None:
790+
_check_global_pool(global_pool)
775791
self.global_pool = global_pool
792+
self.num_classes = num_classes
776793
self.head.reset(num_classes, global_pool)
777794

778795
def forward_intermediates(
@@ -874,6 +891,8 @@ def _cfg(url: str = "", **kwargs: Any) -> Dict[str, Any]:
874891
"cpubone_b1_dwnorm.untrained": _cfg(),
875892
"cpubone_b1_allnorm.untrained": _cfg(),
876893
"cpubone_b2.in1k": _cfg(hf_hub_id="Kaeruu/CPUBone", hf_hub_filename="cpubone_b2.safetensors"),
894+
"cpubone_b2_dwnorm.untrained": _cfg(),
895+
"cpubone_b2_allnorm.untrained": _cfg(),
877896
"cpubone_b3.in1k": _cfg(hf_hub_id="Kaeruu/CPUBone", hf_hub_filename="cpubone_b3.safetensors"),
878897
})
879898

@@ -958,9 +977,8 @@ def cpubone_b1_allnorm(pretrained: bool = False, **kwargs: Any) -> CPUBone:
958977
return _create_cpubone("cpubone_b1_allnorm", pretrained=pretrained, **dict(model_args, **kwargs))
959978

960979

961-
@register_model
962-
def cpubone_b2(pretrained: bool = False, **kwargs: Any) -> CPUBone:
963-
model_args = dict(
980+
def _cpubone_b2_args(local_mbconv_norm: str = 'proj') -> Dict[str, Any]:
981+
return dict(
964982
width_list=[24, 48, 96, 192, 384],
965983
depth_list=[0, 1, 1, 6, 6],
966984
head_widths=(2304, 2560),
@@ -971,10 +989,28 @@ def cpubone_b2(pretrained: bool = False, **kwargs: Any) -> CPUBone:
971989
expand_groups=2,
972990
small_kernels=True,
973991
attn_upsample="nearest",
992+
local_mbconv_norm=local_mbconv_norm,
974993
)
994+
995+
996+
@register_model
997+
def cpubone_b2(pretrained: bool = False, **kwargs: Any) -> CPUBone:
998+
model_args = _cpubone_b2_args()
975999
return _create_cpubone("cpubone_b2", pretrained=pretrained, **dict(model_args, **kwargs))
9761000

9771001

1002+
@register_model
1003+
def cpubone_b2_dwnorm(pretrained: bool = False, **kwargs: Any) -> CPUBone:
1004+
model_args = _cpubone_b2_args(local_mbconv_norm='depth_proj')
1005+
return _create_cpubone("cpubone_b2_dwnorm", pretrained=pretrained, **dict(model_args, **kwargs))
1006+
1007+
1008+
@register_model
1009+
def cpubone_b2_allnorm(pretrained: bool = False, **kwargs: Any) -> CPUBone:
1010+
model_args = _cpubone_b2_args(local_mbconv_norm='all')
1011+
return _create_cpubone("cpubone_b2_allnorm", pretrained=pretrained, **dict(model_args, **kwargs))
1012+
1013+
9781014
@register_model
9791015
def cpubone_b3(pretrained: bool = False, **kwargs: Any) -> CPUBone:
9801016
model_args = dict(

0 commit comments

Comments
 (0)