this project indeed appears to be a complex integrated multi-model system for vessel detection and fishing activity classification using SAR (Synthetic Aperture Radar) TIFF images as input. The system incorporates several advanced deep learning architectures and techniques, including:
1. CenterNet UNet: A combination of CenterNet (for object detection) and U-Net (for image segmentation).
YOLO (You Only Look Once): A real-time object detection system.
NAS (Neural Architecture Search): For optimizing model architecture.
FBN (Feature Pyramid Network): For multi-scale feature extraction.
FAN (Feature Aggregation Network): For combining features from different levels.
Swin Transformer: A hierarchical vision transformer.
Optuna: A hyperparameter optimization framework.
The system is designed to process SAR images and output predictions in a CSV format, including:
Vessel length in meters
Detection coordinates (row and column in the scene)
Scene ID
Vessel classification (True/False)
Fishing activity classification (True/False)
This type of system is typically used for maritime surveillance, monitoring fishing activities, and enforcing maritime regulations.
The configuration files and code snippets you've provided show various aspects of the model architecture, training process, and inference pipeline. Some key components include:
1. Model configuration (encoder, decoder, head)
Data augmentation strategies
Loss functions and optimization settings
Inference settings (tile size, batch size, thresholds)
Ensemble methods and Test Time Augmentation (TTA)
If you have any specific questions about the system or need clarification on any part of the codebase, please feel free to ask.
the outputs looks as 
vessel_length_m,detect_scene_row,detect_scene_column,scene_id,is_vessel,is_fishing
33.99261,11448,13456,1,True,True
28.166428,15632,12622,1,True,True
32.700775,19454,4840,1,True,True
37.80931,12862,2608,1,True,True
48.392456,4902,5522,1,True,False

understand this deeply and review the following two functions and just say "done" and remember everything as we will need all these details to fix errors 

##custom_encoder.py

class UpsampleBlock(nn.Module):
    def __init__(self, in_channels, out_channels, scale_factor=2, mode='bilinear', align_corners=False):
        super(UpsampleBlock, self).__init__()
        self.upsample = nn.Sequential(
            nn.Upsample(scale_factor=scale_factor, mode=mode, align_corners=align_corners),
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.GroupNorm(16, out_channels),
            nn.ReLU(inplace=True)
        )
        print(f"UpsampleBlock initialized with in_channels={in_channels}, out_channels={out_channels}")

    def forward(self, x):
        print(f"UpsampleBlock input shape: {x.shape}")
        output = self.upsample(x)
        print(f"UpsampleBlock output shape: {output.shape}")
        return output
    
