638 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			638 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# --------------------------------------------------------
 | 
						||
# Swin Transformer
 | 
						||
# Copyright (c) 2021 Microsoft
 | 
						||
# Licensed under The MIT License [see LICENSE for details]
 | 
						||
# Written by Ze Liu
 | 
						||
# --------------------------------------------------------
 | 
						||
import math
 | 
						||
 | 
						||
import numpy as np
 | 
						||
import torch
 | 
						||
import torch.nn as nn
 | 
						||
import torch.nn.functional as F
 | 
						||
import torch.utils.checkpoint as checkpoint
 | 
						||
 | 
						||
 | 
						||
def _make_divisible(v, divisor, min_value=None):
 | 
						||
    if min_value is None:
 | 
						||
        min_value = divisor
 | 
						||
    new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
 | 
						||
    if new_v < 0.9 * v:
 | 
						||
        new_v += divisor
 | 
						||
    return new_v
 | 
						||
 | 
						||
def trunc_normal_(tensor, mean=0., std=1., a=-2., b=2.):
 | 
						||
    def _no_grad_trunc_normal_(tensor, mean, std, a, b):
 | 
						||
        def norm_cdf(x):
 | 
						||
            return (1. + math.erf(x / math.sqrt(2.))) / 2.
 | 
						||
 | 
						||
        with torch.no_grad():
 | 
						||
            l = norm_cdf((a - mean) / std)
 | 
						||
            u = norm_cdf((b - mean) / std)
 | 
						||
 | 
						||
            tensor.uniform_(2 * l - 1, 2 * u - 1)
 | 
						||
            tensor.erfinv_()
 | 
						||
 | 
						||
            tensor.mul_(std * math.sqrt(2.))
 | 
						||
            tensor.add_(mean)
 | 
						||
 | 
						||
            tensor.clamp_(min=a, max=b)
 | 
						||
            return tensor
 | 
						||
    return _no_grad_trunc_normal_(tensor, mean, std, a, b)
 | 
						||
 | 
						||
#--------------------------------------#
 | 
						||
#   Gelu激活函数的实现
 | 
						||
#   利用近似的数学公式
 | 
						||
#--------------------------------------#
 | 
						||
class GELU(nn.Module):
 | 
						||
    def __init__(self):
 | 
						||
        super(GELU, self).__init__()
 | 
						||
 | 
						||
    def forward(self, x):
 | 
						||
        return 0.5 * x * (1 + torch.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * torch.pow(x,3))))
 | 
						||
 | 
						||
#-------------------------------------------------------#
 | 
						||
#   对输入进来的图片进行高和宽的压缩
 | 
						||
#   并且进行通道的扩张。
 | 
						||
#-------------------------------------------------------#
 | 
						||
class PatchEmbed(nn.Module):
 | 
						||
    def __init__(self, img_size=[224, 224], patch_size=4, in_chans=3, embed_dim=96, norm_layer=None):
 | 
						||
        super().__init__()
 | 
						||
        # [224, 224]
 | 
						||
        self.img_size           = img_size
 | 
						||
        # [4, 4]
 | 
						||
        self.patch_size         = [patch_size, patch_size]
 | 
						||
        # [56, 56]
 | 
						||
        self.patches_resolution = [self.img_size[0] // self.patch_size[0], self.img_size[1] // self.patch_size[1]]
 | 
						||
 | 
						||
        # 3136
 | 
						||
        self.num_patches        = self.patches_resolution[0] * self.patches_resolution[1]
 | 
						||
        # 3
 | 
						||
        self.in_chans           = in_chans
 | 
						||
        # 96
 | 
						||
        self.embed_dim          = embed_dim
 | 
						||
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   bs, 224, 224, 3 -> bs, 56, 56, 96
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
 | 
						||
        if norm_layer is not None:
 | 
						||
            self.norm = norm_layer(embed_dim)
 | 
						||
        else:
 | 
						||
            self.norm = None
 | 
						||
 | 
						||
    def forward(self, x):
 | 
						||
        B, C, H, W = x.shape
 | 
						||
        # FIXME look at relaxing size constraints
 | 
						||
        assert H == self.img_size[0] and W == self.img_size[1], \
 | 
						||
            f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]} * {self.img_size[1]})."
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   bs, 224, 224, 3 -> bs, 56, 56, 96 -> bs, 3136, 96
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        x = self.proj(x).flatten(2).transpose(1, 2)
 | 
						||
        if self.norm is not None:
 | 
						||
            x = self.norm(x)
 | 
						||
        return x
 | 
						||
 | 
						||
