CoolFace
Modelpublic

RuiTerrty/RemoteSensingChangeDetection-RSCD.HA2F

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
encoder.py392 linesDownload Raw Back to model
1# Copyright (c) Meta Platforms, Inc. and affiliates.2#3# This source code is licensed under the Apache License, Version 2.04# found in the LICENSE file in the root directory of this source tree.5 6# References:7#   https://github.com/facebookresearch/dino/blob/main/vision_transformer.py8#   https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py9 10from functools import partial11import math12import logging13from typing import Sequence, Tuple, Union, Callable14 15import torch16import torch.nn as nn17import torch.utils.checkpoint18from torch.nn.init import trunc_normal_19from einops import rearrange20 21from model.layers import Mlp, PatchEmbed, SwiGLUFFNFused, MemEffAttention, NestedTensorBlock as Block22from model.resnet import resnet1823 24 25def named_apply(fn: Callable, module: nn.Module, name="", depth_first=True, include_root=False) -> nn.Module:26    if not depth_first and include_root:27        fn(module=module, name=name)28    for child_name, child_module in module.named_children():29        child_name = ".".join((name, child_name)) if name else child_name30        named_apply(fn=fn, module=child_module, name=child_name, depth_first=depth_first, include_root=True)31    if depth_first and include_root:32        fn(module=module, name=name)33    return module34 35 36class BlockChunk(nn.ModuleList):37    def forward(self, x):38        for b in self:39            x = b(x)40        return x41 42 43class DinoVisionTransformer(nn.Module):44    def __init__(45            self,46            img_size=224,47            patch_size=16,48            in_chans=3,49            embed_dim=768,50            depth=12,51            num_heads=12,52            mlp_ratio=4.0,53            qkv_bias=True,54            ffn_bias=True,55            proj_bias=True,56            drop_path_rate=0.0,57            drop_path_uniform=False,58            init_values=None,  # for layerscale: None or 0 => no layerscale59            embed_layer=PatchEmbed,60            act_layer=nn.GELU,61            block_fn=Block,62            ffn_layer="mlp",63            block_chunks=0,64            num_register_tokens=0,65            interpolate_antialias=False,66            interpolate_offset=0.1,67    ):68        """69        Args:70            img_size (int, tuple): input image size71            patch_size (int, tuple): patch size72            in_chans (int): number of input channels73            embed_dim (int): embedding dimension74            depth (int): depth of transformer75            num_heads (int): number of attention heads76            mlp_ratio (int): ratio of mlp hidden dim to embedding dim77            qkv_bias (bool): enable bias for qkv if True78            proj_bias (bool): enable bias for proj in attn if True79            ffn_bias (bool): enable bias for ffn if True80            drop_path_rate (float): stochastic depth rate81            drop_path_uniform (bool): apply uniform drop rate across blocks82            weight_init (str): weight init scheme83            init_values (float): layer-scale init values84            embed_layer (nn.Module): patch embedding layer85            act_layer (nn.Module): MLP activation layer86            block_fn (nn.Module): transformer block class87            ffn_layer (str): "mlp", "swiglu", "swiglufused" or "identity"88            block_chunks: (int) split block sequence into block_chunks units for FSDP wrap89            num_register_tokens: (int) number of extra cls tokens (so-called "registers")90            interpolate_antialias: (str) flag to apply anti-aliasing when interpolating positional embeddings91            interpolate_offset: (float) work-around offset to apply when interpolating positional embeddings92        """93        super().__init__()94        norm_layer = partial(nn.LayerNorm, eps=1e-6)95 96        self.num_features = self.embed_dim = embed_dim  # num_features for consistency with other models97        self.n_blocks = depth98        self.num_heads = num_heads99        self.patch_size = patch_size100        self.num_register_tokens = num_register_tokens101        self.interpolate_antialias = interpolate_antialias102        self.interpolate_offset = interpolate_offset103 104        self.patch_embed = embed_layer(img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim)105        num_patches = self.patch_embed.num_patches106 107        self.pos_embed = nn.Parameter(torch.zeros(1, num_patches, embed_dim))108        assert num_register_tokens >= 0109        self.register_tokens = (110            nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim)) if num_register_tokens else None111        )112 113        if drop_path_uniform is True:114            dpr = [drop_path_rate] * depth115        else:116            dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)]  # stochastic depth decay rule117 118        if ffn_layer == "mlp":119            print("using MLP layer as FFN")120            ffn_layer = Mlp121        elif ffn_layer == "swiglufused" or ffn_layer == "swiglu":122            print("using SwiGLU layer as FFN")123            ffn_layer = SwiGLUFFNFused124        elif ffn_layer == "identity":125            print("using Identity layer as FFN")126 127            def f(*args, **kwargs):128                return nn.Identity()129 130            ffn_layer = f131        else:132            raise NotImplementedError133 134        blocks_list = [135            block_fn(136                dim=embed_dim,137                num_heads=num_heads,138                mlp_ratio=mlp_ratio,139                qkv_bias=qkv_bias,140                proj_bias=proj_bias,141                ffn_bias=ffn_bias,142                drop_path=dpr[i],143                norm_layer=norm_layer,144                act_layer=act_layer,145                ffn_layer=ffn_layer,146                init_values=init_values,147            )148            for i in range(depth)149        ]150        if block_chunks > 0:151            self.chunked_blocks = True152            chunked_blocks = []153            chunksize = depth // block_chunks154            for i in range(0, depth, chunksize):155                # this is to keep the block index consistent if we chunk the block list156                chunked_blocks.append([nn.Identity()] * i + blocks_list[i: i + chunksize])157            self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks])158        else:159            self.chunked_blocks = False160            self.blocks = nn.ModuleList(blocks_list)161 162        self.norm = norm_layer(embed_dim)163        self.head = nn.Identity()164 165        self.mask_token = nn.Parameter(torch.zeros(1, embed_dim))166 167        self.init_weights()168 169    def init_weights(self):170        trunc_normal_(self.pos_embed, std=0.02)171        if self.register_tokens is not None:172            nn.init.normal_(self.register_tokens, std=1e-6)173        named_apply(init_weights_vit_timm, self)174 175    def interpolate_pos_encoding(self, x, w, h):176        previous_dtype = x.dtype177        npatch = x.shape[1] - 1178        N = self.pos_embed.shape[1]179        if npatch == N and w == h:180            return self.pos_embed181        patch_pos_embed = self.pos_embed.float()182        dim = x.shape[-1]183        w0 = w // self.patch_size184        h0 = h // self.patch_size185        # we add a small number to avoid floating point error in the interpolation186        # see discussion at https://github.com/facebookresearch/dino/issues/8187        w0, h0 = w0 + self.interpolate_offset, h0 + self.interpolate_offset188 189        sqrt_N = math.sqrt(N)190        sx, sy = float(w0) / sqrt_N, float(h0) / sqrt_N191        patch_pos_embed = nn.functional.interpolate(192            patch_pos_embed.reshape(1, int(sqrt_N), int(sqrt_N), dim).permute(0, 3, 1, 2),193            scale_factor=(sx, sy),194            mode="bicubic",195            antialias=self.interpolate_antialias,196        )197 198        assert int(w0) == patch_pos_embed.shape[-2]199        assert int(h0) == patch_pos_embed.shape[-1]200        patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)201        return patch_pos_embed.to(previous_dtype)202 203    def prepare_tokens_with_masks(self, x, masks=None):204        B, nc, w, h = x.shape205        x = self.patch_embed(x)206        if masks is not None:207            x = torch.where(masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x)208 209        x = x + self.interpolate_pos_encoding(x, w, h)210 211        if self.register_tokens is not None:212            x = torch.cat(213                (214                    x[:, :1],215                    self.register_tokens.expand(x.shape[0], -1, -1),216                    x[:, 1:],217                ),218                dim=1,219            )220 221        return x222 223    def forward_features_list(self, x_list, masks_list):224        x = [self.prepare_tokens_with_masks(x, masks) for x, masks in zip(x_list, masks_list)]225        for blk in self.blocks:226            x = blk(x)227 228        all_x = x229        output = []230        for x, masks in zip(all_x, masks_list):231            x_norm = self.norm(x)232            output.append(233                {234                    "x_norm_clstoken": x_norm[:, 0],235                    "x_norm_regtokens": x_norm[:, 1: self.num_register_tokens + 1],236                    "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1:],237                    "x_prenorm": x,238                    "masks": masks,239                }240            )241        return output242 243    def forward(self, x, masks=None):244        if isinstance(x, list):245            return self.forward_features_list(x, masks)246 247        x = self.prepare_tokens_with_masks(x, masks)248 249        for blk in self.blocks:250            x = blk(x)251 252        x_norm = self.norm(x)253        return x_norm254 255    def _get_intermediate_layers_not_chunked(self, x, n=1):256        x = self.prepare_tokens_with_masks(x)257        # If n is an int, take the n last blocks. If it's a list, take them258        output, total_block_len = [], len(self.blocks)259        blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n260        for i, blk in enumerate(self.blocks):261            x = blk(x)262            if i in blocks_to_take:263                output.append(x)264        assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"265        return output266 267    def _get_intermediate_layers_chunked(self, x, n=1):268        x = self.prepare_tokens_with_masks(x)269        output, i, total_block_len = [], 0, len(self.blocks[-1])270        # If n is an int, take the n last blocks. If it's a list, take them271        blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n272        for block_chunk in self.blocks:273            for blk in block_chunk[i:]:  # Passing the nn.Identity()274                x = blk(x)275                if i in blocks_to_take:276                    output.append(x)277                i += 1278        assert len(output) == len(blocks_to_take), f"only {len(output)} / {len(blocks_to_take)} blocks found"279        return output280 281    def get_intermediate_layers(282            self,283            x: torch.Tensor,284            n: Union[int, Sequence] = 1,  # Layers or n last layers to take285            reshape: bool = False,286            return_class_token: bool = False,287            norm=True,288    ) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor]]]:289        if self.chunked_blocks:290            outputs = self._get_intermediate_layers_chunked(x, n)291        else:292            outputs = self._get_intermediate_layers_not_chunked(x, n)293        if norm:294            outputs = [self.norm(out) for out in outputs]295        class_tokens = [out[:, 0] for out in outputs]296        outputs = [out[:, 1 + self.num_register_tokens:] for out in outputs]297        if reshape:298            B, _, w, h = x.shape299            outputs = [300                out.reshape(B, w // self.patch_size, h // self.patch_size, -1).permute(0, 3, 1, 2).contiguous()301                for out in outputs302            ]303        if return_class_token:304            return tuple(zip(outputs, class_tokens))305        return tuple(outputs)306 307 308def init_weights_vit_timm(module: nn.Module, name: str = ""):309    """ViT weight initialization, original timm impl (for reproducibility)"""310    if isinstance(module, nn.Linear):311        trunc_normal_(module.weight, std=0.02)312        if module.bias is not None:313            nn.init.zeros_(module.bias)314 315 316class Encoder(nn.Module):317    def __init__(self, model_type='small'):318        super().__init__()319        if model_type == 'tiny':320            self.vit = DinoVisionTransformer(321                img_size=256,322                patch_size=16,323                embed_dim=192,324                depth=12,325                num_heads=6,326                mlp_ratio=4,327                block_fn=partial(Block, attn_class=MemEffAttention),328                num_register_tokens=0329            )330            path = "checkpoint/deit_tiny_patch16_224-a1311bcf.pth"331 332        elif model_type == 'small':333            self.vit = DinoVisionTransformer(334                img_size=256,335                patch_size=16,336                embed_dim=384,337                depth=12,338                num_heads=6,339                mlp_ratio=4,340                block_fn=partial(Block, attn_class=MemEffAttention),341                num_register_tokens=0342            )343            path = "checkpoint/dinov2_vits14_pretrain.pth"344 345        else:346            assert False, r'Encoder: check the vit model type'347 348        state_dict = torch.load(path, map_location='cpu')['model'] \349            if model_type == 'tiny' else torch.load(path, map_location='cpu')350 351        for k in ['pos_embed', 'patch_embed.proj.weight']:352            del state_dict[k]353        msg = self.vit.load_state_dict(state_dict, strict=False)354        print(' missing_keys:{},\n unexpected_keys:{}'.format(msg.missing_keys, msg.unexpected_keys))355        print('model_type: {},\n checkpoint_path: {}'.format(model_type, path))356 357        self.resnet = resnet18(pretrained=True)358        self.drop = nn.Dropout(p=0.01)359 360        # 新增特征融合模块361        self.fusion_conv = nn.Sequential(362            nn.Conv2d(512 + 384, 384, kernel_size=1),  # 假设ViT embed_dim=384363            nn.BatchNorm2d(384),364            nn.ReLU(inplace=True)365        )366 367    def detail_capture(self, x):368        x = self.resnet.conv1(x)369        x = self.resnet.bn1(x)370        x = self.resnet.relu(x)371 372        x2 = self.drop(self.resnet.layer1(x))373        x3 = self.resnet.layer2(x2)374        x4 = self.resnet.layer3(x3)375        x5 = self.resnet.layer4(x4)376        return [x2, x3, x4, x5]377 378    def forward(self, x, y):379 380        v_x = self.vit(x)381        v_y = self.vit(y)382 383        v_x = rearrange(v_x, 'b (h w) c -> b c h w', h=16, w=16)384        v_y = rearrange(v_y, 'b (h w) c -> b c h w', h=16, w=16)385 386        c_x = self.detail_capture(x)387        c_y = self.detail_capture(y)388 389        fused_v_x = self.fusion_conv(torch.cat([c_x[-1], v_x], dim=1))390        fused_v_y = self.fusion_conv(torch.cat([c_y[-1], v_y], dim=1))391        return c_x[:-1] + [fused_v_x], c_y[:-1] + [fused_v_y]392