| 7 | from einops import repeat |
| 8 | |
| 9 | class DynamicsModel(nn.Module): |
| 10 | def __init__(self, frame_size=(128, 128), patch_size=4, embed_dim=128, num_heads=8, |
| 11 | hidden_dim=128, num_blocks=4, num_bins=4, n_actions=8, conditioning_dim=3, latent_dim=5, |
| 12 | use_moe=False, num_experts=4, top_k_experts=2, moe_aux_loss_coeff=0.01): |
| 13 | super().__init__() |
| 14 | H, W = frame_size |
| 15 | codebook_size = num_bins**latent_dim |
| 16 | |
| 17 | self.latent_embed = nn.Linear(latent_dim, embed_dim) |
| 18 | self.transformer = STTransformer( |
| 19 | embed_dim, num_heads, hidden_dim, num_blocks, causal=True, |
| 20 | conditioning_dim=conditioning_dim, |
| 21 | use_moe=use_moe, num_experts=num_experts, |
| 22 | top_k_experts=top_k_experts, moe_aux_loss_coeff=moe_aux_loss_coeff, |
| 23 | ) |
| 24 | self.output_mlp = nn.Linear(embed_dim, codebook_size) |
| 25 | |
| 26 | # shared spatial-only PE (zeros in temporal tail) |
| 27 | pe_spatial = build_spatial_only_pe((H, W), patch_size, embed_dim, device='cpu', dtype=torch.float32) # [1,P,E] |
| 28 | self.register_buffer("pos_spatial_dec", pe_spatial, persistent=False) |
| 29 | |
| 30 | # learnable mask token latent |
| 31 | # TODO; try leanable mask embedding in embed space instead of latent space |
| 32 | self.mask_token = nn.Parameter(torch.randn(1, 1, 1, latent_dim) * 0.02) # [1, 1, 1, L] |
| 33 | |
| 34 | def forward(self, discrete_latents, training=True, conditioning=None, targets=None): |
| 35 | # discrete_latents: [B, T, P, L] |
| 36 | # targets: [B, T, P] indices |
| 37 | # conditioning: [B, T, A] |
| 38 | B, T, P, L = discrete_latents.shape |
| 39 | |
| 40 | # convert latents to float for embedding |
| 41 | discrete_latents = discrete_latents.to(dtype=torch.float32) |
| 42 | |
| 43 | # apply MaskGIT random masking during training |
| 44 | if training and self.training: |
| 45 | # per-batch mask ratio in [0.5, 1.0) |
| 46 | mask_ratio = 0.5 + torch.rand((), device=discrete_latents.device) * 0.5 |
| 47 | mask_positions = (torch.rand(B, T, P, device=discrete_latents.device) < mask_ratio) # [B, T, P] |
| 48 | |
| 49 | # guarantee at least one unmasked temporal anchor per (B, P) |
| 50 | # pick a random timestep for each (B,P) and force it to unmask |
| 51 | anchor_idx = torch.randint(0, T, (B, P), device=discrete_latents.device) # [B, P] |
| 52 | mask_positions[torch.arange(B)[:, None], anchor_idx, torch.arange(P)[None, :]] = False # [B, T, P] |
| 53 | |
| 54 | # replace selected latents with mask tokens |
| 55 | mask_token = repeat(self.mask_token.to(discrete_latents.device, discrete_latents.dtype), '1 1 1 L -> B T P L', B=B, T=T, P=P) # [B, T, P, L] |
| 56 | discrete_latents = torch.where(mask_positions.unsqueeze(-1), mask_token, discrete_latents) # [B, T, P, L] |
| 57 | else: |
| 58 | mask_positions = None |
| 59 | |
| 60 | embeddings = self.latent_embed(discrete_latents) # [B, T, P, E] |
| 61 | |
| 62 | # add spatial PE (affects only first 2/3 of dimensions) |
| 63 | # STTransformer adds temporal PE to last 1/3 of dimensions |
| 64 | embeddings = embeddings + self.pos_spatial_dec.to(embeddings.device, embeddings.dtype) |
| 65 | transformed = self.transformer(embeddings, conditioning=conditioning) # [B, T, P, E] |
| 66 |
no outgoing calls
no test coverage detected