belka_gate_fusions: add gate-variants for CVGL exp.
This commit is contained in:
@@ -46,6 +46,7 @@ class ProjectionHead(nn.Module):
|
|||||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
return F.normalize(self.proj(x), dim=-1)
|
return F.normalize(self.proj(x), dim=-1)
|
||||||
|
|
||||||
|
#! GATE-FUSION ORIG ---------------------------------
|
||||||
|
|
||||||
@gin.configurable
|
@gin.configurable
|
||||||
class GatedFusion(nn.Module):
|
class GatedFusion(nn.Module):
|
||||||
@@ -82,6 +83,120 @@ class GatedFusion(nn.Module):
|
|||||||
return torch.sigmoid(self.alpha).item()
|
return torch.sigmoid(self.alpha).item()
|
||||||
|
|
||||||
|
|
||||||
|
#! GATE-FUSIONS MODIFICATIONS ---------------------------------
|
||||||
|
#! in_dim = 1024
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class ResidualGateType(Enum):
|
||||||
|
simple_residual_one_gate = 0,
|
||||||
|
cross_gate = 1,
|
||||||
|
gate_sum = 2,
|
||||||
|
alpha_res_cat = 3,
|
||||||
|
alpha_res_sum = 4
|
||||||
|
|
||||||
|
# TODO: add GatedFusionresidual class to gin
|
||||||
|
@gin.configurable
|
||||||
|
class GatedFusionresidual(nn.Module):
|
||||||
|
"""Learnable gated fusion of image and text embeddings.
|
||||||
|
|
||||||
|
V1 - Simple residual gating with 1 common gate:
|
||||||
|
V2 - Cross residual gating with 2 cross-gates
|
||||||
|
V3 - Gate + Simple Sum of feats x & y
|
||||||
|
V4 - Alpha-weighted residual concat (per sample)
|
||||||
|
V5 - Alpha-weighted residual sum (per sample)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, init_gate: float = 0.7, in_dim = 1024, baseline_mode: bool = False) -> None:
|
||||||
|
super().__init__()
|
||||||
|
# alpha is in logit space: sigmoid(alpha) = init_gate
|
||||||
|
init_alpha = torch.log(torch.tensor(init_gate / (1.0 - init_gate)))
|
||||||
|
self.alpha = nn.Parameter(init_alpha)
|
||||||
|
|
||||||
|
# alphas for separated cases
|
||||||
|
init_alpha_img_cross_gate = torch.log(torch.tensor(init_gate / (1.0 - init_gate)))
|
||||||
|
init_alpha_text_cross_gate = torch.log(torch.tensor(init_gate / (1.0 - init_gate)))
|
||||||
|
self.alpha_img = nn.Parameter(init_alpha_img_cross_gate)
|
||||||
|
self.alpha_text = nn.Parameter(init_alpha_text_cross_gate)
|
||||||
|
|
||||||
|
# weight for sum and cat residual
|
||||||
|
self.final_cat_residual_proj = nn.Linear(in_dim * 2, in_dim)
|
||||||
|
self.weight_net_for_sum = nn.Linear(in_dim, 1)
|
||||||
|
self.weight_net_for_cat = nn.Linear(in_dim * 2, 1)
|
||||||
|
|
||||||
|
self.baseline_mode = baseline_mode
|
||||||
|
|
||||||
|
def FuseSRGF(self,
|
||||||
|
img_feat: torch.Tensor,
|
||||||
|
text_feat: torch.Tensor | None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
gate = torch.sigmoid(self.alpha)
|
||||||
|
img_res = img_feat * gate + img_feat
|
||||||
|
text_res = text_feat * (1 - gate) + text_feat
|
||||||
|
fused_vec = img_res + text_res
|
||||||
|
return fused_vec
|
||||||
|
|
||||||
|
def FuseRCGF(self,
|
||||||
|
img_feat: torch.Tensor,
|
||||||
|
text_feat: torch.Tensor | None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
gate_img = torch.sigmoid(self.alpha_img)
|
||||||
|
gate_text = torch.sigmoid(self.alpha_text)
|
||||||
|
z_img = img_feat + gate_text * img_feat
|
||||||
|
z_text = text_feat + gate_img * text_feat
|
||||||
|
fused_vec = z_img + z_text
|
||||||
|
return fused_vec
|
||||||
|
|
||||||
|
def FuseGSUM(self,
|
||||||
|
img_feat: torch.Tensor,
|
||||||
|
text_feat: torch.Tensor | None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
gate = torch.sigmoid(self.alpha)
|
||||||
|
fuzed_vec = img_feat + text_feat + gate * img_feat + (1.0 - gate) * text_feat
|
||||||
|
return fuzed_vec
|
||||||
|
|
||||||
|
def FuseARGFSum(
|
||||||
|
self,
|
||||||
|
img_feat: torch.Tensor,
|
||||||
|
text_feat: torch.Tensor | None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
gate = torch.sigmoid(self.alpha)
|
||||||
|
residual = img_feat + text_feat
|
||||||
|
res_weight = torch.sigmoid(self.weight_net_for_sum(residual))
|
||||||
|
fuzed_vec = gate * img_feat + (1.0 - gate) * text_feat + res_weight * residual
|
||||||
|
return fuzed_vec
|
||||||
|
|
||||||
|
def FuseARGFCat(
|
||||||
|
self,
|
||||||
|
img_feat: torch.Tensor,
|
||||||
|
text_feat: torch.Tensor | None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
gate = torch.sigmoid(self.alpha)
|
||||||
|
cat_vec = torch.cat([img_feat, text_feat], dim=-1)
|
||||||
|
residual = self.final_cat_residual_proj(cat_vec)
|
||||||
|
res_weight = torch.sigmoid(self.weight_net_for_cat(cat_vec))
|
||||||
|
fuzed_vec = gate * img_feat + (1.0 - gate) * text_feat + res_weight * residual
|
||||||
|
return fuzed_vec
|
||||||
|
|
||||||
|
def forward(
|
||||||
|
self,
|
||||||
|
img_feat: torch.Tensor,
|
||||||
|
text_feat: torch.Tensor | None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
if text_feat is None or self.baseline_mode:
|
||||||
|
return img_feat
|
||||||
|
gate = torch.sigmoid(self.alpha)
|
||||||
|
|
||||||
|
# TODO: switch forwards here
|
||||||
|
|
||||||
|
return gate * img_feat + (1.0 - gate) * text_feat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@gin.configurable
|
@gin.configurable
|
||||||
class DualEncoderCaptionTest(nn.Module):
|
class DualEncoderCaptionTest(nn.Module):
|
||||||
"""GeoRSCLIP dual encoder with gated text fusion on query branch.
|
"""GeoRSCLIP dual encoder with gated text fusion on query branch.
|
||||||
|
|||||||
Reference in New Issue
Block a user