class CustomTimmEncoder(nn.Module):
    def __init__(
        self,
        model_name: str = "efficientnetv2_rw_s",
        num_channels: int = 2,
        features_only: bool = True,
        pretrained: bool = False,
        activation: str = "silu",
        drop_rate: float = 0.0,
        drop_path_rate: float = 0.0,
        image_size: Tuple[int, int] = (224, 224),
    ):
        super(CustomTimmEncoder, self).__init__()
        self.model_name = model_name  # Store model name for conditional handling
        self.encoder = timm.create_model(
            model_name,
            features_only=features_only,
            in_chans=num_channels,
            pretrained=pretrained,
            act_layer=activation,
            drop_rate=drop_rate,
            drop_path_rate=drop_path_rate,
        )
        # Extract channels and strides
        # Extract actual feature channels and strides
       # In CustomTimmEncoder
        self.feature_info = self.encoder.feature_info
        self.channels = [f['num_chs'] for f in self.feature_info]
        #self.channels = [f['num_chs'] for f in self.encoder.feature_info]
        self.strides = [f['reduction'] for f in self.feature_info]
        self.feature_info = self.feature_info
        self.image_size = image_size 
        self.embed_dim = self.channels[-1]  
        self.depths = [2, 2, 6, 2]  
        self.num_heads = [3, 6, 12, 24]  
        self.window_size = 7  
        self.mlp_ratio = 4. 
        self.qkv_bias = True
        self.qk_scale = None
        self.drop_rate = drop_rate
        self.attn_drop_rate = 0.0
        self.drop_path_rate = drop_path_rate
        self.norm_layer = nn.LayerNorm
        self.use_dropout = True
        self.use_custom_pos_embed = False
        self.transformer_output_channels = self.channels[-1]
        self.output_stride = 32 

        # Conditional handling based on encoder type
        if "swin" in model_name.lower() or "transformer" in model_name.lower():
            # Assuming transformer-based encoder has 'patch_embed'
            if hasattr(self.encoder, 'patch_embed'):
                self.conv_stem = self.encoder.patch_embed.proj
            else:
                raise AttributeError(f"The encoder '{model_name}' does not have 'patch_embed' attribute.")
        else:
            # For CNN-based encoders like EfficientNet
            if hasattr(self.encoder, 'conv_stem'):
                self.conv_stem = self.encoder.conv_stem
            elif hasattr(self.encoder, 'conv1'):
                self.conv_stem = self.encoder.conv1  # Example for some other CNNs
            else:
                raise AttributeError(f"The encoder '{model_name}' does not have a recognized stem convolution layer.")
        

    def forward(self, x):
        encoder_features = self.encoder(x)
        if not isinstance(encoder_features, list):
            encoder_features = [encoder_features]
        logger.debug(f"Encoder features shapes: {[f.shape for f in encoder_features]}")

        for idx, encoder_feature in enumerate(encoder_features):
            if encoder_feature.dim() == 3:
                # Missing batch dimension, add it
                encoder_feature = encoder_feature.unsqueeze(0)
                encoder_features[idx] = encoder_feature
                print(f"[WARNING] Feature {idx} missing batch dimension, adding it.")
            elif encoder_feature.dim() < 3:
                raise ValueError(f"Feature {idx} has unexpected number of dimensions: {encoder_feature.dim()}")
            print(f"[DEBUG] Feature {idx} shape: {encoder_feature.shape}")  # Existing debug

        # Additional debug to ensure all features have batch dimension
        for idx, encoder_feature in enumerate(encoder_features):
            if encoder_feature.dim() != 4:
                print(f"[ERROR] Feature {idx} does not have 4 dimensions after processing: {encoder_feature.dim()}")
                raise ValueError(f"Feature {idx} has incorrect number of dimensions: {encoder_feature.dim()}")

        return encoder_features


    def change_input_channels(self, input_channels: int, mode="auto", **kwargs):
        if isinstance(self.conv_stem, Conv2dSame):
            self.conv_stem = make_n_channel_input_conv2d_same(self.conv_stem, input_channels, mode, **kwargs)
        else:
            self.conv_stem = make_n_channel_input(self.conv_stem, input_channels, mode, **kwargs)
        return self
    
class DebugUnetDecoder(nn.Module):
    def __init__(
        self,
        encoder_channels,
        decoder_channels,
        unet_block,
        upsample_block=UpsampleBlock,
        center=False,
        activation='relu'
    ):
        super(DebugUnetDecoder, self).__init__()
        self.center = center
        self.encoder_channels = encoder_channels
        self.decoder_channels = decoder_channels
        self.num_stages = len(decoder_channels)
        self.up_samples = nn.ModuleList()
        self.unet_blocks = nn.ModuleList()
        
        logger.debug(f"Initializing DebugUnetDecoder with encoder_channels={encoder_channels} and decoder_channels={decoder_channels}")
        
        for i in range(self.num_stages):
            if i == 0:
                in_channels = encoder_channels[-1]
            else:
                in_channels = decoder_channels[i-1]
            out_channels = decoder_channels[i]
            
            logger.debug(f"Stage {i}: in_channels={in_channels}, out_channels={out_channels}")
            
            # Check for non-zero channels
            assert in_channels > 0, f"in_channels must be positive, got {in_channels}"
            assert out_channels > 0, f"out_channels must be positive, got {out_channels}"
            
            self.up_samples.append(
                upsample_block(
                    in_channels=in_channels,
                    out_channels=out_channels
                )
            )
            
            if i < len(encoder_channels) - 1:
                unet_in_channels = out_channels + encoder_channels[-(i+2)]
            else:
                unet_in_channels = out_channels
            
            logger.debug(f"Stage {i}: unet_in_channels={unet_in_channels}")
            
            # Check for non-zero unet_in_channels
            assert unet_in_channels > 0, f"unet_in_channels must be positive, got {unet_in_channels}"
            
            self.unet_blocks.append(
                unet_block(
                    in_channels=unet_in_channels,
                    out_channels=out_channels,
                    activation=activation
                )
            )

    def forward(self, encoder_features):
        if not isinstance(encoder_features, list):
            raise TypeError(f"Decoder expected encoder_features to be a list, but got {type(encoder_features)}")
        
        x = encoder_features[-1]
        print(f"Initial x.shape: {x.shape}")
        
        decoded_features = []
        
        for i in range(self.num_stages):
            print(f"\nDecoder Stage {i}")
            print(f"x.shape before upsampling: {x.shape}")
            
            # Upsample
            x = self.up_samples[i](x)
            print(f"x.shape after upsampling: {x.shape}")
            
            # Concatenate with encoder feature
            if i < len(encoder_features) - 1:
                encoder_feat = encoder_features[-(i+2)]
                print(f"Encoder Feature {i} shape: {encoder_feat.shape}")
                
                if x.size()[2:] != encoder_feat.size()[2:]:
                    encoder_feat = F.interpolate(encoder_feat, size=x.size()[2:], mode='nearest')
                    print(f"Resized encoder_feat to {x.size()[2:]}")
                
                x = torch.cat([x, encoder_feat], dim=1)
                print(f"x.shape after concatenation: {x.shape}")
            
            # Pass through UNetBlock
            print(f"Passing through UNetBlock with in_channels={x.shape[1]}")
            x = self.unet_blocks[i](x)
            print(f"x.shape after UNetBlock: {x.shape}")
            
            decoded_features.append(x)
        
        return decoded_features

