<reponame>LAZYzhaoyang/zhaoyangfirst_tianchi<gh_stars>0
import torch
import math
import torch.nn as nn
import torch.nn.functional as F

from einops import rearrange, repeat
from einops.layers.torch import Rearrange

from torch import einsum

#============================Basic Block============================#
class BasicConv(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding_mode='same', relu=True, bn=True, bias=False):
        super(BasicConv, self).__init__()
        self.in_channel = in_channels
        self.out_channel = out_channels
        if padding_mode == 'same':
            padding = (kernel_size-1) // 2
        else:
            padding = 0
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding, bias=bias)
        self.bn = nn.BatchNorm2d(out_channels,eps=1e-5, momentum=0.01, affine=True) if bn else None
        self.relu = nn.ReLU(inplace=True) if relu else None

    def forward(self, x):
        x = self.conv(x)
        if self.bn is not None:
            x = self.bn(x)
        if self.relu is not None:
            x = self.relu(x)
        return x
    
class Flatten(nn.Module):
    def __init__(self):
        super(Flatten, self).__init__()
    def forward(self, x):
        return x.view(x.size(0), -1)
    
class ConvPool(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=2):
        super(ConvPool, self).__init__()
        self.in_channel = in_channels
        self.out_channel = out_channels
        self.kernel_size = kernel_size
        self.stride = kernel_size
        self.padding = 0
        self.pool = BasicConv(in_channels=in_channels, out_channels=out_channels,kernel_size=kernel_size, stride=kernel_size, padding_mode='zero', relu=False, bn=False, bias=False)
        
    def forward(self, x):
        x = self.pool(x)
        return x

#============================Net Block============================#
class Spatial_Attention_Module(nn.Module):
    def __init__(self, channel):
        super(Spatial_Attention_Module, self).__init__()
        self.spatial_conv = BasicConv(in_channels=channel, out_channels=channel, kernel_size=1, relu=False)
        self.sigmoid = nn.Sigmoid()
        
    def forward(self, x):
        value = x
        attn = self.spatial_conv(x)
        scale = self.sigmoid(attn)
        out = value * scale
        
        return out