def window_partition(x, window_size):
 | 
						||
    B, H, W, C  = x.shape
 | 
						||
    #------------------------------------------------------------------#
 | 
						||
    #   bs, 56, 56, 96 -> bs, 8, 7, 8, 7, 96 -> bs * 64, 7, 7, 96
 | 
						||
    #------------------------------------------------------------------#
 | 
						||
    x           = x.view(B, H // window_size, window_size, W // window_size, window_size, C)
 | 
						||
    windows     = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C)
 | 
						||
    return windows
 | 
						||
 | 
						||
def window_reverse(windows, window_size, H, W):
 | 
						||
    #------------------------------------------------------------------#
 | 
						||
    #   bs * 64, 7, 7, 96 -> bs, 8, 8, 7, 7, 96 -> bs, 56, 56, 96
 | 
						||
    #------------------------------------------------------------------#
 | 
						||
    B = int(windows.shape[0] / (H * W / window_size / window_size))
 | 
						||
    x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1)
 | 
						||
    x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1)
 | 
						||
    return x
 | 
						||
 | 
						||
 | 
						||
class WindowAttention(nn.Module):
 | 
						||
    def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.):
 | 
						||
        super().__init__()
 | 
						||
        self.dim            = dim
 | 
						||
        self.window_size    = window_size  # Wh, Ww
 | 
						||
        self.num_heads      = num_heads
 | 
						||
        head_dim            = dim // num_heads
 | 
						||
        self.scale          = qk_scale or head_dim ** -0.5
 | 
						||
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        #   相对坐标矩阵,用于表示每个窗口内,其它点相对于自己的坐标
 | 
						||
        #   由于相对坐标取值范围为-6 ~ +6。中间共13个值,因此需要13 * 13
 | 
						||
        #   13 * 13, num_heads
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        self.relative_position_bias_table = nn.Parameter(
 | 
						||
            torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads)
 | 
						||
        ) 
 | 
						||
        
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        #   该部分用于获取7x7的矩阵内部,其它特征点相对于自身相对坐标
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        coords_h    = torch.arange(self.window_size[0])
 | 
						||
        coords_w    = torch.arange(self.window_size[1])
 | 
						||
        coords      = torch.stack(torch.meshgrid([coords_h, coords_w]))  # 2, Wh, Ww
 | 
						||
        coords_flatten  = torch.flatten(coords, 1)  # 2, Wh*Ww
 | 
						||
        relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :]  # 2, Wh*Ww, Wh*Ww
 | 
						||
        relative_coords = relative_coords.permute(1, 2, 0).contiguous()  # Wh*Ww, Wh*Ww, 2
 | 
						||
        relative_coords[:, :, 0]    += self.window_size[0] - 1  # shift to start from 0
 | 
						||
        relative_coords[:, :, 1]    += self.window_size[1] - 1
 | 
						||
        relative_coords[:, :, 0]    *= 2 * self.window_size[1] - 1
 | 
						||
        relative_position_index     = relative_coords.sum(-1)  # Wh*Ww, Wh*Ww
 | 
						||
        self.register_buffer("relative_position_index", relative_position_index)
 | 
						||
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        #   乘积获得q、k、v,用于计算多头注意力机制
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        self.qkv        = nn.Linear(dim, dim * 3, bias=qkv_bias)
 | 
						||
        self.attn_drop  = nn.Dropout(attn_drop)
 | 
						||
        self.proj       = nn.Linear(dim, dim)
 | 
						||
        self.proj_drop  = nn.Dropout(proj_drop)
 | 
						||
 | 
						||
        trunc_normal_(self.relative_position_bias_table, std=.02)
 | 
						||
        self.softmax = nn.Softmax(dim=-1)
 | 
						||
 | 
						||
    def forward(self, x, mask=None):
 | 
						||
        B_, N, C    = x.shape
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        #   bs * 64, 49, 96 -> bs * 64, 49, 96 * 3 -> 
 | 
						||
        #   bs * 64, 49, 3, num_heads, 32 -> 3, bs * 64, num_head, 49, 32    
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        qkv         = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        #   bs * 64, num_head, 49, 32   
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        q, k, v     = qkv[0], qkv[1], qkv[2] 
 | 
						||
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        #   bs * 64, num_head, 49, 49
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        q       = q * self.scale
 | 
						||
        attn    = (q @ k.transpose(-2, -1))
 | 
						||
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        #   这一步是根据已经求得的注意力,加上相对坐标的偏执量
 | 
						||
        #   形成最后的注意力
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(
 | 
						||
            self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1)  # Wh*Ww,Wh*Ww,nH
 | 
						||
        relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous()
 | 
						||
        attn = attn + relative_position_bias.unsqueeze(0)
 | 
						||
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        #   加上mask,保证分区。
 | 
						||
        #   bs * 64, num_head, 49, 49
 | 
						||
        #--------------------------------------------------------------------------#
 | 
						||
        if mask is not None:
 | 
						||
            nW = mask.shape[0]
 | 
						||
            attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0)
 | 
						||
            attn = attn.view(-1, self.num_heads, N, N)
 | 
						||
            attn = self.softmax(attn)
 | 
						||
        else:
 | 
						||
            attn = self.softmax(attn)
 | 
						||
 | 
						||
        attn = self.attn_drop(attn)
 | 
						||
 | 
						||
        #---------------------------------------------------------------------------------------#
 | 
						||
        #   bs * 64, num_head, 49, 49 @ bs * 64, num_head, 49, 32 -> bs * 64, num_head, 49, 32
 | 
						||
        #    
 | 
						||
        #   bs * 64, num_head, 49, 32 -> bs * 64, 49, 96
 | 
						||
        #---------------------------------------------------------------------------------------#
 | 
						||
        x = (attn @ v).transpose(1, 2).reshape(B_, N, C)
 | 
						||
        x = self.proj(x)
 | 
						||
        x = self.proj_drop(x)
 | 
						||
        return x
 | 
						||
 | 
						||
 | 
						||