class ResidualDeconvolutionUpsample2d(nn.Module):
    def __init__(self, in_channels, out_channels, scale_factor=2, mode='bilinear', align_corners=False):
        super(ResidualDeconvolutionUpsample2d, self).__init__()
        self.upsample = nn.Sequential(
            nn.Upsample(scale_factor=scale_factor, mode=mode, align_corners=align_corners),
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.GroupNorm(16, out_channels),
            nn.ReLU(inplace=True)
        )
    
    def forward(self, x):
        return self.upsample(x)

--------------------------
necks.py
class FPNNeck(nn.Module):
    def __init__(
        self,
        channels: List[int],
        out_channels: int,
        use_cbam: bool = False,
        use_dropblock: bool = False,
        dropblock_prob: float = 0.1,
    ):
        super(FPNNeck, self).__init__()
        self.fpn = EnhancedFPNFuse(
            input_channels=channels,
            output_channels=out_channels,
            mode='bilinear',
            align_corners=False,
            use_cbam=use_cbam,
            use_dropblock=use_dropblock,
            dropblock_prob=dropblock_prob,
        )
        self.out_conv = nn.Conv2d(out_channels, out_channels, kernel_size=1)
    
    def forward(self, feature_maps: List[Tensor]) -> Tensor:
        x = self.fpn(feature_maps)
        x = self.out_conv(x)
        return x

# HyperColumnNeck with CBAM and DropBlock
class HyperColumnNeck(nn.Module):
    def __init__(self, channels: List[int], dropout_rate=0.0, use_cbam: bool = False, use_dropblock: bool = False, dropblock_prob: float = 0.1):
        super(HyperColumnNeck, self).__init__()
        self.fuse = EnhancedFPNFuse(
            input_channels=channels,
            output_channels=sum(channels),
            align_corners=True,
            use_cbam=use_cbam,
            use_dropblock=use_dropblock,
            dropblock_prob=dropblock_prob,
        )
        self.channels = sum(channels)
        self.dropout = nn.Dropout2d(p=dropout_rate, inplace=True)
    
    def forward(self, feature_maps: List[Tensor]) -> Tensor:
        return self.dropout(self.fuse(feature_maps))