class Channel_Attention_Module(nn.Module):
    def __init__(self, in_channel, reduction=4):
        super(Channel_Attention_Module, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)  # 全局自适应池化
        self.max_pool = nn.AdaptiveMaxPool2d(1)
        self.fc1 = nn.Sequential(
            nn.Linear(in_channel, in_channel // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(in_channel // reduction, in_channel, bias=False),
        )
        self.fc2 = nn.Sequential(
            nn.Linear(in_channel, in_channel // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(in_channel // reduction, in_channel, bias=False),
        )
        self.sigmoid = nn.Sigmoid()
        
    def forward(self, x):
        b, c, _, _ = x.size()
        y1 = self.avg_pool(x).view(b, c) # squeeze操作
        y1 = self.fc1(y1).view(b, c, 1, 1) # FC获取通道注意力权重，是具有全局信息的
        y2 = self.max_pool(x).view(b, c)
        y2 = self.fc2(y2).view(b, c, 1, 1)
        y = y1 + y2
        y = self.sigmoid(y)
        out = x * y.expand_as(x)
        return  out 
    
class Non_Local_Module(nn.Module):
    def __init__(self, channel):
        super(Non_Local_Module, self).__init__()
        self.inter_channel = channel // 2
        self.conv_phi = nn.Conv2d(channel, self.inter_channel, kernel_size=1, stride=1,padding=0, bias=False)
        self.conv_theta = nn.Conv2d(channel, self.inter_channel, kernel_size=1, stride=1, padding=0, bias=False)
        self.conv_g = nn.Conv2d(channel, self.inter_channel, kernel_size=1, stride=1, padding=0, bias=False)
        self.softmax = nn.Softmax(dim=1)
        self.conv_mask = nn.Conv2d(self.inter_channel, channel, 1, 1, 0, bias=False)

    def forward(self, x):
        # [N, C, H , W]
        b, c, h, w = x.size()
        # 获取phi特征，维度为[N, C/2, H * W]，注意是要保留batch和通道维度的，是在HW上进行的
        x_phi = self.conv_phi(x).view(b, c, -1)
        # 获取theta特征，维度为[N, H * W, C/2]
        x_theta = self.conv_theta(x).view(b, c, -1).permute(0, 2, 1).contiguous()
        # 获取g特征，维度为[N, H * W, C/2]
        x_g = self.conv_g(x).view(b, c, -1).permute(0, 2, 1).contiguous()
        # 对phi和theta进行矩阵乘，[N, H * W, H * W]
        mul_theta_phi = torch.matmul(x_theta, x_phi)
        # softmax拉到0~1之间
        mul_theta_phi = self.softmax(mul_theta_phi)
        # 与g特征进行矩阵乘运算，[N, H * W, C/2]
        mul_theta_phi_g = torch.matmul(mul_theta_phi, x_g)
        # [N, C/2, H, W]
        mul_theta_phi_g = mul_theta_phi_g.permute(0, 2, 1).contiguous().view(b, self.inter_channel, h, w)
        # 1X1卷积扩充通道数
        mask = self.conv_mask(mul_theta_phi_g)
        out = mask + x # 残差连接
        return out
    
class Residual_Block(nn.Module):
    def __init__(self, channel):
        super(Residual_Block, self).__init__()
        self.convblock1 = BasicConv(in_channels=channel, out_channels=channel, bias=True)
        self.convblock2 = BasicConv(in_channels=channel,out_channels=channel, relu=False, bias=True)
        self.relu = nn.ReLU()
        
    def forward(self, x):
        residual = x
        
        out = self.convblock1(x)
        out = self.convblock2(out)
        out = out + residual
        out = self.relu(out)
        
        return out
    
class Up(nn.Module):
    """Upscaling then double conv"""

    def __init__(self, channels, scale_factor=2, bilinear=False):
        super(Up, self).__init__()
        # if bilinear, use the normal convolutions to reduce the number of channels
        self.bilinear = bilinear
        if bilinear:
            self.up = nn.Upsample(scale_factor=scale_factor, mode='bilinear', align_corners=True)
            self.conv = BasicConv(in_channels=channels, out_channels=channels//scale_factor, bias=False, bn=False)
        else:
            self.up = nn.ConvTranspose2d(channels, channels // scale_factor, kernel_size=scale_factor, stride=scale_factor)
            
    def forward(self, x):

        out = self.up(x)
        if self.bilinear:
            out = self.conv(out)
        return out

class SegHead(nn.Module):
    def __init__(self, in_channels, n_classes):
        super(SegHead, self).__init__()
        self.conv = BasicConv(in_channels=in_channels, out_channels=n_classes,relu=False, bn=False, bias=False)
        self.activation = nn.Softmax2d()

    def forward(self, x):
        out = self.conv(x)
        out = self.activation(out)

        return out


#============================Networks Block============================#
# backbone

class resnet_down(nn.Module):
    def __init__(self, in_channel, out_channel, down_factor=2):
        super(resnet_down, self).__init__()
        self.resnet = Residual_Block(channel=out_channel)
        self.down = ConvPool(in_channels=in_channel, out_channels=out_channel, kernel_size=down_factor)

    def forward(self, x):
        out = self.down(x)
        out = self.resnet(out)

        return out


class resnet_up(nn.Module):
    def __init__(self, in_channel, up_factor=2, bilinear=True):
        super(resnet_up, self).__init__()
        self.up = Up(channels=in_channel, scale_factor=up_factor, bilinear=bilinear)
        self.resnet = Residual_Block(channel= in_channel//up_factor)

    def forward(self, x):
        out = self.up(x)
        out = self.resnet(out)
        return out

class CBAM_Block(nn.Module):
    def __init__(self, in_channel, is_parallel=False, mode='channel_first'):
        super(CBAM_Block, self).__init__()
        self.spatial_att = Spatial_Attention_Module(channel=in_channel)
        self.channel_att = Channel_Attention_Module(in_channel=in_channel)
        self.is_parallel = is_parallel
        self.mode = mode

    def forward(self, x):
        if self.is_parallel:
            spa_att = self.spatial_att(x)
            channel_att = self.channel_att(x)
            out = torch.cat([spa_att, channel_att], dim=1)
        else:
            if self.mode == 'channel_first':
                att = self.channel_att(x)
                out = self.spatial_att(att)
            else:
                att = self.spatial_att(x)
                out = self.channel_att(att)
        return out

#============================Visual Transformer Block============================#

class Residual(nn.Module):
    def __init__(self, fn):
        super().__init__()
        self.fn = fn
    def forward(self, x, **kwargs):
        return self.fn(x, **kwargs) + x

class PreNorm(nn.Module):
    def __init__(self, dim, fn):
        super().__init__()
        self.norm = nn.LayerNorm(dim)
        self.fn = fn
    def forward(self, x, **kwargs):
        return self.fn(self.norm(x), **kwargs)

class FeedForward(nn.Module):
    def __init__(self, dim, hidden_dim, dropout = 0.):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim, hidden_dim),
            nn.GELU(),
            nn.Dropout(dropout),
            nn.Linear(hidden_dim, dim),
            nn.Dropout(dropout)
        )
    def forward(self, x):
        return self.net(x)

class Attention(nn.Module):
    def __init__(self, dim, heads = 8, dim_head = 64, dropout = 0.):
        super().__init__()
        inner_dim = dim_head *  heads
        self.heads = heads
        self.scale = dim_head ** -0.5

        self.to_qkv = nn.Linear(dim, inner_dim * 3, bias = False)
        self.to_out = nn.Sequential(
            nn.Linear(inner_dim, dim),
            nn.Dropout(dropout)
        )

    def forward(self, x, mask = None):
        b, n, _, h = *x.shape, self.heads
        qkv = self.to_qkv(x).chunk(3, dim = -1)
        q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = h), qkv)

        dots = einsum('b h i d, b h j d -> b h i j', q, k) * self.scale
        mask_value = -torch.finfo(dots.dtype).max

        if mask is not None:
            mask = F.pad(mask.flatten(1), (1, 0), value = True)
            assert mask.shape[-1] == dots.shape[-1], 'mask has incorrect dimensions'
            mask = rearrange(mask, 'b i -> b () i ()') * rearrange(mask, 'b j -> b () () j')
            dots.masked_fill_(~mask, mask_value)
            del mask

        attn = dots.softmax(dim=-1)

        out = einsum('b h i j, b h j d -> b h i d', attn, v)
        out = rearrange(out, 'b h n d -> b n (h d)')
        out =  self.to_out(out)
        return out

class Transformer(nn.Module):
    def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout = 0.):
        super().__init__()
        self.layers = nn.ModuleList([])
        for _ in range(depth):
            self.layers.append(nn.ModuleList([
                Residual(PreNorm(dim, Attention(dim, heads = heads, dim_head = dim_head, dropout = dropout))),
                Residual(PreNorm(dim, FeedForward(dim, mlp_dim, dropout = dropout)))
            ]))
    def forward(self, x, mask = None):
        for attn, ff in self.layers:
            x = attn(x, mask = mask)
            x = ff(x)
        return x