def drop_path(x, drop_prob: float = 0., training: bool = False, scale_by_keep: bool = True):
 | 
						||
    """
 | 
						||
    Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
 | 
						||
    This is the same as the DropConnect impl I created for EfficientNet, etc networks, however,
 | 
						||
    the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper...
 | 
						||
    See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... I've opted for
 | 
						||
    changing the layer and argument names to 'drop path' rather than mix DropConnect as a layer name and use
 | 
						||
    'survival rate' as the argument.
 | 
						||
    """
 | 
						||
    if drop_prob == 0. or not training:
 | 
						||
        return x
 | 
						||
    keep_prob       = 1 - drop_prob
 | 
						||
    shape           = (x.shape[0],) + (1,) * (x.ndim - 1)  # work with diff dim tensors, not just 2D ConvNets
 | 
						||
    random_tensor   = x.new_empty(shape).bernoulli_(keep_prob)
 | 
						||
    if keep_prob > 0.0 and scale_by_keep:
 | 
						||
        random_tensor.div_(keep_prob)
 | 
						||
    return x * random_tensor
 | 
						||
 | 
						||
 | 
						||
class DropPath(nn.Module):
 | 
						||
    """
 | 
						||
    Drop paths (Stochastic Depth) per sample  (when applied in main path of residual blocks).
 | 
						||
    """
 | 
						||
    def __init__(self, drop_prob=None, scale_by_keep=True):
 | 
						||
        super(DropPath, self).__init__()
 | 
						||
        self.drop_prob = drop_prob
 | 
						||
        self.scale_by_keep = scale_by_keep
 | 
						||
 | 
						||
    def forward(self, x):
 | 
						||
        return drop_path(x, self.drop_prob, self.training, self.scale_by_keep)
 | 
						||
 | 
						||
 | 
						||
#-------------------------------------------------------#
 | 
						||
#   两次全连接
 | 
						||
#-------------------------------------------------------#
 | 
						||