# Feature Aggregation Network with Additional Enhancements
class FeatureAggregationNetwork(nn.Module):
     """
     Advanced Feature Aggregation Network that fuses multiple encoder feature maps using attention mechanisms.
     
     Args:
         input_channels (List[int]): List of channel dimensions for each input feature map.
         output_channels (int): Number of output channels.
         upsample_mode (str): Mode for upsampling ('nearest', 'bilinear', 'bicubic').
         use_attention (bool): Whether to use attention mechanisms for feature fusion.
         fusion_method (str): Method for feature fusion ('add', 'concat', 'weighted_sum').
     """
     def __init__(
         self,
         input_channels: List[int],
         out_channels: int,
         num_layers: int,
         upsample_mode: str = 'bilinear',
         use_attention: bool = True,
         fusion_method: str = 'weighted_sum'
     ):
         super(FeatureAggregationNetwork, self).__init__()
         self.input_channels = input_channels
         self.num_features = len(input_channels)
         self.out_channels = out_channels
         self.upsample_mode = upsample_mode
         self.use_attention = use_attention
         self.fusion_method = fusion_method
         # Channel alignment layer
         #self.channel_align = nn.Conv2d(input_channels[0], out_channels, kernel_size=1)
         """
         # Channel alignment layers
         self.channel_align_layers = nn.ModuleList([
             nn.Conv2d(in_ch, out_channels, kernel_size=1, bias=False) for in_ch in input_channels
         ])
         """
         # Channel alignment layers
         self.channel_align_layers = nn.ModuleList([
            nn.Conv2d(in_ch, self.out_channels, kernel_size=1, bias=False) for in_ch in self.input_channels
         ])
         # Attention modules (if used)
         if self.use_attention:
             self.attention_layers = nn.ModuleList([
                 SelfAttentionModule(out_channels)
                 for _ in range(self.num_features)
             ])
         
         # Learnable weights for fusion (if using weighted_sum)
         if self.fusion_method == 'weighted_sum':
             self.fusion_weights = nn.Parameter(torch.ones(self.num_features), requires_grad=True)
         
         # Output convolution
         self.output_conv = nn.Sequential(
             nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1, bias=False),
             nn.BatchNorm2d(out_channels),
             nn.ReLU(inplace=True)
         )
        
     def forward(self, feature_maps: Union[Tensor, List[Tensor]]) -> Tensor:
         """
         Forward pass for FeatureAggregationNetwork.
         
         Args:
             feature_maps (List[Tensor]): List of feature maps from different encoder stages.
         
         Returns:
             Tensor: Aggregated feature map.
         """
         if isinstance(feature_maps, Tensor):
             if feature_maps.dim() == 3:
                 # Possibly [channels, height, width], need to add batch dimension
                 feature_maps = feature_maps.unsqueeze(0)
             elif feature_maps.dim() == 4:
                 # Already has batch dimension
                 pass
             else:
                 raise ValueError(f"Expected feature_maps with 3 or 4 dimensions, got {feature_maps.dim()} dimensions.")
             feature_maps = [feature_maps]
         elif isinstance(feature_maps, list):
             # Ensure all feature maps have the correct dimensions
             for i, fm in enumerate(feature_maps):
                 if fm.dim() == 3:
                     feature_maps[i] = fm.unsqueeze(0)
                 elif fm.dim() != 4:
                     raise ValueError(f"Feature map at index {i} has {fm.dim()} dimensions, expected 3 or 4.")
         else:
             raise TypeError(f"Expected feature_maps to be a Tensor or list, but got {type(feature_maps)}")
 
         
         # Determine the target spatial size (smallest height and width)
         target_height = min([fm.size(2) for fm in feature_maps])
         target_width = min([fm.size(3) for fm in feature_maps])
         target_size = (target_height, target_width)
         
         #aligned_features = []channel_align_layers
         #aligned_features = [self.input_conv(fm) for fm in feature_maps]
         aligned_features = [layer(fm) for layer, fm in zip(self.channel_align_layers, feature_maps)]

         for idx, fm in enumerate(feature_maps):
             # Spatial alignment
             if fm.size(2) != target_height or fm.size(3) != target_width:
                 fm = F.interpolate(fm, size=target_size, mode=self.upsample_mode, align_corners=False)
                 logger.debug(f"Resized feature_map[{idx}] to {target_size}")
             
             # Channel alignment
             fm = self.channel_align_layers[idx](fm)
             
             # Attention
             if self.use_attention:
                 fm = self.attention_layers[idx](fm)
             
             aligned_features.append(fm)
        
         # Feature fusion
         if self.fusion_method == 'add':
             fused_feature = sum(aligned_features)
         elif self.fusion_method == 'concat':
             fused_feature = torch.cat(aligned_features, dim=1)
             # Adjust the number of channels to output_channels
             fused_feature = nn.Conv2d(
                 fused_feature.size(1), self.out_channels, kernel_size=1, bias=False
             )(fused_feature)
         elif self.fusion_method == 'weighted_sum':
             # Apply softmax to fusion weights
             weights = F.softmax(self.fusion_weights, dim=0)
             fused_feature = sum(w * f for w, f in zip(weights, aligned_features))
         else:
             raise ValueError(f"Unsupported fusion method: {self.fusion_method}")
         
         # Output convolution
         output = self.output_conv(fused_feature)
         logger.debug(f"FAN output shape: {output.shape}")  # Should be 4D
         return output

