""" ART layer in "ICLR 2023: ACCURATE IMAGE RESTORATION WITH ATTENTION RETRACTABLE TRANSFORMER". """ import torch import torch.nn as nn import torch.nn.functional as F from timm.models.layers import DropPath, to_2tuple, trunc_normal_ from pdb import set_trace as stx import numbers from einops import rearrange import math NEG_INF = -1000000 ########################################################################## class Mlp(nn.Module): def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.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 class DynamicPosBias(nn.Module): def __init__(self, dim, num_heads): super().__init__() self.num_heads = num_heads self.pos_dim = dim // 4 self.pos_proj = nn.Linear(2, self.pos_dim) self.pos1 = nn.Sequential( nn.LayerNorm(self.pos_dim), nn.ReLU(inplace=True), nn.Linear(self.pos_dim, self.pos_dim), ) self.pos2 = nn.Sequential( nn.LayerNorm(self.pos_dim), nn.ReLU(inplace=True), nn.Linear(self.pos_dim, self.pos_dim) ) self.pos3 = nn.Sequential( nn.LayerNorm(self.pos_dim), nn.ReLU(inplace=True), nn.Linear(self.pos_dim, self.num_heads) ) def forward(self, biases): pos = self.pos3(self.pos2(self.pos1(self.pos_proj(biases)))) return pos def flops(self, N): flops = N * 2 * self.pos_dim flops += N * self.pos_dim * self.pos_dim flops += N * self.pos_dim * self.pos_dim flops += N * self.pos_dim * self.num_heads return flops ######################################### class Attention(nn.Module): r""" Multi-head self attention module with dynamic position bias. Args: dim (int): Number of input channels. num_heads (int): Number of attention heads. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0 proj_drop (float, optional): Dropout ratio of output. Default: 0.0 """ def __init__(self, dim, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0., position_bias=True): super().__init__() self.dim = dim self.num_heads = num_heads head_dim = dim // num_heads self.scale = qk_scale or head_dim ** -0.5 self.position_bias = position_bias if self.position_bias: self.pos = DynamicPosBias(self.dim // 4, self.num_heads) 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) self.softmax = nn.Softmax(dim=-1) def forward(self, x, H, W, mask=None): """ Args: x: input features with shape of (num_groups*B, N, C) mask: (0/-inf) mask with shape of (num_groups, Gh*Gw, Gh*Gw) or None H: height of each group W: width of each group """ # print("input to the Attention layer:", x.max(), x.min()) group_size = (H, W) B_, N, C = x.shape assert H * W == N qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4).contiguous() q, k, v = qkv[0], qkv[1], qkv[2] # print("q range:", q.max(), q.min()) # print("k range:", k.max(), k.min()) q = q * self.scale attn = q @ k.transpose(-2, -1).contiguous() # (B_, self.num_heads, N, N), N = H*W # print("attention matrix:", attn.shape, attn) if self.position_bias: # generate mother-set position_bias_h = torch.arange(1 - group_size[0], group_size[0], device=attn.device) position_bias_w = torch.arange(1 - group_size[1], group_size[1], device=attn.device) biases = torch.stack(torch.meshgrid([position_bias_h, position_bias_w])) # 2, 2Gh-1, 2W2-1 biases = biases.flatten(1).transpose(0, 1).contiguous().float() # (2h-1)*(2w-1) 2 # get pair-wise relative position index for each token inside the window coords_h = torch.arange(group_size[0], device=attn.device) coords_w = torch.arange(group_size[1], device=attn.device) coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Gh, Gw coords_flatten = torch.flatten(coords, 1) # 2, Gh*Gw relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Gh*Gw, Gh*Gw relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Gh*Gw, Gh*Gw, 2 relative_coords[:, :, 0] += group_size[0] - 1 # shift to start from 0 relative_coords[:, :, 1] += group_size[1] - 1 relative_coords[:, :, 0] *= 2 * group_size[1] - 1 relative_position_index = relative_coords.sum(-1) # Gh*Gw, Gh*Gw pos = self.pos(biases) # 2Gh-1 * 2Gw-1, heads # select position bias relative_position_bias = pos[relative_position_index.view(-1)].view( group_size[0] * group_size[1], group_size[0] * group_size[1], -1) # Gh*Gw,Gh*Gw,nH relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Gh*Gw, Gh*Gw attn = attn + relative_position_bias.unsqueeze(0) if mask is not None: nP = mask.shape[0] attn = attn.view(B_ // nP, nP, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0) # (B, nP, nHead, N, N) attn = attn.view(-1, self.num_heads, N, N) attn = self.softmax(attn) else: attn = self.softmax(attn) attn = self.attn_drop(attn) x = (attn @ v).transpose(1, 2).reshape(B_, N, C) # print("feature before proj-layer:", x.max(), x.min()) x = self.proj(x) # print("feature after proj-layer:", x.max(), x.min()) x = self.proj_drop(x) return x ########################################################################## class TransformerBlock(nn.Module): r""" ART Transformer Block. Args: dim (int): Number of input channels. num_heads (int): Number of attention heads. window_size: window size of dense attention interval: interval size of sparse attention ds_flag (int): use Dense Attention or Sparse Attention, 0 for DAB and 1 for SAB. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. drop (float, optional): Dropout rate. Default: 0.0 attn_drop (float, optional): Attention dropout rate. Default: 0.0 drop_path (float, optional): Stochastic depth rate. Default: 0.0 # act_layer (nn.Module, optional): Activation layer. Default: nn.GELU norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm """ def __init__(self, dim, num_heads, window_size=7, interval=8, ds_flag=0, mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm, pre_norm=True): super().__init__() self.dim = dim self.num_heads = num_heads self.window_size = window_size self.interval = interval self.ds_flag = ds_flag self.mlp_ratio = mlp_ratio # self.conv = nn.Conv2d(dim, dim, kernel_size=1) self.norm1 = norm_layer(dim) self.attn = Attention( dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop, position_bias=True) # self.conv = ConvBlock(self.dim, self.dim, 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) # self.bn_norm = nn.BatchNorm2d(dim) self.pre_norm = pre_norm def forward(self, x, x_size): H, W = x_size B, L, C = x.shape assert L == H * W, "input feature has wrong size %d, %d, %d" % (L, H, W) if min(H, W) <= self.window_size: # if window size is larger than input resolution, we don't partition windows self.ds_flag = 0 self.window_size = min(H, W) # x = self.conv(x) shortcut = x if self.pre_norm: # print("using pre_norm") x = self.norm1(x) ## 归一化之后特征范围变得非常小 x = x.view(B, H, W, C) # print("normalized input range:", x.max(), x.min(), x.mean()) # padding size_par = self.interval if self.ds_flag == 1 else self.window_size pad_l = pad_t = 0 pad_r = (size_par - W % size_par) % size_par pad_b = (size_par - H % size_par) % size_par x = F.pad(x, (0, 0, pad_l, pad_r, pad_t, pad_b)) _, Hd, Wd, _ = x.shape mask = torch.zeros((1, Hd, Wd, 1), device=x.device) if pad_b > 0: mask[:, -pad_b:, :, :] = -1 if pad_r > 0: mask[:, :, -pad_r:, :] = -1 # print("pad_b and pad_r:", pad_b, pad_r) # partition the whole feature map into several groups if self.ds_flag == 0: # Dense Attention G = Gh = Gw = self.window_size x = x.reshape(B, Hd // G, G, Wd // G, G, C).permute(0, 1, 3, 2, 4, 5).contiguous() x = x.reshape(B * Hd * Wd // G ** 2, G ** 2, C) nP = Hd * Wd // G ** 2 # number of partitioning groups # attn_mask if pad_r > 0 or pad_b > 0: mask = mask.reshape(1, Hd // G, G, Wd // G, G, 1).permute(0, 1, 3, 2, 4, 5).contiguous() mask = mask.reshape(nP, 1, G * G) attn_mask = torch.zeros((nP, G * G, G * G), device=x.device) attn_mask = attn_mask.masked_fill(mask < 0, NEG_INF) else: attn_mask = None if self.ds_flag == 1: # Sparse Attention I, Gh, Gw = self.interval, Hd // self.interval, Wd // self.interval x = x.reshape(B, Gh, I, Gw, I, C).permute(0, 2, 4, 1, 3, 5).contiguous() x = x.reshape(B * I * I, Gh * Gw, C) nP = I ** 2 # number of partitioning groups # attn_mask if pad_r > 0 or pad_b > 0: mask = mask.reshape(1, Gh, I, Gw, I, 1).permute(0, 2, 4, 1, 3, 5).contiguous() mask = mask.reshape(nP, 1, Gh * Gw) attn_mask = torch.zeros((nP, Gh * Gw, Gh * Gw), device=x.device) attn_mask = attn_mask.masked_fill(mask < 0, NEG_INF) else: attn_mask = None # MSA # print("attn mask:", attn_mask) x = self.attn(x, Gh, Gw, mask=attn_mask) # nP*B, Gh*Gw, C # merge embeddings if self.ds_flag == 0: x = x.reshape(B, Hd // G, Wd // G, G, G, C).permute(0, 1, 3, 2, 4, 5).contiguous() # B, Hd//G, G, Wd//G, G, C else: x = x.reshape(B, I, I, Gh, Gw, C).permute(0, 3, 1, 4, 2, 5).contiguous() # B, Gh, I, Gw, I, C x = x.reshape(B, Hd, Wd, C) # print("range of feature before layer:", x.max(), x.min(), x.mean()) # x = self.conv(x.permute(0, 3, 1, 2)).permute(0, 2, 3, 1) # x_bn = self.bn_norm(x.permute(0, 3, 1, 2)) # remove padding if pad_r > 0 or pad_b > 0: x = x[:, :H, :W, :].contiguous() x = x.view(B, H * W, C) # FFN # print("[ART layer] input and output feature difference:", torch.mean(torch.abs(shortcut - x))) # print("range of feature before ART layer:", shortcut.max(), shortcut.min(), shortcut.mean()) # print("range of feature after ART layer:", x.max(), x.min(), x.mean()) if self.pre_norm: x = shortcut + self.drop_path(x) x = x + self.drop_path(self.mlp(self.norm2(x))) else: # print("using post_norm") x = self.norm1(shortcut + self.drop_path(x)) x = self.norm2(x + self.drop_path(self.mlp(x))) # x = shortcut + self.drop_path(x) # x = x + self.drop_path(self.mlp(x)) # print("range of fused feature:", x.max(), x.min()) # print("x:", x.shape) return x def extra_repr(self) -> str: return f"dim={self.dim}, num_heads={self.num_heads}, " \ f"window_size={self.window_size}, ds_flag={self.ds_flag}, mlp_ratio={self.mlp_ratio}" class ConvBlock(nn.Module): """ A Convolutional Block that consists of two convolution layers each followed by instance normalization, LeakyReLU activation and dropout. """ def __init__(self, in_chans: int, out_chans: int, drop_prob: float): """ Args: in_chans: Number of channels in the input. out_chans: Number of channels in the output. drop_prob: Dropout probability. """ super().__init__() self.in_chans = in_chans self.out_chans = out_chans self.drop_prob = drop_prob self.layers = nn.Sequential( nn.Conv2d(in_chans, out_chans, kernel_size=3, padding=1, bias=False), nn.InstanceNorm2d(out_chans), nn.LeakyReLU(negative_slope=0.2, inplace=True), nn.Dropout2d(drop_prob), nn.Conv2d(out_chans, out_chans, kernel_size=3, padding=1, bias=False), nn.InstanceNorm2d(out_chans), nn.LeakyReLU(negative_slope=0.2, inplace=True), nn.Dropout2d(drop_prob), ) def forward(self, image: torch.Tensor) -> torch.Tensor: return self.layers(image) ########################################################################## class Cross_TransformerBlock(nn.Module): r""" ART Transformer Block. Args: dim (int): Number of input channels. num_heads (int): Number of attention heads. window_size: window size of dense attention interval: interval size of sparse attention ds_flag (int): use Dense Attention or Sparse Attention, 0 for DAB and 1 for SAB. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. drop (float, optional): Dropout rate. Default: 0.0 attn_drop (float, optional): Attention dropout rate. Default: 0.0 drop_path (float, optional): Stochastic depth rate. Default: 0.0 # act_layer (nn.Module, optional): Activation layer. Default: nn.GELU norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm """ def __init__(self, dim, num_heads, window_size=7, interval=8, ds_flag=0, mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm): super().__init__() self.dim = dim self.num_heads = num_heads self.window_size = window_size self.interval = interval self.ds_flag = ds_flag self.mlp_ratio = mlp_ratio self.norm1 = norm_layer(dim) self.attn = Attention( dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop, position_bias=True) 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_x = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) self.mlp_y = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) def forward(self, x, y, x_size): """ x, y: feature maps of two modalities. They are from the same level with same feature size. """ H, W = x_size B, L, C = x.shape assert L == H * W, "input feature has wrong size %d, %d, %d" % (L, H, W) if min(H, W) <= self.window_size: # if window size is larger than input resolution, we don't partition windows self.ds_flag = 0 self.window_size = min(H, W) shortcut_x = x x = self.norm1(x) x = x.view(B, H, W, C) shortcut_y = y y = self.norm1(y) y = y.view(B, H, W, C) # padding size_par = self.interval if self.ds_flag == 1 else self.window_size pad_l = pad_t = 0 pad_r = (size_par - W % size_par) % size_par pad_b = (size_par - H % size_par) % size_par x = F.pad(x, (0, 0, pad_l, pad_r, pad_t, pad_b)) y = F.pad(y, (0, 0, pad_l, pad_r, pad_t, pad_b)) _, Hd, Wd, _ = x.shape mask = torch.zeros((1, Hd, Wd, 1), device=x.device) if pad_b > 0: mask[:, -pad_b:, :, :] = -1 if pad_r > 0: mask[:, :, -pad_r:, :] = -1 # print("pad_b and pad_r:", pad_b, pad_r) # partition the whole feature map into several groups if self.ds_flag == 0: # Dense Attention G = Gh = Gw = self.window_size x = x.reshape(B, Hd // G, G, Wd // G, G, C).permute(0, 1, 3, 2, 4, 5).contiguous() x = x.reshape(B * Hd * Wd // G ** 2, G ** 2, C) y = y.reshape(B, Hd // G, G, Wd // G, G, C).permute(0, 1, 3, 2, 4, 5).contiguous() y = y.reshape(B * Hd * Wd // G ** 2, G ** 2, C) nP = Hd * Wd // G ** 2 # number of partitioning groups # attn_mask if pad_r > 0 or pad_b > 0: mask = mask.reshape(1, Hd // G, G, Wd // G, G, 1).permute(0, 1, 3, 2, 4, 5).contiguous() mask = mask.reshape(nP, 1, G * G) attn_mask = torch.zeros((nP, G * G, G * G), device=x.device) attn_mask = attn_mask.masked_fill(mask < 0, NEG_INF) else: attn_mask = None if self.ds_flag == 1: # Sparse Attention I, Gh, Gw = self.interval, Hd // self.interval, Wd // self.interval x = x.reshape(B, Gh, I, Gw, I, C).permute(0, 2, 4, 1, 3, 5).contiguous() x = x.reshape(B * I * I, Gh * Gw, C) y = y.reshape(B, Gh, I, Gw, I, C).permute(0, 2, 4, 1, 3, 5).contiguous() y = y.reshape(B * I * I, Gh * Gw, C) nP = I ** 2 # number of partitioning groups # attn_mask if pad_r > 0 or pad_b > 0: mask = mask.reshape(1, Gh, I, Gw, I, 1).permute(0, 2, 4, 1, 3, 5).contiguous() mask = mask.reshape(nP, 1, Gh * Gw) attn_mask = torch.zeros((nP, Gh * Gw, Gh * Gw), device=x.device) attn_mask = attn_mask.masked_fill(mask < 0, NEG_INF) else: attn_mask = None # Inside each window, fuse the x and y, then compute self-attention. xy = torch.cat((x, y), 1) xy = self.attn(xy, Gh, 2*Gw, mask=attn_mask) # nP*B, Gh*2Gw, C # print("fused xy:", xy.shape) # merge embeddings if self.ds_flag == 0: x = xy[:, :xy.shape[1]//2, :].reshape(B, Hd // G, Wd // G, G, G, C).permute(0, 1, 3, 2, 4, 5).contiguous() # B, Hd//G, G, Wd//G, G, C y = xy[:, xy.shape[1]//2:, :].reshape(B, Hd // G, Wd // G, G, G, C).permute(0, 1, 3, 2, 4, 5).contiguous() # B, Hd//G, G, Wd//G, G, C else: x = xy[:, :xy.shape[1]//2, :].reshape(B, I, I, Gh, Gw, C).permute(0, 3, 1, 4, 2, 5).contiguous() # B, Gh, I, Gw, I, C y = xy[:, xy.shape[1]//2:, :].reshape(B, I, I, Gh, Gw, C).permute(0, 3, 1, 4, 2, 5).contiguous() # B, Gh, I, Gw, I, C x = x.reshape(B, Hd, Wd, C) y = y.reshape(B, Hd, Wd, C) # remove padding if pad_r > 0 or pad_b > 0: x = x[:, :H, :W, :].contiguous() y = y[:, :H, :W, :].contiguous() x = x.view(B, H * W, C) y = y.view(B, H * W, C) # FFN x = shortcut_x + self.drop_path(x) x = x + self.drop_path(self.mlp_x(self.norm2(x))) # print("x:", x.shape) y = shortcut_y + self.drop_path(y) y = y + self.drop_path(self.mlp_y(self.norm2(y))) return x, y def extra_repr(self) -> str: return f"dim={self.dim}, num_heads={self.num_heads}, " \ f"window_size={self.window_size}, ds_flag={self.ds_flag}, mlp_ratio={self.mlp_ratio}" ########################################################################## class Cross_TransformerBlock_v2(nn.Module): r""" ART Transformer Block. 将两个模态的特征沿着channel方向concat, 一起取window. 之后把取出来的两个模态中同一个window的所有patches合并,送到transformer处理。 Args: dim (int): Number of input channels. num_heads (int): Number of attention heads. window_size: window size of dense attention interval: interval size of sparse attention ds_flag (int): use Dense Attention or Sparse Attention, 0 for DAB and 1 for SAB. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. drop (float, optional): Dropout rate. Default: 0.0 attn_drop (float, optional): Attention dropout rate. Default: 0.0 drop_path (float, optional): Stochastic depth rate. Default: 0.0 # act_layer (nn.Module, optional): Activation layer. Default: nn.GELU norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm """ def __init__(self, dim, num_heads, window_size=7, interval=8, ds_flag=0, mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm): super().__init__() self.dim = dim self.num_heads = num_heads self.window_size = window_size self.interval = interval self.ds_flag = ds_flag self.mlp_ratio = mlp_ratio self.norm1 = norm_layer(dim) self.attn = Attention( dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop, position_bias=True) 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_x = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) self.mlp_y = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) def forward(self, x, y, x_size): """ x, y: feature maps of two modalities. They are from the same level with same feature size. """ H, W = x_size B, L, C = x.shape assert L == H * W, "input feature has wrong size %d, %d, %d" % (L, H, W) if min(H, W) <= self.window_size: # if window size is larger than input resolution, we don't partition windows self.ds_flag = 0 self.window_size = min(H, W) shortcut_x = x x = self.norm1(x) x = x.view(B, H, W, C) shortcut_y = y y = self.norm1(y) y = y.view(B, H, W, C) # padding xy = torch.cat((x, y), -1) size_par = self.interval if self.ds_flag == 1 else self.window_size pad_l = pad_t = 0 pad_r = (size_par - W % size_par) % size_par pad_b = (size_par - H % size_par) % size_par xy = F.pad(xy, (0, 0, pad_l, pad_r, pad_t, pad_b)) _, Hd, Wd, _ = x.shape mask = torch.zeros((1, Hd, Wd, 1), device=x.device) if pad_b > 0: mask[:, -pad_b:, :, :] = -1 if pad_r > 0: mask[:, :, -pad_r:, :] = -1 # print("pad_b and pad_r:", pad_b, pad_r) # partition the whole feature map into several groups if self.ds_flag == 0: # Dense Attention G = Gh = Gw = self.window_size xy = xy.reshape(B, Hd // G, G, Wd // G, G, 2*C).permute(0, 1, 3, 2, 4, 5).contiguous() xy = xy.reshape(B * Hd * Wd // G ** 2, G ** 2, 2*C) nP = Hd * Wd // G ** 2 # number of partitioning groups # attn_mask if pad_r > 0 or pad_b > 0: mask = mask.reshape(1, Hd // G, G, Wd // G, G, 1).permute(0, 1, 3, 2, 4, 5).contiguous() mask = mask.reshape(nP, 1, G * G) attn_mask = torch.zeros((nP, G * G, G * G), device=x.device) attn_mask = attn_mask.masked_fill(mask < 0, NEG_INF) else: attn_mask = None if self.ds_flag == 1: # Sparse Attention I, Gh, Gw = self.interval, Hd // self.interval, Wd // self.interval xy = xy.reshape(B, Gh, I, Gw, I, 2*C).permute(0, 2, 4, 1, 3, 5).contiguous() xy = xy.reshape(B * I * I, Gh * Gw, 2*C) nP = I ** 2 # number of partitioning groups # attn_mask if pad_r > 0 or pad_b > 0: mask = mask.reshape(1, Gh, I, Gw, I, 1).permute(0, 2, 4, 1, 3, 5).contiguous() mask = mask.reshape(nP, 1, Gh * Gw) attn_mask = torch.zeros((nP, Gh * Gw, Gh * Gw), device=x.device) attn_mask = attn_mask.masked_fill(mask < 0, NEG_INF) else: attn_mask = None # Inside each window, fuse the x and y, then compute self-attention. x = xy[:, :, :C] y = xy[:, :, C:] xy = torch.cat((x, y), 1) xy = self.attn(xy, Gh, 2*Gw, mask=attn_mask) # nP*B, Gh*2Gw, C # print("fused xy:", xy.shape) # merge embeddings if self.ds_flag == 0: x = xy[:, :xy.shape[1]//2, :].reshape(B, Hd // G, Wd // G, G, G, C).permute(0, 1, 3, 2, 4, 5).contiguous() # B, Hd//G, G, Wd//G, G, C y = xy[:, xy.shape[1]//2:, :].reshape(B, Hd // G, Wd // G, G, G, C).permute(0, 1, 3, 2, 4, 5).contiguous() # B, Hd//G, G, Wd//G, G, C else: x = xy[:, :xy.shape[1]//2, :].reshape(B, I, I, Gh, Gw, C).permute(0, 3, 1, 4, 2, 5).contiguous() # B, Gh, I, Gw, I, C y = xy[:, xy.shape[1]//2:, :].reshape(B, I, I, Gh, Gw, C).permute(0, 3, 1, 4, 2, 5).contiguous() # B, Gh, I, Gw, I, C x = x.reshape(B, Hd, Wd, C) y = y.reshape(B, Hd, Wd, C) # remove padding if pad_r > 0 or pad_b > 0: x = x[:, :H, :W, :].contiguous() y = y[:, :H, :W, :].contiguous() x = x.view(B, H * W, C) y = y.view(B, H * W, C) # FFN x = shortcut_x + self.drop_path(x) x = x + self.drop_path(self.mlp_x(self.norm2(x))) # print("x:", x.shape) y = shortcut_y + self.drop_path(y) y = y + self.drop_path(self.mlp_y(self.norm2(y))) return x, y def extra_repr(self) -> str: return f"dim={self.dim}, num_heads={self.num_heads}, " \ f"window_size={self.window_size}, ds_flag={self.ds_flag}, mlp_ratio={self.mlp_ratio}"