| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| import math |
| from inspect import isfunction |
| from einops import rearrange, repeat |
| from torch import einsum |
|
|
| def exists(val): |
| return val is not None |
|
|
| def default(val, d): |
| if exists(val): |
| return val |
| return d() if isfunction(d) else d |
|
|
| class CrossAttention(nn.Module): |
| def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0.): |
| super().__init__() |
| inner_dim = dim_head * heads |
| context_dim = default(context_dim, query_dim) |
|
|
| self.scale = dim_head ** -0.5 |
| self.heads = heads |
|
|
| self.to_q = nn.Linear(query_dim, inner_dim, bias=False) |
| self.to_k = nn.Linear(context_dim, inner_dim, bias=False) |
| self.to_v = nn.Linear(context_dim, inner_dim, bias=False) |
|
|
| self.to_out = nn.Sequential( |
| nn.Linear(inner_dim, query_dim), |
| nn.Dropout(dropout) |
| ) |
|
|
| def forward(self, x, context=None, mask=None, return_attn=False, need_softmax=True): |
| h = self.heads |
| b = x.shape[0] |
|
|
| q = self.to_q(x) |
| context = default(context, x) |
| k = self.to_k(context) |
| v = self.to_v(context) |
|
|
| q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v)) |
|
|
| sim = einsum('b i d, b j d -> b i j', q, k) * self.scale |
|
|
| if exists(mask): |
| mask = rearrange(mask, 'b ... -> b (...)') |
| max_neg_value = -torch.finfo(sim.dtype).max |
| mask = repeat(mask, 'b j -> (b h) () j', h=h) |
| sim.masked_fill_(~mask, max_neg_value) |
|
|
| if need_softmax: |
| attn = sim.softmax(dim=-1) |
| else: |
| attn = sim |
|
|
| out = einsum('b i j, b j d -> b i d', attn, v) |
| out = rearrange(out, '(b h) n d -> b n (h d)', h=h) |
| if return_attn: |
| attn = attn.view(b, h, attn.shape[-2], attn.shape[-1]) |
| return self.to_out(out), attn |
| else: |
| return self.to_out(out) |
|
|
| class FourierEmbedder(): |
| def __init__(self, num_freqs=64, temperature=100): |
| self.num_freqs = num_freqs |
| self.temperature = temperature |
| self.freq_bands = temperature ** ( torch.arange(num_freqs) / num_freqs ) |
|
|
| @ torch.no_grad() |
| def __call__(self, x, cat_dim=-1): |
| out = [] |
| for freq in self.freq_bands: |
| out.append( torch.sin( freq*x ) ) |
| out.append( torch.cos( freq*x ) ) |
| return torch.cat(out, cat_dim) |
|
|
| class PositionNet(nn.Module): |
| def __init__(self, in_dim, out_dim, fourier_freqs=8): |
| super().__init__() |
| self.in_dim = in_dim |
| self.out_dim = out_dim |
|
|
| self.fourier_embedder = FourierEmbedder(num_freqs=fourier_freqs) |
| self.position_dim = fourier_freqs * 2 * 8 |
|
|
| |
| self.linears_position = nn.Sequential( |
| nn.Linear(self.position_dim, 512), |
| nn.SiLU(), |
| nn.Linear(512, 512), |
| nn.SiLU(), |
| nn.Linear(512, out_dim), |
| ) |
|
|
| def forward(self, boxes): |
|
|
| |
| xyxy_embedding = self.fourier_embedder(boxes) |
| xyxy_embedding = self.linears_position(xyxy_embedding) |
|
|
| return xyxy_embedding |
|
|
| class LayoutAttention(nn.Module): |
| def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0., use_lora=False): |
| super().__init__() |
| inner_dim = dim_head * heads |
| context_dim = default(context_dim, query_dim) |
|
|
| self.use_lora = use_lora |
| self.scale = dim_head ** -0.5 |
| self.heads = heads |
|
|
| self.to_q = nn.Linear(query_dim, inner_dim, bias=False) |
| self.to_k = nn.Linear(context_dim, inner_dim, bias=False) |
| self.to_v = nn.Linear(context_dim, inner_dim, bias=False) |
|
|
| self.to_out = nn.Sequential( |
| nn.Linear(inner_dim, query_dim), |
| nn.Dropout(dropout) |
| ) |
|
|
| def forward(self, x, context=None, mask=None, return_attn=False, need_softmax=True, guidance_mask=None): |
| h = self.heads |
| b = x.shape[0] |
|
|
| q = self.to_q(x) |
| context = default(context, x) |
| k = self.to_k(context) |
| v = self.to_v(context) |
|
|
| q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v)) |
|
|
| sim = einsum('b i d, b j d -> b i j', q, k) * self.scale |
|
|
| _, phase_num, H, W = guidance_mask.shape |
| HW = H * W |
| guidance_mask_o = guidance_mask.view(b * phase_num, HW, 1) |
| guidance_mask_t = guidance_mask.view(b * phase_num, 1, HW) |
| guidance_mask_sim = torch.bmm(guidance_mask_o, guidance_mask_t) |
| guidance_mask_sim = guidance_mask_sim.view(b, phase_num, HW, HW).sum(dim=1) |
| guidance_mask_sim[guidance_mask_sim > 1] = 1 |
| guidance_mask_sim = guidance_mask_sim.view(b, 1, HW, HW) |
| guidance_mask_sim = guidance_mask_sim.repeat(1, self.heads, 1, 1) |
| guidance_mask_sim = guidance_mask_sim.view(b * self.heads, HW, HW) |
|
|
| sim[:, :, :HW][guidance_mask_sim == 0] = -torch.finfo(sim.dtype).max |
|
|
| if exists(mask): |
| mask = rearrange(mask, 'b ... -> b (...)') |
| max_neg_value = -torch.finfo(sim.dtype).max |
| mask = repeat(mask, 'b j -> (b h) () j', h=h) |
| sim.masked_fill_(~mask, max_neg_value) |
|
|
| |
|
|
| if need_softmax: |
| attn = sim.softmax(dim=-1) |
| else: |
| attn = sim |
| |
| out = einsum('b i j, b j d -> b i d', attn, v) |
| out = rearrange(out, '(b h) n d -> b n (h d)', h=h) |
| if return_attn: |
| attn = attn.view(b, h, attn.shape[-2], attn.shape[-1]) |
| return self.to_out(out), attn |
| else: |
| return self.to_out(out) |
|
|
| |
| class GEGLU(nn.Module): |
| def __init__(self, dim_in, dim_out): |
| super().__init__() |
| self.proj = nn.Linear(dim_in, dim_out * 2) |
|
|
| def forward(self, x): |
| x, gate = self.proj(x).chunk(2, dim=-1) |
| return x * F.gelu(gate) |
|
|
| class FeedForward(nn.Module): |
| def __init__(self, dim, dim_out=None, mult=4, glu=False, dropout=0.): |
| super().__init__() |
| inner_dim = int(dim * mult) |
| dim_out = default(dim_out, dim) |
| project_in = nn.Sequential( |
| nn.Linear(dim, inner_dim), |
| nn.GELU() |
| ) if not glu else GEGLU(dim, inner_dim) |
|
|
| self.net = nn.Sequential( |
| project_in, |
| nn.Dropout(dropout), |
| nn.Linear(inner_dim, dim_out) |
| ) |
|
|
| def forward(self, x): |
| return self.net(x) |
|
|
| class SelfAttention(nn.Module): |
| def __init__(self, query_dim, heads=8, dim_head=64, dropout=0.): |
| super().__init__() |
| inner_dim = dim_head * heads |
| self.scale = dim_head ** -0.5 |
| self.heads = heads |
|
|
| self.to_q = nn.Linear(query_dim, inner_dim, bias=False) |
| self.to_k = nn.Linear(query_dim, inner_dim, bias=False) |
| self.to_v = nn.Linear(query_dim, inner_dim, bias=False) |
|
|
| self.to_out = nn.Sequential(nn.Linear(inner_dim, query_dim), nn.Dropout(dropout) ) |
|
|
| def forward(self, x): |
| q = self.to_q(x) |
| k = self.to_k(x) |
| v = self.to_v(x) |
|
|
| B, N, HC = q.shape |
| H = self.heads |
| C = HC // H |
|
|
| q = q.view(B,N,H,C).permute(0,2,1,3).reshape(B*H,N,C) |
| k = k.view(B,N,H,C).permute(0,2,1,3).reshape(B*H,N,C) |
| v = v.view(B,N,H,C).permute(0,2,1,3).reshape(B*H,N,C) |
|
|
| sim = torch.einsum('b i c, b j c -> b i j', q, k) * self.scale |
| attn = sim.softmax(dim=-1) |
|
|
| out = torch.einsum('b i j, b j c -> b i c', attn, v) |
| out = out.view(B,H,N,C).permute(0,2,1,3).reshape(B,N,(H*C)) |
|
|
| return self.to_out(out) |
|
|
| class GatedSelfAttentionDense(nn.Module): |
| def __init__(self, query_dim, context_dim, n_heads, d_head): |
| super().__init__() |
| |
| |
| self.linear = nn.Linear(context_dim, query_dim) |
|
|
| self.attn = SelfAttention(query_dim=query_dim, heads=n_heads, dim_head=d_head) |
| self.ff = FeedForward(query_dim, glu=True) |
|
|
| self.norm1 = nn.LayerNorm(query_dim) |
| self.norm2 = nn.LayerNorm(query_dim) |
|
|
| self.register_parameter('alpha_attn', nn.Parameter(torch.tensor(0.)) ) |
| self.register_parameter('alpha_dense', nn.Parameter(torch.tensor(0.)) ) |
|
|
| |
| |
| self.scale = 1 |
|
|
|
|
| def forward(self, x, objs): |
|
|
| N_visual = x.shape[1] |
| objs = self.linear(objs) |
|
|
| x = x + self.scale*torch.tanh(self.alpha_attn) * self.attn( self.norm1(torch.cat([x,objs],dim=1)) )[:,0:N_visual,:] |
| x = x + self.scale*torch.tanh(self.alpha_dense) * self.ff( self.norm2(x) ) |
| |
| return x |
|
|
| class MIFusion(nn.Module): |
| def __init__(self, C, attn_type='base', context_dim=768, heads=8): |
| |
| super().__init__() |
| self.ea_obj = CrossAttention(query_dim=C, context_dim=context_dim, |
| heads=heads, dim_head=C // heads, |
| dropout=0.0) |
| self.norm_obj = nn.LayerNorm(C) |
| self.ea2 = CrossAttention(query_dim=C, context_dim=context_dim, |
| heads=heads, dim_head=C // heads, |
| dropout=0.0) |
| self.norm2 = nn.LayerNorm(C) |
| self.pos_net = PositionNet(in_dim=context_dim, out_dim=context_dim) |
| self.la = LayoutAttention(query_dim=C, heads=heads, |
| dim_head=C // heads, dropout=0.0) |
|
|
| def forward(self, ca_x, other_info): |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| height, width = other_info['height'], other_info['width'] |
| instance_num = other_info['instance_num'] |
| B, _, HW, C = ca_x.shape |
| down_scale = int(math.sqrt(height * width // ca_x.shape[2])) |
| H = height // down_scale |
| W = width // down_scale |
|
|
| guidance_masks = other_info['guidance_masks'] |
| guidance_masks = F.interpolate(guidance_masks, size=(H, W), mode='bilinear') |
|
|
| supplement_mask = other_info['supplement_mask'] |
| supplement_mask = F.interpolate(supplement_mask, size=(H, W), mode='bilinear') |
|
|
| image_token = other_info['image_token'] |
| assert image_token.shape == ca_x.shape |
|
|
| context_pooler = other_info['context_pooler'] |
| box = other_info['box'] |
| box = box.view(B * instance_num, 1, -1) |
| box_token = self.pos_net(box) |
| |
| |
| img_features, bg_features = other_info['ref_features'] |
| |
| context_fg = torch.cat([context_pooler[1:, ...], img_features, box_token], dim=1) |
| ea_x, anchor_attn = self.ea_obj(self.norm_obj(image_token[:, 1:, ...].view(B * instance_num, HW, C)), |
| context=context_fg, return_attn=True) |
| ea_x = ea_x.view(B, instance_num, HW, C) |
| sigmoid_values = other_info['sigmoid_values'] |
| sigmoid_values = F.interpolate(sigmoid_values, size=(H, W), mode='bilinear') |
| ea_x = ea_x * sigmoid_values.view(B, instance_num, HW, 1) |
| ca_x[:, 1:, ...] = ca_x[:, 1:, ...] * sigmoid_values.view(B, instance_num, HW, 1) |
| ca_x[:, 1:, ...] = ca_x[:, 1:, ...] + ea_x |
| |
| context_bg = torch.cat([context_pooler[[0], ...], bg_features], dim=1) |
| ea_x_bg, _ = self.ea2(self.norm2(ca_x[:, 1:, ...].sum(dim=1, keepdim=True).view(B * 1, HW, C)), |
| context=context_bg, return_attn=True) |
| ca_x[:, 0, ...] = ca_x[:, 0, ...] + ea_x_bg |
| |
| |
| fusion_template = self.la(x=image_token[:, 0, ...], guidance_mask=torch.cat([guidance_masks[:, :, ...], supplement_mask], dim=1)) |
| fusion_template = fusion_template.view(B, 1, HW, C) |
| ca_x = torch.cat([ca_x, fusion_template], dim = 1) |
| out = torch.sum(ca_x, dim=1) |
| return out |
|
|
| class MIFusionPrototype(nn.Module): |
| def __init__(self, C, attn_type='base', context_dim=768, heads=8, prototype_dim=1024): |
| super().__init__() |
| self.prototype_attn = CrossAttention(query_dim=C, context_dim=prototype_dim, |
| heads=heads, dim_head=C // heads, |
| dropout=0.0) |
| self.norm_prototype = nn.LayerNorm(C) |
| self.la = LayoutAttention(query_dim=C, heads=heads, |
| dim_head=C // heads, dropout=0.0) |
| self.gating_param = nn.Parameter(torch.zeros(1)) |
| self._init_novel_weights() |
|
|
| def _init_novel_weights(self): |
| if hasattr(self.prototype_attn, 'to_out'): |
| output_layer = self.prototype_attn.to_out[0] if isinstance(self.prototype_attn.to_out, nn.Sequential) or isinstance(self.prototype_attn.to_out, nn.ModuleList) else self.prototype_attn.to_out |
| nn.init.zeros_(output_layer.weight) |
| nn.init.zeros_(output_layer.bias) |
| if hasattr(self.prototype_attn, 'to_q'): |
| nn.init.xavier_uniform_(self.prototype_attn.to_q.weight) |
| if hasattr(self.prototype_attn, 'to_k'): |
| nn.init.xavier_uniform_(self.prototype_attn.to_k.weight) |
| if hasattr(self.prototype_attn, 'to_v'): |
| nn.init.xavier_uniform_(self.prototype_attn.to_v.weight) |
| if hasattr(self.la, 'to_out'): |
| output_layer = self.la.to_out[0] if isinstance(self.la.to_out, nn.Sequential) or isinstance(self.la.to_out, nn.ModuleList) else self.la.to_out |
| nn.init.zeros_(output_layer.weight) |
| nn.init.zeros_(output_layer.bias) |
|
|
| def forward(self, ca_x, other_info): |
| height, width = other_info['height'], other_info['width'] |
| instance_num = other_info['instance_num'] |
| B, _, HW, C = ca_x.shape |
| down_scale = int(math.sqrt(height * width // ca_x.shape[2])) |
| H = height // down_scale |
| W = width // down_scale |
|
|
| guidance_masks = other_info['guidance_masks'] |
| guidance_masks = F.interpolate(guidance_masks, size=(H, W), mode='bilinear') |
|
|
| supplement_mask = other_info['supplement_mask'] |
| supplement_mask = F.interpolate(supplement_mask, size=(H, W), mode='bilinear') |
|
|
| sigmoid_values = other_info['sigmoid_values'] |
| sigmoid_values = F.interpolate(sigmoid_values, size=(H, W), mode='bilinear') |
|
|
| image_token = other_info['image_token'] |
| assert image_token.shape == ca_x.shape |
| |
| prototypes = other_info['prototypes'] |
|
|
| base, side_feats_input = ca_x[:, 0, ...].clone(), ca_x[:, 1:, ...].clone() |
|
|
| mask_flat = sigmoid_values.view(B * instance_num, -1) |
| x_flat = image_token[:, 1:, ...].reshape(B * instance_num, HW, C) |
|
|
| keep_k = 256 |
| _, topk_indices = torch.topk(mask_flat, k=keep_k, dim=1) |
| gather_indices = topk_indices.unsqueeze(-1).expand(-1, -1, C) |
| x_selected = torch.gather(x_flat, 1, gather_indices) |
| ea_selected, primitive_attn = self.prototype_attn(self.norm_prototype(x_selected), context=prototypes, return_attn=True) |
| ea_full = torch.zeros_like(x_flat) |
| ea_full.scatter_(1, gather_indices, ea_selected) |
| ea_x = ea_full.view(B, instance_num, HW, C) |
|
|
| ea_x = ea_x * sigmoid_values.view(B, instance_num, HW, 1) |
| side_feats_gated = side_feats_input * sigmoid_values.view(B, instance_num, HW, 1) |
| total_side_residual = side_feats_gated + ea_x |
| |
| |
| fusion_template = self.la(x=image_token[:, 0, ...], guidance_mask=torch.cat([guidance_masks[:, :, ...], supplement_mask], dim=1)) |
| fusion_template = fusion_template.view(B, 1, HW, C) |
| |
| final_residual = torch.sum(total_side_residual, dim=1) + fusion_template.squeeze(1) |
| out = base + self.gating_param * final_residual |
| return out |