class ViT(nn.Module):
    def __init__(self, *, image_size, patch_size, num_classes, dim, depth, heads, mlp_dim, pool = 'cls', channels = 3, dim_head = 64, dropout = 0., emb_dropout = 0.):
        super().__init__()
        assert image_size % patch_size == 0, 'Image dimensions must be divisible by the patch size.'
        num_patches = (image_size // patch_size) ** 2
        patch_dim = channels * patch_size ** 2
        assert pool in {'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)'

        self.to_patch_embedding = nn.Sequential(
            Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_size, p2 = patch_size),
            nn.Linear(patch_dim, dim),
        )

        self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim))
        self.cls_token = nn.Parameter(torch.randn(1, 1, dim))
        self.dropout = nn.Dropout(emb_dropout)

        self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout)

        self.pool = pool
        self.to_latent = nn.Identity()

        self.mlp_head = nn.Sequential(
            nn.LayerNorm(dim),
            nn.Linear(dim, num_classes)
        )

    def forward(self, img, mask = None):
        x = self.to_patch_embedding(img)
        b, n, _ = x.shape

        cls_tokens = repeat(self.cls_token, '() n d -> b n d', b = b)
        x = torch.cat((cls_tokens, x), dim=1)
        x += self.pos_embedding[:, :(n + 1)]
        x = self.dropout(x)

        x = self.transformer(x, mask)

        x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0]

        x = self.to_latent(x)
        return self.mlp_head(x)





import torch
import math
import torch.nn as nn
import torch.nn.functional as F

from einops import rearrange, repeat
from einops.layers.torch import Rearrange

from torch import einsum