class Mlp(nn.Module):
 | 
						||
    def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=GELU, drop=0.):
 | 
						||
        super().__init__()
 | 
						||
        out_features = out_features or in_features
 | 
						||
        hidden_features = hidden_features or in_features
 | 
						||
        self.fc1 = nn.Linear(in_features, hidden_features)
 | 
						||
        self.act = act_layer()
 | 
						||
        self.fc2 = nn.Linear(hidden_features, out_features)
 | 
						||
        self.drop = nn.Dropout(drop)
 | 
						||
 | 
						||
    def forward(self, x):
 | 
						||
        x = self.fc1(x)
 | 
						||
        x = self.act(x)
 | 
						||
        x = self.drop(x)
 | 
						||
        x = self.fc2(x)
 | 
						||
        x = self.drop(x)
 | 
						||
        return x
 | 
						||
 | 
						||
#-------------------------------------------------------#
 | 
						||
#   每个阶段重复的基础模块
 | 
						||
#   在这其中会使用WindowAttention进行特征提取
 | 
						||
#-------------------------------------------------------#
 | 
						||
class SwinTransformerBlock(nn.Module):
 | 
						||
    def __init__(self, dim, input_resolution, num_heads, window_size=7, shift_size=0,
 | 
						||
                 mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0.,
 | 
						||
                 act_layer=GELU, norm_layer=nn.LayerNorm):
 | 
						||
        super().__init__()
 | 
						||
        self.dim                = dim
 | 
						||
        self.input_resolution   = input_resolution
 | 
						||
        self.num_heads          = num_heads
 | 
						||
        self.window_size        = window_size
 | 
						||
        self.shift_size         = shift_size
 | 
						||
 | 
						||
        self.mlp_ratio          = mlp_ratio
 | 
						||
        if min(self.input_resolution) <= self.window_size:
 | 
						||
            self.shift_size = 0
 | 
						||
            self.window_size = min(self.input_resolution)
 | 
						||
        assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size"
 | 
						||
 | 
						||
        self.norm1  = norm_layer(dim)
 | 
						||
        self.attn   = WindowAttention(
 | 
						||
            dim, 
 | 
						||
            window_size = [self.window_size, self.window_size], 
 | 
						||
            num_heads   = num_heads,
 | 
						||
            qkv_bias    = qkv_bias, 
 | 
						||
            qk_scale    = qk_scale, 
 | 
						||
            attn_drop   = attn_drop, 
 | 
						||
            proj_drop   = drop
 | 
						||
        )
 | 
						||
 | 
						||
        self.drop_path  = DropPath(drop_path) if drop_path > 0. else nn.Identity()
 | 
						||
        self.norm2      = norm_layer(dim)
 | 
						||
        mlp_hidden_dim  = int(dim * mlp_ratio)
 | 
						||
        self.mlp        = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)
 | 
						||
 | 
						||
        if self.shift_size > 0:
 | 
						||
            #----------------------------------------------------------------#
 | 
						||
            #   由于进行特征提取时,会对输入的特征层进行的平移
 | 
						||
            #   如:
 | 
						||
            #   [                                   [
 | 
						||
            #       [1, 2, 3],                          [5, 6, 4],   
 | 
						||
            #       [4, 5, 6],          -->             [8, 9, 7],
 | 
						||
            #       [7, 8, 9],                          [1, 2, 3],
 | 
						||
            #   ]                                   ]
 | 
						||
            #   这一步的作用就是使得平移后的区域块只计算自己部分的注意力机制
 | 
						||
            #----------------------------------------------------------------#
 | 
						||
            H, W = self.input_resolution
 | 
						||
            _H, _W  =  _make_divisible(H, self.window_size), _make_divisible(W, self.window_size),
 | 
						||
            img_mask = torch.zeros((1, _H, _W, 1))  # 1 H W 1
 | 
						||
            h_slices = (slice(0, -self.window_size),
 | 
						||
                        slice(-self.window_size, -self.shift_size),
 | 
						||
                        slice(-self.shift_size, None))
 | 
						||
            w_slices = (slice(0, -self.window_size),
 | 
						||
                        slice(-self.window_size, -self.shift_size),
 | 
						||
                        slice(-self.shift_size, None))
 | 
						||
            cnt = 0
 | 
						||
            for h in h_slices:
 | 
						||
                for w in w_slices:
 | 
						||
                    img_mask[:, h, w, :] = cnt
 | 
						||
                    cnt += 1
 | 
						||
 | 
						||
            mask_windows = window_partition(img_mask, self.window_size)  # nW, window_size, window_size, 1
 | 
						||
            mask_windows = mask_windows.view(-1, self.window_size * self.window_size)
 | 
						||
            attn_mask       = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
 | 
						||
            attn_mask       = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0))
 | 
						||
            self.attn_mask  = attn_mask.cpu().numpy()
 | 
						||
        else:
 | 
						||
            self.attn_mask = None
 | 
						||
 | 
						||
    def forward(self, x):
 | 
						||
        H, W = self.input_resolution
 | 
						||
        B, L, C = x.shape
 | 
						||
        assert L == H * W, "input feature has wrong size"
 | 
						||
        #-----------------------------------------------#
 | 
						||
        #   bs, 3136, 96 -> bs, 56, 56, 96
 | 
						||
        #-----------------------------------------------#
 | 
						||
        shortcut = x
 | 
						||
        x = self.norm1(x)
 | 
						||
        x = x.view(B, H, W, C)
 | 
						||
 | 
						||
        _H, _W  =  _make_divisible(H, self.window_size), _make_divisible(W, self.window_size),
 | 
						||
        x       = x.permute(0, 3, 1, 2)
 | 
						||
        x       = F.interpolate(x, [_H, _W], mode='bicubic', align_corners=False).permute(0, 2, 3, 1)
 | 
						||
 | 
						||
        #-----------------------------------------------#
 | 
						||
        #   进行特征层的平移
 | 
						||
        #-----------------------------------------------#
 | 
						||
        if self.shift_size > 0:
 | 
						||
            shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2))
 | 
						||
        else:
 | 
						||
            shifted_x = x
 | 
						||
        #------------------------------------------------------------------------------------------#
 | 
						||
        #   bs, 56, 56, 96 -> bs * 64, 7, 7, 96 -> bs * 64, 49, 96
 | 
						||
        #------------------------------------------------------------------------------------------#
 | 
						||
        x_windows = window_partition(shifted_x, self.window_size)  # num_windows * B, window_size, window_size, C
 | 
						||
        x_windows = x_windows.view(-1, self.window_size * self.window_size, C)  # nW*B, window_size*window_size, C
 | 
						||
 | 
						||
        #-----------------------------------------------#
 | 
						||
        #   bs * 64, 49, 97 -> bs * 64, 49, 97
 | 
						||
        #-----------------------------------------------#
 | 
						||
        if type(self.attn_mask) != type(None):
 | 
						||
            attn_mask = torch.tensor(self.attn_mask).cuda() if x.is_cuda else torch.tensor(self.attn_mask)
 | 
						||
        else:
 | 
						||
            attn_mask = None
 | 
						||
        attn_windows = self.attn(x_windows, mask=attn_mask)  # nW*B, window_size*window_size, C
 | 
						||
        #-----------------------------------------------#
 | 
						||
        #   bs * 64, 49, 97 -> bs, 56, 56, 96
 | 
						||
        #-----------------------------------------------#
 | 
						||
        attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C)
 | 
						||
        shifted_x = window_reverse(attn_windows, self.window_size, _H, _W)  # B H' W' C
 | 
						||
 | 
						||
        #-----------------------------------------------#
 | 
						||
        #   将特征层平移回来
 | 
						||
        #-----------------------------------------------#
 | 
						||
        if self.shift_size > 0:
 | 
						||
            x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2))
 | 
						||
        else:
 | 
						||
            x = shifted_x
 | 
						||
        
 | 
						||
        x = x.permute(0, 3, 1, 2)
 | 
						||
        x = F.interpolate(x, [H, W], mode='bicubic', align_corners=False).permute(0, 2, 3, 1)
 | 
						||
        #-----------------------------------------------#
 | 
						||
        #   bs, 3136, 96
 | 
						||
        #-----------------------------------------------#
 | 
						||
        x = x.view(B, H * W, C)
 | 
						||
        #-----------------------------------------------#
 | 
						||
        #   FFN
 | 
						||
        #   bs, 3136, 96
 | 
						||
        #-----------------------------------------------#
 | 
						||
        x = shortcut + self.drop_path(x)
 | 
						||
        x = x + self.drop_path(self.mlp(self.norm2(x)))
 | 
						||
 | 
						||
        return x
 | 
						||
 | 
						||
