1313import torch .nn .functional as F
1414
1515from 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
1718from ._builder import build_model_with_cfg
1819from ._features import feature_take_indices
1920from ._features_fx import register_notrace_module
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+
3447def 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
9791015def cpubone_b3 (pretrained : bool = False , ** kwargs : Any ) -> CPUBone :
9801016 model_args = dict (
0 commit comments