------------------------
###transform_classifier.py


class DebugSwinTransformer(SwinTransformer):
    def __init__(
        self,
        img_size: Union[int, Tuple[int, int]],
        patch_size: int = 4,
        in_chans: int = 2,
        embed_dim: int = 96,
        depths: list = [2, 2, 6, 2],
        num_heads: list = [3, 6, 12, 24],
        window_size: Tuple[int, int] = (4, 4),
        mlp_ratio: float = 4.0,
        qkv_bias: bool = True,
        qk_scale: Optional[float] = None,
        drop_rate: float = 0.0,
        attn_drop_rate: float = 0.0,
        drop_path_rate: float = 0.1,
        norm_layer: Optional[str] = 'torch.nn.LayerNorm',  # Fully qualified class path
        ape: bool = False,
        patch_norm: bool = True,
        use_checkpoint: bool = False,
    ):
        """
        DebugSwinTransformer extends the SwinTransformer to include debug statements.

        Args:
            norm_layer (str): Fully qualified class path for normalization layer (e.g., 'torch.nn.LayerNorm').
            All other parameters are the same as SwinTransformer.
        """
        # Enhanced assertion for window_size
        if isinstance(window_size, int):
            assert window_size > 0, f"Invalid window_size: {window_size}"
            self.window_size = (window_size, window_size)
        elif isinstance(window_size, (tuple, list)):
            assert len(window_size) == 2, f"window_size must have two dimensions, got {len(window_size)}"
            assert all(isinstance(w, int) and w > 0 for w in window_size), f"Invalid window_size elements: {window_size}"
            self.window_size = tuple(window_size)
        else:
            raise AssertionError(f"Invalid window_size type: {type(window_size)}. Expected int or tuple/list of two ints.")

        # Convert norm_layer string to actual class
        try:
            norm_layer_class = get_class(norm_layer)
        except ImportError as e:
            logger.error(f"Error loading norm_layer: {e}")
            raise

        # Initialize the parent SwinTransformer with validated window_size and norm_layer_class
        super().__init__(
            img_size=img_size,
            patch_size=patch_size,
            in_chans=in_chans,
            embed_dim=embed_dim,
            depths=depths,
            num_heads=num_heads,
            window_size=self.window_size,
            mlp_ratio=mlp_ratio,
            qkv_bias=qkv_bias,
            qk_scale=qk_scale,
            drop_rate=drop_rate,
            attn_drop_rate=attn_drop_rate,
            drop_path_rate=drop_path_rate,
            norm_layer=norm_layer_class,  # Pass the class, not the string
            ape=ape,
            patch_norm=patch_norm,
            use_checkpoint=use_checkpoint,
        )

    def forward_features(self, x):
        logger.debug(f"SwinTransformer.forward_features: window_size={self.window_size}, input_shape={x.shape}")
        return super().forward_features(x)

    def _attn(self, x):
        logger.debug(f"SwinTransformer._attn: window_size={self.window_size}")
        # Add assertion to prevent invalid window_size during attention
        if isinstance(self.window_size, int):
            assert self.window_size > 0, f"Invalid window_size during _attn: {self.window_size}"
        else:
            assert all(isinstance(w, int) and w > 0 for w in self.window_size), f"Invalid window_size during _attn: {self.window_size}"
        return super()._attn(x)