#-------------------------------------------------------#
 | 
						||
#   对输入进来的特征层进行高和宽的压缩
 | 
						||
#   进行跨特征点的特征提取,提取完成后进行堆叠。
 | 
						||
#-------------------------------------------------------#
 | 
						||
class PatchMerging(nn.Module):
 | 
						||
    def __init__(self, input_resolution, dim, norm_layer=nn.LayerNorm):
 | 
						||
        super().__init__()
 | 
						||
        self.input_resolution   = input_resolution
 | 
						||
        self.dim                = dim
 | 
						||
 | 
						||
        self.norm               = norm_layer(4 * dim)
 | 
						||
        self.reduction          = nn.Linear(4 * dim, 2 * dim, bias=False)
 | 
						||
 | 
						||
    def forward(self, x):
 | 
						||
        H, W = self.input_resolution
 | 
						||
        B, L, C = x.shape
 | 
						||
        assert L == H * W, "input feature has wrong size"
 | 
						||
        assert H % 2 == 0 and W % 2 == 0, f"x size ({H}*{W}) are not even."
 | 
						||
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   bs, 3136, 96 -> bs, 56, 56, 96
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        x = x.view(B, H, W, C)
 | 
						||
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   x0 ~ x3   bs, 56, 56, 96 -> bs, 28, 28, 96
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        x0 = x[:, 0::2, 0::2, :]  # B H/2 W/2 C
 | 
						||
        x1 = x[:, 1::2, 0::2, :]  # B H/2 W/2 C
 | 
						||
        x2 = x[:, 0::2, 1::2, :]  # B H/2 W/2 C
 | 
						||
        x3 = x[:, 1::2, 1::2, :]  # B H/2 W/2 C
 | 
						||
        
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   4 X bs, 28, 28, 96 -> bs, 28, 28, 384
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        x = torch.cat([x0, x1, x2, x3], -1)  # B H/2 W/2 4*C
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   bs, 28, 28, 384 -> bs, 784, 384
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        x = x.view(B, -1, 4 * C)  # B H/2*W/2 4*C
 | 
						||
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   bs, 784, 384 -> bs, 784, 192
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        x = self.norm(x)
 | 
						||
        x = self.reduction(x)
 | 
						||
        return x
 | 
						||
 | 
						||
 | 
						||
