SII-CDZ commited on
Commit
04da911
·
verified ·
1 Parent(s): abfc35a

Delete modeling_prismatic.py.back.20260315_133310

Browse files
modeling_prismatic.py.back.20260315_133310 DELETED
@@ -1,1498 +0,0 @@
1
- """
2
- modeling_prismatic.py
3
-
4
- Core HuggingFace-style PrismaticPreTrainedModel and PrismaticForConditionalGeneration class definitions.
5
- Inherits from the default `transformers.PretrainedModel`. Meant to be standalone and self-contained,
6
- but exactly replicate the logic in `prismatic.models.vlms.prismatic.py`.
7
- """
8
-
9
- import logging
10
- from dataclasses import dataclass
11
- from functools import partial
12
- import math
13
- import random
14
- from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple, Union
15
- import numpy as np
16
- import timm
17
- import tokenizers
18
- import torch
19
- import torch.nn as nn
20
- import transformers
21
- from timm.models.vision_transformer import LayerScale
22
- from transformers import AutoModelForCausalLM, PretrainedConfig, PreTrainedModel
23
- from transformers.modeling_outputs import ModelOutput
24
-
25
- from prismatic.training.train_utils import (
26
- get_current_action_mask,
27
- get_next_actions_mask,
28
- )
29
- from prismatic.vla.constants import (
30
- ACTION_DIM,
31
- ACTION_PROPRIO_NORMALIZATION_TYPE,
32
- ACTION_TOKEN_BEGIN_IDX,
33
- IGNORE_INDEX,
34
- NUM_ACTIONS_CHUNK,
35
- STOP_INDEX,
36
- NormalizationType,
37
- NUM_TOKENS
38
- )
39
- from .configuration_prismatic import OpenVLAConfig, PrismaticConfig
40
-
41
-
42
-
43
- # Set up logger
44
- logger = logging.getLogger(__name__)
45
-
46
-
47
- # === Utility Functions for Monkey-Patching ===
48
- def unpack_tuple(fn: Callable[[Any], Tuple[Any]]) -> Callable[[Any], Any]:
49
- def wrapper(*args: Any, **kwargs: Any) -> Any:
50
- result = fn(*args, **kwargs)
51
- return result[0] if isinstance(result, tuple) else result
52
-
53
- return wrapper
54
-
55
-
56
-
57
- # HF Transformers overwrites parameters with names containing `gamma`; we're going to patch VisionBackbone.LayerScale.
58
- # =>> TIMM :: https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/vision_transformer.py#L109
59
- # =>> Transformers :: https://github.com/huggingface/transformers/blob/main/src/transformers/modeling_utils.py#L3960
60
- def _ls_new_forward(self, x: torch.Tensor) -> torch.Tensor:
61
- return x.mul_(self.scale_factor) if self.inplace else x * self.scale_factor
62
-
63
-
64
-
65
- def ls_apply_patch(ls_module: LayerScale):
66
- ls_module.scale_factor = nn.Parameter(ls_module.gamma.clone())
67
- ls_module.forward = _ls_new_forward.__get__(ls_module, LayerScale)
68
- del ls_module.gamma
69
-
70
-
71
-
72
- # === Prismatic Vision Backbone (nn.Module) Definitions (w/ Fused Backbone Support) ===
73
- class PrismaticVisionBackbone(nn.Module):
74
- """
75
- Vision backbone for Prismatic models that handles image feature extraction.
76
-
77
- Supports both single backbone (e.g., SigLIP) and fused backbone (e.g., SigLIP + DINOv2) configurations.
78
- For fused backbones, features from both models are concatenated along the feature dimension.
79
- """
80
-
81
- def __init__(
82
- self,
83
- use_fused_vision_backbone: bool,
84
- image_sizes: List[int],
85
- timm_model_ids: List[str],
86
- timm_override_act_layers: List[Optional[str]],
87
- ) -> None:
88
- """
89
- Initialize the vision backbone.
90
-
91
- Args:
92
- use_fused_vision_backbone: Whether to use two backbones and fuse their features
93
- image_sizes: List of image sizes for each backbone
94
- timm_model_ids: List of TIMM model IDs to use for each backbone
95
- timm_override_act_layers: List of activation layer overrides for each backbone
96
- """
97
- super().__init__()
98
- self.use_fused_vision_backbone = use_fused_vision_backbone
99
- self.num_images_in_input = 1 # Default value, can be overridden later
100
-
101
- # Validate number of (fused) vision backbones
102
- if len(timm_model_ids) > 2:
103
- raise ValueError("Prismatic models only support up to 2 (fused) vision backbones!")
104
-
105
- # Create primary featurizer
106
- self.featurizer = self._create_featurizer(
107
- model_id=timm_model_ids[0], img_size=image_sizes[0], act_layer=timm_override_act_layers[0]
108
- )
109
- self.embed_dim = self.featurizer.embed_dim
110
-
111
- # Create secondary featurizer if using fused backbone
112
- if self.use_fused_vision_backbone:
113
- self.fused_featurizer = self._create_featurizer(
114
- model_id=timm_model_ids[1], img_size=image_sizes[1], act_layer=timm_override_act_layers[1]
115
- )
116
- self.embed_dim += self.fused_featurizer.embed_dim
117
-
118
- # Patch LayerScale modules for HF compatibility
119
- self._patch_layer_scales()
120
-
121
-
122
- def _create_featurizer(self, model_id: str, img_size: int, act_layer: Optional[str]) -> nn.Module:
123
- """
124
- Create a TIMM-based featurizer model with appropriate configurations.
125
-
126
- Args:
127
- model_id: The TIMM model ID to load
128
- img_size: Input image size for the model
129
- act_layer: Override for the activation layer type
130
-
131
- Returns:
132
- A configured featurizer model
133
- """
134
- featurizer = timm.create_model(
135
- model_id,
136
- pretrained=False,
137
- num_classes=0,
138
- img_size=img_size,
139
- act_layer=act_layer,
140
- )
141
-
142
- # Monkey-patch the forward function to extract the second-to-last layer features
143
- num_blocks = len(featurizer.blocks)
144
- featurizer.forward = unpack_tuple(partial(featurizer.get_intermediate_layers, n={num_blocks - 2}))
145
-
146
- return featurizer
147
-
148
-
149
- def _patch_layer_scales(self) -> None:
150
- """
151
- Patch all LayerScale modules to be compatible with HF's parameter naming.
152
-
153
- HF Transformers overwrites parameters with names containing 'gamma',
154
- so we need to rename and modify the forward method.
155
- """
156
- # Patch primary featurizer
157
- for module in self.featurizer.modules():
158
- if isinstance(module, LayerScale):
159
- ls_apply_patch(module)
160
-
161
- # Patch secondary featurizer if it exists
162
- if self.use_fused_vision_backbone:
163
- for module in self.fused_featurizer.modules():
164
- if isinstance(module, LayerScale):
165
- ls_apply_patch(module)
166
-
167
-
168
- def get_num_patches(self) -> int:
169
- """
170
- Returns the number of vision patches output by the vision backbone.
171
-
172
- Returns:
173
- Number of patches per image
174
- """
175
- return self.featurizer.patch_embed.num_patches
176
-
177
-
178
- def get_num_images_in_input(self) -> int:
179
- """
180
- Returns the number of input images for the vision backbone.
181
-
182
- Returns:
183
- Number of images expected in the input
184
- """
185
- return self.num_images_in_input
186
-
187
-
188
- def set_num_images_in_input(self, num_images_in_input: int) -> None:
189
- """
190
- Sets the number of input images for the vision backbone.
191
-
192
- Args:
193
- num_images_in_input: Number of images to expect in the input
194
- """
195
- self.num_images_in_input = num_images_in_input
196
-
197
-
198
- def forward(self, pixel_values: torch.Tensor) -> torch.Tensor:
199
- """
200
- Implements the forward pass for the vision backbone.
201
-
202
- If `self.use_fused_vision_backbone == True`, uses both SigLIP and DINOv2 transformers to extract visual features
203
- (otherwise uses SigLIP only). Allows multi-image inputs (but only for fused vision backbone).
204
-
205
- Args:
206
- pixel_values (torch.Tensor): Pixels for input image(s), (B, C, H, W).
207
- """
208
- if self.num_images_in_input == 1:
209
- if not self.use_fused_vision_backbone:
210
- return self.featurizer(pixel_values)
211
-
212
- # Split `pixel_values :: [bsz, 2 * 3, resolution, resolution]` =>> featurize =>> channel stack
213
- img, img_fused = torch.split(pixel_values, [3, 3], dim=1)
214
- patches, patches_fused = self.featurizer(img), self.fused_featurizer(img_fused)
215
-
216
- return torch.cat([patches, patches_fused], dim=2)
217
-
218
- else:
219
- assert self.use_fused_vision_backbone, "Multi-image inputs require using fused backbone!"
220
-
221
- # Split `pixel_values` into individual images (each with 6 channels: 3 for SigLIP + 3 for DINOv2)
222
- images = torch.split(pixel_values, [6] * self.num_images_in_input, dim=1)
223
-
224
- # Process each image and collect patches
225
- all_patches = []
226
- for img in images:
227
- # Split each image further into two stacks of channels (each with 3 channels)
228
- img_regular, img_fused = torch.split(img, [3, 3], dim=1)
229
-
230
- # Get patches from both SigLIP and DINOv2 vision transformers
231
- patches = self.featurizer(img_regular)
232
- patches_fused = self.fused_featurizer(img_fused)
233
-
234
- # Concatenate SigLIP and DINOv2 patches along the hidden dimension
235
- combined_patches = torch.cat([patches, patches_fused], dim=2)
236
- all_patches.append(combined_patches)
237
-
238
- # Concatenate all patches along the patch dimension
239
- return torch.cat(all_patches, dim=1)
240
-
241
-
242
-
243
- # === Prismatic Projector (nn.Module) Definitions ===
244
- class PrismaticProjector(nn.Module):
245
- def __init__(self, use_fused_vision_backbone: bool, vision_dim: int, llm_dim: int) -> None:
246
- super().__init__()
247
- self.use_fused_vision_backbone = use_fused_vision_backbone
248
- self.vision_dim, self.llm_dim = vision_dim, llm_dim
249
-
250
- # Switch on `use_fused_vision_backbone` =>> use slightly different MLPs and projection factors!
251
- if not self.use_fused_vision_backbone:
252
- self.fc1 = nn.Linear(self.vision_dim, self.llm_dim, bias=True)
253
- self.fc2 = nn.Linear(self.llm_dim, self.llm_dim, bias=True)
254
- self.act_fn1 = nn.GELU()
255
- else:
256
- initial_projection_dim = 4 * vision_dim
257
- self.fc1 = nn.Linear(self.vision_dim, initial_projection_dim, bias=True)
258
- self.fc2 = nn.Linear(initial_projection_dim, self.llm_dim, bias=True)
259
- self.fc3 = nn.Linear(self.llm_dim, self.llm_dim, bias=True)
260
- self.act_fn1 = nn.GELU()
261
- self.act_fn2 = nn.GELU()
262
-
263
- def forward(self, img_patches: torch.Tensor) -> torch.Tensor:
264
- if not self.use_fused_vision_backbone:
265
- projected_features = self.fc1(img_patches)
266
- projected_features = self.act_fn1(projected_features)
267
- projected_features = self.fc2(projected_features)
268
- else:
269
- projected_features = self.fc1(img_patches)
270
- projected_features = self.act_fn1(projected_features)
271
- projected_features = self.fc2(projected_features)
272
- projected_features = self.act_fn2(projected_features)
273
- projected_features = self.fc3(projected_features)
274
-
275
- return projected_features
276
-
277
-
278
-
279
- # === Main HF Class Definitions ===
280
- @dataclass
281
- class PrismaticCausalLMOutputWithPast(ModelOutput):
282
- """Base class for Prismatic casual (visually-conditioned) language model outputs; also exposes visual features."""
283
-
284
- loss: Optional[torch.FloatTensor] = None
285
- logits: torch.FloatTensor = None
286
- past_key_values: Optional[Tuple[Tuple[torch.FloatTensor]]] = None
287
- hidden_states: Optional[Tuple[torch.FloatTensor, ...]] = None
288
- attentions: Optional[Tuple[torch.FloatTensor]] = None
289
-
290
- # Additions for VLMs
291
- projector_features: Optional[torch.FloatTensor] = None
292
- language_embeddings: Optional[torch.FloatTensor] = None
293
-
294
-
295
-
296
- class PrismaticPreTrainedModel(PreTrainedModel):
297
- config_class: PretrainedConfig = PrismaticConfig
298
- base_model_prefix: str = "model"
299
- supports_gradient_checkpointing: bool = True
300
-
301
- _no_split_modules: ClassVar[List[str]] = ["PrismaticProjector"]
302
- _skip_keys_device_placement: str = "past_key_values"
303
- _supports_flash_attn_2: bool = True
304
-
305
- def _init_weights(self, module: nn.Module) -> None:
306
- # Important :: this HF ported version is *not* meant for training from scratch; only inference and fine-tuning!
307
- # => As such, this init_weights code is not correct; if training VLMs from scratch, use the main codebase at
308
- # https://github.com/TRI-ML/prismatic-vlms
309
- std = (
310
- self.config.initializer_range
311
- if hasattr(self.config, "initializer_range")
312
- else self.config.text_config.initializer_range
313
- )
314
-
315
- if hasattr(module, "class_embedding"):
316
- module.class_embedding.data.normal_(mean=0.0, std=std)
317
-
318
- if isinstance(module, (nn.Linear, nn.Conv2d)):
319
- module.weight.data.normal_(mean=0.0, std=std)
320
- if module.bias is not None:
321
- module.bias.data.zero_()
322
- elif isinstance(module, nn.Embedding):
323
- module.weight.data.normal_(mean=0.0, std=std)
324
- if module.padding_idx is not None:
325
- module.weight.data[module.padding_idx].zero_()
326
-
327
- @property
328
- def _supports_sdpa(self) -> bool:
329
- """Check LLM supports SDPA Attention"""
330
- return self.language_model._supports_sdpa
331
-
332
-
333
-
334
- class PrismaticForConditionalGeneration(PrismaticPreTrainedModel):
335
- def __init__(self, config: PrismaticConfig) -> None:
336
- super().__init__(config)
337
-
338
- # [Validation] Lightweight Validate on `config` Fields + Dependency Versions
339
- if config.use_fused_vision_backbone is None:
340
- raise ValueError("Missing config field `use_fused_vision_backbone`")
341
-
342
- if timm.__version__ not in {"0.9.10", "0.9.11", "0.9.12", "0.9.16"}:
343
- raise NotImplementedError(
344
- "TIMM Version must be >= 0.9.10 and < 1.0.0 (breaking); please raise a GitHub Issue "
345
- "if you urgently need support for latest TIMM versions."
346
- )
347
-
348
- if (transformers.__version__ != "4.40.1") or (tokenizers.__version__ != "0.19.1"):
349
- logger.warning(
350
- f"Expected `transformers==4.40.1` and `tokenizers==0.19.1` but got "
351
- f"`transformers=={transformers.__version__}` and `tokenizers=={tokenizers.__version__}`; "
352
- f"there might be inference-time regressions due to dependency changes. If in doubt, please"
353
- f"use the above versions."
354
- )
355
-
356
- # Instantiate PrismaticVisionBackbone (w/ Potential Fused Backbone)
357
- self.vision_backbone = PrismaticVisionBackbone(
358
- config.use_fused_vision_backbone, config.image_sizes, config.timm_model_ids, config.timm_override_act_layers
359
- )
360
-
361
- # Create Multimodal Projector
362
- self.projector = PrismaticProjector(
363
- config.use_fused_vision_backbone,
364
- vision_dim=self.vision_backbone.embed_dim,
365
- llm_dim=config.text_config.hidden_size,
366
- )
367
-
368
- # Instantiate LLM Backbone
369
- self.language_model = AutoModelForCausalLM.from_config(
370
- config.text_config, attn_implementation=config._attn_implementation
371
- )
372
-
373
- self.vocab_size = config.text_config.vocab_size
374
- self.pad_token_id = config.pad_token_id
375
- self.llm_dim = config.text_config.hidden_size
376
-
377
- # Action query token
378
- self.action_queries = nn.Embedding(NUM_TOKENS, self.llm_dim)
379
- self.action_queries.weight.data.zero_()
380
-
381
- # Uniform wrist dropout / language conditioning flags (persisted via config)
382
- self.uniform_vision_dropout_enabled = getattr(config, "uniform_vision_dropout_enabled", False)
383
- self.uniform_vision_dropout_ratio = getattr(config, "uniform_vision_dropout_ratio", 0.0)
384
- self.language_conditioning_enabled = getattr(config, "language_conditioning_enabled", False)
385
-
386
- # HF Boilerplate =>> initializes weights via `_init_weights()` and sets gradient checkpointing
387
- self.post_init()
388
-
389
- # === `PreTrainedModel` Boilerplate ===
390
- def get_input_embeddings(self) -> nn.Module:
391
- return self.language_model.get_input_embeddings()
392
- def set_version(self, version: str):
393
- self.version = version
394
- return self.version
395
-
396
-
397
- def set_input_embeddings(self, value: nn.Module) -> None:
398
- self.language_model.set_input_embeddings(value)
399
-
400
- def get_output_embeddings(self) -> nn.Module:
401
- return self.language_model.get_output_embeddings()
402
-
403
- def set_output_embeddings(self, new_embeddings: nn.Module) -> None:
404
- self.language_model.set_output_embeddings(new_embeddings)
405
-
406
- def get_decoder(self) -> nn.Module:
407
- return self.language_model.get_decoder()
408
-
409
- def set_decoder(self, decoder: nn.Module) -> None:
410
- self.language_model.set_decoder(decoder)
411
-
412
- def tie_weights(self) -> None:
413
- self.language_model.tie_weights() # Note: `Llama-2` and `Mistral` don't tie weights (no-op)
414
-
415
- def resize_token_embeddings(
416
- self, new_num_tokens: Optional[int] = None, pad_to_multiple_of: Optional[int] = None
417
- ) -> nn.Embedding:
418
- updated_embeddings = self.language_model.resize_token_embeddings(new_num_tokens, pad_to_multiple_of)
419
-
420
- # Update config/instance variables
421
- self.config.text_config.vocab_size = updated_embeddings.num_embeddings
422
- self.vocab_size = updated_embeddings.num_embeddings
423
-
424
- return updated_embeddings
425
-
426
- def set_uniform_vision_dropout(self, enabled: bool, dropout_ratio: float) -> None:
427
- """Enable or disable uniform wrist patch dropout during multimodal masking."""
428
- self.uniform_vision_dropout_enabled = enabled
429
- self.uniform_vision_dropout_ratio = dropout_ratio
430
- setattr(self.config, "uniform_vision_dropout_enabled", enabled)
431
- setattr(self.config, "uniform_vision_dropout_ratio", float(dropout_ratio))
432
-
433
- def set_language_conditioning(self, enabled: bool) -> None:
434
- """Toggle language conditioning inputs for the action head."""
435
- self.language_conditioning_enabled = enabled
436
- setattr(self.config, "language_conditioning_enabled", enabled)
437
-
438
- def _replace_input_embeddings(self, input_embeddings, all_actions_mask, noisy_action_features):
439
- """
440
- Replace embeddings in input_embeddings at positions where all_actions_mask is True
441
- with embeddings from noisy_action_features, using vectorized operations.
442
-
443
- Args:
444
- input_embeddings: Tensor of shape (B, S, D)
445
- all_actions_mask: Boolean tensor of shape (B, S)
446
- noisy_action_features: Tensor of shape (B, K, D) where K is the number of True values in mask per sample
447
-
448
- Returns:
449
- Modified input_embeddings tensor
450
- """
451
- # Clone input to avoid modifying the original tensor
452
- new_input_embeddings = input_embeddings.clone()
453
-
454
- # Create a tensor with the same shape of input_embeddings to hold the noisy action features
455
- repositioned_noisy_action_features = torch.zeros_like(input_embeddings)
456
-
457
- # Create batch indices for splicing
458
- batch_indices = torch.arange(input_embeddings.shape[0], device=input_embeddings.device)
459
- batch_indices = batch_indices.unsqueeze(1).expand(-1, noisy_action_features.shape[1])
460
-
461
- # Get indices where mask is True for each sample
462
- masked_indices = torch.stack([torch.where(mask)[0] for mask in all_actions_mask])
463
-
464
- # Move the noisy action features into their correct positions
465
- # print(noisy_action_features.size())
466
-
467
- repositioned_noisy_action_features[batch_indices, masked_indices] = noisy_action_features
468
-
469
- # Combine original input embeddings and noisy action embeddings using the mask
470
- new_input_embeddings = torch.where(
471
- all_actions_mask.unsqueeze(-1), repositioned_noisy_action_features, new_input_embeddings
472
- )
473
-
474
- return new_input_embeddings
475
-
476
- def _process_action_masks(self, labels):
477
- """Helper to get action masks from labels"""
478
- current_action_mask = get_current_action_mask(labels)
479
- next_actions_mask = get_next_actions_mask(labels)
480
- all_actions_mask = current_action_mask | next_actions_mask # (B, seq_len)
481
- return all_actions_mask
482
-
483
- def _process_vision_features(self, pixel_values, language_embeddings=None, use_film=False):
484
- """Process vision features with optional FiLM conditioning"""
485
- if use_film:
486
- # FiLM: Infuse language inputs into visual features
487
- patch_features = self.vision_backbone(pixel_values, language_embeddings) # (bsz, 256 * num_images, D)
488
- else:
489
- patch_features = self.vision_backbone(pixel_values) # (bsz, 256 * num_images, D)
490
-
491
- # Project patch embeddings into language embedding space
492
- return self.projector(patch_features)
493
-
494
- def _process_proprio_features(self, projected_patch_embeddings, proprio, proprio_projector):
495
- """Process proprioceptive features and append to vision features"""
496
- if proprio_projector is not None and proprio is not None:
497
- # projected_patch_embeddings: (bsz, num_patches * num_images, llm_dim)
498
- # proprio: (bsz, proprio_dim) or (propro_dim,)
499
- proprio = proprio.reshape(projected_patch_embeddings.shape[0], -1) # (bsz, proprio_dim)
500
- proprio_features = proprio_projector(proprio) # (bsz, llm_dim)
501
- proprio_features = proprio_features.unsqueeze(dim=1) # (bsz, 1, llm_dim)
502
- # For simplicity, just append proprio token to the end of projected vision patch tokens
503
- return torch.cat((projected_patch_embeddings, proprio_features), dim=1)
504
- return projected_patch_embeddings
505
-
506
- def _get_wrist_enabled_mask(
507
- self,
508
- modality_mask: Optional[torch.Tensor],
509
- batch_size: int,
510
- device: torch.device,
511
- ) -> Optional[torch.Tensor]:
512
- num_wrist_views = max(self.vision_backbone.get_num_images_in_input() - 1, 0)
513
- if num_wrist_views == 0:
514
- return None
515
-
516
- wrist_enabled = torch.ones((batch_size, num_wrist_views), dtype=torch.bool, device=device)
517
- if modality_mask is None:
518
- return wrist_enabled
519
-
520
- if modality_mask.dim() == 1:
521
- modality_mask = modality_mask.unsqueeze(0)
522
-
523
- if modality_mask.shape[1] >= 2:
524
- vision_enabled = modality_mask[:, 1].bool().unsqueeze(1)
525
- wrist_enabled = vision_enabled.repeat(1, num_wrist_views)
526
-
527
- if modality_mask.shape[1] > 4:
528
- provided_wrist_flags = min(num_wrist_views, modality_mask.shape[1] - 4)
529
- wrist_enabled[:, :provided_wrist_flags] = modality_mask[:, 4 : 4 + provided_wrist_flags].bool()
530
-
531
- return wrist_enabled
532
-
533
- def _get_strict_mae_enabled_mask(
534
- self,
535
- modality_mask: Optional[torch.Tensor],
536
- batch_size: int,
537
- device: torch.device,
538
- ) -> torch.Tensor:
539
- """
540
- Return per-sample boolean flag for strict MAE-style masking.
541
- Layout convention (dataset-side modality_mask):
542
- [language, vision, query, proprio, wrist_flag_0, wrist_flag_1, strict_mae_flag]
543
- """
544
- strict_enabled = torch.zeros((batch_size,), dtype=torch.bool, device=device)
545
- if modality_mask is None:
546
- return strict_enabled
547
-
548
- if modality_mask.dim() == 1:
549
- modality_mask = modality_mask.unsqueeze(0)
550
-
551
- strict_flag_idx = 6
552
- if modality_mask.shape[1] > strict_flag_idx:
553
- strict_enabled = modality_mask[:, strict_flag_idx].bool()
554
- return strict_enabled
555
-
556
- def _build_multimodal_attention(
557
- self,
558
- input_embeddings,
559
- projected_patch_embeddings,
560
- attention_mask,
561
- modality_mask=None,
562
- action_mask: Optional[torch.Tensor] = None,
563
- use_query_mask: bool = False,
564
- query_mask_ratio: float = 0.0,
565
- ):
566
- """Build multimodal embeddings and attention mask, with optional intra-query masking."""
567
-
568
- projected_patch_attention_mask = None
569
- if attention_mask is not None:
570
- device = attention_mask.device
571
- dtype_mask = input_embeddings.dtype
572
- attention_keep = attention_mask.bool().clone()
573
- # Preserve the original token keep mask before query masking mutates it.
574
- base_attention_keep = attention_keep.clone()
575
-
576
- batch_size = attention_keep.shape[0]
577
- vision_enabled = torch.ones(batch_size, dtype=torch.bool, device=device)
578
- query_enabled = torch.ones(batch_size, dtype=torch.bool, device=device)
579
- proprio_enabled = torch.ones(batch_size, dtype=torch.bool, device=device)
580
- wrist_enabled = None
581
- strict_mae_enabled = torch.zeros(batch_size, dtype=torch.bool, device=device)
582
-
583
- if modality_mask is not None:
584
- modality_mask = modality_mask.to(device)
585
- if modality_mask.dim() == 1:
586
- modality_mask = modality_mask.unsqueeze(0)
587
- num_modalities = modality_mask.shape[1]
588
- if num_modalities >= 2:
589
- vision_enabled = modality_mask[:, 1].bool()
590
- if num_modalities >= 3:
591
- query_enabled = modality_mask[:, 2].bool()
592
- if num_modalities >= 4:
593
- proprio_enabled = modality_mask[:, 3].bool()
594
- wrist_enabled = self._get_wrist_enabled_mask(modality_mask, batch_size, device)
595
- strict_mae_enabled = self._get_strict_mae_enabled_mask(modality_mask, batch_size, device)
596
- else:
597
- wrist_enabled = self._get_wrist_enabled_mask(None, batch_size, device)
598
-
599
- if strict_mae_enabled.any():
600
- strict_token_mask = (~base_attention_keep) & strict_mae_enabled.unsqueeze(1)
601
- if strict_token_mask.any():
602
- input_embeddings = input_embeddings.masked_fill(strict_token_mask.unsqueeze(-1), 0)
603
-
604
- wrist_patch_dropout_mask = None
605
- if (
606
- self.training
607
- and self.uniform_vision_dropout_enabled
608
- and self.vision_backbone.get_num_images_in_input() > 1
609
- ):
610
- wrist_patch_dropout_mask = self._build_uniform_wrist_dropout_mask(
611
- projected_patch_embeddings,
612
- vision_enabled,
613
- wrist_enabled,
614
- dtype=projected_patch_embeddings.dtype,
615
- device=device,
616
- )
617
-
618
- if (
619
- self.training
620
- and use_query_mask
621
- and query_mask_ratio > 0.0
622
- and action_mask is not None
623
- ):
624
- action_mask_bool = action_mask.to(device=device).bool()
625
- for b in range(batch_size):
626
- if query_enabled[b]:
627
- continue
628
- query_indices = torch.where(action_mask_bool[b] & attention_keep[b])[0]
629
- if query_indices.numel() == 0:
630
- continue
631
- keep = torch.rand(query_indices.numel(), device=device) > query_mask_ratio
632
- if not keep.any():
633
- keep[torch.randint(query_indices.numel(), (1,), device=device)] = True
634
- attention_keep[b, query_indices] = keep
635
-
636
- keep_float = keep.to(input_embeddings.dtype)
637
- input_embeddings[b, query_indices] *= keep_float.unsqueeze(-1)
638
- kept_tokens = keep_float.sum()
639
- if kept_tokens > 0:
640
- scale = query_indices.numel() / kept_tokens
641
- input_embeddings[b, query_indices] *= scale
642
-
643
- attention_mask = attention_keep.to(dtype_mask)
644
-
645
- projected_patch_attention_mask = torch.ones(
646
- (projected_patch_embeddings.shape[0], projected_patch_embeddings.shape[1]),
647
- dtype=dtype_mask,
648
- device=device,
649
- )
650
- if modality_mask is not None:
651
- patches_per_image = self.vision_backbone.get_num_patches()
652
- num_vision_tokens = patches_per_image * self.vision_backbone.get_num_images_in_input()
653
- wrist_start = patches_per_image
654
- wrist_end = num_vision_tokens
655
- if wrist_end > wrist_start:
656
- if wrist_enabled is not None:
657
- for wrist_idx in range(wrist_enabled.shape[1]):
658
- start_idx = patches_per_image * (wrist_idx + 1)
659
- end_idx = min(start_idx + patches_per_image, num_vision_tokens)
660
- if end_idx <= start_idx:
661
- continue
662
- wrist_mask = wrist_enabled[:, wrist_idx].to(projected_patch_attention_mask.dtype).unsqueeze(1)
663
- projected_patch_attention_mask[:, start_idx:end_idx] *= wrist_mask
664
- else:
665
- vision_mask = vision_enabled.to(projected_patch_attention_mask.dtype).unsqueeze(1)
666
- projected_patch_attention_mask[:, wrist_start:wrist_end] *= vision_mask
667
- if projected_patch_attention_mask.shape[1] > num_vision_tokens:
668
- proprio_mask = proprio_enabled.to(projected_patch_attention_mask.dtype).unsqueeze(1)
669
- projected_patch_attention_mask[:, num_vision_tokens:] *= proprio_mask
670
-
671
- if strict_mae_enabled.any() and wrist_enabled is not None:
672
- strict_wrist_keep = torch.ones_like(projected_patch_attention_mask)
673
- for wrist_idx in range(wrist_enabled.shape[1]):
674
- start_idx = patches_per_image * (wrist_idx + 1)
675
- end_idx = min(start_idx + patches_per_image, num_vision_tokens)
676
- if end_idx <= start_idx:
677
- continue
678
- unmasked_or_disabled = (wrist_enabled[:, wrist_idx] | (~strict_mae_enabled)).to(
679
- strict_wrist_keep.dtype
680
- ).unsqueeze(1)
681
- strict_wrist_keep[:, start_idx:end_idx] *= unmasked_or_disabled
682
- projected_patch_embeddings = projected_patch_embeddings * strict_wrist_keep.unsqueeze(-1)
683
-
684
- if wrist_patch_dropout_mask is not None:
685
- projected_patch_attention_mask = projected_patch_attention_mask * wrist_patch_dropout_mask
686
- projected_patch_embeddings = projected_patch_embeddings * wrist_patch_dropout_mask.unsqueeze(-1)
687
-
688
- multimodal_embeddings = torch.cat(
689
- [input_embeddings[:, :1, :], projected_patch_embeddings, input_embeddings[:, 1:, :]], dim=1
690
- )
691
-
692
- multimodal_attention_mask = None
693
- if attention_mask is not None:
694
- multimodal_attention_mask = torch.cat(
695
- [attention_mask[:, :1], projected_patch_attention_mask, attention_mask[:, 1:]], dim=1
696
- )
697
-
698
- return multimodal_embeddings, multimodal_attention_mask
699
-
700
- def _build_multimodal_labels(self, labels, projected_patch_embeddings):
701
- """Build multimodal labels with IGNORE_INDEX for patch embeddings"""
702
- if labels is not None:
703
- projected_patch_labels = torch.full(
704
- (projected_patch_embeddings.shape[0], projected_patch_embeddings.shape[1]),
705
- fill_value=IGNORE_INDEX,
706
- dtype=labels.dtype,
707
- device=labels.device,
708
- )
709
- return torch.cat([labels[:, :1], projected_patch_labels, labels[:, 1:]], dim=1)
710
- return None
711
-
712
- def _build_uniform_wrist_dropout_mask(
713
- self,
714
- projected_patch_embeddings: torch.Tensor,
715
- vision_enabled: torch.Tensor,
716
- wrist_enabled: Optional[torch.Tensor],
717
- dtype: torch.dtype,
718
- device: torch.device,
719
- ) -> Optional[torch.Tensor]:
720
- """Build DropPath-style mask for wrist patches ensuring even coverage."""
721
-
722
- batch_size, total_tokens = projected_patch_embeddings.shape[:2]
723
- patches_per_image = self.vision_backbone.get_num_patches()
724
- num_images = self.vision_backbone.get_num_images_in_input()
725
- if patches_per_image == 0 or num_images <= 1:
726
- return None
727
-
728
- mask = torch.ones((batch_size, total_tokens), device=device, dtype=dtype)
729
- grid_size = max(int(math.sqrt(patches_per_image)), 1)
730
- wrist_ranges = [
731
- (img_idx * patches_per_image, (img_idx + 1) * patches_per_image)
732
- for img_idx in range(1, num_images)
733
- ]
734
-
735
- dropout_ratio = max(0.0, min(float(self.uniform_vision_dropout_ratio), 1.0))
736
-
737
- for b in range(batch_size):
738
- if not vision_enabled[b]:
739
- continue
740
- for wrist_idx, (start_idx, end_idx) in enumerate(wrist_ranges):
741
- if wrist_enabled is not None and wrist_idx < wrist_enabled.shape[1] and not wrist_enabled[b, wrist_idx]:
742
- continue
743
- num_patches = end_idx - start_idx
744
- if num_patches <= 0:
745
- continue
746
- dropout_tokens = int(round(num_patches * dropout_ratio))
747
- dropout_tokens = min(dropout_tokens, max(num_patches - 1, 0))
748
-
749
- keep_mask = torch.ones(num_patches, device=device, dtype=dtype)
750
- if dropout_tokens > 0:
751
- selected_indices = self._sample_uniform_patch_indices(num_patches, dropout_tokens, grid_size)
752
- if selected_indices:
753
- keep_mask[selected_indices] = 0.0
754
- kept = keep_mask.sum()
755
- if kept > 0 and kept < num_patches:
756
- keep_mask = keep_mask * (num_patches / kept)
757
- mask[b, start_idx:end_idx] = keep_mask
758
-
759
- if torch.all(mask == 1):
760
- return None
761
- return mask
762
-
763
- def _sample_uniform_patch_indices(
764
- self,
765
- patches_per_image: int,
766
- num_to_drop: int,
767
- grid_size: int,
768
- ) -> List[int]:
769
- if num_to_drop <= 0:
770
- return []
771
-
772
- selected: List[int] = []
773
- attempts = 0
774
- max_attempts = max(num_to_drop * 20, 1)
775
-
776
- while len(selected) < num_to_drop and attempts < max_attempts:
777
- candidate = random.randrange(patches_per_image)
778
- row, col = divmod(candidate, grid_size)
779
- if any(abs(row - r0) <= 1 and abs(col - c0) <= 1 for idx0 in selected for r0, c0 in [divmod(idx0, grid_size)]):
780
- attempts += 1
781
- continue
782
- selected.append(candidate)
783
-
784
- if len(selected) < num_to_drop:
785
- remaining = [idx for idx in range(patches_per_image) if idx not in selected]
786
- if remaining:
787
- needed = min(num_to_drop - len(selected), len(remaining))
788
- selected.extend(random.sample(remaining, needed))
789
-
790
- return selected[:num_to_drop]
791
-
792
- # === Core Prismatic VLM `forward()` Logic ===
793
- def forward(
794
- self,
795
- input_ids: Optional[torch.LongTensor] = None,
796
- attention_mask: Optional[torch.Tensor] = None,
797
- pixel_values: Optional[torch.FloatTensor] = None,
798
- labels: Optional[torch.LongTensor] = None,
799
- inputs_embeds: Optional[torch.FloatTensor] = None,
800
- past_key_values: Optional[List[torch.FloatTensor]] = None,
801
- use_cache: Optional[bool] = None,
802
- output_attentions: Optional[bool] = None,
803
- output_hidden_states: Optional[bool] = None,
804
- output_projector_features: Optional[bool] = None,
805
- return_dict: Optional[bool] = None,
806
- proprio=None,
807
- proprio_projector=None,
808
- noisy_actions=None,
809
- noisy_action_projector=None,
810
- diffusion_timestep_embeddings=None,
811
- use_film: bool = False,
812
- modality_mask: Optional[torch.Tensor] = None,
813
- use_query_mask: bool = False,
814
- query_mask_ratio: float = 0.0,
815
- ) -> Union[Tuple, PrismaticCausalLMOutputWithPast]:
816
- """Run a forward pass through the VLM, returning a PrismaticCausalLMOutputWithPast instance."""
817
- output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
818
- output_hidden_states = (
819
- output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
820
- )
821
- output_projector_features = output_projector_features if output_projector_features is not None else False
822
- return_dict = return_dict if return_dict is not None else self.config.use_return_dict
823
-
824
- # Respect `use_cache` only if not training (even if `gradient_checkpointing` is off)
825
- use_cache = use_cache and not self.training
826
-
827
- # Instantiate Placeholder for Projector Features / Language Conditioning
828
- projected_patch_embeddings = None
829
- language_embeddings = None
830
-
831
- # === Handle Generation with Cache (`input_ids.shape[1] == 1`) =>> requires `past_keys_values` ===
832
- if input_ids.shape[1] == 1:
833
- assert input_ids.shape[0] == 1, "Generation is only currently supported for batch size of 1!"
834
- assert past_key_values is not None, "You must provide `past_key_values` during cached generation!"
835
- assert labels is None, "Unexpected key `labels` provided during cached generation!"
836
-
837
- language_model_output = self.language_model(
838
- input_ids=input_ids,
839
- attention_mask=None,
840
- position_ids=None,
841
- past_key_values=past_key_values,
842
- inputs_embeds=None,
843
- labels=None,
844
- use_cache=use_cache,
845
- output_attentions=output_attentions,
846
- output_hidden_states=output_hidden_states,
847
- return_dict=return_dict,
848
- )
849
-
850
- # === Handle Unimodal Forward ===
851
- elif pixel_values is None:
852
- assert (input_ids is not None) and (inputs_embeds is None), "Missing `input_ids` in language-only forward!"
853
- assert past_key_values is None, "Unexpected key `past_key_values` provided during language-only forward!"
854
-
855
- language_model_output = self.language_model(
856
- input_ids=input_ids,
857
- attention_mask=attention_mask,
858
- position_ids=None,
859
- past_key_values=None,
860
- inputs_embeds=None,
861
- labels=labels,
862
- use_cache=use_cache,
863
- output_attentions=output_attentions,
864
- output_hidden_states=output_hidden_states,
865
- return_dict=return_dict,
866
- )
867
-
868
- # === Handle Multimodal Forward ===
869
- elif (input_ids.shape[0] == pixel_values.shape[0]) or (inputs_embeds.shape[0] == pixel_values.shape[0]):
870
- assert past_key_values is None, "Unexpected key `past_key_values` provided during multimodal forward!"
871
-
872
- # Get input embeddings (from language model embeddings)
873
- input_embeddings = self.get_input_embeddings()(input_ids) # (B, seq_len, D)
874
- if labels is not None and labels.device != input_embeddings.device:
875
- labels = labels.to(input_embeddings.device)
876
-
877
-
878
- # Extract action masks
879
- all_actions_mask = self._process_action_masks(labels)
880
-
881
- # Extract the language portion of the input embeddings (i.e. remove the action tokens portion)
882
-
883
- # print(input_embeddings[~all_actions_mask].size())
884
- language_embeddings = input_embeddings[~all_actions_mask].reshape(
885
- input_embeddings.shape[0], -1, input_embeddings.shape[2]
886
- ) # (B, lang_seq_len, llm_dim)
887
-
888
- # Get visual features
889
- projected_patch_embeddings = self._process_vision_features(pixel_values, language_embeddings, use_film)
890
-
891
- if modality_mask is not None:
892
- modality_mask = modality_mask.to(projected_patch_embeddings.device)
893
- if modality_mask.dim() == 1:
894
- modality_mask = modality_mask.unsqueeze(0)
895
- modality_mask = modality_mask.to(projected_patch_embeddings.dtype)
896
-
897
- #NOTE Add proprioceptive state if provided
898
- # if proprio_projector is not None:
899
- # projected_patch_embeddings = self._process_proprio_features(
900
- # projected_patch_embeddings,
901
- # proprio,
902
- # proprio_projector,
903
- # )
904
-
905
- # Process action embeddings - standard action queries
906
- action_queries = self.action_queries.weight # (NUM_TOKENS, h)
907
- action_queries = action_queries.unsqueeze(0).repeat(input_embeddings.shape[0], 1, 1) # (B, NUM_TOKENS, h)
908
-
909
- # Replace action token embeddings with action queries
910
- all_actions_mask = self._process_action_masks(labels)
911
- input_embeddings = self._replace_input_embeddings(
912
- input_embeddings, all_actions_mask, action_queries
913
- )
914
-
915
- # Build multimodal embeddings & attention mask
916
- multimodal_embeddings, multimodal_attention_mask = self._build_multimodal_attention(
917
- input_embeddings,
918
- projected_patch_embeddings,
919
- attention_mask,
920
- modality_mask=modality_mask,
921
- action_mask=all_actions_mask,
922
- use_query_mask=use_query_mask,
923
- query_mask_ratio=query_mask_ratio,
924
- )
925
-
926
- # Build labels for multimodal sequence if needed
927
- multimodal_labels = self._build_multimodal_labels(labels, projected_patch_embeddings)
928
-
929
- # Dispatch to language model
930
- language_model_output = self.language_model(
931
- input_ids=None,
932
- attention_mask=multimodal_attention_mask,
933
- position_ids=None,
934
- past_key_values=None,
935
- inputs_embeds=multimodal_embeddings,
936
- labels=None,
937
- use_cache=use_cache,
938
- output_attentions=output_attentions,
939
- output_hidden_states=output_hidden_states,
940
- return_dict=return_dict,
941
- )
942
-
943
- # === Otherwise =>> Assume Invalid! ===
944
- elif (input_ids.shape[0] != pixel_values.shape[0]) or (inputs_embeds.shape[0] != pixel_values.shape[0]):
945
- raise ValueError("Non-homogenous batch of (text, image) input -- forward() does not support mixed batches!")
946
-
947
- else:
948
- raise ValueError(
949
- "Invalid PrismaticForConditionalGeneration `forward()` call with provided arguments:\n"
950
- f"=> `input_ids` = {input_ids is not None}\n"
951
- f"=> `attention_mask` = {attention_mask is not None}\n"
952
- f"=> `pixel_values` = {pixel_values is not None}\n"
953
- f"=> `labels` = {labels is not None}\n"
954
- f"=> `input_embeds` = {inputs_embeds is not None}\n"
955
- f"=> `past_key_values` = {past_key_values is not None}\n"
956
- f"=> `use_cache` = {use_cache}"
957
- )
958
-
959
- # Unpack `language_model_output` and return PrismaticCausalLMOutputWithPast (or tuple if not `return_dict`)
960
- if not return_dict:
961
- if output_projector_features and (projected_patch_embeddings is not None):
962
- return *language_model_output, projected_patch_embeddings
963
-
964
- return language_model_output
965
-
966
- return PrismaticCausalLMOutputWithPast(
967
- loss=language_model_output.loss,
968
- logits=language_model_output.logits,
969
- past_key_values=language_model_output.past_key_values,
970
- hidden_states=language_model_output.hidden_states,
971
- attentions=language_model_output.attentions,
972
- projector_features=projected_patch_embeddings,
973
- language_embeddings=language_embeddings,
974
- )
975
-
976
-
977
- # === GenerationMixin Methods ===
978
- class OpenVLAForActionPrediction(PrismaticForConditionalGeneration):
979
- config_class: PretrainedConfig = OpenVLAConfig
980
-
981
- def __init__(self, config: OpenVLAConfig) -> None:
982
- super().__init__(config)
983
- self.norm_stats = config.norm_stats
984
-
985
-
986
- # Compute action bins
987
- self.bins = np.linspace(-1, 1, config.n_action_bins)
988
- self.bin_centers = (self.bins[:-1] + self.bins[1:]) / 2.0
989
-
990
- # Compute vocab size for de-tokenization -- revert added "multiple of"
991
- self.vocab_size = self.config.text_config.vocab_size - self.config.pad_to_multiple_of
992
-
993
- def _prepare_input_for_action_prediction(self, input_ids, attention_mask):
994
- """Prepares input for action prediction by adding necessary tokens"""
995
- # Add (ACTION_DIM * NUM_ACTIONS_CHUNK) placeholder tokens to input_ids to simulate action tokens
996
- placeholder_action_token_ids = (
997
- torch.ones((input_ids.shape[0], NUM_TOKENS)).to(input_ids.device).to(input_ids.dtype)
998
- )
999
- input_ids = torch.cat([input_ids, placeholder_action_token_ids], dim=-1)
1000
-
1001
- # Add stop token to sequence (needed in non-causal bi-directional self-attention, as it appears at train time)
1002
- stop_token_id = torch.ones((input_ids.shape[0], 1)).to(input_ids.device).to(input_ids.dtype) * STOP_INDEX
1003
- input_ids = torch.cat([input_ids, stop_token_id], dim=-1)
1004
-
1005
- # Extend the attention mask to fit the new shape of input
1006
- # Note: Only batch size == 1 supported right now
1007
- mask_extension = (
1008
- torch.ones((attention_mask.shape[0], input_ids.shape[-1] - attention_mask.shape[-1]))
1009
- .to(attention_mask.device)
1010
- .to(attention_mask.dtype)
1011
- )
1012
- attention_mask = torch.cat([attention_mask, mask_extension], dim=-1)
1013
-
1014
- return input_ids, attention_mask
1015
-
1016
- def _prepare_labels_for_action_prediction(self, labels, input_ids):
1017
- """Creates labels tensor for action prediction if not provided"""
1018
- # Extend labels tensor with fake action labels
1019
- ARBITRARY_ACTION_TOKEN_IDX = ACTION_TOKEN_BEGIN_IDX + 1
1020
- labels_extension = (
1021
- torch.ones((labels.shape[0], input_ids.shape[-1] - labels.shape[-1])).to(labels.device).to(labels.dtype)
1022
- * ARBITRARY_ACTION_TOKEN_IDX
1023
- )
1024
- labels = torch.cat([labels, labels_extension], dim=-1)
1025
-
1026
- # Replace last label token with stop token
1027
- labels[:, -1] = STOP_INDEX
1028
-
1029
- return labels
1030
-
1031
- def _unnormalize_actions(self, normalized_actions, unnorm_key=None):
1032
- """Unnormalize actions using dataset statistics"""
1033
- action_norm_stats = self.get_action_stats(unnorm_key)
1034
-
1035
- if ACTION_PROPRIO_NORMALIZATION_TYPE == NormalizationType.BOUNDS:
1036
- mask = action_norm_stats.get("mask", np.ones_like(action_norm_stats["min"], dtype=bool))
1037
- action_high, action_low = np.array(action_norm_stats["max"]), np.array(action_norm_stats["min"])
1038
- elif ACTION_PROPRIO_NORMALIZATION_TYPE == NormalizationType.BOUNDS_Q99:
1039
- mask = action_norm_stats.get("mask", np.ones_like(action_norm_stats["q01"], dtype=bool))
1040
- action_high, action_low = np.array(action_norm_stats["q99"]), np.array(action_norm_stats["q01"])
1041
- else:
1042
- raise ValueError("Unsupported action/proprio normalization type detected!")
1043
-
1044
- actions = np.where(
1045
- mask,
1046
- 0.5 * (normalized_actions + 1) * (action_high - action_low + 1e-8) + action_low,
1047
- normalized_actions,
1048
- )
1049
-
1050
- return actions
1051
-
1052
-
1053
- def _regression_or_discrete_prediction(
1054
- self,
1055
- input_embeddings,
1056
- all_actions_mask,
1057
- projected_patch_embeddings,
1058
- attention_mask,
1059
- labels,
1060
- NUM_PATCHES,
1061
- NUM_PROMPT_TOKENS,
1062
- action_head=None,
1063
- proprio=None,
1064
- proprio_projector=None,
1065
- ):
1066
- """Run L1 regression-based continuous action prediction or discrete action tokens prediction."""
1067
-
1068
- # Standard action queries
1069
- action_queries = self.action_queries.weight # (num_tokens, h)
1070
- action_queries = action_queries.unsqueeze(0).repeat(input_embeddings.shape[0], 1, 1) # (B, num_tokens, h)
1071
-
1072
- # Replace action token embeddings with action queries
1073
- input_embeddings = self._replace_input_embeddings(input_embeddings.clone(), all_actions_mask, action_queries)
1074
-
1075
- # Build multimodal embeddings and attention mask
1076
- multimodal_embeddings, multimodal_attention_mask = self._build_multimodal_attention(
1077
- input_embeddings, projected_patch_embeddings, attention_mask
1078
- )
1079
-
1080
- # Forward pass through language model
1081
- language_model_output = self.language_model(
1082
- input_ids=None,
1083
- attention_mask=multimodal_attention_mask,
1084
- position_ids=None,
1085
- past_key_values=None,
1086
- inputs_embeds=multimodal_embeddings,
1087
- labels=None,
1088
- use_cache=None,
1089
- output_attentions=False,
1090
- output_hidden_states=True,
1091
- return_dict=True,
1092
- )
1093
-
1094
- # Extract hidden states for action tokens
1095
- multi_layer_hidden_states = []
1096
-
1097
- for item in language_model_output.hidden_states[0:]:
1098
- # last_hidden_states = output.hidden_states[-1] # (B, seq_len, D)
1099
- # Get hidden states for text portion of prompt+response (after the vision patches)
1100
- text_hidden_states = item
1101
- # Get hidden states for action portion of response
1102
- actions_hidden_states = text_hidden_states[:, NUM_PATCHES+ NUM_PROMPT_TOKENS : NUM_PATCHES + NUM_PROMPT_TOKENS + NUM_TOKENS, :,].reshape(1, 1, NUM_TOKENS , -1).to(torch.bfloat16)
1103
-
1104
- batch_size = item.shape[0]
1105
- task_latten_states = item[:, :NUM_PATCHES].reshape(batch_size, 1, NUM_PATCHES , -1)
1106
- all_hidden_states = torch.cat((task_latten_states, actions_hidden_states),2)
1107
- multi_layer_hidden_states.append(all_hidden_states)
1108
-
1109
- multi_layer_hidden_states = torch.cat(multi_layer_hidden_states, dim = 1)
1110
-
1111
-
1112
- # Handle different prediction methods
1113
- if action_head is not None:
1114
- # L1 regression prediction
1115
- normalized_actions = action_head.predict_action(
1116
- multi_layer_hidden_states,
1117
- proprio=proprio,
1118
- proprio_projector=proprio_projector,
1119
- )
1120
- normalized_actions = normalized_actions.reshape(NUM_ACTIONS_CHUNK, ACTION_DIM)
1121
- normalized_actions = normalized_actions.float().cpu().detach().numpy()
1122
- else:
1123
- # Discrete token-based prediction
1124
- predicted_action_token_ids = (
1125
- language_model_output.logits[
1126
- :,
1127
- NUM_PATCHES + NUM_PROMPT_TOKENS : NUM_PATCHES + NUM_PROMPT_TOKENS + ACTION_DIM * NUM_ACTIONS_CHUNK,
1128
- ]
1129
- .argmax(dim=2)
1130
- .cpu()
1131
- .numpy()
1132
- )
1133
- discretized_actions = self.vocab_size - predicted_action_token_ids
1134
- discretized_actions = np.clip(discretized_actions - 1, a_min=0, a_max=self.bin_centers.shape[0] - 1)
1135
- normalized_actions = self.bin_centers[discretized_actions]
1136
- normalized_actions = normalized_actions.reshape(NUM_ACTIONS_CHUNK, ACTION_DIM)
1137
-
1138
- return normalized_actions, actions_hidden_states
1139
-
1140
- def _unwrap_action_head(self, action_head: Optional[nn.Module]) -> Optional[nn.Module]:
1141
- """Return the underlying nn.Module if wrapped (e.g., DDP)."""
1142
- if action_head is None:
1143
- return None
1144
- return action_head.module if hasattr(action_head, "module") else action_head
1145
-
1146
- def _gather_action_hidden_states(
1147
- self,
1148
- hidden_states: Tuple[torch.Tensor, ...],
1149
- num_patches: int,
1150
- num_prompt_tokens: int,
1151
- ) -> Tuple[torch.Tensor, torch.Tensor]:
1152
- """Extract task and action hidden states across layers for downstream heads."""
1153
- multi_layer_hidden_states = []
1154
- actions_hidden_states = None
1155
- for item in hidden_states:
1156
- batch_size = item.shape[0]
1157
- actions_hidden_states = item[
1158
- :,
1159
- num_patches + num_prompt_tokens : num_patches + num_prompt_tokens + NUM_TOKENS,
1160
- :,
1161
- ].reshape(batch_size, 1, NUM_TOKENS, -1)
1162
- task_latten_states = item[:, :num_patches].reshape(batch_size, 1, num_patches, -1)
1163
- all_hidden_states = torch.cat((task_latten_states, actions_hidden_states), 2)
1164
- multi_layer_hidden_states.append(all_hidden_states)
1165
- return torch.cat(multi_layer_hidden_states, dim=1), actions_hidden_states
1166
-
1167
- def _compute_action_hidden_states(
1168
- self,
1169
- input_embeddings,
1170
- all_actions_mask,
1171
- projected_patch_embeddings,
1172
- attention_mask,
1173
- labels,
1174
- NUM_PATCHES,
1175
- NUM_PROMPT_TOKENS,
1176
- ) -> Tuple[torch.Tensor, torch.Tensor]:
1177
- """Build multimodal sequence and collect hidden states for flow / mip inference."""
1178
- # Standard action queries
1179
- action_queries = self.action_queries.weight
1180
- action_queries = action_queries.unsqueeze(0).repeat(input_embeddings.shape[0], 1, 1)
1181
-
1182
- input_embeddings = self._replace_input_embeddings(
1183
- input_embeddings.clone(), all_actions_mask, action_queries
1184
- )
1185
-
1186
- multimodal_embeddings, multimodal_attention_mask = self._build_multimodal_attention(
1187
- input_embeddings, projected_patch_embeddings, attention_mask
1188
- )
1189
-
1190
- language_model_output = self.language_model(
1191
- input_ids=None,
1192
- attention_mask=multimodal_attention_mask,
1193
- position_ids=None,
1194
- past_key_values=None,
1195
- inputs_embeds=multimodal_embeddings,
1196
- labels=None,
1197
- use_cache=None,
1198
- output_attentions=False,
1199
- output_hidden_states=True,
1200
- return_dict=True,
1201
- )
1202
-
1203
- multi_layer_hidden_states, actions_hidden_states = self._gather_action_hidden_states(
1204
- language_model_output.hidden_states,
1205
- NUM_PATCHES,
1206
- NUM_PROMPT_TOKENS,
1207
- )
1208
- return multi_layer_hidden_states, actions_hidden_states
1209
-
1210
- def _predict_flow_actions(
1211
- self,
1212
- base_hidden_states: torch.Tensor,
1213
- action_head: nn.Module,
1214
- proprio=None,
1215
- proprio_projector=None,
1216
- flow_num_steps: int = 1,
1217
- flow_sample_mode: str = "stochastic",
1218
- ) -> torch.Tensor:
1219
- """Run ODE-style integration using the learned velocity field for flow inference.
1220
-
1221
- Args:
1222
- base_hidden_states: Base hidden states for action prediction
1223
- action_head: Action head module
1224
- proprio: Proprioceptive features
1225
- proprio_projector: Proprioceptive projector
1226
- flow_num_steps: Number of ODE solver steps
1227
- flow_sample_mode: Sampling mode (stochastic or deterministic)
1228
- """
1229
- batch_size = base_hidden_states.shape[0]
1230
- device = base_hidden_states.device
1231
- dtype = base_hidden_states.dtype
1232
-
1233
- num_steps = max(1, int(flow_num_steps))
1234
- sample_mode = flow_sample_mode
1235
-
1236
- # Initialize action state
1237
- if sample_mode == "stochastic":
1238
- act_s = torch.randn(
1239
- batch_size, NUM_ACTIONS_CHUNK, ACTION_DIM, device=device, dtype=dtype
1240
- )
1241
- else:
1242
- act_s = torch.zeros(
1243
- batch_size, NUM_ACTIONS_CHUNK, ACTION_DIM, device=device, dtype=dtype
1244
- )
1245
-
1246
- # ODE integration
1247
- t_schedule = torch.linspace(0.0, 1.0, steps=num_steps + 1, device=device, dtype=dtype)
1248
- for i in range(num_steps):
1249
- s_val = t_schedule[i]
1250
- t_val = t_schedule[i + 1]
1251
- timestep = s_val.expand(batch_size, 1)
1252
-
1253
- # Predict velocity at current state
1254
- # action_init: (B, NUM_ACTIONS_CHUNK, ACTION_DIM)
1255
- # timestep: (B, 1)
1256
- velocity = action_head.predict_action(
1257
- base_hidden_states,
1258
- proprio=proprio,
1259
- proprio_projector=proprio_projector,
1260
- action_init=act_s,
1261
- timestep=timestep,
1262
- phase="Inference",
1263
- )
1264
- if velocity.dim() == 2:
1265
- velocity = velocity.view(batch_size, NUM_ACTIONS_CHUNK, ACTION_DIM)
1266
-
1267
- # Euler step: act_s = act_s + velocity * dt
1268
- act_s = act_s + velocity * (t_val - s_val)
1269
-
1270
- return act_s
1271
-
1272
- def _predict_mip_actions(
1273
- self,
1274
- base_hidden_states: torch.Tensor,
1275
- action_head: nn.Module,
1276
- proprio=None,
1277
- proprio_projector=None,
1278
- flow_t_two_step: float = 0.9,
1279
- ) -> torch.Tensor:
1280
- """Two-step deterministic MIP inference.
1281
-
1282
- Args:
1283
- base_hidden_states: Base hidden states for action prediction
1284
- action_head: Action head module
1285
- proprio: Proprioceptive features
1286
- proprio_projector: Proprioceptive projector
1287
- flow_t_two_step: Two-step midpoint timestep
1288
- """
1289
- batch_size = base_hidden_states.shape[0]
1290
- device = base_hidden_states.device
1291
- dtype = base_hidden_states.dtype
1292
-
1293
- t_two_step = float(flow_t_two_step)
1294
-
1295
- # Initialize states
1296
- zero_actions = torch.zeros(
1297
- batch_size, NUM_ACTIONS_CHUNK, ACTION_DIM, device=device, dtype=dtype
1298
- )
1299
- t_zero = torch.zeros(batch_size, 1, device=device, dtype=dtype)
1300
- t_mid = torch.full((batch_size, 1), t_two_step, device=device, dtype=dtype)
1301
-
1302
- # First step: predict from zero
1303
- # action_init: (B, NUM_ACTIONS_CHUNK, ACTION_DIM)
1304
- # timestep: (B, 1)
1305
- pred_first = action_head.predict_action(
1306
- base_hidden_states,
1307
- proprio=proprio,
1308
- proprio_projector=proprio_projector,
1309
- action_init=zero_actions,
1310
- timestep=t_zero,
1311
- phase="Inference",
1312
- )
1313
- if pred_first.dim() == 2:
1314
- pred_first = pred_first.view(batch_size, NUM_ACTIONS_CHUNK, ACTION_DIM)
1315
- # print(f"pred_first: {pred_first}")
1316
- # Second step: predict from pred_first
1317
- pred_second = action_head.predict_action(
1318
- base_hidden_states,
1319
- proprio=proprio,
1320
- proprio_projector=proprio_projector,
1321
- action_init=pred_first,
1322
- timestep=t_mid,
1323
- phase="Inference",
1324
- )
1325
- if pred_second.dim() == 2:
1326
- pred_second = pred_second.view(batch_size, NUM_ACTIONS_CHUNK, ACTION_DIM)
1327
-
1328
- return pred_second
1329
-
1330
-
1331
- def predict_action(
1332
- self,
1333
- input_ids: Optional[torch.LongTensor] = None,
1334
- unnorm_key: Optional[str] = None,
1335
- proprio=None,
1336
- proprio_projector=None,
1337
- action_head=None,
1338
- noisy_action_projector=None,
1339
- use_film: bool = False,
1340
- action_head_config=None,
1341
- **kwargs: str,
1342
- ) -> np.ndarray:
1343
- """Predict actions from input sequence, with options for different prediction methods.
1344
-
1345
- Args:
1346
- input_ids: Input token ids
1347
- unnorm_key: Key for unnormalization statistics
1348
- proprio: Proprioceptive features
1349
- proprio_projector: Projector for proprioceptive features
1350
- action_head: Optional head for L1 regression or diffusion-based prediction
1351
- noisy_action_projector: Projector for noisy actions in diffusion-based prediction
1352
- use_film: Whether to use FiLM conditioning
1353
- action_head_config: Configuration for action head (includes flow/mip settings)
1354
- **kwargs: Additional arguments including pixel_values and attention_mask
1355
-
1356
- Returns:
1357
- Tuple of (unnormalized_actions, action_hidden_states)
1358
- """
1359
-
1360
- pixel_values = kwargs["pixel_values"] # [1, 12, 224, 224]
1361
- attention_mask = kwargs["attention_mask"] #
1362
-
1363
- # Create fake labels tensor (needed for action mask)
1364
- labels = input_ids.clone()
1365
- labels[:] = IGNORE_INDEX
1366
-
1367
- # Get number of tokens in prompt (excluding the start token)
1368
- NUM_PROMPT_TOKENS = input_ids.shape[-1] - 1 # Subtract action tokens and stop token
1369
-
1370
- # Prepare inputs by adding necessary tokens
1371
- input_ids, attention_mask = self._prepare_input_for_action_prediction(input_ids, attention_mask)
1372
-
1373
- # Update labels tensor for action mask computation later
1374
- labels = self._prepare_labels_for_action_prediction(labels, input_ids)
1375
-
1376
- # Get input embeddings and action masks
1377
- input_embeddings = self.get_input_embeddings()(input_ids)
1378
- all_actions_mask = self._process_action_masks(labels)
1379
-
1380
- # Extract language embeddings
1381
- language_embeddings = input_embeddings[~all_actions_mask].reshape(
1382
- input_embeddings.shape[0], -1, input_embeddings.shape[2]
1383
- )
1384
-
1385
- # Process vision features
1386
- projected_patch_embeddings = self._process_vision_features(pixel_values, language_embeddings, use_film)
1387
-
1388
- # Add proprioceptive features if provided
1389
- use_proprio = proprio_projector is not None and proprio is not None
1390
- if use_proprio:
1391
- proprio = torch.Tensor(proprio).to(projected_patch_embeddings.device, dtype=projected_patch_embeddings.dtype)
1392
- # projected_patch_embeddings = self._process_proprio_features(
1393
- # projected_patch_embeddings, proprio, proprio_projector
1394
- # )
1395
-
1396
- # Calculate number of patches (including proprio token if present)
1397
- NUM_PATCHES = self.vision_backbone.get_num_patches() * self.vision_backbone.get_num_images_in_input()
1398
- # if use_proprio:
1399
- # NUM_PATCHES += 1
1400
-
1401
- actions_hidden_states = None
1402
- normalized_actions = None
1403
- loss_type = getattr(action_head_config, "loss_type", None)
1404
- unwrapped_action_head = self._unwrap_action_head(action_head)
1405
-
1406
- if loss_type in ("flow", "mip"):
1407
- if unwrapped_action_head is None:
1408
- raise ValueError(f"`action_head` must be provided for {loss_type} inference.")
1409
-
1410
- # Get flow/mip settings from action_head_config if provided, otherwise fall back to self.config
1411
- if action_head_config is not None:
1412
- flow_num_steps = getattr(action_head_config, "flow_num_steps", 1)
1413
- flow_sample_mode = getattr(action_head_config, "flow_sample_mode", "stochastic")
1414
- flow_t_two_step = getattr(action_head_config, "flow_t_two_step", 0.9)
1415
- else:
1416
- flow_num_steps = getattr(self.config, "flow_num_steps", 1)
1417
- flow_sample_mode = getattr(self.config, "flow_sample_mode", "stochastic")
1418
- flow_t_two_step = getattr(self.config, "flow_t_two_step", 0.9)
1419
-
1420
- multi_layer_hidden_states, actions_hidden_states = self._compute_action_hidden_states(
1421
- input_embeddings,
1422
- all_actions_mask,
1423
- projected_patch_embeddings,
1424
- attention_mask,
1425
- labels,
1426
- NUM_PATCHES,
1427
- NUM_PROMPT_TOKENS,
1428
- )
1429
-
1430
- if loss_type == "flow":
1431
- normalized_actions = self._predict_flow_actions(
1432
- multi_layer_hidden_states,
1433
- unwrapped_action_head,
1434
- proprio=proprio,
1435
- proprio_projector=proprio_projector,
1436
- flow_num_steps=flow_num_steps,
1437
- flow_sample_mode=flow_sample_mode,
1438
- )
1439
- else:
1440
- normalized_actions = self._predict_mip_actions(
1441
- multi_layer_hidden_states,
1442
- unwrapped_action_head,
1443
- proprio=proprio,
1444
- proprio_projector=proprio_projector,
1445
- flow_t_two_step=flow_t_two_step,
1446
- )
1447
- else:
1448
- # Run regression or discrete token-based prediction
1449
- normalized_actions, actions_hidden_states = self._regression_or_discrete_prediction(
1450
- input_embeddings,
1451
- all_actions_mask,
1452
- projected_patch_embeddings,
1453
- attention_mask,
1454
- labels,
1455
- NUM_PATCHES,
1456
- NUM_PROMPT_TOKENS,
1457
- action_head=action_head,
1458
- proprio=proprio,
1459
- proprio_projector=proprio_projector,
1460
- )
1461
-
1462
- if isinstance(normalized_actions, torch.Tensor):
1463
- normalized_actions = normalized_actions.reshape(normalized_actions.shape[0], -1, ACTION_DIM)
1464
- normalized_actions = normalized_actions[0].float().cpu().detach().numpy()
1465
-
1466
- # Unnormalize predicted actions
1467
- actions = self._unnormalize_actions(normalized_actions, unnorm_key)
1468
-
1469
- return actions, actions_hidden_states
1470
-
1471
-
1472
-
1473
- @staticmethod
1474
- def _check_unnorm_key(norm_stats: Dict[str, Dict[str, Any]], unnorm_key: Optional[str]) -> str:
1475
- """Validate and resolve the unnormalization key for action statistics"""
1476
- if unnorm_key is None:
1477
- assert len(norm_stats) == 1, (
1478
- f"Your model was trained on more than one dataset, "
1479
- f"please pass a `unnorm_key` from the following options to choose the statistics "
1480
- f"used for un-normalizing actions: {norm_stats.keys()}"
1481
- )
1482
- unnorm_key = next(iter(norm_stats.keys()))
1483
-
1484
- assert unnorm_key in norm_stats, (
1485
- f"The `unnorm_key` you chose is not in the set of available dataset statistics, "
1486
- f"please choose from: {norm_stats.keys()}"
1487
- )
1488
- return unnorm_key
1489
-
1490
- def get_action_dim(self, unnorm_key: Optional[str] = None) -> int:
1491
- """Get the dimensionality of the policy's action space."""
1492
- unnorm_key = self._check_unnorm_key(self.norm_stats, unnorm_key)
1493
- return len(self.norm_stats[unnorm_key]["action"]["min"])
1494
-
1495
- def get_action_stats(self, unnorm_key: Optional[str] = None) -> Dict[str, Any]:
1496
- """Get all the logged statistics for the given dataset."""
1497
- unnorm_key = self._check_unnorm_key(self.norm_stats, unnorm_key)
1498
- return self.norm_stats[unnorm_key]["action"]