Scheduled Commit
Browse files
groot/vla/model/trex_track_force/model.py
CHANGED
|
@@ -121,13 +121,14 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 121 |
gradient_checkpointing: bool = False,
|
| 122 |
diffusion_model_pretrained_path: str | None = None,
|
| 123 |
concat_first_frame_latent: bool = False,
|
|
|
|
| 124 |
**_: Any,
|
| 125 |
) -> None:
|
| 126 |
if action_dim != PADDED_ACTION_DIM:
|
| 127 |
raise ValueError("physical 62-D action must use WAN width 64")
|
| 128 |
if action_horizon != TRACK_HORIZON:
|
| 129 |
raise ValueError("action and track future horizons must both be 16")
|
| 130 |
-
if track_points != NUM_TRACK_POINTS:
|
| 131 |
raise ValueError("T-Rex track layout is fixed at 250 points")
|
| 132 |
|
| 133 |
super().__init__(
|
|
@@ -158,7 +159,10 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 158 |
concat_first_frame_latent=concat_first_frame_latent,
|
| 159 |
)
|
| 160 |
self.action_horizon = action_horizon
|
| 161 |
-
self.
|
|
|
|
|
|
|
|
|
|
| 162 |
self.track_horizon = track_horizon
|
| 163 |
self.max_track_history = max_track_history
|
| 164 |
self.max_blocks = max_blocks
|
|
@@ -188,16 +192,17 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 188 |
compatible_blocks.append(block)
|
| 189 |
self.blocks = compatible_blocks
|
| 190 |
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
|
|
|
| 201 |
self.modality_embedding = nn.Embedding(len(TokenType), dim)
|
| 202 |
self.obs_input_projection = (
|
| 203 |
nn.Identity()
|
|
@@ -212,7 +217,8 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 212 |
)
|
| 213 |
|
| 214 |
head_dim = dim // num_heads
|
| 215 |
-
|
|
|
|
| 216 |
self.freqs_obs_1d = rope_params(
|
| 217 |
max_blocks * max(frame_seqlen * num_frame_per_block, 1) + frame_seqlen,
|
| 218 |
head_dim,
|
|
@@ -507,8 +513,8 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 507 |
obs: torch.Tensor,
|
| 508 |
action: torch.Tensor,
|
| 509 |
state: torch.Tensor,
|
| 510 |
-
track_past: torch.Tensor,
|
| 511 |
-
track_future: torch.Tensor,
|
| 512 |
) -> tuple[torch.Tensor, BlockTokenLayout]:
|
| 513 |
layout = BlockTokenLayout(
|
| 514 |
num_blocks=obs.shape[1],
|
|
@@ -527,13 +533,20 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 527 |
].view(1, 1, self.dim)
|
| 528 |
)
|
| 529 |
for block_index in range(layout.num_blocks):
|
| 530 |
-
|
| 531 |
(obs[:, block_index], TokenType.OBS),
|
| 532 |
(action[:, block_index], TokenType.ACTION),
|
| 533 |
(state[:, block_index], TokenType.STATE),
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 537 |
groups.append(
|
| 538 |
values
|
| 539 |
+ self.modality_embedding.weight[int(token_type)].view(
|
|
@@ -564,21 +577,26 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 564 |
-1, layout.action_tokens_per_block
|
| 565 |
),
|
| 566 |
zeros.expand(-1, layout.state_tokens_per_block),
|
| 567 |
-
zeros.expand(-1, layout.track_points),
|
| 568 |
-
track_tau[:, block : block + 1].expand(
|
| 569 |
-
-1, layout.track_points
|
| 570 |
-
),
|
| 571 |
)
|
| 572 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 573 |
return torch.cat(groups, dim=1)
|
| 574 |
|
| 575 |
def _pack_rope(
|
| 576 |
self, layout: BlockTokenLayout, video_rope: torch.Tensor, device: torch.device
|
| 577 |
) -> torch.Tensor:
|
| 578 |
-
|
| 579 |
-
self.
|
| 580 |
-
|
| 581 |
-
|
| 582 |
video_rope = self._squeeze_rope(video_rope).to(device)
|
| 583 |
groups: list[torch.Tensor] = []
|
| 584 |
video_cursor = 0
|
|
@@ -606,21 +624,22 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 606 |
]
|
| 607 |
)
|
| 608 |
)
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
self.
|
|
|
|
|
|
|
| 613 |
)
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
)
|
| 623 |
-
)
|
| 624 |
packed = torch.cat(groups, dim=0)
|
| 625 |
if packed.shape[0] != layout.sequence_length:
|
| 626 |
raise RuntimeError("packed RoPE does not match token layout")
|
|
@@ -629,17 +648,23 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 629 |
@staticmethod
|
| 630 |
def _unpack(
|
| 631 |
values: torch.Tensor, layout: BlockTokenLayout
|
| 632 |
-
) -> tuple[
|
|
|
|
|
|
|
| 633 |
obs, action, past, future = [], [], [], []
|
| 634 |
for block in range(layout.num_blocks):
|
| 635 |
slices = layout.slices(block)
|
| 636 |
obs.append(values[:, slices[TokenType.OBS]])
|
| 637 |
action.append(values[:, slices[TokenType.ACTION]])
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 643 |
|
| 644 |
def _causal_coarse_memory(
|
| 645 |
self,
|
|
@@ -707,11 +732,11 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 707 |
*,
|
| 708 |
action: torch.Tensor,
|
| 709 |
state: torch.Tensor,
|
| 710 |
-
past_track_xy: torch.Tensor,
|
| 711 |
-
past_track_visibility: torch.Tensor | None,
|
| 712 |
-
future_track_xy: torch.Tensor,
|
| 713 |
-
future_track_visibility: torch.Tensor | None,
|
| 714 |
-
tau: float | torch.Tensor,
|
| 715 |
obs_tokens: torch.Tensor | None = None,
|
| 716 |
conditioning_obs_tokens: torch.Tensor | None = None,
|
| 717 |
video_latents: torch.Tensor | None = None,
|
|
@@ -767,23 +792,30 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 767 |
action_tokens, state_tokens = self._encode_action_state(
|
| 768 |
action, state, action_tau_tensor
|
| 769 |
)
|
| 770 |
-
|
| 771 |
-
past_track_xy
|
| 772 |
-
|
| 773 |
-
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 780 |
hidden, layout = self._pack_tokens(
|
| 781 |
conditioning,
|
| 782 |
obs,
|
| 783 |
action_tokens,
|
| 784 |
state_tokens,
|
| 785 |
-
|
| 786 |
-
|
| 787 |
)
|
| 788 |
|
| 789 |
token_tau = self._pack_scalar_by_layout(
|
|
@@ -838,7 +870,9 @@ class CausalWanTrackForceModel(DreamZeroCausalWanModel):
|
|
| 838 |
action_flow = self.action_decoder(
|
| 839 |
action_hidden.flatten(1, 2), category
|
| 840 |
).view(batch, num_blocks, self.action_horizon, self.action_dim)
|
| 841 |
-
track_flow =
|
|
|
|
|
|
|
| 842 |
obs_flow_tokens = self.obs_token_decoder(obs_hidden)
|
| 843 |
|
| 844 |
memory_indices = layout.indices(
|
|
|
|
| 121 |
gradient_checkpointing: bool = False,
|
| 122 |
diffusion_model_pretrained_path: str | None = None,
|
| 123 |
concat_first_frame_latent: bool = False,
|
| 124 |
+
use_track: bool = True,
|
| 125 |
**_: Any,
|
| 126 |
) -> None:
|
| 127 |
if action_dim != PADDED_ACTION_DIM:
|
| 128 |
raise ValueError("physical 62-D action must use WAN width 64")
|
| 129 |
if action_horizon != TRACK_HORIZON:
|
| 130 |
raise ValueError("action and track future horizons must both be 16")
|
| 131 |
+
if use_track and track_points != NUM_TRACK_POINTS:
|
| 132 |
raise ValueError("T-Rex track layout is fixed at 250 points")
|
| 133 |
|
| 134 |
super().__init__(
|
|
|
|
| 159 |
concat_first_frame_latent=concat_first_frame_latent,
|
| 160 |
)
|
| 161 |
self.action_horizon = action_horizon
|
| 162 |
+
self.use_track = use_track
|
| 163 |
+
# With tracks disabled the packed layout carries zero track tokens,
|
| 164 |
+
# which every layout/mask helper already supports.
|
| 165 |
+
self.track_points = track_points if use_track else 0
|
| 166 |
self.track_horizon = track_horizon
|
| 167 |
self.max_track_history = max_track_history
|
| 168 |
self.max_blocks = max_blocks
|
|
|
|
| 192 |
compatible_blocks.append(block)
|
| 193 |
self.blocks = compatible_blocks
|
| 194 |
|
| 195 |
+
if use_track:
|
| 196 |
+
self.track_encoder = TrackEncoder(
|
| 197 |
+
dim,
|
| 198 |
+
num_points=track_points,
|
| 199 |
+
future_horizon=track_horizon,
|
| 200 |
+
max_history_frames=max_track_history,
|
| 201 |
+
max_blocks=max_blocks,
|
| 202 |
+
)
|
| 203 |
+
self.track_decoder = TrackDecoder(
|
| 204 |
+
dim, num_points=track_points, future_horizon=track_horizon
|
| 205 |
+
)
|
| 206 |
self.modality_embedding = nn.Embedding(len(TokenType), dim)
|
| 207 |
self.obs_input_projection = (
|
| 208 |
nn.Identity()
|
|
|
|
| 217 |
)
|
| 218 |
|
| 219 |
head_dim = dim // num_heads
|
| 220 |
+
if use_track:
|
| 221 |
+
self.freqs_track = rope_params(max_blocks * 2 * track_points, head_dim)
|
| 222 |
self.freqs_obs_1d = rope_params(
|
| 223 |
max_blocks * max(frame_seqlen * num_frame_per_block, 1) + frame_seqlen,
|
| 224 |
head_dim,
|
|
|
|
| 513 |
obs: torch.Tensor,
|
| 514 |
action: torch.Tensor,
|
| 515 |
state: torch.Tensor,
|
| 516 |
+
track_past: torch.Tensor | None,
|
| 517 |
+
track_future: torch.Tensor | None,
|
| 518 |
) -> tuple[torch.Tensor, BlockTokenLayout]:
|
| 519 |
layout = BlockTokenLayout(
|
| 520 |
num_blocks=obs.shape[1],
|
|
|
|
| 533 |
].view(1, 1, self.dim)
|
| 534 |
)
|
| 535 |
for block_index in range(layout.num_blocks):
|
| 536 |
+
entries: list[tuple[torch.Tensor, TokenType]] = [
|
| 537 |
(obs[:, block_index], TokenType.OBS),
|
| 538 |
(action[:, block_index], TokenType.ACTION),
|
| 539 |
(state[:, block_index], TokenType.STATE),
|
| 540 |
+
]
|
| 541 |
+
if layout.track_points:
|
| 542 |
+
assert track_past is not None and track_future is not None
|
| 543 |
+
entries.extend(
|
| 544 |
+
(
|
| 545 |
+
(track_past[:, block_index], TokenType.TRACK_PAST),
|
| 546 |
+
(track_future[:, block_index], TokenType.TRACK_FUTURE),
|
| 547 |
+
)
|
| 548 |
+
)
|
| 549 |
+
for values, token_type in entries:
|
| 550 |
groups.append(
|
| 551 |
values
|
| 552 |
+ self.modality_embedding.weight[int(token_type)].view(
|
|
|
|
| 577 |
-1, layout.action_tokens_per_block
|
| 578 |
),
|
| 579 |
zeros.expand(-1, layout.state_tokens_per_block),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 580 |
)
|
| 581 |
)
|
| 582 |
+
if layout.track_points:
|
| 583 |
+
groups.extend(
|
| 584 |
+
(
|
| 585 |
+
zeros.expand(-1, layout.track_points),
|
| 586 |
+
track_tau[:, block : block + 1].expand(
|
| 587 |
+
-1, layout.track_points
|
| 588 |
+
),
|
| 589 |
+
)
|
| 590 |
+
)
|
| 591 |
return torch.cat(groups, dim=1)
|
| 592 |
|
| 593 |
def _pack_rope(
|
| 594 |
self, layout: BlockTokenLayout, video_rope: torch.Tensor, device: torch.device
|
| 595 |
) -> torch.Tensor:
|
| 596 |
+
self.freqs_action = self.freqs_action.to(device)
|
| 597 |
+
self.freqs_state = self.freqs_state.to(device)
|
| 598 |
+
if self.use_track:
|
| 599 |
+
self.freqs_track = self.freqs_track.to(device)
|
| 600 |
video_rope = self._squeeze_rope(video_rope).to(device)
|
| 601 |
groups: list[torch.Tensor] = []
|
| 602 |
video_cursor = 0
|
|
|
|
| 624 |
]
|
| 625 |
)
|
| 626 |
)
|
| 627 |
+
if layout.track_points:
|
| 628 |
+
track_start = block * 2 * self.track_points
|
| 629 |
+
groups.append(
|
| 630 |
+
self._squeeze_rope(
|
| 631 |
+
self.freqs_track[track_start : track_start + self.track_points]
|
| 632 |
+
)
|
| 633 |
)
|
| 634 |
+
groups.append(
|
| 635 |
+
self._squeeze_rope(
|
| 636 |
+
self.freqs_track[
|
| 637 |
+
track_start
|
| 638 |
+
+ self.track_points : track_start
|
| 639 |
+
+ 2 * self.track_points
|
| 640 |
+
]
|
| 641 |
+
)
|
| 642 |
)
|
|
|
|
| 643 |
packed = torch.cat(groups, dim=0)
|
| 644 |
if packed.shape[0] != layout.sequence_length:
|
| 645 |
raise RuntimeError("packed RoPE does not match token layout")
|
|
|
|
| 648 |
@staticmethod
|
| 649 |
def _unpack(
|
| 650 |
values: torch.Tensor, layout: BlockTokenLayout
|
| 651 |
+
) -> tuple[
|
| 652 |
+
torch.Tensor, torch.Tensor, torch.Tensor | None, torch.Tensor | None
|
| 653 |
+
]:
|
| 654 |
obs, action, past, future = [], [], [], []
|
| 655 |
for block in range(layout.num_blocks):
|
| 656 |
slices = layout.slices(block)
|
| 657 |
obs.append(values[:, slices[TokenType.OBS]])
|
| 658 |
action.append(values[:, slices[TokenType.ACTION]])
|
| 659 |
+
if layout.track_points:
|
| 660 |
+
past.append(values[:, slices[TokenType.TRACK_PAST]])
|
| 661 |
+
future.append(values[:, slices[TokenType.TRACK_FUTURE]])
|
| 662 |
+
return (
|
| 663 |
+
torch.stack(obs, dim=1),
|
| 664 |
+
torch.stack(action, dim=1),
|
| 665 |
+
torch.stack(past, dim=1) if past else None,
|
| 666 |
+
torch.stack(future, dim=1) if future else None,
|
| 667 |
+
)
|
| 668 |
|
| 669 |
def _causal_coarse_memory(
|
| 670 |
self,
|
|
|
|
| 732 |
*,
|
| 733 |
action: torch.Tensor,
|
| 734 |
state: torch.Tensor,
|
| 735 |
+
past_track_xy: torch.Tensor | None = None,
|
| 736 |
+
past_track_visibility: torch.Tensor | None = None,
|
| 737 |
+
future_track_xy: torch.Tensor | None = None,
|
| 738 |
+
future_track_visibility: torch.Tensor | None = None,
|
| 739 |
+
tau: float | torch.Tensor = 1.0,
|
| 740 |
obs_tokens: torch.Tensor | None = None,
|
| 741 |
conditioning_obs_tokens: torch.Tensor | None = None,
|
| 742 |
video_latents: torch.Tensor | None = None,
|
|
|
|
| 792 |
action_tokens, state_tokens = self._encode_action_state(
|
| 793 |
action, state, action_tau_tensor
|
| 794 |
)
|
| 795 |
+
if self.use_track:
|
| 796 |
+
if past_track_xy is None or future_track_xy is None:
|
| 797 |
+
raise ValueError("track inputs are required when use_track=True")
|
| 798 |
+
track_tokens = self.track_encoder(
|
| 799 |
+
past_track_xy.to(device, dtype),
|
| 800 |
+
None
|
| 801 |
+
if past_track_visibility is None
|
| 802 |
+
else past_track_visibility.to(device, dtype),
|
| 803 |
+
future_track_xy.to(device, dtype),
|
| 804 |
+
# Future visibility is a supervision mask, not an input. Feeding
|
| 805 |
+
# it here would leak target occlusion unavailable at inference.
|
| 806 |
+
None,
|
| 807 |
+
)
|
| 808 |
+
track_past_tokens = track_tokens.past_tokens
|
| 809 |
+
track_future_tokens = track_tokens.future_tokens
|
| 810 |
+
else:
|
| 811 |
+
track_past_tokens = track_future_tokens = None
|
| 812 |
hidden, layout = self._pack_tokens(
|
| 813 |
conditioning,
|
| 814 |
obs,
|
| 815 |
action_tokens,
|
| 816 |
state_tokens,
|
| 817 |
+
track_past_tokens,
|
| 818 |
+
track_future_tokens,
|
| 819 |
)
|
| 820 |
|
| 821 |
token_tau = self._pack_scalar_by_layout(
|
|
|
|
| 870 |
action_flow = self.action_decoder(
|
| 871 |
action_hidden.flatten(1, 2), category
|
| 872 |
).view(batch, num_blocks, self.action_horizon, self.action_dim)
|
| 873 |
+
track_flow = (
|
| 874 |
+
self.track_decoder(future_hidden) if self.use_track else None
|
| 875 |
+
)
|
| 876 |
obs_flow_tokens = self.obs_token_decoder(obs_hidden)
|
| 877 |
|
| 878 |
memory_indices = layout.indices(
|