#============================Basic Block============================#
class BasicConv(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding_mode='same', relu=True, bn=True, bias=False):
        super(BasicConv, self).__init__()
        self.in_channel = in_channels
        self.out_channel = out_channels
        if padding_mode == 'same':
            padding = (kernel_size-1) // 2
        else:
            padding = 0
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding, bias=bias)
        self.bn = nn.BatchNorm2d(out_channels,eps=1e-5, momentum=0.01, affine=True) if bn else None
        self.relu = nn.ReLU(inplace=True) if relu else None

    def forward(self, x):
        x = self.conv(x)
        if self.bn is not None:
            x = self.bn(x)
        if self.relu is not None:
            x = self.relu(x)
        return x
    
class Flatten(nn.Module):
    def __init__(self):
        super(Flatten, self).__init__()
    def forward(self, x):
        return x.view(x.size(0), -1)
    
class ConvPool(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=2):
        super(ConvPool, self).__init__()
        self.in_channel = in_channels
        self.out_channel = out_channels
        self.kernel_size = kernel_size
        self.stride = kernel_size
        self.padding = 0
        self.pool = BasicConv(in_channels=in_channels, out_channels=out_channels,kernel_size=kernel_size, stride=kernel_size, padding_mode='zero', relu=False, bn=False, bias=False)
        
    def forward(self, x):
        x = self.pool(x)
        return x

#============================Net Block============================#
class Spatial_Attention_Module(nn.Module):
    def __init__(self, channel):
        super(Spatial_Attention_Module, self).__init__()
        self.spatial_conv = BasicConv(in_channels=channel, out_channels=channel, kernel_size=1, relu=False)
        self.sigmoid = nn.Sigmoid()
        
    def forward(self, x):
        value = x
        attn = self.spatial_conv(x)
        scale = self.sigmoid(attn)
        out = value * scale
        
        return out

