| import logging |
| import math |
|
|
| import torch |
| from torch import Tensor |
| from torch import nn |
| import torch.nn.functional as F |
|
|
| import openpi.models.gemma as _gemma |
| from openpi.models_pytorch.gemma_pytorch import PaliGemmaWithExpertModel |
| import openpi.models_pytorch.preprocessing_pytorch as _preprocessing |
|
|
|
|
| def get_safe_dtype(target_dtype, device_type): |
| """Get a safe dtype for the given device type.""" |
| if device_type == "cpu": |
| |
| if target_dtype == torch.bfloat16: |
| return torch.float32 |
| if target_dtype == torch.float64: |
| return torch.float64 |
| return target_dtype |
|
|
|
|
| def create_sinusoidal_pos_embedding( |
| time: torch.tensor, dimension: int, min_period: float, max_period: float, device="cpu" |
| ) -> Tensor: |
| """Computes sine-cosine positional embedding vectors for scalar positions.""" |
| if dimension % 2 != 0: |
| raise ValueError(f"dimension ({dimension}) must be divisible by 2") |
|
|
| if time.ndim != 1: |
| raise ValueError("The time tensor is expected to be of shape `(batch_size, )`.") |
|
|
| dtype = get_safe_dtype(torch.float64, device.type) |
| fraction = torch.linspace(0.0, 1.0, dimension // 2, dtype=dtype, device=device) |
| period = min_period * (max_period / min_period) ** fraction |
|
|
| |
| scaling_factor = 1.0 / period * 2 * math.pi |
| sin_input = scaling_factor[None, :] * time[:, None] |
| return torch.cat([torch.sin(sin_input), torch.cos(sin_input)], dim=1) |
|
|
|
|
| def sample_beta(alpha, beta, bsize, device): |
| alpha_t = torch.as_tensor(alpha, dtype=torch.float32, device=device) |
| beta_t = torch.as_tensor(beta, dtype=torch.float32, device=device) |
| dist = torch.distributions.Beta(alpha_t, beta_t) |
| return dist.sample((bsize,)) |
|
|
|
|
| def make_att_2d_masks(pad_masks, att_masks): |
| """Copied from big_vision. |
| |
| Tokens can attend to valid inputs tokens which have a cumulative mask_ar |
| smaller or equal to theirs. This way `mask_ar` int[B, N] can be used to |
| setup several types of attention, for example: |
| |
| [[1 1 1 1 1 1]]: pure causal attention. |
| |
| [[0 0 0 1 1 1]]: prefix-lm attention. The first 3 tokens can attend between |
| themselves and the last 3 tokens have a causal attention. The first |
| entry could also be a 1 without changing behaviour. |
| |
| [[1 0 1 0 1 0 0 1 0 0]]: causal attention between 4 blocks. Tokens of a |
| block can attend all previous blocks and all tokens on the same block. |
| |
| Args: |
| input_mask: bool[B, N] true if its part of the input, false if padding. |
| mask_ar: int32[B, N] mask that's 1 where previous tokens cannot depend on |
| it and 0 where it shares the same attention mask as the previous token. |
| """ |
| if att_masks.ndim != 2: |
| raise ValueError(att_masks.ndim) |
| if pad_masks.ndim != 2: |
| raise ValueError(pad_masks.ndim) |
|
|
| cumsum = torch.cumsum(att_masks, dim=1) |
| att_2d_masks = cumsum[:, None, :] <= cumsum[:, :, None] |
| pad_2d_masks = pad_masks[:, None, :] * pad_masks[:, :, None] |
| return att_2d_masks & pad_2d_masks |
|
|
|
|
| class LoRALinear(nn.Linear): |
| """Linear layer with additive LoRA weights while preserving base state keys.""" |
|
|
| def __init__(self, linear: nn.Linear, *, rank: int, alpha: float): |
| super().__init__( |
| linear.in_features, |
| linear.out_features, |
| bias=linear.bias is not None, |
| device=linear.weight.device, |
| dtype=linear.weight.dtype, |
| ) |
| self.weight.data.copy_(linear.weight.data) |
| if linear.bias is not None and self.bias is not None: |
| self.bias.data.copy_(linear.bias.data) |
| self.rank = int(rank) |
| self.alpha = float(alpha) |
| self.scaling = self.alpha / max(self.rank, 1) |
| self.lora_A = nn.Parameter(torch.empty(self.rank, linear.in_features, device=linear.weight.device)) |
| self.lora_B = nn.Parameter(torch.empty(linear.out_features, self.rank, device=linear.weight.device)) |
| nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5)) |
| nn.init.zeros_(self.lora_B) |
|
|
| def forward(self, input: Tensor) -> Tensor: |
| base = F.linear(input, self.weight, self.bias) |
| lora_a = self.lora_A.to(dtype=input.dtype, device=input.device) |
| lora_b = self.lora_B.to(dtype=input.dtype, device=input.device) |
| update = F.linear(F.linear(input, lora_a), lora_b) |
| return base + update.to(dtype=base.dtype) * self.scaling |
|
|
|
|
| class PI0Pytorch(nn.Module): |
| def __init__(self, config): |
| super().__init__() |
| self.config = config |
| self.pi05 = config.pi05 |
| self.speed_modulation = bool(getattr(config, "speed_modulation", False)) |
| self.soft_prompt_p = int(getattr(config, "soft_prompt_p", 0)) |
| self.soft_prompt_speeds = tuple(getattr(config, "soft_prompt_speeds", ())) |
|
|
| paligemma_config = _gemma.get_config(config.paligemma_variant) |
| action_expert_config = _gemma.get_config(config.action_expert_variant) |
|
|
| self.paligemma_with_expert = PaliGemmaWithExpertModel( |
| paligemma_config, |
| action_expert_config, |
| use_adarms=[False, True] if self.pi05 else [False, False], |
| precision=config.dtype, |
| ) |
| self._pytorch_lora_module_count = 0 |
| self._apply_pytorch_lora_if_requested(paligemma_config, action_expert_config) |
|
|
| self.action_in_proj = nn.Linear(config.action_dim, action_expert_config.width) |
| self.action_out_proj = nn.Linear(action_expert_config.width, config.action_dim) |
|
|
| if self.pi05: |
| self.time_mlp_in = nn.Linear(action_expert_config.width, action_expert_config.width) |
| self.time_mlp_out = nn.Linear(action_expert_config.width, action_expert_config.width) |
| if self.speed_modulation: |
| |
| |
| |
| |
| |
| self.speed_mod_mlp_in = nn.Linear(1, action_expert_config.width) |
| self.speed_mod_mlp_out = nn.Linear(action_expert_config.width, action_expert_config.width) |
| self.speed_condition_mlp_in = nn.Linear(2 * action_expert_config.width, action_expert_config.width) |
| self.speed_condition_mlp_out = nn.Linear(action_expert_config.width, action_expert_config.width) |
| else: |
| self.state_proj = nn.Linear(config.action_dim, action_expert_config.width) |
| self.action_time_mlp_in = nn.Linear(2 * action_expert_config.width, action_expert_config.width) |
| self.action_time_mlp_out = nn.Linear(action_expert_config.width, action_expert_config.width) |
|
|
| |
| |
| |
| |
| if self.soft_prompt_p > 0 and self.soft_prompt_speeds: |
| paligemma_width = paligemma_config.width |
| k = len(self.soft_prompt_speeds) |
| self.soft_prompt_tokens = nn.Parameter( |
| torch.empty(k, self.soft_prompt_p, paligemma_width) |
| ) |
| nn.init.normal_(self.soft_prompt_tokens, mean=0.0, std=0.02) |
| self.register_buffer( |
| "soft_prompt_anchors", |
| torch.tensor(self.soft_prompt_speeds, dtype=torch.float32), |
| persistent=False, |
| ) |
|
|
| torch.set_float32_matmul_precision("high") |
| if config.pytorch_compile_mode is not None: |
| self.sample_actions = torch.compile(self.sample_actions, mode=config.pytorch_compile_mode) |
|
|
| |
| self.gradient_checkpointing_enabled = False |
|
|
| msg = "transformers_replace is not installed correctly. Please install it with `uv pip install transformers==4.53.2` and `cp -r ./src/openpi/models_pytorch/transformers_replace/* .venv/lib/python3.11/site-packages/transformers/`." |
| try: |
| from transformers.models.siglip import check |
|
|
| if not check.check_whether_transformers_replace_is_installed_correctly(): |
| raise ValueError(msg) |
| except ImportError: |
| raise ValueError(msg) from None |
|
|
| def _apply_pytorch_lora_if_requested(self, paligemma_config, action_expert_config) -> None: |
| explicit_targets = set(getattr(self.config, "pytorch_lora_targets", ())) |
| use_explicit = bool(getattr(self.config, "pytorch_lora", False)) |
| use_paligemma = use_explicit and "paligemma" in explicit_targets |
| use_action_expert = use_explicit and "action_expert" in explicit_targets |
|
|
| if "lora" in str(getattr(self.config, "paligemma_variant", "")): |
| use_paligemma = True |
| if "lora" in str(getattr(self.config, "action_expert_variant", "")): |
| use_action_expert = True |
|
|
| if use_paligemma: |
| self._pytorch_lora_module_count += self._replace_gemma_linears_with_lora( |
| self.paligemma_with_expert.paligemma.language_model, |
| paligemma_config, |
| ) |
| if use_action_expert: |
| self._pytorch_lora_module_count += self._replace_gemma_linears_with_lora( |
| self.paligemma_with_expert.gemma_expert.model, |
| action_expert_config, |
| ) |
| if self._pytorch_lora_module_count: |
| self._freeze_pytorch_lora_base() |
| logging.info("Enabled PyTorch LoRA on %d Linear layers", self._pytorch_lora_module_count) |
|
|
| def _lora_rank_alpha(self, gemma_config, kind: str) -> tuple[int, float]: |
| lora_config = getattr(gemma_config, "lora_configs", {}).get(kind) |
| rank = int(getattr(lora_config, "rank", getattr(self.config, "pytorch_lora_rank", 16))) |
| alpha = float(getattr(lora_config, "alpha", getattr(self.config, "pytorch_lora_alpha", 16.0))) |
| if bool(getattr(self.config, "pytorch_lora", False)): |
| rank = int(getattr(self.config, "pytorch_lora_rank", rank)) |
| alpha = float(getattr(self.config, "pytorch_lora_alpha", alpha)) |
| return rank, alpha |
|
|
| def _replace_gemma_linears_with_lora(self, root: nn.Module, gemma_config) -> int: |
| attn_children = {"q_proj", "k_proj", "v_proj", "o_proj"} |
| ffn_children = {"gate_proj", "up_proj", "down_proj"} |
| replaced = 0 |
| for _module_name, module in list(root.named_modules()): |
| for child_name, child in list(module.named_children()): |
| if not isinstance(child, nn.Linear): |
| continue |
| if child_name in attn_children: |
| rank, alpha = self._lora_rank_alpha(gemma_config, "attn") |
| elif child_name in ffn_children: |
| rank, alpha = self._lora_rank_alpha(gemma_config, "ffn") |
| else: |
| continue |
| setattr(module, child_name, LoRALinear(child, rank=rank, alpha=alpha)) |
| replaced += 1 |
| return replaced |
|
|
| def _freeze_pytorch_lora_base(self) -> None: |
| for param in self.paligemma_with_expert.parameters(): |
| param.requires_grad = False |
| for name, param in self.paligemma_with_expert.named_parameters(): |
| if "lora_" in name: |
| param.requires_grad = True |
|
|
| @property |
| def pytorch_lora_enabled(self) -> bool: |
| return self._pytorch_lora_module_count > 0 |
|
|
| def gradient_checkpointing_enable(self): |
| """Enable gradient checkpointing for memory optimization.""" |
| self.gradient_checkpointing_enabled = True |
| self.paligemma_with_expert.paligemma.language_model.gradient_checkpointing = True |
| self.paligemma_with_expert.paligemma.vision_tower.gradient_checkpointing = True |
| self.paligemma_with_expert.gemma_expert.model.gradient_checkpointing = True |
|
|
| logging.info("Enabled gradient checkpointing for PI0Pytorch model") |
|
|
| def gradient_checkpointing_disable(self): |
| """Disable gradient checkpointing.""" |
| self.gradient_checkpointing_enabled = False |
| self.paligemma_with_expert.paligemma.language_model.gradient_checkpointing = False |
| self.paligemma_with_expert.paligemma.vision_tower.gradient_checkpointing = False |
| self.paligemma_with_expert.gemma_expert.model.gradient_checkpointing = False |
|
|
| logging.info("Disabled gradient checkpointing for PI0Pytorch model") |
|
|
| def is_gradient_checkpointing_enabled(self): |
| """Check if gradient checkpointing is enabled.""" |
| return self.gradient_checkpointing_enabled |
|
|
| def _apply_checkpoint(self, func, *args, **kwargs): |
| """Helper method to apply gradient checkpointing if enabled.""" |
| if self.gradient_checkpointing_enabled and self.training: |
| return torch.utils.checkpoint.checkpoint( |
| func, *args, use_reentrant=False, preserve_rng_state=False, **kwargs |
| ) |
| return func(*args, **kwargs) |
|
|
| def _prepare_attention_masks_4d(self, att_2d_masks): |
| """Helper method to prepare 4D attention masks for transformer.""" |
| att_2d_masks_4d = att_2d_masks[:, None, :, :] |
| return torch.where(att_2d_masks_4d, 0.0, -2.3819763e38) |
|
|
| def _preprocess_observation(self, observation, *, train=True): |
| """Helper method to preprocess observation.""" |
| observation = _preprocessing.preprocess_observation_pytorch(observation, train=train) |
| return ( |
| list(observation.images.values()), |
| list(observation.image_masks.values()), |
| observation.tokenized_prompt, |
| observation.tokenized_prompt_mask, |
| observation.state, |
| getattr(observation, "speed", None), |
| ) |
|
|
| def sample_noise(self, shape, device): |
| return torch.normal( |
| mean=0.0, |
| std=1.0, |
| size=shape, |
| dtype=torch.float32, |
| device=device, |
| ) |
|
|
| def sample_time(self, bsize, device): |
| time_beta = sample_beta(1.5, 1.0, bsize, device) |
| time = time_beta * 0.999 + 0.001 |
| return time.to(dtype=torch.float32, device=device) |
|
|
| def embed_prefix( |
| self, images, img_masks, lang_tokens, lang_masks, speed=None |
| ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| """Embed images with SigLIP and language tokens with embedding layer to prepare |
| for PaliGemma transformer processing. |
| |
| When soft-prompt speed conditioning is enabled (``soft_prompt_p > 0``), |
| ``P`` learnable tokens (looked up by nearest match between ``speed`` and |
| ``soft_prompt_anchors``) are inserted between the image and language |
| tokens, with full attention. |
| """ |
| embs = [] |
| pad_masks = [] |
| att_masks = [] |
|
|
| |
| for img, img_mask in zip(images, img_masks, strict=True): |
|
|
| def image_embed_func(img): |
| return self.paligemma_with_expert.embed_image(img) |
|
|
| img_emb = self._apply_checkpoint(image_embed_func, img) |
|
|
| bsize, num_img_embs = img_emb.shape[:2] |
|
|
| embs.append(img_emb) |
| pad_masks.append(img_mask[:, None].expand(bsize, num_img_embs)) |
|
|
| |
| att_masks += [0] * num_img_embs |
|
|
| |
| |
| if self.soft_prompt_p > 0 and self.soft_prompt_speeds: |
| if speed is None: |
| raise ValueError( |
| "soft-prompt speed conditioning is enabled but observation.speed is None. " |
| "Make sure the data config emits speed (e.g., speed_integration='soft_prompt')." |
| ) |
| |
| speed_flat = speed.reshape(-1).to(self.soft_prompt_anchors.dtype).to( |
| self.soft_prompt_anchors.device |
| ) |
| |
| dist = (speed_flat[:, None] - self.soft_prompt_anchors[None, :]).abs() |
| speed_idx = dist.argmin(dim=1) |
| soft_prompt_emb = self.soft_prompt_tokens[speed_idx] |
| soft_prompt_emb = soft_prompt_emb.to(dtype=embs[0].dtype, device=embs[0].device) |
| embs.append(soft_prompt_emb) |
| pad_masks.append( |
| torch.ones( |
| soft_prompt_emb.shape[:2], dtype=pad_masks[0].dtype, device=pad_masks[0].device |
| ) |
| ) |
| att_masks += [0] * self.soft_prompt_p |
|
|
| |
| def lang_embed_func(lang_tokens): |
| lang_emb = self.paligemma_with_expert.embed_language_tokens(lang_tokens) |
| lang_emb_dim = lang_emb.shape[-1] |
| return lang_emb * math.sqrt(lang_emb_dim) |
|
|
| lang_emb = self._apply_checkpoint(lang_embed_func, lang_tokens) |
|
|
| embs.append(lang_emb) |
| pad_masks.append(lang_masks) |
|
|
| |
| num_lang_embs = lang_emb.shape[1] |
| att_masks += [0] * num_lang_embs |
|
|
| embs = torch.cat(embs, dim=1) |
| pad_masks = torch.cat(pad_masks, dim=1) |
| att_masks = torch.tensor(att_masks, dtype=torch.bool, device=pad_masks.device) |
|
|
| |
| bsize = pad_masks.shape[0] |
| att_masks = att_masks[None, :].expand(bsize, len(att_masks)) |
|
|
| return embs, pad_masks, att_masks |
|
|
| def embed_suffix(self, state, noisy_actions, timestep, speed=None): |
| """Embed state, noisy_actions, timestep to prepare for Expert Gemma processing.""" |
| embs = [] |
| pad_masks = [] |
| att_masks = [] |
|
|
| if not self.pi05: |
| if self.state_proj.weight.dtype == torch.float32: |
| state = state.to(torch.float32) |
|
|
| |
| def state_proj_func(state): |
| return self.state_proj(state) |
|
|
| state_emb = self._apply_checkpoint(state_proj_func, state) |
|
|
| embs.append(state_emb[:, None, :]) |
| bsize = state_emb.shape[0] |
| device = state_emb.device |
|
|
| state_mask = torch.ones(bsize, 1, dtype=torch.bool, device=device) |
| pad_masks.append(state_mask) |
|
|
| |
| att_masks += [1] |
|
|
| |
| time_emb = create_sinusoidal_pos_embedding( |
| timestep, self.action_in_proj.out_features, min_period=4e-3, max_period=4.0, device=timestep.device |
| ) |
| time_emb = time_emb.type(dtype=timestep.dtype) |
|
|
| |
| def action_proj_func(noisy_actions): |
| return self.action_in_proj(noisy_actions) |
|
|
| action_emb = self._apply_checkpoint(action_proj_func, noisy_actions) |
|
|
| if not self.pi05: |
| time_emb = time_emb[:, None, :].expand_as(action_emb) |
| action_time_emb = torch.cat([action_emb, time_emb], dim=2) |
|
|
| |
| def mlp_func(action_time_emb): |
| x = self.action_time_mlp_in(action_time_emb) |
| x = F.silu(x) |
| return self.action_time_mlp_out(x) |
|
|
| action_time_emb = self._apply_checkpoint(mlp_func, action_time_emb) |
| adarms_cond = None |
| else: |
| |
| def time_mlp_func(time_emb): |
| x = self.time_mlp_in(time_emb) |
| x = F.silu(x) |
| x = self.time_mlp_out(x) |
| return F.silu(x) |
|
|
| time_emb = self._apply_checkpoint(time_mlp_func, time_emb) |
| if self.speed_modulation: |
| if speed is None: |
| speed_input = torch.ones( |
| noisy_actions.shape[0], 1, dtype=time_emb.dtype, device=noisy_actions.device |
| ) |
| else: |
| speed_input = speed.reshape(-1, 1).to(dtype=time_emb.dtype, device=noisy_actions.device) |
|
|
| def speed_mlp_func(speed_input): |
| x = self.speed_mod_mlp_in(speed_input) |
| x = F.silu(x) |
| x = self.speed_mod_mlp_out(x) |
| return F.silu(x) |
|
|
| speed_emb = self._apply_checkpoint(speed_mlp_func, speed_input) |
|
|
| def condition_mlp_func(condition_emb): |
| x = self.speed_condition_mlp_in(condition_emb) |
| x = F.silu(x) |
| x = self.speed_condition_mlp_out(x) |
| return F.silu(x) |
|
|
| time_emb = self._apply_checkpoint(condition_mlp_func, torch.cat([time_emb, speed_emb], dim=-1)) |
| action_time_emb = action_emb |
| adarms_cond = time_emb |
|
|
| |
| embs.append(action_time_emb) |
|
|
| bsize, action_time_dim = action_time_emb.shape[:2] |
| action_time_mask = torch.ones(bsize, action_time_dim, dtype=torch.bool, device=timestep.device) |
| pad_masks.append(action_time_mask) |
|
|
| |
| att_masks += [1] + ([0] * (self.config.action_horizon - 1)) |
|
|
| embs = torch.cat(embs, dim=1) |
| pad_masks = torch.cat(pad_masks, dim=1) |
| att_masks = torch.tensor(att_masks, dtype=embs.dtype, device=embs.device) |
| att_masks = att_masks[None, :].expand(bsize, len(att_masks)) |
|
|
| return embs, pad_masks, att_masks, adarms_cond |
|
|
| def forward(self, observation, actions, noise=None, time=None) -> Tensor: |
| """Do a full training forward pass and compute the loss (batch_size x num_steps x num_motors)""" |
| images, img_masks, lang_tokens, lang_masks, state, speed = self._preprocess_observation( |
| observation, train=True |
| ) |
|
|
| if noise is None: |
| noise = self.sample_noise(actions.shape, actions.device) |
|
|
| if time is None: |
| time = self.sample_time(actions.shape[0], actions.device) |
|
|
| time_expanded = time[:, None, None] |
| x_t = time_expanded * noise + (1 - time_expanded) * actions |
| u_t = noise - actions |
|
|
| prefix_embs, prefix_pad_masks, prefix_att_masks = self.embed_prefix( |
| images, img_masks, lang_tokens, lang_masks, speed=speed |
| ) |
| suffix_embs, suffix_pad_masks, suffix_att_masks, adarms_cond = self.embed_suffix(state, x_t, time, speed=speed) |
| if ( |
| self.paligemma_with_expert.paligemma.language_model.layers[0].self_attn.q_proj.weight.dtype |
| == torch.bfloat16 |
| ): |
| suffix_embs = suffix_embs.to(dtype=torch.bfloat16) |
| prefix_embs = prefix_embs.to(dtype=torch.bfloat16) |
|
|
| pad_masks = torch.cat([prefix_pad_masks, suffix_pad_masks], dim=1) |
| att_masks = torch.cat([prefix_att_masks, suffix_att_masks], dim=1) |
|
|
| att_2d_masks = make_att_2d_masks(pad_masks, att_masks) |
| position_ids = torch.cumsum(pad_masks, dim=1) - 1 |
|
|
| |
| att_2d_masks_4d = self._prepare_attention_masks_4d(att_2d_masks) |
|
|
| |
| def forward_func(prefix_embs, suffix_embs, att_2d_masks_4d, position_ids, adarms_cond): |
| (_, suffix_out), _ = self.paligemma_with_expert.forward( |
| attention_mask=att_2d_masks_4d, |
| position_ids=position_ids, |
| past_key_values=None, |
| inputs_embeds=[prefix_embs, suffix_embs], |
| use_cache=False, |
| adarms_cond=[None, adarms_cond], |
| ) |
| return suffix_out |
|
|
| suffix_out = self._apply_checkpoint( |
| forward_func, prefix_embs, suffix_embs, att_2d_masks_4d, position_ids, adarms_cond |
| ) |
|
|
| suffix_out = suffix_out[:, -self.config.action_horizon :] |
| suffix_out = suffix_out.to(dtype=torch.float32) |
|
|
| |
| def action_out_proj_func(suffix_out): |
| return self.action_out_proj(suffix_out) |
|
|
| v_t = self._apply_checkpoint(action_out_proj_func, suffix_out) |
|
|
| return F.mse_loss(u_t, v_t, reduction="none") |
|
|
| @torch.no_grad() |
| def sample_actions(self, device, observation, noise=None, num_steps=10) -> Tensor: |
| """Do a full inference forward and compute the action (batch_size x num_steps x num_motors)""" |
| bsize = observation.state.shape[0] |
| if noise is None: |
| actions_shape = (bsize, self.config.action_horizon, self.config.action_dim) |
| noise = self.sample_noise(actions_shape, device) |
|
|
| images, img_masks, lang_tokens, lang_masks, state, speed = self._preprocess_observation( |
| observation, train=False |
| ) |
|
|
| prefix_embs, prefix_pad_masks, prefix_att_masks = self.embed_prefix( |
| images, img_masks, lang_tokens, lang_masks, speed=speed |
| ) |
| prefix_att_2d_masks = make_att_2d_masks(prefix_pad_masks, prefix_att_masks) |
| prefix_position_ids = torch.cumsum(prefix_pad_masks, dim=1) - 1 |
|
|
| |
| prefix_att_2d_masks_4d = self._prepare_attention_masks_4d(prefix_att_2d_masks) |
| self.paligemma_with_expert.paligemma.language_model.config._attn_implementation = "eager" |
|
|
| _, past_key_values = self.paligemma_with_expert.forward( |
| attention_mask=prefix_att_2d_masks_4d, |
| position_ids=prefix_position_ids, |
| past_key_values=None, |
| inputs_embeds=[prefix_embs, None], |
| use_cache=True, |
| ) |
|
|
| dt = -1.0 / num_steps |
| dt = torch.tensor(dt, dtype=torch.float32, device=device) |
|
|
| x_t = noise |
| time = torch.tensor(1.0, dtype=torch.float32, device=device) |
| while time >= -dt / 2: |
| expanded_time = time.expand(bsize) |
| v_t = self.denoise_step( |
| state, |
| prefix_pad_masks, |
| past_key_values, |
| x_t, |
| expanded_time, |
| speed, |
| ) |
|
|
| |
| x_t = x_t + dt * v_t |
| time += dt |
| return x_t |
|
|
| def denoise_step( |
| self, |
| state, |
| prefix_pad_masks, |
| past_key_values, |
| x_t, |
| timestep, |
| speed=None, |
| ): |
| """Apply one denoising step of the noise `x_t` at a given timestep.""" |
| suffix_embs, suffix_pad_masks, suffix_att_masks, adarms_cond = self.embed_suffix( |
| state, x_t, timestep, speed=speed |
| ) |
|
|
| suffix_len = suffix_pad_masks.shape[1] |
| batch_size = prefix_pad_masks.shape[0] |
| prefix_len = prefix_pad_masks.shape[1] |
|
|
| prefix_pad_2d_masks = prefix_pad_masks[:, None, :].expand(batch_size, suffix_len, prefix_len) |
|
|
| suffix_att_2d_masks = make_att_2d_masks(suffix_pad_masks, suffix_att_masks) |
|
|
| full_att_2d_masks = torch.cat([prefix_pad_2d_masks, suffix_att_2d_masks], dim=2) |
|
|
| prefix_offsets = torch.sum(prefix_pad_masks, dim=-1)[:, None] |
| position_ids = prefix_offsets + torch.cumsum(suffix_pad_masks, dim=1) - 1 |
|
|
| |
| full_att_2d_masks_4d = self._prepare_attention_masks_4d(full_att_2d_masks) |
| self.paligemma_with_expert.gemma_expert.model.config._attn_implementation = "eager" |
|
|
| outputs_embeds, _ = self.paligemma_with_expert.forward( |
| attention_mask=full_att_2d_masks_4d, |
| position_ids=position_ids, |
| past_key_values=past_key_values, |
| inputs_embeds=[None, suffix_embs], |
| use_cache=False, |
| adarms_cond=[None, adarms_cond], |
| ) |
|
|
| suffix_out = outputs_embeds[1] |
| suffix_out = suffix_out[:, -self.config.action_horizon :] |
| suffix_out = suffix_out.to(dtype=torch.float32) |
| return self.action_out_proj(suffix_out) |
|
|