AFR-DFV-v2 / dinov3 /eval /detection /models /global_rpe_decomp_decoder.py
Addax-Data-Science's picture
Upload 162 files
d9bb75c verified
Raw
History Blame Contribute Delete
14.9 kB
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This software may be used and distributed in accordance with
# the terms of the DINOv3 License Agreement.
# ------------------------------------------------------------------------
# Plain-DETR
# Copyright (c) 2023 Xi'an Jiaotong University & Microsoft Research Asia.
# Licensed under The MIT License [see LICENSE for details]
# ------------------------------------------------------------------------
# -*- coding: utf-8 -*-
import numpy as np
import torch
import torch.nn as nn
import torch.utils.checkpoint as checkpoint
from ..util.box_ops import box_xyxy_to_cxcywh, delta2bbox
from ..util.misc import _get_activation_fn, _get_clones, inverse_sigmoid
class GlobalCrossAttention(nn.Module):
def __init__(
self,
dim,
num_heads,
qkv_bias=True,
qk_scale=None,
attn_drop=0.0,
proj_drop=0.0,
rpe_hidden_dim=512,
rpe_type="linear",
feature_stride=16,
reparam=False,
):
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.rpe_type = rpe_type
self.feature_stride = feature_stride
self.reparam = reparam
self.cpb_mlp1 = self.build_cpb_mlp(2, rpe_hidden_dim, num_heads)
self.cpb_mlp2 = self.build_cpb_mlp(2, rpe_hidden_dim, num_heads)
self.q = nn.Linear(dim, dim, bias=qkv_bias)
self.k = nn.Linear(dim, dim, bias=qkv_bias)
self.v = nn.Linear(dim, dim, 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 build_cpb_mlp(self, in_dim, hidden_dim, out_dim):
cpb_mlp = nn.Sequential(
nn.Linear(in_dim, hidden_dim, bias=True), nn.ReLU(inplace=True), nn.Linear(hidden_dim, out_dim, bias=False)
)
return cpb_mlp
def forward(
self,
query,
reference_points,
k_input_flatten,
v_input_flatten,
input_spatial_shapes,
input_padding_mask=None,
):
assert len(input_spatial_shapes) == 1, "This is designed for single-scale decoder."
h, w = input_spatial_shapes[0]
stride = self.feature_stride
ref_pts = torch.cat(
[
reference_points[:, :, :, :2] - reference_points[:, :, :, 2:] / 2,
reference_points[:, :, :, :2] + reference_points[:, :, :, 2:] / 2,
],
dim=-1,
) # B, nQ, 1, 4
if not self.reparam:
ref_pts[..., 0::2] *= w * stride
ref_pts[..., 1::2] *= h * stride
pos_x = (
torch.linspace(0.5, w - 0.5, w, dtype=torch.float32, device=ref_pts.device)[None, None, :, None] * stride
) # 1, 1, w, 1
pos_y = (
torch.linspace(0.5, h - 0.5, h, dtype=torch.float32, device=ref_pts.device)[None, None, :, None] * stride
) # 1, 1, h, 1
if self.rpe_type == "abs_log8":
delta_x = ref_pts[..., 0::2] - pos_x # B, nQ, w, 2
delta_y = ref_pts[..., 1::2] - pos_y # B, nQ, h, 2
delta_x = torch.sign(delta_x) * torch.log2(torch.abs(delta_x) + 1.0) / np.log2(8)
delta_y = torch.sign(delta_y) * torch.log2(torch.abs(delta_y) + 1.0) / np.log2(8)
elif self.rpe_type == "linear":
delta_x = ref_pts[..., 0::2] - pos_x # B, nQ, w, 2
delta_y = ref_pts[..., 1::2] - pos_y # B, nQ, h, 2
else:
raise NotImplementedError
rpe_x, rpe_y = self.cpb_mlp1(delta_x), self.cpb_mlp2(delta_y) # B, nQ, w/h, nheads
rpe = (rpe_x[:, :, None] + rpe_y[:, :, :, None]).flatten(2, 3) # B, nQ, h, w, nheads -> B, nQ, h*w, nheads
rpe = rpe.permute(0, 3, 1, 2)
B_, N, C = k_input_flatten.shape
k = self.k(k_input_flatten).reshape(B_, N, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
v = self.v(v_input_flatten).reshape(B_, N, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
B_, N, C = query.shape
q = self.q(query).reshape(B_, N, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
attn_mask = rpe
if input_padding_mask is not None:
attn_mask += input_padding_mask[:, None, None] * -100
attn_mask = attn_mask.contiguous() # to enable efficient attention
x = torch.nn.functional.scaled_dot_product_attention(
query=q,
key=k,
value=v,
attn_mask=attn_mask,
dropout_p=self.attn_drop.p if self.training else 0,
scale=self.scale,
)
x = x.transpose(1, 2).reshape(B_, N, C)
x = self.proj(x)
x = self.proj_drop(x)
return x
class GlobalDecoderLayer(nn.Module):
def __init__(
self,
d_model=256,
d_ffn=1024,
dropout=0.1,
activation="relu",
n_heads=8,
norm_type="post_norm",
rpe_hidden_dim=512,
rpe_type="box_norm",
feature_stride=16,
reparam=False,
):
super().__init__()
self.norm_type = norm_type
# global cross attention
self.cross_attn = GlobalCrossAttention(
d_model,
n_heads,
rpe_hidden_dim=rpe_hidden_dim,
rpe_type=rpe_type,
feature_stride=feature_stride,
reparam=reparam,
)
self.dropout1 = nn.Dropout(dropout)
self.norm1 = nn.LayerNorm(d_model)
# self attention
self.self_attn = nn.MultiheadAttention(d_model, n_heads, dropout=dropout)
self.dropout2 = nn.Dropout(dropout)
self.norm2 = nn.LayerNorm(d_model)
# ffn
self.linear1 = nn.Linear(d_model, d_ffn)
self.activation = _get_activation_fn(activation)
self.dropout3 = nn.Dropout(dropout)
self.linear2 = nn.Linear(d_ffn, d_model)
self.dropout4 = nn.Dropout(dropout)
self.norm3 = nn.LayerNorm(d_model)
@staticmethod
def with_pos_embed(tensor, pos):
return tensor if pos is None else tensor + pos
def forward_pre(
self,
tgt,
query_pos,
reference_points,
src,
src_pos_embed,
src_spatial_shapes,
src_padding_mask=None,
self_attn_mask=None,
):
# self attention
tgt2 = self.norm2(tgt)
q = k = self.with_pos_embed(tgt2, query_pos)
tgt2 = self.self_attn(
q.transpose(0, 1), k.transpose(0, 1), tgt2.transpose(0, 1), attn_mask=self_attn_mask, need_weights=False
)[0].transpose(0, 1)
tgt = tgt + self.dropout2(tgt2)
# global cross attention
tgt2 = self.norm1(tgt)
tgt2 = self.cross_attn(
self.with_pos_embed(tgt2, query_pos),
reference_points,
self.with_pos_embed(src, src_pos_embed),
src,
src_spatial_shapes,
src_padding_mask,
)
tgt = tgt + self.dropout1(tgt2)
# ffn
tgt2 = self.norm3(tgt)
tgt2 = self.linear2(self.dropout3(self.activation(self.linear1(tgt2))))
tgt = tgt + self.dropout4(tgt2)
return tgt
def forward_post(
self,
tgt,
query_pos,
reference_points,
src,
src_pos_embed,
src_spatial_shapes,
src_padding_mask=None,
self_attn_mask=None,
):
# self attention
q = k = self.with_pos_embed(tgt, query_pos)
tgt2 = self.self_attn(
q.transpose(0, 1), k.transpose(0, 1), tgt.transpose(0, 1), attn_mask=self_attn_mask, need_weights=False
)[0].transpose(0, 1)
tgt = tgt + self.dropout2(tgt2)
tgt = self.norm2(tgt)
# cross attention
tgt2 = self.cross_attn(
self.with_pos_embed(tgt, query_pos),
reference_points,
self.with_pos_embed(src, src_pos_embed),
src,
src_spatial_shapes,
src_padding_mask,
)
tgt = tgt + self.dropout1(tgt2)
tgt = self.norm1(tgt)
# ffn
tgt2 = self.linear2(self.dropout3(self.activation(self.linear1(tgt))))
tgt = tgt + self.dropout4(tgt2)
tgt = self.norm3(tgt)
return tgt
def forward(
self,
tgt,
query_pos,
reference_points,
src,
src_pos_embed,
src_spatial_shapes,
src_padding_mask=None,
self_attn_mask=None,
):
if self.norm_type == "pre_norm":
return self.forward_pre(
tgt,
query_pos,
reference_points,
src,
src_pos_embed,
src_spatial_shapes,
src_padding_mask,
self_attn_mask,
)
if self.norm_type == "post_norm":
return self.forward_post(
tgt,
query_pos,
reference_points,
src,
src_pos_embed,
src_spatial_shapes,
src_padding_mask,
self_attn_mask,
)
class GlobalDecoder(nn.Module):
def __init__(
self,
decoder_layer,
num_layers,
return_intermediate=False,
look_forward_twice=False,
use_checkpoint=False,
d_model=256,
norm_type="post_norm",
reparam=False,
):
super().__init__()
self.layers = _get_clones(decoder_layer, num_layers)
self.num_layers = num_layers
self.return_intermediate = return_intermediate
self.look_forward_twice = look_forward_twice
self.use_checkpoint = use_checkpoint
# hack implementation for iterative bounding box refinement and two-stage Deformable DETR
self.bbox_embed = None
self.class_embed = None
self.reparam = reparam
self.norm_type = norm_type
if self.norm_type == "pre_norm":
self.final_layer_norm = nn.LayerNorm(d_model)
else:
self.final_layer_norm = None
def _reset_parameters(self):
# stolen from Swin Transformer
def _init_weights(m):
if isinstance(m, nn.Linear):
nn.init.trunc_normal_(m.weight, std=0.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)
self.apply(_init_weights)
def forward(
self,
tgt,
reference_points,
src,
src_pos_embed,
src_spatial_shapes,
src_level_start_index,
src_valid_ratios,
query_pos=None,
src_padding_mask=None,
self_attn_mask=None,
max_shape=None,
):
output = tgt
intermediate = []
intermediate_reference_points = []
for lid, layer in enumerate(self.layers):
if self.reparam:
reference_points_input = reference_points[:, :, None]
else:
if reference_points.shape[-1] == 4:
reference_points_input = (
reference_points[:, :, None] * torch.cat([src_valid_ratios, src_valid_ratios], -1)[:, None]
)
else:
assert reference_points.shape[-1] == 2
reference_points_input = reference_points[:, :, None] * src_valid_ratios[:, None]
if self.use_checkpoint:
output = checkpoint.checkpoint(
layer,
output,
query_pos,
reference_points_input,
src,
src_pos_embed,
src_spatial_shapes,
src_padding_mask,
self_attn_mask,
)
else:
output = layer(
output,
query_pos,
reference_points_input,
src,
src_pos_embed,
src_spatial_shapes,
src_padding_mask,
self_attn_mask,
)
if self.final_layer_norm is not None:
output_after_norm = self.final_layer_norm(output)
else:
output_after_norm = output
# hack implementation for iterative bounding box refinement
if self.bbox_embed is not None:
tmp = self.bbox_embed[lid](output_after_norm)
if reference_points.shape[-1] == 4:
if self.reparam:
new_reference_points = box_xyxy_to_cxcywh(delta2bbox(reference_points, tmp, max_shape))
else:
new_reference_points = tmp + inverse_sigmoid(reference_points)
new_reference_points = new_reference_points.sigmoid()
else:
if self.reparam:
raise NotImplementedError
assert reference_points.shape[-1] == 2
new_reference_points = tmp
new_reference_points[..., :2] = tmp[..., :2] + inverse_sigmoid(reference_points)
new_reference_points = new_reference_points.sigmoid()
reference_points = new_reference_points.detach()
if self.return_intermediate:
intermediate.append(output_after_norm)
intermediate_reference_points.append(
new_reference_points if self.look_forward_twice else reference_points
)
if self.return_intermediate:
return torch.stack(intermediate), torch.stack(intermediate_reference_points)
return output_after_norm, reference_points
def build_global_rpe_decomp_decoder(args):
decoder_layer = GlobalDecoderLayer(
d_model=args.hidden_dim,
d_ffn=args.dim_feedforward,
dropout=args.dropout,
activation="relu",
n_heads=args.nheads,
norm_type=args.norm_type,
rpe_hidden_dim=args.decoder_rpe_hidden_dim,
rpe_type=args.decoder_rpe_type,
feature_stride=args.proposal_in_stride,
reparam=args.reparam,
)
decoder = GlobalDecoder(
decoder_layer,
num_layers=args.dec_layers,
return_intermediate=True,
look_forward_twice=args.look_forward_twice,
use_checkpoint=args.decoder_use_checkpoint,
d_model=args.hidden_dim,
norm_type=args.norm_type,
reparam=args.reparam,
)
return decoder