class Channel_Attention_Module(nn.Module):
    def __init__(self, in_channel, reduction=4):
        super(Channel_Attention_Module, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)  # 全局自适应池化
        self.max_pool = nn.AdaptiveMaxPool2d(1)
        self.fc1 = nn.Sequential(
            nn.Linear(in_channel, in_channel // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(in_channel // reduction, in_channel, bias=False),
        )
        self.fc2 = nn.Sequential(
            nn.Linear(in_channel, in_channel // reduction, bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(in_channel // reduction, in_channel, bias=False),
        )
        self.sigmoid = nn.Sigmoid()
        
    def forward(self, x):
        b, c, _, _ = x.size()
        y1 = self.avg_pool(x).view(b, c) # squeeze操作
        y1 = self.fc1(y1).view(b, c, 1, 1) # FC获取通道注意力权重，是具有全局信息的
        y2 = self.max_pool(x).view(b, c)
        y2 = self.fc2(y2).view(b, c, 1, 1)
        y = y1 + y2
        y = self.sigmoid(y)
        out = x * y.expand_as(x)
        return  out 
    
class Non_Local_Module(nn.Module):
    def __init__(self, channel):
        super(Non_Local_Module, self).__init__()
        self.inter_channel = channel // 2
        self.conv_phi = nn.Conv2d(channel, self.inter_channel, kernel_size=1, stride=1,padding=0, bias=False)
        self.conv_theta = nn.Conv2d(channel, self.inter_channel, kernel_size=1, stride=1, padding=0, bias=False)
        self.conv_g = nn.Conv2d(channel, self.inter_channel, kernel_size=1, stride=1, padding=0, bias=False)
        self.softmax = nn.Softmax(dim=1)
        self.conv_mask = nn.Conv2d(self.inter_channel, channel, 1, 1, 0, bias=False)

    def forward(self, x):
        # [N, C, H , W]
        b, c, h, w = x.size()
        # 获取phi特征，维度为[N, C/2, H * W]，注意是要保留batch和通道维度的，是在HW上进行的
        x_phi = self.conv_phi(x).view(b, c, -1)
        # 获取theta特征，维度为[N, H * W, C/2]
        x_theta = self.conv_theta(x).view(b, c, -1).permute(0, 2, 1).contiguous()
        # 获取g特征，维度为[N, H * W, C/2]
        x_g = self.conv_g(x).view(b, c, -1).permute(0, 2, 1).contiguous()
        # 对phi和theta进行矩阵乘，[N, H * W, H * W]
        mul_theta_phi = torch.matmul(x_theta, x_phi)
        # softmax拉到0~1之间
        mul_theta_phi = self.softmax(mul_theta_phi)
        # 与g特征进行矩阵乘运算，[N, H * W, C/2]
        mul_theta_phi_g = torch.matmul(mul_theta_phi, x_g)
        # [N, C/2, H, W]
        mul_theta_phi_g = mul_theta_phi_g.permute(0, 2, 1).contiguous().view(b, self.inter_channel, h, w)
        # 1X1卷积扩充通道数
        mask = self.conv_mask(mul_theta_phi_g)
        out = mask + x # 残差连接
        return out
    
class Residual_Block(nn.Module):
    def __init__(self, channel):
        super(Residual_Block, self).__init__()
        self.convblock1 = BasicConv(in_channels=channel, out_channels=channel, bias=True)
        self.convblock2 = BasicConv(in_channels=channel,out_channels=channel, relu=False, bias=True)
        self.relu = nn.ReLU()
        
    def forward(self, x):
        residual = x
        
        out = self.convblock1(x)
        out = self.convblock2(out)
        out = out + residual
        out = self.relu(out)
        
        return out
    
class Up(nn.Module):
    """Upscaling then double conv"""

    def __init__(self, channels, scale_factor=2, bilinear=False):
        super(Up, self).__init__()
        # if bilinear, use the normal convolutions to reduce the number of channels
        self.bilinear = bilinear
        if bilinear:
            self.up = nn.Upsample(scale_factor=scale_factor, mode='bilinear', align_corners=True)
            self.conv = BasicConv(in_channels=channels, out_channels=channels//scale_factor, bias=False, bn=False)
        else:
            self.up = nn.ConvTranspose2d(channels, channels // scale_factor, kernel_size=scale_factor, stride=scale_factor)
            
    def forward(self, x):

        out = self.up(x)
        if self.bilinear:
            out = self.conv(out)
        return out

class SegHead(nn.Module):
    def __init__(self, in_channels, n_classes):
        super(SegHead, self).__init__()
        self.conv = BasicConv(in_channels=in_channels, out_channels=n_classes,relu=False, bn=False, bias=False)
        self.activation = nn.Softmax2d()

    def forward(self, x):
        out = self.conv(x)
        out = self.activation(out)

        return out


#============================Networks Block============================#
# backbone

class resnet_down(nn.Module):
    def __init__(self, in_channel, out_channel, down_factor=2):
        super(resnet_down, self).__init__()
        self.resnet = Residual_Block(channel=out_channel)
        self.down = ConvPool(in_channels=in_channel, out_channels=out_channel, kernel_size=down_factor)

    def forward(self, x):
        out = self.down(x)
        out = self.resnet(out)

        return out


class resnet_up(nn.Module):
    def __init__(self, in_channel, up_factor=2, bilinear=True):
        super(resnet_up, self).__init__()
        self.up = Up(channels=in_channel, scale_factor=up_factor, bilinear=bilinear)
        self.resnet = Residual_Block(channel= in_channel//up_factor)

    def forward(self, x):
        out = self.up(x)
        out = self.resnet(out)
        return out

class CBAM_Block(nn.Module):
    def __init__(self, in_channel, is_parallel=False, mode='channel_first'):
        super(CBAM_Block, self).__init__()
        self.spatial_att = Spatial_Attention_Module(channel=in_channel)
        self.channel_att = Channel_Attention_Module(in_channel=in_channel)
        self.is_parallel = is_parallel
        self.mode = mode

    def forward(self, x):
        if self.is_parallel:
            spa_att = self.spatial_att(x)
            channel_att = self.channel_att(x)
            out = torch.cat([spa_att, channel_att], dim=1)
        else:
            if self.mode == 'channel_first':
                att = self.channel_att(x)
                out = self.spatial_att(att)
            else:
                att = self.spatial_att(x)
                out = self.channel_att(att)
        return out

#============================Visual Transformer Block============================#

class Residual(nn.Module):
    def __init__(self, fn):
        super().__init__()
        self.fn = fn
    def forward(self, x, **kwargs):
        return self.fn(x, **kwargs) + x

class PreNorm(nn.Module):
    def __init__(self, dim, fn):
        super().__init__()
        self.norm = nn.LayerNorm(dim)
        self.fn = fn
    def forward(self, x, **kwargs):
        return self.fn(self.norm(x), **kwargs)

class FeedForward(nn.Module):
    def __init__(self, dim, hidden_dim, dropout = 0.):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim, hidden_dim),
            nn.GELU(),
            nn.Dropout(dropout),
            nn.Linear(hidden_dim, dim),
            nn.Dropout(dropout)
        )
    def forward(self, x):
        return self.net(x)

class Attention(nn.Module):
    def __init__(self, dim, heads = 8, dim_head = 64, dropout = 0.):
        super().__init__()
        inner_dim = dim_head *  heads
        self.heads = heads
        self.scale = dim_head ** -0.5

        self.to_qkv = nn.Linear(dim, inner_dim * 3, bias = False)
        self.to_out = nn.Sequential(
            nn.Linear(inner_dim, dim),
            nn.Dropout(dropout)
        )

    def forward(self, x, mask = None):
        b, n, _, h = *x.shape, self.heads
        qkv = self.to_qkv(x).chunk(3, dim = -1)
        q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = h), qkv)

        dots = einsum('b h i d, b h j d -> b h i j', q, k) * self.scale
        mask_value = -torch.finfo(dots.dtype).max

        if mask is not None:
            mask = F.pad(mask.flatten(1), (1, 0), value = True)
            assert mask.shape[-1] == dots.shape[-1], 'mask has incorrect dimensions'
            mask = rearrange(mask, 'b i -> b () i ()') * rearrange(mask, 'b j -> b () () j')
            dots.masked_fill_(~mask, mask_value)
            del mask

        attn = dots.softmax(dim=-1)

        out = einsum('b h i j, b h j d -> b h i d', attn, v)
        out = rearrange(out, 'b h n d -> b n (h d)')
        out =  self.to_out(out)
        return out

class Transformer(nn.Module):
    def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout = 0.):
        super().__init__()
        self.layers = nn.ModuleList([])
        for _ in range(depth):
            self.layers.append(nn.ModuleList([
                Residual(PreNorm(dim, Attention(dim, heads = heads, dim_head = dim_head, dropout = dropout))),
                Residual(PreNorm(dim, FeedForward(dim, mlp_dim, dropout = dropout)))
            ]))
    def forward(self, x, mask = None):
        for attn, ff in self.layers:
            x = attn(x, mask = mask)
            x = ff(x)
        return x