class SwinTransformerEncoder(nn.Module):
    def __init__(
        self,
        img_size: Union[int, Tuple[int, int]],
        embed_dim: int = 96,
        depths: list = [2, 2, 6, 2],
        num_heads: list = [3, 6, 12, 24],
        window_size: Tuple[int, int] = (4, 4),
        mlp_ratio: float = 4.0,
        qkv_bias: bool = True,
        qk_scale: Optional[float] = None,
        drop_rate: float = 0.0,
        attn_drop_rate: float = 0.0,
        drop_path_rate: float = 0.1,
        norm_layer: Optional[str] = 'torch.nn.LayerNorm',  # Fully qualified class path
        in_chans: int = 2,  # Number of input channels
        use_dropout: bool = False,
        use_custom_pos_embed: bool = False,
    ):
        """
        Swin Transformer Encoder with enhanced features.

        Args:
            norm_layer (str): Fully qualified class path for normalization layer (e.g., 'torch.nn.LayerNorm').
            All other parameters are the same as before.
        """
        super().__init__()
        # Validate img_size
        assert (
            (isinstance(img_size, int) and img_size > 0) or
            (isinstance(img_size, (list, tuple)) and 
             len(img_size) == 2 and 
             all(isinstance(x, int) and x > 0 for x in img_size))
        ), f"Invalid img_size={img_size}. Must be a positive int or tuple/list of two positive ints."

        self.window_size = window_size 

        # Convert norm_layer string to actual class
        try:
            norm_layer_class = get_class(norm_layer)
        except ImportError as e:
            logger.error(f"Error loading norm_layer: {e}")
            raise

        self.swin = DebugSwinTransformer(
            img_size=img_size,  # Now can be int or tuple/list
            patch_size=4,
            in_chans=in_chans,
            embed_dim=embed_dim,
            depths=depths,
            num_heads=num_heads,
            window_size=window_size,
            mlp_ratio=mlp_ratio,
            qkv_bias=qkv_bias,
            qk_scale=qk_scale,
            drop_rate=drop_rate,
            attn_drop_rate=attn_drop_rate,
            drop_path_rate=drop_path_rate,
            norm_layer=norm_layer_class,  # Pass the class, not the string
            ape=False,
            patch_norm=True,
            use_checkpoint=False
        )

        # Initialize advanced weight initialization
        self._init_weights()

        # Integrate TorchMetrics for monitoring
        self.accuracy = torchmetrics.Accuracy(task="multiclass", num_classes=2, average='macro')
        self.topk_accuracy = torchmetrics.Accuracy(task="multiclass", num_classes=2, average='macro')

        # Optional dropout layer after the transformer
        self.dropout = nn.Dropout(drop_rate) if use_dropout else nn.Identity()

        self.use_custom_pos_embed = use_custom_pos_embed
        if use_custom_pos_embed:
            # Adjust positional embedding based on img_size
            if isinstance(img_size, int):
                pos_embed_size = (img_size // 4, img_size // 4)
            else:
                pos_embed_size = (img_size[0] // 4, img_size[1] // 4)
            self.pos_embed = nn.Parameter(torch.zeros(1, embed_dim, *pos_embed_size))
            nn.init.kaiming_normal_(self.pos_embed, mode='fan_out', nonlinearity='relu')

    def _init_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Linear):
                nn.init.trunc_normal_(m.weight, std=.02)
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.LayerNorm):
                nn.init.constant_(m.bias, 0)
                nn.init.constant_(m.weight, 1.0)
            elif isinstance(m, nn.Conv2d):
                if m.weight.dim() > 1:
                    nn.init.xavier_uniform_(m.weight)
                else:
                    nn.init.normal_(m.weight)
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)

    def forward(self, x):
        """
        Forward pass through the Swin Transformer Encoder.

        Args:
            x (Tensor): Input tensor of shape [B, C, H, W].

        Returns:
            Tensor: Extracted features from the encoder.
        
        if self.use_custom_pos_embed:
            x = x + self.pos_embed
        # Use forward_features to retain spatial dimensions
        features = self.swin.forward_features(x)
        features = self.dropout(features)
        return features
        """
        def forward(self, x):
            features = self.swin.forward_features(x)
            features = self.dropout(features)
            # Reshape the output to match the expected shape
            B, C, H, W = x.shape
            features = features.permute(0, 2, 1).reshape(B, -1, H // self.swin.patch_size, W // self.swin.patch_size)
            return features