Repository navigation
pi: initialize from PaliGemma with a scratch action expert #677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
RyanPCo
wants to merge
2
commits into
ryanco/torch-compile-train
Choose a base branch
from
ryanco/pi-scratch-init
base: ryanco/torch-compile-train
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
egomimic/hydra_configs/model/pi0.5_bc_mecka_6d_paligemma_init.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| defaults: | ||
| - pi0.5_bc_mecka_6d | ||
| - pi0.5_paligemma_init | ||
| - _self_ | ||
|
|
||
| # pi0.5_bc_mecka_6d with the pi0.5 base checkpoint swapped for a PaliGemma-only | ||
| # init -- the ablation that asks how much of the mecka 6D result comes from | ||
| # pi0.5's robot pretraining rather than from the VLM. Everything else (the | ||
| # cartesian 6D action layout, the action converter, the prompt blocks, the | ||
| # optimizer) is the parent's; see pi0.5_paligemma_init.yaml for the two keys | ||
| # this arm changes and for the lr knobs worth sweeping. | ||
| # | ||
| # Pairs with the same data configs as the parent (data=mecka_all_6d, | ||
| # mecka_fold_freeform*_6d) and evaluator=eval_pi_wristframe_6d. Norm stats are | ||
| # data-config-specific, not model-specific, so this arm reuses the parent's. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # OVERLAY, not a runnable model config. Compose it AFTER any pi0.5_* config to | ||
| # swap the pi0.5 base checkpoint for a PaliGemma-only init: the VLM prefix comes | ||
| # from the public paligemma-3b-pt-224 release and the action expert, the action | ||
| # in/out projections and the adaRMS time MLPs start from their PyTorch init. | ||
| # | ||
| # defaults: | ||
| # - pi0.5_bc_mecka_6d | ||
| # - pi0.5_paligemma_init | ||
| # - _self_ | ||
| # | ||
| # It sets only the two init keys, so it never clobbers the config it is layered | ||
| # on (action_dim, ac_keys, domains, the prompt flags all survive). Per-run, | ||
| # without a new file: | ||
| # model.robomimic_model.config.pytorch_weight_path=null \ | ||
| # model.robomimic_model.config.paligemma_weight_path=google/paligemma-3b-pt-224 | ||
| # | ||
| # The optimizer is left at pi0.5_base's fine-tuning recipe (constant 3e-5 after | ||
| # 2000 warmup steps). A randomly initialized action expert may well want a | ||
| # higher peak and a longer warmup; that is a per-run decision, e.g. | ||
| # model.optimizer.lr=1e-4 model.scheduler.num_warmup_steps=5000 | ||
| # | ||
| # Multi-rank runs should pre-fetch the checkpoint once, or every rank races on | ||
| # the same Hugging Face download lock: | ||
| # python -c "from egomimic.models.paligemma_init import resolve_paligemma_dir as r; print(r('google/paligemma-3b-pt-224'))" | ||
| robomimic_model: | ||
| config: | ||
| pytorch_weight_path: null | ||
| # $EGOVERSE_PALIGEMMA_WEIGHTS overrides: point it at a local snapshot dir to | ||
| # skip the Hub entirely. | ||
| paligemma_weight_path: ${oc.env:EGOVERSE_PALIGEMMA_WEIGHTS,google/paligemma-3b-pt-224} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| """Adapters that let openpi's PyTorch pi0 run against transformers 5. | ||
|
|
||
| `external/openpi` pins `transformers==4.53.2` and ships a `transformers_replace` | ||
| patch written against it (its own `check.py` asserts that version), which is also | ||
| this repo's pin. A venv on transformers 5 works too, but there the stock modules | ||
| have moved on, so a few of openpi's call sites no longer line up. Nothing here | ||
| runs below transformers 5, and each shim is a no-op when it finds openpi's own | ||
| spelling already in place -- so porting the fork's patch forward makes this | ||
| module inert. Delete it then. | ||
|
|
||
| Applied from `egomimic.algo.pi`, which is the only importer of openpi. | ||
| """ | ||
|
|
||
| import logging | ||
|
|
||
| import torch | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| _APPLIED = False | ||
|
|
||
|
|
||
| def _patch_embed_image() -> bool: | ||
| """`get_image_features` returns a `BaseModelOutputWithPooling`, not a tensor. | ||
|
|
||
| transformers 5's `PaliGemmaModel.get_image_features` returns the vision | ||
| output object with the projected features stashed on `pooler_output`, while | ||
| openpi's `embed_prefix` reads `.shape` off the result. Unwrap it. | ||
| """ | ||
| from openpi.models_pytorch.gemma_pytorch import PaliGemmaWithExpertModel | ||
|
|
||
| original = PaliGemmaWithExpertModel.embed_image | ||
|
|
||
| def embed_image(self, image: torch.Tensor) -> torch.Tensor: | ||
| features = original(self, image) | ||
| if isinstance(features, torch.Tensor): | ||
| return features | ||
| return features.pooler_output | ||
|
|
||
| PaliGemmaWithExpertModel.embed_image = embed_image | ||
| return True | ||
|
|
||
|
|
||
| def _patch_paligemma_bc_properties() -> bool: | ||
| """`paligemma.language_model` / `.vision_tower` / `.multi_modal_projector`. | ||
|
|
||
| transformers 5 moved those submodules under `PaliGemmaModel` and dropped the | ||
| back-compat properties that used to forward to them from | ||
| `PaliGemmaForConditionalGeneration`. openpi still reads all three off the | ||
| outer model. Put the forwarders back. | ||
| """ | ||
| from transformers import PaliGemmaForConditionalGeneration | ||
|
|
||
| added = [] | ||
| for name in ("language_model", "vision_tower", "multi_modal_projector"): | ||
| if hasattr(PaliGemmaForConditionalGeneration, name): | ||
| continue | ||
| setattr( | ||
| PaliGemmaForConditionalGeneration, | ||
| name, | ||
| property(lambda self, _name=name: getattr(self.model, _name)), | ||
| ) | ||
| added.append(name) | ||
| return bool(added) | ||
|
|
||
|
|
||
| # openpi keeps SigLIP's patch and position embeddings in float32 by substring on | ||
| # `vision_tower.vision_model.embeddings.*`; the flattened 5.x spelling of the same | ||
| # three tensors. | ||
| _FP32_KEEP_FLAT = ( | ||
| "vision_tower.embeddings.patch_embedding.", | ||
| "vision_tower.embeddings.position_embedding.", | ||
| ) | ||
|
|
||
|
|
||
| def _patch_fp32_keep_list() -> bool: | ||
| """openpi's float32 keep-list misses the SigLIP embeddings. | ||
|
|
||
| `to_bfloat16_for_selected_params` casts everything to bf16 and then restores | ||
| float32 on names containing `vision_tower.vision_model.embeddings.*`. | ||
| transformers 5 flattened `SiglipVisionModel`, so nothing matches and the | ||
| patch embedding and position embedding stay bf16 -- the tensors openpi | ||
| protects because small updates vanish in bf16. Re-apply the keep under the | ||
| flattened names; with the nested names present they match nothing. | ||
| """ | ||
| from openpi.models_pytorch.gemma_pytorch import PaliGemmaWithExpertModel | ||
|
|
||
| original = PaliGemmaWithExpertModel.to_bfloat16_for_selected_params | ||
|
|
||
| def to_bfloat16_for_selected_params(self, precision="bfloat16"): | ||
| original(self, precision) | ||
| if precision != "bfloat16": | ||
| return | ||
| for name, param in self.named_parameters(): | ||
| if any(selector in name for selector in _FP32_KEEP_FLAT): | ||
| param.data = param.data.to(dtype=torch.float32) | ||
|
|
||
| PaliGemmaWithExpertModel.to_bfloat16_for_selected_params = ( | ||
| to_bfloat16_for_selected_params | ||
| ) | ||
| return True | ||
|
|
||
|
|
||
| def apply() -> None: | ||
| """Install every shim once. Safe to call repeatedly.""" | ||
| global _APPLIED | ||
| if _APPLIED: | ||
| return | ||
| _APPLIED = True | ||
|
|
||
| import transformers | ||
|
|
||
| major = int(transformers.__version__.split(".")[0]) | ||
| if major < 5: | ||
| return | ||
|
|
||
| shims = ( | ||
| ("embed_image", _patch_embed_image), | ||
| ("paligemma_bc_properties", _patch_paligemma_bc_properties), | ||
| ("fp32_keep_list", _patch_fp32_keep_list), | ||
| ) | ||
| applied = [name for name, fn in shims if fn()] | ||
| if applied: | ||
| logger.info( | ||
| "openpi targets transformers 4.53 and this environment has %s; " | ||
| "applied compatibility shims: %s", | ||
| transformers.__version__, | ||
| ", ".join(applied), | ||
| ) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
high (correctness): Any rebuild of a PaliGemma-init PI from its saved config downloads PaliGemma again.
paligemma_weight_pathis saved in the checkpoint'sconfig_tree, and two paths miss it.trainHydra._build_model_config_treenulls onlypytorch_weight_pathon resume/requeue: that key is already null for this arm, sohas_weightsis False and nothing is nulled.rollout._patch_checkpoint_pathsreturns early whenpytorch_weight_pathis None. As a result, every resumed rank and the robot rollout callsnapshot_downloadfor the gatedgoogle/paligemma-3b-pt-224and load 3B weights that the checkpoint state_dict then overwrites. Without network access or a Gemma-licensed HF token, this fails. Please nullpaligemma_weight_pathalongsidepytorch_weight_pathin both places when weights come from a checkpoint, and add a resolve test that setsckpt_pathon the paligemma-init config.