BonanDing's picture
Add isolated Minecraft and RE10K baseline evaluation suite
59630ba verified
Raw History Blame Contribute Delete
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