#-------------------------------------------------------#
 | 
						||
#   Swin-Transformer的基础模块。
 | 
						||
#   使用窗口多头注意力机制进行特征提取。
 | 
						||
#   使用PatchMerging进行高和宽的压缩。
 | 
						||
#-------------------------------------------------------#
 | 
						||
class BasicLayer(nn.Module):
 | 
						||
    def __init__(self, dim, input_resolution, depth, num_heads, window_size,
 | 
						||
                 mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0.,
 | 
						||
                 drop_path=0., norm_layer=nn.LayerNorm, downsample=None, use_checkpoint=False):
 | 
						||
        super().__init__()
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   四个阶段对应不同的dim
 | 
						||
        #   [96, 192, 384, 768]
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        self.dim                = dim
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   四个阶段对应不同的输入分辨率
 | 
						||
        #   [[56, 56], [28, 28], [14, 14], [7, 7]]
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        self.input_resolution   = input_resolution
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   四个阶段对应不同的多头注意力机制重复次数  
 | 
						||
        #   [2, 2, 6, 2]
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        self.depth              = depth
 | 
						||
        self.use_checkpoint     = use_checkpoint
 | 
						||
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        #   根据depth的次数利用窗口多头注意力机制进行特征提取。
 | 
						||
        #-------------------------------------------------------#
 | 
						||
        self.blocks = nn.ModuleList(
 | 
						||
            [
 | 
						||
                SwinTransformerBlock(
 | 
						||
                    dim         = dim, 
 | 
						||
                    input_resolution = input_resolution,
 | 
						||
                    num_heads   = num_heads, 
 | 
						||
                    window_size = window_size,
 | 
						||
                    shift_size  = 0 if (i % 2 == 0) else window_size // 2,
 | 
						||
                    mlp_ratio   = mlp_ratio,
 | 
						||
                    qkv_bias    = qkv_bias, 
 | 
						||
                    qk_scale    = qk_scale,
 | 
						||
                    drop        = drop, 
 | 
						||
                    attn_drop   = attn_drop,
 | 
						||
                    drop_path   = drop_path[i] if isinstance(drop_path, list) else drop_path,
 | 
						||
                    norm_layer  = norm_layer
 | 
						||
                )
 | 
						||
                for i in range(depth)
 | 
						||
            ]
 | 
						||
        )
 | 
						||
 | 
						||
        if downsample is not None:
 | 
						||
            #-------------------------------------------------------#
 | 
						||
            #   判断是否要进行下采样,即:高宽压缩
 | 
						||
            #-------------------------------------------------------#
 | 
						||
            self.downsample = downsample(input_resolution, dim=dim, norm_layer=norm_layer)
 | 
						||
        else:
 | 
						||
            self.downsample = None
 | 
						||
 | 
						||
    def forward(self, x):
 | 
						||
        for blk in self.blocks:
 | 
						||
            if self.use_checkpoint:
 | 
						||
                x_ = checkpoint.checkpoint(blk, x)
 | 
						||
            else:
 | 
						||
                x_ = blk(x)
 | 
						||
        if self.downsample is not None:
 | 
						||
            x = self.downsample(x_)
 | 
						||
        else:
 | 
						||
            x = x_
 | 
						||
        return x_, x
 | 
						||
 | 
						||
