Download GeometryForcing/external/vggt_alignment_loss.py from BonanDing/worldmem-baseline-evals: direct link, hf CLI and curl.
- Browser
- Download file 11.4 kB
-
https://huggingface.co/BonanDing/worldmem-baseline-evals/resolve/main/GeometryForcing/external/vggt_alignment_loss.py
- Command line
-
hf download hf://BonanDing/worldmem-baseline-evals/GeometryForcing/external/vggt_alignment_loss.py
-
curl -L -o vggt_alignment_loss.py https://huggingface.co/BonanDing/worldmem-baseline-evals/resolve/main/GeometryForcing/external/vggt_alignment_loss.py
11.4 kB
| import sys | |
| sys.path.append('../') | |
| import os | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from external.vggt import VGGT | |
| from einops import rearrange | |
| from torch import Tensor | |
| from external.alignment_projector import ConvProjector | |
| def mean_flat(x): | |
| """ | |
| Take the mean over all non-batch dimensions. | |
| """ | |
| return torch.mean(x, dim=list(range(1, len(x.size())))) | |
| class VGGTAlignmentLoss(nn.Module): | |
| def __init__(self, | |
| alignment_context_length: int = 16, | |
| apply_unnormalize_recon: bool = False, | |
| unormalize_lambda: float = 1, # lambda for unormalize recon loss | |
| latents_info: list = None, # the shape of generative model latents | |
| encoder_info: list = [24, 1374, 2048], # the shape of encoder output | |
| mid_channels: int = 128, | |
| vggt_layer_index=None, # -1 means last layer, -2 means second last layer, etc. | |
| ): | |
| super().__init__() | |
| self.alignment_context_length = alignment_context_length | |
| self.unormalize_lambda = unormalize_lambda | |
| # === 1. 初始化冻结的 VGGT 模型 === | |
| self.vggt_model = VGGT.from_pretrained("facebook/VGGT-1B") | |
| self.vggt_model.eval() | |
| for p in self.vggt_model.parameters(): | |
| p.requires_grad = False | |
| # import pdb; pdb.set_trace() | |
| # === 2. 构建连接器 === | |
| # for realestate 10k uvit setting: totally 7 blocks | |
| if latents_info is None: | |
| latents_info = [ | |
| [576, 32, 32], | |
| [256, 64, 64], | |
| [128, 128, 128] | |
| ] | |
| elif latents_info ==-1 or latents_info ==0 : | |
| latents_info = [ | |
| [128, 128, 128] | |
| ] | |
| elif latents_info == -2 or latents_info == 1: | |
| # 倒数第二 | |
| latents_info = [ | |
| [256, 64, 64] | |
| ] | |
| elif latents_info == -3 or latents_info == 2: | |
| latents_info = [ | |
| [576, 32, 32] | |
| ] | |
| elif latents_info == -4 or latents_info==3: | |
| latents_info = [ | |
| [1152, 16, 16], | |
| ] | |
| self.latents_info = latents_info | |
| self.encoder_info = encoder_info | |
| self.mid_channels = mid_channels | |
| self.projectors = nn.ModuleList() | |
| for c, h, w in self.latents_info: | |
| projector = ConvProjector( | |
| in_channels=c, | |
| in_h=h, | |
| in_w=w, | |
| out_h=self.encoder_info[1], | |
| out_w=self.encoder_info[2], | |
| mid_channels=mid_channels | |
| ) | |
| self.projectors.append(projector) | |
| ## this module aims to recon normalized latent feature to unnormalized feature | |
| self.apply_unnormalize_recon = apply_unnormalize_recon | |
| if self.apply_unnormalize_recon: | |
| self.feature_unormalizer = nn.Sequential( | |
| nn.Conv2d(self.encoder_info[0], mid_channels, kernel_size=1), | |
| nn.ReLU(), | |
| nn.Conv2d(mid_channels, self.encoder_info[0], kernel_size=1) | |
| ) | |
| def load_projector_from_ckpt(self,ckpt_path): | |
| ckpt = torch.load(ckpt_path,map_location='cpu',weights_only=False) | |
| new_state_dict = {k.replace("alignment_loss.",""):v for k,v in ckpt['state_dict'].items()} | |
| missing, unexpected = self.load_state_dict(new_state_dict,strict=False) | |
| # filter missing and unexpected keys | |
| # 1. vggt is loaded from pretrained model | |
| missing = [k for k in missing if not k.startswith("vggt_model.")] | |
| # 2. model is saved in ckpt but not in here | |
| unexpected = [k for k in unexpected if not k.startswith("diffusion_model.")] | |
| print(f"Load projector from {ckpt_path} successfully. with missing keys: {missing}, unexpected keys: {unexpected}") | |
| def latent_to_aggregated_token(self,latent, layer_index=-1): | |
| """ | |
| Args: | |
| latent: list[Tensor], shape [BxT,C,H,W] | |
| Returns: | |
| aggregated_tokens_lists: list[Tensor], shape [B,T,1374,2048] | |
| """ | |
| # assert self.apply_unnormalize_recon is True, "Please set apply_unnormalize_recon to True to enable unnormalize operation" | |
| aggregated_tokens_lists = [] | |
| projector = self.projectors[layer_index] | |
| if latent.dim() == 5: | |
| b, t, c, h, w = latent.shape | |
| latent = rearrange(latent, 'b t c h w -> (b t) c h w') | |
| else: | |
| b, c, h, w = latent.shape | |
| t = 16 | |
| latent = latent | |
| projected_latent = projector(latent) # [BT, c, h , w , ] | |
| ph = projected_latent.shape[2] | |
| # projected_latent = rearrange(projected_latent, '(b t) c h w -> b t c h w', t = self.temporal_length) | |
| projected_latent_flat = rearrange(projected_latent, 'b c h w -> b c (h w)') # [B, C, H*W] | |
| projected_latent_flat_norm = F.normalize(projected_latent_flat, p=2, dim=-1) # [B, C, H*W] | |
| projected_latent_norm = rearrange(projected_latent_flat_norm, 'b c (h w) -> b c h w', h=ph) # [B, C, H, W] | |
| # unnormalize | |
| if self.apply_unnormalize_recon: | |
| print(f"[external.vggt_alignment_loss][VGGTAlignmentLoss] Applying unnormalize recon operation.") | |
| pred_gt_latent = self.feature_unormalizer(projected_latent_norm) # [B,T,24,512,512] | |
| else: | |
| print(f"[external.vggt_alignment_loss][VGGTAlignmentLoss] without unnormalize recon operation.") | |
| pred_gt_latent = projected_latent_norm | |
| # uninterpolate from 512 512 -> 1374 2048 | |
| pred_gt_latent = F.interpolate(pred_gt_latent, size=(1374, 2048), mode='bilinear', align_corners=False) | |
| pred_gt_latent = rearrange(pred_gt_latent, '(b t) c h w -> b t c h w', h=1374,t=t) # [B, C, H, W] | |
| aggregated_tokens_list = [pred_gt_latent[:,:,i,:,:] for i in range(pred_gt_latent.shape[2])] # each item is [B,T,1374,2048] | |
| aggregated_tokens_lists.append(aggregated_tokens_list) | |
| return aggregated_tokens_lists | |
| def forward_vggt_prediction(self, images,latent_list,layer_index=-1,return_original_predictions=False): | |
| """ | |
| Args: | |
| images: original images, shape [B, T, C, H, W] | |
| latent_list: list[Tensor], shape [B,T,24,1374,2048] # | |
| intermidiate output: | |
| aggregated_tokens_list: List[B,N,1374,2048],len(latents)=24 | |
| Returns: | |
| point_cloud | |
| """ | |
| images = self.vggt_processor(images) # [B, T, C, H, W] | |
| latent = latent_list[-1] | |
| # import pdb; pdb.set_trace() | |
| aggregated_tokens_lists = self.latent_to_aggregated_token(latent,layer_index) # list[Tensor], shape [B,T,1374,2048] | |
| # print(f"[external.vggt_alignment_loss][VGGTAlignmentLoss][forward_vggt_prediction] Default Using Last Layer Output") | |
| aggregated_tokens_list = aggregated_tokens_lists[0] | |
| predictions,original_predictions = self.vggt_model.forward_with_external_feature( | |
| images, | |
| aggregated_tokens_list=aggregated_tokens_list, | |
| return_original_predictions=return_original_predictions | |
| ) | |
| return predictions ,original_predictions | |
| def vggt_processor(self, images: Tensor): | |
| # images: b t c h w | |
| b = images.shape[0] | |
| images = rearrange(images, 'b f c h w -> (b f) c h w') | |
| # print(f"[external.vggt_alignment_loss][VGGTAlignmentLoss][vggt_processor] using imagesize 224x224") | |
| # using a 224x224 will cause drop from 317->327(fvd) | |
| images_resized = F.interpolate(images, size=(518, 518), mode="bilinear", align_corners=False) | |
| images_resized = rearrange(images_resized, '(b f) c h w -> b f c h w', b=b) | |
| images_resized = torch.clamp(images_resized, 0.0, 1.0) | |
| return images_resized | |
| def forward(self, latents, images): | |
| """ | |
| Args: | |
| latents from UViT : (B, T * H * W, C) T*H*W = patches | |
| latents: list[Tensor], shape [B,T,C,H,W] | |
| images: Tensor, [B, C, H, W],原始图像 | |
| Returns: | |
| alignment_loss: 对齐损失(平均 cosine 距离) | |
| """ | |
| images = images[:, :self.alignment_context_length, :, :, :] # [B, T, C, H, W] | |
| images = self.vggt_processor(images) # [B, 3, T, H, W] | |
| with torch.no_grad(): | |
| # target_feats = self.vggt_model.encode(images) # list[Tensor], shape [B,T,1374, 2048] | |
| aggregated_tokens_list, patch_start_idx = self.vggt_model.shortcut_forward(images) # 24x [] | |
| # interpolate to few dimension | |
| target_h,target_w = self.encoder_info[1], self.encoder_info[2] # 1374, 2048 | |
| aggregated_tokens_list = [F.interpolate(tokens, size=(target_h, target_w), mode='bilinear', align_corners=False) for tokens in aggregated_tokens_list] | |
| aggregated_tokens = torch.stack(aggregated_tokens_list) # [B*T, 512, 512] | |
| target_feats = rearrange(aggregated_tokens,'c b t h w -> b t c h w') | |
| alignment_loss = 0. | |
| unormalize_loss = 0. | |
| assert len(latents) == len(self.projectors), f"latents length {len(latents)} should match projectors length {len(self.projectors)}" | |
| for latent, projector in zip(latents, self.projectors): | |
| # print(f"[externl.vggt_alignment_loss][VGGTAlignmentLoss][forward] latent shape: {latent.shape}, projector: {projector}") | |
| latent = latent[:,:self.alignment_context_length,...] | |
| latent = rearrange(latent, 'b t c h w -> (b t) c h w') # [B*T, C, H, W] | |
| target_feat = rearrange(target_feats, 'b t c h w -> (b t) c h w') # [B, C, H, W] | |
| # target feature : torch.Size([16, 24, 512, 512]) | |
| latent_proj = projector(latent) # [BT, C ,H, W] | |
| H,W = latent_proj.shape[2], latent_proj.shape[3] | |
| # flatten空间维度 | |
| latent_proj_flat = rearrange(latent_proj, 'b c h w -> b c (h w)') | |
| target_feat_flat = rearrange(target_feat, 'b c h w -> b c (h w)') | |
| # 假设 T是通道,S是空间flatten: | |
| latent_proj_norm = F.normalize(latent_proj_flat, p=2, dim=-1) # 归一化通道维 | |
| target_feat_norm = F.normalize(target_feat_flat, p=2, dim=-1) # B C H*W | |
| # 计算每个空间点的向量相似度(点积), | |
| # sum over通道T维度得到空间向量相似度,shape: [B, S] | |
| alignment_loss += mean_flat(-(latent_proj_norm * target_feat_norm)).sum(dim=-1) # [B, S] | |
| if self.apply_unnormalize_recon: | |
| # 计算 unnormalize loss | |
| latent_proj_norm = rearrange(latent_proj_norm, 'b c (h w) -> b c h w ',h = H) # [B, C, S, 1] | |
| # import pdb; pdb.set_trace() | |
| unormalized_latent_proj = self.feature_unormalizer(latent_proj_norm) | |
| # mse loss | |
| unormalize_loss += F.mse_loss(unormalized_latent_proj, target_feat) | |
| alignment_loss /= len(latents) | |
| if self.apply_unnormalize_recon: | |
| unormalize_loss /= len(latents) | |
| unormalize_lambda = self.unormalize_lambda | |
| unormalize_loss = unormalize_lambda * unormalize_loss | |
| loss = alignment_loss + unormalize_loss | |
| else: | |
| loss = alignment_loss | |
| return loss | |