dsp-repro-bundle / models /dsp /layers.py
junwatu's picture
Upload folder using huggingface_hub
c881b77 verified
Raw
History Blame Contribute Delete
19 kB
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) # [15, 64, 1280]
context = default(context, x)
k = self.to_k(context) # [15, 18, 1280]
v = self.to_v(context) # [15, 18, 1280]
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) # torch.Size([5, 30, 64])
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 # 2 is sin&cos, 8 is xyxyxyxy
# -------------------------------------------------------------- #
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):
# embedding position (it may includes padding as placeholder)
xyxy_embedding = self.fourier_embedder(boxes) # B*1*4 --> B*1*C torch.Size([5, 1, 64])
xyxy_embedding = self.linears_position(xyxy_embedding) # B*1*C --> B*1*768 torch.Size([5, 1, 768])
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) # (B * phase_num, HW, HW)
guidance_mask_sim = guidance_mask_sim.view(b, phase_num, HW, HW).sum(dim=1)
guidance_mask_sim[guidance_mask_sim > 1] = 1 # (B, HW, HW)
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) # (B * head, 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)
# attention, what we cannot get enough of
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)
# feedforward
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) # B*N*(H*C)
k = self.to_k(x) # B*N*(H*C)
v = self.to_v(x) # B*N*(H*C)
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) # (B*H)*N*C
k = k.view(B,N,H,C).permute(0,2,1,3).reshape(B*H,N,C) # (B*H)*N*C
v = v.view(B,N,H,C).permute(0,2,1,3).reshape(B*H,N,C) # (B*H)*N*C
sim = torch.einsum('b i c, b j c -> b i j', q, k) * self.scale # (B*H)*N*N
attn = sim.softmax(dim=-1) # (B*H)*N*N
out = torch.einsum('b i j, b j c -> b i c', attn, v) # (B*H)*N*C
out = out.view(B,H,N,C).permute(0,2,1,3).reshape(B,N,(H*C)) # 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__()
# we need a linear projection since we need cat visual feature and obj feature
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.)) )
# this can be useful: we can externally change magnitude of tanh(alpha)
# for example, when it is set to 0, then the entire model is same as original one
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):
# context_dim: SD1.4 768 SD2.1 1024
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):
# x: (B, instance_num+1, HW, C)
# guidance_mask: (B, instance_num, H, W)
# box: (instance_num, 4)
# image_token: (B, instance_num+1, HW, C)
# Reminder for shapes
# ca_x # [1, 16, 64, 1280] # After Cross-Attn with encoder_hidden_states
# guidance_mask # [1, 15, 64, 64]
# other_info['image_token'] # [1, 16, 64, 1280] # Original hidden_states repeated 1+instance_num times
# other_info['context'] # [16, 77, 768]
# other_info['box'] # [1, 15, 8]
# other_info['context_pooler'] # [16, 1, 768]
# other_info['supplement_mask'] # [1, 1, 64, 64]
# other_info['height'] = height # 512
# other_info['width'] = width # 512
# other_info['ref_features'] # [15, 16, 768], [1, 16, 768]
# other_info['sigmoid_values'] # [1, 16, 768]
height, width = other_info['height'], other_info['width'] # 512, 512
instance_num = other_info['instance_num'] # 15
B, _, HW, C = ca_x.shape # [1, 16, 64, 1280]
down_scale = int(math.sqrt(height * width // ca_x.shape[2])) # 64
H = height // down_scale # 8
W = width // down_scale # 8
guidance_masks = other_info['guidance_masks']
guidance_masks = F.interpolate(guidance_masks, size=(H, W), mode='bilinear') # [1, 15, 8, 8]
supplement_mask = other_info['supplement_mask'] # (1, 1, 64, 64)
supplement_mask = F.interpolate(supplement_mask, size=(H, W), mode='bilinear') # (1, 1, 8, 8)
image_token = other_info['image_token'] # [1, 16, 64, 1280]
assert image_token.shape == ca_x.shape
context_pooler = other_info['context_pooler'] # [16, 1, 768]
box = other_info['box'] # [1, 15, 8]
box = box.view(B * instance_num, 1, -1) # [15, 1, 8]
box_token = self.pos_net(box) # [15, 1, 768]
# add reference image feature as condition
img_features, bg_features = other_info['ref_features'] # [15, 16, 768], [1, 16, 768]
context_fg = torch.cat([context_pooler[1:, ...], img_features, box_token], dim=1) # [15, 1 (text) + 16 (ref) + 1 (box), 768]
ea_x, anchor_attn = self.ea_obj(self.norm_obj(image_token[:, 1:, ...].view(B * instance_num, HW, C)), # [15, 64, 1280]
context=context_fg, return_attn=True) # ea_x.shape [15, 64, 1280]
ea_x = ea_x.view(B, instance_num, HW, C) # ea_x.shape [1, 15, 64, 1280]
sigmoid_values = other_info['sigmoid_values'] # [1, 15, 64, 64]
sigmoid_values = F.interpolate(sigmoid_values, size=(H, W), mode='bilinear') # [1, 15, 8, 8]
ea_x = ea_x * sigmoid_values.view(B, instance_num, HW, 1) # [1, 15, 64, 1280] * [1, 15, 64, 1]
ca_x[:, 1:, ...] = ca_x[:, 1:, ...] * sigmoid_values.view(B, instance_num, HW, 1) # (B, phase_num, HW, C)
ca_x[:, 1:, ...] = ca_x[:, 1:, ...] + ea_x # ca_x.shape [1, 16, 64, 1280]
context_bg = torch.cat([context_pooler[[0], ...], bg_features], dim=1) # [1, 1 (text) + 16 (ref), 768]
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) # [1, 64, 1280]
ca_x[:, 0, ...] = ca_x[:, 0, ...] + ea_x_bg # [1, 64, 1280]
# image_token[:, 0, ...].shape [1, 64, 1280] ; torch.cat([guidance_mask[:, :, ...], supplement_mask], dim=1).shape [1, 1 + 15, 8, 8]
fusion_template = self.la(x=image_token[:, 0, ...], guidance_mask=torch.cat([guidance_masks[:, :, ...], supplement_mask], dim=1)) # [1, 64, 1280]
fusion_template = fusion_template.view(B, 1, HW, C) # [1, 1, 64, 1280]
ca_x = torch.cat([ca_x, fusion_template], dim = 1) # [1, 17, 64, 1280]
out = torch.sum(ca_x, dim=1) # [1, 64, 1280]
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'] # 512, 512
instance_num = other_info['instance_num'] # 15
B, _, HW, C = ca_x.shape # [1, 16, 64, 1280]
down_scale = int(math.sqrt(height * width // ca_x.shape[2])) # 64
H = height // down_scale # 8
W = width // down_scale # 8
guidance_masks = other_info['guidance_masks']
guidance_masks = F.interpolate(guidance_masks, size=(H, W), mode='bilinear') # [1, 15, 8, 8]
supplement_mask = other_info['supplement_mask'] # (1, 1, 64, 64)
supplement_mask = F.interpolate(supplement_mask, size=(H, W), mode='bilinear') # (1, 1, 8, 8)
sigmoid_values = other_info['sigmoid_values'] # [1, 15, 64, 64]
sigmoid_values = F.interpolate(sigmoid_values, size=(H, W), mode='bilinear') # [1, 15, 8, 8]
image_token = other_info['image_token'] # [1, 16, 64, 1280]
assert image_token.shape == ca_x.shape
prototypes = other_info['prototypes'] # [15, 64, 768]
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) # [1, 15, 64, 1280] * [1, 15, 64, 1]
side_feats_gated = side_feats_input * sigmoid_values.view(B, instance_num, HW, 1) # (B, phase_num, HW, C)
total_side_residual = side_feats_gated + ea_x # ca_x.shape [1, 16, 64, 1280]
# image_token[:, 0, ...].shape [1, 64, 1280] ; torch.cat([guidance_mask[:, :, ...], supplement_mask], dim=1).shape [1, 1 + 15, 8, 8]
fusion_template = self.la(x=image_token[:, 0, ...], guidance_mask=torch.cat([guidance_masks[:, :, ...], supplement_mask], dim=1)) # [1, 64, 1280]
fusion_template = fusion_template.view(B, 1, HW, C) # [1, 1, 64, 1280]
final_residual = torch.sum(total_side_residual, dim=1) + fusion_template.squeeze(1)
out = base + self.gating_param * final_residual
return out