class SwinTransformer(nn.Module):
 | 
						||
    def __init__(self, img_size=[640, 640], patch_size=4, in_chans=3, num_classes=1000,
 | 
						||
                 embed_dim=96, depths=[2, 2, 6, 2], num_heads=[3, 6, 12, 24],
 | 
						||
                 window_size=7, mlp_ratio=4., qkv_bias=True, qk_scale=None,
 | 
						||
                 drop_rate=0., attn_drop_rate=0., drop_path_rate=0.1,
 | 
						||
                 norm_layer=nn.LayerNorm, ape=False, patch_norm=True,
 | 
						||
                 use_checkpoint=False, **kwargs):
 | 
						||
        super().__init__()
 | 
						||
        self.num_classes    = num_classes
 | 
						||
        self.num_layers     = len(depths)
 | 
						||
        self.embed_dim      = embed_dim
 | 
						||
        self.ape            = ape
 | 
						||
        self.patch_norm     = patch_norm
 | 
						||
        self.num_features   = int(embed_dim * 2 ** (self.num_layers - 1))
 | 
						||
        self.mlp_ratio      = mlp_ratio
 | 
						||
        
 | 
						||
        #--------------------------------------------------#
 | 
						||
        #   bs, 224, 224, 3 -> bs, 3136, 96
 | 
						||
        #--------------------------------------------------#
 | 
						||
        self.patch_embed = PatchEmbed(
 | 
						||
            img_size    = img_size, 
 | 
						||
            patch_size  = patch_size,
 | 
						||
            in_chans    = in_chans, 
 | 
						||
            embed_dim   = embed_dim,
 | 
						||
            norm_layer  = norm_layer if self.patch_norm else None
 | 
						||
        )
 | 
						||
 | 
						||
        #--------------------------------------------------#
 | 
						||
        #   PatchEmbed之后的图像序列长度        3136
 | 
						||
        #   PatchEmbed之后的图像对应的分辨率    [56, 56]
 | 
						||
        #--------------------------------------------------#
 | 
						||
        num_patches             = self.patch_embed.num_patches
 | 
						||
        patches_resolution      = self.patch_embed.patches_resolution
 | 
						||
        self.patches_resolution = patches_resolution
 | 
						||
 | 
						||
        if self.ape:
 | 
						||
            self.absolute_pos_embed = nn.Parameter(torch.zeros(1, num_patches, embed_dim))
 | 
						||
            trunc_normal_(self.absolute_pos_embed, std=.02)
 | 
						||
 | 
						||
        self.pos_drop = nn.Dropout(p=drop_rate)
 | 
						||
 | 
						||
        #--------------------------------------------------#
 | 
						||
        #   stochastic depth
 | 
						||
        #--------------------------------------------------#
 | 
						||
        dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))]  # stochastic depth decay rule
 | 
						||
 | 
						||
        #---------------------------------------------------------------#
 | 
						||
        #   构建swin-transform的每个阶段
 | 
						||
        #   bs, 3136, 96 -> bs, 784, 192 -> bs, 196, 384 -> bs, 49, 768
 | 
						||
        #---------------------------------------------------------------#
 | 
						||
        self.layers = nn.ModuleList()
 | 
						||
        for i_layer in range(self.num_layers):
 | 
						||
            layer = BasicLayer(
 | 
						||
                dim                 = int(embed_dim * 2 ** i_layer),
 | 
						||
                input_resolution    = (patches_resolution[0] // (2 ** i_layer), patches_resolution[1] // (2 ** i_layer)),
 | 
						||
                depth               = depths[i_layer],
 | 
						||
                num_heads           = num_heads[i_layer],
 | 
						||
                window_size         = window_size,
 | 
						||
                mlp_ratio           = self.mlp_ratio,
 | 
						||
                qkv_bias            = qkv_bias, 
 | 
						||
                qk_scale            = qk_scale,
 | 
						||
                drop                = drop_rate, 
 | 
						||
                attn_drop           = attn_drop_rate,
 | 
						||
                drop_path           = dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])],
 | 
						||
                norm_layer          = norm_layer,
 | 
						||
                downsample          = PatchMerging if (i_layer < self.num_layers - 1) else None,
 | 
						||
                use_checkpoint      = use_checkpoint
 | 
						||
            )
 | 
						||
            self.layers.append(layer)
 | 
						||
 | 
						||
        self.apply(self._init_weights)
 | 
						||
 | 
						||
    def _init_weights(self, m):
 | 
						||
        if isinstance(m, nn.Linear):
 | 
						||
            trunc_normal_(m.weight, std=.02)
 | 
						||
            if isinstance(m, nn.Linear) and 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)
 | 
						||
 | 
						||
    @torch.jit.ignore
 | 
						||
    def no_weight_decay(self):
 | 
						||
        return {'absolute_pos_embed'}
 | 
						||
 | 
						||
    @torch.jit.ignore
 | 
						||
    def no_weight_decay_keywords(self):
 | 
						||
        return {'relative_position_bias_table'}
 | 
						||
 | 
						||
    def forward(self, x):
 | 
						||
        x = self.patch_embed(x)
 | 
						||
        if self.ape:
 | 
						||
            x = x + self.absolute_pos_embed
 | 
						||
        x = self.pos_drop(x)
 | 
						||
 | 
						||
        inverval_outs = []
 | 
						||
        for i, layer in enumerate(self.layers):
 | 
						||
            x_, x = layer(x)
 | 
						||
            if i != 0:
 | 
						||
                inverval_outs.append(x_)
 | 
						||
        
 | 
						||
        outs = []
 | 
						||
        for i, layer in enumerate(inverval_outs):
 | 
						||
            H, W    = (self.patches_resolution[0] // (2 ** (i + 1)), self.patches_resolution[1] // (2 ** (i + 1)))
 | 
						||
            B, L, C = layer.shape
 | 
						||
            layer   = layer.view([B, H, W, C]).permute([0, 3, 1, 2])
 | 
						||
            outs.append(layer)
 | 
						||
 | 
						||
        return outs
 | 
						||
    
 | 
						||
def Swin_transformer_Tiny(pretrained = False, input_shape = [640, 640], **kwargs):
 | 
						||
    model = SwinTransformer(input_shape, depths=[2, 2, 6, 2], **kwargs)
 | 
						||
    if pretrained:
 | 
						||
        url = "https://github.com/bubbliiiing/yolov5-pytorch/releases/download/v1.0/swin_tiny_patch4_window7.pth"
 | 
						||
        checkpoint = torch.hub.load_state_dict_from_url(url=url, map_location="cpu", model_dir="./model_data")
 | 
						||
        model.load_state_dict(checkpoint, strict=False)
 | 
						||
        print("Load weights from ", url.split('/')[-1])
 | 
						||
        
 | 
						||
    return model |