class ViT(nn.Module):
    def __init__(self, *, image_size, patch_size, num_classes, dim, depth, heads, mlp_dim, pool = 'cls', channels = 3, dim_head = 64, dropout = 0., emb_dropout = 0.):
        super().__init__()
        assert image_size % patch_size == 0, 'Image dimensions must be divisible by the patch size.'
        num_patches = (image_size // patch_size) ** 2
        patch_dim = channels * patch_size ** 2
        assert pool in {'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)'

        self.to_patch_embedding = nn.Sequential(
            Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_size, p2 = patch_size),
            nn.Linear(patch_dim, dim),
        )

        self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim))
        self.cls_token = nn.Parameter(torch.randn(1, 1, dim))
        self.dropout = nn.Dropout(emb_dropout)

        self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout)

        self.pool = pool
        self.to_latent = nn.Identity()

        self.mlp_head = nn.Sequential(
            nn.LayerNorm(dim),
            nn.Linear(dim, num_classes)
        )

    def forward(self, img, mask = None):
        x = self.to_patch_embedding(img)
        b, n, _ = x.shape

        cls_tokens = repeat(self.cls_token, '() n d -> b n d', b = b)
        x = torch.cat((cls_tokens, x), dim=1)
        x += self.pos_embedding[:, :(n + 1)]
        x = self.dropout(x)

        x = self.transformer(x, mask)

        x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0]

        x = self.to_latent(x)
        return self.mlp_head(x)
#============================Basic Block============================# #============================Net Block============================# # 全局自适应池化 # squeeze操作 # FC获取通道注意力权重，是具有全局信息的 # [N, C, H , W] # 获取phi特征，维度为[N, C/2, H * W]，注意是要保留batch和通道维度的，是在HW上进行的 # 获取theta特征，维度为[N, H * W, C/2] # 获取g特征，维度为[N, H * W, C/2] # 对phi和theta进行矩阵乘，[N, H * W, H * W] # softmax拉到0~1之间 # 与g特征进行矩阵乘运算，[N, H * W, C/2] # [N, C/2, H, W] # 1X1卷积扩充通道数 # 残差连接 Upscaling then double conv # if bilinear, use the normal convolutions to reduce the number of channels #============================Networks Block============================# # backbone #============================Visual Transformer Block============================#