Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- pi05_twotasks_pytorch/code/openpi-main/README.md +323 -0
- pi05_twotasks_pytorch/code/openpi-main/pyproject.toml +140 -0
- pi05_twotasks_pytorch/code/openpi-main/scripts/compute_norm_stats.py +214 -0
- pi05_twotasks_pytorch/code/openpi-main/scripts/train.py +390 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/__init__.py +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/__pycache__/__init__.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/__pycache__/transforms.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/conftest.py +17 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__init__.py +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/__init__.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/gemma.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/gemma_fast.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/lora.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/model.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/pi0.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/pi0_config.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/pi0_fast.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/siglip.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/tokenizer.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/gemma.py +459 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/gemma_fast.py +437 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/lora.py +148 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/lora_test.py +94 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/model.py +332 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/model_test.py +94 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/pi0.py +279 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/pi0_config.py +117 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/pi0_fast.py +313 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/pi0_test.py +46 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/siglip.py +373 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/tokenizer.py +371 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/tokenizer_test.py +27 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/utils/__pycache__/fsq_tokenizer.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/utils/fsq_tokenizer.py +472 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/vit.py +307 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/__pycache__/gemma_pytorch.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/__pycache__/pi0_pytorch.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/__pycache__/preprocessing_pytorch.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/gemma_pytorch.py +280 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/pi0_pytorch.py +462 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/preprocessing_pytorch.py +173 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/gemma/configuration_gemma.py +173 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/gemma/modeling_gemma.py +862 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/paligemma/modeling_paligemma.py +622 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/siglip/check.py +4 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/siglip/modeling_siglip.py +1237 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/policies/__pycache__/aloha_policy.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/policies/__pycache__/droid_policy.cpython-311.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/policies/__pycache__/franka_ee_policy.cpython-310.pyc +0 -0
- pi05_twotasks_pytorch/code/openpi-main/src/openpi/policies/__pycache__/franka_ee_policy.cpython-311.pyc +0 -0
pi05_twotasks_pytorch/code/openpi-main/README.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# openpi
|
| 2 |
+
|
| 3 |
+
openpi holds open-source models and packages for robotics, published by the [Physical Intelligence team](https://www.physicalintelligence.company/).
|
| 4 |
+
|
| 5 |
+
Currently, this repo contains three types of models:
|
| 6 |
+
- the [π₀ model](https://www.physicalintelligence.company/blog/pi0), a flow-based vision-language-action model (VLA).
|
| 7 |
+
- the [π₀-FAST model](https://www.physicalintelligence.company/research/fast), an autoregressive VLA, based on the FAST action tokenizer.
|
| 8 |
+
- the [π₀.₅ model](https://www.physicalintelligence.company/blog/pi05), an upgraded version of π₀ with better open-world generalization trained with [knowledge insulation](https://www.physicalintelligence.company/research/knowledge_insulation). Note that, in this repository, we currently only support the flow matching head for both $\pi_{0.5}$ training and inference.
|
| 9 |
+
|
| 10 |
+
For all models, we provide _base model_ checkpoints, pre-trained on 10k+ hours of robot data, and examples for using them out of the box or fine-tuning them to your own datasets.
|
| 11 |
+
|
| 12 |
+
This is an experiment: $\pi_0$ was developed for our own robots, which differ from the widely used platforms such as [ALOHA](https://tonyzhaozh.github.io/aloha/) and [DROID](https://droid-dataset.github.io/), and though we are optimistic that researchers and practitioners will be able to run creative new experiments adapting $\pi_0$ to their own platforms, we do not expect every such attempt to be successful. All this is to say: $\pi_0$ may or may not work for you, but you are welcome to try it and see!
|
| 13 |
+
|
| 14 |
+
## Updates
|
| 15 |
+
|
| 16 |
+
- [Sept 2025] We released PyTorch support in openpi.
|
| 17 |
+
- [Sept 2025] We released pi05, an upgraded version of pi0 with better open-world generalization.
|
| 18 |
+
- [Sept 2025]: We have added an [improved idle filter](examples/droid/README_train.md#data-filtering) for DROID training.
|
| 19 |
+
- [Jun 2025]: We have added [instructions](examples/droid/README_train.md) for using `openpi` to train VLAs on the full [DROID dataset](https://droid-dataset.github.io/). This is an approximate open-source implementation of the training pipeline used to train pi0-FAST-DROID.
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
## Requirements
|
| 23 |
+
|
| 24 |
+
To run the models in this repository, you will need an NVIDIA GPU with at least the following specifications. These estimations assume a single GPU, but you can also use multiple GPUs with model parallelism to reduce per-GPU memory requirements by configuring `fsdp_devices` in the training config. Please also note that the current training script does not yet support multi-node training.
|
| 25 |
+
|
| 26 |
+
| Mode | Memory Required | Example GPU |
|
| 27 |
+
| ------------------ | --------------- | ------------------ |
|
| 28 |
+
| Inference | > 8 GB | RTX 4090 |
|
| 29 |
+
| Fine-Tuning (LoRA) | > 22.5 GB | RTX 4090 |
|
| 30 |
+
| Fine-Tuning (Full) | > 70 GB | A100 (80GB) / H100 |
|
| 31 |
+
|
| 32 |
+
The repo has been tested with Ubuntu 22.04, we do not currently support other operating systems.
|
| 33 |
+
|
| 34 |
+
## Installation
|
| 35 |
+
|
| 36 |
+
When cloning this repo, make sure to update submodules:
|
| 37 |
+
|
| 38 |
+
```bash
|
| 39 |
+
git clone --recurse-submodules git@github.com:Physical-Intelligence/openpi.git
|
| 40 |
+
|
| 41 |
+
# Or if you already cloned the repo:
|
| 42 |
+
git submodule update --init --recursive
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
We use [uv](https://docs.astral.sh/uv/) to manage Python dependencies. See the [uv installation instructions](https://docs.astral.sh/uv/getting-started/installation/) to set it up. Once uv is installed, run the following to set up the environment:
|
| 46 |
+
|
| 47 |
+
```bash
|
| 48 |
+
GIT_LFS_SKIP_SMUDGE=1 uv sync
|
| 49 |
+
GIT_LFS_SKIP_SMUDGE=1 uv pip install -e .
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
NOTE: `GIT_LFS_SKIP_SMUDGE=1` is needed to pull LeRobot as a dependency.
|
| 53 |
+
|
| 54 |
+
**Docker**: As an alternative to uv installation, we provide instructions for installing openpi using Docker. If you encounter issues with your system setup, consider using Docker to simplify installation. See [Docker Setup](docs/docker.md) for more details.
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
## Model Checkpoints
|
| 60 |
+
|
| 61 |
+
### Base Models
|
| 62 |
+
We provide multiple base VLA model checkpoints. These checkpoints have been pre-trained on 10k+ hours of robot data, and can be used for fine-tuning.
|
| 63 |
+
|
| 64 |
+
| Model | Use Case | Description | Checkpoint Path |
|
| 65 |
+
| ------------ | ----------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
|
| 66 |
+
| $\pi_0$ | Fine-Tuning | Base [π₀ model](https://www.physicalintelligence.company/blog/pi0) for fine-tuning | `gs://openpi-assets/checkpoints/pi0_base` |
|
| 67 |
+
| $\pi_0$-FAST | Fine-Tuning | Base autoregressive [π₀-FAST model](https://www.physicalintelligence.company/research/fast) for fine-tuning | `gs://openpi-assets/checkpoints/pi0_fast_base` |
|
| 68 |
+
| $\pi_{0.5}$ | Fine-Tuning | Base [π₀.₅ model](https://www.physicalintelligence.company/blog/pi05) for fine-tuning | `gs://openpi-assets/checkpoints/pi05_base` |
|
| 69 |
+
|
| 70 |
+
### Fine-Tuned Models
|
| 71 |
+
We also provide "expert" checkpoints for various robot platforms and tasks. These models are fine-tuned from the base models above and intended to run directly on the target robot. These may or may not work on your particular robot. Since these checkpoints were fine-tuned on relatively small datasets collected with more widely available robots, such as ALOHA and the DROID Franka setup, they might not generalize to your particular setup, though we found some of these, especially the DROID checkpoint, to generalize quite broadly in practice.
|
| 72 |
+
|
| 73 |
+
| Model | Use Case | Description | Checkpoint Path |
|
| 74 |
+
| ------------------------ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
|
| 75 |
+
| $\pi_0$-FAST-DROID | Inference | $\pi_0$-FAST model fine-tuned on the [DROID dataset](https://droid-dataset.github.io/): can perform a wide range of simple table-top manipulation tasks 0-shot in new scenes on the DROID robot platform | `gs://openpi-assets/checkpoints/pi0_fast_droid` |
|
| 76 |
+
| $\pi_0$-DROID | Fine-Tuning | $\pi_0$ model fine-tuned on the [DROID dataset](https://droid-dataset.github.io/): faster inference than $\pi_0$-FAST-DROID, but may not follow language commands as well | `gs://openpi-assets/checkpoints/pi0_droid` |
|
| 77 |
+
| $\pi_0$-ALOHA-towel | Inference | $\pi_0$ model fine-tuned on internal [ALOHA](https://tonyzhaozh.github.io/aloha/) data: can fold diverse towels 0-shot on ALOHA robot platforms | `gs://openpi-assets/checkpoints/pi0_aloha_towel` |
|
| 78 |
+
| $\pi_0$-ALOHA-tupperware | Inference | $\pi_0$ model fine-tuned on internal [ALOHA](https://tonyzhaozh.github.io/aloha/) data: can unpack food from a tupperware container | `gs://openpi-assets/checkpoints/pi0_aloha_tupperware` |
|
| 79 |
+
| $\pi_0$-ALOHA-pen-uncap | Inference | $\pi_0$ model fine-tuned on public [ALOHA](https://dit-policy.github.io/) data: can uncap a pen | `gs://openpi-assets/checkpoints/pi0_aloha_pen_uncap` |
|
| 80 |
+
| $\pi_{0.5}$-LIBERO | Inference | $\pi_{0.5}$ model fine-tuned for the [LIBERO](https://libero-project.github.io/datasets) benchmark: gets state-of-the-art performance (see [LIBERO README](examples/libero/README.md)) | `gs://openpi-assets/checkpoints/pi05_libero` |
|
| 81 |
+
| $\pi_{0.5}$-DROID | Inference / Fine-Tuning | $\pi_{0.5}$ model fine-tuned on the [DROID dataset](https://droid-dataset.github.io/) with [knowledge insulation](https://www.physicalintelligence.company/research/knowledge_insulation): fast inference and good language-following | `gs://openpi-assets/checkpoints/pi05_droid` |
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
By default, checkpoints are automatically downloaded from `gs://openpi-assets` and are cached in `~/.cache/openpi` when needed. You can overwrite the download path by setting the `OPENPI_DATA_HOME` environment variable.
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
## Running Inference for a Pre-Trained Model
|
| 90 |
+
|
| 91 |
+
Our pre-trained model checkpoints can be run with a few lines of code (here our $\pi_0$-FAST-DROID model):
|
| 92 |
+
```python
|
| 93 |
+
from openpi.training import config as _config
|
| 94 |
+
from openpi.policies import policy_config
|
| 95 |
+
from openpi.shared import download
|
| 96 |
+
|
| 97 |
+
config = _config.get_config("pi05_droid")
|
| 98 |
+
checkpoint_dir = download.maybe_download("gs://openpi-assets/checkpoints/pi05_droid")
|
| 99 |
+
|
| 100 |
+
# Create a trained policy.
|
| 101 |
+
policy = policy_config.create_trained_policy(config, checkpoint_dir)
|
| 102 |
+
|
| 103 |
+
# Run inference on a dummy example.
|
| 104 |
+
example = {
|
| 105 |
+
"observation/exterior_image_1_left": ...,
|
| 106 |
+
"observation/wrist_image_left": ...,
|
| 107 |
+
...
|
| 108 |
+
"prompt": "pick up the fork"
|
| 109 |
+
}
|
| 110 |
+
action_chunk = policy.infer(example)["actions"]
|
| 111 |
+
```
|
| 112 |
+
You can also test this out in the [example notebook](examples/inference.ipynb).
|
| 113 |
+
|
| 114 |
+
We provide detailed step-by-step examples for running inference of our pre-trained checkpoints on [DROID](examples/droid/README.md) and [ALOHA](examples/aloha_real/README.md) robots.
|
| 115 |
+
|
| 116 |
+
**Remote Inference**: We provide [examples and code](docs/remote_inference.md) for running inference of our models **remotely**: the model can run on a different server and stream actions to the robot via a websocket connection. This makes it easy to use more powerful GPUs off-robot and keep robot and policy environments separate.
|
| 117 |
+
|
| 118 |
+
**Test inference without a robot**: We provide a [script](examples/simple_client/README.md) for testing inference without a robot. This script will generate a random observation and run inference with the model. See [here](examples/simple_client/README.md) for more details.
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
## Fine-Tuning Base Models on Your Own Data
|
| 125 |
+
|
| 126 |
+
We will fine-tune the $\pi_{0.5}$ model on the [LIBERO dataset](https://libero-project.github.io/datasets) as a running example for how to fine-tune a base model on your own data. We will explain three steps:
|
| 127 |
+
1. Convert your data to a LeRobot dataset (which we use for training)
|
| 128 |
+
2. Defining training configs and running training
|
| 129 |
+
3. Spinning up a policy server and running inference
|
| 130 |
+
|
| 131 |
+
### 1. Convert your data to a LeRobot dataset
|
| 132 |
+
|
| 133 |
+
We provide a minimal example script for converting LIBERO data to a LeRobot dataset in [`examples/libero/convert_libero_data_to_lerobot.py`](examples/libero/convert_libero_data_to_lerobot.py). You can easily modify it to convert your own data! You can download the raw LIBERO dataset from [here](https://huggingface.co/datasets/openvla/modified_libero_rlds), and run the script with:
|
| 134 |
+
|
| 135 |
+
```bash
|
| 136 |
+
uv run examples/libero/convert_libero_data_to_lerobot.py --data_dir /path/to/your/libero/data
|
| 137 |
+
```
|
| 138 |
+
|
| 139 |
+
**Note:** If you just want to fine-tune on LIBERO, you can skip this step, because our LIBERO fine-tuning configs point to a pre-converted LIBERO dataset. This step is merely an example that you can adapt to your own data.
|
| 140 |
+
|
| 141 |
+
### 2. Defining training configs and running training
|
| 142 |
+
|
| 143 |
+
To fine-tune a base model on your own data, you need to define configs for data processing and training. We provide example configs with detailed comments for LIBERO below, which you can modify for your own dataset:
|
| 144 |
+
|
| 145 |
+
- [`LiberoInputs` and `LiberoOutputs`](src/openpi/policies/libero_policy.py): Defines the data mapping from the LIBERO environment to the model and vice versa. Will be used for both, training and inference.
|
| 146 |
+
- [`LeRobotLiberoDataConfig`](src/openpi/training/config.py): Defines how to process raw LIBERO data from LeRobot dataset for training.
|
| 147 |
+
- [`TrainConfig`](src/openpi/training/config.py): Defines fine-tuning hyperparameters, data config, and weight loader.
|
| 148 |
+
|
| 149 |
+
We provide example fine-tuning configs for [π₀](src/openpi/training/config.py), [π₀-FAST](src/openpi/training/config.py), and [π₀.₅](src/openpi/training/config.py) on LIBERO data.
|
| 150 |
+
|
| 151 |
+
Before we can run training, we need to compute the normalization statistics for the training data. Run the script below with the name of your training config:
|
| 152 |
+
|
| 153 |
+
```bash
|
| 154 |
+
uv run scripts/compute_norm_stats.py --config-name pi05_libero
|
| 155 |
+
```
|
| 156 |
+
|
| 157 |
+
Now we can kick off training with the following command (the `--overwrite` flag is used to overwrite existing checkpoints if you rerun fine-tuning with the same config):
|
| 158 |
+
|
| 159 |
+
```bash
|
| 160 |
+
XLA_PYTHON_CLIENT_MEM_FRACTION=0.9 uv run scripts/train.py pi05_libero --exp-name=my_experiment --overwrite
|
| 161 |
+
```
|
| 162 |
+
|
| 163 |
+
The command will log training progress to the console and save checkpoints to the `checkpoints` directory. You can also monitor training progress on the Weights & Biases dashboard. For maximally using the GPU memory, set `XLA_PYTHON_CLIENT_MEM_FRACTION=0.9` before running training -- this enables JAX to use up to 90% of the GPU memory (vs. the default of 75%).
|
| 164 |
+
|
| 165 |
+
**Note:** We provide functionality for *reloading* normalization statistics for state / action normalization from pre-training. This can be beneficial if you are fine-tuning to a new task on a robot that was part of our pre-training mixture. For more details on how to reload normalization statistics, see the [norm_stats.md](docs/norm_stats.md) file.
|
| 166 |
+
|
| 167 |
+
### 3. Spinning up a policy server and running inference
|
| 168 |
+
|
| 169 |
+
Once training is complete, we can run inference by spinning up a policy server and then querying it from a LIBERO evaluation script. Launching a model server is easy (we use the checkpoint for iteration 20,000 for this example, modify as needed):
|
| 170 |
+
|
| 171 |
+
```bash
|
| 172 |
+
uv run scripts/serve_policy.py policy:checkpoint --policy.config=pi05_libero --policy.dir=checkpoints/pi05_libero/my_experiment/20000
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
This will spin up a server that listens on port 8000 and waits for observations to be sent to it. We can then run an evaluation script (or robot runtime) that queries the server.
|
| 176 |
+
|
| 177 |
+
For running the LIBERO eval in particular, we provide (and recommend using) a Dockerized workflow that handles both the policy server and the evaluation script together. See the [LIBERO README](examples/libero/README.md) for more details.
|
| 178 |
+
|
| 179 |
+
If you want to embed a policy server call in your own robot runtime, we have a minimal example of how to do so in the [remote inference docs](docs/remote_inference.md).
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
### More Examples
|
| 184 |
+
|
| 185 |
+
We provide more examples for how to fine-tune and run inference with our models on the ALOHA platform in the following READMEs:
|
| 186 |
+
- [ALOHA Simulator](examples/aloha_sim)
|
| 187 |
+
- [ALOHA Real](examples/aloha_real)
|
| 188 |
+
- [UR5](examples/ur5)
|
| 189 |
+
|
| 190 |
+
## PyTorch Support
|
| 191 |
+
|
| 192 |
+
openpi now provides PyTorch implementations of π₀ and π₀.₅ models alongside the original JAX versions! The PyTorch implementation has been validated on the LIBERO benchmark (both inference and finetuning). A few features are currently not supported (this may change in the future):
|
| 193 |
+
|
| 194 |
+
- The π₀-FAST model
|
| 195 |
+
- Mixed precision training
|
| 196 |
+
- FSDP (fully-sharded data parallelism) training
|
| 197 |
+
- LoRA (low-rank adaptation) training
|
| 198 |
+
- EMA (exponential moving average) weights during training
|
| 199 |
+
|
| 200 |
+
### Setup
|
| 201 |
+
1. Make sure that you have the latest version of all dependencies installed: `uv sync`
|
| 202 |
+
|
| 203 |
+
2. Double check that you have transformers 4.53.2 installed: `uv pip show transformers`
|
| 204 |
+
|
| 205 |
+
3. Apply the transformers library patches:
|
| 206 |
+
```bash
|
| 207 |
+
cp -r ./src/openpi/models_pytorch/transformers_replace/* .venv/lib/python3.11/site-packages/transformers/
|
| 208 |
+
```
|
| 209 |
+
|
| 210 |
+
This overwrites several files in the transformers library with necessary model changes: 1) supporting AdaRMS, 2) correctly controlling the precision of activations, and 3) allowing the KV cache to be used without being updated.
|
| 211 |
+
|
| 212 |
+
**WARNING**: With the default uv link mode (hardlink), this will permanently affect the transformers library in your uv cache, meaning the changes will survive reinstallations of transformers and could even propagate to other projects that use transformers. To fully undo this operation, you must run `uv cache clean transformers`.
|
| 213 |
+
|
| 214 |
+
### Converting JAX Models to PyTorch
|
| 215 |
+
|
| 216 |
+
To convert a JAX model checkpoint to PyTorch format:
|
| 217 |
+
|
| 218 |
+
```bash
|
| 219 |
+
uv run examples/convert_jax_model_to_pytorch.py \
|
| 220 |
+
--checkpoint_dir /path/to/jax/checkpoint \
|
| 221 |
+
--config_name <config name> \
|
| 222 |
+
--output_path /path/to/converted/pytorch/checkpoint
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
### Running Inference with PyTorch
|
| 226 |
+
|
| 227 |
+
The PyTorch implementation uses the same API as the JAX version - you only need to change the checkpoint path to point to the converted PyTorch model:
|
| 228 |
+
|
| 229 |
+
```python
|
| 230 |
+
from openpi.training import config as _config
|
| 231 |
+
from openpi.policies import policy_config
|
| 232 |
+
from openpi.shared import download
|
| 233 |
+
|
| 234 |
+
config = _config.get_config("pi05_droid")
|
| 235 |
+
checkpoint_dir = "/path/to/converted/pytorch/checkpoint"
|
| 236 |
+
|
| 237 |
+
# Create a trained policy (automatically detects PyTorch format)
|
| 238 |
+
policy = policy_config.create_trained_policy(config, checkpoint_dir)
|
| 239 |
+
|
| 240 |
+
# Run inference (same API as JAX)
|
| 241 |
+
action_chunk = policy.infer(example)["actions"]
|
| 242 |
+
```
|
| 243 |
+
|
| 244 |
+
### Policy Server with PyTorch
|
| 245 |
+
|
| 246 |
+
The policy server works identically with PyTorch models - just point to the converted checkpoint directory:
|
| 247 |
+
|
| 248 |
+
```bash
|
| 249 |
+
uv run scripts/serve_policy.py policy:checkpoint \
|
| 250 |
+
--policy.config=pi05_droid \
|
| 251 |
+
--policy.dir=/path/to/converted/pytorch/checkpoint
|
| 252 |
+
```
|
| 253 |
+
|
| 254 |
+
### Finetuning with PyTorch
|
| 255 |
+
|
| 256 |
+
To finetune a model in PyTorch:
|
| 257 |
+
|
| 258 |
+
1. Convert the JAX base model to PyTorch format:
|
| 259 |
+
```bash
|
| 260 |
+
uv run examples/convert_jax_model_to_pytorch.py \
|
| 261 |
+
--config_name <config name> \
|
| 262 |
+
--checkpoint_dir /path/to/jax/base/model \
|
| 263 |
+
--output_path /path/to/pytorch/base/model
|
| 264 |
+
```
|
| 265 |
+
|
| 266 |
+
2. Specify the converted PyTorch model path in your config using `pytorch_weight_path`
|
| 267 |
+
|
| 268 |
+
3. Launch training using one of these modes:
|
| 269 |
+
|
| 270 |
+
```bash
|
| 271 |
+
# Single GPU training:
|
| 272 |
+
uv run scripts/train_pytorch.py <config_name> --exp_name <run_name> --save_interval <interval>
|
| 273 |
+
|
| 274 |
+
# Example:
|
| 275 |
+
uv run scripts/train_pytorch.py debug --exp_name pytorch_test
|
| 276 |
+
uv run scripts/train_pytorch.py debug --exp_name pytorch_test --resume # Resume from latest checkpoint
|
| 277 |
+
|
| 278 |
+
# Multi-GPU training (single node):
|
| 279 |
+
uv run torchrun --standalone --nnodes=1 --nproc_per_node=<num_gpus> scripts/train_pytorch.py <config_name> --exp_name <run_name>
|
| 280 |
+
|
| 281 |
+
# Example:
|
| 282 |
+
uv run torchrun --standalone --nnodes=1 --nproc_per_node=2 scripts/train_pytorch.py pi0_aloha_sim --exp_name pytorch_ddp_test
|
| 283 |
+
uv run torchrun --standalone --nnodes=1 --nproc_per_node=2 scripts/train_pytorch.py pi0_aloha_sim --exp_name pytorch_ddp_test --resume
|
| 284 |
+
|
| 285 |
+
# Multi-Node Training:
|
| 286 |
+
uv run torchrun \
|
| 287 |
+
--nnodes=<num_nodes> \
|
| 288 |
+
--nproc_per_node=<gpus_per_node> \
|
| 289 |
+
--node_rank=<rank_of_node> \
|
| 290 |
+
--master_addr=<master_ip> \
|
| 291 |
+
--master_port=<port> \
|
| 292 |
+
scripts/train_pytorch.py <config_name> --exp_name=<run_name> --save_interval <interval>
|
| 293 |
+
```
|
| 294 |
+
|
| 295 |
+
### Precision Settings
|
| 296 |
+
|
| 297 |
+
JAX and PyTorch implementations handle precision as follows:
|
| 298 |
+
|
| 299 |
+
**JAX:**
|
| 300 |
+
1. Inference: most weights and computations in bfloat16, with a few computations in float32 for stability
|
| 301 |
+
2. Training: defaults to mixed precision: weights and gradients in float32, (most) activations and computations in bfloat16. You can change to full float32 training by setting `dtype` to float32 in the config.
|
| 302 |
+
|
| 303 |
+
**PyTorch:**
|
| 304 |
+
1. Inference: matches JAX -- most weights and computations in bfloat16, with a few weights converted to float32 for stability
|
| 305 |
+
2. Training: supports either full bfloat16 (default) or full float32. You can change it by setting `pytorch_training_precision` in the config. bfloat16 uses less memory but exhibits higher losses compared to float32. Mixed precision is not yet supported.
|
| 306 |
+
|
| 307 |
+
With torch.compile, inference speed is comparable between JAX and PyTorch.
|
| 308 |
+
|
| 309 |
+
## Troubleshooting
|
| 310 |
+
|
| 311 |
+
We will collect common issues and their solutions here. If you encounter an issue, please check here first. If you can't find a solution, please file an issue on the repo (see [here](CONTRIBUTING.md) for guidelines).
|
| 312 |
+
|
| 313 |
+
| Issue | Resolution |
|
| 314 |
+
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| 315 |
+
| `uv sync` fails with dependency conflicts | Try removing the virtual environment directory (`rm -rf .venv`) and running `uv sync` again. If issues persist, check that you have the latest version of `uv` installed (`uv self update`). |
|
| 316 |
+
| Training runs out of GPU memory | Make sure you set `XLA_PYTHON_CLIENT_MEM_FRACTION=0.9` (or higher) before running training to allow JAX to use more GPU memory. You can also use `--fsdp-devices <n>` where `<n>` is your number of GPUs, to enable [fully-sharded data parallelism](https://engineering.fb.com/2021/07/15/open-source/fsdp/), which reduces memory usage in exchange for slower training (the amount of slowdown depends on your particular setup). If you are still running out of memory, you may want to consider disabling EMA. |
|
| 317 |
+
| Policy server connection errors | Check that the server is running and listening on the expected port. Verify network connectivity and firewall settings between client and server. |
|
| 318 |
+
| Missing norm stats error when training | Run `scripts/compute_norm_stats.py` with your config name before starting training. |
|
| 319 |
+
| Dataset download fails | Check your internet connection. For HuggingFace datasets, ensure you're logged in (`huggingface-cli login`). |
|
| 320 |
+
| CUDA/GPU errors | Verify NVIDIA drivers are installed correctly. For Docker, ensure nvidia-container-toolkit is installed. Check GPU compatibility. You do NOT need CUDA libraries installed at a system level --- they will be installed via uv. You may even want to try *uninstalling* system CUDA libraries if you run into CUDA issues, since system libraries can sometimes cause conflicts. |
|
| 321 |
+
| Import errors when running examples | Make sure you've installed all dependencies with `uv sync`. Some examples may have additional requirements listed in their READMEs. |
|
| 322 |
+
| Action dimensions mismatch | Verify your data processing transforms match the expected input/output dimensions of your robot. Check the action space definitions in your policy classes. |
|
| 323 |
+
| Diverging training loss | Check the `q01`, `q99`, and `std` values in `norm_stats.json` for your dataset. Certain dimensions that are rarely used can end up with very small `q01`, `q99`, or `std` values, leading to huge states and actions after normalization. You can manually adjust the norm stats as a workaround. |
|
pi05_twotasks_pytorch/code/openpi-main/pyproject.toml
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "openpi"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "Physical Intelligence open source repo"
|
| 5 |
+
readme = "README.md"
|
| 6 |
+
requires-python = ">=3.11"
|
| 7 |
+
license = { file = "LICENSE" }
|
| 8 |
+
dependencies = [
|
| 9 |
+
"augmax>=0.3.4",
|
| 10 |
+
"dm-tree>=0.1.8",
|
| 11 |
+
"einops>=0.8.0",
|
| 12 |
+
"equinox>=0.11.8",
|
| 13 |
+
"flatbuffers>=24.3.25",
|
| 14 |
+
"flax==0.10.2",
|
| 15 |
+
"fsspec[gcs]>=2024.6.0",
|
| 16 |
+
"gym-aloha>=0.1.1",
|
| 17 |
+
"imageio>=2.36.1",
|
| 18 |
+
"jax[cuda12]==0.5.3",
|
| 19 |
+
"jaxtyping==0.2.36",
|
| 20 |
+
"lerobot",
|
| 21 |
+
"ml_collections==1.0.0",
|
| 22 |
+
"numpy>=1.22.4,<2.0.0",
|
| 23 |
+
"numpydantic>=1.6.6",
|
| 24 |
+
"opencv-python-headless>=4.10.0.84",
|
| 25 |
+
"openpi-client",
|
| 26 |
+
"orbax-checkpoint==0.11.13",
|
| 27 |
+
"pillow>=11.0.0",
|
| 28 |
+
"sentencepiece>=0.2.0",
|
| 29 |
+
"torch==2.7.1",
|
| 30 |
+
"tqdm-loggable>=0.2",
|
| 31 |
+
"typing-extensions>=4.12.2",
|
| 32 |
+
"tyro>=0.9.5",
|
| 33 |
+
"wandb>=0.26.1",
|
| 34 |
+
"filelock>=3.16.1",
|
| 35 |
+
"beartype==0.19.0",
|
| 36 |
+
"chex==0.1.90",
|
| 37 |
+
"treescope>=0.1.7",
|
| 38 |
+
"transformers==4.53.2",
|
| 39 |
+
"rich>=14.0.0",
|
| 40 |
+
"polars>=1.30.0",
|
| 41 |
+
]
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
[project.urls]
|
| 45 |
+
Repository = "https://github.com/Physical-Intelligence/openpi"
|
| 46 |
+
|
| 47 |
+
[dependency-groups]
|
| 48 |
+
dev = [
|
| 49 |
+
"pytest>=8.3.4",
|
| 50 |
+
"ruff>=0.8.6",
|
| 51 |
+
"pre-commit>=4.0.1",
|
| 52 |
+
"ipykernel>=6.29.5",
|
| 53 |
+
"ipywidgets>=8.1.5",
|
| 54 |
+
"matplotlib>=3.10.0",
|
| 55 |
+
"pynvml>=12.0.0",
|
| 56 |
+
]
|
| 57 |
+
rlds = [
|
| 58 |
+
# NOTE: tensorflow-cpu 2.15.0 only ships cp311 wheels on PyPI.
|
| 59 |
+
# Use `uv venv --python 3.11` before `uv sync --group rlds`.
|
| 60 |
+
"dlimp",
|
| 61 |
+
"tensorflow-cpu==2.15.0",
|
| 62 |
+
"tensorflow-datasets==4.9.9",
|
| 63 |
+
]
|
| 64 |
+
|
| 65 |
+
[tool.uv]
|
| 66 |
+
override-dependencies = ["ml-dtypes==0.4.1", "tensorstore==0.1.74"]
|
| 67 |
+
|
| 68 |
+
[tool.uv.sources]
|
| 69 |
+
openpi-client = { workspace = true }
|
| 70 |
+
lerobot = { git = "https://github.com/huggingface/lerobot", rev = "0cf864870cf29f4738d3ade893e6fd13fbd7cdb5" }
|
| 71 |
+
dlimp = { git = "https://github.com/kvablack/dlimp", rev = "ad72ce3a9b414db2185bc0b38461d4101a65477a" }
|
| 72 |
+
|
| 73 |
+
[tool.uv.workspace]
|
| 74 |
+
members = ["packages/*"]
|
| 75 |
+
|
| 76 |
+
[tool.ruff]
|
| 77 |
+
line-length = 120
|
| 78 |
+
target-version = "py311"
|
| 79 |
+
extend-exclude = ["docker", "third_party", "src/openpi/models_pytorch/transformers_replace/*"]
|
| 80 |
+
|
| 81 |
+
[tool.ruff.lint]
|
| 82 |
+
# https://docs.astral.sh/ruff/rules/
|
| 83 |
+
select = [
|
| 84 |
+
"B",
|
| 85 |
+
"C4",
|
| 86 |
+
"DTZ",
|
| 87 |
+
"E4",
|
| 88 |
+
"E7",
|
| 89 |
+
"E9",
|
| 90 |
+
"F",
|
| 91 |
+
"FBT",
|
| 92 |
+
"FURB",
|
| 93 |
+
"I",
|
| 94 |
+
"ICN",
|
| 95 |
+
"ISC",
|
| 96 |
+
"LOG",
|
| 97 |
+
"N",
|
| 98 |
+
"PD",
|
| 99 |
+
"PERF",
|
| 100 |
+
"PIE",
|
| 101 |
+
"PLC",
|
| 102 |
+
"PLE",
|
| 103 |
+
"PLR1",
|
| 104 |
+
"PLR5",
|
| 105 |
+
"PLW",
|
| 106 |
+
"PT",
|
| 107 |
+
"Q",
|
| 108 |
+
"RET",
|
| 109 |
+
"RUF",
|
| 110 |
+
"SIM",
|
| 111 |
+
"SLF",
|
| 112 |
+
"T10",
|
| 113 |
+
"T20",
|
| 114 |
+
"UP",
|
| 115 |
+
"W",
|
| 116 |
+
]
|
| 117 |
+
ignore = [
|
| 118 |
+
"F722", # Conflicts with array typing.
|
| 119 |
+
"T201", # We use print statements.
|
| 120 |
+
"PD008", # Lots of false positives.
|
| 121 |
+
"ISC001", # Disabling to support ruff format.
|
| 122 |
+
"LOG015", # Use logger.info.
|
| 123 |
+
]
|
| 124 |
+
unfixable = [
|
| 125 |
+
"B905", # Fix defaults to strict=False, which is not what we want.
|
| 126 |
+
]
|
| 127 |
+
|
| 128 |
+
[tool.ruff.lint.isort]
|
| 129 |
+
force-single-line = true
|
| 130 |
+
force-sort-within-sections = true
|
| 131 |
+
single-line-exclusions = ["collections.abc", "typing", "typing_extensions"]
|
| 132 |
+
known-third-party = ["wandb"]
|
| 133 |
+
|
| 134 |
+
[build-system]
|
| 135 |
+
requires = ["hatchling"]
|
| 136 |
+
build-backend = "hatchling.build"
|
| 137 |
+
|
| 138 |
+
[tool.pytest.ini_options]
|
| 139 |
+
markers = ["manual: should be run manually."]
|
| 140 |
+
testpaths = ["src", "scripts", "packages"]
|
pi05_twotasks_pytorch/code/openpi-main/scripts/compute_norm_stats.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compute normalization statistics for a config.
|
| 2 |
+
|
| 3 |
+
This script is used to compute the normalization statistics for a given config. It
|
| 4 |
+
will compute the mean and standard deviation of the data in the dataset and save it
|
| 5 |
+
to the config assets directory.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
|
| 10 |
+
import numpy as np
|
| 11 |
+
import tqdm
|
| 12 |
+
import tyro
|
| 13 |
+
|
| 14 |
+
import openpi.models.model as _model
|
| 15 |
+
import openpi.shared.normalize as normalize
|
| 16 |
+
import openpi.training.config as _config
|
| 17 |
+
import openpi.training.data_loader as _data_loader
|
| 18 |
+
import openpi.transforms as transforms
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class RemoveStrings(transforms.DataTransformFn):
|
| 22 |
+
def __call__(self, x: dict) -> dict:
|
| 23 |
+
return {k: v for k, v in x.items() if not np.issubdtype(np.asarray(v).dtype, np.str_)}
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class KeepNormKeys(transforms.DataTransformFn):
|
| 27 |
+
def __call__(self, x: dict) -> dict:
|
| 28 |
+
return {"state": np.asarray(x["state"], dtype=np.float32), "actions": np.asarray(x["actions"], dtype=np.float32)}
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def create_torch_dataloader(
|
| 32 |
+
data_config: _config.DataConfig,
|
| 33 |
+
action_horizon: int,
|
| 34 |
+
batch_size: int,
|
| 35 |
+
model_config: _model.BaseModelConfig,
|
| 36 |
+
num_workers: int,
|
| 37 |
+
max_frames: int | None = None,
|
| 38 |
+
fast_state_action_only: bool = False,
|
| 39 |
+
) -> tuple[_data_loader.Dataset, int]:
|
| 40 |
+
if data_config.repo_id is None:
|
| 41 |
+
raise ValueError("Data config must have a repo_id")
|
| 42 |
+
dataset = _data_loader.create_torch_dataset(data_config, action_horizon, model_config)
|
| 43 |
+
if fast_state_action_only:
|
| 44 |
+
transform_fns = [
|
| 45 |
+
*data_config.repack_transforms.inputs,
|
| 46 |
+
KeepNormKeys(),
|
| 47 |
+
]
|
| 48 |
+
else:
|
| 49 |
+
transform_fns = [
|
| 50 |
+
*data_config.repack_transforms.inputs,
|
| 51 |
+
*data_config.data_transforms.inputs,
|
| 52 |
+
# Remove strings since they are not supported by JAX and are not needed to compute norm stats.
|
| 53 |
+
RemoveStrings(),
|
| 54 |
+
]
|
| 55 |
+
dataset = _data_loader.TransformedDataset(dataset, transform_fns)
|
| 56 |
+
if max_frames is not None and max_frames < len(dataset):
|
| 57 |
+
num_batches = max_frames // batch_size
|
| 58 |
+
shuffle = True
|
| 59 |
+
else:
|
| 60 |
+
num_batches = len(dataset) // batch_size
|
| 61 |
+
shuffle = False
|
| 62 |
+
data_loader = _data_loader.TorchDataLoader(
|
| 63 |
+
dataset,
|
| 64 |
+
local_batch_size=batch_size,
|
| 65 |
+
num_workers=num_workers,
|
| 66 |
+
shuffle=shuffle,
|
| 67 |
+
num_batches=num_batches,
|
| 68 |
+
)
|
| 69 |
+
return data_loader, num_batches
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def create_rlds_dataloader(
|
| 73 |
+
data_config: _config.DataConfig,
|
| 74 |
+
action_horizon: int,
|
| 75 |
+
batch_size: int,
|
| 76 |
+
max_frames: int | None = None,
|
| 77 |
+
) -> tuple[_data_loader.Dataset, int]:
|
| 78 |
+
dataset = _data_loader.create_rlds_dataset(data_config, action_horizon, batch_size, shuffle=False)
|
| 79 |
+
dataset = _data_loader.IterableTransformedDataset(
|
| 80 |
+
dataset,
|
| 81 |
+
[
|
| 82 |
+
*data_config.repack_transforms.inputs,
|
| 83 |
+
*data_config.data_transforms.inputs,
|
| 84 |
+
# Remove strings since they are not supported by JAX and are not needed to compute norm stats.
|
| 85 |
+
RemoveStrings(),
|
| 86 |
+
],
|
| 87 |
+
is_batched=True,
|
| 88 |
+
)
|
| 89 |
+
if max_frames is not None and max_frames < len(dataset):
|
| 90 |
+
num_batches = max_frames // batch_size
|
| 91 |
+
else:
|
| 92 |
+
# NOTE: this length is currently hard-coded for DROID.
|
| 93 |
+
num_batches = len(dataset) // batch_size
|
| 94 |
+
data_loader = _data_loader.RLDSDataLoader(
|
| 95 |
+
dataset,
|
| 96 |
+
num_batches=num_batches,
|
| 97 |
+
)
|
| 98 |
+
return data_loader, num_batches
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def _repo_ids(repo_id) -> list[str]:
|
| 104 |
+
if isinstance(repo_id, (tuple, list)):
|
| 105 |
+
return [str(x) for x in repo_id]
|
| 106 |
+
return [str(repo_id)]
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def _write_norm_stats(config: _config.TrainConfig, data_config: _config.DataConfig, norm_stats: dict[str, normalize.NormStats]) -> None:
|
| 110 |
+
asset_id = data_config.asset_id or data_config.repo_id
|
| 111 |
+
if not isinstance(asset_id, str):
|
| 112 |
+
raise ValueError("Multi-dataset configs must set assets.asset_id before computing norm stats.")
|
| 113 |
+
output_path = config.assets_dirs / asset_id
|
| 114 |
+
print(f"Writing stats to: {output_path}")
|
| 115 |
+
normalize.save(output_path, norm_stats)
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def compute_parquet_state_action_norm_stats(
|
| 119 |
+
config: _config.TrainConfig,
|
| 120 |
+
data_config: _config.DataConfig,
|
| 121 |
+
max_frames: int | None = None,
|
| 122 |
+
state_key: str = "observation.state",
|
| 123 |
+
action_key: str = "action",
|
| 124 |
+
) -> None:
|
| 125 |
+
import pathlib
|
| 126 |
+
import pyarrow.parquet as pq
|
| 127 |
+
|
| 128 |
+
stats = {"state": normalize.RunningStats(), "actions": normalize.RunningStats()}
|
| 129 |
+
total_frames = 0
|
| 130 |
+
parquet_files = []
|
| 131 |
+
for root in _repo_ids(data_config.repo_id):
|
| 132 |
+
root_path = os.path.abspath(root)
|
| 133 |
+
if not os.path.exists(os.path.join(root_path, "meta", "info.json")):
|
| 134 |
+
raise ValueError(f"parquet_state_action_only requires local LeRobot roots, got: {root}")
|
| 135 |
+
files = sorted(str(p) for p in pathlib.Path(root_path).glob("data/chunk-*/episode_*.parquet"))
|
| 136 |
+
parquet_files.extend(files)
|
| 137 |
+
print(f"Using parquet state/action-only norm stats path over {len(parquet_files)} parquet files.")
|
| 138 |
+
for parquet_path in tqdm.tqdm(parquet_files, desc="Reading parquet stats"):
|
| 139 |
+
table = pq.read_table(parquet_path, columns=[state_key, action_key])
|
| 140 |
+
state = np.asarray(table[state_key].to_pylist(), dtype=np.float32)
|
| 141 |
+
action = np.asarray(table[action_key].to_pylist(), dtype=np.float32)
|
| 142 |
+
if max_frames is not None:
|
| 143 |
+
remaining = max_frames - total_frames
|
| 144 |
+
if remaining <= 0:
|
| 145 |
+
break
|
| 146 |
+
state = state[:remaining]
|
| 147 |
+
action = action[:remaining]
|
| 148 |
+
if len(state) == 0:
|
| 149 |
+
continue
|
| 150 |
+
stats["state"].update(state)
|
| 151 |
+
stats["actions"].update(action)
|
| 152 |
+
total_frames += len(state)
|
| 153 |
+
if max_frames is not None and total_frames >= max_frames:
|
| 154 |
+
break
|
| 155 |
+
if total_frames == 0:
|
| 156 |
+
raise ValueError("No frames were read for norm stats.")
|
| 157 |
+
print(f"Read {total_frames} frames for norm stats.")
|
| 158 |
+
norm_stats = {key: stat.get_statistics() for key, stat in stats.items()}
|
| 159 |
+
_write_norm_stats(config, data_config, norm_stats)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def main(
|
| 163 |
+
config_name: str,
|
| 164 |
+
max_frames: int | None = None,
|
| 165 |
+
fast_state_action_only: bool = False,
|
| 166 |
+
parquet_state_action_only: bool = False,
|
| 167 |
+
state_key: str = "observation.state",
|
| 168 |
+
action_key: str = "action",
|
| 169 |
+
):
|
| 170 |
+
config = _config.get_config(config_name)
|
| 171 |
+
data_config = config.data.create(config.assets_dirs, config.model)
|
| 172 |
+
|
| 173 |
+
if parquet_state_action_only:
|
| 174 |
+
compute_parquet_state_action_norm_stats(config, data_config, max_frames, state_key, action_key)
|
| 175 |
+
return
|
| 176 |
+
|
| 177 |
+
if data_config.rlds_data_dir is not None:
|
| 178 |
+
if fast_state_action_only:
|
| 179 |
+
raise ValueError("fast_state_action_only is only implemented for torch/LeRobot datasets.")
|
| 180 |
+
data_loader, num_batches = create_rlds_dataloader(
|
| 181 |
+
data_config, config.model.action_horizon, config.batch_size, max_frames
|
| 182 |
+
)
|
| 183 |
+
else:
|
| 184 |
+
if fast_state_action_only:
|
| 185 |
+
print("Using fast state/action-only norm stats path; image/video transforms are skipped.")
|
| 186 |
+
data_loader, num_batches = create_torch_dataloader(
|
| 187 |
+
data_config,
|
| 188 |
+
config.model.action_horizon,
|
| 189 |
+
config.batch_size,
|
| 190 |
+
config.model,
|
| 191 |
+
config.num_workers,
|
| 192 |
+
max_frames,
|
| 193 |
+
fast_state_action_only=fast_state_action_only,
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
keys = ["state", "actions"]
|
| 197 |
+
stats = {key: normalize.RunningStats() for key in keys}
|
| 198 |
+
|
| 199 |
+
for batch in tqdm.tqdm(data_loader, total=num_batches, desc="Computing stats"):
|
| 200 |
+
for key in keys:
|
| 201 |
+
stats[key].update(np.asarray(batch[key]))
|
| 202 |
+
|
| 203 |
+
norm_stats = {key: stats.get_statistics() for key, stats in stats.items()}
|
| 204 |
+
|
| 205 |
+
asset_id = data_config.asset_id or data_config.repo_id
|
| 206 |
+
if not isinstance(asset_id, str):
|
| 207 |
+
raise ValueError("Multi-dataset configs must set assets.asset_id before computing norm stats.")
|
| 208 |
+
output_path = config.assets_dirs / asset_id
|
| 209 |
+
print(f"Writing stats to: {output_path}")
|
| 210 |
+
normalize.save(output_path, norm_stats)
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
if __name__ == "__main__":
|
| 214 |
+
tyro.cli(main)
|
pi05_twotasks_pytorch/code/openpi-main/scripts/train.py
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import dataclasses
|
| 2 |
+
import functools
|
| 3 |
+
import logging
|
| 4 |
+
import os
|
| 5 |
+
import platform
|
| 6 |
+
import struct
|
| 7 |
+
import time
|
| 8 |
+
from typing import Any
|
| 9 |
+
|
| 10 |
+
import etils.epath as epath
|
| 11 |
+
import flax.nnx as nnx
|
| 12 |
+
from flax.training import common_utils
|
| 13 |
+
import flax.traverse_util as traverse_util
|
| 14 |
+
import jax
|
| 15 |
+
import jax.experimental
|
| 16 |
+
import jax.numpy as jnp
|
| 17 |
+
import numpy as np
|
| 18 |
+
import optax
|
| 19 |
+
import tqdm_loggable.auto as tqdm
|
| 20 |
+
import wandb
|
| 21 |
+
|
| 22 |
+
import openpi.models.model as _model
|
| 23 |
+
import openpi.shared.array_typing as at
|
| 24 |
+
import openpi.shared.nnx_utils as nnx_utils
|
| 25 |
+
import openpi.training.checkpoints as _checkpoints
|
| 26 |
+
import openpi.training.config as _config
|
| 27 |
+
import openpi.training.data_loader as _data_loader
|
| 28 |
+
import openpi.training.optimizer as _optimizer
|
| 29 |
+
import openpi.training.sharding as sharding
|
| 30 |
+
import openpi.training.utils as training_utils
|
| 31 |
+
import openpi.training.weight_loaders as _weight_loaders
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def init_logging():
|
| 35 |
+
"""Custom logging format for better readability."""
|
| 36 |
+
level_mapping = {"DEBUG": "D", "INFO": "I", "WARNING": "W", "ERROR": "E", "CRITICAL": "C"}
|
| 37 |
+
|
| 38 |
+
class CustomFormatter(logging.Formatter):
|
| 39 |
+
def format(self, record):
|
| 40 |
+
record.levelname = level_mapping.get(record.levelname, record.levelname)
|
| 41 |
+
return super().format(record)
|
| 42 |
+
|
| 43 |
+
formatter = CustomFormatter(
|
| 44 |
+
fmt="%(asctime)s.%(msecs)03d [%(levelname)s] %(message)-80s (%(process)d:%(filename)s:%(lineno)s)",
|
| 45 |
+
datefmt="%H:%M:%S",
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
logger = logging.getLogger()
|
| 49 |
+
logger.setLevel(logging.INFO)
|
| 50 |
+
logger.handlers[0].setFormatter(formatter)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
_CRC32C_TABLE = None
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def _crc32c(data: bytes) -> int:
|
| 57 |
+
global _CRC32C_TABLE
|
| 58 |
+
if _CRC32C_TABLE is None:
|
| 59 |
+
table = []
|
| 60 |
+
for i in range(256):
|
| 61 |
+
crc = i
|
| 62 |
+
for _ in range(8):
|
| 63 |
+
if crc & 1:
|
| 64 |
+
crc = (crc >> 1) ^ 0x82F63B78
|
| 65 |
+
else:
|
| 66 |
+
crc >>= 1
|
| 67 |
+
table.append(crc & 0xFFFFFFFF)
|
| 68 |
+
_CRC32C_TABLE = table
|
| 69 |
+
|
| 70 |
+
crc = 0xFFFFFFFF
|
| 71 |
+
for byte in data:
|
| 72 |
+
crc = (crc >> 8) ^ _CRC32C_TABLE[(crc ^ byte) & 0xFF]
|
| 73 |
+
return crc ^ 0xFFFFFFFF
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def _masked_crc32c(data: bytes) -> int:
|
| 77 |
+
crc = _crc32c(data)
|
| 78 |
+
return (((crc >> 15) | ((crc << 17) & 0xFFFFFFFF)) + 0xA282EAD8) & 0xFFFFFFFF
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _varint(value: int) -> bytes:
|
| 82 |
+
out = bytearray()
|
| 83 |
+
while value > 0x7F:
|
| 84 |
+
out.append((value & 0x7F) | 0x80)
|
| 85 |
+
value >>= 7
|
| 86 |
+
out.append(value)
|
| 87 |
+
return bytes(out)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _bytes_field(field: int, value: bytes) -> bytes:
|
| 91 |
+
return _varint((field << 3) | 2) + _varint(len(value)) + value
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _event_file_version_record() -> bytes:
|
| 95 |
+
wall_time = time.time()
|
| 96 |
+
return _varint((1 << 3) | 1) + struct.pack("<d", wall_time) + _bytes_field(3, b"brain.Event:2")
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def _scalar_event_record(tag: str, value: float, step: int) -> bytes:
|
| 100 |
+
tag_bytes = tag.encode("utf-8")
|
| 101 |
+
scalar_value = _bytes_field(1, tag_bytes) + _varint((2 << 3) | 5) + struct.pack("<f", float(value))
|
| 102 |
+
summary = _bytes_field(1, scalar_value)
|
| 103 |
+
return (
|
| 104 |
+
_varint((1 << 3) | 1)
|
| 105 |
+
+ struct.pack("<d", time.time())
|
| 106 |
+
+ _varint((2 << 3) | 0)
|
| 107 |
+
+ _varint(int(step))
|
| 108 |
+
+ _bytes_field(5, summary)
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
class _TensorBoardEventWriter:
|
| 113 |
+
"""Minimal TensorBoard scalar event writer.
|
| 114 |
+
|
| 115 |
+
It writes TFRecord event files with scalar summaries, avoiding an extra runtime
|
| 116 |
+
dependency on tensorboard inside the training environment.
|
| 117 |
+
"""
|
| 118 |
+
|
| 119 |
+
def __init__(self, log_dir: str):
|
| 120 |
+
os.makedirs(log_dir, exist_ok=True)
|
| 121 |
+
filename = f"events.out.tfevents.{int(time.time())}.{platform.node()}.{os.getpid()}.0"
|
| 122 |
+
self.path = os.path.join(log_dir, filename)
|
| 123 |
+
self._file = open(self.path, "wb")
|
| 124 |
+
self._write_record(_event_file_version_record())
|
| 125 |
+
self.flush()
|
| 126 |
+
|
| 127 |
+
def _write_record(self, payload: bytes):
|
| 128 |
+
length = struct.pack("<Q", len(payload))
|
| 129 |
+
self._file.write(length)
|
| 130 |
+
self._file.write(struct.pack("<I", _masked_crc32c(length)))
|
| 131 |
+
self._file.write(payload)
|
| 132 |
+
self._file.write(struct.pack("<I", _masked_crc32c(payload)))
|
| 133 |
+
|
| 134 |
+
def add_scalar(self, tag: str, value: float, step: int):
|
| 135 |
+
self._write_record(_scalar_event_record(tag, float(value), int(step)))
|
| 136 |
+
|
| 137 |
+
def flush(self):
|
| 138 |
+
self._file.flush()
|
| 139 |
+
|
| 140 |
+
def close(self):
|
| 141 |
+
self._file.close()
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def init_tensorboard(config: _config.TrainConfig):
|
| 145 |
+
if not config.tensorboard_enabled:
|
| 146 |
+
return None
|
| 147 |
+
log_dir = config.tensorboard_log_dir or str(config.checkpoint_dir / "tensorboard")
|
| 148 |
+
writer = _TensorBoardEventWriter(log_dir)
|
| 149 |
+
logging.info("TensorBoard logging enabled: %s", log_dir)
|
| 150 |
+
logging.info("TensorBoard event file: %s", writer.path)
|
| 151 |
+
return writer
|
| 152 |
+
|
| 153 |
+
def init_wandb(config: _config.TrainConfig, *, resuming: bool, log_code: bool = False, enabled: bool = True):
|
| 154 |
+
if not enabled:
|
| 155 |
+
wandb.init(mode="disabled")
|
| 156 |
+
return
|
| 157 |
+
|
| 158 |
+
ckpt_dir = config.checkpoint_dir
|
| 159 |
+
if not ckpt_dir.exists():
|
| 160 |
+
raise FileNotFoundError(f"Checkpoint directory {ckpt_dir} does not exist.")
|
| 161 |
+
if resuming:
|
| 162 |
+
run_id = (ckpt_dir / "wandb_id.txt").read_text().strip()
|
| 163 |
+
wandb.init(id=run_id, resume="must", project=config.project_name)
|
| 164 |
+
else:
|
| 165 |
+
wandb.init(
|
| 166 |
+
name=config.exp_name,
|
| 167 |
+
config=dataclasses.asdict(config),
|
| 168 |
+
project=config.project_name,
|
| 169 |
+
)
|
| 170 |
+
(ckpt_dir / "wandb_id.txt").write_text(wandb.run.id)
|
| 171 |
+
|
| 172 |
+
if log_code:
|
| 173 |
+
wandb.run.log_code(epath.Path(__file__).parent.parent)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def _load_weights_and_validate(loader: _weight_loaders.WeightLoader, params_shape: at.Params) -> at.Params:
|
| 177 |
+
"""Loads and validates the weights. Returns a loaded subset of the weights."""
|
| 178 |
+
loaded_params = loader.load(params_shape)
|
| 179 |
+
at.check_pytree_equality(expected=params_shape, got=loaded_params, check_shapes=True, check_dtypes=True)
|
| 180 |
+
|
| 181 |
+
# Remove jax.ShapeDtypeStruct from the loaded params. This makes sure that only the loaded params are returned.
|
| 182 |
+
return traverse_util.unflatten_dict(
|
| 183 |
+
{k: v for k, v in traverse_util.flatten_dict(loaded_params).items() if not isinstance(v, jax.ShapeDtypeStruct)}
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
@at.typecheck
|
| 188 |
+
def init_train_state(
|
| 189 |
+
config: _config.TrainConfig, init_rng: at.KeyArrayLike, mesh: jax.sharding.Mesh, *, resume: bool
|
| 190 |
+
) -> tuple[training_utils.TrainState, Any]:
|
| 191 |
+
tx = _optimizer.create_optimizer(config.optimizer, config.lr_schedule, weight_decay_mask=None)
|
| 192 |
+
|
| 193 |
+
def init(rng: at.KeyArrayLike, partial_params: at.Params | None = None) -> training_utils.TrainState:
|
| 194 |
+
rng, model_rng = jax.random.split(rng)
|
| 195 |
+
# initialize the model (and its parameters).
|
| 196 |
+
model = config.model.create(model_rng)
|
| 197 |
+
|
| 198 |
+
# Merge the partial params into the model.
|
| 199 |
+
if partial_params is not None:
|
| 200 |
+
graphdef, state = nnx.split(model)
|
| 201 |
+
# This will produce an error if the partial params are not a subset of the state.
|
| 202 |
+
state.replace_by_pure_dict(partial_params)
|
| 203 |
+
model = nnx.merge(graphdef, state)
|
| 204 |
+
|
| 205 |
+
params = nnx.state(model)
|
| 206 |
+
# Convert frozen params to bfloat16.
|
| 207 |
+
params = nnx_utils.state_map(params, config.freeze_filter, lambda p: p.replace(p.value.astype(jnp.bfloat16)))
|
| 208 |
+
|
| 209 |
+
return training_utils.TrainState(
|
| 210 |
+
step=0,
|
| 211 |
+
params=params,
|
| 212 |
+
model_def=nnx.graphdef(model),
|
| 213 |
+
tx=tx,
|
| 214 |
+
opt_state=tx.init(params.filter(config.trainable_filter)),
|
| 215 |
+
ema_decay=config.ema_decay,
|
| 216 |
+
ema_params=None if config.ema_decay is None else params,
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
train_state_shape = jax.eval_shape(init, init_rng)
|
| 220 |
+
state_sharding = sharding.fsdp_sharding(train_state_shape, mesh, log=True)
|
| 221 |
+
|
| 222 |
+
if resume:
|
| 223 |
+
return train_state_shape, state_sharding
|
| 224 |
+
|
| 225 |
+
partial_params = _load_weights_and_validate(config.weight_loader, train_state_shape.params.to_pure_dict())
|
| 226 |
+
replicated_sharding = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec())
|
| 227 |
+
|
| 228 |
+
# Initialize the train state and mix in the partial params.
|
| 229 |
+
train_state = jax.jit(
|
| 230 |
+
init,
|
| 231 |
+
donate_argnums=(1,), # donate the partial params buffer.
|
| 232 |
+
in_shardings=replicated_sharding,
|
| 233 |
+
out_shardings=state_sharding,
|
| 234 |
+
)(init_rng, partial_params)
|
| 235 |
+
|
| 236 |
+
return train_state, state_sharding
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
@at.typecheck
|
| 240 |
+
def train_step(
|
| 241 |
+
config: _config.TrainConfig,
|
| 242 |
+
rng: at.KeyArrayLike,
|
| 243 |
+
state: training_utils.TrainState,
|
| 244 |
+
batch: tuple[_model.Observation, _model.Actions],
|
| 245 |
+
) -> tuple[training_utils.TrainState, dict[str, at.Array]]:
|
| 246 |
+
model = nnx.merge(state.model_def, state.params)
|
| 247 |
+
model.train()
|
| 248 |
+
|
| 249 |
+
@at.typecheck
|
| 250 |
+
def loss_fn(
|
| 251 |
+
model: _model.BaseModel, rng: at.KeyArrayLike, observation: _model.Observation, actions: _model.Actions
|
| 252 |
+
):
|
| 253 |
+
chunked_loss = model.compute_loss(rng, observation, actions, train=True)
|
| 254 |
+
return jnp.mean(chunked_loss)
|
| 255 |
+
|
| 256 |
+
train_rng = jax.random.fold_in(rng, state.step)
|
| 257 |
+
observation, actions = batch
|
| 258 |
+
|
| 259 |
+
# Filter out frozen params.
|
| 260 |
+
diff_state = nnx.DiffState(0, config.trainable_filter)
|
| 261 |
+
loss, grads = nnx.value_and_grad(loss_fn, argnums=diff_state)(model, train_rng, observation, actions)
|
| 262 |
+
|
| 263 |
+
params = state.params.filter(config.trainable_filter)
|
| 264 |
+
updates, new_opt_state = state.tx.update(grads, state.opt_state, params)
|
| 265 |
+
new_params = optax.apply_updates(params, updates)
|
| 266 |
+
|
| 267 |
+
# Update the model in place and return the new full state.
|
| 268 |
+
nnx.update(model, new_params)
|
| 269 |
+
new_params = nnx.state(model)
|
| 270 |
+
|
| 271 |
+
new_state = dataclasses.replace(state, step=state.step + 1, params=new_params, opt_state=new_opt_state)
|
| 272 |
+
if state.ema_decay is not None:
|
| 273 |
+
new_state = dataclasses.replace(
|
| 274 |
+
new_state,
|
| 275 |
+
ema_params=jax.tree.map(
|
| 276 |
+
lambda old, new: state.ema_decay * old + (1 - state.ema_decay) * new, state.ema_params, new_params
|
| 277 |
+
),
|
| 278 |
+
)
|
| 279 |
+
|
| 280 |
+
# Filter out params that aren't kernels.
|
| 281 |
+
kernel_params = nnx.state(
|
| 282 |
+
model,
|
| 283 |
+
nnx.All(
|
| 284 |
+
nnx.Param,
|
| 285 |
+
nnx.Not(nnx_utils.PathRegex(".*/(bias|scale|pos_embedding|input_embedding)")),
|
| 286 |
+
lambda _, x: x.value.ndim > 1,
|
| 287 |
+
),
|
| 288 |
+
)
|
| 289 |
+
info = {
|
| 290 |
+
"loss": loss,
|
| 291 |
+
"grad_norm": optax.global_norm(grads),
|
| 292 |
+
"param_norm": optax.global_norm(kernel_params),
|
| 293 |
+
}
|
| 294 |
+
return new_state, info
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
def main(config: _config.TrainConfig):
|
| 298 |
+
init_logging()
|
| 299 |
+
logging.info(f"Running on: {platform.node()}")
|
| 300 |
+
|
| 301 |
+
if config.batch_size % jax.device_count() != 0:
|
| 302 |
+
raise ValueError(
|
| 303 |
+
f"Batch size {config.batch_size} must be divisible by the number of devices {jax.device_count()}."
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
jax.config.update("jax_compilation_cache_dir", str(epath.Path("~/.cache/jax").expanduser()))
|
| 307 |
+
|
| 308 |
+
rng = jax.random.key(config.seed)
|
| 309 |
+
train_rng, init_rng = jax.random.split(rng)
|
| 310 |
+
|
| 311 |
+
mesh = sharding.make_mesh(config.fsdp_devices)
|
| 312 |
+
data_sharding = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec(sharding.DATA_AXIS))
|
| 313 |
+
replicated_sharding = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec())
|
| 314 |
+
|
| 315 |
+
checkpoint_manager, resuming = _checkpoints.initialize_checkpoint_dir(
|
| 316 |
+
config.checkpoint_dir,
|
| 317 |
+
keep_period=config.keep_period,
|
| 318 |
+
overwrite=config.overwrite,
|
| 319 |
+
resume=config.resume,
|
| 320 |
+
)
|
| 321 |
+
init_wandb(config, resuming=resuming, enabled=config.wandb_enabled)
|
| 322 |
+
tb_writer = init_tensorboard(config)
|
| 323 |
+
|
| 324 |
+
data_loader = _data_loader.create_data_loader(
|
| 325 |
+
config,
|
| 326 |
+
sharding=data_sharding,
|
| 327 |
+
shuffle=True,
|
| 328 |
+
)
|
| 329 |
+
data_iter = iter(data_loader)
|
| 330 |
+
batch = next(data_iter)
|
| 331 |
+
logging.info(f"Initialized data loader:\n{training_utils.array_tree_to_info(batch)}")
|
| 332 |
+
|
| 333 |
+
# Log images from first batch to sanity check.
|
| 334 |
+
images_to_log = [
|
| 335 |
+
wandb.Image(np.concatenate([np.array(img[i]) for img in batch[0].images.values()], axis=1))
|
| 336 |
+
for i in range(min(5, len(next(iter(batch[0].images.values())))))
|
| 337 |
+
]
|
| 338 |
+
wandb.log({"camera_views": images_to_log}, step=0)
|
| 339 |
+
|
| 340 |
+
train_state, train_state_sharding = init_train_state(config, init_rng, mesh, resume=resuming)
|
| 341 |
+
jax.block_until_ready(train_state)
|
| 342 |
+
logging.info(f"Initialized train state:\n{training_utils.array_tree_to_info(train_state.params)}")
|
| 343 |
+
|
| 344 |
+
if resuming:
|
| 345 |
+
train_state = _checkpoints.restore_state(checkpoint_manager, train_state, data_loader)
|
| 346 |
+
|
| 347 |
+
ptrain_step = jax.jit(
|
| 348 |
+
functools.partial(train_step, config),
|
| 349 |
+
in_shardings=(replicated_sharding, train_state_sharding, data_sharding),
|
| 350 |
+
out_shardings=(train_state_sharding, replicated_sharding),
|
| 351 |
+
donate_argnums=(1,),
|
| 352 |
+
)
|
| 353 |
+
|
| 354 |
+
start_step = int(train_state.step)
|
| 355 |
+
pbar = tqdm.tqdm(
|
| 356 |
+
range(start_step, config.num_train_steps),
|
| 357 |
+
initial=start_step,
|
| 358 |
+
total=config.num_train_steps,
|
| 359 |
+
dynamic_ncols=True,
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
infos = []
|
| 363 |
+
for step in pbar:
|
| 364 |
+
with sharding.set_mesh(mesh):
|
| 365 |
+
train_state, info = ptrain_step(train_rng, train_state, batch)
|
| 366 |
+
infos.append(info)
|
| 367 |
+
if step % config.log_interval == 0:
|
| 368 |
+
stacked_infos = common_utils.stack_forest(infos)
|
| 369 |
+
reduced_info = jax.device_get(jax.tree.map(jnp.mean, stacked_infos))
|
| 370 |
+
info_str = ", ".join(f"{k}={v:.4f}" for k, v in reduced_info.items())
|
| 371 |
+
pbar.write(f"Step {step}: {info_str}")
|
| 372 |
+
wandb.log(reduced_info, step=step)
|
| 373 |
+
if tb_writer is not None:
|
| 374 |
+
for key, value in reduced_info.items():
|
| 375 |
+
tb_writer.add_scalar(f"train/{key}", float(value), step)
|
| 376 |
+
tb_writer.flush()
|
| 377 |
+
infos = []
|
| 378 |
+
batch = next(data_iter)
|
| 379 |
+
|
| 380 |
+
if (step % config.save_interval == 0 and step > start_step) or step == config.num_train_steps - 1:
|
| 381 |
+
_checkpoints.save_state(checkpoint_manager, train_state, data_loader, step)
|
| 382 |
+
|
| 383 |
+
logging.info("Waiting for checkpoint manager to finish")
|
| 384 |
+
checkpoint_manager.wait_until_finished()
|
| 385 |
+
if tb_writer is not None:
|
| 386 |
+
tb_writer.close()
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
if __name__ == "__main__":
|
| 390 |
+
main(_config.cli())
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/__init__.py
ADDED
|
File without changes
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (202 Bytes). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/__pycache__/transforms.cpython-311.pyc
ADDED
|
Binary file (31.3 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/conftest.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
import pynvml
|
| 4 |
+
import pytest
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def set_jax_cpu_backend_if_no_gpu() -> None:
|
| 8 |
+
try:
|
| 9 |
+
pynvml.nvmlInit()
|
| 10 |
+
pynvml.nvmlShutdown()
|
| 11 |
+
except pynvml.NVMLError:
|
| 12 |
+
# No GPU found.
|
| 13 |
+
os.environ["JAX_PLATFORMS"] = "cpu"
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def pytest_configure(config: pytest.Config) -> None:
|
| 17 |
+
set_jax_cpu_backend_if_no_gpu()
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__init__.py
ADDED
|
File without changes
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (209 Bytes). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/gemma.cpython-311.pyc
ADDED
|
Binary file (27.3 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/gemma_fast.cpython-311.pyc
ADDED
|
Binary file (21.6 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/lora.cpython-311.pyc
ADDED
|
Binary file (9.04 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/model.cpython-311.pyc
ADDED
|
Binary file (17.9 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/pi0.cpython-311.pyc
ADDED
|
Binary file (16.9 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/pi0_config.cpython-311.pyc
ADDED
|
Binary file (6.05 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/pi0_fast.cpython-311.pyc
ADDED
|
Binary file (19.2 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/siglip.cpython-311.pyc
ADDED
|
Binary file (16.3 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/__pycache__/tokenizer.cpython-311.pyc
ADDED
|
Binary file (23.7 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/gemma.py
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 Big Vision Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Gemma adaptation for Pi, taken from big_vision.
|
| 16 |
+
|
| 17 |
+
We follow this einsum axis naming convention:
|
| 18 |
+
B: batch
|
| 19 |
+
T: query length
|
| 20 |
+
S: k/v length
|
| 21 |
+
N: num query heads
|
| 22 |
+
K: num k/v heads
|
| 23 |
+
G: num query heads per k/v head
|
| 24 |
+
H: head dim
|
| 25 |
+
D: d_model ("features")
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
from collections.abc import Sequence
|
| 29 |
+
import dataclasses
|
| 30 |
+
from typing import Literal, TypeAlias
|
| 31 |
+
|
| 32 |
+
import einops
|
| 33 |
+
import flax.linen as nn
|
| 34 |
+
import jax
|
| 35 |
+
import jax.numpy as jnp
|
| 36 |
+
|
| 37 |
+
import openpi.models.lora as lora
|
| 38 |
+
import openpi.shared.array_typing as at
|
| 39 |
+
import openpi.training.sharding as sharding
|
| 40 |
+
|
| 41 |
+
PALIGEMMA_VOCAB_SIZE = 257_152
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@dataclasses.dataclass
|
| 45 |
+
class Config:
|
| 46 |
+
width: int
|
| 47 |
+
depth: int
|
| 48 |
+
mlp_dim: int
|
| 49 |
+
num_heads: int
|
| 50 |
+
num_kv_heads: int
|
| 51 |
+
head_dim: int
|
| 52 |
+
lora_configs: dict[str, lora.LoRAConfig] = dataclasses.field(default_factory=dict)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
Variant = Literal["dummy", "gemma_300m", "gemma_300m_lora", "gemma_2b", "gemma_2b_lora"]
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def get_config(variant: Variant) -> Config:
|
| 59 |
+
"""Returns config for specified gemma variant."""
|
| 60 |
+
if variant == "dummy":
|
| 61 |
+
return Config(
|
| 62 |
+
width=64,
|
| 63 |
+
depth=4,
|
| 64 |
+
mlp_dim=128,
|
| 65 |
+
num_heads=8,
|
| 66 |
+
num_kv_heads=1,
|
| 67 |
+
head_dim=16,
|
| 68 |
+
)
|
| 69 |
+
if variant == "gemma_300m":
|
| 70 |
+
# 311M params
|
| 71 |
+
return Config(
|
| 72 |
+
width=1024,
|
| 73 |
+
depth=18,
|
| 74 |
+
mlp_dim=4096,
|
| 75 |
+
num_heads=8,
|
| 76 |
+
num_kv_heads=1,
|
| 77 |
+
head_dim=256,
|
| 78 |
+
)
|
| 79 |
+
if variant == "gemma_2b":
|
| 80 |
+
return Config(
|
| 81 |
+
width=2048,
|
| 82 |
+
depth=18,
|
| 83 |
+
mlp_dim=16_384,
|
| 84 |
+
num_heads=8,
|
| 85 |
+
num_kv_heads=1,
|
| 86 |
+
head_dim=256,
|
| 87 |
+
)
|
| 88 |
+
if variant == "gemma_2b_lora":
|
| 89 |
+
return Config(
|
| 90 |
+
width=2048,
|
| 91 |
+
depth=18,
|
| 92 |
+
mlp_dim=16_384,
|
| 93 |
+
num_heads=8,
|
| 94 |
+
num_kv_heads=1,
|
| 95 |
+
head_dim=256,
|
| 96 |
+
lora_configs={"attn": lora.LoRAConfig(rank=16, alpha=16.0), "ffn": lora.LoRAConfig(rank=16, alpha=16.0)},
|
| 97 |
+
)
|
| 98 |
+
if variant == "gemma_300m_lora":
|
| 99 |
+
# 311M params
|
| 100 |
+
return Config(
|
| 101 |
+
width=1024,
|
| 102 |
+
depth=18,
|
| 103 |
+
mlp_dim=4096,
|
| 104 |
+
num_heads=8,
|
| 105 |
+
num_kv_heads=1,
|
| 106 |
+
head_dim=256,
|
| 107 |
+
lora_configs={"attn": lora.LoRAConfig(rank=32, alpha=32.0), "ffn": lora.LoRAConfig(rank=32, alpha=32.0)},
|
| 108 |
+
)
|
| 109 |
+
raise ValueError(f"Unknown variant: {variant}")
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
@at.typecheck
|
| 113 |
+
class RMSNorm(nn.Module):
|
| 114 |
+
@nn.compact
|
| 115 |
+
def __call__(self, x, cond):
|
| 116 |
+
dtype = x.dtype # original dtype, could be half-precision
|
| 117 |
+
var = jnp.mean(jnp.square(x.astype(jnp.float32)), axis=-1, keepdims=True) # compute variance in float32
|
| 118 |
+
normed_inputs = jnp.asarray(x * jnp.reciprocal(jnp.sqrt(var + 1e-06))) # compute normalization in float32
|
| 119 |
+
if cond is None:
|
| 120 |
+
# regular RMSNorm
|
| 121 |
+
scale = self.param("scale", nn.initializers.zeros_init(), (x.shape[-1]))
|
| 122 |
+
normed_inputs = normed_inputs * (
|
| 123 |
+
1 + scale
|
| 124 |
+
) # scale by learned parameter in float32 (matches Flax implementation)
|
| 125 |
+
return normed_inputs.astype(dtype), None # return in original dtype
|
| 126 |
+
|
| 127 |
+
# adaptive RMSNorm
|
| 128 |
+
modulation = nn.Dense(x.shape[-1] * 3, kernel_init=nn.initializers.zeros, dtype=dtype)(cond)
|
| 129 |
+
scale, shift, gate = jnp.split(modulation[:, None, :], 3, axis=-1)
|
| 130 |
+
normed_inputs = normed_inputs * (1 + scale) + shift # scale and shift in float32
|
| 131 |
+
return normed_inputs.astype(dtype), gate
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
@at.typecheck
|
| 135 |
+
class Embedder(nn.Module):
|
| 136 |
+
"""Embedder module."""
|
| 137 |
+
|
| 138 |
+
vocab_size: int
|
| 139 |
+
embed_dim: int
|
| 140 |
+
|
| 141 |
+
def setup(self):
|
| 142 |
+
self.input_embedding_table = self.param(
|
| 143 |
+
"input_embedding",
|
| 144 |
+
nn.initializers.normal(),
|
| 145 |
+
(self.vocab_size, self.embed_dim),
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
def encode(self, x):
|
| 149 |
+
x = self.input_embedding_table[(x,)]
|
| 150 |
+
x *= jnp.sqrt(self.embed_dim).astype(x.dtype)
|
| 151 |
+
return x
|
| 152 |
+
|
| 153 |
+
def decode(self, x):
|
| 154 |
+
return jnp.dot(x, self.input_embedding_table.T)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
@at.typecheck
|
| 158 |
+
class Attention(nn.Module):
|
| 159 |
+
"""Attention module."""
|
| 160 |
+
|
| 161 |
+
configs: Sequence[Config]
|
| 162 |
+
|
| 163 |
+
@nn.compact
|
| 164 |
+
def __call__(self, xs, positions, attn_mask, kv_cache):
|
| 165 |
+
# all experts must share the same head dim, num heads, and num kv heads for self-attention to work
|
| 166 |
+
assert all(config.head_dim == self.configs[0].head_dim for config in self.configs)
|
| 167 |
+
assert all(config.num_heads == self.configs[0].num_heads for config in self.configs)
|
| 168 |
+
assert all(config.num_kv_heads == self.configs[0].num_kv_heads for config in self.configs)
|
| 169 |
+
|
| 170 |
+
dtype = next(x.dtype for x in xs if x is not None) # original dtype, could be half-precision
|
| 171 |
+
|
| 172 |
+
qkvs = []
|
| 173 |
+
for i, (x, config) in enumerate(zip(xs, self.configs, strict=True)):
|
| 174 |
+
if x is None:
|
| 175 |
+
continue
|
| 176 |
+
if config.num_kv_heads == config.num_heads:
|
| 177 |
+
qkv_einsum = lora.Einsum(
|
| 178 |
+
shape=(3, config.num_heads, config.width, config.head_dim),
|
| 179 |
+
name=_name("qkv_einsum", i),
|
| 180 |
+
init_fn=nn.initializers.lecun_normal(in_axis=-2, out_axis=-1, batch_axis=(0, 1)),
|
| 181 |
+
lora_config=config.lora_configs.get("attn"),
|
| 182 |
+
)
|
| 183 |
+
qkvs.append(qkv_einsum("BSD,3KDH->3BSKH", x))
|
| 184 |
+
else:
|
| 185 |
+
q_einsum = lora.Einsum(
|
| 186 |
+
shape=(config.num_heads, config.width, config.head_dim),
|
| 187 |
+
name=_name("q_einsum", i),
|
| 188 |
+
init_fn=nn.initializers.lecun_normal(in_axis=-2, out_axis=-1, batch_axis=(0,)),
|
| 189 |
+
lora_config=config.lora_configs.get("attn"),
|
| 190 |
+
)
|
| 191 |
+
q = q_einsum("BTD,NDH->BTNH", x)
|
| 192 |
+
kv_einsum = lora.Einsum(
|
| 193 |
+
shape=(2, config.num_kv_heads, config.width, config.head_dim),
|
| 194 |
+
name=_name("kv_einsum", i),
|
| 195 |
+
init_fn=nn.initializers.lecun_normal(in_axis=-2, out_axis=-1, batch_axis=(0, 1)),
|
| 196 |
+
lora_config=config.lora_configs.get("attn"),
|
| 197 |
+
)
|
| 198 |
+
k, v = kv_einsum("BSD,2KDH->2BSKH", x)
|
| 199 |
+
qkvs.append((q, k, v))
|
| 200 |
+
|
| 201 |
+
q, k, v = (jnp.concatenate(y, axis=1) for y in zip(*qkvs, strict=True))
|
| 202 |
+
|
| 203 |
+
q = _apply_rope(q, positions=positions)
|
| 204 |
+
q *= self.configs[0].head_dim ** -0.5
|
| 205 |
+
|
| 206 |
+
k = _apply_rope(k, positions=positions)
|
| 207 |
+
|
| 208 |
+
# should still be half-precision here (if input was half-precision)
|
| 209 |
+
assert q.dtype == k.dtype == v.dtype == dtype
|
| 210 |
+
|
| 211 |
+
if kv_cache is not None:
|
| 212 |
+
cache_k, cache_v = kv_cache
|
| 213 |
+
k = jnp.concatenate([cache_k, k], axis=1)
|
| 214 |
+
v = jnp.concatenate([cache_v, v], axis=1)
|
| 215 |
+
|
| 216 |
+
q = einops.rearrange(q, "B T (K G) H -> B T K G H", K=self.configs[0].num_kv_heads)
|
| 217 |
+
logits = jnp.einsum("BTKGH,BSKH->BKGTS", q, k, preferred_element_type=jnp.float32)
|
| 218 |
+
|
| 219 |
+
if attn_mask.shape != (q.shape[0], 1, q.shape[1], k.shape[1]):
|
| 220 |
+
raise ValueError(
|
| 221 |
+
f"Attention mask with shape {attn_mask.shape} but shapes for q and k are: {q.shape} and {k.shape}"
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
# big_neg = jnp.finfo(logits.dtype).min
|
| 225 |
+
big_neg = -2.3819763e38 # See gemma/modules.py
|
| 226 |
+
masked_logits = jnp.where(attn_mask[:, :, None, :, :], logits, big_neg)
|
| 227 |
+
|
| 228 |
+
probs = jax.nn.softmax(masked_logits, axis=-1).astype(dtype)
|
| 229 |
+
|
| 230 |
+
encoded = jnp.einsum("BKGTS,BSKH->BTKGH", probs, v)
|
| 231 |
+
encoded = einops.rearrange(encoded, "B T K G H -> B T (K G) H")
|
| 232 |
+
|
| 233 |
+
out = []
|
| 234 |
+
start = 0
|
| 235 |
+
for i, (x, config) in enumerate(zip(xs, self.configs, strict=True)):
|
| 236 |
+
if x is not None:
|
| 237 |
+
end = start + x.shape[1]
|
| 238 |
+
out_einsum = lora.Einsum(
|
| 239 |
+
shape=(config.num_heads, config.head_dim, config.width),
|
| 240 |
+
name=_name("attn_vec_einsum", i),
|
| 241 |
+
init_fn=nn.initializers.lecun_normal(in_axis=(-3, -2), out_axis=-1),
|
| 242 |
+
lora_config=config.lora_configs.get("attn"),
|
| 243 |
+
)
|
| 244 |
+
out.append(out_einsum("BTNH,NHD->BTD", encoded[:, start:end]))
|
| 245 |
+
start = end
|
| 246 |
+
else:
|
| 247 |
+
out.append(None)
|
| 248 |
+
|
| 249 |
+
return out, (k, v)
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
@at.typecheck
|
| 253 |
+
class FeedForward(nn.Module):
|
| 254 |
+
"""Feed forward module."""
|
| 255 |
+
|
| 256 |
+
features: int
|
| 257 |
+
hidden_dim: int
|
| 258 |
+
|
| 259 |
+
@nn.compact
|
| 260 |
+
def __call__(self, x):
|
| 261 |
+
dtype = x.dtype # original dtype, could be half-precision
|
| 262 |
+
w_gating = self.param(
|
| 263 |
+
"gating_einsum",
|
| 264 |
+
nn.initializers.lecun_normal(in_axis=-2, out_axis=-1, batch_axis=(0,)),
|
| 265 |
+
(2, self.features, self.hidden_dim),
|
| 266 |
+
).astype(dtype)
|
| 267 |
+
ff_gate = jnp.dot(x, w_gating[0])
|
| 268 |
+
gate_value = nn.gelu(ff_gate)
|
| 269 |
+
|
| 270 |
+
ff1 = jnp.dot(x, w_gating[1])
|
| 271 |
+
activations = gate_value * ff1
|
| 272 |
+
|
| 273 |
+
w_linear = self.param(
|
| 274 |
+
"linear",
|
| 275 |
+
nn.initializers.lecun_normal(in_axis=-2, out_axis=-1),
|
| 276 |
+
(self.hidden_dim, self.features),
|
| 277 |
+
).astype(dtype)
|
| 278 |
+
outputs = jnp.dot(activations, w_linear)
|
| 279 |
+
assert outputs.dtype == dtype
|
| 280 |
+
return outputs
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
@at.typecheck
|
| 284 |
+
class Block(nn.Module):
|
| 285 |
+
"""Transformer block."""
|
| 286 |
+
|
| 287 |
+
configs: tuple[Config, ...]
|
| 288 |
+
|
| 289 |
+
dropout: float = 0.0
|
| 290 |
+
dropout_bdims: tuple[int, ...] = ()
|
| 291 |
+
|
| 292 |
+
@nn.compact
|
| 293 |
+
def __call__(self, xs, kv_cache, positions, attn_mask, adarms_cond, deterministic=True): # noqa: FBT002
|
| 294 |
+
xs = sharding.activation_sharding_constraint(xs)
|
| 295 |
+
drop = nn.Dropout(self.dropout, self.dropout_bdims) if self.dropout else lambda x, _: x
|
| 296 |
+
|
| 297 |
+
attn = Attention(configs=self.configs, name="attn")
|
| 298 |
+
|
| 299 |
+
pre_attn = []
|
| 300 |
+
gates = []
|
| 301 |
+
for i, x in enumerate(xs):
|
| 302 |
+
if x is not None:
|
| 303 |
+
x, gate = RMSNorm(name=_name("pre_attention_norm", i))(x, adarms_cond[i]) # noqa: PLW2901
|
| 304 |
+
pre_attn.append(x)
|
| 305 |
+
gates.append(gate if x is not None else None)
|
| 306 |
+
|
| 307 |
+
pre_attn = sharding.activation_sharding_constraint(pre_attn)
|
| 308 |
+
post_attn, kv_cache = attn(pre_attn, positions, attn_mask, kv_cache)
|
| 309 |
+
post_attn = jax.tree.map(lambda x: drop(x, deterministic), post_attn)
|
| 310 |
+
post_attn = sharding.activation_sharding_constraint(post_attn)
|
| 311 |
+
xs = [_gated_residual(x, y, gate) for x, y, gate in zip(xs, post_attn, gates, strict=True)]
|
| 312 |
+
xs = sharding.activation_sharding_constraint(xs)
|
| 313 |
+
|
| 314 |
+
out = []
|
| 315 |
+
gates = []
|
| 316 |
+
for i, (x, config) in enumerate(zip(xs, self.configs, strict=True)):
|
| 317 |
+
if x is not None:
|
| 318 |
+
x, gate = RMSNorm(name=_name("pre_ffw_norm", i))(x, adarms_cond[i]) # noqa: PLW2901
|
| 319 |
+
x = lora.FeedForward( # noqa: PLW2901
|
| 320 |
+
features=config.width,
|
| 321 |
+
hidden_dim=config.mlp_dim,
|
| 322 |
+
name=_name("mlp", i),
|
| 323 |
+
lora_config=config.lora_configs.get("ffn"),
|
| 324 |
+
)(x)
|
| 325 |
+
out.append(x)
|
| 326 |
+
gates.append(gate if x is not None else None)
|
| 327 |
+
|
| 328 |
+
out = sharding.activation_sharding_constraint(out)
|
| 329 |
+
out = jax.tree.map(lambda x: drop(x, deterministic), out)
|
| 330 |
+
xs = [_gated_residual(x, y, gate) for x, y, gate in zip(xs, out, gates, strict=True)]
|
| 331 |
+
xs = sharding.activation_sharding_constraint(xs)
|
| 332 |
+
|
| 333 |
+
return xs, kv_cache
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
KVCache: TypeAlias = tuple[at.Float[at.Array, "l b _t _k _h"], at.Float[at.Array, "l b _t _v _h"]]
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
@at.typecheck
|
| 340 |
+
class Module(nn.Module):
|
| 341 |
+
"""Transformer model, supporting a mixture of different weights for different tokens."""
|
| 342 |
+
|
| 343 |
+
configs: Sequence[Config] # list of configs, one for each expert
|
| 344 |
+
embed_dtype: str
|
| 345 |
+
|
| 346 |
+
dropout: float = 0.0
|
| 347 |
+
dropout_bdims: tuple[int, ...] = () # Every float is dropped independently.
|
| 348 |
+
adarms: bool = False
|
| 349 |
+
|
| 350 |
+
def setup(self):
|
| 351 |
+
# all experts must have the same depth
|
| 352 |
+
assert all(config.depth == self.configs[0].depth for config in self.configs)
|
| 353 |
+
|
| 354 |
+
self.embedder = Embedder(
|
| 355 |
+
vocab_size=PALIGEMMA_VOCAB_SIZE,
|
| 356 |
+
embed_dim=self.configs[0].width, # embedder for first expert only
|
| 357 |
+
name="embedder",
|
| 358 |
+
)
|
| 359 |
+
block_cls = nn.remat(
|
| 360 |
+
Block,
|
| 361 |
+
prevent_cse=False,
|
| 362 |
+
static_argnums=(5,), # 0=self, 6=deterministic
|
| 363 |
+
policy=jax.checkpoint_policies.nothing_saveable,
|
| 364 |
+
)
|
| 365 |
+
self.layers = nn.scan(
|
| 366 |
+
block_cls,
|
| 367 |
+
variable_axes={"params": 0},
|
| 368 |
+
split_rngs={"params": True, "dropout": True},
|
| 369 |
+
in_axes=(
|
| 370 |
+
0,
|
| 371 |
+
nn.broadcast,
|
| 372 |
+
nn.broadcast,
|
| 373 |
+
nn.broadcast,
|
| 374 |
+
nn.broadcast,
|
| 375 |
+
), # 0=kv_cache, 1=positions, 2=mask, 3=adarms_cond, 4=deterministic
|
| 376 |
+
length=self.configs[0].depth,
|
| 377 |
+
)(
|
| 378 |
+
configs=self.configs,
|
| 379 |
+
dropout=self.dropout,
|
| 380 |
+
dropout_bdims=self.dropout_bdims,
|
| 381 |
+
)
|
| 382 |
+
self.final_norms = [RMSNorm(name=_name("final_norm", i)) for i in range(len(self.configs))]
|
| 383 |
+
|
| 384 |
+
@at.typecheck
|
| 385 |
+
def embed(self, tokens: at.Int[at.Array, "b t"]) -> at.Float[at.Array, "b t d"]:
|
| 386 |
+
return self.embedder.encode(tokens).astype(self.embed_dtype)
|
| 387 |
+
|
| 388 |
+
@at.typecheck
|
| 389 |
+
def __call__(
|
| 390 |
+
self,
|
| 391 |
+
# list of token arrays, one for each expert, or None if that expert should not be run
|
| 392 |
+
embedded: Sequence[at.Float[at.Array, "b _t _d"] | None],
|
| 393 |
+
positions: at.Int[at.Array, "b t"],
|
| 394 |
+
mask: at.Bool[at.Array, "b t s"],
|
| 395 |
+
adarms_cond: Sequence[at.Float[at.Array, "b _d"] | None] | None = None,
|
| 396 |
+
*,
|
| 397 |
+
kv_cache: KVCache | None = None,
|
| 398 |
+
deterministic: bool = True,
|
| 399 |
+
) -> tuple[Sequence[at.Float[at.Array, "b _t _d"] | None], KVCache]:
|
| 400 |
+
embedded = jax.tree.map(lambda e: e.astype(self.embed_dtype), embedded)
|
| 401 |
+
mask = jnp.asarray(mask)[:, None, :, :]
|
| 402 |
+
if adarms_cond is None:
|
| 403 |
+
adarms_cond = [None] * len(self.configs)
|
| 404 |
+
|
| 405 |
+
embedded, kv_cache = self.layers(embedded, kv_cache, positions, mask, adarms_cond, deterministic)
|
| 406 |
+
|
| 407 |
+
assert all(e.dtype == jnp.dtype(self.embed_dtype) for e in embedded if e is not None)
|
| 408 |
+
|
| 409 |
+
return [
|
| 410 |
+
f(e, a)[0] if e is not None else e for f, e, a in zip(self.final_norms, embedded, adarms_cond, strict=True)
|
| 411 |
+
], kv_cache
|
| 412 |
+
|
| 413 |
+
def init(self, use_adarms: Sequence[bool]):
|
| 414 |
+
"""Convenience method for initializing all parameters, necessary due to the quirks of linen."""
|
| 415 |
+
self.embed(jnp.zeros((1, 1), dtype=jnp.int32))
|
| 416 |
+
self(
|
| 417 |
+
[jnp.zeros((1, 1, c.width)) for c in self.configs],
|
| 418 |
+
jnp.zeros((1, len(self.configs)), dtype=jnp.int32),
|
| 419 |
+
jnp.zeros((1, len(self.configs), len(self.configs)), dtype=bool),
|
| 420 |
+
adarms_cond=[jnp.zeros((1, c.width)) if u else None for u, c in zip(use_adarms, self.configs, strict=True)],
|
| 421 |
+
)
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
def _apply_rope(x, *, positions, max_wavelength=10_000):
|
| 425 |
+
"""Applies RoPE positions [B, L] to x [B, L, H, D]."""
|
| 426 |
+
freq_exponents = (2.0 / x.shape[-1]) * jnp.arange(x.shape[-1] // 2, dtype=jnp.float32)
|
| 427 |
+
timescale = max_wavelength**freq_exponents
|
| 428 |
+
radians = positions[..., None] / timescale[None, None, :]
|
| 429 |
+
radians = radians[..., None, :]
|
| 430 |
+
assert radians.dtype == jnp.float32
|
| 431 |
+
# radians.shape = [...,L,1,d=D/2]
|
| 432 |
+
sin, cos = jnp.sin(radians), jnp.cos(radians)
|
| 433 |
+
x1, x2 = jnp.split(x, 2, axis=-1)
|
| 434 |
+
res = jnp.concatenate([x1 * cos - x2 * sin, x2 * cos + x1 * sin], axis=-1)
|
| 435 |
+
assert res.dtype == jnp.float32
|
| 436 |
+
# The original bigvision impl allows RoPE to upcast to float32. It is then immediately downcast again to the cache
|
| 437 |
+
# dtype when in inference mode (but not in training mode). I don't think any of this was intentional. Based on the
|
| 438 |
+
# original DeepMind impl, as well as the widely-used transformers impl, it is ok to always downcast back to bfloat16
|
| 439 |
+
# here.
|
| 440 |
+
return res.astype(x.dtype)
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
def _name(name, i):
|
| 444 |
+
# we name layers like this because we want the first expert's weights to have no suffix (e.g., "attn"), so that they
|
| 445 |
+
# can be loaded seamlessly from the existing PaliGemma checkpoint. subsequent experts will have a suffix (e.g.,
|
| 446 |
+
# "attn_1") and their weights will be initialized from scratch. in practice, we only use two experts -- PaliGemma,
|
| 447 |
+
# and the action expert.
|
| 448 |
+
if i == 0:
|
| 449 |
+
return name
|
| 450 |
+
return f"{name}_{i}"
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
def _gated_residual(x, y, gate):
|
| 454 |
+
assert (x is None) == (y is None)
|
| 455 |
+
if x is None:
|
| 456 |
+
return None
|
| 457 |
+
if gate is None:
|
| 458 |
+
return x + y
|
| 459 |
+
return x + y * gate
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/gemma_fast.py
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 Big Vision Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""
|
| 16 |
+
Gemma model implementation from big_vision/models/ppp/gemma.py (with small modifications for NNX compatibility)
|
| 17 |
+
Used for FAST autoregressive policies.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
import dataclasses
|
| 21 |
+
from typing import Literal, TypeAlias
|
| 22 |
+
|
| 23 |
+
import einops
|
| 24 |
+
import flax.linen as nn
|
| 25 |
+
import jax
|
| 26 |
+
import jax.numpy as jnp
|
| 27 |
+
import ml_collections
|
| 28 |
+
|
| 29 |
+
import openpi.models.lora as lora
|
| 30 |
+
import openpi.shared.array_typing as at
|
| 31 |
+
|
| 32 |
+
Variant = Literal["gemma_2b", "gemma_2b_lora"]
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def get_config(variant):
|
| 36 |
+
"""Returns config for specified gemma variant."""
|
| 37 |
+
if variant == "gemma_2b":
|
| 38 |
+
return ml_collections.ConfigDict(
|
| 39 |
+
{
|
| 40 |
+
"variant": variant,
|
| 41 |
+
"width": 2048,
|
| 42 |
+
"depth": 18,
|
| 43 |
+
"mlp_dim": 16_384,
|
| 44 |
+
"num_heads": 8,
|
| 45 |
+
"num_kv_heads": 1,
|
| 46 |
+
"head_dim": 256,
|
| 47 |
+
"norm_eps": 1e-6,
|
| 48 |
+
"vocab_size": 257_152,
|
| 49 |
+
"scan": True,
|
| 50 |
+
"remat_policy": "nothing_saveable",
|
| 51 |
+
}
|
| 52 |
+
)
|
| 53 |
+
if variant == "gemma_2b_lora":
|
| 54 |
+
return ml_collections.ConfigDict(
|
| 55 |
+
{
|
| 56 |
+
"variant": variant,
|
| 57 |
+
"width": 2048,
|
| 58 |
+
"depth": 18,
|
| 59 |
+
"mlp_dim": 16_384,
|
| 60 |
+
"num_heads": 8,
|
| 61 |
+
"num_kv_heads": 1,
|
| 62 |
+
"head_dim": 256,
|
| 63 |
+
"norm_eps": 1e-6,
|
| 64 |
+
"vocab_size": 257_152,
|
| 65 |
+
"scan": True,
|
| 66 |
+
"remat_policy": "nothing_saveable",
|
| 67 |
+
"lora_configs": {
|
| 68 |
+
"attn": lora.LoRAConfig(rank=16, alpha=16.0),
|
| 69 |
+
"ffn": lora.LoRAConfig(rank=16, alpha=16.0),
|
| 70 |
+
},
|
| 71 |
+
}
|
| 72 |
+
)
|
| 73 |
+
raise ValueError(f"Unknown variant: {variant}")
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
@at.typecheck
|
| 77 |
+
class Einsum(nn.Module):
|
| 78 |
+
shape: tuple[int, ...]
|
| 79 |
+
|
| 80 |
+
@nn.compact
|
| 81 |
+
def __call__(self, eqn, x):
|
| 82 |
+
dtype = x.dtype # original dtype, could be half-precision
|
| 83 |
+
w = self.param("w", nn.initializers.zeros_init(), self.shape).astype(dtype)
|
| 84 |
+
return jnp.einsum(eqn, x, w)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
@at.typecheck
|
| 88 |
+
class RMSNorm(nn.Module):
|
| 89 |
+
@nn.compact
|
| 90 |
+
def __call__(self, x):
|
| 91 |
+
dtype = x.dtype # original dtype, could be half-precision
|
| 92 |
+
scale = self.param("scale", nn.initializers.zeros_init(), (x.shape[-1]))
|
| 93 |
+
var = jnp.mean(jnp.square(x.astype(jnp.float32)), axis=-1, keepdims=True) # compute variance in float32
|
| 94 |
+
normed_inputs = jnp.asarray(x * jnp.reciprocal(jnp.sqrt(var + 1e-06))) # compute normalization in float32
|
| 95 |
+
normed_inputs = normed_inputs * (
|
| 96 |
+
1 + scale
|
| 97 |
+
) # scale by learned parameter in float32 (matches Flax implementation)
|
| 98 |
+
return normed_inputs.astype(dtype) # return in original dtype
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
@at.typecheck
|
| 102 |
+
class Embedder(nn.Module):
|
| 103 |
+
"""Embedder module."""
|
| 104 |
+
|
| 105 |
+
vocab_size: int
|
| 106 |
+
embed_dim: int
|
| 107 |
+
|
| 108 |
+
def setup(self):
|
| 109 |
+
self.input_embedding_table = self.param(
|
| 110 |
+
"input_embedding",
|
| 111 |
+
nn.initializers.zeros_init(),
|
| 112 |
+
(self.vocab_size, self.embed_dim),
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
def encode(self, x):
|
| 116 |
+
x = self.input_embedding_table[(x,)]
|
| 117 |
+
x *= jnp.sqrt(self.embed_dim).astype(x.dtype)
|
| 118 |
+
return x
|
| 119 |
+
|
| 120 |
+
def decode(self, x):
|
| 121 |
+
return jnp.dot(x, self.input_embedding_table.T)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
@at.typecheck
|
| 125 |
+
class Attention(nn.Module):
|
| 126 |
+
"""Attention module."""
|
| 127 |
+
|
| 128 |
+
num_heads: int
|
| 129 |
+
num_kv_heads: int
|
| 130 |
+
features: int
|
| 131 |
+
head_dim: int
|
| 132 |
+
|
| 133 |
+
cache_dtype: str | None = None
|
| 134 |
+
|
| 135 |
+
lora_config: lora.LoRAConfig | None = None
|
| 136 |
+
|
| 137 |
+
def setup(self):
|
| 138 |
+
if self.num_kv_heads == self.num_heads:
|
| 139 |
+
self.qkv_einsum = lora.Einsum(
|
| 140 |
+
shape=(3, self.num_heads, self.features, self.head_dim),
|
| 141 |
+
name="qkv_einsum",
|
| 142 |
+
init_fn=nn.initializers.lecun_normal(in_axis=-2, out_axis=-1, batch_axis=(0, 1)),
|
| 143 |
+
lora_config=self.lora_config,
|
| 144 |
+
)
|
| 145 |
+
else:
|
| 146 |
+
self.q_einsum = lora.Einsum(
|
| 147 |
+
shape=(self.num_heads, self.features, self.head_dim),
|
| 148 |
+
name="q_einsum",
|
| 149 |
+
init_fn=nn.initializers.lecun_normal(in_axis=-2, out_axis=-1, batch_axis=(0,)),
|
| 150 |
+
lora_config=self.lora_config,
|
| 151 |
+
)
|
| 152 |
+
self.kv_einsum = lora.Einsum(
|
| 153 |
+
shape=(2, self.num_kv_heads, self.features, self.head_dim),
|
| 154 |
+
name="kv_einsum",
|
| 155 |
+
init_fn=nn.initializers.lecun_normal(in_axis=-2, out_axis=-1, batch_axis=(0, 1)),
|
| 156 |
+
lora_config=self.lora_config,
|
| 157 |
+
)
|
| 158 |
+
self.attn_vec_einsum = lora.Einsum(
|
| 159 |
+
shape=(self.num_heads, self.head_dim, self.features),
|
| 160 |
+
name="attn_vec_einsum",
|
| 161 |
+
init_fn=nn.initializers.lecun_normal(in_axis=-2, out_axis=-1, batch_axis=(0,)),
|
| 162 |
+
lora_config=self.lora_config,
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
def _init_cache(self, k, v, cache_size):
|
| 166 |
+
"""Initialize KV cache"""
|
| 167 |
+
prefill_len = k.shape[1]
|
| 168 |
+
pad_width = ((0, 0), (0, cache_size - prefill_len), (0, 0), (0, 0))
|
| 169 |
+
cache_dtype = self.cache_dtype or k.dtype
|
| 170 |
+
k_cache = jnp.pad(k.astype(cache_dtype), pad_width)
|
| 171 |
+
v_cache = jnp.pad(v.astype(cache_dtype), pad_width)
|
| 172 |
+
idx = jnp.zeros((k.shape[0],), dtype=jnp.int32) + prefill_len
|
| 173 |
+
return idx, k_cache, v_cache
|
| 174 |
+
|
| 175 |
+
def _update_cache(self, k, v, idx, k_cache, v_cache):
|
| 176 |
+
"""Update KV cache with new values"""
|
| 177 |
+
assert k.shape[1] == 1, "Only support kv-cache updates of length 1"
|
| 178 |
+
indices = (0, idx[0], 0, 0)
|
| 179 |
+
cache_dtype = self.cache_dtype or k.dtype
|
| 180 |
+
k_new = jax.lax.dynamic_update_slice(k_cache, k.astype(cache_dtype), indices)
|
| 181 |
+
v_new = jax.lax.dynamic_update_slice(v_cache, v.astype(cache_dtype), indices)
|
| 182 |
+
idx_new = idx + 1
|
| 183 |
+
return idx_new, k_new, v_new
|
| 184 |
+
|
| 185 |
+
@nn.compact
|
| 186 |
+
def __call__(self, x, positions, attn_mask, kv_cache, decode, deterministic=True): # noqa: FBT002
|
| 187 |
+
dtype = x.dtype # original dtype, could be half-precision
|
| 188 |
+
if self.num_kv_heads == self.num_heads:
|
| 189 |
+
q, k, v = self.qkv_einsum("BSD,3KDH->3BSKH", x)
|
| 190 |
+
else:
|
| 191 |
+
q = self.q_einsum("BTD,NDH->BTNH", x)
|
| 192 |
+
k, v = self.kv_einsum("BSD,2KDH->2BSKH", x)
|
| 193 |
+
|
| 194 |
+
q = _apply_rope(q, positions=positions) # promotes to float32
|
| 195 |
+
q *= self.head_dim**-0.5
|
| 196 |
+
|
| 197 |
+
k = _apply_rope(k, positions=positions) # promotes to float32
|
| 198 |
+
|
| 199 |
+
if kv_cache is None:
|
| 200 |
+
idx, k_cache, v_cache = self._init_cache(k, v, attn_mask.shape[-1])
|
| 201 |
+
else:
|
| 202 |
+
idx, k_cache, v_cache = kv_cache
|
| 203 |
+
idx, k_cache, v_cache = self._update_cache(k, v, idx, k_cache, v_cache)
|
| 204 |
+
|
| 205 |
+
k, v = k_cache, v_cache
|
| 206 |
+
kv_cache = (idx, k_cache, v_cache)
|
| 207 |
+
|
| 208 |
+
q = einops.rearrange(q, "B T (K G) H -> B T K G H", K=self.num_kv_heads)
|
| 209 |
+
logits = jnp.einsum("BTKGH,BSKH->BKGTS", q, k, preferred_element_type=jnp.float32)
|
| 210 |
+
|
| 211 |
+
if attn_mask.shape != (q.shape[0], 1, q.shape[1], k.shape[1]):
|
| 212 |
+
raise ValueError(
|
| 213 |
+
f"Attention mask with shape {attn_mask.shape} but shapes for q and k are: {q.shape} and {k.shape}"
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
# big_neg = jnp.finfo(logits.dtype).min
|
| 217 |
+
big_neg = -2.3819763e38 # See gemma/modules.py
|
| 218 |
+
masked_logits = jnp.where(attn_mask[:, :, None, :, :], logits, big_neg)
|
| 219 |
+
|
| 220 |
+
probs = jax.nn.softmax(masked_logits, axis=-1).astype(dtype)
|
| 221 |
+
|
| 222 |
+
encoded = jnp.einsum("BKGTS,BSKH->BTKGH", probs, v)
|
| 223 |
+
encoded = einops.rearrange(encoded, "B T K G H -> B T (K G) H")
|
| 224 |
+
return self.attn_vec_einsum("BTNH,NHD->BTD", encoded), kv_cache
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
@at.typecheck
|
| 228 |
+
class Block(nn.Module):
|
| 229 |
+
"""Transformer block."""
|
| 230 |
+
|
| 231 |
+
num_heads: int
|
| 232 |
+
num_kv_heads: int
|
| 233 |
+
embed_dim: int
|
| 234 |
+
head_dim: int
|
| 235 |
+
hidden_dim: int
|
| 236 |
+
|
| 237 |
+
dropout: float = 0.0
|
| 238 |
+
dropout_bdims: tuple[int, ...] = ()
|
| 239 |
+
cache_dtype: str | None = None
|
| 240 |
+
lora_configs: ml_collections.ConfigDict = dataclasses.field(default_factory=ml_collections.ConfigDict)
|
| 241 |
+
|
| 242 |
+
def setup(self):
|
| 243 |
+
self.pre_attention_norm = RMSNorm()
|
| 244 |
+
self.attn = Attention(
|
| 245 |
+
num_heads=self.num_heads,
|
| 246 |
+
num_kv_heads=self.num_kv_heads,
|
| 247 |
+
features=self.embed_dim,
|
| 248 |
+
head_dim=self.head_dim,
|
| 249 |
+
cache_dtype=self.cache_dtype,
|
| 250 |
+
lora_config=self.lora_configs.get("attn"),
|
| 251 |
+
)
|
| 252 |
+
self.pre_ffw_norm = RMSNorm()
|
| 253 |
+
self.mlp = lora.FeedForward(
|
| 254 |
+
features=self.embed_dim, hidden_dim=self.hidden_dim, name="mlp", lora_config=self.lora_configs.get("ffn")
|
| 255 |
+
)
|
| 256 |
+
if self.dropout:
|
| 257 |
+
self.drop = nn.Dropout(self.dropout, self.dropout_bdims)
|
| 258 |
+
else:
|
| 259 |
+
self.drop = lambda x, _: x
|
| 260 |
+
|
| 261 |
+
def __call__(self, x, kv_cache, positions, attn_mask, decode, deterministic=True): # noqa: FBT002
|
| 262 |
+
x = nn.with_logical_constraint(x, ("act_batch", "act_len", "act_emb"))
|
| 263 |
+
inputs_normalized = self.pre_attention_norm(x)
|
| 264 |
+
attn_output, kv_cache = self.attn(inputs_normalized, positions, attn_mask, kv_cache, decode, deterministic)
|
| 265 |
+
attn_output = self.drop(attn_output, deterministic)
|
| 266 |
+
attn_output += x
|
| 267 |
+
residual = attn_output
|
| 268 |
+
attn_output = self.pre_ffw_norm(attn_output)
|
| 269 |
+
outputs = self.mlp(attn_output)
|
| 270 |
+
outputs = self.drop(outputs, deterministic)
|
| 271 |
+
outputs = residual + outputs
|
| 272 |
+
return outputs, kv_cache
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
KVCache: TypeAlias = tuple[at.Int[at.Array, " b"], at.Float[at.Array, "b _t _k _h"], at.Float[at.Array, "b _t _v _h"]]
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
@at.typecheck
|
| 279 |
+
class Module(nn.Module):
|
| 280 |
+
"""gemma model."""
|
| 281 |
+
|
| 282 |
+
variant: str
|
| 283 |
+
|
| 284 |
+
width: int
|
| 285 |
+
depth: int
|
| 286 |
+
mlp_dim: int
|
| 287 |
+
num_heads: int
|
| 288 |
+
num_kv_heads: int
|
| 289 |
+
head_dim: int
|
| 290 |
+
norm_eps: float
|
| 291 |
+
vocab_size: int
|
| 292 |
+
embed_dtype: str
|
| 293 |
+
|
| 294 |
+
dropout: float = 0.0
|
| 295 |
+
dropout_bdims: tuple[int, ...] = () # Every float is dropped independently.
|
| 296 |
+
cache_dtype: str | None = None
|
| 297 |
+
|
| 298 |
+
scan: bool = False
|
| 299 |
+
remat_policy: str = "none"
|
| 300 |
+
lora_configs: ml_collections.ConfigDict = dataclasses.field(default_factory=ml_collections.ConfigDict)
|
| 301 |
+
|
| 302 |
+
@nn.compact
|
| 303 |
+
def __call__(
|
| 304 |
+
self,
|
| 305 |
+
tokens=None,
|
| 306 |
+
embedded_prefix=None,
|
| 307 |
+
embed_only=False, # noqa: FBT002
|
| 308 |
+
pre_logits=None,
|
| 309 |
+
positions=None,
|
| 310 |
+
mask=None,
|
| 311 |
+
decode=False, # noqa: FBT002
|
| 312 |
+
kv_cache=None,
|
| 313 |
+
deterministic=True, # noqa: FBT002
|
| 314 |
+
return_prelogits=False, # noqa: FBT002
|
| 315 |
+
):
|
| 316 |
+
"""Embed only, or complete forward pass.
|
| 317 |
+
|
| 318 |
+
Args:
|
| 319 |
+
tokens: Embedded, then and appended to `embedded_prefix`. Can be None.
|
| 320 |
+
embedded_prefix: Optional prefix that is already embedded.
|
| 321 |
+
embed_only: Whether to compute embeddings only.
|
| 322 |
+
pre_logits: If present computes logits from pre_logits and returns.
|
| 323 |
+
positions: Optional `[B, T]` allows to specify the absolute position of
|
| 324 |
+
the tokens.
|
| 325 |
+
mask: Optional attention mask `[B, T, S]`.
|
| 326 |
+
decode: Whether to use kv-cache. Caller must pass masks and positions.
|
| 327 |
+
deterministic: Forwarded to all dropout layers.
|
| 328 |
+
return_prelogits: Whether to return the pre-logits.
|
| 329 |
+
|
| 330 |
+
Returns:
|
| 331 |
+
If `embed_only=False`, then `(logits, out)` will be returned.
|
| 332 |
+
If `embed_only=True`, then the embeddings will be returned.
|
| 333 |
+
If `return_prelogits=True`, then the pre-logits will be returned.
|
| 334 |
+
"""
|
| 335 |
+
out = {}
|
| 336 |
+
|
| 337 |
+
embedder = Embedder(vocab_size=self.vocab_size, embed_dim=self.width, name="embedder")
|
| 338 |
+
|
| 339 |
+
if pre_logits is not None:
|
| 340 |
+
x = out["pre_logits"] = pre_logits
|
| 341 |
+
logits = out["logits"] = embedder.decode(x)
|
| 342 |
+
return logits, out
|
| 343 |
+
|
| 344 |
+
x = []
|
| 345 |
+
if embedded_prefix is not None:
|
| 346 |
+
x.append(embedded_prefix)
|
| 347 |
+
if tokens is not None:
|
| 348 |
+
x.append(embedder.encode(tokens))
|
| 349 |
+
|
| 350 |
+
x = jnp.concatenate(x, axis=-2)
|
| 351 |
+
x = x.astype(self.embed_dtype)
|
| 352 |
+
batch_size, seq_len, width = x.shape
|
| 353 |
+
|
| 354 |
+
if embed_only:
|
| 355 |
+
return x
|
| 356 |
+
|
| 357 |
+
if decode:
|
| 358 |
+
assert positions is not None and mask is not None, ( # noqa: PT018
|
| 359 |
+
"Must explicitly pass positions and mask for decoding."
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
if positions is None:
|
| 363 |
+
positions = jnp.arange(seq_len).astype(jnp.int32)[None, :]
|
| 364 |
+
assert positions.shape[1] == x.shape[1], (positions.shape, x.shape)
|
| 365 |
+
|
| 366 |
+
if mask is None:
|
| 367 |
+
mask = nn.attention.make_causal_mask(jnp.ones([batch_size, seq_len]))
|
| 368 |
+
if mask.ndim == 3:
|
| 369 |
+
mask = mask[:, None, :, :]
|
| 370 |
+
cache_size = max(seq_len, mask.shape[-1])
|
| 371 |
+
assert mask.shape == (batch_size, 1, seq_len, cache_size), mask.shape
|
| 372 |
+
|
| 373 |
+
if self.remat_policy == "none":
|
| 374 |
+
block_cls = Block
|
| 375 |
+
else:
|
| 376 |
+
block_cls = nn.remat(
|
| 377 |
+
Block,
|
| 378 |
+
prevent_cse=not self.scan,
|
| 379 |
+
static_argnums=(5, 6), # 0=self, 5=decode, 6=deterministic
|
| 380 |
+
policy=getattr(jax.checkpoint_policies, self.remat_policy),
|
| 381 |
+
)
|
| 382 |
+
|
| 383 |
+
block_kw = {
|
| 384 |
+
"num_heads": self.num_heads,
|
| 385 |
+
"head_dim": self.head_dim,
|
| 386 |
+
"num_kv_heads": self.num_kv_heads,
|
| 387 |
+
"embed_dim": width,
|
| 388 |
+
"hidden_dim": self.mlp_dim,
|
| 389 |
+
"dropout": self.dropout,
|
| 390 |
+
"dropout_bdims": self.dropout_bdims,
|
| 391 |
+
"cache_dtype": self.cache_dtype,
|
| 392 |
+
"lora_configs": self.lora_configs,
|
| 393 |
+
}
|
| 394 |
+
layers = self.scope.push("layers")
|
| 395 |
+
blocks = [
|
| 396 |
+
nn.scan(
|
| 397 |
+
block_cls,
|
| 398 |
+
variable_axes={"params": 0},
|
| 399 |
+
split_rngs={"params": True, "dropout": True},
|
| 400 |
+
in_axes=(0, nn.broadcast, nn.broadcast, nn.broadcast, nn.broadcast), # 0=kv_cache, 1=positions, 2=mask
|
| 401 |
+
length=self.depth,
|
| 402 |
+
)(parent=layers, **block_kw)
|
| 403 |
+
]
|
| 404 |
+
for block in blocks:
|
| 405 |
+
x, kv_cache = block(x, kv_cache, positions, mask, decode, deterministic)
|
| 406 |
+
|
| 407 |
+
assert x.dtype == jnp.dtype(self.embed_dtype) # Sanity check.
|
| 408 |
+
out["encoded"] = x
|
| 409 |
+
|
| 410 |
+
x = RMSNorm(name="final_norm")(x)
|
| 411 |
+
out["pre_logits"] = x
|
| 412 |
+
if return_prelogits:
|
| 413 |
+
return x, kv_cache, out
|
| 414 |
+
|
| 415 |
+
x = embedder.decode(x)
|
| 416 |
+
out["logits"] = x
|
| 417 |
+
|
| 418 |
+
return x, kv_cache, out
|
| 419 |
+
|
| 420 |
+
def init(self):
|
| 421 |
+
"""Convenience method for initializing all parameters, necessary due to the quirks of linen."""
|
| 422 |
+
self(jnp.zeros((1, 1), dtype=jnp.int32))
|
| 423 |
+
|
| 424 |
+
|
| 425 |
+
def _apply_rope(x, *, positions, max_wavelength=10_000):
|
| 426 |
+
"""Applies RoPE positions [B, L] to x [B, L, H, D]."""
|
| 427 |
+
freq_exponents = (2.0 / x.shape[-1]) * jnp.arange(x.shape[-1] // 2, dtype=jnp.float32)
|
| 428 |
+
timescale = max_wavelength**freq_exponents
|
| 429 |
+
radians = positions[..., None] / timescale[None, None, :]
|
| 430 |
+
radians = radians[..., None, :]
|
| 431 |
+
assert radians.dtype == jnp.float32
|
| 432 |
+
# radians.shape = [...,L,1,d=D/2]
|
| 433 |
+
sin, cos = jnp.sin(radians), jnp.cos(radians)
|
| 434 |
+
x1, x2 = jnp.split(x, 2, axis=-1)
|
| 435 |
+
res = jnp.concatenate([x1 * cos - x2 * sin, x2 * cos + x1 * sin], axis=-1)
|
| 436 |
+
assert res.dtype == jnp.float32
|
| 437 |
+
return res
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/lora.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import re
|
| 3 |
+
|
| 4 |
+
import flax.linen as nn
|
| 5 |
+
import flax.struct as struct
|
| 6 |
+
import jax.numpy as jnp
|
| 7 |
+
|
| 8 |
+
import openpi.shared.array_typing as at
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@struct.dataclass
|
| 12 |
+
class LoRAConfig:
|
| 13 |
+
"""Configuration for LoRA."""
|
| 14 |
+
|
| 15 |
+
# LoRA rank.
|
| 16 |
+
rank: int
|
| 17 |
+
# LoRA scaling factor.
|
| 18 |
+
alpha: float = 1.0
|
| 19 |
+
# Initialization function for LoRA parameters.
|
| 20 |
+
init_fn: nn.initializers.Initializer = nn.initializers.normal(stddev=0.01)
|
| 21 |
+
# Enable rank-stabilized LoRA: https://arxiv.org/pdf/2312.03732
|
| 22 |
+
rslora: bool = False
|
| 23 |
+
# Axes in the weight to apply LoRA to. Should typically be the last two axes.
|
| 24 |
+
axes: tuple[int, int] = (-2, -1)
|
| 25 |
+
# Axis label which is used by LoRA in einsum equations. Must not be present in the original equation.
|
| 26 |
+
label: str = "L"
|
| 27 |
+
|
| 28 |
+
@property
|
| 29 |
+
def scaling_value(self) -> float:
|
| 30 |
+
return self.alpha / math.sqrt(self.rank) if self.rslora else self.alpha / self.rank
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class Einsum(nn.Module):
|
| 34 |
+
"""Einsum with LoRA support. Can be used as a drop-in replacement for the Gemma Einsum."""
|
| 35 |
+
|
| 36 |
+
# Shape of the weight.
|
| 37 |
+
shape: tuple[int, ...]
|
| 38 |
+
# Initialization function for the weight.
|
| 39 |
+
init_fn: nn.initializers.Initializer = nn.initializers.zeros
|
| 40 |
+
# If not None, apply LoRA to the weight.
|
| 41 |
+
lora_config: LoRAConfig | None = None
|
| 42 |
+
|
| 43 |
+
def setup(self):
|
| 44 |
+
self.w = self.param("w", self.init_fn, self.shape)
|
| 45 |
+
|
| 46 |
+
if config := self.lora_config:
|
| 47 |
+
# Setup LoRA parameters.
|
| 48 |
+
shape_a, shape_b = list(self.shape), list(self.shape)
|
| 49 |
+
shape_a[config.axes[1]] = config.rank
|
| 50 |
+
shape_b[config.axes[0]] = config.rank
|
| 51 |
+
self.w_a = self.param("lora_a", config.init_fn, shape_a)
|
| 52 |
+
self.w_b = self.param("lora_b", config.init_fn, shape_b)
|
| 53 |
+
|
| 54 |
+
@nn.compact
|
| 55 |
+
def __call__(self, eqn: str, x):
|
| 56 |
+
dtype = x.dtype # original dtype, could be half-precision
|
| 57 |
+
result = jnp.einsum(eqn, x, self.w.astype(dtype))
|
| 58 |
+
|
| 59 |
+
if config := self.lora_config:
|
| 60 |
+
eqn_a, eqn_b = self._make_lora_eqns(eqn)
|
| 61 |
+
lora = jnp.einsum(eqn_a, x, self.w_a.astype(dtype))
|
| 62 |
+
lora = jnp.einsum(eqn_b, lora, self.w_b.astype(dtype))
|
| 63 |
+
result = result + lora * config.scaling_value
|
| 64 |
+
|
| 65 |
+
return result
|
| 66 |
+
|
| 67 |
+
def _make_lora_eqns(self, eqn: str) -> tuple[str, str]:
|
| 68 |
+
if "L" in eqn:
|
| 69 |
+
raise ValueError(f"L already in eqn: {eqn}")
|
| 70 |
+
if not (m := re.match("(.*),(.*)->(.*)", eqn)):
|
| 71 |
+
raise ValueError(f"Unsupported einsum eqn: {eqn}")
|
| 72 |
+
lhs, rhs, out = m.groups()
|
| 73 |
+
|
| 74 |
+
assert self.lora_config is not None
|
| 75 |
+
a_label, b_label = (rhs[x] for x in self.lora_config.axes)
|
| 76 |
+
label = self.lora_config.label
|
| 77 |
+
|
| 78 |
+
a_rhs = rhs.replace(b_label, label)
|
| 79 |
+
a_out = out.replace(b_label, label)
|
| 80 |
+
eqn_a = f"{lhs},{a_rhs}->{a_out}"
|
| 81 |
+
|
| 82 |
+
b_rhs = rhs.replace(a_label, label)
|
| 83 |
+
eqn_b = f"{a_out},{b_rhs}->{out}"
|
| 84 |
+
|
| 85 |
+
return eqn_a, eqn_b
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class FeedForward(nn.Module):
|
| 89 |
+
"""Feed forward module."""
|
| 90 |
+
|
| 91 |
+
features: int
|
| 92 |
+
hidden_dim: int
|
| 93 |
+
# If not None, apply LoRA to the weight.
|
| 94 |
+
lora_config: LoRAConfig | None = None
|
| 95 |
+
|
| 96 |
+
def setup(self):
|
| 97 |
+
self.w_gating = self.param(
|
| 98 |
+
"gating_einsum",
|
| 99 |
+
nn.initializers.lecun_normal(in_axis=-2, out_axis=-1, batch_axis=(0,)),
|
| 100 |
+
(2, self.features, self.hidden_dim),
|
| 101 |
+
)
|
| 102 |
+
self.w_linear = self.param(
|
| 103 |
+
"linear",
|
| 104 |
+
nn.initializers.lecun_normal(in_axis=-2, out_axis=-1),
|
| 105 |
+
(self.hidden_dim, self.features),
|
| 106 |
+
)
|
| 107 |
+
self.w_gating_lora = None
|
| 108 |
+
self.w_linear_lora = None
|
| 109 |
+
if self.lora_config:
|
| 110 |
+
# Setup LoRA parameters.
|
| 111 |
+
# TODO: follow up with a simplified init_fn api.
|
| 112 |
+
self.w_gating_lora = (
|
| 113 |
+
self.param("gating_einsum_lora_a", self.lora_config.init_fn, (2, self.features, self.lora_config.rank)),
|
| 114 |
+
self.param(
|
| 115 |
+
"gating_einsum_lora_b", self.lora_config.init_fn, (2, self.lora_config.rank, self.hidden_dim)
|
| 116 |
+
),
|
| 117 |
+
)
|
| 118 |
+
self.w_linear_lora = (
|
| 119 |
+
self.param("linear_lora_a", self.lora_config.init_fn, (self.hidden_dim, self.lora_config.rank)),
|
| 120 |
+
self.param("linear_lora_b", self.lora_config.init_fn, (self.lora_config.rank, self.features)),
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
@nn.compact
|
| 124 |
+
def __call__(self, x):
|
| 125 |
+
dtype = x.dtype # original dtype, could be half-precision
|
| 126 |
+
ff_gate = self._dot(
|
| 127 |
+
x,
|
| 128 |
+
self.w_gating[0],
|
| 129 |
+
None if self.w_gating_lora is None else (self.w_gating_lora[0][0], self.w_gating_lora[1][0]),
|
| 130 |
+
)
|
| 131 |
+
gate_value = nn.gelu(ff_gate)
|
| 132 |
+
|
| 133 |
+
ff1 = self._dot(
|
| 134 |
+
x,
|
| 135 |
+
self.w_gating[1],
|
| 136 |
+
None if self.w_gating_lora is None else (self.w_gating_lora[0][1], self.w_gating_lora[1][1]),
|
| 137 |
+
)
|
| 138 |
+
activations = gate_value * ff1
|
| 139 |
+
|
| 140 |
+
outputs = self._dot(activations, self.w_linear, self.w_linear_lora)
|
| 141 |
+
assert outputs.dtype == dtype
|
| 142 |
+
return outputs
|
| 143 |
+
|
| 144 |
+
def _dot(self, x: at.Array, w: at.Array, lora_weights: tuple[at.Array, at.Array] | None) -> at.Array:
|
| 145 |
+
base = jnp.dot(x, w.astype(x.dtype))
|
| 146 |
+
if lora_weights is None:
|
| 147 |
+
return base
|
| 148 |
+
return base + jnp.dot(jnp.dot(x, lora_weights[0].astype(x.dtype)), lora_weights[1].astype(x.dtype))
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/lora_test.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import flax.linen as nn
|
| 2 |
+
import jax
|
| 3 |
+
import jax.numpy as jnp
|
| 4 |
+
|
| 5 |
+
import openpi.models.lora as lora
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def test_lora_einsum_params_shape():
|
| 9 |
+
shape = (3, 8, 32, 4) # (3KDH)
|
| 10 |
+
einsum = lora.Einsum(shape)
|
| 11 |
+
lora0 = lora.Einsum(shape, lora_config=lora.LoRAConfig(rank=2))
|
| 12 |
+
lora1 = lora.Einsum(shape, lora_config=lora.LoRAConfig(rank=2, axes=(1, 2)))
|
| 13 |
+
|
| 14 |
+
key = jax.random.key(0)
|
| 15 |
+
x = jax.random.normal(key, (8, 64, 32)) # (BSD)
|
| 16 |
+
eqn = "BSD,3KDH->3BSKH"
|
| 17 |
+
|
| 18 |
+
# Ensure that lora parameters are not initialized when LoRA is not used.
|
| 19 |
+
params = einsum.init(key, eqn, x)
|
| 20 |
+
assert "lora_a" not in params["params"]
|
| 21 |
+
assert "lora_b" not in params["params"]
|
| 22 |
+
|
| 23 |
+
# Check that default axes work.
|
| 24 |
+
params_lora0 = lora0.init(key, eqn, x)
|
| 25 |
+
assert params_lora0["params"]["lora_a"].shape == (3, 8, 32, 2)
|
| 26 |
+
assert params_lora0["params"]["lora_b"].shape == (3, 8, 2, 4)
|
| 27 |
+
|
| 28 |
+
# Check that user provided axes work.
|
| 29 |
+
params_lora1 = lora1.init(key, eqn, x)
|
| 30 |
+
assert params_lora1["params"]["lora_a"].shape == (3, 8, 2, 4)
|
| 31 |
+
assert params_lora1["params"]["lora_b"].shape == (3, 2, 32, 4)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def test_lora_einsum_same_output():
|
| 35 |
+
shape = (3, 8, 32, 4) # (3KDH)
|
| 36 |
+
einsum = lora.Einsum(shape)
|
| 37 |
+
einsum_lora = lora.Einsum(shape, lora_config=lora.LoRAConfig(rank=2, init_fn=nn.initializers.zeros))
|
| 38 |
+
|
| 39 |
+
key = jax.random.key(0)
|
| 40 |
+
x = jax.random.normal(key, (8, 64, 32)) # (BSD)
|
| 41 |
+
eqn = "BSD,3KDH->3BSKH"
|
| 42 |
+
|
| 43 |
+
params = einsum.init(key, eqn, x)
|
| 44 |
+
output = einsum.apply(params, eqn, x)
|
| 45 |
+
|
| 46 |
+
params_lora = einsum_lora.init(key, eqn, x)
|
| 47 |
+
output_lora = einsum_lora.apply(params_lora, eqn, x)
|
| 48 |
+
|
| 49 |
+
# Results are the same since the LoRA parameters are initialized to zeros.
|
| 50 |
+
assert jnp.allclose(output, output_lora)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def test_lora_ffn_params_shape():
|
| 54 |
+
ffn = lora.FeedForward(features=8, hidden_dim=32)
|
| 55 |
+
ffn_lora = lora.FeedForward(
|
| 56 |
+
features=8,
|
| 57 |
+
hidden_dim=32,
|
| 58 |
+
lora_config=lora.LoRAConfig(rank=2),
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
key = jax.random.key(0)
|
| 62 |
+
x = jax.random.normal(key, (2, 8))
|
| 63 |
+
|
| 64 |
+
params = ffn.init(key, x)
|
| 65 |
+
assert params["params"]["gating_einsum"].shape == (2, 8, 32)
|
| 66 |
+
assert params["params"]["linear"].shape == (32, 8)
|
| 67 |
+
|
| 68 |
+
params_lora = ffn_lora.init(key, x)
|
| 69 |
+
assert params_lora["params"]["gating_einsum"].shape == (2, 8, 32)
|
| 70 |
+
assert params_lora["params"]["linear"].shape == (32, 8)
|
| 71 |
+
assert params_lora["params"]["gating_einsum_lora_a"].shape == (2, 8, 2)
|
| 72 |
+
assert params_lora["params"]["gating_einsum_lora_b"].shape == (2, 2, 32)
|
| 73 |
+
assert params_lora["params"]["linear_lora_a"].shape == (32, 2)
|
| 74 |
+
assert params_lora["params"]["linear_lora_b"].shape == (2, 8)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def test_lora_ffn_same_output():
|
| 78 |
+
ffn = lora.FeedForward(features=8, hidden_dim=32)
|
| 79 |
+
ffn_lora = lora.FeedForward(
|
| 80 |
+
features=8,
|
| 81 |
+
hidden_dim=32,
|
| 82 |
+
lora_config=lora.LoRAConfig(rank=2, init_fn=nn.initializers.zeros),
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
key = jax.random.key(0)
|
| 86 |
+
x = jax.random.normal(key, (2, 8))
|
| 87 |
+
|
| 88 |
+
params = ffn.init(key, x)
|
| 89 |
+
output = ffn.apply(params, x)
|
| 90 |
+
|
| 91 |
+
params_lora = ffn_lora.init(key, x)
|
| 92 |
+
output_lora = ffn_lora.apply(params_lora, x)
|
| 93 |
+
|
| 94 |
+
assert jnp.allclose(output, output_lora)
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/model.py
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
from collections.abc import Sequence
|
| 3 |
+
import dataclasses
|
| 4 |
+
import enum
|
| 5 |
+
import logging
|
| 6 |
+
import pathlib
|
| 7 |
+
from typing import Generic, TypeVar
|
| 8 |
+
|
| 9 |
+
import augmax
|
| 10 |
+
from flax import nnx
|
| 11 |
+
from flax import struct
|
| 12 |
+
from flax import traverse_util
|
| 13 |
+
import jax
|
| 14 |
+
import jax.numpy as jnp
|
| 15 |
+
import numpy as np
|
| 16 |
+
import orbax.checkpoint as ocp
|
| 17 |
+
import safetensors
|
| 18 |
+
import torch
|
| 19 |
+
|
| 20 |
+
from openpi.models_pytorch import pi0_pytorch
|
| 21 |
+
from openpi.shared import image_tools
|
| 22 |
+
import openpi.shared.array_typing as at
|
| 23 |
+
|
| 24 |
+
logger = logging.getLogger("openpi")
|
| 25 |
+
|
| 26 |
+
# Type variable for array types (JAX arrays, PyTorch tensors, or numpy arrays)
|
| 27 |
+
ArrayT = TypeVar("ArrayT", bound=jax.Array | torch.Tensor | np.ndarray)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class ModelType(enum.Enum):
|
| 31 |
+
"""Supported model types."""
|
| 32 |
+
|
| 33 |
+
PI0 = "pi0"
|
| 34 |
+
PI0_FAST = "pi0_fast"
|
| 35 |
+
PI05 = "pi05"
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# The model always expects these images
|
| 39 |
+
IMAGE_KEYS = (
|
| 40 |
+
"base_0_rgb",
|
| 41 |
+
"left_wrist_0_rgb",
|
| 42 |
+
"right_wrist_0_rgb",
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
# This may need change if we release a small model.
|
| 47 |
+
IMAGE_RESOLUTION = (224, 224)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
# Data format
|
| 51 |
+
#
|
| 52 |
+
# Data transforms produce the model input as a nested dictionary which is later converted
|
| 53 |
+
# into `Obesrvation` and `Actions` objects. See below.
|
| 54 |
+
#
|
| 55 |
+
# In the dictory form, this data should look like:
|
| 56 |
+
# {
|
| 57 |
+
# # Observation data.
|
| 58 |
+
# "image": {
|
| 59 |
+
# "base_0_rgb": (float32|uint8)[*b, h, w, 3], # RGB image in [-1, 1] or [0, 255]
|
| 60 |
+
# ... # Additional camera views
|
| 61 |
+
# },
|
| 62 |
+
# "image_mask": {
|
| 63 |
+
# "base_0_rgb": bool[*b], # True if image is valid
|
| 64 |
+
# ... # Masks for additional views
|
| 65 |
+
# },
|
| 66 |
+
# "state": float32[*b, s], # Low-dimensional robot state
|
| 67 |
+
# "tokenized_prompt": int32[*b, l], # Optional, tokenized language prompt
|
| 68 |
+
# "tokenized_prompt_mask": bool[*b, l], # Optional, mask for tokenized prompt
|
| 69 |
+
# "token_ar_mask": int32[*b, l], # Optional, autoregressive mask for FAST model
|
| 70 |
+
# "token_loss_mask": bool[*b, l], # Optional, loss mask for FAST model
|
| 71 |
+
#
|
| 72 |
+
# # Actions data.
|
| 73 |
+
# "actions": float32[*b ah ad]
|
| 74 |
+
# }
|
| 75 |
+
# where:
|
| 76 |
+
# *b = batch dimensions
|
| 77 |
+
# h,w = image height/width
|
| 78 |
+
# s = state dimension
|
| 79 |
+
# l = sequence length
|
| 80 |
+
#
|
| 81 |
+
@at.typecheck
|
| 82 |
+
@struct.dataclass
|
| 83 |
+
class Observation(Generic[ArrayT]):
|
| 84 |
+
"""Holds observations, i.e., inputs to the model.
|
| 85 |
+
|
| 86 |
+
See `Observation.from_dict` to see the expected dictionary form. This is the format
|
| 87 |
+
that should be produced by the data transforms.
|
| 88 |
+
"""
|
| 89 |
+
|
| 90 |
+
# Images, in [-1, 1] float32.
|
| 91 |
+
images: dict[str, at.Float[ArrayT, "*b h w c"]]
|
| 92 |
+
# Image masks, with same keys as images.
|
| 93 |
+
image_masks: dict[str, at.Bool[ArrayT, "*b"]]
|
| 94 |
+
# Low-dimensional robot state.
|
| 95 |
+
state: at.Float[ArrayT, "*b s"]
|
| 96 |
+
|
| 97 |
+
# Tokenized prompt.
|
| 98 |
+
tokenized_prompt: at.Int[ArrayT, "*b l"] | None = None
|
| 99 |
+
# Tokenized prompt mask.
|
| 100 |
+
tokenized_prompt_mask: at.Bool[ArrayT, "*b l"] | None = None
|
| 101 |
+
|
| 102 |
+
# pi0-fast model specific fields.
|
| 103 |
+
|
| 104 |
+
# Token auto-regressive mask (for FAST autoregressive model).
|
| 105 |
+
token_ar_mask: at.Int[ArrayT, "*b l"] | None = None
|
| 106 |
+
# Token loss mask (for FAST autoregressive model).
|
| 107 |
+
token_loss_mask: at.Bool[ArrayT, "*b l"] | None = None
|
| 108 |
+
|
| 109 |
+
@classmethod
|
| 110 |
+
def from_dict(cls, data: at.PyTree[ArrayT]) -> "Observation[ArrayT]":
|
| 111 |
+
"""This method defines the mapping between unstructured data (i.e., nested dict) to the structured Observation format."""
|
| 112 |
+
# Ensure that tokenized_prompt and tokenized_prompt_mask are provided together.
|
| 113 |
+
if ("tokenized_prompt" in data) != ("tokenized_prompt_mask" in data):
|
| 114 |
+
raise ValueError("tokenized_prompt and tokenized_prompt_mask must be provided together.")
|
| 115 |
+
# If images are uint8, convert them to [-1, 1] float32.
|
| 116 |
+
for key in data["image"]:
|
| 117 |
+
if data["image"][key].dtype == np.uint8:
|
| 118 |
+
data["image"][key] = data["image"][key].astype(np.float32) / 255.0 * 2.0 - 1.0
|
| 119 |
+
elif hasattr(data["image"][key], "dtype") and data["image"][key].dtype == torch.uint8:
|
| 120 |
+
data["image"][key] = data["image"][key].to(torch.float32).permute(0, 3, 1, 2) / 255.0 * 2.0 - 1.0
|
| 121 |
+
return cls(
|
| 122 |
+
images=data["image"],
|
| 123 |
+
image_masks=data["image_mask"],
|
| 124 |
+
state=data["state"],
|
| 125 |
+
tokenized_prompt=data.get("tokenized_prompt"),
|
| 126 |
+
tokenized_prompt_mask=data.get("tokenized_prompt_mask"),
|
| 127 |
+
token_ar_mask=data.get("token_ar_mask"),
|
| 128 |
+
token_loss_mask=data.get("token_loss_mask"),
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
def to_dict(self) -> at.PyTree[ArrayT]:
|
| 132 |
+
"""Convert the Observation to a nested dict."""
|
| 133 |
+
result = dataclasses.asdict(self)
|
| 134 |
+
result["image"] = result.pop("images")
|
| 135 |
+
result["image_mask"] = result.pop("image_masks")
|
| 136 |
+
return result
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
# Defines the format of the actions. This field is included as "actions" inside the dictionary
|
| 140 |
+
# produced by the data transforms.
|
| 141 |
+
Actions = at.Float[ArrayT, "*b ah ad"]
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def preprocess_observation(
|
| 145 |
+
rng: at.KeyArrayLike | None,
|
| 146 |
+
observation: Observation,
|
| 147 |
+
*,
|
| 148 |
+
train: bool = False,
|
| 149 |
+
image_keys: Sequence[str] = IMAGE_KEYS,
|
| 150 |
+
image_resolution: tuple[int, int] = IMAGE_RESOLUTION,
|
| 151 |
+
) -> Observation:
|
| 152 |
+
"""Preprocess the observations by performing image augmentations (if train=True), resizing (if necessary), and
|
| 153 |
+
filling in a default image mask (if necessary).
|
| 154 |
+
"""
|
| 155 |
+
|
| 156 |
+
if not set(image_keys).issubset(observation.images):
|
| 157 |
+
raise ValueError(f"images dict missing keys: expected {image_keys}, got {list(observation.images)}")
|
| 158 |
+
|
| 159 |
+
batch_shape = observation.state.shape[:-1]
|
| 160 |
+
|
| 161 |
+
out_images = {}
|
| 162 |
+
for key in image_keys:
|
| 163 |
+
image = observation.images[key]
|
| 164 |
+
if image.shape[1:3] != image_resolution:
|
| 165 |
+
logger.info(f"Resizing image {key} from {image.shape[1:3]} to {image_resolution}")
|
| 166 |
+
image = image_tools.resize_with_pad(image, *image_resolution)
|
| 167 |
+
|
| 168 |
+
if train:
|
| 169 |
+
# Convert from [-1, 1] to [0, 1] for augmax.
|
| 170 |
+
image = image / 2.0 + 0.5
|
| 171 |
+
|
| 172 |
+
transforms = []
|
| 173 |
+
if "wrist" not in key:
|
| 174 |
+
height, width = image.shape[1:3]
|
| 175 |
+
transforms += [
|
| 176 |
+
augmax.RandomCrop(int(width * 0.95), int(height * 0.95)),
|
| 177 |
+
augmax.Resize(width, height),
|
| 178 |
+
augmax.Rotate((-5, 5)),
|
| 179 |
+
]
|
| 180 |
+
transforms += [
|
| 181 |
+
augmax.ColorJitter(brightness=0.3, contrast=0.4, saturation=0.5),
|
| 182 |
+
]
|
| 183 |
+
sub_rngs = jax.random.split(rng, image.shape[0])
|
| 184 |
+
image = jax.vmap(augmax.Chain(*transforms))(sub_rngs, image)
|
| 185 |
+
|
| 186 |
+
# Back to [-1, 1].
|
| 187 |
+
image = image * 2.0 - 1.0
|
| 188 |
+
|
| 189 |
+
out_images[key] = image
|
| 190 |
+
|
| 191 |
+
# obtain mask
|
| 192 |
+
out_masks = {}
|
| 193 |
+
for key in out_images:
|
| 194 |
+
if key not in observation.image_masks:
|
| 195 |
+
# do not mask by default
|
| 196 |
+
out_masks[key] = jnp.ones(batch_shape, dtype=jnp.bool)
|
| 197 |
+
else:
|
| 198 |
+
out_masks[key] = jnp.asarray(observation.image_masks[key])
|
| 199 |
+
|
| 200 |
+
return Observation(
|
| 201 |
+
images=out_images,
|
| 202 |
+
image_masks=out_masks,
|
| 203 |
+
state=observation.state,
|
| 204 |
+
tokenized_prompt=observation.tokenized_prompt,
|
| 205 |
+
tokenized_prompt_mask=observation.tokenized_prompt_mask,
|
| 206 |
+
token_ar_mask=observation.token_ar_mask,
|
| 207 |
+
token_loss_mask=observation.token_loss_mask,
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
@dataclasses.dataclass(frozen=True)
|
| 212 |
+
class BaseModelConfig(abc.ABC):
|
| 213 |
+
"""Configuration shared by all models. Specific models should inherit from this class, and implement the `create`
|
| 214 |
+
method to create the corresponding model.
|
| 215 |
+
"""
|
| 216 |
+
|
| 217 |
+
# Action space dimension.
|
| 218 |
+
action_dim: int
|
| 219 |
+
# Action sequence length.
|
| 220 |
+
action_horizon: int
|
| 221 |
+
# Tokenized prompt maximum length.
|
| 222 |
+
max_token_len: int
|
| 223 |
+
|
| 224 |
+
@property
|
| 225 |
+
@abc.abstractmethod
|
| 226 |
+
def model_type(self) -> ModelType:
|
| 227 |
+
"""The model type."""
|
| 228 |
+
|
| 229 |
+
@abc.abstractmethod
|
| 230 |
+
def create(self, rng: at.KeyArrayLike) -> "BaseModel":
|
| 231 |
+
"""Create a new model, initializing parameters."""
|
| 232 |
+
|
| 233 |
+
def load(self, params: at.Params, *, remove_extra_params: bool = True) -> "BaseModel":
|
| 234 |
+
"""Create a model with the given parameters."""
|
| 235 |
+
model = nnx.eval_shape(self.create, jax.random.key(0))
|
| 236 |
+
graphdef, state = nnx.split(model)
|
| 237 |
+
if remove_extra_params:
|
| 238 |
+
params = ocp.transform_utils.intersect_trees(state.to_pure_dict(), params)
|
| 239 |
+
at.check_pytree_equality(expected=state.to_pure_dict(), got=params, check_shapes=True, check_dtypes=False)
|
| 240 |
+
state.replace_by_pure_dict(params)
|
| 241 |
+
return nnx.merge(graphdef, state)
|
| 242 |
+
|
| 243 |
+
def load_pytorch(self, train_config, weight_path: str):
|
| 244 |
+
logger.info(f"train_config: {train_config}")
|
| 245 |
+
model = pi0_pytorch.PI0Pytorch(config=train_config.model)
|
| 246 |
+
safetensors.torch.load_model(model, weight_path)
|
| 247 |
+
return model
|
| 248 |
+
|
| 249 |
+
@abc.abstractmethod
|
| 250 |
+
def inputs_spec(self, *, batch_size: int = 1) -> tuple[Observation, Actions]:
|
| 251 |
+
"""Returns the input specification for the model. Values are jax.ShapeDtypeStruct."""
|
| 252 |
+
|
| 253 |
+
def fake_obs(self, batch_size: int = 1) -> Observation:
|
| 254 |
+
observation_spec, _ = self.inputs_spec(batch_size=batch_size)
|
| 255 |
+
return jax.tree.map(lambda x: jnp.ones(x.shape, x.dtype), observation_spec)
|
| 256 |
+
|
| 257 |
+
def fake_act(self, batch_size: int = 1) -> Actions:
|
| 258 |
+
_, action_spec = self.inputs_spec(batch_size=batch_size)
|
| 259 |
+
return jax.tree.map(lambda x: jnp.ones(x.shape, x.dtype), action_spec)
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
@dataclasses.dataclass
|
| 263 |
+
class BaseModel(nnx.Module, abc.ABC):
|
| 264 |
+
"""Base class for all model implementations. Specific models should inherit from this class. They should call
|
| 265 |
+
super().__init__() to initialize the shared attributes (action_dim, action_horizon, and max_token_len).
|
| 266 |
+
"""
|
| 267 |
+
|
| 268 |
+
action_dim: int
|
| 269 |
+
action_horizon: int
|
| 270 |
+
max_token_len: int
|
| 271 |
+
|
| 272 |
+
@abc.abstractmethod
|
| 273 |
+
def compute_loss(
|
| 274 |
+
self,
|
| 275 |
+
rng: at.KeyArrayLike,
|
| 276 |
+
observation: Observation,
|
| 277 |
+
actions: Actions,
|
| 278 |
+
*,
|
| 279 |
+
train: bool = False,
|
| 280 |
+
) -> at.Float[at.Array, "*b ah"]: ...
|
| 281 |
+
|
| 282 |
+
@abc.abstractmethod
|
| 283 |
+
def sample_actions(self, rng: at.KeyArrayLike, observation: Observation, **kwargs) -> Actions: ...
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def restore_params(
|
| 287 |
+
params_path: pathlib.Path | str,
|
| 288 |
+
*,
|
| 289 |
+
restore_type: type[np.ndarray] | type[jax.Array] = jax.Array,
|
| 290 |
+
dtype: jnp.dtype | None = None,
|
| 291 |
+
sharding: jax.sharding.Sharding | None = None,
|
| 292 |
+
) -> at.Params:
|
| 293 |
+
"""Restores unstructured params PyTree from a checkpoint.
|
| 294 |
+
|
| 295 |
+
This works with checkpoints saved with `save_state` during openpi training (see `training/checkpoints.py`) as
|
| 296 |
+
well as pre-trained checkpoints released for openpi.
|
| 297 |
+
|
| 298 |
+
Args:
|
| 299 |
+
params_path: The local path to the checkpoint directory.
|
| 300 |
+
restore_type: The type to restore the params as. Can be set to `np.ndarray` to load the params as a numpy array.
|
| 301 |
+
dtype: The dtype to restore all params as. If not provided, will use the original dtype from the checkpoint.
|
| 302 |
+
sharding: The sharding to use for the params. If not provided, the params will be replicated across all devices.
|
| 303 |
+
|
| 304 |
+
Returns:
|
| 305 |
+
The restored params.
|
| 306 |
+
"""
|
| 307 |
+
params_path = pathlib.Path(params_path).resolve() if not str(params_path).startswith("gs://") else params_path
|
| 308 |
+
|
| 309 |
+
if restore_type is jax.Array and sharding is None:
|
| 310 |
+
mesh = jax.sharding.Mesh(jax.devices(), ("x",))
|
| 311 |
+
sharding = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec())
|
| 312 |
+
|
| 313 |
+
with ocp.PyTreeCheckpointer() as ckptr:
|
| 314 |
+
metadata = ckptr.metadata(params_path)
|
| 315 |
+
item = {"params": metadata["params"]}
|
| 316 |
+
|
| 317 |
+
params = ckptr.restore(
|
| 318 |
+
params_path,
|
| 319 |
+
ocp.args.PyTreeRestore(
|
| 320 |
+
item=item,
|
| 321 |
+
restore_args=jax.tree.map(
|
| 322 |
+
lambda _: ocp.ArrayRestoreArgs(sharding=sharding, restore_type=restore_type, dtype=dtype), item
|
| 323 |
+
),
|
| 324 |
+
),
|
| 325 |
+
)["params"]
|
| 326 |
+
|
| 327 |
+
# If the params were saved with `save_state` during openpi training, every key path will end with "value", which is
|
| 328 |
+
# added by `nnx.State`. We remove the "value" suffix here and always return what NNX calls a "pure dict".
|
| 329 |
+
flat_params = traverse_util.flatten_dict(params)
|
| 330 |
+
if all(kp[-1] == "value" for kp in flat_params):
|
| 331 |
+
flat_params = {kp[:-1]: v for kp, v in flat_params.items()}
|
| 332 |
+
return traverse_util.unflatten_dict(flat_params)
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/model_test.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flax import nnx
|
| 2 |
+
import jax
|
| 3 |
+
import pytest
|
| 4 |
+
|
| 5 |
+
from openpi.models import model as _model
|
| 6 |
+
from openpi.models import pi0_config
|
| 7 |
+
from openpi.models import pi0_fast
|
| 8 |
+
from openpi.shared import download
|
| 9 |
+
from openpi.shared import nnx_utils
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def test_pi0_model():
|
| 13 |
+
key = jax.random.key(0)
|
| 14 |
+
config = pi0_config.Pi0Config()
|
| 15 |
+
model = config.create(key)
|
| 16 |
+
|
| 17 |
+
batch_size = 2
|
| 18 |
+
obs, act = config.fake_obs(batch_size), config.fake_act(batch_size)
|
| 19 |
+
|
| 20 |
+
loss = nnx_utils.module_jit(model.compute_loss)(key, obs, act)
|
| 21 |
+
assert loss.shape == (batch_size, config.action_horizon)
|
| 22 |
+
|
| 23 |
+
actions = nnx_utils.module_jit(model.sample_actions)(key, obs, num_steps=10)
|
| 24 |
+
assert actions.shape == (batch_size, model.action_horizon, model.action_dim)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def test_pi0_lora_model():
|
| 28 |
+
key = jax.random.key(0)
|
| 29 |
+
config = pi0_config.Pi0Config(paligemma_variant="gemma_2b_lora")
|
| 30 |
+
model = config.create(key)
|
| 31 |
+
|
| 32 |
+
batch_size = 2
|
| 33 |
+
obs, act = config.fake_obs(batch_size), config.fake_act(batch_size)
|
| 34 |
+
|
| 35 |
+
loss = nnx_utils.module_jit(model.compute_loss)(key, obs, act)
|
| 36 |
+
assert loss.shape == (batch_size, config.action_horizon)
|
| 37 |
+
|
| 38 |
+
actions = nnx_utils.module_jit(model.sample_actions)(key, obs, num_steps=10)
|
| 39 |
+
assert actions.shape == (batch_size, model.action_horizon, model.action_dim)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def test_pi0_fast_model():
|
| 43 |
+
key = jax.random.key(0)
|
| 44 |
+
config = pi0_fast.Pi0FASTConfig()
|
| 45 |
+
model = config.create(key)
|
| 46 |
+
|
| 47 |
+
batch_size = 2
|
| 48 |
+
obs, act = config.fake_obs(batch_size), config.fake_act(batch_size)
|
| 49 |
+
|
| 50 |
+
loss = nnx_utils.module_jit(model.compute_loss)(key, obs, act)
|
| 51 |
+
assert loss.shape == (batch_size,)
|
| 52 |
+
|
| 53 |
+
actions = nnx_utils.module_jit(model.sample_actions)(key, obs)
|
| 54 |
+
assert actions.shape == (batch_size, 256)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def test_pi0_fast_lora_model():
|
| 58 |
+
key = jax.random.key(0)
|
| 59 |
+
config = pi0_fast.Pi0FASTConfig(paligemma_variant="gemma_2b_lora")
|
| 60 |
+
model = config.create(key)
|
| 61 |
+
|
| 62 |
+
batch_size = 2
|
| 63 |
+
obs, act = config.fake_obs(batch_size), config.fake_act(batch_size)
|
| 64 |
+
|
| 65 |
+
loss = nnx_utils.module_jit(model.compute_loss)(key, obs, act)
|
| 66 |
+
assert loss.shape == (batch_size,)
|
| 67 |
+
|
| 68 |
+
actions = nnx_utils.module_jit(model.sample_actions)(key, obs)
|
| 69 |
+
assert actions.shape == (batch_size, 256)
|
| 70 |
+
|
| 71 |
+
lora_filter = nnx_utils.PathRegex(".*lora.*")
|
| 72 |
+
model_state = nnx.state(model)
|
| 73 |
+
|
| 74 |
+
lora_state_elems = list(model_state.filter(lora_filter))
|
| 75 |
+
assert len(lora_state_elems) > 0
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
@pytest.mark.manual
|
| 79 |
+
def test_model_restore():
|
| 80 |
+
key = jax.random.key(0)
|
| 81 |
+
config = pi0_config.Pi0Config()
|
| 82 |
+
|
| 83 |
+
batch_size = 2
|
| 84 |
+
obs, act = config.fake_obs(batch_size), config.fake_act(batch_size)
|
| 85 |
+
|
| 86 |
+
model = config.load(
|
| 87 |
+
_model.restore_params(download.maybe_download("gs://openpi-assets/checkpoints/pi0_base/params"))
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
loss = model.compute_loss(key, obs, act)
|
| 91 |
+
assert loss.shape == (batch_size, config.action_horizon)
|
| 92 |
+
|
| 93 |
+
actions = model.sample_actions(key, obs, num_steps=10)
|
| 94 |
+
assert actions.shape == (batch_size, model.action_horizon, model.action_dim)
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/pi0.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
|
| 3 |
+
import einops
|
| 4 |
+
import flax.nnx as nnx
|
| 5 |
+
import flax.nnx.bridge as nnx_bridge
|
| 6 |
+
import jax
|
| 7 |
+
import jax.numpy as jnp
|
| 8 |
+
from typing_extensions import override
|
| 9 |
+
|
| 10 |
+
from openpi.models import model as _model
|
| 11 |
+
from openpi.models import pi0_config
|
| 12 |
+
import openpi.models.gemma as _gemma
|
| 13 |
+
import openpi.models.siglip as _siglip
|
| 14 |
+
from openpi.shared import array_typing as at
|
| 15 |
+
|
| 16 |
+
logger = logging.getLogger("openpi")
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def make_attn_mask(input_mask, mask_ar):
|
| 20 |
+
"""Adapted from big_vision.
|
| 21 |
+
|
| 22 |
+
Tokens can attend to valid inputs tokens which have a cumulative mask_ar
|
| 23 |
+
smaller or equal to theirs. This way `mask_ar` bool[?B, N] can be used to
|
| 24 |
+
setup several types of attention, for example:
|
| 25 |
+
|
| 26 |
+
[[1 1 1 1 1 1]]: pure causal attention.
|
| 27 |
+
|
| 28 |
+
[[0 0 0 1 1 1]]: prefix-lm attention. The first 3 tokens can attend between
|
| 29 |
+
themselves and the last 3 tokens have a causal attention. The first
|
| 30 |
+
entry could also be a 1 without changing behaviour.
|
| 31 |
+
|
| 32 |
+
[[1 0 1 0 1 0 0 1 0 0]]: causal attention between 4 blocks. Tokens of a
|
| 33 |
+
block can attend all previous blocks and all tokens on the same block.
|
| 34 |
+
|
| 35 |
+
Args:
|
| 36 |
+
input_mask: bool[B, N] true if its part of the input, false if padding.
|
| 37 |
+
mask_ar: bool[?B, N] mask that's true where previous tokens cannot depend on
|
| 38 |
+
it and false where it shares the same attention mask as the previous token.
|
| 39 |
+
"""
|
| 40 |
+
mask_ar = jnp.broadcast_to(mask_ar, input_mask.shape)
|
| 41 |
+
cumsum = jnp.cumsum(mask_ar, axis=1)
|
| 42 |
+
attn_mask = cumsum[:, None, :] <= cumsum[:, :, None]
|
| 43 |
+
valid_mask = input_mask[:, None, :] * input_mask[:, :, None]
|
| 44 |
+
return jnp.logical_and(attn_mask, valid_mask)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
@at.typecheck
|
| 48 |
+
def posemb_sincos(
|
| 49 |
+
pos: at.Real[at.Array, " b"], embedding_dim: int, min_period: float, max_period: float
|
| 50 |
+
) -> at.Float[at.Array, "b {embedding_dim}"]:
|
| 51 |
+
"""Computes sine-cosine positional embedding vectors for scalar positions."""
|
| 52 |
+
if embedding_dim % 2 != 0:
|
| 53 |
+
raise ValueError(f"embedding_dim ({embedding_dim}) must be divisible by 2")
|
| 54 |
+
|
| 55 |
+
fraction = jnp.linspace(0.0, 1.0, embedding_dim // 2)
|
| 56 |
+
period = min_period * (max_period / min_period) ** fraction
|
| 57 |
+
sinusoid_input = jnp.einsum(
|
| 58 |
+
"i,j->ij",
|
| 59 |
+
pos,
|
| 60 |
+
1.0 / period * 2 * jnp.pi,
|
| 61 |
+
precision=jax.lax.Precision.HIGHEST,
|
| 62 |
+
)
|
| 63 |
+
return jnp.concatenate([jnp.sin(sinusoid_input), jnp.cos(sinusoid_input)], axis=-1)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class Pi0(_model.BaseModel):
|
| 67 |
+
def __init__(self, config: pi0_config.Pi0Config, rngs: nnx.Rngs):
|
| 68 |
+
super().__init__(config.action_dim, config.action_horizon, config.max_token_len)
|
| 69 |
+
self.pi05 = config.pi05
|
| 70 |
+
paligemma_config = _gemma.get_config(config.paligemma_variant)
|
| 71 |
+
action_expert_config = _gemma.get_config(config.action_expert_variant)
|
| 72 |
+
# TODO: rewrite gemma in NNX. For now, use bridge.
|
| 73 |
+
llm = nnx_bridge.ToNNX(
|
| 74 |
+
_gemma.Module(
|
| 75 |
+
configs=[paligemma_config, action_expert_config],
|
| 76 |
+
embed_dtype=config.dtype,
|
| 77 |
+
adarms=config.pi05,
|
| 78 |
+
)
|
| 79 |
+
)
|
| 80 |
+
llm.lazy_init(rngs=rngs, method="init", use_adarms=[False, True] if config.pi05 else [False, False])
|
| 81 |
+
img = nnx_bridge.ToNNX(
|
| 82 |
+
_siglip.Module(
|
| 83 |
+
num_classes=paligemma_config.width,
|
| 84 |
+
variant="So400m/14",
|
| 85 |
+
pool_type="none",
|
| 86 |
+
scan=True,
|
| 87 |
+
dtype_mm=config.dtype,
|
| 88 |
+
)
|
| 89 |
+
)
|
| 90 |
+
img.lazy_init(next(iter(config.fake_obs().images.values())), train=False, rngs=rngs)
|
| 91 |
+
self.PaliGemma = nnx.Dict(llm=llm, img=img)
|
| 92 |
+
self.action_in_proj = nnx.Linear(config.action_dim, action_expert_config.width, rngs=rngs)
|
| 93 |
+
if config.pi05:
|
| 94 |
+
self.time_mlp_in = nnx.Linear(action_expert_config.width, action_expert_config.width, rngs=rngs)
|
| 95 |
+
self.time_mlp_out = nnx.Linear(action_expert_config.width, action_expert_config.width, rngs=rngs)
|
| 96 |
+
else:
|
| 97 |
+
self.state_proj = nnx.Linear(config.action_dim, action_expert_config.width, rngs=rngs)
|
| 98 |
+
self.action_time_mlp_in = nnx.Linear(2 * action_expert_config.width, action_expert_config.width, rngs=rngs)
|
| 99 |
+
self.action_time_mlp_out = nnx.Linear(action_expert_config.width, action_expert_config.width, rngs=rngs)
|
| 100 |
+
self.action_out_proj = nnx.Linear(action_expert_config.width, config.action_dim, rngs=rngs)
|
| 101 |
+
|
| 102 |
+
# This attribute gets automatically set by model.train() and model.eval().
|
| 103 |
+
self.deterministic = True
|
| 104 |
+
|
| 105 |
+
@at.typecheck
|
| 106 |
+
def embed_prefix(
|
| 107 |
+
self, obs: _model.Observation
|
| 108 |
+
) -> tuple[at.Float[at.Array, "b s emb"], at.Bool[at.Array, "b s"], at.Bool[at.Array, " s"]]:
|
| 109 |
+
input_mask = []
|
| 110 |
+
ar_mask = []
|
| 111 |
+
tokens = []
|
| 112 |
+
# embed images
|
| 113 |
+
for name in obs.images:
|
| 114 |
+
image_tokens, _ = self.PaliGemma.img(obs.images[name], train=False)
|
| 115 |
+
|
| 116 |
+
tokens.append(image_tokens)
|
| 117 |
+
input_mask.append(
|
| 118 |
+
einops.repeat(
|
| 119 |
+
obs.image_masks[name],
|
| 120 |
+
"b -> b s",
|
| 121 |
+
s=image_tokens.shape[1],
|
| 122 |
+
)
|
| 123 |
+
)
|
| 124 |
+
# image tokens attend to each other
|
| 125 |
+
ar_mask += [False] * image_tokens.shape[1]
|
| 126 |
+
|
| 127 |
+
# add language (aka tokenized inputs)
|
| 128 |
+
if obs.tokenized_prompt is not None:
|
| 129 |
+
tokenized_inputs = self.PaliGemma.llm(obs.tokenized_prompt, method="embed")
|
| 130 |
+
tokens.append(tokenized_inputs)
|
| 131 |
+
input_mask.append(obs.tokenized_prompt_mask)
|
| 132 |
+
# full attention between image and language inputs
|
| 133 |
+
ar_mask += [False] * tokenized_inputs.shape[1]
|
| 134 |
+
tokens = jnp.concatenate(tokens, axis=1)
|
| 135 |
+
input_mask = jnp.concatenate(input_mask, axis=1)
|
| 136 |
+
ar_mask = jnp.array(ar_mask)
|
| 137 |
+
return tokens, input_mask, ar_mask
|
| 138 |
+
|
| 139 |
+
@at.typecheck
|
| 140 |
+
def embed_suffix(
|
| 141 |
+
self, obs: _model.Observation, noisy_actions: _model.Actions, timestep: at.Float[at.Array, " b"]
|
| 142 |
+
) -> tuple[
|
| 143 |
+
at.Float[at.Array, "b s emb"],
|
| 144 |
+
at.Bool[at.Array, "b s"],
|
| 145 |
+
at.Bool[at.Array, " s"],
|
| 146 |
+
at.Float[at.Array, "b emb"] | None,
|
| 147 |
+
]:
|
| 148 |
+
input_mask = []
|
| 149 |
+
ar_mask = []
|
| 150 |
+
tokens = []
|
| 151 |
+
if not self.pi05:
|
| 152 |
+
# add a single state token
|
| 153 |
+
state_token = self.state_proj(obs.state)[:, None, :]
|
| 154 |
+
tokens.append(state_token)
|
| 155 |
+
input_mask.append(jnp.ones((obs.state.shape[0], 1), dtype=jnp.bool_))
|
| 156 |
+
# image/language inputs do not attend to state or actions
|
| 157 |
+
ar_mask += [True]
|
| 158 |
+
|
| 159 |
+
action_tokens = self.action_in_proj(noisy_actions)
|
| 160 |
+
# embed timestep using sine-cosine positional encoding with sensitivity in the range [0, 1]
|
| 161 |
+
time_emb = posemb_sincos(timestep, self.action_in_proj.out_features, min_period=4e-3, max_period=4.0)
|
| 162 |
+
if self.pi05:
|
| 163 |
+
# time MLP (for adaRMS)
|
| 164 |
+
time_emb = self.time_mlp_in(time_emb)
|
| 165 |
+
time_emb = nnx.swish(time_emb)
|
| 166 |
+
time_emb = self.time_mlp_out(time_emb)
|
| 167 |
+
time_emb = nnx.swish(time_emb)
|
| 168 |
+
action_expert_tokens = action_tokens
|
| 169 |
+
adarms_cond = time_emb
|
| 170 |
+
else:
|
| 171 |
+
# mix timestep + action information using an MLP (no adaRMS)
|
| 172 |
+
time_tokens = einops.repeat(time_emb, "b emb -> b s emb", s=self.action_horizon)
|
| 173 |
+
action_time_tokens = jnp.concatenate([action_tokens, time_tokens], axis=-1)
|
| 174 |
+
action_time_tokens = self.action_time_mlp_in(action_time_tokens)
|
| 175 |
+
action_time_tokens = nnx.swish(action_time_tokens)
|
| 176 |
+
action_time_tokens = self.action_time_mlp_out(action_time_tokens)
|
| 177 |
+
action_expert_tokens = action_time_tokens
|
| 178 |
+
adarms_cond = None
|
| 179 |
+
tokens.append(action_expert_tokens)
|
| 180 |
+
input_mask.append(jnp.ones(action_expert_tokens.shape[:2], dtype=jnp.bool_))
|
| 181 |
+
# image/language/state inputs do not attend to action tokens
|
| 182 |
+
ar_mask += [True] + ([False] * (self.action_horizon - 1))
|
| 183 |
+
tokens = jnp.concatenate(tokens, axis=1)
|
| 184 |
+
input_mask = jnp.concatenate(input_mask, axis=1)
|
| 185 |
+
ar_mask = jnp.array(ar_mask)
|
| 186 |
+
return tokens, input_mask, ar_mask, adarms_cond
|
| 187 |
+
|
| 188 |
+
@override
|
| 189 |
+
def compute_loss(
|
| 190 |
+
self, rng: at.KeyArrayLike, observation: _model.Observation, actions: _model.Actions, *, train: bool = False
|
| 191 |
+
) -> at.Float[at.Array, "*b ah"]:
|
| 192 |
+
preprocess_rng, noise_rng, time_rng = jax.random.split(rng, 3)
|
| 193 |
+
observation = _model.preprocess_observation(preprocess_rng, observation, train=train)
|
| 194 |
+
|
| 195 |
+
batch_shape = actions.shape[:-2]
|
| 196 |
+
noise = jax.random.normal(noise_rng, actions.shape)
|
| 197 |
+
time = jax.random.beta(time_rng, 1.5, 1, batch_shape) * 0.999 + 0.001
|
| 198 |
+
time_expanded = time[..., None, None]
|
| 199 |
+
x_t = time_expanded * noise + (1 - time_expanded) * actions
|
| 200 |
+
u_t = noise - actions
|
| 201 |
+
|
| 202 |
+
# one big forward pass of prefix + suffix at once
|
| 203 |
+
prefix_tokens, prefix_mask, prefix_ar_mask = self.embed_prefix(observation)
|
| 204 |
+
suffix_tokens, suffix_mask, suffix_ar_mask, adarms_cond = self.embed_suffix(observation, x_t, time)
|
| 205 |
+
input_mask = jnp.concatenate([prefix_mask, suffix_mask], axis=1)
|
| 206 |
+
ar_mask = jnp.concatenate([prefix_ar_mask, suffix_ar_mask], axis=0)
|
| 207 |
+
attn_mask = make_attn_mask(input_mask, ar_mask)
|
| 208 |
+
positions = jnp.cumsum(input_mask, axis=1) - 1
|
| 209 |
+
(prefix_out, suffix_out), _ = self.PaliGemma.llm(
|
| 210 |
+
[prefix_tokens, suffix_tokens], mask=attn_mask, positions=positions, adarms_cond=[None, adarms_cond]
|
| 211 |
+
)
|
| 212 |
+
v_t = self.action_out_proj(suffix_out[:, -self.action_horizon :])
|
| 213 |
+
|
| 214 |
+
return jnp.mean(jnp.square(v_t - u_t), axis=-1)
|
| 215 |
+
|
| 216 |
+
@override
|
| 217 |
+
def sample_actions(
|
| 218 |
+
self,
|
| 219 |
+
rng: at.KeyArrayLike,
|
| 220 |
+
observation: _model.Observation,
|
| 221 |
+
*,
|
| 222 |
+
num_steps: int | at.Int[at.Array, ""] = 10,
|
| 223 |
+
noise: at.Float[at.Array, "b ah ad"] | None = None,
|
| 224 |
+
) -> _model.Actions:
|
| 225 |
+
observation = _model.preprocess_observation(None, observation, train=False)
|
| 226 |
+
# note that we use the convention more common in diffusion literature, where t=1 is noise and t=0 is the target
|
| 227 |
+
# distribution. yes, this is the opposite of the pi0 paper, and I'm sorry.
|
| 228 |
+
dt = -1.0 / num_steps
|
| 229 |
+
batch_size = observation.state.shape[0]
|
| 230 |
+
if noise is None:
|
| 231 |
+
noise = jax.random.normal(rng, (batch_size, self.action_horizon, self.action_dim))
|
| 232 |
+
|
| 233 |
+
# first fill KV cache with a forward pass of the prefix
|
| 234 |
+
prefix_tokens, prefix_mask, prefix_ar_mask = self.embed_prefix(observation)
|
| 235 |
+
prefix_attn_mask = make_attn_mask(prefix_mask, prefix_ar_mask)
|
| 236 |
+
positions = jnp.cumsum(prefix_mask, axis=1) - 1
|
| 237 |
+
_, kv_cache = self.PaliGemma.llm([prefix_tokens, None], mask=prefix_attn_mask, positions=positions)
|
| 238 |
+
|
| 239 |
+
def step(carry):
|
| 240 |
+
x_t, time = carry
|
| 241 |
+
suffix_tokens, suffix_mask, suffix_ar_mask, adarms_cond = self.embed_suffix(
|
| 242 |
+
observation, x_t, jnp.broadcast_to(time, batch_size)
|
| 243 |
+
)
|
| 244 |
+
# `suffix_attn_mask` is shape (b, suffix_len, suffix_len) indicating how the suffix tokens can attend to each
|
| 245 |
+
# other
|
| 246 |
+
suffix_attn_mask = make_attn_mask(suffix_mask, suffix_ar_mask)
|
| 247 |
+
# `prefix_attn_mask` is shape (b, suffix_len, prefix_len) indicating how the suffix tokens can attend to the
|
| 248 |
+
# prefix tokens
|
| 249 |
+
prefix_attn_mask = einops.repeat(prefix_mask, "b p -> b s p", s=suffix_tokens.shape[1])
|
| 250 |
+
# `combined_mask` is shape (b, suffix_len, prefix_len + suffix_len) indicating how the suffix tokens (which
|
| 251 |
+
# generate the queries) can attend to the full prefix + suffix sequence (which generates the keys and values)
|
| 252 |
+
full_attn_mask = jnp.concatenate([prefix_attn_mask, suffix_attn_mask], axis=-1)
|
| 253 |
+
assert full_attn_mask.shape == (
|
| 254 |
+
batch_size,
|
| 255 |
+
suffix_tokens.shape[1],
|
| 256 |
+
prefix_tokens.shape[1] + suffix_tokens.shape[1],
|
| 257 |
+
)
|
| 258 |
+
# `positions` is shape (b, suffix_len) indicating the positions of the suffix tokens
|
| 259 |
+
positions = jnp.sum(prefix_mask, axis=-1)[:, None] + jnp.cumsum(suffix_mask, axis=-1) - 1
|
| 260 |
+
|
| 261 |
+
(prefix_out, suffix_out), _ = self.PaliGemma.llm(
|
| 262 |
+
[None, suffix_tokens],
|
| 263 |
+
mask=full_attn_mask,
|
| 264 |
+
positions=positions,
|
| 265 |
+
kv_cache=kv_cache,
|
| 266 |
+
adarms_cond=[None, adarms_cond],
|
| 267 |
+
)
|
| 268 |
+
assert prefix_out is None
|
| 269 |
+
v_t = self.action_out_proj(suffix_out[:, -self.action_horizon :])
|
| 270 |
+
|
| 271 |
+
return x_t + dt * v_t, time + dt
|
| 272 |
+
|
| 273 |
+
def cond(carry):
|
| 274 |
+
x_t, time = carry
|
| 275 |
+
# robust to floating-point error
|
| 276 |
+
return time >= -dt / 2
|
| 277 |
+
|
| 278 |
+
x_0, _ = jax.lax.while_loop(cond, step, (noise, 1.0))
|
| 279 |
+
return x_0
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/pi0_config.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import dataclasses
|
| 2 |
+
from typing import TYPE_CHECKING
|
| 3 |
+
|
| 4 |
+
import flax.nnx as nnx
|
| 5 |
+
import jax
|
| 6 |
+
import jax.numpy as jnp
|
| 7 |
+
from typing_extensions import override
|
| 8 |
+
|
| 9 |
+
from openpi.models import model as _model
|
| 10 |
+
import openpi.models.gemma as _gemma
|
| 11 |
+
from openpi.shared import array_typing as at
|
| 12 |
+
import openpi.shared.nnx_utils as nnx_utils
|
| 13 |
+
|
| 14 |
+
if TYPE_CHECKING:
|
| 15 |
+
from openpi.models.pi0 import Pi0
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@dataclasses.dataclass(frozen=True)
|
| 19 |
+
class Pi0Config(_model.BaseModelConfig):
|
| 20 |
+
dtype: str = "bfloat16"
|
| 21 |
+
paligemma_variant: _gemma.Variant = "gemma_2b"
|
| 22 |
+
action_expert_variant: _gemma.Variant = "gemma_300m"
|
| 23 |
+
|
| 24 |
+
# Set the model specific defaults.
|
| 25 |
+
action_dim: int = 32
|
| 26 |
+
action_horizon: int = 50
|
| 27 |
+
max_token_len: int = None # type: ignore
|
| 28 |
+
# Pi05 has two differences from Pi0:
|
| 29 |
+
# - the state input is part of the discrete language tokens rather than a continuous input that is part of the suffix
|
| 30 |
+
# - the action expert uses adaRMSNorm to inject the flow matching timestep
|
| 31 |
+
pi05: bool = False
|
| 32 |
+
# This config option is not used directly by the model, but it is read by the ModelTransformFactory.
|
| 33 |
+
discrete_state_input: bool = None # type: ignore
|
| 34 |
+
|
| 35 |
+
pytorch_compile_mode: str | None = "max-autotune"
|
| 36 |
+
|
| 37 |
+
def __post_init__(self):
|
| 38 |
+
if self.max_token_len is None:
|
| 39 |
+
object.__setattr__(self, "max_token_len", 200 if self.pi05 else 48)
|
| 40 |
+
if self.discrete_state_input is None:
|
| 41 |
+
object.__setattr__(self, "discrete_state_input", self.pi05)
|
| 42 |
+
if self.pytorch_compile_mode is not None:
|
| 43 |
+
assert self.pytorch_compile_mode in [
|
| 44 |
+
"default",
|
| 45 |
+
"reduce-overhead",
|
| 46 |
+
"max-autotune",
|
| 47 |
+
"max-autotune-no-cudagraphs",
|
| 48 |
+
]
|
| 49 |
+
|
| 50 |
+
@property
|
| 51 |
+
@override
|
| 52 |
+
def model_type(self) -> _model.ModelType:
|
| 53 |
+
if self.pi05:
|
| 54 |
+
return _model.ModelType.PI05
|
| 55 |
+
return _model.ModelType.PI0
|
| 56 |
+
|
| 57 |
+
@override
|
| 58 |
+
def create(self, rng: at.KeyArrayLike) -> "Pi0":
|
| 59 |
+
from openpi.models.pi0 import Pi0
|
| 60 |
+
|
| 61 |
+
return Pi0(self, rngs=nnx.Rngs(rng))
|
| 62 |
+
|
| 63 |
+
@override
|
| 64 |
+
def inputs_spec(self, *, batch_size: int = 1) -> tuple[_model.Observation, _model.Actions]:
|
| 65 |
+
image_spec = jax.ShapeDtypeStruct([batch_size, *_model.IMAGE_RESOLUTION, 3], jnp.float32)
|
| 66 |
+
image_mask_spec = jax.ShapeDtypeStruct([batch_size], jnp.bool_)
|
| 67 |
+
|
| 68 |
+
with at.disable_typechecking():
|
| 69 |
+
observation_spec = _model.Observation(
|
| 70 |
+
images={
|
| 71 |
+
"base_0_rgb": image_spec,
|
| 72 |
+
"left_wrist_0_rgb": image_spec,
|
| 73 |
+
"right_wrist_0_rgb": image_spec,
|
| 74 |
+
},
|
| 75 |
+
image_masks={
|
| 76 |
+
"base_0_rgb": image_mask_spec,
|
| 77 |
+
"left_wrist_0_rgb": image_mask_spec,
|
| 78 |
+
"right_wrist_0_rgb": image_mask_spec,
|
| 79 |
+
},
|
| 80 |
+
state=jax.ShapeDtypeStruct([batch_size, self.action_dim], jnp.float32),
|
| 81 |
+
tokenized_prompt=jax.ShapeDtypeStruct([batch_size, self.max_token_len], jnp.int32),
|
| 82 |
+
tokenized_prompt_mask=jax.ShapeDtypeStruct([batch_size, self.max_token_len], bool),
|
| 83 |
+
)
|
| 84 |
+
action_spec = jax.ShapeDtypeStruct([batch_size, self.action_horizon, self.action_dim], jnp.float32)
|
| 85 |
+
|
| 86 |
+
return observation_spec, action_spec
|
| 87 |
+
|
| 88 |
+
def get_freeze_filter(self) -> nnx.filterlib.Filter:
|
| 89 |
+
"""Returns the freeze filter based on the model config."""
|
| 90 |
+
filters = []
|
| 91 |
+
has_lora = False
|
| 92 |
+
gemma_params_filter = nnx_utils.PathRegex(".*llm.*")
|
| 93 |
+
action_expert_params_filter = nnx_utils.PathRegex(".*llm.*_1.*")
|
| 94 |
+
if "lora" in self.paligemma_variant:
|
| 95 |
+
filters.append(
|
| 96 |
+
gemma_params_filter,
|
| 97 |
+
)
|
| 98 |
+
if "lora" not in self.action_expert_variant:
|
| 99 |
+
# If only freeze gemma params, exclude action expert params.
|
| 100 |
+
filters.append(
|
| 101 |
+
nnx.Not(action_expert_params_filter),
|
| 102 |
+
)
|
| 103 |
+
has_lora = True
|
| 104 |
+
elif "lora" in self.action_expert_variant:
|
| 105 |
+
filters.append(
|
| 106 |
+
action_expert_params_filter,
|
| 107 |
+
)
|
| 108 |
+
has_lora = True
|
| 109 |
+
|
| 110 |
+
if has_lora:
|
| 111 |
+
# If any lora is used, exclude all lora params.
|
| 112 |
+
filters.append(
|
| 113 |
+
nnx.Not(nnx_utils.PathRegex(".*lora.*")),
|
| 114 |
+
)
|
| 115 |
+
if not filters:
|
| 116 |
+
return nnx.Nothing
|
| 117 |
+
return nnx.All(*filters)
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/pi0_fast.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import dataclasses
|
| 2 |
+
import logging
|
| 3 |
+
from typing import Any
|
| 4 |
+
|
| 5 |
+
import einops
|
| 6 |
+
import flax.nnx as nnx
|
| 7 |
+
import flax.nnx.bridge as nnx_bridge
|
| 8 |
+
import jax
|
| 9 |
+
import jax.numpy as jnp
|
| 10 |
+
from typing_extensions import override
|
| 11 |
+
|
| 12 |
+
from openpi.models import model as _model
|
| 13 |
+
import openpi.models.gemma_fast as _gemma
|
| 14 |
+
import openpi.models.siglip as _siglip
|
| 15 |
+
from openpi.shared import array_typing as at
|
| 16 |
+
import openpi.shared.nnx_utils as nnx_utils
|
| 17 |
+
|
| 18 |
+
logger = logging.getLogger("openpi")
|
| 19 |
+
|
| 20 |
+
PALIGEMMA_EOS_TOKEN = 1
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def make_attn_mask(input_mask, mask_ar):
|
| 24 |
+
"""Adapted from big_vision.
|
| 25 |
+
|
| 26 |
+
Tokens can attend to valid inputs tokens which have a cumulative mask_ar
|
| 27 |
+
smaller or equal to theirs. This way `mask_ar` bool[?B, N] can be used to
|
| 28 |
+
setup several types of attention, for example:
|
| 29 |
+
|
| 30 |
+
[[1 1 1 1 1 1]]: pure causal attention.
|
| 31 |
+
|
| 32 |
+
[[0 0 0 1 1 1]]: prefix-lm attention. The first 3 tokens can attend between
|
| 33 |
+
themselves and the last 3 tokens have a causal attention. The first
|
| 34 |
+
entry could also be a 1 without changing behaviour.
|
| 35 |
+
|
| 36 |
+
[[1 0 1 0 1 0 0 1 0 0]]: causal attention between 4 blocks. Tokens of a
|
| 37 |
+
block can attend all previous blocks and all tokens on the same block.
|
| 38 |
+
|
| 39 |
+
Args:
|
| 40 |
+
input_mask: bool[B, N] true if its part of the input, false if padding.
|
| 41 |
+
mask_ar: bool[?B, N] mask that's true where previous tokens cannot depend on
|
| 42 |
+
it and false where it shares the same attention mask as the previous token.
|
| 43 |
+
"""
|
| 44 |
+
mask_ar = jnp.broadcast_to(mask_ar, input_mask.shape)
|
| 45 |
+
cumsum = jnp.cumsum(mask_ar, axis=1)
|
| 46 |
+
attn_mask = cumsum[:, None, :] <= cumsum[:, :, None]
|
| 47 |
+
valid_mask = input_mask[:, None, :] * input_mask[:, :, None]
|
| 48 |
+
return jnp.logical_and(attn_mask, valid_mask)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@jax.vmap
|
| 52 |
+
def left_to_right_align(x, input_mask, attn_mask):
|
| 53 |
+
"""Converts input from left-align to right-aligned."""
|
| 54 |
+
# Due to vmap, this is operating in a single example (not batch level).
|
| 55 |
+
assert x.ndim == 2
|
| 56 |
+
assert input_mask.ndim == 1
|
| 57 |
+
assert attn_mask.ndim == 2
|
| 58 |
+
assert x.shape[0] == input_mask.shape[0]
|
| 59 |
+
assert attn_mask.shape[0] == attn_mask.shape[1], attn_mask.shape
|
| 60 |
+
seqlen = jnp.max(input_mask * jnp.arange(input_mask.shape[0])) + 1
|
| 61 |
+
x = jnp.roll(x, -seqlen, axis=0)
|
| 62 |
+
input_mask = jnp.roll(input_mask, -seqlen, axis=0)
|
| 63 |
+
attn_mask = jnp.roll(attn_mask, -seqlen, axis=(0, 1))
|
| 64 |
+
return x, input_mask, attn_mask
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def put_along_last_axis(arr, indices, values):
|
| 68 |
+
"""Like np.put_along_axis(..., axis=-1), since jax is missing it."""
|
| 69 |
+
assert arr.ndim == indices.ndim == values.ndim, (arr.ndim, indices.ndim, values.ndim)
|
| 70 |
+
onehot = jax.nn.one_hot(indices, arr.shape[-1], dtype=values.dtype)
|
| 71 |
+
put_mask = jnp.einsum("...i,...in->...n", jnp.ones(values.shape, jnp.int32), onehot)
|
| 72 |
+
put_values = jnp.einsum("...i,...in->...n", values, onehot)
|
| 73 |
+
return jnp.where(put_mask, put_values, arr)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
@dataclasses.dataclass(frozen=True)
|
| 77 |
+
class Pi0FASTConfig(_model.BaseModelConfig):
|
| 78 |
+
dtype: str = "bfloat16"
|
| 79 |
+
paligemma_variant: _gemma.Variant = "gemma_2b"
|
| 80 |
+
|
| 81 |
+
# Set the model specific defaults.
|
| 82 |
+
action_dim: int = 32
|
| 83 |
+
action_horizon: int = 32
|
| 84 |
+
max_token_len: int = 250
|
| 85 |
+
|
| 86 |
+
# Tokenizer for the fast model.
|
| 87 |
+
fast_model_tokenizer: Any | None = None
|
| 88 |
+
# Keyword arguments for the fast model tokenizer.
|
| 89 |
+
fast_model_tokenizer_kwargs: dict[str, Any] | None = None
|
| 90 |
+
|
| 91 |
+
@property
|
| 92 |
+
@override
|
| 93 |
+
def model_type(self) -> _model.ModelType:
|
| 94 |
+
return _model.ModelType.PI0_FAST
|
| 95 |
+
|
| 96 |
+
@override
|
| 97 |
+
def create(self, rng: at.KeyArrayLike) -> "Pi0FAST":
|
| 98 |
+
return Pi0FAST(self, rngs=nnx.Rngs(rng))
|
| 99 |
+
|
| 100 |
+
@override
|
| 101 |
+
def inputs_spec(self, *, batch_size: int = 1) -> tuple[_model.Observation, _model.Actions]:
|
| 102 |
+
image_spec = jax.ShapeDtypeStruct([batch_size, *_model.IMAGE_RESOLUTION, 3], jnp.float32)
|
| 103 |
+
image_mask_spec = jax.ShapeDtypeStruct([batch_size], jnp.bool_)
|
| 104 |
+
|
| 105 |
+
with at.disable_typechecking():
|
| 106 |
+
observation_spec = _model.Observation(
|
| 107 |
+
images={
|
| 108 |
+
"base_0_rgb": image_spec,
|
| 109 |
+
"base_1_rgb": image_spec,
|
| 110 |
+
"wrist_0_rgb": image_spec,
|
| 111 |
+
},
|
| 112 |
+
image_masks={
|
| 113 |
+
"base_0_rgb": image_mask_spec,
|
| 114 |
+
"base_1_rgb": image_mask_spec,
|
| 115 |
+
"wrist_0_rgb": image_mask_spec,
|
| 116 |
+
},
|
| 117 |
+
state=jax.ShapeDtypeStruct([batch_size, self.action_dim], jnp.float32),
|
| 118 |
+
tokenized_prompt=jax.ShapeDtypeStruct([batch_size, self.max_token_len], jnp.int32),
|
| 119 |
+
tokenized_prompt_mask=jax.ShapeDtypeStruct([batch_size, self.max_token_len], bool),
|
| 120 |
+
token_ar_mask=jax.ShapeDtypeStruct([batch_size, self.max_token_len], jnp.int32),
|
| 121 |
+
token_loss_mask=jax.ShapeDtypeStruct([batch_size, self.max_token_len], jnp.bool_),
|
| 122 |
+
)
|
| 123 |
+
action_spec = jax.ShapeDtypeStruct([batch_size, self.action_horizon, self.action_dim], jnp.float32)
|
| 124 |
+
|
| 125 |
+
return observation_spec, action_spec
|
| 126 |
+
|
| 127 |
+
def get_freeze_filter(self) -> nnx.filterlib.Filter:
|
| 128 |
+
"""Returns the freeze filter based on the model config."""
|
| 129 |
+
if "lora" in self.paligemma_variant:
|
| 130 |
+
return nnx.All(nnx_utils.PathRegex(".*llm.*"), nnx.Not(nnx_utils.PathRegex(".*lora.*")))
|
| 131 |
+
return nnx.Nothing
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
class Pi0FAST(_model.BaseModel):
|
| 135 |
+
def __init__(self, config: Pi0FASTConfig, rngs: nnx.Rngs):
|
| 136 |
+
super().__init__(config.action_dim, config.action_horizon, config.max_token_len)
|
| 137 |
+
paligemma_config = _gemma.get_config(config.paligemma_variant)
|
| 138 |
+
# TODO: rewrite gemma in NNX. For now, use bridge.
|
| 139 |
+
llm = nnx_bridge.ToNNX(
|
| 140 |
+
_gemma.Module(
|
| 141 |
+
**paligemma_config,
|
| 142 |
+
embed_dtype=config.dtype,
|
| 143 |
+
cache_dtype=config.dtype,
|
| 144 |
+
)
|
| 145 |
+
)
|
| 146 |
+
llm.lazy_init(rngs=rngs, method="init")
|
| 147 |
+
img = nnx_bridge.ToNNX(
|
| 148 |
+
_siglip.Module(
|
| 149 |
+
num_classes=paligemma_config.width,
|
| 150 |
+
variant="So400m/14",
|
| 151 |
+
pool_type="none",
|
| 152 |
+
scan=True,
|
| 153 |
+
dtype_mm=config.dtype,
|
| 154 |
+
)
|
| 155 |
+
)
|
| 156 |
+
img.lazy_init(next(iter(config.fake_obs().images.values())), train=False, rngs=rngs)
|
| 157 |
+
self.PaliGemma = nnx.Dict(llm=llm, img=img)
|
| 158 |
+
|
| 159 |
+
@at.typecheck
|
| 160 |
+
def embed_inputs(
|
| 161 |
+
self, obs: _model.Observation
|
| 162 |
+
) -> tuple[at.Float[at.Array, "b s emb"], at.Bool[at.Array, "b s"], at.Int[at.Array, "b s"]]:
|
| 163 |
+
input_mask = []
|
| 164 |
+
ar_mask = []
|
| 165 |
+
token_embeddings = []
|
| 166 |
+
# embed images
|
| 167 |
+
for name in obs.images:
|
| 168 |
+
image_token_embeddings, _ = self.PaliGemma.img(obs.images[name], train=False)
|
| 169 |
+
|
| 170 |
+
token_embeddings.append(image_token_embeddings)
|
| 171 |
+
input_mask.append(
|
| 172 |
+
einops.repeat(
|
| 173 |
+
obs.image_masks[name],
|
| 174 |
+
"b -> b s",
|
| 175 |
+
s=image_token_embeddings.shape[1],
|
| 176 |
+
)
|
| 177 |
+
)
|
| 178 |
+
# image tokens attend to each other --> AR mask = 0
|
| 179 |
+
ar_mask.append(0 * input_mask[-1])
|
| 180 |
+
|
| 181 |
+
# add tokenized inputs
|
| 182 |
+
assert obs.tokenized_prompt is not None, "Tokenized prompt is required"
|
| 183 |
+
assert obs.tokenized_prompt_mask is not None, "Tokenized prompt mask is required"
|
| 184 |
+
assert obs.token_ar_mask is not None, "Token auto-regressive mask is required"
|
| 185 |
+
tokenized_inputs_embeddings = self.PaliGemma.llm(obs.tokenized_prompt, embed_only=True)
|
| 186 |
+
token_embeddings.append(tokenized_inputs_embeddings)
|
| 187 |
+
input_mask.append(obs.tokenized_prompt_mask)
|
| 188 |
+
ar_mask.append(obs.token_ar_mask)
|
| 189 |
+
|
| 190 |
+
# return embeddings, input mask, and ar mask
|
| 191 |
+
return (
|
| 192 |
+
jnp.concatenate(token_embeddings, axis=1),
|
| 193 |
+
jnp.concatenate(input_mask, axis=1),
|
| 194 |
+
jnp.concatenate(ar_mask, axis=1),
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
@override
|
| 198 |
+
def compute_loss(
|
| 199 |
+
self, rng: at.KeyArrayLike, observation: _model.Observation, actions: _model.Actions, *, train: bool = False
|
| 200 |
+
) -> at.Float[at.Array, "*b ah"]:
|
| 201 |
+
observation = _model.preprocess_observation(
|
| 202 |
+
rng, observation, train=train, image_keys=list(observation.images.keys())
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
# Compute inputs: one big forward pass of prefix + suffix at once
|
| 206 |
+
input_token_embeddings, input_mask, ar_mask = self.embed_inputs(observation)
|
| 207 |
+
attn_mask = make_attn_mask(input_mask, ar_mask)
|
| 208 |
+
|
| 209 |
+
# Compute one-hot targets: we predict *next* token, so shift the input tokens by one.
|
| 210 |
+
targets = jax.nn.one_hot(
|
| 211 |
+
observation.tokenized_prompt[:, 1:],
|
| 212 |
+
self.PaliGemma.llm.module.vocab_size,
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
# Each input predicts *next* token, so we don't input the last token.
|
| 216 |
+
pre_logits, _, _ = self.PaliGemma.llm(
|
| 217 |
+
embedded_prefix=input_token_embeddings[:, :-1],
|
| 218 |
+
mask=attn_mask[:, :-1, :-1],
|
| 219 |
+
return_prelogits=True,
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
# Only decode logits for the target tokens to save memory
|
| 223 |
+
# (decoding matmul is large because it is a seq_len x vocab_size dense layer).
|
| 224 |
+
logits, _ = self.PaliGemma.llm(
|
| 225 |
+
pre_logits=pre_logits[:, -targets.shape[1] :],
|
| 226 |
+
)
|
| 227 |
+
logp = jax.nn.log_softmax(logits, axis=-1)
|
| 228 |
+
|
| 229 |
+
# Compute CE loss on token targets
|
| 230 |
+
assert observation.token_loss_mask is not None, "Token loss mask is required"
|
| 231 |
+
loss_mask = observation.token_loss_mask[:, 1:]
|
| 232 |
+
token_pplx = jnp.sum(targets * logp, axis=-1)
|
| 233 |
+
return -jnp.sum(token_pplx * loss_mask, axis=-1) / jnp.clip(jnp.sum(loss_mask, -1), 1)
|
| 234 |
+
|
| 235 |
+
@override
|
| 236 |
+
def sample_actions(
|
| 237 |
+
self,
|
| 238 |
+
rng: at.KeyArrayLike,
|
| 239 |
+
observation: _model.Observation,
|
| 240 |
+
*,
|
| 241 |
+
max_decoding_steps: int | at.Int[at.Array, ""] = 256,
|
| 242 |
+
temperature: float = 0.0,
|
| 243 |
+
) -> _model.Actions:
|
| 244 |
+
# TODO: this is a hack to get the image keys.
|
| 245 |
+
observation = _model.preprocess_observation(
|
| 246 |
+
None, observation, train=False, image_keys=list(observation.images.keys())
|
| 247 |
+
)
|
| 248 |
+
|
| 249 |
+
# embed inputs
|
| 250 |
+
prefix_token_embeddings, prefix_mask, prefix_ar_mask = self.embed_inputs(observation)
|
| 251 |
+
prefix_attn_mask = make_attn_mask(prefix_mask, prefix_ar_mask)
|
| 252 |
+
|
| 253 |
+
# left to right align all input token sequences
|
| 254 |
+
prefix_token_embeddings, prefix_mask, prefix_attn_mask = left_to_right_align(
|
| 255 |
+
prefix_token_embeddings, prefix_mask, prefix_attn_mask
|
| 256 |
+
)
|
| 257 |
+
prefill_size = prefix_token_embeddings.shape[1]
|
| 258 |
+
prefill_len = jnp.sum(prefix_mask, axis=-1)
|
| 259 |
+
prefix_start = prefill_size - prefill_len
|
| 260 |
+
|
| 261 |
+
# first fill KV cache with a forward pass of the prefix
|
| 262 |
+
# pad attention mask to set the size of the KV cache (prefill_size + max_decoding_steps)
|
| 263 |
+
prefix_attn_mask = jnp.pad(prefix_attn_mask, ((0, 0), (0, 0), (0, max_decoding_steps)))
|
| 264 |
+
prefix_positions = jnp.cumsum(prefix_mask, axis=-1) - 1
|
| 265 |
+
prefix_logits, kv_cache, _ = self.PaliGemma.llm(
|
| 266 |
+
embedded_prefix=prefix_token_embeddings, mask=prefix_attn_mask, positions=prefix_positions, decode=True
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
# prepare decoding -- final logit decodes the first token
|
| 270 |
+
last_logit = prefix_logits[:, -1:]
|
| 271 |
+
output_tokens = jnp.zeros((last_logit.shape[0], max_decoding_steps))
|
| 272 |
+
|
| 273 |
+
def step(carry):
|
| 274 |
+
rng, last_logit, output_tokens, cache, _, step = carry
|
| 275 |
+
|
| 276 |
+
# Sample token from last logit
|
| 277 |
+
# Split RNG for this step
|
| 278 |
+
rng, rng_step = jax.random.split(rng)
|
| 279 |
+
token = jax.lax.cond(
|
| 280 |
+
temperature > 0.0,
|
| 281 |
+
lambda _: jax.random.categorical(rng_step, last_logit / temperature, axis=-1),
|
| 282 |
+
lambda _: jnp.argmax(last_logit, axis=-1),
|
| 283 |
+
operand=None,
|
| 284 |
+
)
|
| 285 |
+
output_tokens = put_along_last_axis(output_tokens, jnp.broadcast_to(step, (token.shape[0], 1)), token)
|
| 286 |
+
|
| 287 |
+
# Check for early stopping --> stop if all batch elements have EOS token
|
| 288 |
+
has_eos = jnp.any(token == PALIGEMMA_EOS_TOKEN, axis=-1)
|
| 289 |
+
all_eos = jnp.all(has_eos)
|
| 290 |
+
|
| 291 |
+
# Decode one step
|
| 292 |
+
token_embedding = self.PaliGemma.llm(token, embed_only=True)
|
| 293 |
+
positions = prefill_len[:, None] + step + 1
|
| 294 |
+
mask = jnp.logical_and(
|
| 295 |
+
jnp.arange(prefill_size + max_decoding_steps)[None, None, :] >= prefix_start[:, None, None],
|
| 296 |
+
jnp.arange(prefill_size + max_decoding_steps)[None, None, :]
|
| 297 |
+
< (jnp.broadcast_to(prefill_size + step + 1, (prefix_start.shape[0], 1, 1))),
|
| 298 |
+
)
|
| 299 |
+
last_logit, kv_cache, _ = self.PaliGemma.llm(
|
| 300 |
+
embedded_prefix=token_embedding, mask=mask, positions=positions, decode=True, kv_cache=cache
|
| 301 |
+
)
|
| 302 |
+
|
| 303 |
+
return rng, last_logit, output_tokens, kv_cache, all_eos, step + 1
|
| 304 |
+
|
| 305 |
+
def cond(carry):
|
| 306 |
+
_, _, _, _, all_eos, step = carry
|
| 307 |
+
return (~all_eos) & (step < max_decoding_steps)
|
| 308 |
+
|
| 309 |
+
# Use lax.while_loop so we can jit the full decoding loop.
|
| 310 |
+
_, _, output_tokens, _, _, _ = jax.lax.while_loop(
|
| 311 |
+
cond, step, (rng, last_logit, output_tokens, kv_cache, False, 0)
|
| 312 |
+
)
|
| 313 |
+
return output_tokens
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/pi0_test.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import flax.nnx as nnx
|
| 2 |
+
import jax
|
| 3 |
+
|
| 4 |
+
import openpi.models.pi0_config as _pi0_config
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def _get_frozen_state(config: _pi0_config.Pi0Config) -> nnx.State:
|
| 8 |
+
abstract_model = nnx.eval_shape(config.create, jax.random.key(0))
|
| 9 |
+
|
| 10 |
+
freeze_filter = config.get_freeze_filter()
|
| 11 |
+
return nnx.state(abstract_model, nnx.All(nnx.Param, freeze_filter)).flat_state()
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_pi0_full_finetune():
|
| 15 |
+
config = _pi0_config.Pi0Config()
|
| 16 |
+
state = _get_frozen_state(config)
|
| 17 |
+
assert len(state) == 0
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def test_pi0_gemma_lora():
|
| 21 |
+
config = _pi0_config.Pi0Config(paligemma_variant="gemma_2b_lora")
|
| 22 |
+
state = _get_frozen_state(config)
|
| 23 |
+
assert len(state) == 9
|
| 24 |
+
assert all("lora" not in p for p in state)
|
| 25 |
+
assert all("llm" in p for p in state)
|
| 26 |
+
assert all("_1" not in p for p in state)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def test_pi0_action_expert_lora():
|
| 30 |
+
config = _pi0_config.Pi0Config(action_expert_variant="gemma_300m_lora")
|
| 31 |
+
state = _get_frozen_state(config)
|
| 32 |
+
# excluding embedder, rest of the params should be same as gemma_lora.
|
| 33 |
+
assert len(state) == 8
|
| 34 |
+
assert all("lora" not in p for p in state)
|
| 35 |
+
assert all("llm" in p for p in state)
|
| 36 |
+
# all frozen params should have _1 in their path since it's the action expert.
|
| 37 |
+
assert all(any("_1" in p for p in path) for path in state)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def test_pi0_all_lora():
|
| 41 |
+
config = _pi0_config.Pi0Config(paligemma_variant="gemma_2b_lora", action_expert_variant="gemma_300m_lora")
|
| 42 |
+
state = _get_frozen_state(config)
|
| 43 |
+
# sum of gemma_lora and action_expert_lora's frozen params.
|
| 44 |
+
assert len(state) == 17
|
| 45 |
+
assert all("lora" not in p for p in state)
|
| 46 |
+
assert all("llm" in p for p in state)
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/siglip.py
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 Big Vision Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""A refactored and simplified ViT adoptation for Pi, taken from big_vision."""
|
| 16 |
+
|
| 17 |
+
from collections.abc import Sequence
|
| 18 |
+
|
| 19 |
+
import flax.linen as nn
|
| 20 |
+
import jax
|
| 21 |
+
import jax.numpy as jnp
|
| 22 |
+
import numpy as np
|
| 23 |
+
|
| 24 |
+
import openpi.training.sharding as sharding
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def posemb_sincos_2d(h, w, width, temperature=10_000.0, dtype=jnp.float32):
|
| 28 |
+
"""Follows the MoCo v3 logic."""
|
| 29 |
+
y, x = jnp.mgrid[:h, :w]
|
| 30 |
+
|
| 31 |
+
assert width % 4 == 0, "Width must be mult of 4 for sincos posemb"
|
| 32 |
+
omega = jnp.arange(width // 4) / (width // 4 - 1)
|
| 33 |
+
omega = 1.0 / (temperature**omega)
|
| 34 |
+
y = jnp.einsum("m,d->md", y.flatten(), omega)
|
| 35 |
+
x = jnp.einsum("m,d->md", x.flatten(), omega)
|
| 36 |
+
pe = jnp.concatenate([jnp.sin(x), jnp.cos(x), jnp.sin(y), jnp.cos(y)], axis=1)
|
| 37 |
+
return jnp.asarray(pe, dtype)[None, :, :]
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def get_posemb(self, typ, seqshape, width, name, dtype=jnp.float32):
|
| 41 |
+
if typ == "learn":
|
| 42 |
+
return self.param(
|
| 43 |
+
name,
|
| 44 |
+
nn.initializers.normal(stddev=1 / np.sqrt(width)),
|
| 45 |
+
(1, np.prod(seqshape), width),
|
| 46 |
+
dtype,
|
| 47 |
+
)
|
| 48 |
+
if typ == "sincos2d":
|
| 49 |
+
return posemb_sincos_2d(*seqshape, width, dtype=dtype)
|
| 50 |
+
raise ValueError(f"Unknown posemb type: {typ}")
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class MlpBlock(nn.Module):
|
| 54 |
+
"""Transformer MLP / feed-forward block."""
|
| 55 |
+
|
| 56 |
+
mlp_dim: int | None = None # Defaults to 4x input dim
|
| 57 |
+
dropout: float = 0.0
|
| 58 |
+
dtype_mm: str = "float32"
|
| 59 |
+
|
| 60 |
+
@nn.compact
|
| 61 |
+
def __call__(self, x, deterministic=True): # noqa: FBT002
|
| 62 |
+
"""Applies Transformer MlpBlock module."""
|
| 63 |
+
inits = {
|
| 64 |
+
"kernel_init": nn.initializers.xavier_uniform(),
|
| 65 |
+
"bias_init": nn.initializers.normal(stddev=1e-6),
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
_, _, d = x.shape # n,l,d
|
| 69 |
+
x = nn.Dense(self.mlp_dim or 4 * d, dtype=self.dtype_mm, **inits)(x)
|
| 70 |
+
x = nn.gelu(x)
|
| 71 |
+
x = nn.Dropout(rate=self.dropout)(x, deterministic)
|
| 72 |
+
return nn.Dense(d, dtype=self.dtype_mm, **inits)(x)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
class Encoder1DBlock(nn.Module):
|
| 76 |
+
"""Single transformer encoder block (MHSA + MLP)."""
|
| 77 |
+
|
| 78 |
+
mlp_dim: int | None = None # Defaults to 4x input dim
|
| 79 |
+
num_heads: int = 12
|
| 80 |
+
dropout: float = 0.0
|
| 81 |
+
dtype_mm: str = "float32"
|
| 82 |
+
|
| 83 |
+
@nn.compact
|
| 84 |
+
def __call__(self, x, deterministic=True): # noqa: FBT002
|
| 85 |
+
out = {}
|
| 86 |
+
x = sharding.activation_sharding_constraint(x)
|
| 87 |
+
y = nn.LayerNorm(dtype=self.dtype_mm)(x)
|
| 88 |
+
y = out["sa"] = nn.MultiHeadDotProductAttention(
|
| 89 |
+
num_heads=self.num_heads,
|
| 90 |
+
kernel_init=nn.initializers.xavier_uniform(),
|
| 91 |
+
deterministic=deterministic,
|
| 92 |
+
dtype=self.dtype_mm,
|
| 93 |
+
)(y, y)
|
| 94 |
+
y = sharding.activation_sharding_constraint(y)
|
| 95 |
+
y = nn.Dropout(rate=self.dropout)(y, deterministic)
|
| 96 |
+
x = out["+sa"] = x + y
|
| 97 |
+
|
| 98 |
+
y = nn.LayerNorm(dtype=self.dtype_mm)(x)
|
| 99 |
+
y = out["mlp"] = MlpBlock(
|
| 100 |
+
mlp_dim=self.mlp_dim,
|
| 101 |
+
dropout=self.dropout,
|
| 102 |
+
dtype_mm=self.dtype_mm,
|
| 103 |
+
)(y, deterministic)
|
| 104 |
+
y = sharding.activation_sharding_constraint(y)
|
| 105 |
+
y = nn.Dropout(rate=self.dropout)(y, deterministic)
|
| 106 |
+
x = out["+mlp"] = x + y
|
| 107 |
+
x = sharding.activation_sharding_constraint(x)
|
| 108 |
+
return x, out
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
class Encoder(nn.Module):
|
| 112 |
+
"""Transformer Model Encoder for sequence to sequence translation."""
|
| 113 |
+
|
| 114 |
+
depth: int
|
| 115 |
+
mlp_dim: int | None = None # Defaults to 4x input dim
|
| 116 |
+
num_heads: int = 12
|
| 117 |
+
dropout: float = 0.0
|
| 118 |
+
scan: bool = False
|
| 119 |
+
remat_policy: str = "nothing_saveable"
|
| 120 |
+
dtype_mm: str = "float32"
|
| 121 |
+
|
| 122 |
+
@nn.compact
|
| 123 |
+
def __call__(self, x, deterministic=True): # noqa: FBT002
|
| 124 |
+
out = {}
|
| 125 |
+
|
| 126 |
+
if self.scan:
|
| 127 |
+
block = nn.remat(
|
| 128 |
+
Encoder1DBlock,
|
| 129 |
+
prevent_cse=False,
|
| 130 |
+
static_argnums=(2,), # 0=self, 2=deterministic
|
| 131 |
+
policy=getattr(jax.checkpoint_policies, self.remat_policy, None),
|
| 132 |
+
)
|
| 133 |
+
x, scan_out = nn.scan(
|
| 134 |
+
block,
|
| 135 |
+
variable_axes={"params": 0},
|
| 136 |
+
split_rngs={"params": True, "dropout": True},
|
| 137 |
+
in_axes=nn.broadcast,
|
| 138 |
+
length=self.depth,
|
| 139 |
+
)(
|
| 140 |
+
name="encoderblock",
|
| 141 |
+
dtype_mm=self.dtype_mm,
|
| 142 |
+
mlp_dim=self.mlp_dim,
|
| 143 |
+
num_heads=self.num_heads,
|
| 144 |
+
dropout=self.dropout,
|
| 145 |
+
)(x, deterministic)
|
| 146 |
+
for lyr in range(self.depth):
|
| 147 |
+
out[f"block{lyr:02d}"] = jax.tree.map(lambda o, lyr=lyr: o[lyr], scan_out)
|
| 148 |
+
else:
|
| 149 |
+
# Input Encoder
|
| 150 |
+
for lyr in range(self.depth):
|
| 151 |
+
block_cur = Encoder1DBlock(
|
| 152 |
+
name=f"encoderblock_{lyr}",
|
| 153 |
+
dtype_mm=self.dtype_mm,
|
| 154 |
+
mlp_dim=self.mlp_dim,
|
| 155 |
+
num_heads=self.num_heads,
|
| 156 |
+
dropout=self.dropout,
|
| 157 |
+
)
|
| 158 |
+
x, out[f"block{lyr:02d}"] = block_cur(x, deterministic)
|
| 159 |
+
out["pre_ln"] = x # Alias for last block, but without the number in it.
|
| 160 |
+
|
| 161 |
+
return nn.LayerNorm(name="encoder_norm", dtype=self.dtype_mm)(x), out
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
class MAPHead(nn.Module):
|
| 165 |
+
"""Multihead Attention Pooling."""
|
| 166 |
+
|
| 167 |
+
mlp_dim: int | None = None # Defaults to 4x input dim
|
| 168 |
+
num_heads: int = 12
|
| 169 |
+
dtype_mm: str = "float32"
|
| 170 |
+
|
| 171 |
+
@nn.compact
|
| 172 |
+
def __call__(self, x):
|
| 173 |
+
n, _, d = x.shape # n,l,d
|
| 174 |
+
probe = self.param("probe", nn.initializers.xavier_uniform(), (1, 1, d), x.dtype)
|
| 175 |
+
probe = jnp.tile(probe, [n, 1, 1])
|
| 176 |
+
|
| 177 |
+
x = nn.MultiHeadDotProductAttention(
|
| 178 |
+
num_heads=self.num_heads,
|
| 179 |
+
dtype=self.dtype_mm,
|
| 180 |
+
kernel_init=nn.initializers.xavier_uniform(),
|
| 181 |
+
)(probe, x)
|
| 182 |
+
|
| 183 |
+
y = nn.LayerNorm(dtype=self.dtype_mm)(x)
|
| 184 |
+
x = x + MlpBlock(mlp_dim=self.mlp_dim, dtype=self.dtype_mm)(y)
|
| 185 |
+
return x[:, 0]
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
class _Module(nn.Module):
|
| 189 |
+
"""ViT model."""
|
| 190 |
+
|
| 191 |
+
num_classes: int | None = None
|
| 192 |
+
patch_size: Sequence[int] = (16, 16)
|
| 193 |
+
width: int = 768
|
| 194 |
+
depth: int = 12
|
| 195 |
+
mlp_dim: int | None = None # Defaults to 4x input dim
|
| 196 |
+
num_heads: int = 12
|
| 197 |
+
posemb: str = "learn" # Can also be "sincos2d"
|
| 198 |
+
rep_size: int | bool = False
|
| 199 |
+
dropout: float = 0.0
|
| 200 |
+
pool_type: str = "gap" # Can also be "map" or "tok"
|
| 201 |
+
head_zeroinit: bool = True
|
| 202 |
+
scan: bool = False
|
| 203 |
+
# or "dots_with_no_batch_dims_saveable" for more speed (memory costly)
|
| 204 |
+
remat_policy: str = "nothing_saveable"
|
| 205 |
+
dtype_mm: str = "float32"
|
| 206 |
+
|
| 207 |
+
@nn.compact
|
| 208 |
+
def __call__(self, image, *, train=False):
|
| 209 |
+
out = {}
|
| 210 |
+
|
| 211 |
+
# Kevin edit: do patch extraction and posemb in float32,
|
| 212 |
+
# because I feel like it's a bit safer.
|
| 213 |
+
image = jnp.asarray(image, jnp.float32)
|
| 214 |
+
|
| 215 |
+
# Patch extraction
|
| 216 |
+
x = out["stem"] = nn.Conv(
|
| 217 |
+
self.width,
|
| 218 |
+
self.patch_size,
|
| 219 |
+
strides=self.patch_size,
|
| 220 |
+
padding="VALID",
|
| 221 |
+
name="embedding",
|
| 222 |
+
dtype=jnp.float32,
|
| 223 |
+
)(image)
|
| 224 |
+
|
| 225 |
+
n, h, w, c = x.shape
|
| 226 |
+
x = jnp.reshape(x, [n, h * w, c])
|
| 227 |
+
|
| 228 |
+
# Add posemb before adding extra token.
|
| 229 |
+
x = out["with_posemb"] = x + get_posemb(self, self.posemb, (h, w), c, "pos_embedding", jnp.float32)
|
| 230 |
+
|
| 231 |
+
if self.pool_type == "tok":
|
| 232 |
+
cls = self.param("cls", nn.initializers.zeros, (1, 1, c), x.dtype)
|
| 233 |
+
x = jnp.concatenate([jnp.tile(cls, [n, 1, 1]), x], axis=1)
|
| 234 |
+
|
| 235 |
+
n, _, c = x.shape # n,l,d
|
| 236 |
+
x = nn.Dropout(rate=self.dropout)(x, not train)
|
| 237 |
+
|
| 238 |
+
# Kevin edit: now cast back to dtype_mm (potentially half precision)
|
| 239 |
+
x = x.astype(self.dtype_mm)
|
| 240 |
+
|
| 241 |
+
x, out["encoder"] = Encoder(
|
| 242 |
+
depth=self.depth,
|
| 243 |
+
mlp_dim=self.mlp_dim,
|
| 244 |
+
num_heads=self.num_heads,
|
| 245 |
+
dropout=self.dropout,
|
| 246 |
+
scan=self.scan,
|
| 247 |
+
remat_policy=self.remat_policy,
|
| 248 |
+
dtype_mm=self.dtype_mm,
|
| 249 |
+
name="Transformer",
|
| 250 |
+
)(x, deterministic=not train)
|
| 251 |
+
encoded = out["encoded"] = x
|
| 252 |
+
|
| 253 |
+
if self.pool_type == "map":
|
| 254 |
+
x = out["head_input"] = MAPHead(
|
| 255 |
+
num_heads=self.num_heads,
|
| 256 |
+
mlp_dim=self.mlp_dim,
|
| 257 |
+
dtype=self.dtype_mm,
|
| 258 |
+
)(x)
|
| 259 |
+
elif self.pool_type == "gap":
|
| 260 |
+
x = out["head_input"] = jnp.mean(x, axis=1)
|
| 261 |
+
elif self.pool_type == "0":
|
| 262 |
+
x = out["head_input"] = x[:, 0]
|
| 263 |
+
elif self.pool_type == "tok":
|
| 264 |
+
x = out["head_input"] = x[:, 0]
|
| 265 |
+
encoded = encoded[:, 1:]
|
| 266 |
+
elif self.pool_type == "none":
|
| 267 |
+
pass
|
| 268 |
+
else:
|
| 269 |
+
raise ValueError(f"Unknown pool type: '{self.pool_type}'")
|
| 270 |
+
|
| 271 |
+
x_2d = jnp.reshape(encoded, [n, h, w, -1])
|
| 272 |
+
|
| 273 |
+
if self.rep_size:
|
| 274 |
+
rep_size = self.width if self.rep_size is True else self.rep_size
|
| 275 |
+
hid = nn.Dense(rep_size, dtype=self.dtype_mm, name="pre_logits")
|
| 276 |
+
# NOTE: In the past we did not include tanh in pre_logits.
|
| 277 |
+
# For few-shot, it should not matter much, as it whitens anyways.
|
| 278 |
+
x_2d = nn.tanh(hid(x_2d))
|
| 279 |
+
x = nn.tanh(hid(x))
|
| 280 |
+
|
| 281 |
+
out["pre_logits_2d"] = x_2d
|
| 282 |
+
out["pre_logits"] = x
|
| 283 |
+
|
| 284 |
+
if self.num_classes:
|
| 285 |
+
kw = {"kernel_init": nn.initializers.zeros} if self.head_zeroinit else {}
|
| 286 |
+
head = nn.Dense(self.num_classes, dtype=self.dtype_mm, name="head", **kw)
|
| 287 |
+
x_2d = out["logits_2d"] = head(x_2d)
|
| 288 |
+
x = out["logits"] = head(x)
|
| 289 |
+
|
| 290 |
+
return x, out
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def Module(num_classes=None, *, variant=None, **kw): # pylint: disable=invalid-name # noqa: N802
|
| 294 |
+
"""Factory function, because linen really don't like what I'm doing!"""
|
| 295 |
+
return _Module(num_classes, **{**decode_variant(variant), **kw})
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
def decode_variant(variant):
|
| 299 |
+
"""Converts a string like "B" or "B/32" into a params dict."""
|
| 300 |
+
if variant is None:
|
| 301 |
+
return {}
|
| 302 |
+
|
| 303 |
+
v, patch = variant, {}
|
| 304 |
+
if "/" in variant:
|
| 305 |
+
v, patch = variant.split("/")
|
| 306 |
+
patch = {"patch_size": (int(patch), int(patch))}
|
| 307 |
+
|
| 308 |
+
return {
|
| 309 |
+
# pylint:disable=line-too-long
|
| 310 |
+
# Reference: Table 2 of https://arxiv.org/abs/2106.04560.
|
| 311 |
+
"width": {
|
| 312 |
+
"mu": 32,
|
| 313 |
+
"Ti": 192,
|
| 314 |
+
"S": 384,
|
| 315 |
+
"M": 512,
|
| 316 |
+
"B": 768,
|
| 317 |
+
"L": 1024,
|
| 318 |
+
"So400m": 1152,
|
| 319 |
+
"H": 1280,
|
| 320 |
+
"g": 1408,
|
| 321 |
+
"g-opt": 1536,
|
| 322 |
+
"G": 1664,
|
| 323 |
+
"G-opt": 1536,
|
| 324 |
+
"e": 1792,
|
| 325 |
+
}[v],
|
| 326 |
+
"depth": {
|
| 327 |
+
"mu": 1,
|
| 328 |
+
"Ti": 12,
|
| 329 |
+
"S": 12,
|
| 330 |
+
"M": 12,
|
| 331 |
+
"B": 12,
|
| 332 |
+
"L": 24,
|
| 333 |
+
"So400m": 27,
|
| 334 |
+
"H": 32,
|
| 335 |
+
"g": 40,
|
| 336 |
+
"g-opt": 40,
|
| 337 |
+
"G": 48,
|
| 338 |
+
"G-opt": 48,
|
| 339 |
+
"e": 56,
|
| 340 |
+
}[v],
|
| 341 |
+
"mlp_dim": {
|
| 342 |
+
"mu": 128,
|
| 343 |
+
"Ti": 768,
|
| 344 |
+
"S": 1536,
|
| 345 |
+
"M": 2048,
|
| 346 |
+
"B": 3072,
|
| 347 |
+
"L": 4096,
|
| 348 |
+
"So400m": 4304,
|
| 349 |
+
"H": 5120,
|
| 350 |
+
"g": 6144,
|
| 351 |
+
"g-opt": 6144,
|
| 352 |
+
"G": 8192,
|
| 353 |
+
"G-opt": 8192,
|
| 354 |
+
"e": 15360,
|
| 355 |
+
}[v],
|
| 356 |
+
"num_heads": {
|
| 357 |
+
"mu": 2,
|
| 358 |
+
"Ti": 3,
|
| 359 |
+
"S": 6,
|
| 360 |
+
"M": 8,
|
| 361 |
+
"B": 12,
|
| 362 |
+
"L": 16,
|
| 363 |
+
"So400m": 16,
|
| 364 |
+
"H": 16,
|
| 365 |
+
"g": 16,
|
| 366 |
+
"g-opt": 16,
|
| 367 |
+
"G": 16,
|
| 368 |
+
"G-opt": 16,
|
| 369 |
+
"e": 16,
|
| 370 |
+
}[v],
|
| 371 |
+
# pylint:enable=line-too-long
|
| 372 |
+
**patch,
|
| 373 |
+
}
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/tokenizer.py
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
import jax
|
| 5 |
+
import numpy as np
|
| 6 |
+
import orbax.checkpoint as ocp
|
| 7 |
+
import sentencepiece
|
| 8 |
+
from transformers import AutoProcessor
|
| 9 |
+
|
| 10 |
+
import openpi.models.utils.fsq_tokenizer as fsq_tokenizer
|
| 11 |
+
import openpi.shared.download as download
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class PaligemmaTokenizer:
|
| 15 |
+
def __init__(self, max_len: int = 48):
|
| 16 |
+
self._max_len = max_len
|
| 17 |
+
|
| 18 |
+
path = download.maybe_download("gs://big_vision/paligemma_tokenizer.model", gs={"token": "anon"})
|
| 19 |
+
with path.open("rb") as f:
|
| 20 |
+
self._tokenizer = sentencepiece.SentencePieceProcessor(model_proto=f.read())
|
| 21 |
+
|
| 22 |
+
def tokenize(self, prompt: str, state: np.ndarray | None = None) -> tuple[np.ndarray, np.ndarray]:
|
| 23 |
+
cleaned_text = prompt.strip().replace("_", " ").replace("\n", " ")
|
| 24 |
+
if state is not None:
|
| 25 |
+
# This is the Pi05 format, where the state is part of the discrete language input.
|
| 26 |
+
discretized_state = np.digitize(state, bins=np.linspace(-1, 1, 256 + 1)[:-1]) - 1
|
| 27 |
+
state_str = " ".join(map(str, discretized_state))
|
| 28 |
+
full_prompt = f"Task: {cleaned_text}, State: {state_str};\nAction: "
|
| 29 |
+
tokens = self._tokenizer.encode(full_prompt, add_bos=True)
|
| 30 |
+
else:
|
| 31 |
+
# This is the Pi0 format, where the state is part of the continuous action expert input.
|
| 32 |
+
# tokenize "\n" separately as the "start of answer" token
|
| 33 |
+
tokens = self._tokenizer.encode(cleaned_text, add_bos=True) + self._tokenizer.encode("\n")
|
| 34 |
+
tokens_len = len(tokens)
|
| 35 |
+
if tokens_len < self._max_len:
|
| 36 |
+
padding = [False] * (self._max_len - tokens_len)
|
| 37 |
+
mask = [True] * tokens_len + padding
|
| 38 |
+
tokens = tokens + padding
|
| 39 |
+
else:
|
| 40 |
+
if len(tokens) > self._max_len:
|
| 41 |
+
logging.warning(
|
| 42 |
+
f"Token length ({len(tokens)}) exceeds max length ({self._max_len}), truncating. "
|
| 43 |
+
"Consider increasing the `max_token_len` in your model config if this happens frequently."
|
| 44 |
+
)
|
| 45 |
+
tokens = tokens[: self._max_len]
|
| 46 |
+
mask = [True] * self._max_len
|
| 47 |
+
|
| 48 |
+
return np.asarray(tokens), np.asarray(mask)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class FASTTokenizer:
|
| 52 |
+
def __init__(self, max_len: int = 256, fast_tokenizer_path: str = "physical-intelligence/fast"):
|
| 53 |
+
self._max_len = max_len
|
| 54 |
+
|
| 55 |
+
# Download base PaliGemma tokenizer
|
| 56 |
+
path = download.maybe_download("gs://big_vision/paligemma_tokenizer.model", gs={"token": "anon"})
|
| 57 |
+
with path.open("rb") as f:
|
| 58 |
+
self._paligemma_tokenizer = sentencepiece.SentencePieceProcessor(model_proto=f.read())
|
| 59 |
+
|
| 60 |
+
# Instantiate FAST tokenizer
|
| 61 |
+
self._fast_tokenizer = AutoProcessor.from_pretrained(fast_tokenizer_path, trust_remote_code=True)
|
| 62 |
+
self._fast_skip_tokens = 128 # Skip last 128 tokens in PaliGemma vocab since they are special tokens
|
| 63 |
+
|
| 64 |
+
def tokenize(
|
| 65 |
+
self, prompt: str, state: np.ndarray, actions: np.ndarray | None
|
| 66 |
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
| 67 |
+
cleaned_text = prompt.lower().strip().replace("_", " ")
|
| 68 |
+
|
| 69 |
+
# Convention: state gets discretized into 256 discrete bins (assumed range after normalization: [-1, 1])
|
| 70 |
+
discretized_state = np.digitize(state, bins=np.linspace(-1, 1, 256 + 1)[:-1]) - 1
|
| 71 |
+
|
| 72 |
+
# Convention: prefix includes prompt and string-representation of state, followed by ';'
|
| 73 |
+
state_str = " ".join(map(str, discretized_state))
|
| 74 |
+
prefix = f"Task: {cleaned_text}, State: {state_str};\n"
|
| 75 |
+
prefix_tokens = self._paligemma_tokenizer.encode(prefix, add_bos=True)
|
| 76 |
+
|
| 77 |
+
if actions is not None:
|
| 78 |
+
# Tokenize actions with FAST tokenizer --> map to last tokens in PaliGemma vocab
|
| 79 |
+
action_tokens = self._fast_tokenizer(actions[None])[0]
|
| 80 |
+
action_tokens_in_pg = self._act_tokens_to_paligemma_tokens(action_tokens)
|
| 81 |
+
|
| 82 |
+
# Convention: postfix contains 'Action:' followed by FAST tokens, followed by '|'
|
| 83 |
+
postfix_tokens = (
|
| 84 |
+
self._paligemma_tokenizer.encode("Action: ")
|
| 85 |
+
+ action_tokens_in_pg.tolist()
|
| 86 |
+
+ self._paligemma_tokenizer.encode("|", add_eos=True)
|
| 87 |
+
)
|
| 88 |
+
else:
|
| 89 |
+
postfix_tokens = []
|
| 90 |
+
|
| 91 |
+
# Create output token sequence & masks
|
| 92 |
+
# AR mask is 0 on prefix (bidirectional attention) and 1 on postfix (causal attention to all previous tokens)
|
| 93 |
+
tokens = prefix_tokens + postfix_tokens
|
| 94 |
+
token_mask = [True] * len(tokens)
|
| 95 |
+
ar_mask = [0] * len(prefix_tokens) + [1] * len(postfix_tokens)
|
| 96 |
+
loss_mask = [False] * len(prefix_tokens) + [True] * len(postfix_tokens) # Loss on postfix only
|
| 97 |
+
|
| 98 |
+
# Pad tokens to max length
|
| 99 |
+
tokens_len = len(tokens)
|
| 100 |
+
if tokens_len < self._max_len:
|
| 101 |
+
padding = [False] * (self._max_len - tokens_len)
|
| 102 |
+
tokens = tokens + padding
|
| 103 |
+
token_mask = token_mask + padding
|
| 104 |
+
ar_mask = ar_mask + padding
|
| 105 |
+
loss_mask = loss_mask + padding
|
| 106 |
+
else:
|
| 107 |
+
if len(tokens) > self._max_len:
|
| 108 |
+
logging.warning(
|
| 109 |
+
f"Token length ({len(tokens)}) exceeds max length ({self._max_len}), truncating. "
|
| 110 |
+
"Consider increasing the `max_token_len` in your model config if this happens frequently."
|
| 111 |
+
)
|
| 112 |
+
tokens = tokens[: self._max_len]
|
| 113 |
+
token_mask = token_mask[: self._max_len]
|
| 114 |
+
ar_mask = ar_mask[: self._max_len]
|
| 115 |
+
loss_mask = loss_mask[: self._max_len]
|
| 116 |
+
|
| 117 |
+
return np.asarray(tokens), np.asarray(token_mask), np.asarray(ar_mask), np.asarray(loss_mask)
|
| 118 |
+
|
| 119 |
+
def extract_actions(self, tokens: np.ndarray, action_horizon: int, action_dim: int) -> np.ndarray:
|
| 120 |
+
# Decode predicted output tokens
|
| 121 |
+
decoded_tokens = self._paligemma_tokenizer.decode(tokens.tolist())
|
| 122 |
+
|
| 123 |
+
# Extract actions from FAST model outputs
|
| 124 |
+
if "Action: " not in decoded_tokens:
|
| 125 |
+
return np.zeros((action_horizon, action_dim), dtype=np.float32)
|
| 126 |
+
|
| 127 |
+
# Extract actions from decoded tokens
|
| 128 |
+
raw_action_tokens = np.array(
|
| 129 |
+
self._paligemma_tokenizer.encode(decoded_tokens.split("Action: ")[1].split("|")[0].strip())
|
| 130 |
+
)
|
| 131 |
+
action_tokens = self._act_tokens_to_paligemma_tokens(raw_action_tokens)
|
| 132 |
+
return self._fast_tokenizer.decode(
|
| 133 |
+
[action_tokens.tolist()], time_horizon=action_horizon, action_dim=action_dim
|
| 134 |
+
)[0]
|
| 135 |
+
|
| 136 |
+
def _act_tokens_to_paligemma_tokens(self, tokens: np.ndarray | list[int]) -> np.ndarray:
|
| 137 |
+
if isinstance(tokens, list):
|
| 138 |
+
tokens = np.array(tokens)
|
| 139 |
+
return self._paligemma_tokenizer.vocab_size() - 1 - self._fast_skip_tokens - tokens
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
###########################################################################
|
| 143 |
+
## The tokenizers below are used for RoboArena baseline implementations. ##
|
| 144 |
+
## They are *not* used for pi0-style models. ##
|
| 145 |
+
###########################################################################
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
class BinningTokenizer:
|
| 149 |
+
"""
|
| 150 |
+
Standard RT-2 / OpenVLA style binning tokenizer.
|
| 151 |
+
"""
|
| 152 |
+
|
| 153 |
+
def __init__(self, max_len: int = 256, n_bins: int = 256):
|
| 154 |
+
self._max_len = max_len
|
| 155 |
+
self._n_bins = n_bins
|
| 156 |
+
|
| 157 |
+
# Download base PaliGemma tokenizer
|
| 158 |
+
path = download.maybe_download("gs://big_vision/paligemma_tokenizer.model", gs={"token": "anon"})
|
| 159 |
+
with path.open("rb") as f:
|
| 160 |
+
self._paligemma_tokenizer = sentencepiece.SentencePieceProcessor(model_proto=f.read())
|
| 161 |
+
|
| 162 |
+
self._fast_skip_tokens = 128 # Skip last 128 tokens in PaliGemma vocab since they are special tokens
|
| 163 |
+
|
| 164 |
+
def tokenize(
|
| 165 |
+
self, prompt: str, state: np.ndarray, actions: np.ndarray | None
|
| 166 |
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
| 167 |
+
"""Tokenize a prompt and state into a sequence of tokens.
|
| 168 |
+
|
| 169 |
+
Args:
|
| 170 |
+
prompt: The text prompt to tokenize.
|
| 171 |
+
state: The state array to discretize and tokenize.
|
| 172 |
+
actions: Must be None. Action encoding is not currently supported.
|
| 173 |
+
|
| 174 |
+
Returns:
|
| 175 |
+
A tuple of (tokens, token_mask, ar_mask, targets).
|
| 176 |
+
|
| 177 |
+
Raises:
|
| 178 |
+
NotImplementedError: If actions is not None.
|
| 179 |
+
"""
|
| 180 |
+
cleaned_text = prompt.lower().strip().replace("_", " ")
|
| 181 |
+
|
| 182 |
+
# Convention: state gets discretized into 256 discrete bins (assumed range after normalization: [-1, 1])
|
| 183 |
+
discretized_state = np.digitize(state, bins=np.linspace(-1, 1, 256 + 1)[:-1]) - 1
|
| 184 |
+
|
| 185 |
+
# Convention: prefix includes prompt and string-representation of state, followed by ';'
|
| 186 |
+
state_str = " ".join(map(str, discretized_state))
|
| 187 |
+
prefix = f"Task: {cleaned_text}, State: {state_str};\n"
|
| 188 |
+
prefix_tokens = self._paligemma_tokenizer.encode(prefix, add_bos=True)
|
| 189 |
+
|
| 190 |
+
if actions is not None:
|
| 191 |
+
raise NotImplementedError("BinningTokenizer does not support encoding actions atm (only for inference use)")
|
| 192 |
+
postfix_tokens = []
|
| 193 |
+
|
| 194 |
+
# Create output token sequence & masks
|
| 195 |
+
# AR mask is 0 on prefix (bidirectional attention) and 1 on postfix (causal attention to all previous tokens)
|
| 196 |
+
tokens = prefix_tokens + postfix_tokens
|
| 197 |
+
token_mask = [True] * len(tokens)
|
| 198 |
+
ar_mask = [0] * len(prefix_tokens) + [1] * len(postfix_tokens)
|
| 199 |
+
loss_mask = [False] * len(prefix_tokens) + [True] * len(postfix_tokens) # Loss on postfix only
|
| 200 |
+
|
| 201 |
+
# Pad tokens to max length
|
| 202 |
+
tokens_len = len(tokens)
|
| 203 |
+
if tokens_len < self._max_len:
|
| 204 |
+
padding = [False] * (self._max_len - tokens_len)
|
| 205 |
+
tokens = tokens + padding
|
| 206 |
+
token_mask = token_mask + padding
|
| 207 |
+
ar_mask = ar_mask + padding
|
| 208 |
+
loss_mask = loss_mask + padding
|
| 209 |
+
else:
|
| 210 |
+
if len(tokens) > self._max_len:
|
| 211 |
+
logging.warning(
|
| 212 |
+
f"Token length ({len(tokens)}) exceeds max length ({self._max_len}), truncating. "
|
| 213 |
+
"Consider increasing the `max_token_len` in your model config if this happens frequently."
|
| 214 |
+
)
|
| 215 |
+
tokens = tokens[: self._max_len]
|
| 216 |
+
token_mask = token_mask[: self._max_len]
|
| 217 |
+
ar_mask = ar_mask[: self._max_len]
|
| 218 |
+
loss_mask = loss_mask[: self._max_len]
|
| 219 |
+
|
| 220 |
+
return np.asarray(tokens), np.asarray(token_mask), np.asarray(ar_mask), np.asarray(loss_mask)
|
| 221 |
+
|
| 222 |
+
def extract_actions(self, tokens: np.ndarray, action_horizon: int, action_dim: int) -> np.ndarray:
|
| 223 |
+
# Decode predicted output tokens
|
| 224 |
+
decoded_tokens = self._paligemma_tokenizer.decode(tokens.tolist())
|
| 225 |
+
|
| 226 |
+
# Extract actions from FAST model outputs
|
| 227 |
+
if "Action: " not in decoded_tokens:
|
| 228 |
+
return np.zeros((action_horizon, action_dim), dtype=np.float32)
|
| 229 |
+
|
| 230 |
+
# Extract actions from decoded tokens
|
| 231 |
+
raw_action_tokens = np.array(
|
| 232 |
+
self._paligemma_tokenizer.encode(decoded_tokens.split("Action: ")[1].split("|")[0].strip())
|
| 233 |
+
)
|
| 234 |
+
action_tokens = self._act_tokens_to_paligemma_tokens(raw_action_tokens)
|
| 235 |
+
if len(action_tokens) < action_horizon * action_dim:
|
| 236 |
+
return np.zeros([action_horizon, action_dim], dtype=np.float32)
|
| 237 |
+
action_tokens = action_tokens[: (action_horizon * action_dim)].reshape([action_horizon, action_dim])
|
| 238 |
+
return action_tokens / self._n_bins * 2 - 1
|
| 239 |
+
|
| 240 |
+
def _act_tokens_to_paligemma_tokens(self, tokens: np.ndarray | list[int]) -> np.ndarray:
|
| 241 |
+
if isinstance(tokens, list):
|
| 242 |
+
tokens = np.array(tokens)
|
| 243 |
+
return self._paligemma_tokenizer.vocab_size() - 1 - self._fast_skip_tokens - tokens
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
class FSQTokenizer:
|
| 247 |
+
"""
|
| 248 |
+
FSQ tokenizer from the FAST paper baselines.
|
| 249 |
+
"""
|
| 250 |
+
|
| 251 |
+
def __init__(self, max_len: int = 256, fsq_tokenizer_path: str | None = None):
|
| 252 |
+
self._max_len = max_len
|
| 253 |
+
|
| 254 |
+
assert fsq_tokenizer_path is not None, "fsq_tokenizer_path must be provided"
|
| 255 |
+
# Download tokenizer
|
| 256 |
+
path = download.maybe_download(fsq_tokenizer_path)
|
| 257 |
+
tok_path = os.path.join(path, os.listdir(path)[0])
|
| 258 |
+
|
| 259 |
+
# Split step from path
|
| 260 |
+
step = int(tok_path.split("/")[-1])
|
| 261 |
+
base_path = tok_path.rsplit("/", 1)[0]
|
| 262 |
+
|
| 263 |
+
mgr = ocp.CheckpointManager(
|
| 264 |
+
base_path,
|
| 265 |
+
item_handlers={
|
| 266 |
+
"params": ocp.StandardCheckpointHandler(),
|
| 267 |
+
"opt_state": ocp.StandardCheckpointHandler(),
|
| 268 |
+
"config": ocp.JsonCheckpointHandler(),
|
| 269 |
+
},
|
| 270 |
+
options=ocp.CheckpointManagerOptions(max_to_keep=1),
|
| 271 |
+
)
|
| 272 |
+
|
| 273 |
+
try:
|
| 274 |
+
restored = mgr.restore(
|
| 275 |
+
step, args=ocp.args.Composite(config=ocp.args.JsonRestore(), params=ocp.args.StandardRestore())
|
| 276 |
+
)
|
| 277 |
+
config = restored["config"]
|
| 278 |
+
self._params = restored["params"]
|
| 279 |
+
self._fsq_tokenizer = fsq_tokenizer.FsqAttentionTokenizer(**config)
|
| 280 |
+
except Exception as e:
|
| 281 |
+
raise RuntimeError(
|
| 282 |
+
f"Failed to load FSQ tokenizer checkpoint from {fsq_tokenizer_path}. Error: {e!s}"
|
| 283 |
+
) from e
|
| 284 |
+
|
| 285 |
+
# Compile tokenize and detokenize functions
|
| 286 |
+
self._tokenize_fn = jax.jit(
|
| 287 |
+
lambda params, x: self._fsq_tokenizer.apply({"params": params}, x, method=self._fsq_tokenizer.tokenize)
|
| 288 |
+
)
|
| 289 |
+
self._detokenize_fn = jax.jit(
|
| 290 |
+
lambda params, x: self._fsq_tokenizer.apply({"params": params}, x, method=self._fsq_tokenizer.detokenize)
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
# Download base PaliGemma tokenizer
|
| 294 |
+
path = download.maybe_download("gs://big_vision/paligemma_tokenizer.model", gs={"token": "anon"})
|
| 295 |
+
with path.open("rb") as f:
|
| 296 |
+
self._paligemma_tokenizer = sentencepiece.SentencePieceProcessor(model_proto=f.read())
|
| 297 |
+
|
| 298 |
+
self._fast_skip_tokens = 128 # Skip last 128 tokens in PaliGemma vocab since they are special tokens
|
| 299 |
+
|
| 300 |
+
def tokenize(
|
| 301 |
+
self, prompt: str, state: np.ndarray, actions: np.ndarray | None
|
| 302 |
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
| 303 |
+
cleaned_text = prompt.lower().strip().replace("_", " ")
|
| 304 |
+
|
| 305 |
+
# Convention: state gets discretized into 256 discrete bins (assumed range after normalization: [-1, 1])
|
| 306 |
+
discretized_state = np.digitize(state, bins=np.linspace(-1, 1, 256 + 1)[:-1]) - 1
|
| 307 |
+
|
| 308 |
+
# Convention: prefix includes prompt and string-representation of state, followed by ';'
|
| 309 |
+
state_str = " ".join(map(str, discretized_state))
|
| 310 |
+
prefix = f"Task: {cleaned_text}, State: {state_str};\n"
|
| 311 |
+
prefix_tokens = self._paligemma_tokenizer.encode(prefix, add_bos=True)
|
| 312 |
+
|
| 313 |
+
if actions is not None:
|
| 314 |
+
raise NotImplementedError("FSQTokenizer does not support encoding actions atm (only for inference use)")
|
| 315 |
+
postfix_tokens = []
|
| 316 |
+
|
| 317 |
+
# Create output token sequence & masks
|
| 318 |
+
# AR mask is 0 on prefix (bidirectional attention) and 1 on postfix (causal attention to all previous tokens)
|
| 319 |
+
tokens = prefix_tokens + postfix_tokens
|
| 320 |
+
token_mask = [True] * len(tokens)
|
| 321 |
+
ar_mask = [0] * len(prefix_tokens) + [1] * len(postfix_tokens)
|
| 322 |
+
loss_mask = [False] * len(prefix_tokens) + [True] * len(postfix_tokens) # Loss on postfix only
|
| 323 |
+
|
| 324 |
+
# Pad tokens to max length
|
| 325 |
+
tokens_len = len(tokens)
|
| 326 |
+
if tokens_len < self._max_len:
|
| 327 |
+
padding = [False] * (self._max_len - tokens_len)
|
| 328 |
+
tokens = tokens + padding
|
| 329 |
+
token_mask = token_mask + padding
|
| 330 |
+
ar_mask = ar_mask + padding
|
| 331 |
+
loss_mask = loss_mask + padding
|
| 332 |
+
else:
|
| 333 |
+
if len(tokens) > self._max_len:
|
| 334 |
+
logging.warning(
|
| 335 |
+
f"Token length ({len(tokens)}) exceeds max length ({self._max_len}), truncating. "
|
| 336 |
+
"Consider increasing the `max_token_len` in your model config if this happens frequently."
|
| 337 |
+
)
|
| 338 |
+
tokens = tokens[: self._max_len]
|
| 339 |
+
token_mask = token_mask[: self._max_len]
|
| 340 |
+
ar_mask = ar_mask[: self._max_len]
|
| 341 |
+
loss_mask = loss_mask[: self._max_len]
|
| 342 |
+
|
| 343 |
+
return np.asarray(tokens), np.asarray(token_mask), np.asarray(ar_mask), np.asarray(loss_mask)
|
| 344 |
+
|
| 345 |
+
def extract_actions(self, tokens: np.ndarray, action_horizon: int, action_dim: int) -> np.ndarray:
|
| 346 |
+
# Decode predicted output tokens
|
| 347 |
+
decoded_tokens = self._paligemma_tokenizer.decode(tokens.tolist())
|
| 348 |
+
|
| 349 |
+
# Extract actions from FAST model outputs
|
| 350 |
+
if "Action: " not in decoded_tokens:
|
| 351 |
+
return np.zeros((action_horizon, action_dim), dtype=np.float32)
|
| 352 |
+
|
| 353 |
+
# Extract actions from decoded tokens
|
| 354 |
+
raw_action_tokens = np.array(
|
| 355 |
+
self._paligemma_tokenizer.encode(decoded_tokens.split("Action: ")[1].split("|")[0].strip())
|
| 356 |
+
)
|
| 357 |
+
action_tokens = self._act_tokens_to_paligemma_tokens(raw_action_tokens)
|
| 358 |
+
try:
|
| 359 |
+
# Move computation to CPU and compile on-demand
|
| 360 |
+
device = jax.devices("cpu")[0]
|
| 361 |
+
with jax.default_device(device):
|
| 362 |
+
detok_act = self._detokenize_fn(self._params, action_tokens[None, ...])[0]
|
| 363 |
+
return detok_act[: action_horizon * action_dim].reshape([action_horizon, action_dim])
|
| 364 |
+
except Exception as e:
|
| 365 |
+
logging.warning(f"Error decoding FSQ: {e}")
|
| 366 |
+
return np.zeros((action_horizon, action_dim))
|
| 367 |
+
|
| 368 |
+
def _act_tokens_to_paligemma_tokens(self, tokens: np.ndarray | list[int]) -> np.ndarray:
|
| 369 |
+
if isinstance(tokens, list):
|
| 370 |
+
tokens = np.array(tokens)
|
| 371 |
+
return self._paligemma_tokenizer.vocab_size() - 1 - self._fast_skip_tokens - tokens
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/tokenizer_test.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
from openpi.models import tokenizer as _tokenizer
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def test_tokenize():
|
| 7 |
+
tokenizer = _tokenizer.PaligemmaTokenizer(max_len=10)
|
| 8 |
+
tokens, masks = tokenizer.tokenize("Hello, world!")
|
| 9 |
+
|
| 10 |
+
assert tokens.shape == (10,)
|
| 11 |
+
assert masks.shape == (10,)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_fast_tokenizer():
|
| 15 |
+
prompt = "Hello, world!"
|
| 16 |
+
state = np.random.rand(5).astype(np.float32)
|
| 17 |
+
action = np.random.rand(3, 2).astype(np.float32)
|
| 18 |
+
tokenizer = _tokenizer.FASTTokenizer(max_len=256)
|
| 19 |
+
tokens, token_masks, ar_masks, loss_masks = tokenizer.tokenize(prompt, state, action)
|
| 20 |
+
|
| 21 |
+
assert tokens.shape == (256,)
|
| 22 |
+
assert token_masks.shape == (256,)
|
| 23 |
+
assert ar_masks.shape == (256,)
|
| 24 |
+
assert loss_masks.shape == (256,)
|
| 25 |
+
|
| 26 |
+
act = tokenizer.extract_actions(tokens, 3, 2)
|
| 27 |
+
assert act.shape == (3, 2)
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/utils/__pycache__/fsq_tokenizer.cpython-311.pyc
ADDED
|
Binary file (28.6 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/utils/fsq_tokenizer.py
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
from typing import Any, Literal
|
| 3 |
+
|
| 4 |
+
import chex
|
| 5 |
+
from einops import einops
|
| 6 |
+
from flax import linen as nn
|
| 7 |
+
from flax.linen.module import Module
|
| 8 |
+
from flax.linen.module import compact
|
| 9 |
+
from flax.struct import dataclass
|
| 10 |
+
from flax.typing import Array
|
| 11 |
+
import jax
|
| 12 |
+
import jax.numpy as jnp
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class FsqCodebook(nn.Module):
|
| 16 |
+
input_dim: int
|
| 17 |
+
target_codebook_size: int
|
| 18 |
+
codebook_type: Literal["fsq", "lfq"]
|
| 19 |
+
|
| 20 |
+
_bins_per_dim: tuple[int] | None = None
|
| 21 |
+
|
| 22 |
+
@property
|
| 23 |
+
def bins_per_dim(self) -> tuple[int]:
|
| 24 |
+
if self._bins_per_dim is not None:
|
| 25 |
+
return self._bins_per_dim
|
| 26 |
+
|
| 27 |
+
if self.codebook_type == "fsq":
|
| 28 |
+
return self._get_bins_fsq(self.target_codebook_size)
|
| 29 |
+
elif self.codebook_type == "lfq": # noqa: RET505
|
| 30 |
+
return self._get_bins_lfq(self.target_codebook_size)
|
| 31 |
+
elif self.codebook_type == "custom":
|
| 32 |
+
return self._get_bins_custom(self.target_codebook_size)
|
| 33 |
+
else:
|
| 34 |
+
raise ValueError(f"Codebook type {self.codebook_type} not supported.")
|
| 35 |
+
|
| 36 |
+
@property
|
| 37 |
+
def place_values(self) -> jnp.ndarray:
|
| 38 |
+
place_values = [1]
|
| 39 |
+
for b in self.bins_per_dim[:-1]:
|
| 40 |
+
place_values.append(place_values[-1] * b)
|
| 41 |
+
return jnp.array(place_values)
|
| 42 |
+
|
| 43 |
+
@staticmethod
|
| 44 |
+
def _get_bins_fsq(target_codebook_size: int) -> tuple[int]:
|
| 45 |
+
"""
|
| 46 |
+
Get bins per dimension based on codebook size, from the original FSQ paper.
|
| 47 |
+
"""
|
| 48 |
+
if target_codebook_size == 2**8:
|
| 49 |
+
return (8, 6, 5)
|
| 50 |
+
elif target_codebook_size == 2**10: # noqa: RET505
|
| 51 |
+
return (8, 5, 5, 5)
|
| 52 |
+
elif target_codebook_size == 2**12:
|
| 53 |
+
return (7, 5, 5, 5, 5)
|
| 54 |
+
elif target_codebook_size == 2**14:
|
| 55 |
+
return (8, 8, 8, 6, 5)
|
| 56 |
+
elif target_codebook_size == 2**16:
|
| 57 |
+
return (8, 8, 8, 5, 5, 5)
|
| 58 |
+
else:
|
| 59 |
+
raise ValueError(f"Codebook size {target_codebook_size} not supported.")
|
| 60 |
+
|
| 61 |
+
@staticmethod
|
| 62 |
+
def _get_bins_custom(target_codebook_size: int) -> tuple[int]:
|
| 63 |
+
if target_codebook_size == 2**8:
|
| 64 |
+
return (16, 16)
|
| 65 |
+
elif target_codebook_size == 2**10: # noqa: RET505
|
| 66 |
+
return (32, 32)
|
| 67 |
+
elif target_codebook_size == 2**12:
|
| 68 |
+
return (64, 64)
|
| 69 |
+
elif target_codebook_size == 2**14:
|
| 70 |
+
return (128, 128)
|
| 71 |
+
elif target_codebook_size == 2**16:
|
| 72 |
+
return (256, 256)
|
| 73 |
+
return None
|
| 74 |
+
|
| 75 |
+
@staticmethod
|
| 76 |
+
def _get_bins_lfq(target_codebook_size: int) -> tuple[int]:
|
| 77 |
+
"""
|
| 78 |
+
Get bins per dimension according to the Lookup-Free Quantization paper (2 bins per dimension)
|
| 79 |
+
"""
|
| 80 |
+
assert target_codebook_size & (target_codebook_size - 1) == 0, "Codebook size should be a power of two for LFQ"
|
| 81 |
+
|
| 82 |
+
return (2,) * int(math.log2(target_codebook_size))
|
| 83 |
+
|
| 84 |
+
def setup(self):
|
| 85 |
+
self.proj_down = nn.Dense(len(self.bins_per_dim))
|
| 86 |
+
self.proj_up = nn.Dense(self.input_dim)
|
| 87 |
+
|
| 88 |
+
def __call__(self, inputs: jnp.ndarray) -> tuple[jnp.ndarray, jnp.ndarray]:
|
| 89 |
+
tokens, z = self.encode(inputs)
|
| 90 |
+
output = self.decode(tokens, z_grad=z)
|
| 91 |
+
return tokens, output
|
| 92 |
+
|
| 93 |
+
def encode(self, inputs: jnp.ndarray) -> tuple[jnp.ndarray, jnp.ndarray]:
|
| 94 |
+
bases = jnp.array(self.bins_per_dim)
|
| 95 |
+
|
| 96 |
+
x = self.proj_down(inputs)
|
| 97 |
+
z = jnp.tanh(x)
|
| 98 |
+
|
| 99 |
+
# Quantize
|
| 100 |
+
digits = jnp.round((z + 1) * (bases - 1) / 2).astype(jnp.int32)
|
| 101 |
+
tokens = self.undigitize(digits)
|
| 102 |
+
|
| 103 |
+
return tokens, z
|
| 104 |
+
|
| 105 |
+
def decode(self, tokens: jnp.ndarray, z_grad: jax.Array | None = None) -> jnp.ndarray:
|
| 106 |
+
bases = jnp.array(self.bins_per_dim)
|
| 107 |
+
digits = self.digitize(tokens)
|
| 108 |
+
|
| 109 |
+
z_q = digits / (bases - 1) * 2 - 1
|
| 110 |
+
|
| 111 |
+
if z_grad is not None:
|
| 112 |
+
chex.assert_equal_shape([z_q, z_grad])
|
| 113 |
+
z_q = jax.lax.stop_gradient(z_q - z_grad) + z_grad
|
| 114 |
+
|
| 115 |
+
return self.proj_up(z_q)
|
| 116 |
+
|
| 117 |
+
def undigitize(self, digits: jnp.ndarray) -> jnp.ndarray:
|
| 118 |
+
return jnp.sum(digits * jnp.array(self.place_values), axis=-1)
|
| 119 |
+
|
| 120 |
+
def digitize(self, tokens: jnp.ndarray) -> jnp.ndarray:
|
| 121 |
+
return (tokens[..., None] // jnp.array(self.place_values)) % jnp.array(self.bins_per_dim)
|
| 122 |
+
|
| 123 |
+
@property
|
| 124 |
+
def vocab_size(self) -> int:
|
| 125 |
+
return math.prod(self.bins_per_dim)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
class ResNetDownBlock(nn.Module):
|
| 129 |
+
stride: int = 1
|
| 130 |
+
n_filters: int = 64
|
| 131 |
+
dropout_rate: float = 0.0
|
| 132 |
+
group_size: int = 32
|
| 133 |
+
|
| 134 |
+
@nn.compact
|
| 135 |
+
def __call__(self, x: jnp.ndarray, *, train: bool = True) -> jnp.ndarray:
|
| 136 |
+
skip = x
|
| 137 |
+
|
| 138 |
+
if self.stride > 1 or x.shape[-1] != self.n_filters:
|
| 139 |
+
skip = nn.Conv(self.n_filters, (self.stride,), (self.stride,), "SAME")(skip)
|
| 140 |
+
|
| 141 |
+
x = nn.Conv(self.n_filters, (3,), (self.stride,), "SAME")(x)
|
| 142 |
+
x = nn.GroupNorm(num_groups=self.n_filters // self.group_size)(x)
|
| 143 |
+
x = nn.Dropout(self.dropout_rate)(x, deterministic=not train)
|
| 144 |
+
x = nn.relu(x)
|
| 145 |
+
x = nn.Conv(self.n_filters, (3,), (1,), "SAME")(x)
|
| 146 |
+
|
| 147 |
+
return skip + x
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
class ResNetUpBlock(nn.Module):
|
| 151 |
+
stride: int = 1
|
| 152 |
+
n_filters: int = 64
|
| 153 |
+
dropout_rate: float = 0.0
|
| 154 |
+
group_size: int = 32
|
| 155 |
+
|
| 156 |
+
@nn.compact
|
| 157 |
+
def __call__(self, x: jnp.ndarray, *, train: bool = True) -> jnp.ndarray:
|
| 158 |
+
skip = x
|
| 159 |
+
|
| 160 |
+
if self.stride > 1:
|
| 161 |
+
skip = nn.ConvTranspose(self.n_filters, (self.stride,), (self.stride,), "SAME")(skip)
|
| 162 |
+
|
| 163 |
+
x = nn.ConvTranspose(self.n_filters, (3,), (self.stride,), "SAME")(x)
|
| 164 |
+
x = nn.GroupNorm(num_groups=self.n_filters // self.group_size)(x)
|
| 165 |
+
x = nn.Dropout(self.dropout_rate)(x, deterministic=not train)
|
| 166 |
+
x = nn.relu(x)
|
| 167 |
+
x = nn.ConvTranspose(self.n_filters, (3,), (1,), "SAME")(x)
|
| 168 |
+
|
| 169 |
+
return skip + x
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
@dataclass
|
| 173 |
+
class LfqCodebookOutput:
|
| 174 |
+
tokens: jnp.ndarray
|
| 175 |
+
z: jnp.ndarray
|
| 176 |
+
z_q: jnp.ndarray
|
| 177 |
+
token_log_probs: jnp.ndarray
|
| 178 |
+
commit_loss: jnp.ndarray
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
class LookupFreeQuantization(nn.Module):
|
| 182 |
+
num_dims: int
|
| 183 |
+
latent_dim: int
|
| 184 |
+
|
| 185 |
+
def setup(self):
|
| 186 |
+
self.codebook = jnp.array([-1, 1])
|
| 187 |
+
self.activation = nn.tanh
|
| 188 |
+
|
| 189 |
+
self.project_down = nn.Dense(self.num_dims)
|
| 190 |
+
self.project_up = nn.Dense(self.latent_dim)
|
| 191 |
+
|
| 192 |
+
def encode(self, z: jnp.ndarray) -> jnp.ndarray:
|
| 193 |
+
z = self.project_down(z)
|
| 194 |
+
token_squared_distances = jnp.square(z[..., None] - self.codebook)
|
| 195 |
+
token_bits = jnp.argmin(token_squared_distances, axis=-1)
|
| 196 |
+
return jnp.sum(token_bits * (2 ** jnp.arange(self.num_dims)), axis=-1)
|
| 197 |
+
|
| 198 |
+
def decode(self, tokens: jnp.ndarray) -> jnp.ndarray:
|
| 199 |
+
token_bits = (tokens[..., None] & (2 ** jnp.arange(self.num_dims))).astype(jnp.int32)
|
| 200 |
+
return self.project_up(self.codebook[token_bits])
|
| 201 |
+
|
| 202 |
+
def loss(self, x: jnp.ndarray) -> LfqCodebookOutput:
|
| 203 |
+
z = self.project_down(x)
|
| 204 |
+
z = self.activation(z)
|
| 205 |
+
|
| 206 |
+
token_squared_distances = jnp.square(z[..., None] - self.codebook)
|
| 207 |
+
tokens = jnp.argmin(token_squared_distances, axis=-1)
|
| 208 |
+
|
| 209 |
+
token_bit_log_probs = -token_squared_distances
|
| 210 |
+
# Compute token log probs for tokens 0..2^num_dims-1 by summing corresponding log-probs
|
| 211 |
+
token_bit_expansions = jnp.bitwise_and(
|
| 212 |
+
jnp.arange(2**self.num_dims)[None, :], 2 ** jnp.arange(self.num_dims)[:, None]
|
| 213 |
+
).astype(jnp.int32)
|
| 214 |
+
token_log_probs = (
|
| 215 |
+
token_bit_log_probs[..., 0] @ (1 - token_bit_expansions)
|
| 216 |
+
+ token_bit_log_probs[..., 1] @ token_bit_expansions
|
| 217 |
+
) # (batch_size, num_tokens, 2 ** num_dims)
|
| 218 |
+
token_log_probs = jax.lax.stop_gradient(jax.nn.log_softmax(token_log_probs, axis=-1))
|
| 219 |
+
chex.assert_shape(token_log_probs, (*x.shape[:-1], 2**self.num_dims))
|
| 220 |
+
|
| 221 |
+
z_q = self.codebook[tokens]
|
| 222 |
+
commit_loss = jnp.square(z - z_q).mean()
|
| 223 |
+
z_q = jax.lax.stop_gradient(z_q - z) + z
|
| 224 |
+
|
| 225 |
+
z_q = self.project_up(z_q)
|
| 226 |
+
z = self.project_up(z)
|
| 227 |
+
|
| 228 |
+
tokens = jnp.sum(tokens * (len(self.codebook) ** jnp.arange(self.num_dims)), axis=-1)
|
| 229 |
+
return LfqCodebookOutput(
|
| 230 |
+
tokens=tokens,
|
| 231 |
+
z=z,
|
| 232 |
+
z_q=z_q,
|
| 233 |
+
token_log_probs=jnp.zeros(()),
|
| 234 |
+
commit_loss=commit_loss,
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def make_block_causal_attention_matrix(q: jnp.ndarray, k: jnp.ndarray, bs_q: int, bs_k: int) -> jnp.ndarray:
|
| 239 |
+
return nn.make_attention_mask(q, k, pairwise_fn=lambda x, y: jnp.greater_equal(x // bs_k, y // bs_q))
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
class GeGLU(Module):
|
| 243 |
+
"""Gated Linear Unit with GELU (GeGLU) activation function.
|
| 244 |
+
GeGLU is a Flax layer that combines a linear transformation with a GELU
|
| 245 |
+
activation function in a gating mechanism. It is often used in Transformer models
|
| 246 |
+
to provide non-linear capabilities while preserving a strong linear component.
|
| 247 |
+
|
| 248 |
+
Attributes:
|
| 249 |
+
features: the number of output features (default: None).
|
| 250 |
+
"""
|
| 251 |
+
|
| 252 |
+
output_dim: int = -1
|
| 253 |
+
|
| 254 |
+
@compact
|
| 255 |
+
def __call__(self, inputs: Array) -> Array:
|
| 256 |
+
"""Applies the GeGLU activation to the inputs.
|
| 257 |
+
Args:
|
| 258 |
+
inputs: the nd-array to apply the GeGLU activation function to.
|
| 259 |
+
Returns:
|
| 260 |
+
The transformed input.
|
| 261 |
+
"""
|
| 262 |
+
output_dim = inputs.shape[-1] if self.output_dim == -1 else self.output_dim
|
| 263 |
+
|
| 264 |
+
x = nn.Dense(output_dim * 2)(inputs)
|
| 265 |
+
x, gate = x[..., :output_dim], x[..., output_dim:]
|
| 266 |
+
return x * nn.gelu(gate)
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
class CrossAttentionLayer(nn.Module):
|
| 270 |
+
dropout_rate: float = 0.0
|
| 271 |
+
num_heads: int = None
|
| 272 |
+
causal: bool = False
|
| 273 |
+
mlp_ratio: float = 4.0
|
| 274 |
+
|
| 275 |
+
@nn.compact
|
| 276 |
+
def __call__(
|
| 277 |
+
self,
|
| 278 |
+
x: jnp.ndarray,
|
| 279 |
+
y: jnp.ndarray,
|
| 280 |
+
*,
|
| 281 |
+
mask_self: jnp.ndarray | None = None,
|
| 282 |
+
mask_cross: jnp.ndarray | None = None,
|
| 283 |
+
train: bool = True,
|
| 284 |
+
) -> jnp.ndarray:
|
| 285 |
+
d_embed = x.shape[-1]
|
| 286 |
+
seq_len_q = x.shape[-2]
|
| 287 |
+
seq_len_k = y.shape[-2]
|
| 288 |
+
|
| 289 |
+
if self.causal:
|
| 290 |
+
# One block size will be 1
|
| 291 |
+
bs_q = max(seq_len_q // seq_len_k, 1)
|
| 292 |
+
bs_k = max(seq_len_k // seq_len_q, 1)
|
| 293 |
+
|
| 294 |
+
mask_self = nn.make_causal_mask(x[..., 0])
|
| 295 |
+
mask_cross = make_block_causal_attention_matrix(x[..., 0], y[..., 0], bs_q, bs_k)
|
| 296 |
+
|
| 297 |
+
# Self-attention block
|
| 298 |
+
skip = x
|
| 299 |
+
x = nn.LayerNorm()(x)
|
| 300 |
+
x = nn.MultiHeadDotProductAttention(
|
| 301 |
+
num_heads=self.num_heads or d_embed // 64,
|
| 302 |
+
dropout_rate=self.dropout_rate,
|
| 303 |
+
deterministic=not train,
|
| 304 |
+
)(x, x, x, mask=mask_self)
|
| 305 |
+
x = skip + x
|
| 306 |
+
|
| 307 |
+
# Cross-attention block
|
| 308 |
+
skip = x
|
| 309 |
+
x = nn.LayerNorm()(x)
|
| 310 |
+
x = nn.MultiHeadDotProductAttention(
|
| 311 |
+
num_heads=self.num_heads or d_embed // 64,
|
| 312 |
+
dropout_rate=self.dropout_rate,
|
| 313 |
+
deterministic=not train,
|
| 314 |
+
)(x, y, y, mask=mask_cross)
|
| 315 |
+
x = skip + x
|
| 316 |
+
|
| 317 |
+
# MLP block
|
| 318 |
+
skip = x
|
| 319 |
+
x = nn.LayerNorm()(x)
|
| 320 |
+
x = nn.Dense(int(d_embed * self.mlp_ratio))(x)
|
| 321 |
+
x = nn.Dropout(self.dropout_rate)(x, deterministic=not train)
|
| 322 |
+
x = GeGLU()(x)
|
| 323 |
+
x = nn.Dense(d_embed)(x)
|
| 324 |
+
return skip + x
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
def sinusoidal_pe_init(_, shape: tuple[int, int]) -> jnp.ndarray:
|
| 328 |
+
seq_len, d_embed = shape
|
| 329 |
+
|
| 330 |
+
position = jnp.arange(0, seq_len, 1)
|
| 331 |
+
div_term = jnp.exp(jnp.arange(0, d_embed, 2) * -(jnp.log(10000.0) / d_embed))
|
| 332 |
+
return jnp.concatenate(
|
| 333 |
+
[
|
| 334 |
+
jnp.sin(position[:, jnp.newaxis] * div_term),
|
| 335 |
+
jnp.cos(position[:, jnp.newaxis] * div_term),
|
| 336 |
+
],
|
| 337 |
+
axis=-1,
|
| 338 |
+
)
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
class TokenizerEncoderDecoder(nn.Module):
|
| 342 |
+
num_tokens: int
|
| 343 |
+
num_cross_tokens: int
|
| 344 |
+
num_layers: int
|
| 345 |
+
causal: bool
|
| 346 |
+
|
| 347 |
+
mlp_ratio: float = 4.0
|
| 348 |
+
use_state_conditioning: bool = False
|
| 349 |
+
|
| 350 |
+
@nn.compact
|
| 351 |
+
def __call__(
|
| 352 |
+
self,
|
| 353 |
+
y: jnp.ndarray,
|
| 354 |
+
*,
|
| 355 |
+
train: bool = True,
|
| 356 |
+
state_conditioning: jnp.ndarray | None = None,
|
| 357 |
+
mask: jnp.ndarray | None = None,
|
| 358 |
+
) -> jnp.ndarray:
|
| 359 |
+
x = self.param("q_embed", sinusoidal_pe_init, (self.num_tokens, y.shape[-1]))
|
| 360 |
+
x = jax.numpy.broadcast_to(x, y.shape[:-2] + x.shape[-2:])
|
| 361 |
+
|
| 362 |
+
if mask is not None:
|
| 363 |
+
# mask is (batch_dims..., num_cross_tokens)
|
| 364 |
+
chex.assert_equal_shape([y[..., 0], mask])
|
| 365 |
+
attn_mask = einops.repeat(mask, "... kv -> ... 1 q kv", q=self.num_tokens)
|
| 366 |
+
else:
|
| 367 |
+
attn_mask = jnp.ones((*y.shape[:-2], 1, self.num_tokens, self.num_cross_tokens))
|
| 368 |
+
|
| 369 |
+
if self.use_state_conditioning:
|
| 370 |
+
assert state_conditioning is not None, "State conditioning is required for this model."
|
| 371 |
+
state_embed = nn.Dense(y.shape[-1], name="state_proj")(state_conditioning)[..., None, :]
|
| 372 |
+
y = jnp.concatenate([y, state_embed], axis=-2)
|
| 373 |
+
attn_mask = jnp.concatenate([attn_mask, jnp.ones_like(attn_mask[..., 0:1])], axis=-1)
|
| 374 |
+
|
| 375 |
+
y = y + self.param("y_pos_enc", sinusoidal_pe_init, y.shape[-2:])
|
| 376 |
+
|
| 377 |
+
for _ in range(self.num_layers):
|
| 378 |
+
x = CrossAttentionLayer(causal=self.causal, mlp_ratio=self.mlp_ratio)(
|
| 379 |
+
x, y, train=train, mask_self=None, mask_cross=attn_mask
|
| 380 |
+
)
|
| 381 |
+
|
| 382 |
+
return x
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
class FsqAttentionTokenizer(nn.Module):
|
| 386 |
+
embed_dim: int
|
| 387 |
+
data_dim: int
|
| 388 |
+
data_horizon: int
|
| 389 |
+
num_tokens: int
|
| 390 |
+
num_layers: int
|
| 391 |
+
target_codebook_size: int
|
| 392 |
+
causal: bool = False
|
| 393 |
+
mlp_ratio: float = 2.0
|
| 394 |
+
|
| 395 |
+
bound: float | None = None
|
| 396 |
+
|
| 397 |
+
use_state_conditioning: bool = False
|
| 398 |
+
|
| 399 |
+
@property
|
| 400 |
+
def vocab_size(self) -> int:
|
| 401 |
+
return math.prod(FsqCodebook._get_bins_fsq(self.target_codebook_size)) # noqa: SLF001
|
| 402 |
+
|
| 403 |
+
def setup(self):
|
| 404 |
+
self.proj = nn.Dense(self.embed_dim)
|
| 405 |
+
self.encoder = TokenizerEncoderDecoder(
|
| 406 |
+
num_tokens=self.num_tokens,
|
| 407 |
+
num_cross_tokens=self.data_horizon,
|
| 408 |
+
num_layers=self.num_layers,
|
| 409 |
+
causal=self.causal,
|
| 410 |
+
use_state_conditioning=self.use_state_conditioning,
|
| 411 |
+
mlp_ratio=self.mlp_ratio,
|
| 412 |
+
)
|
| 413 |
+
self.codebook = FsqCodebook(
|
| 414 |
+
input_dim=self.embed_dim,
|
| 415 |
+
target_codebook_size=self.target_codebook_size,
|
| 416 |
+
codebook_type="custom",
|
| 417 |
+
)
|
| 418 |
+
self.decoder = TokenizerEncoderDecoder(
|
| 419 |
+
num_tokens=self.data_horizon,
|
| 420 |
+
num_cross_tokens=self.num_tokens,
|
| 421 |
+
num_layers=self.num_layers,
|
| 422 |
+
causal=self.causal,
|
| 423 |
+
use_state_conditioning=self.use_state_conditioning,
|
| 424 |
+
mlp_ratio=self.mlp_ratio,
|
| 425 |
+
)
|
| 426 |
+
|
| 427 |
+
self.proj_mean = nn.Dense(self.data_dim)
|
| 428 |
+
self.out_scale = self.param("out_scale", lambda _: jnp.full((), 1.0))
|
| 429 |
+
|
| 430 |
+
def tokenize(
|
| 431 |
+
self, action: jnp.ndarray, *, obs: jnp.ndarray | None = None, train: bool = False
|
| 432 |
+
) -> tuple[jnp.ndarray, jnp.ndarray]:
|
| 433 |
+
if self.bound is not None:
|
| 434 |
+
action = jnp.clip(action, -self.bound, self.bound)
|
| 435 |
+
|
| 436 |
+
x = self.proj(action)
|
| 437 |
+
x = self.encoder(x, train=train, state_conditioning=obs)
|
| 438 |
+
|
| 439 |
+
return self.codebook.encode(x)
|
| 440 |
+
|
| 441 |
+
def detokenize(self, tokens: jnp.ndarray, *, obs: jnp.ndarray | None = None) -> jnp.ndarray:
|
| 442 |
+
x = self.decoder(self.codebook.decode(tokens), state_conditioning=obs)
|
| 443 |
+
mean = self.proj_mean(x)
|
| 444 |
+
return mean * self.out_scale
|
| 445 |
+
|
| 446 |
+
def loss(
|
| 447 |
+
self, action: jnp.ndarray, *, obs: jnp.ndarray | None = None, train: bool = True
|
| 448 |
+
) -> tuple[jnp.ndarray, dict[str, jnp.ndarray]]:
|
| 449 |
+
# Encode
|
| 450 |
+
x = self.proj(action)
|
| 451 |
+
z = self.encoder(x, train=train, state_conditioning=obs)
|
| 452 |
+
|
| 453 |
+
# Quantize
|
| 454 |
+
tokens, z = self.codebook(z)
|
| 455 |
+
|
| 456 |
+
# Decode
|
| 457 |
+
x = self.decoder(z, train=train, state_conditioning=obs)
|
| 458 |
+
mean = self.proj_mean(x) * self.out_scale
|
| 459 |
+
|
| 460 |
+
mse = jnp.mean(jnp.square(action - mean))
|
| 461 |
+
mae = jnp.mean(jnp.abs(action - mean))
|
| 462 |
+
|
| 463 |
+
return mse, {
|
| 464 |
+
"mse": mse,
|
| 465 |
+
"mae": mae,
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
def __call__(self, *args: Any, **kwargs: Any) -> tuple[jnp.ndarray, dict[str, jnp.ndarray]]:
|
| 469 |
+
"""
|
| 470 |
+
Dummy for .init
|
| 471 |
+
"""
|
| 472 |
+
return self.loss(*args, **kwargs)
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models/vit.py
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 Google LLC.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""ViT implementation adapted from https://github.com/google-research/vision_transformer/blob/main/vit_jax/models_vit.py."""
|
| 15 |
+
|
| 16 |
+
from collections.abc import Callable
|
| 17 |
+
from typing import Any
|
| 18 |
+
|
| 19 |
+
import flax.linen as nn
|
| 20 |
+
import jax
|
| 21 |
+
import jax.numpy as jnp
|
| 22 |
+
|
| 23 |
+
from openpi.models import resnet as models_resnet
|
| 24 |
+
|
| 25 |
+
Array = Any
|
| 26 |
+
PRNGKey = Any
|
| 27 |
+
Shape = tuple[int]
|
| 28 |
+
Dtype = Any
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class IdentityLayer(nn.Module):
|
| 32 |
+
"""Identity layer, convenient for giving a name to an array."""
|
| 33 |
+
|
| 34 |
+
@nn.compact
|
| 35 |
+
def __call__(self, x):
|
| 36 |
+
return x
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class AddPositionEmbs(nn.Module):
|
| 40 |
+
"""Adds learned positional embeddings to the inputs.
|
| 41 |
+
|
| 42 |
+
Attributes:
|
| 43 |
+
posemb_init: positional embedding initializer.
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
posemb_init: Callable[[PRNGKey, Shape, Dtype], Array]
|
| 47 |
+
param_dtype: Dtype = jnp.float32
|
| 48 |
+
|
| 49 |
+
@nn.compact
|
| 50 |
+
def __call__(self, inputs):
|
| 51 |
+
"""Applies the AddPositionEmbs module.
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
inputs: Inputs to the layer.
|
| 55 |
+
|
| 56 |
+
Returns:
|
| 57 |
+
Output tensor with shape `(bs, timesteps, in_dim)`.
|
| 58 |
+
"""
|
| 59 |
+
# inputs.shape is (batch_size, seq_len, emb_dim).
|
| 60 |
+
assert inputs.ndim == 3, f"Number of dimensions should be 3, but it is: {inputs.ndim}"
|
| 61 |
+
pos_emb_shape = (1, inputs.shape[1], inputs.shape[2])
|
| 62 |
+
pe = self.param("pos_embedding", self.posemb_init, pos_emb_shape, self.param_dtype)
|
| 63 |
+
return inputs + pe
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class MlpBlock(nn.Module):
|
| 67 |
+
"""Transformer MLP / feed-forward block."""
|
| 68 |
+
|
| 69 |
+
mlp_dim: int
|
| 70 |
+
dtype: Dtype = jnp.float32
|
| 71 |
+
param_dtype: Dtype = jnp.float32
|
| 72 |
+
out_dim: int | None = None
|
| 73 |
+
dropout_rate: float = 0.1
|
| 74 |
+
kernel_init: Callable[[PRNGKey, Shape, Dtype], Array] = nn.initializers.xavier_uniform()
|
| 75 |
+
bias_init: Callable[[PRNGKey, Shape, Dtype], Array] = nn.initializers.normal(stddev=1e-6)
|
| 76 |
+
|
| 77 |
+
@nn.compact
|
| 78 |
+
def __call__(self, inputs, *, deterministic):
|
| 79 |
+
"""Applies Transformer MlpBlock module."""
|
| 80 |
+
actual_out_dim = inputs.shape[-1] if self.out_dim is None else self.out_dim
|
| 81 |
+
x = nn.Dense(
|
| 82 |
+
features=self.mlp_dim,
|
| 83 |
+
dtype=self.dtype,
|
| 84 |
+
param_dtype=self.param_dtype,
|
| 85 |
+
kernel_init=self.kernel_init,
|
| 86 |
+
bias_init=self.bias_init,
|
| 87 |
+
)( # pytype: disable=wrong-arg-types
|
| 88 |
+
inputs
|
| 89 |
+
)
|
| 90 |
+
x = nn.gelu(x)
|
| 91 |
+
x = nn.Dropout(rate=self.dropout_rate)(x, deterministic=deterministic)
|
| 92 |
+
output = nn.Dense(
|
| 93 |
+
features=actual_out_dim,
|
| 94 |
+
dtype=self.dtype,
|
| 95 |
+
param_dtype=self.param_dtype,
|
| 96 |
+
kernel_init=self.kernel_init,
|
| 97 |
+
bias_init=self.bias_init,
|
| 98 |
+
)( # pytype: disable=wrong-arg-types
|
| 99 |
+
x
|
| 100 |
+
)
|
| 101 |
+
return nn.Dropout(rate=self.dropout_rate)(output, deterministic=deterministic)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
class Encoder1DBlock(nn.Module):
|
| 105 |
+
"""Transformer encoder layer.
|
| 106 |
+
|
| 107 |
+
Attributes:
|
| 108 |
+
inputs: input data.
|
| 109 |
+
mlp_dim: dimension of the mlp on top of attention block.
|
| 110 |
+
dtype: the dtype of the computation (default: float32).
|
| 111 |
+
dropout_rate: dropout rate.
|
| 112 |
+
attention_dropout_rate: dropout for attention heads.
|
| 113 |
+
deterministic: bool, deterministic or not (to apply dropout).
|
| 114 |
+
num_heads: Number of heads in nn.MultiHeadDotProductAttention
|
| 115 |
+
"""
|
| 116 |
+
|
| 117 |
+
mlp_dim: int
|
| 118 |
+
num_heads: int
|
| 119 |
+
dtype: Dtype = jnp.float32
|
| 120 |
+
dropout_rate: float = 0.1
|
| 121 |
+
attention_dropout_rate: float = 0.1
|
| 122 |
+
|
| 123 |
+
@nn.compact
|
| 124 |
+
def __call__(self, inputs, deterministic):
|
| 125 |
+
"""Applies Encoder1DBlock module.
|
| 126 |
+
|
| 127 |
+
Args:
|
| 128 |
+
inputs: Inputs to the layer.
|
| 129 |
+
deterministic: Dropout will not be applied when set to true.
|
| 130 |
+
|
| 131 |
+
Returns:
|
| 132 |
+
output after transformer encoder block.
|
| 133 |
+
"""
|
| 134 |
+
|
| 135 |
+
# Attention block.
|
| 136 |
+
assert inputs.ndim == 3, f"Expected (batch, seq, hidden) got {inputs.shape}"
|
| 137 |
+
x = nn.LayerNorm(dtype=self.dtype)(inputs)
|
| 138 |
+
x = nn.MultiHeadDotProductAttention(
|
| 139 |
+
dtype=self.dtype,
|
| 140 |
+
kernel_init=nn.initializers.xavier_uniform(),
|
| 141 |
+
broadcast_dropout=False,
|
| 142 |
+
deterministic=deterministic,
|
| 143 |
+
dropout_rate=self.attention_dropout_rate,
|
| 144 |
+
num_heads=self.num_heads,
|
| 145 |
+
# why isn't this true by default???
|
| 146 |
+
force_fp32_for_softmax=True,
|
| 147 |
+
)(x, x)
|
| 148 |
+
x = nn.Dropout(rate=self.dropout_rate)(x, deterministic=deterministic)
|
| 149 |
+
x = x + inputs
|
| 150 |
+
|
| 151 |
+
# MLP block.
|
| 152 |
+
y = nn.LayerNorm(dtype=self.dtype)(x)
|
| 153 |
+
y = MlpBlock(mlp_dim=self.mlp_dim, dtype=self.dtype, dropout_rate=self.dropout_rate)(
|
| 154 |
+
y, deterministic=deterministic
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
return x + y, None
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
class Encoder(nn.Module):
|
| 161 |
+
"""Transformer Model Encoder for sequence to sequence translation.
|
| 162 |
+
|
| 163 |
+
Attributes:
|
| 164 |
+
num_layers: number of layers
|
| 165 |
+
mlp_dim: dimension of the mlp on top of attention block
|
| 166 |
+
num_heads: Number of heads in nn.MultiHeadDotProductAttention
|
| 167 |
+
dropout_rate: dropout rate.
|
| 168 |
+
attention_dropout_rate: dropout rate in self attention.
|
| 169 |
+
"""
|
| 170 |
+
|
| 171 |
+
dtype: jax.typing.DTypeLike
|
| 172 |
+
num_layers: int
|
| 173 |
+
mlp_dim: int
|
| 174 |
+
num_heads: int
|
| 175 |
+
dropout_rate: float = 0.1
|
| 176 |
+
attention_dropout_rate: float = 0.1
|
| 177 |
+
add_position_embedding: bool = True
|
| 178 |
+
|
| 179 |
+
@nn.compact
|
| 180 |
+
def __call__(self, x, *, train):
|
| 181 |
+
"""Applies Transformer model on the inputs.
|
| 182 |
+
|
| 183 |
+
Args:
|
| 184 |
+
x: Inputs to the layer.
|
| 185 |
+
train: Set to `True` when training.
|
| 186 |
+
|
| 187 |
+
Returns:
|
| 188 |
+
output of a transformer encoder.
|
| 189 |
+
"""
|
| 190 |
+
assert x.ndim == 3 # (batch, len, emb)
|
| 191 |
+
|
| 192 |
+
if self.add_position_embedding:
|
| 193 |
+
x = AddPositionEmbs(
|
| 194 |
+
posemb_init=nn.initializers.normal(stddev=0.02), # from BERT.
|
| 195 |
+
name="posembed_input",
|
| 196 |
+
)(x)
|
| 197 |
+
x = nn.Dropout(rate=self.dropout_rate)(x, deterministic=not train)
|
| 198 |
+
|
| 199 |
+
x = x.astype(self.dtype)
|
| 200 |
+
# Input Encoder
|
| 201 |
+
block = nn.remat(Encoder1DBlock, prevent_cse=False, static_argnums=(2,))
|
| 202 |
+
x, _ = nn.scan(
|
| 203 |
+
block,
|
| 204 |
+
variable_axes={"params": 0},
|
| 205 |
+
split_rngs={"params": True, "dropout": True},
|
| 206 |
+
in_axes=nn.broadcast,
|
| 207 |
+
length=self.num_layers,
|
| 208 |
+
)(
|
| 209 |
+
name="encoderblock",
|
| 210 |
+
mlp_dim=self.mlp_dim,
|
| 211 |
+
dropout_rate=self.dropout_rate,
|
| 212 |
+
attention_dropout_rate=self.attention_dropout_rate,
|
| 213 |
+
dtype=self.dtype,
|
| 214 |
+
num_heads=self.num_heads,
|
| 215 |
+
)(x, not train)
|
| 216 |
+
return nn.LayerNorm(name="encoder_norm", dtype=self.dtype)(x)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
class VisionTransformer(nn.Module):
|
| 220 |
+
"""VisionTransformer."""
|
| 221 |
+
|
| 222 |
+
dtype: jax.typing.DTypeLike
|
| 223 |
+
num_classes: int
|
| 224 |
+
patches: Any
|
| 225 |
+
transformer: Any
|
| 226 |
+
hidden_size: int
|
| 227 |
+
resnet: Any | None = None
|
| 228 |
+
representation_size: int | None = None
|
| 229 |
+
classifier: str = "token"
|
| 230 |
+
head_bias_init: float = 0.0
|
| 231 |
+
encoder: type[nn.Module] = Encoder
|
| 232 |
+
model_name: str | None = None
|
| 233 |
+
|
| 234 |
+
@nn.compact
|
| 235 |
+
def __call__(self, inputs, *, train):
|
| 236 |
+
x = inputs
|
| 237 |
+
# (Possibly partial) ResNet root.
|
| 238 |
+
if self.resnet is not None:
|
| 239 |
+
width = int(64 * self.resnet.width_factor)
|
| 240 |
+
|
| 241 |
+
# Root block.
|
| 242 |
+
x = models_resnet.StdConv(
|
| 243 |
+
features=width, kernel_size=(7, 7), strides=(2, 2), use_bias=False, name="conv_root"
|
| 244 |
+
)(x)
|
| 245 |
+
x = nn.GroupNorm(name="gn_root")(x)
|
| 246 |
+
x = nn.relu(x)
|
| 247 |
+
x = nn.max_pool(x, window_shape=(3, 3), strides=(2, 2), padding="SAME")
|
| 248 |
+
|
| 249 |
+
# ResNet stages.
|
| 250 |
+
if self.resnet.num_layers:
|
| 251 |
+
x = models_resnet.ResNetStage(
|
| 252 |
+
block_size=self.resnet.num_layers[0], nout=width, first_stride=(1, 1), name="block1"
|
| 253 |
+
)(x)
|
| 254 |
+
for i, block_size in enumerate(self.resnet.num_layers[1:], 1):
|
| 255 |
+
x = models_resnet.ResNetStage(
|
| 256 |
+
block_size=block_size, nout=width * 2**i, first_stride=(2, 2), name=f"block{i + 1}"
|
| 257 |
+
)(x)
|
| 258 |
+
|
| 259 |
+
n, h, w, c = x.shape
|
| 260 |
+
|
| 261 |
+
# We can merge s2d+emb into a single conv; it's the same.
|
| 262 |
+
x = nn.Conv(
|
| 263 |
+
features=self.hidden_size,
|
| 264 |
+
kernel_size=self.patches.size,
|
| 265 |
+
strides=self.patches.size,
|
| 266 |
+
padding="VALID",
|
| 267 |
+
name="embedding",
|
| 268 |
+
)(x)
|
| 269 |
+
|
| 270 |
+
# Here, x is a grid of embeddings.
|
| 271 |
+
|
| 272 |
+
# (Possibly partial) Transformer.
|
| 273 |
+
if self.transformer is not None:
|
| 274 |
+
n, h, w, c = x.shape
|
| 275 |
+
x = jnp.reshape(x, [n, h * w, c])
|
| 276 |
+
|
| 277 |
+
# If we want to add a class token, add it here.
|
| 278 |
+
if self.classifier in ["token", "token_unpooled"]:
|
| 279 |
+
cls = self.param("cls", nn.initializers.zeros, (1, 1, c))
|
| 280 |
+
cls = jnp.tile(cls, [n, 1, 1])
|
| 281 |
+
x = jnp.concatenate([cls, x], axis=1)
|
| 282 |
+
|
| 283 |
+
x = self.encoder(name="Transformer", **self.transformer, dtype=self.dtype)(x, train=train)
|
| 284 |
+
|
| 285 |
+
if self.classifier == "token":
|
| 286 |
+
x = x[:, 0]
|
| 287 |
+
elif self.classifier == "gap":
|
| 288 |
+
x = jnp.mean(x, axis=list(range(1, x.ndim - 1))) # (1,) or (1,2)
|
| 289 |
+
elif self.classifier in ["unpooled", "token_unpooled"]:
|
| 290 |
+
pass
|
| 291 |
+
else:
|
| 292 |
+
raise ValueError(f"Invalid classifier={self.classifier}")
|
| 293 |
+
|
| 294 |
+
if self.representation_size is not None:
|
| 295 |
+
x = nn.Dense(features=self.representation_size, name="pre_logits")(x)
|
| 296 |
+
x = nn.tanh(x)
|
| 297 |
+
else:
|
| 298 |
+
x = IdentityLayer(name="pre_logits")(x)
|
| 299 |
+
|
| 300 |
+
if self.num_classes:
|
| 301 |
+
x = nn.Dense(
|
| 302 |
+
features=self.num_classes,
|
| 303 |
+
name="head",
|
| 304 |
+
kernel_init=nn.initializers.zeros,
|
| 305 |
+
bias_init=nn.initializers.constant(self.head_bias_init),
|
| 306 |
+
)(x)
|
| 307 |
+
return x
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/__pycache__/gemma_pytorch.cpython-311.pyc
ADDED
|
Binary file (13.9 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/__pycache__/pi0_pytorch.cpython-311.pyc
ADDED
|
Binary file (24.8 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/__pycache__/preprocessing_pytorch.cpython-311.pyc
ADDED
|
Binary file (7 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/gemma_pytorch.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Literal
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
from torch import nn
|
| 5 |
+
from transformers import GemmaForCausalLM
|
| 6 |
+
from transformers import PaliGemmaForConditionalGeneration
|
| 7 |
+
from transformers.models.auto import CONFIG_MAPPING
|
| 8 |
+
from transformers.models.gemma import modeling_gemma
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class PaliGemmaWithExpertModel(nn.Module):
|
| 12 |
+
def __init__(
|
| 13 |
+
self,
|
| 14 |
+
vlm_config,
|
| 15 |
+
action_expert_config,
|
| 16 |
+
use_adarms=None,
|
| 17 |
+
precision: Literal["bfloat16", "float32"] = "bfloat16",
|
| 18 |
+
):
|
| 19 |
+
if use_adarms is None:
|
| 20 |
+
use_adarms = [False, False]
|
| 21 |
+
super().__init__()
|
| 22 |
+
|
| 23 |
+
vlm_config_hf = CONFIG_MAPPING["paligemma"]()
|
| 24 |
+
vlm_config_hf._vocab_size = 257152 # noqa: SLF001
|
| 25 |
+
vlm_config_hf.image_token_index = 257152
|
| 26 |
+
vlm_config_hf.text_config.hidden_size = vlm_config.width
|
| 27 |
+
vlm_config_hf.text_config.intermediate_size = vlm_config.mlp_dim
|
| 28 |
+
vlm_config_hf.text_config.num_attention_heads = vlm_config.num_heads
|
| 29 |
+
vlm_config_hf.text_config.head_dim = vlm_config.head_dim
|
| 30 |
+
vlm_config_hf.text_config.num_hidden_layers = vlm_config.depth
|
| 31 |
+
vlm_config_hf.text_config.num_key_value_heads = vlm_config.num_kv_heads
|
| 32 |
+
vlm_config_hf.text_config.hidden_activation = "gelu_pytorch_tanh"
|
| 33 |
+
vlm_config_hf.text_config.torch_dtype = "float32"
|
| 34 |
+
vlm_config_hf.text_config.vocab_size = 257152
|
| 35 |
+
vlm_config_hf.text_config.use_adarms = use_adarms[0]
|
| 36 |
+
vlm_config_hf.text_config.adarms_cond_dim = vlm_config.width if use_adarms[0] else None
|
| 37 |
+
vlm_config_hf.vision_config.intermediate_size = 4304
|
| 38 |
+
vlm_config_hf.vision_config.projection_dim = 2048
|
| 39 |
+
vlm_config_hf.vision_config.projector_hidden_act = "gelu_fast"
|
| 40 |
+
vlm_config_hf.vision_config.torch_dtype = "float32"
|
| 41 |
+
|
| 42 |
+
action_expert_config_hf = CONFIG_MAPPING["gemma"](
|
| 43 |
+
head_dim=action_expert_config.head_dim,
|
| 44 |
+
hidden_size=action_expert_config.width,
|
| 45 |
+
intermediate_size=action_expert_config.mlp_dim,
|
| 46 |
+
num_attention_heads=action_expert_config.num_heads,
|
| 47 |
+
num_hidden_layers=action_expert_config.depth,
|
| 48 |
+
num_key_value_heads=action_expert_config.num_kv_heads,
|
| 49 |
+
vocab_size=257152,
|
| 50 |
+
hidden_activation="gelu_pytorch_tanh",
|
| 51 |
+
torch_dtype="float32",
|
| 52 |
+
use_adarms=use_adarms[1],
|
| 53 |
+
adarms_cond_dim=action_expert_config.width if use_adarms[1] else None,
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
self.paligemma = PaliGemmaForConditionalGeneration(config=vlm_config_hf)
|
| 57 |
+
self.gemma_expert = GemmaForCausalLM(config=action_expert_config_hf)
|
| 58 |
+
self.gemma_expert.model.embed_tokens = None
|
| 59 |
+
|
| 60 |
+
self.to_bfloat16_for_selected_params(precision)
|
| 61 |
+
|
| 62 |
+
def to_bfloat16_for_selected_params(self, precision: Literal["bfloat16", "float32"] = "bfloat16"):
|
| 63 |
+
if precision == "bfloat16":
|
| 64 |
+
self.to(dtype=torch.bfloat16)
|
| 65 |
+
elif precision == "float32":
|
| 66 |
+
self.to(dtype=torch.float32)
|
| 67 |
+
return
|
| 68 |
+
else:
|
| 69 |
+
raise ValueError(f"Invalid precision: {precision}")
|
| 70 |
+
|
| 71 |
+
params_to_keep_float32 = [
|
| 72 |
+
"vision_tower.vision_model.embeddings.patch_embedding.weight",
|
| 73 |
+
"vision_tower.vision_model.embeddings.patch_embedding.bias",
|
| 74 |
+
"vision_tower.vision_model.embeddings.position_embedding.weight",
|
| 75 |
+
"input_layernorm",
|
| 76 |
+
"post_attention_layernorm",
|
| 77 |
+
"model.norm",
|
| 78 |
+
]
|
| 79 |
+
|
| 80 |
+
for name, param in self.named_parameters():
|
| 81 |
+
if any(selector in name for selector in params_to_keep_float32):
|
| 82 |
+
param.data = param.data.to(dtype=torch.float32)
|
| 83 |
+
|
| 84 |
+
def embed_image(self, image: torch.Tensor):
|
| 85 |
+
return self.paligemma.model.get_image_features(image)
|
| 86 |
+
|
| 87 |
+
def embed_language_tokens(self, tokens: torch.Tensor):
|
| 88 |
+
return self.paligemma.language_model.embed_tokens(tokens)
|
| 89 |
+
|
| 90 |
+
def forward(
|
| 91 |
+
self,
|
| 92 |
+
attention_mask: torch.Tensor | None = None,
|
| 93 |
+
position_ids: torch.LongTensor | None = None,
|
| 94 |
+
past_key_values: list[torch.FloatTensor] | None = None,
|
| 95 |
+
inputs_embeds: list[torch.FloatTensor] | None = None,
|
| 96 |
+
use_cache: bool | None = None,
|
| 97 |
+
adarms_cond: list[torch.Tensor] | None = None,
|
| 98 |
+
):
|
| 99 |
+
if adarms_cond is None:
|
| 100 |
+
adarms_cond = [None, None]
|
| 101 |
+
if inputs_embeds[1] is None:
|
| 102 |
+
prefix_output = self.paligemma.language_model.forward(
|
| 103 |
+
inputs_embeds=inputs_embeds[0],
|
| 104 |
+
attention_mask=attention_mask,
|
| 105 |
+
position_ids=position_ids,
|
| 106 |
+
past_key_values=past_key_values,
|
| 107 |
+
use_cache=use_cache,
|
| 108 |
+
adarms_cond=adarms_cond[0] if adarms_cond is not None else None,
|
| 109 |
+
)
|
| 110 |
+
prefix_past_key_values = prefix_output.past_key_values
|
| 111 |
+
prefix_output = prefix_output.last_hidden_state
|
| 112 |
+
suffix_output = None
|
| 113 |
+
elif inputs_embeds[0] is None:
|
| 114 |
+
suffix_output = self.gemma_expert.model.forward(
|
| 115 |
+
inputs_embeds=inputs_embeds[1],
|
| 116 |
+
attention_mask=attention_mask,
|
| 117 |
+
position_ids=position_ids,
|
| 118 |
+
past_key_values=past_key_values,
|
| 119 |
+
use_cache=use_cache,
|
| 120 |
+
adarms_cond=adarms_cond[1] if adarms_cond is not None else None,
|
| 121 |
+
)
|
| 122 |
+
suffix_output = suffix_output.last_hidden_state
|
| 123 |
+
prefix_output = None
|
| 124 |
+
prefix_past_key_values = None
|
| 125 |
+
else:
|
| 126 |
+
models = [self.paligemma.language_model, self.gemma_expert.model]
|
| 127 |
+
num_layers = self.paligemma.config.text_config.num_hidden_layers
|
| 128 |
+
|
| 129 |
+
# Check if gradient checkpointing is enabled for any of the models
|
| 130 |
+
use_gradient_checkpointing = (
|
| 131 |
+
hasattr(self.gemma_expert.model, "gradient_checkpointing")
|
| 132 |
+
and self.gemma_expert.model.gradient_checkpointing
|
| 133 |
+
and self.training
|
| 134 |
+
) or (hasattr(self, "gradient_checkpointing") and self.gradient_checkpointing and self.training)
|
| 135 |
+
|
| 136 |
+
# Force enable gradient checkpointing if we're in training mode and the model supports it
|
| 137 |
+
if self.training and hasattr(self.gemma_expert.model, "gradient_checkpointing"):
|
| 138 |
+
if not self.gemma_expert.model.gradient_checkpointing:
|
| 139 |
+
print("Forcing gradient checkpointing to be enabled for Gemma expert model")
|
| 140 |
+
self.gemma_expert.model.gradient_checkpointing = True
|
| 141 |
+
use_gradient_checkpointing = True
|
| 142 |
+
|
| 143 |
+
# Debug gradient checkpointing status
|
| 144 |
+
if hasattr(self, "_debug_gc_printed") and not self._debug_gc_printed:
|
| 145 |
+
print(f"Gemma expert model gradient checkpointing: {use_gradient_checkpointing}")
|
| 146 |
+
print(f"Model training mode: {self.training}")
|
| 147 |
+
print(
|
| 148 |
+
f"Gemma expert model has gradient_checkpointing attr: {hasattr(self.gemma_expert.model, 'gradient_checkpointing')}"
|
| 149 |
+
)
|
| 150 |
+
if hasattr(self.gemma_expert.model, "gradient_checkpointing"):
|
| 151 |
+
print(
|
| 152 |
+
f"Gemma expert model gradient_checkpointing value: {self.gemma_expert.model.gradient_checkpointing}"
|
| 153 |
+
)
|
| 154 |
+
self._debug_gc_printed = True
|
| 155 |
+
|
| 156 |
+
# Define the complete layer computation function for gradient checkpointing
|
| 157 |
+
def compute_layer_complete(layer_idx, inputs_embeds, attention_mask, position_ids, adarms_cond):
|
| 158 |
+
models = [self.paligemma.language_model, self.gemma_expert.model]
|
| 159 |
+
|
| 160 |
+
query_states = []
|
| 161 |
+
key_states = []
|
| 162 |
+
value_states = []
|
| 163 |
+
gates = []
|
| 164 |
+
for i, hidden_states in enumerate(inputs_embeds):
|
| 165 |
+
layer = models[i].layers[layer_idx]
|
| 166 |
+
hidden_states, gate = layer.input_layernorm(hidden_states, cond=adarms_cond[i]) # noqa: PLW2901
|
| 167 |
+
gates.append(gate)
|
| 168 |
+
|
| 169 |
+
input_shape = hidden_states.shape[:-1]
|
| 170 |
+
hidden_shape = (*input_shape, -1, layer.self_attn.head_dim)
|
| 171 |
+
query_state = layer.self_attn.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 172 |
+
key_state = layer.self_attn.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 173 |
+
value_state = layer.self_attn.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 174 |
+
|
| 175 |
+
query_states.append(query_state)
|
| 176 |
+
key_states.append(key_state)
|
| 177 |
+
value_states.append(value_state)
|
| 178 |
+
|
| 179 |
+
# Concatenate and process attention
|
| 180 |
+
query_states = torch.cat(query_states, dim=2)
|
| 181 |
+
key_states = torch.cat(key_states, dim=2)
|
| 182 |
+
value_states = torch.cat(value_states, dim=2)
|
| 183 |
+
|
| 184 |
+
dummy_tensor = torch.zeros(
|
| 185 |
+
query_states.shape[0],
|
| 186 |
+
query_states.shape[2],
|
| 187 |
+
query_states.shape[-1],
|
| 188 |
+
device=query_states.device,
|
| 189 |
+
dtype=query_states.dtype,
|
| 190 |
+
)
|
| 191 |
+
cos, sin = self.paligemma.model.language_model.rotary_emb(dummy_tensor, position_ids)
|
| 192 |
+
query_states, key_states = modeling_gemma.apply_rotary_pos_emb(
|
| 193 |
+
query_states, key_states, cos, sin, unsqueeze_dim=1
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
batch_size = query_states.shape[0]
|
| 197 |
+
scaling = self.paligemma.language_model.layers[layer_idx].self_attn.scaling
|
| 198 |
+
|
| 199 |
+
# Attention computation
|
| 200 |
+
att_output, _ = modeling_gemma.eager_attention_forward(
|
| 201 |
+
self.paligemma.language_model.layers[layer_idx].self_attn,
|
| 202 |
+
query_states,
|
| 203 |
+
key_states,
|
| 204 |
+
value_states,
|
| 205 |
+
attention_mask,
|
| 206 |
+
scaling,
|
| 207 |
+
)
|
| 208 |
+
# Get head_dim from the current layer, not from the model
|
| 209 |
+
head_dim = self.paligemma.language_model.layers[layer_idx].self_attn.head_dim
|
| 210 |
+
att_output = att_output.reshape(batch_size, -1, 1 * 8 * head_dim)
|
| 211 |
+
|
| 212 |
+
# Process layer outputs
|
| 213 |
+
outputs_embeds = []
|
| 214 |
+
start_pos = 0
|
| 215 |
+
for i, hidden_states in enumerate(inputs_embeds):
|
| 216 |
+
layer = models[i].layers[layer_idx]
|
| 217 |
+
end_pos = start_pos + hidden_states.shape[1]
|
| 218 |
+
|
| 219 |
+
if att_output.dtype != layer.self_attn.o_proj.weight.dtype:
|
| 220 |
+
att_output = att_output.to(layer.self_attn.o_proj.weight.dtype)
|
| 221 |
+
out_emb = layer.self_attn.o_proj(att_output[:, start_pos:end_pos])
|
| 222 |
+
|
| 223 |
+
# first residual
|
| 224 |
+
out_emb = modeling_gemma._gated_residual(hidden_states, out_emb, gates[i]) # noqa: SLF001
|
| 225 |
+
after_first_residual = out_emb.clone()
|
| 226 |
+
out_emb, gate = layer.post_attention_layernorm(out_emb, cond=adarms_cond[i])
|
| 227 |
+
# Convert to bfloat16 if the next layer (mlp) uses bfloat16
|
| 228 |
+
if layer.mlp.up_proj.weight.dtype == torch.bfloat16:
|
| 229 |
+
out_emb = out_emb.to(dtype=torch.bfloat16)
|
| 230 |
+
|
| 231 |
+
out_emb = layer.mlp(out_emb)
|
| 232 |
+
# second residual
|
| 233 |
+
out_emb = modeling_gemma._gated_residual(after_first_residual, out_emb, gate) # noqa: SLF001
|
| 234 |
+
outputs_embeds.append(out_emb)
|
| 235 |
+
start_pos = end_pos
|
| 236 |
+
|
| 237 |
+
return outputs_embeds
|
| 238 |
+
|
| 239 |
+
# Process all layers with gradient checkpointing if enabled
|
| 240 |
+
for layer_idx in range(num_layers):
|
| 241 |
+
if use_gradient_checkpointing:
|
| 242 |
+
inputs_embeds = torch.utils.checkpoint.checkpoint(
|
| 243 |
+
compute_layer_complete,
|
| 244 |
+
layer_idx,
|
| 245 |
+
inputs_embeds,
|
| 246 |
+
attention_mask,
|
| 247 |
+
position_ids,
|
| 248 |
+
adarms_cond,
|
| 249 |
+
use_reentrant=False,
|
| 250 |
+
preserve_rng_state=False,
|
| 251 |
+
)
|
| 252 |
+
else:
|
| 253 |
+
inputs_embeds = compute_layer_complete(
|
| 254 |
+
layer_idx, inputs_embeds, attention_mask, position_ids, adarms_cond
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
# Old code removed - now using compute_layer_complete function above
|
| 258 |
+
|
| 259 |
+
# final norm
|
| 260 |
+
# Define final norm computation function for gradient checkpointing
|
| 261 |
+
def compute_final_norms(inputs_embeds, adarms_cond):
|
| 262 |
+
outputs_embeds = []
|
| 263 |
+
for i, hidden_states in enumerate(inputs_embeds):
|
| 264 |
+
out_emb, _ = models[i].norm(hidden_states, cond=adarms_cond[i])
|
| 265 |
+
outputs_embeds.append(out_emb)
|
| 266 |
+
return outputs_embeds
|
| 267 |
+
|
| 268 |
+
# Apply gradient checkpointing to final norm if enabled
|
| 269 |
+
if use_gradient_checkpointing:
|
| 270 |
+
outputs_embeds = torch.utils.checkpoint.checkpoint(
|
| 271 |
+
compute_final_norms, inputs_embeds, adarms_cond, use_reentrant=False, preserve_rng_state=False
|
| 272 |
+
)
|
| 273 |
+
else:
|
| 274 |
+
outputs_embeds = compute_final_norms(inputs_embeds, adarms_cond)
|
| 275 |
+
|
| 276 |
+
prefix_output = outputs_embeds[0]
|
| 277 |
+
suffix_output = outputs_embeds[1]
|
| 278 |
+
prefix_past_key_values = None
|
| 279 |
+
|
| 280 |
+
return [prefix_output, suffix_output], prefix_past_key_values
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/pi0_pytorch.py
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import math
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from torch import Tensor
|
| 6 |
+
from torch import nn
|
| 7 |
+
import torch.nn.functional as F # noqa: N812
|
| 8 |
+
|
| 9 |
+
import openpi.models.gemma as _gemma
|
| 10 |
+
from openpi.models_pytorch.gemma_pytorch import PaliGemmaWithExpertModel
|
| 11 |
+
import openpi.models_pytorch.preprocessing_pytorch as _preprocessing
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def get_safe_dtype(target_dtype, device_type):
|
| 15 |
+
"""Get a safe dtype for the given device type."""
|
| 16 |
+
if device_type == "cpu":
|
| 17 |
+
# CPU doesn't support bfloat16, use float32 instead
|
| 18 |
+
if target_dtype == torch.bfloat16:
|
| 19 |
+
return torch.float32
|
| 20 |
+
if target_dtype == torch.float64:
|
| 21 |
+
return torch.float64
|
| 22 |
+
return target_dtype
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def create_sinusoidal_pos_embedding(
|
| 26 |
+
time: torch.tensor, dimension: int, min_period: float, max_period: float, device="cpu"
|
| 27 |
+
) -> Tensor:
|
| 28 |
+
"""Computes sine-cosine positional embedding vectors for scalar positions."""
|
| 29 |
+
if dimension % 2 != 0:
|
| 30 |
+
raise ValueError(f"dimension ({dimension}) must be divisible by 2")
|
| 31 |
+
|
| 32 |
+
if time.ndim != 1:
|
| 33 |
+
raise ValueError("The time tensor is expected to be of shape `(batch_size, )`.")
|
| 34 |
+
|
| 35 |
+
dtype = get_safe_dtype(torch.float64, device.type)
|
| 36 |
+
fraction = torch.linspace(0.0, 1.0, dimension // 2, dtype=dtype, device=device)
|
| 37 |
+
period = min_period * (max_period / min_period) ** fraction
|
| 38 |
+
|
| 39 |
+
# Compute the outer product
|
| 40 |
+
scaling_factor = 1.0 / period * 2 * math.pi
|
| 41 |
+
sin_input = scaling_factor[None, :] * time[:, None]
|
| 42 |
+
return torch.cat([torch.sin(sin_input), torch.cos(sin_input)], dim=1)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def sample_beta(alpha, beta, bsize, device):
|
| 46 |
+
alpha_t = torch.as_tensor(alpha, dtype=torch.float32, device=device)
|
| 47 |
+
beta_t = torch.as_tensor(beta, dtype=torch.float32, device=device)
|
| 48 |
+
dist = torch.distributions.Beta(alpha_t, beta_t)
|
| 49 |
+
return dist.sample((bsize,))
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def make_att_2d_masks(pad_masks, att_masks):
|
| 53 |
+
"""Copied from big_vision.
|
| 54 |
+
|
| 55 |
+
Tokens can attend to valid inputs tokens which have a cumulative mask_ar
|
| 56 |
+
smaller or equal to theirs. This way `mask_ar` int[B, N] can be used to
|
| 57 |
+
setup several types of attention, for example:
|
| 58 |
+
|
| 59 |
+
[[1 1 1 1 1 1]]: pure causal attention.
|
| 60 |
+
|
| 61 |
+
[[0 0 0 1 1 1]]: prefix-lm attention. The first 3 tokens can attend between
|
| 62 |
+
themselves and the last 3 tokens have a causal attention. The first
|
| 63 |
+
entry could also be a 1 without changing behaviour.
|
| 64 |
+
|
| 65 |
+
[[1 0 1 0 1 0 0 1 0 0]]: causal attention between 4 blocks. Tokens of a
|
| 66 |
+
block can attend all previous blocks and all tokens on the same block.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
input_mask: bool[B, N] true if its part of the input, false if padding.
|
| 70 |
+
mask_ar: int32[B, N] mask that's 1 where previous tokens cannot depend on
|
| 71 |
+
it and 0 where it shares the same attention mask as the previous token.
|
| 72 |
+
"""
|
| 73 |
+
if att_masks.ndim != 2:
|
| 74 |
+
raise ValueError(att_masks.ndim)
|
| 75 |
+
if pad_masks.ndim != 2:
|
| 76 |
+
raise ValueError(pad_masks.ndim)
|
| 77 |
+
|
| 78 |
+
cumsum = torch.cumsum(att_masks, dim=1)
|
| 79 |
+
att_2d_masks = cumsum[:, None, :] <= cumsum[:, :, None]
|
| 80 |
+
pad_2d_masks = pad_masks[:, None, :] * pad_masks[:, :, None]
|
| 81 |
+
return att_2d_masks & pad_2d_masks
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class PI0Pytorch(nn.Module):
|
| 85 |
+
def __init__(self, config):
|
| 86 |
+
super().__init__()
|
| 87 |
+
self.config = config
|
| 88 |
+
self.pi05 = config.pi05
|
| 89 |
+
|
| 90 |
+
paligemma_config = _gemma.get_config(config.paligemma_variant)
|
| 91 |
+
action_expert_config = _gemma.get_config(config.action_expert_variant)
|
| 92 |
+
|
| 93 |
+
self.paligemma_with_expert = PaliGemmaWithExpertModel(
|
| 94 |
+
paligemma_config,
|
| 95 |
+
action_expert_config,
|
| 96 |
+
use_adarms=[False, True] if self.pi05 else [False, False],
|
| 97 |
+
precision=config.dtype,
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
self.action_in_proj = nn.Linear(config.action_dim, action_expert_config.width)
|
| 101 |
+
self.action_out_proj = nn.Linear(action_expert_config.width, config.action_dim)
|
| 102 |
+
|
| 103 |
+
if self.pi05:
|
| 104 |
+
self.time_mlp_in = nn.Linear(action_expert_config.width, action_expert_config.width)
|
| 105 |
+
self.time_mlp_out = nn.Linear(action_expert_config.width, action_expert_config.width)
|
| 106 |
+
else:
|
| 107 |
+
self.state_proj = nn.Linear(config.action_dim, action_expert_config.width)
|
| 108 |
+
self.action_time_mlp_in = nn.Linear(2 * action_expert_config.width, action_expert_config.width)
|
| 109 |
+
self.action_time_mlp_out = nn.Linear(action_expert_config.width, action_expert_config.width)
|
| 110 |
+
|
| 111 |
+
torch.set_float32_matmul_precision("high")
|
| 112 |
+
if config.pytorch_compile_mode is not None:
|
| 113 |
+
self.sample_actions = torch.compile(self.sample_actions, mode=config.pytorch_compile_mode)
|
| 114 |
+
|
| 115 |
+
# Initialize gradient checkpointing flag
|
| 116 |
+
self.gradient_checkpointing_enabled = False
|
| 117 |
+
|
| 118 |
+
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/`."
|
| 119 |
+
try:
|
| 120 |
+
from transformers.models.siglip import check
|
| 121 |
+
|
| 122 |
+
if not check.check_whether_transformers_replace_is_installed_correctly():
|
| 123 |
+
raise ValueError(msg)
|
| 124 |
+
except ImportError:
|
| 125 |
+
raise ValueError(msg) from None
|
| 126 |
+
|
| 127 |
+
def gradient_checkpointing_enable(self):
|
| 128 |
+
"""Enable gradient checkpointing for memory optimization."""
|
| 129 |
+
self.gradient_checkpointing_enabled = True
|
| 130 |
+
self.paligemma_with_expert.paligemma.language_model.gradient_checkpointing = True
|
| 131 |
+
self.paligemma_with_expert.paligemma.vision_tower.gradient_checkpointing = True
|
| 132 |
+
self.paligemma_with_expert.gemma_expert.model.gradient_checkpointing = True
|
| 133 |
+
|
| 134 |
+
logging.info("Enabled gradient checkpointing for PI0Pytorch model")
|
| 135 |
+
|
| 136 |
+
def gradient_checkpointing_disable(self):
|
| 137 |
+
"""Disable gradient checkpointing."""
|
| 138 |
+
self.gradient_checkpointing_enabled = False
|
| 139 |
+
self.paligemma_with_expert.paligemma.language_model.gradient_checkpointing = False
|
| 140 |
+
self.paligemma_with_expert.paligemma.vision_tower.gradient_checkpointing = False
|
| 141 |
+
self.paligemma_with_expert.gemma_expert.model.gradient_checkpointing = False
|
| 142 |
+
|
| 143 |
+
logging.info("Disabled gradient checkpointing for PI0Pytorch model")
|
| 144 |
+
|
| 145 |
+
def is_gradient_checkpointing_enabled(self):
|
| 146 |
+
"""Check if gradient checkpointing is enabled."""
|
| 147 |
+
return self.gradient_checkpointing_enabled
|
| 148 |
+
|
| 149 |
+
def _apply_checkpoint(self, func, *args, **kwargs):
|
| 150 |
+
"""Helper method to apply gradient checkpointing if enabled."""
|
| 151 |
+
if self.gradient_checkpointing_enabled and self.training:
|
| 152 |
+
return torch.utils.checkpoint.checkpoint(
|
| 153 |
+
func, *args, use_reentrant=False, preserve_rng_state=False, **kwargs
|
| 154 |
+
)
|
| 155 |
+
return func(*args, **kwargs)
|
| 156 |
+
|
| 157 |
+
def _prepare_attention_masks_4d(self, att_2d_masks):
|
| 158 |
+
"""Helper method to prepare 4D attention masks for transformer."""
|
| 159 |
+
att_2d_masks_4d = att_2d_masks[:, None, :, :]
|
| 160 |
+
return torch.where(att_2d_masks_4d, 0.0, -2.3819763e38)
|
| 161 |
+
|
| 162 |
+
def _preprocess_observation(self, observation, *, train=True):
|
| 163 |
+
"""Helper method to preprocess observation."""
|
| 164 |
+
observation = _preprocessing.preprocess_observation_pytorch(observation, train=train)
|
| 165 |
+
return (
|
| 166 |
+
list(observation.images.values()),
|
| 167 |
+
list(observation.image_masks.values()),
|
| 168 |
+
observation.tokenized_prompt,
|
| 169 |
+
observation.tokenized_prompt_mask,
|
| 170 |
+
observation.state,
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
def sample_noise(self, shape, device):
|
| 174 |
+
return torch.normal(
|
| 175 |
+
mean=0.0,
|
| 176 |
+
std=1.0,
|
| 177 |
+
size=shape,
|
| 178 |
+
dtype=torch.float32,
|
| 179 |
+
device=device,
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
def sample_time(self, bsize, device):
|
| 183 |
+
time_beta = sample_beta(1.5, 1.0, bsize, device)
|
| 184 |
+
time = time_beta * 0.999 + 0.001
|
| 185 |
+
return time.to(dtype=torch.float32, device=device)
|
| 186 |
+
|
| 187 |
+
def embed_prefix(
|
| 188 |
+
self, images, img_masks, lang_tokens, lang_masks
|
| 189 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 190 |
+
"""Embed images with SigLIP and language tokens with embedding layer to prepare
|
| 191 |
+
for PaliGemma transformer processing.
|
| 192 |
+
"""
|
| 193 |
+
embs = []
|
| 194 |
+
pad_masks = []
|
| 195 |
+
att_masks = []
|
| 196 |
+
|
| 197 |
+
# Process images
|
| 198 |
+
for img, img_mask in zip(images, img_masks, strict=True):
|
| 199 |
+
|
| 200 |
+
def image_embed_func(img):
|
| 201 |
+
return self.paligemma_with_expert.embed_image(img)
|
| 202 |
+
|
| 203 |
+
img_emb = self._apply_checkpoint(image_embed_func, img)
|
| 204 |
+
|
| 205 |
+
bsize, num_img_embs = img_emb.shape[:2]
|
| 206 |
+
|
| 207 |
+
embs.append(img_emb)
|
| 208 |
+
pad_masks.append(img_mask[:, None].expand(bsize, num_img_embs))
|
| 209 |
+
|
| 210 |
+
# Create attention masks so that image tokens attend to each other
|
| 211 |
+
att_masks += [0] * num_img_embs
|
| 212 |
+
|
| 213 |
+
# Process language tokens
|
| 214 |
+
def lang_embed_func(lang_tokens):
|
| 215 |
+
lang_emb = self.paligemma_with_expert.embed_language_tokens(lang_tokens)
|
| 216 |
+
lang_emb_dim = lang_emb.shape[-1]
|
| 217 |
+
return lang_emb * math.sqrt(lang_emb_dim)
|
| 218 |
+
|
| 219 |
+
lang_emb = self._apply_checkpoint(lang_embed_func, lang_tokens)
|
| 220 |
+
|
| 221 |
+
embs.append(lang_emb)
|
| 222 |
+
pad_masks.append(lang_masks)
|
| 223 |
+
|
| 224 |
+
# full attention between image and language inputs
|
| 225 |
+
num_lang_embs = lang_emb.shape[1]
|
| 226 |
+
att_masks += [0] * num_lang_embs
|
| 227 |
+
|
| 228 |
+
embs = torch.cat(embs, dim=1)
|
| 229 |
+
pad_masks = torch.cat(pad_masks, dim=1)
|
| 230 |
+
att_masks = torch.tensor(att_masks, dtype=torch.bool, device=pad_masks.device)
|
| 231 |
+
|
| 232 |
+
# Get batch size from the first dimension of the concatenated tensors
|
| 233 |
+
bsize = pad_masks.shape[0]
|
| 234 |
+
att_masks = att_masks[None, :].expand(bsize, len(att_masks))
|
| 235 |
+
|
| 236 |
+
return embs, pad_masks, att_masks
|
| 237 |
+
|
| 238 |
+
def embed_suffix(self, state, noisy_actions, timestep):
|
| 239 |
+
"""Embed state, noisy_actions, timestep to prepare for Expert Gemma processing."""
|
| 240 |
+
embs = []
|
| 241 |
+
pad_masks = []
|
| 242 |
+
att_masks = []
|
| 243 |
+
|
| 244 |
+
if not self.pi05:
|
| 245 |
+
if self.state_proj.weight.dtype == torch.float32:
|
| 246 |
+
state = state.to(torch.float32)
|
| 247 |
+
|
| 248 |
+
# Embed state
|
| 249 |
+
def state_proj_func(state):
|
| 250 |
+
return self.state_proj(state)
|
| 251 |
+
|
| 252 |
+
state_emb = self._apply_checkpoint(state_proj_func, state)
|
| 253 |
+
|
| 254 |
+
embs.append(state_emb[:, None, :])
|
| 255 |
+
bsize = state_emb.shape[0]
|
| 256 |
+
device = state_emb.device
|
| 257 |
+
|
| 258 |
+
state_mask = torch.ones(bsize, 1, dtype=torch.bool, device=device)
|
| 259 |
+
pad_masks.append(state_mask)
|
| 260 |
+
|
| 261 |
+
# Set attention masks so that image and language inputs do not attend to state or actions
|
| 262 |
+
att_masks += [1]
|
| 263 |
+
|
| 264 |
+
# Embed timestep using sine-cosine positional encoding with sensitivity in the range [0, 1]
|
| 265 |
+
time_emb = create_sinusoidal_pos_embedding(
|
| 266 |
+
timestep, self.action_in_proj.out_features, min_period=4e-3, max_period=4.0, device=timestep.device
|
| 267 |
+
)
|
| 268 |
+
time_emb = time_emb.type(dtype=timestep.dtype)
|
| 269 |
+
|
| 270 |
+
# Fuse timestep + action information using an MLP
|
| 271 |
+
def action_proj_func(noisy_actions):
|
| 272 |
+
return self.action_in_proj(noisy_actions)
|
| 273 |
+
|
| 274 |
+
action_emb = self._apply_checkpoint(action_proj_func, noisy_actions)
|
| 275 |
+
|
| 276 |
+
if not self.pi05:
|
| 277 |
+
time_emb = time_emb[:, None, :].expand_as(action_emb)
|
| 278 |
+
action_time_emb = torch.cat([action_emb, time_emb], dim=2)
|
| 279 |
+
|
| 280 |
+
# Apply MLP layers
|
| 281 |
+
def mlp_func(action_time_emb):
|
| 282 |
+
x = self.action_time_mlp_in(action_time_emb)
|
| 283 |
+
x = F.silu(x) # swish == silu
|
| 284 |
+
return self.action_time_mlp_out(x)
|
| 285 |
+
|
| 286 |
+
action_time_emb = self._apply_checkpoint(mlp_func, action_time_emb)
|
| 287 |
+
adarms_cond = None
|
| 288 |
+
else:
|
| 289 |
+
# time MLP (for adaRMS)
|
| 290 |
+
def time_mlp_func(time_emb):
|
| 291 |
+
x = self.time_mlp_in(time_emb)
|
| 292 |
+
x = F.silu(x) # swish == silu
|
| 293 |
+
x = self.time_mlp_out(x)
|
| 294 |
+
return F.silu(x)
|
| 295 |
+
|
| 296 |
+
time_emb = self._apply_checkpoint(time_mlp_func, time_emb)
|
| 297 |
+
action_time_emb = action_emb
|
| 298 |
+
adarms_cond = time_emb
|
| 299 |
+
|
| 300 |
+
# Add to input tokens
|
| 301 |
+
embs.append(action_time_emb)
|
| 302 |
+
|
| 303 |
+
bsize, action_time_dim = action_time_emb.shape[:2]
|
| 304 |
+
action_time_mask = torch.ones(bsize, action_time_dim, dtype=torch.bool, device=timestep.device)
|
| 305 |
+
pad_masks.append(action_time_mask)
|
| 306 |
+
|
| 307 |
+
# Set attention masks so that image, language and state inputs do not attend to action tokens
|
| 308 |
+
att_masks += [1] + ([0] * (self.config.action_horizon - 1))
|
| 309 |
+
|
| 310 |
+
embs = torch.cat(embs, dim=1)
|
| 311 |
+
pad_masks = torch.cat(pad_masks, dim=1)
|
| 312 |
+
att_masks = torch.tensor(att_masks, dtype=embs.dtype, device=embs.device)
|
| 313 |
+
att_masks = att_masks[None, :].expand(bsize, len(att_masks))
|
| 314 |
+
|
| 315 |
+
return embs, pad_masks, att_masks, adarms_cond
|
| 316 |
+
|
| 317 |
+
def forward(self, observation, actions, noise=None, time=None) -> Tensor:
|
| 318 |
+
"""Do a full training forward pass and compute the loss (batch_size x num_steps x num_motors)"""
|
| 319 |
+
images, img_masks, lang_tokens, lang_masks, state = self._preprocess_observation(observation, train=True)
|
| 320 |
+
|
| 321 |
+
if noise is None:
|
| 322 |
+
noise = self.sample_noise(actions.shape, actions.device)
|
| 323 |
+
|
| 324 |
+
if time is None:
|
| 325 |
+
time = self.sample_time(actions.shape[0], actions.device)
|
| 326 |
+
|
| 327 |
+
time_expanded = time[:, None, None]
|
| 328 |
+
x_t = time_expanded * noise + (1 - time_expanded) * actions
|
| 329 |
+
u_t = noise - actions
|
| 330 |
+
|
| 331 |
+
prefix_embs, prefix_pad_masks, prefix_att_masks = self.embed_prefix(images, img_masks, lang_tokens, lang_masks)
|
| 332 |
+
suffix_embs, suffix_pad_masks, suffix_att_masks, adarms_cond = self.embed_suffix(state, x_t, time)
|
| 333 |
+
if (
|
| 334 |
+
self.paligemma_with_expert.paligemma.language_model.layers[0].self_attn.q_proj.weight.dtype
|
| 335 |
+
== torch.bfloat16
|
| 336 |
+
):
|
| 337 |
+
suffix_embs = suffix_embs.to(dtype=torch.bfloat16)
|
| 338 |
+
prefix_embs = prefix_embs.to(dtype=torch.bfloat16)
|
| 339 |
+
|
| 340 |
+
pad_masks = torch.cat([prefix_pad_masks, suffix_pad_masks], dim=1)
|
| 341 |
+
att_masks = torch.cat([prefix_att_masks, suffix_att_masks], dim=1)
|
| 342 |
+
|
| 343 |
+
att_2d_masks = make_att_2d_masks(pad_masks, att_masks)
|
| 344 |
+
position_ids = torch.cumsum(pad_masks, dim=1) - 1
|
| 345 |
+
|
| 346 |
+
# Prepare attention masks
|
| 347 |
+
att_2d_masks_4d = self._prepare_attention_masks_4d(att_2d_masks)
|
| 348 |
+
|
| 349 |
+
# Apply gradient checkpointing if enabled
|
| 350 |
+
def forward_func(prefix_embs, suffix_embs, att_2d_masks_4d, position_ids, adarms_cond):
|
| 351 |
+
(_, suffix_out), _ = self.paligemma_with_expert.forward(
|
| 352 |
+
attention_mask=att_2d_masks_4d,
|
| 353 |
+
position_ids=position_ids,
|
| 354 |
+
past_key_values=None,
|
| 355 |
+
inputs_embeds=[prefix_embs, suffix_embs],
|
| 356 |
+
use_cache=False,
|
| 357 |
+
adarms_cond=[None, adarms_cond],
|
| 358 |
+
)
|
| 359 |
+
return suffix_out
|
| 360 |
+
|
| 361 |
+
suffix_out = self._apply_checkpoint(
|
| 362 |
+
forward_func, prefix_embs, suffix_embs, att_2d_masks_4d, position_ids, adarms_cond
|
| 363 |
+
)
|
| 364 |
+
|
| 365 |
+
suffix_out = suffix_out[:, -self.config.action_horizon :]
|
| 366 |
+
suffix_out = suffix_out.to(dtype=torch.float32)
|
| 367 |
+
|
| 368 |
+
# Apply gradient checkpointing to final action projection if enabled
|
| 369 |
+
def action_out_proj_func(suffix_out):
|
| 370 |
+
return self.action_out_proj(suffix_out)
|
| 371 |
+
|
| 372 |
+
v_t = self._apply_checkpoint(action_out_proj_func, suffix_out)
|
| 373 |
+
|
| 374 |
+
return F.mse_loss(u_t, v_t, reduction="none")
|
| 375 |
+
|
| 376 |
+
@torch.no_grad()
|
| 377 |
+
def sample_actions(self, device, observation, noise=None, num_steps=10) -> Tensor:
|
| 378 |
+
"""Do a full inference forward and compute the action (batch_size x num_steps x num_motors)"""
|
| 379 |
+
bsize = observation.state.shape[0]
|
| 380 |
+
if noise is None:
|
| 381 |
+
actions_shape = (bsize, self.config.action_horizon, self.config.action_dim)
|
| 382 |
+
noise = self.sample_noise(actions_shape, device)
|
| 383 |
+
|
| 384 |
+
images, img_masks, lang_tokens, lang_masks, state = self._preprocess_observation(observation, train=False)
|
| 385 |
+
|
| 386 |
+
prefix_embs, prefix_pad_masks, prefix_att_masks = self.embed_prefix(images, img_masks, lang_tokens, lang_masks)
|
| 387 |
+
prefix_att_2d_masks = make_att_2d_masks(prefix_pad_masks, prefix_att_masks)
|
| 388 |
+
prefix_position_ids = torch.cumsum(prefix_pad_masks, dim=1) - 1
|
| 389 |
+
|
| 390 |
+
# Compute image and language key value cache
|
| 391 |
+
prefix_att_2d_masks_4d = self._prepare_attention_masks_4d(prefix_att_2d_masks)
|
| 392 |
+
self.paligemma_with_expert.paligemma.language_model.config._attn_implementation = "eager" # noqa: SLF001
|
| 393 |
+
|
| 394 |
+
_, past_key_values = self.paligemma_with_expert.forward(
|
| 395 |
+
attention_mask=prefix_att_2d_masks_4d,
|
| 396 |
+
position_ids=prefix_position_ids,
|
| 397 |
+
past_key_values=None,
|
| 398 |
+
inputs_embeds=[prefix_embs, None],
|
| 399 |
+
use_cache=True,
|
| 400 |
+
)
|
| 401 |
+
|
| 402 |
+
dt = -1.0 / num_steps
|
| 403 |
+
dt = torch.tensor(dt, dtype=torch.float32, device=device)
|
| 404 |
+
|
| 405 |
+
x_t = noise
|
| 406 |
+
time = torch.tensor(1.0, dtype=torch.float32, device=device)
|
| 407 |
+
while time >= -dt / 2:
|
| 408 |
+
expanded_time = time.expand(bsize)
|
| 409 |
+
v_t = self.denoise_step(
|
| 410 |
+
state,
|
| 411 |
+
prefix_pad_masks,
|
| 412 |
+
past_key_values,
|
| 413 |
+
x_t,
|
| 414 |
+
expanded_time,
|
| 415 |
+
)
|
| 416 |
+
|
| 417 |
+
# Euler step - use new tensor assignment instead of in-place operation
|
| 418 |
+
x_t = x_t + dt * v_t
|
| 419 |
+
time += dt
|
| 420 |
+
return x_t
|
| 421 |
+
|
| 422 |
+
def denoise_step(
|
| 423 |
+
self,
|
| 424 |
+
state,
|
| 425 |
+
prefix_pad_masks,
|
| 426 |
+
past_key_values,
|
| 427 |
+
x_t,
|
| 428 |
+
timestep,
|
| 429 |
+
):
|
| 430 |
+
"""Apply one denoising step of the noise `x_t` at a given timestep."""
|
| 431 |
+
suffix_embs, suffix_pad_masks, suffix_att_masks, adarms_cond = self.embed_suffix(state, x_t, timestep)
|
| 432 |
+
|
| 433 |
+
suffix_len = suffix_pad_masks.shape[1]
|
| 434 |
+
batch_size = prefix_pad_masks.shape[0]
|
| 435 |
+
prefix_len = prefix_pad_masks.shape[1]
|
| 436 |
+
|
| 437 |
+
prefix_pad_2d_masks = prefix_pad_masks[:, None, :].expand(batch_size, suffix_len, prefix_len)
|
| 438 |
+
|
| 439 |
+
suffix_att_2d_masks = make_att_2d_masks(suffix_pad_masks, suffix_att_masks)
|
| 440 |
+
|
| 441 |
+
full_att_2d_masks = torch.cat([prefix_pad_2d_masks, suffix_att_2d_masks], dim=2)
|
| 442 |
+
|
| 443 |
+
prefix_offsets = torch.sum(prefix_pad_masks, dim=-1)[:, None]
|
| 444 |
+
position_ids = prefix_offsets + torch.cumsum(suffix_pad_masks, dim=1) - 1
|
| 445 |
+
|
| 446 |
+
# Prepare attention masks
|
| 447 |
+
full_att_2d_masks_4d = self._prepare_attention_masks_4d(full_att_2d_masks)
|
| 448 |
+
self.paligemma_with_expert.gemma_expert.model.config._attn_implementation = "eager" # noqa: SLF001
|
| 449 |
+
|
| 450 |
+
outputs_embeds, _ = self.paligemma_with_expert.forward(
|
| 451 |
+
attention_mask=full_att_2d_masks_4d,
|
| 452 |
+
position_ids=position_ids,
|
| 453 |
+
past_key_values=past_key_values,
|
| 454 |
+
inputs_embeds=[None, suffix_embs],
|
| 455 |
+
use_cache=False,
|
| 456 |
+
adarms_cond=[None, adarms_cond],
|
| 457 |
+
)
|
| 458 |
+
|
| 459 |
+
suffix_out = outputs_embeds[1]
|
| 460 |
+
suffix_out = suffix_out[:, -self.config.action_horizon :]
|
| 461 |
+
suffix_out = suffix_out.to(dtype=torch.float32)
|
| 462 |
+
return self.action_out_proj(suffix_out)
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/preprocessing_pytorch.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections.abc import Sequence
|
| 2 |
+
import logging
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
from openpi.shared import image_tools
|
| 7 |
+
|
| 8 |
+
logger = logging.getLogger("openpi")
|
| 9 |
+
|
| 10 |
+
# Constants moved from model.py
|
| 11 |
+
IMAGE_KEYS = (
|
| 12 |
+
"base_0_rgb",
|
| 13 |
+
"left_wrist_0_rgb",
|
| 14 |
+
"right_wrist_0_rgb",
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
IMAGE_RESOLUTION = (224, 224)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def preprocess_observation_pytorch(
|
| 21 |
+
observation,
|
| 22 |
+
*,
|
| 23 |
+
train: bool = False,
|
| 24 |
+
image_keys: Sequence[str] = IMAGE_KEYS,
|
| 25 |
+
image_resolution: tuple[int, int] = IMAGE_RESOLUTION,
|
| 26 |
+
):
|
| 27 |
+
"""Torch.compile-compatible version of preprocess_observation_pytorch with simplified type annotations.
|
| 28 |
+
|
| 29 |
+
This function avoids complex type annotations that can cause torch.compile issues.
|
| 30 |
+
"""
|
| 31 |
+
if not set(image_keys).issubset(observation.images):
|
| 32 |
+
raise ValueError(f"images dict missing keys: expected {image_keys}, got {list(observation.images)}")
|
| 33 |
+
|
| 34 |
+
batch_shape = observation.state.shape[:-1]
|
| 35 |
+
|
| 36 |
+
out_images = {}
|
| 37 |
+
for key in image_keys:
|
| 38 |
+
image = observation.images[key]
|
| 39 |
+
|
| 40 |
+
# TODO: This is a hack to handle both [B, C, H, W] and [B, H, W, C] formats
|
| 41 |
+
# Handle both [B, C, H, W] and [B, H, W, C] formats
|
| 42 |
+
is_channels_first = image.shape[1] == 3 # Check if channels are in dimension 1
|
| 43 |
+
|
| 44 |
+
if is_channels_first:
|
| 45 |
+
# Convert [B, C, H, W] to [B, H, W, C] for processing
|
| 46 |
+
image = image.permute(0, 2, 3, 1)
|
| 47 |
+
|
| 48 |
+
if image.shape[1:3] != image_resolution:
|
| 49 |
+
logger.info(f"Resizing image {key} from {image.shape[1:3]} to {image_resolution}")
|
| 50 |
+
image = image_tools.resize_with_pad_torch(image, *image_resolution)
|
| 51 |
+
|
| 52 |
+
if train:
|
| 53 |
+
# Convert from [-1, 1] to [0, 1] for PyTorch augmentations
|
| 54 |
+
image = image / 2.0 + 0.5
|
| 55 |
+
|
| 56 |
+
# Apply PyTorch-based augmentations
|
| 57 |
+
if "wrist" not in key:
|
| 58 |
+
# Geometric augmentations for non-wrist cameras
|
| 59 |
+
height, width = image.shape[1:3]
|
| 60 |
+
|
| 61 |
+
# Random crop and resize
|
| 62 |
+
crop_height = int(height * 0.95)
|
| 63 |
+
crop_width = int(width * 0.95)
|
| 64 |
+
|
| 65 |
+
# Random crop
|
| 66 |
+
max_h = height - crop_height
|
| 67 |
+
max_w = width - crop_width
|
| 68 |
+
if max_h > 0 and max_w > 0:
|
| 69 |
+
# Use tensor operations instead of .item() for torch.compile compatibility
|
| 70 |
+
start_h = torch.randint(0, max_h + 1, (1,), device=image.device)
|
| 71 |
+
start_w = torch.randint(0, max_w + 1, (1,), device=image.device)
|
| 72 |
+
image = image[:, start_h : start_h + crop_height, start_w : start_w + crop_width, :]
|
| 73 |
+
|
| 74 |
+
# Resize back to original size
|
| 75 |
+
image = torch.nn.functional.interpolate(
|
| 76 |
+
image.permute(0, 3, 1, 2), # [b, h, w, c] -> [b, c, h, w]
|
| 77 |
+
size=(height, width),
|
| 78 |
+
mode="bilinear",
|
| 79 |
+
align_corners=False,
|
| 80 |
+
).permute(0, 2, 3, 1) # [b, c, h, w] -> [b, h, w, c]
|
| 81 |
+
|
| 82 |
+
# Random rotation (small angles)
|
| 83 |
+
# Use tensor operations instead of .item() for torch.compile compatibility
|
| 84 |
+
angle = torch.rand(1, device=image.device) * 10 - 5 # Random angle between -5 and 5 degrees
|
| 85 |
+
if torch.abs(angle) > 0.1: # Only rotate if angle is significant
|
| 86 |
+
# Convert to radians
|
| 87 |
+
angle_rad = angle * torch.pi / 180.0
|
| 88 |
+
|
| 89 |
+
# Create rotation matrix
|
| 90 |
+
cos_a = torch.cos(angle_rad)
|
| 91 |
+
sin_a = torch.sin(angle_rad)
|
| 92 |
+
|
| 93 |
+
# Apply rotation using grid_sample
|
| 94 |
+
grid_x = torch.linspace(-1, 1, width, device=image.device)
|
| 95 |
+
grid_y = torch.linspace(-1, 1, height, device=image.device)
|
| 96 |
+
|
| 97 |
+
# Create meshgrid
|
| 98 |
+
grid_y, grid_x = torch.meshgrid(grid_y, grid_x, indexing="ij")
|
| 99 |
+
|
| 100 |
+
# Expand to batch dimension
|
| 101 |
+
grid_x = grid_x.unsqueeze(0).expand(image.shape[0], -1, -1)
|
| 102 |
+
grid_y = grid_y.unsqueeze(0).expand(image.shape[0], -1, -1)
|
| 103 |
+
|
| 104 |
+
# Apply rotation transformation
|
| 105 |
+
grid_x_rot = grid_x * cos_a - grid_y * sin_a
|
| 106 |
+
grid_y_rot = grid_x * sin_a + grid_y * cos_a
|
| 107 |
+
|
| 108 |
+
# Stack and reshape for grid_sample
|
| 109 |
+
grid = torch.stack([grid_x_rot, grid_y_rot], dim=-1)
|
| 110 |
+
|
| 111 |
+
image = torch.nn.functional.grid_sample(
|
| 112 |
+
image.permute(0, 3, 1, 2), # [b, h, w, c] -> [b, c, h, w]
|
| 113 |
+
grid,
|
| 114 |
+
mode="bilinear",
|
| 115 |
+
padding_mode="zeros",
|
| 116 |
+
align_corners=False,
|
| 117 |
+
).permute(0, 2, 3, 1) # [b, c, h, w] -> [b, h, w, c]
|
| 118 |
+
|
| 119 |
+
# Color augmentations for all cameras
|
| 120 |
+
# Random brightness
|
| 121 |
+
# Use tensor operations instead of .item() for torch.compile compatibility
|
| 122 |
+
brightness_factor = 0.7 + torch.rand(1, device=image.device) * 0.6 # Random factor between 0.7 and 1.3
|
| 123 |
+
image = image * brightness_factor
|
| 124 |
+
|
| 125 |
+
# Random contrast
|
| 126 |
+
# Use tensor operations instead of .item() for torch.compile compatibility
|
| 127 |
+
contrast_factor = 0.6 + torch.rand(1, device=image.device) * 0.8 # Random factor between 0.6 and 1.4
|
| 128 |
+
mean = image.mean(dim=[1, 2, 3], keepdim=True)
|
| 129 |
+
image = (image - mean) * contrast_factor + mean
|
| 130 |
+
|
| 131 |
+
# Random saturation (convert to HSV, modify S, convert back)
|
| 132 |
+
# For simplicity, we'll just apply a random scaling to the color channels
|
| 133 |
+
# Use tensor operations instead of .item() for torch.compile compatibility
|
| 134 |
+
saturation_factor = 0.5 + torch.rand(1, device=image.device) * 1.0 # Random factor between 0.5 and 1.5
|
| 135 |
+
gray = image.mean(dim=-1, keepdim=True)
|
| 136 |
+
image = gray + (image - gray) * saturation_factor
|
| 137 |
+
|
| 138 |
+
# Clamp values to [0, 1]
|
| 139 |
+
image = torch.clamp(image, 0, 1)
|
| 140 |
+
|
| 141 |
+
# Back to [-1, 1]
|
| 142 |
+
image = image * 2.0 - 1.0
|
| 143 |
+
|
| 144 |
+
# Convert back to [B, C, H, W] format if it was originally channels-first
|
| 145 |
+
if is_channels_first:
|
| 146 |
+
image = image.permute(0, 3, 1, 2) # [B, H, W, C] -> [B, C, H, W]
|
| 147 |
+
|
| 148 |
+
out_images[key] = image
|
| 149 |
+
|
| 150 |
+
# obtain mask
|
| 151 |
+
out_masks = {}
|
| 152 |
+
for key in out_images:
|
| 153 |
+
if key not in observation.image_masks:
|
| 154 |
+
# do not mask by default
|
| 155 |
+
out_masks[key] = torch.ones(batch_shape, dtype=torch.bool, device=observation.state.device)
|
| 156 |
+
else:
|
| 157 |
+
out_masks[key] = observation.image_masks[key]
|
| 158 |
+
|
| 159 |
+
# Create a simple object with the required attributes instead of using the complex Observation class
|
| 160 |
+
class SimpleProcessedObservation:
|
| 161 |
+
def __init__(self, **kwargs):
|
| 162 |
+
for key, value in kwargs.items():
|
| 163 |
+
setattr(self, key, value)
|
| 164 |
+
|
| 165 |
+
return SimpleProcessedObservation(
|
| 166 |
+
images=out_images,
|
| 167 |
+
image_masks=out_masks,
|
| 168 |
+
state=observation.state,
|
| 169 |
+
tokenized_prompt=observation.tokenized_prompt,
|
| 170 |
+
tokenized_prompt_mask=observation.tokenized_prompt_mask,
|
| 171 |
+
token_ar_mask=observation.token_ar_mask,
|
| 172 |
+
token_loss_mask=observation.token_loss_mask,
|
| 173 |
+
)
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/gemma/configuration_gemma.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
| 2 |
+
# This file was automatically generated from src/transformers/models/gemma/modular_gemma.py.
|
| 3 |
+
# Do NOT edit this file manually as any edits will be overwritten by the generation of
|
| 4 |
+
# the file from the modular. If any change should be done, please apply the change to the
|
| 5 |
+
# modular_gemma.py file directly. One of our CI enforces this.
|
| 6 |
+
# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
| 7 |
+
# coding=utf-8
|
| 8 |
+
# Copyright 2024 Google Inc. HuggingFace Inc. team. All rights reserved.
|
| 9 |
+
#
|
| 10 |
+
#
|
| 11 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 12 |
+
# you may not use this file except in compliance with the License.
|
| 13 |
+
# You may obtain a copy of the License at
|
| 14 |
+
#
|
| 15 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 16 |
+
#
|
| 17 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 18 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 19 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 20 |
+
# See the License for the specific language governing permissions and
|
| 21 |
+
# limitations under the License.
|
| 22 |
+
from typing import Optional
|
| 23 |
+
from ...configuration_utils import PretrainedConfig
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class GemmaConfig(PretrainedConfig):
|
| 27 |
+
r"""
|
| 28 |
+
This is the configuration class to store the configuration of a [`GemmaModel`]. It is used to instantiate an Gemma
|
| 29 |
+
model according to the specified arguments, defining the model architecture. Instantiating a configuration with the
|
| 30 |
+
defaults will yield a similar configuration to that of the Gemma-7B.
|
| 31 |
+
e.g. [google/gemma-7b](https://huggingface.co/google/gemma-7b)
|
| 32 |
+
Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
|
| 33 |
+
documentation from [`PretrainedConfig`] for more information.
|
| 34 |
+
Args:
|
| 35 |
+
vocab_size (`int`, *optional*, defaults to 256000):
|
| 36 |
+
Vocabulary size of the Gemma model. Defines the number of different tokens that can be represented by the
|
| 37 |
+
`inputs_ids` passed when calling [`GemmaModel`]
|
| 38 |
+
hidden_size (`int`, *optional*, defaults to 3072):
|
| 39 |
+
Dimension of the hidden representations.
|
| 40 |
+
intermediate_size (`int`, *optional*, defaults to 24576):
|
| 41 |
+
Dimension of the MLP representations.
|
| 42 |
+
num_hidden_layers (`int`, *optional*, defaults to 28):
|
| 43 |
+
Number of hidden layers in the Transformer decoder.
|
| 44 |
+
num_attention_heads (`int`, *optional*, defaults to 16):
|
| 45 |
+
Number of attention heads for each attention layer in the Transformer decoder.
|
| 46 |
+
num_key_value_heads (`int`, *optional*, defaults to 16):
|
| 47 |
+
This is the number of key_value heads that should be used to implement Grouped Query Attention. If
|
| 48 |
+
`num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if
|
| 49 |
+
`num_key_value_heads=1` the model will use Multi Query Attention (MQA) otherwise GQA is used. When
|
| 50 |
+
converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed
|
| 51 |
+
by meanpooling all the original heads within that group. For more details, check out [this
|
| 52 |
+
paper](https://huggingface.co/papers/2305.13245). If it is not specified, will default to
|
| 53 |
+
`num_attention_heads`.
|
| 54 |
+
head_dim (`int`, *optional*, defaults to 256):
|
| 55 |
+
The attention head dimension.
|
| 56 |
+
hidden_act (`str` or `function`, *optional*, defaults to `"gelu_pytorch_tanh"`):
|
| 57 |
+
The legacy activation function. It is overwritten by the `hidden_activation`.
|
| 58 |
+
hidden_activation (`str` or `function`, *optional*):
|
| 59 |
+
The non-linear activation function (function or string) in the decoder. Will default to `"gelu_pytorch_tanh"`
|
| 60 |
+
if not specified. `"gelu_pytorch_tanh"` uses an approximation of the `"gelu"` activation function.
|
| 61 |
+
max_position_embeddings (`int`, *optional*, defaults to 8192):
|
| 62 |
+
The maximum sequence length that this model might ever be used with.
|
| 63 |
+
initializer_range (`float`, *optional*, defaults to 0.02):
|
| 64 |
+
The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
|
| 65 |
+
rms_norm_eps (`float`, *optional*, defaults to 1e-06):
|
| 66 |
+
The epsilon used by the rms normalization layers.
|
| 67 |
+
use_cache (`bool`, *optional*, defaults to `True`):
|
| 68 |
+
Whether or not the model should return the last key/values attentions (not used by all models). Only
|
| 69 |
+
relevant if `config.is_decoder=True`.
|
| 70 |
+
pad_token_id (`int`, *optional*, defaults to 0):
|
| 71 |
+
Padding token id.
|
| 72 |
+
eos_token_id (`int`, *optional*, defaults to 1):
|
| 73 |
+
End of stream token id.
|
| 74 |
+
bos_token_id (`int`, *optional*, defaults to 2):
|
| 75 |
+
Beginning of stream token id.
|
| 76 |
+
tie_word_embeddings (`bool`, *optional*, defaults to `True`):
|
| 77 |
+
Whether to tie weight embeddings
|
| 78 |
+
rope_theta (`float`, *optional*, defaults to 10000.0):
|
| 79 |
+
The base period of the RoPE embeddings.
|
| 80 |
+
attention_bias (`bool`, defaults to `False`, *optional*, defaults to `False`):
|
| 81 |
+
Whether to use a bias in the query, key, value and output projection layers during self-attention.
|
| 82 |
+
attention_dropout (`float`, *optional*, defaults to 0.0):
|
| 83 |
+
The dropout ratio for the attention probabilities.
|
| 84 |
+
use_adarms (`bool`, *optional*, defaults to `False`):
|
| 85 |
+
Whether to use ADARMS.
|
| 86 |
+
adarms_cond_dim (`int`, *optional*, defaults to `None`):
|
| 87 |
+
The dimension of the ADARMS condition.
|
| 88 |
+
```python
|
| 89 |
+
>>> from transformers import GemmaModel, GemmaConfig
|
| 90 |
+
>>> # Initializing a Gemma gemma-7b style configuration
|
| 91 |
+
>>> configuration = GemmaConfig()
|
| 92 |
+
>>> # Initializing a model from the gemma-7b style configuration
|
| 93 |
+
>>> model = GemmaModel(configuration)
|
| 94 |
+
>>> # Accessing the model configuration
|
| 95 |
+
>>> configuration = model.config
|
| 96 |
+
```"""
|
| 97 |
+
|
| 98 |
+
model_type = "gemma"
|
| 99 |
+
keys_to_ignore_at_inference = ["past_key_values"]
|
| 100 |
+
base_model_tp_plan = {
|
| 101 |
+
"layers.*.self_attn.q_proj": "colwise",
|
| 102 |
+
"layers.*.self_attn.k_proj": "colwise",
|
| 103 |
+
"layers.*.self_attn.v_proj": "colwise",
|
| 104 |
+
"layers.*.self_attn.o_proj": "rowwise",
|
| 105 |
+
"layers.*.mlp.gate_proj": "colwise",
|
| 106 |
+
"layers.*.mlp.up_proj": "colwise",
|
| 107 |
+
"layers.*.mlp.down_proj": "rowwise",
|
| 108 |
+
}
|
| 109 |
+
base_model_pp_plan = {
|
| 110 |
+
"embed_tokens": (["input_ids"], ["inputs_embeds"]),
|
| 111 |
+
"layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
|
| 112 |
+
"norm": (["hidden_states"], ["hidden_states"]),
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
def __init__(
|
| 116 |
+
self,
|
| 117 |
+
vocab_size=256000,
|
| 118 |
+
hidden_size=3072,
|
| 119 |
+
intermediate_size=24576,
|
| 120 |
+
num_hidden_layers=28,
|
| 121 |
+
num_attention_heads=16,
|
| 122 |
+
num_key_value_heads=16,
|
| 123 |
+
head_dim=256,
|
| 124 |
+
hidden_act="gelu_pytorch_tanh",
|
| 125 |
+
hidden_activation=None,
|
| 126 |
+
max_position_embeddings=8192,
|
| 127 |
+
initializer_range=0.02,
|
| 128 |
+
rms_norm_eps=1e-6,
|
| 129 |
+
use_cache=True,
|
| 130 |
+
pad_token_id=0,
|
| 131 |
+
eos_token_id=1,
|
| 132 |
+
bos_token_id=2,
|
| 133 |
+
tie_word_embeddings=True,
|
| 134 |
+
rope_theta=10000.0,
|
| 135 |
+
attention_bias=False,
|
| 136 |
+
attention_dropout=0.0,
|
| 137 |
+
use_adarms: bool = False,
|
| 138 |
+
adarms_cond_dim: Optional[int] = None,
|
| 139 |
+
**kwargs,
|
| 140 |
+
):
|
| 141 |
+
self.vocab_size = vocab_size
|
| 142 |
+
self.max_position_embeddings = max_position_embeddings
|
| 143 |
+
self.hidden_size = hidden_size
|
| 144 |
+
self.intermediate_size = intermediate_size
|
| 145 |
+
self.num_hidden_layers = num_hidden_layers
|
| 146 |
+
self.num_attention_heads = num_attention_heads
|
| 147 |
+
self.head_dim = head_dim
|
| 148 |
+
self.num_key_value_heads = num_key_value_heads
|
| 149 |
+
self.hidden_act = hidden_act
|
| 150 |
+
self.hidden_activation = hidden_activation
|
| 151 |
+
self.initializer_range = initializer_range
|
| 152 |
+
self.rms_norm_eps = rms_norm_eps
|
| 153 |
+
self.use_cache = use_cache
|
| 154 |
+
self.rope_theta = rope_theta
|
| 155 |
+
self.attention_bias = attention_bias
|
| 156 |
+
self.attention_dropout = attention_dropout
|
| 157 |
+
self.use_adarms = use_adarms
|
| 158 |
+
self.adarms_cond_dim = adarms_cond_dim
|
| 159 |
+
|
| 160 |
+
# Set default for adarms_cond_dim if use_adarms is True
|
| 161 |
+
if self.use_adarms and self.adarms_cond_dim is None:
|
| 162 |
+
self.adarms_cond_dim = self.hidden_size
|
| 163 |
+
|
| 164 |
+
super().__init__(
|
| 165 |
+
pad_token_id=pad_token_id,
|
| 166 |
+
bos_token_id=bos_token_id,
|
| 167 |
+
eos_token_id=eos_token_id,
|
| 168 |
+
tie_word_embeddings=tie_word_embeddings,
|
| 169 |
+
**kwargs,
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
__all__ = ["GemmaConfig"]
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/gemma/modeling_gemma.py
ADDED
|
@@ -0,0 +1,862 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
| 2 |
+
# This file was automatically generated from src/transformers/models/gemma/modular_gemma.py.
|
| 3 |
+
# Do NOT edit this file manually as any edits will be overwritten by the generation of
|
| 4 |
+
# the file from the modular. If any change should be done, please apply the change to the
|
| 5 |
+
# modular_gemma.py file directly. One of our CI enforces this.
|
| 6 |
+
# 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
| 7 |
+
# coding=utf-8
|
| 8 |
+
# Copyright 2024 Google Inc. HuggingFace Inc. team. All rights reserved.
|
| 9 |
+
#
|
| 10 |
+
#
|
| 11 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 12 |
+
# you may not use this file except in compliance with the License.
|
| 13 |
+
# You may obtain a copy of the License at
|
| 14 |
+
#
|
| 15 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 16 |
+
#
|
| 17 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 18 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 19 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 20 |
+
# See the License for the specific language governing permissions and
|
| 21 |
+
# limitations under the License.
|
| 22 |
+
from typing import Callable, Optional, Union
|
| 23 |
+
|
| 24 |
+
import torch
|
| 25 |
+
from torch import nn
|
| 26 |
+
|
| 27 |
+
from ...activations import ACT2FN
|
| 28 |
+
from ...cache_utils import Cache, DynamicCache
|
| 29 |
+
from ...generation import GenerationMixin
|
| 30 |
+
from ...masking_utils import create_causal_mask
|
| 31 |
+
from ...modeling_flash_attention_utils import FlashAttentionKwargs
|
| 32 |
+
from ...modeling_layers import GradientCheckpointingLayer
|
| 33 |
+
from ...modeling_outputs import (
|
| 34 |
+
BaseModelOutputWithPast,
|
| 35 |
+
CausalLMOutputWithPast,
|
| 36 |
+
SequenceClassifierOutputWithPast,
|
| 37 |
+
TokenClassifierOutput,
|
| 38 |
+
)
|
| 39 |
+
from ...modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
|
| 40 |
+
from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
|
| 41 |
+
from ...processing_utils import Unpack
|
| 42 |
+
from ...utils import LossKwargs, auto_docstring, can_return_tuple, logging
|
| 43 |
+
from .configuration_gemma import GemmaConfig
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
logger = logging.get_logger(__name__)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class GemmaRMSNorm(nn.Module):
|
| 50 |
+
def __init__(self, dim: int, eps: float = 1e-6, cond_dim: Optional[int] = None):
|
| 51 |
+
super().__init__()
|
| 52 |
+
self.eps = eps
|
| 53 |
+
self.dim = dim
|
| 54 |
+
self.cond_dim = cond_dim
|
| 55 |
+
|
| 56 |
+
# Dense layer for adaptive normalization (if cond_dim is provided)
|
| 57 |
+
if cond_dim is not None:
|
| 58 |
+
#self.dense = nn.Linear(cond_dim, dim * 3, bias=True, dtype=torch.bfloat16)
|
| 59 |
+
self.dense = nn.Linear(cond_dim, dim * 3, bias=True)
|
| 60 |
+
# Initialize with zeros (matches source implementation)
|
| 61 |
+
nn.init.zeros_(self.dense.weight)
|
| 62 |
+
else:
|
| 63 |
+
self.weight = nn.Parameter(torch.zeros(dim, dtype=torch.bfloat16))
|
| 64 |
+
self.dense = None
|
| 65 |
+
|
| 66 |
+
def _norm(self, x):
|
| 67 |
+
# Compute variance in float32 (like the source implementation)
|
| 68 |
+
var = torch.mean(torch.square(x.float()), dim=-1, keepdim=True)
|
| 69 |
+
# Compute normalization in float32
|
| 70 |
+
normed_inputs = x * torch.rsqrt(var + self.eps)
|
| 71 |
+
return normed_inputs
|
| 72 |
+
|
| 73 |
+
def forward(self, x, cond=None):
|
| 74 |
+
dtype = x.dtype # original dtype, could be half-precision
|
| 75 |
+
normed_inputs = self._norm(x)
|
| 76 |
+
|
| 77 |
+
if cond is None or self.dense is None:
|
| 78 |
+
# regular RMSNorm
|
| 79 |
+
# scale by learned parameter in float32 (matches source implementation)
|
| 80 |
+
normed_inputs = normed_inputs * (1.0 + self.weight.float())
|
| 81 |
+
return normed_inputs.to(dtype), None # return in original dtype with None gate
|
| 82 |
+
|
| 83 |
+
# adaptive RMSNorm (if cond is provided and dense layer exists)
|
| 84 |
+
if cond.shape[-1] != self.cond_dim:
|
| 85 |
+
raise ValueError(f"Expected cond dimension {self.cond_dim}, got {cond.shape[-1]}")
|
| 86 |
+
|
| 87 |
+
#self.dense.to(dtype=torch.bfloat16).to(dtype=torch.float32)
|
| 88 |
+
modulation = self.dense(cond)
|
| 89 |
+
# Reshape modulation to broadcast properly: [batch, 1, features] for [batch, seq, features]
|
| 90 |
+
if len(x.shape) == 3: # [batch, seq, features]
|
| 91 |
+
modulation = modulation.unsqueeze(1)
|
| 92 |
+
|
| 93 |
+
scale, shift, gate = torch.chunk(modulation, 3, dim=-1)
|
| 94 |
+
|
| 95 |
+
# Apply adaptive normalization: use model weight dtype to ensure compatibility
|
| 96 |
+
# model_dtype = self.dense.weight.dtype # Use the model's dtype (bfloat16)
|
| 97 |
+
# scale = scale.to(model_dtype)
|
| 98 |
+
# shift = shift.to(model_dtype)
|
| 99 |
+
# gate = gate.to(model_dtype)
|
| 100 |
+
# normed_inputs = normed_inputs.to(model_dtype) # Convert normed_inputs to model dtype
|
| 101 |
+
|
| 102 |
+
normed_inputs = normed_inputs * (1 + scale.to(torch.float32)) + shift.to(torch.float32)
|
| 103 |
+
|
| 104 |
+
return normed_inputs.to(dtype), gate.to(dtype)
|
| 105 |
+
|
| 106 |
+
def extra_repr(self):
|
| 107 |
+
repr_str = f"{tuple(self.weight.shape)}, eps={self.eps}"
|
| 108 |
+
if self.dense is not None:
|
| 109 |
+
repr_str += f", adaptive=True, cond_dim={self.cond_dim}"
|
| 110 |
+
return repr_str
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
class GemmaMLP(nn.Module):
|
| 114 |
+
def __init__(self, config):
|
| 115 |
+
super().__init__()
|
| 116 |
+
self.config = config
|
| 117 |
+
self.hidden_size = config.hidden_size
|
| 118 |
+
self.intermediate_size = config.intermediate_size
|
| 119 |
+
self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
| 120 |
+
self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
| 121 |
+
self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
|
| 122 |
+
self.act_fn = ACT2FN[config.hidden_act]
|
| 123 |
+
|
| 124 |
+
def forward(self, x):
|
| 125 |
+
down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
|
| 126 |
+
return down_proj
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class GemmaRotaryEmbedding(nn.Module):
|
| 130 |
+
def __init__(self, config: GemmaConfig, device=None):
|
| 131 |
+
super().__init__()
|
| 132 |
+
# BC: "rope_type" was originally "type"
|
| 133 |
+
if hasattr(config, "rope_scaling") and config.rope_scaling is not None:
|
| 134 |
+
self.rope_type = config.rope_scaling.get("rope_type", config.rope_scaling.get("type"))
|
| 135 |
+
else:
|
| 136 |
+
self.rope_type = "default"
|
| 137 |
+
self.max_seq_len_cached = config.max_position_embeddings
|
| 138 |
+
self.original_max_seq_len = config.max_position_embeddings
|
| 139 |
+
|
| 140 |
+
self.config = config
|
| 141 |
+
self.rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
|
| 142 |
+
|
| 143 |
+
inv_freq, self.attention_scaling = self.rope_init_fn(self.config, device)
|
| 144 |
+
self.register_buffer("inv_freq", inv_freq, persistent=False)
|
| 145 |
+
self.original_inv_freq = self.inv_freq
|
| 146 |
+
|
| 147 |
+
@torch.no_grad()
|
| 148 |
+
@dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
|
| 149 |
+
def forward(self, x, position_ids):
|
| 150 |
+
inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
|
| 151 |
+
position_ids_expanded = position_ids[:, None, :].float()
|
| 152 |
+
|
| 153 |
+
device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
|
| 154 |
+
with torch.autocast(device_type=device_type, enabled=False): # Force float32
|
| 155 |
+
freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
|
| 156 |
+
emb = torch.cat((freqs, freqs), dim=-1)
|
| 157 |
+
cos = emb.cos() * self.attention_scaling
|
| 158 |
+
sin = emb.sin() * self.attention_scaling
|
| 159 |
+
|
| 160 |
+
return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def rotate_half(x):
|
| 164 |
+
"""Rotates half the hidden dims of the input."""
|
| 165 |
+
x1 = x[..., : x.shape[-1] // 2]
|
| 166 |
+
x2 = x[..., x.shape[-1] // 2 :]
|
| 167 |
+
return torch.cat((-x2, x1), dim=-1)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None, unsqueeze_dim=1):
|
| 171 |
+
"""Applies Rotary Position Embedding to the query and key tensors.
|
| 172 |
+
|
| 173 |
+
Args:
|
| 174 |
+
q (`torch.Tensor`): The query tensor.
|
| 175 |
+
k (`torch.Tensor`): The key tensor.
|
| 176 |
+
cos (`torch.Tensor`): The cosine part of the rotary embedding.
|
| 177 |
+
sin (`torch.Tensor`): The sine part of the rotary embedding.
|
| 178 |
+
position_ids (`torch.Tensor`, *optional*):
|
| 179 |
+
Deprecated and unused.
|
| 180 |
+
unsqueeze_dim (`int`, *optional*, defaults to 1):
|
| 181 |
+
The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
|
| 182 |
+
sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
|
| 183 |
+
that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
|
| 184 |
+
k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
|
| 185 |
+
cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
|
| 186 |
+
the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
|
| 187 |
+
Returns:
|
| 188 |
+
`tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
|
| 189 |
+
"""
|
| 190 |
+
cos = cos.unsqueeze(unsqueeze_dim)
|
| 191 |
+
sin = sin.unsqueeze(unsqueeze_dim)
|
| 192 |
+
q_embed = (q * cos) + (rotate_half(q) * sin)
|
| 193 |
+
k_embed = (k * cos) + (rotate_half(k) * sin)
|
| 194 |
+
return q_embed, k_embed
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
|
| 198 |
+
"""
|
| 199 |
+
This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
|
| 200 |
+
num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
|
| 201 |
+
"""
|
| 202 |
+
batch, num_key_value_heads, slen, head_dim = hidden_states.shape
|
| 203 |
+
if n_rep == 1:
|
| 204 |
+
return hidden_states
|
| 205 |
+
hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
|
| 206 |
+
return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def _gated_residual(x, y, gate):
|
| 210 |
+
"""
|
| 211 |
+
Applies gated residual connection with optional gate parameter.
|
| 212 |
+
|
| 213 |
+
Args:
|
| 214 |
+
x: Input tensor (residual)
|
| 215 |
+
y: Output tensor to be added
|
| 216 |
+
gate: Optional gate tensor to modulate the addition
|
| 217 |
+
|
| 218 |
+
Returns:
|
| 219 |
+
x + y if gate is None, otherwise x + y * gate
|
| 220 |
+
"""
|
| 221 |
+
if x is None and y is None:
|
| 222 |
+
return None
|
| 223 |
+
if x is None or y is None:
|
| 224 |
+
return x if x is not None else y
|
| 225 |
+
if gate is None:
|
| 226 |
+
return x + y
|
| 227 |
+
return x + y * gate
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def eager_attention_forward(
|
| 231 |
+
module: nn.Module,
|
| 232 |
+
query: torch.Tensor,
|
| 233 |
+
key: torch.Tensor,
|
| 234 |
+
value: torch.Tensor,
|
| 235 |
+
attention_mask: Optional[torch.Tensor],
|
| 236 |
+
scaling: float,
|
| 237 |
+
dropout: float = 0.0,
|
| 238 |
+
**kwargs,
|
| 239 |
+
):
|
| 240 |
+
key_states = repeat_kv(key, module.num_key_value_groups)
|
| 241 |
+
value_states = repeat_kv(value, module.num_key_value_groups)
|
| 242 |
+
|
| 243 |
+
attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
|
| 244 |
+
if attention_mask is not None:
|
| 245 |
+
causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
|
| 246 |
+
attn_weights = attn_weights + causal_mask
|
| 247 |
+
|
| 248 |
+
attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
|
| 249 |
+
attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
|
| 250 |
+
attn_output = torch.matmul(attn_weights, value_states)
|
| 251 |
+
attn_output = attn_output.transpose(1, 2).contiguous()
|
| 252 |
+
|
| 253 |
+
return attn_output, attn_weights
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
class GemmaAttention(nn.Module):
|
| 257 |
+
"""Multi-headed attention from 'Attention Is All You Need' paper"""
|
| 258 |
+
|
| 259 |
+
def __init__(self, config: GemmaConfig, layer_idx: int):
|
| 260 |
+
super().__init__()
|
| 261 |
+
self.config = config
|
| 262 |
+
self.layer_idx = layer_idx
|
| 263 |
+
self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
|
| 264 |
+
self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
|
| 265 |
+
self.scaling = self.head_dim**-0.5
|
| 266 |
+
self.attention_dropout = config.attention_dropout
|
| 267 |
+
self.is_causal = True
|
| 268 |
+
|
| 269 |
+
self.q_proj = nn.Linear(
|
| 270 |
+
config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias
|
| 271 |
+
)
|
| 272 |
+
self.k_proj = nn.Linear(
|
| 273 |
+
config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
|
| 274 |
+
)
|
| 275 |
+
self.v_proj = nn.Linear(
|
| 276 |
+
config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias
|
| 277 |
+
)
|
| 278 |
+
self.o_proj = nn.Linear(
|
| 279 |
+
config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias
|
| 280 |
+
)
|
| 281 |
+
|
| 282 |
+
def forward(
|
| 283 |
+
self,
|
| 284 |
+
hidden_states: torch.Tensor,
|
| 285 |
+
position_embeddings: tuple[torch.Tensor, torch.Tensor],
|
| 286 |
+
attention_mask: Optional[torch.Tensor],
|
| 287 |
+
past_key_value: Optional[Cache] = None,
|
| 288 |
+
cache_position: Optional[torch.LongTensor] = None,
|
| 289 |
+
use_cache: bool = False,
|
| 290 |
+
**kwargs: Unpack[FlashAttentionKwargs],
|
| 291 |
+
) -> tuple[torch.Tensor, Optional[torch.Tensor], Optional[tuple[torch.Tensor]]]:
|
| 292 |
+
input_shape = hidden_states.shape[:-1]
|
| 293 |
+
hidden_shape = (*input_shape, -1, self.head_dim)
|
| 294 |
+
|
| 295 |
+
query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 296 |
+
key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 297 |
+
value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 298 |
+
|
| 299 |
+
cos, sin = position_embeddings
|
| 300 |
+
query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
|
| 301 |
+
|
| 302 |
+
# Use cache if provided
|
| 303 |
+
if past_key_value is not None:
|
| 304 |
+
if use_cache:
|
| 305 |
+
# sin and cos are specific to RoPE models; cache_position needed for the static cache
|
| 306 |
+
cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position}
|
| 307 |
+
key_states, value_states = past_key_value.update(key_states, value_states, self.layer_idx, cache_kwargs)
|
| 308 |
+
else:
|
| 309 |
+
key_states = torch.cat([past_key_value[self.layer_idx][0], key_states], dim=2)
|
| 310 |
+
value_states = torch.cat([past_key_value[self.layer_idx][1], value_states], dim=2)
|
| 311 |
+
|
| 312 |
+
attention_interface: Callable = eager_attention_forward
|
| 313 |
+
if self.config._attn_implementation != "eager":
|
| 314 |
+
attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
|
| 315 |
+
|
| 316 |
+
attn_output, attn_weights = attention_interface(
|
| 317 |
+
self,
|
| 318 |
+
query_states,
|
| 319 |
+
key_states,
|
| 320 |
+
value_states,
|
| 321 |
+
attention_mask,
|
| 322 |
+
dropout=0.0 if not self.training else self.attention_dropout,
|
| 323 |
+
scaling=self.scaling,
|
| 324 |
+
**kwargs,
|
| 325 |
+
)
|
| 326 |
+
|
| 327 |
+
attn_output = attn_output.reshape(*input_shape, -1).contiguous()
|
| 328 |
+
attn_output = self.o_proj(attn_output)
|
| 329 |
+
return attn_output, attn_weights
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
class GemmaDecoderLayer(GradientCheckpointingLayer):
|
| 333 |
+
def __init__(self, config: GemmaConfig, layer_idx: int):
|
| 334 |
+
super().__init__()
|
| 335 |
+
self.hidden_size = config.hidden_size
|
| 336 |
+
|
| 337 |
+
self.self_attn = GemmaAttention(config=config, layer_idx=layer_idx)
|
| 338 |
+
|
| 339 |
+
self.mlp = GemmaMLP(config)
|
| 340 |
+
cond_dim = getattr(config, 'adarms_cond_dim', None) if getattr(config, 'use_adarms', False) else None
|
| 341 |
+
self.input_layernorm = GemmaRMSNorm(config.hidden_size, eps=config.rms_norm_eps, cond_dim=cond_dim)
|
| 342 |
+
self.post_attention_layernorm = GemmaRMSNorm(config.hidden_size, eps=config.rms_norm_eps, cond_dim=cond_dim)
|
| 343 |
+
|
| 344 |
+
def forward(
|
| 345 |
+
self,
|
| 346 |
+
hidden_states: torch.Tensor,
|
| 347 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 348 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 349 |
+
past_key_value: Optional[Cache] = None,
|
| 350 |
+
output_attentions: Optional[bool] = False,
|
| 351 |
+
use_cache: Optional[bool] = False,
|
| 352 |
+
cache_position: Optional[torch.LongTensor] = None,
|
| 353 |
+
position_embeddings: Optional[tuple[torch.Tensor, torch.Tensor]] = None, # necessary, but kept here for BC
|
| 354 |
+
adarms_cond: Optional[torch.Tensor] = None,
|
| 355 |
+
**kwargs: Unpack[FlashAttentionKwargs],
|
| 356 |
+
) -> tuple[torch.FloatTensor, Optional[tuple[torch.FloatTensor, torch.FloatTensor]]]:
|
| 357 |
+
residual = hidden_states
|
| 358 |
+
hidden_states, gate = self.input_layernorm(hidden_states, adarms_cond)
|
| 359 |
+
|
| 360 |
+
# Self Attention
|
| 361 |
+
hidden_states, self_attn_weights = self.self_attn(
|
| 362 |
+
hidden_states=hidden_states,
|
| 363 |
+
attention_mask=attention_mask,
|
| 364 |
+
position_ids=position_ids,
|
| 365 |
+
past_key_value=past_key_value,
|
| 366 |
+
output_attentions=output_attentions,
|
| 367 |
+
use_cache=use_cache,
|
| 368 |
+
cache_position=cache_position,
|
| 369 |
+
position_embeddings=position_embeddings,
|
| 370 |
+
**kwargs,
|
| 371 |
+
)
|
| 372 |
+
hidden_states = _gated_residual(residual, hidden_states, gate)
|
| 373 |
+
|
| 374 |
+
# Fully Connected
|
| 375 |
+
residual = hidden_states
|
| 376 |
+
hidden_states, gate = self.post_attention_layernorm(hidden_states, adarms_cond)
|
| 377 |
+
hidden_states = self.mlp(hidden_states)
|
| 378 |
+
hidden_states = _gated_residual(residual, hidden_states, gate)
|
| 379 |
+
|
| 380 |
+
outputs = (hidden_states,)
|
| 381 |
+
if output_attentions:
|
| 382 |
+
outputs += (self_attn_weights,)
|
| 383 |
+
|
| 384 |
+
return outputs
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
@auto_docstring
|
| 388 |
+
class GemmaPreTrainedModel(PreTrainedModel):
|
| 389 |
+
config_class = GemmaConfig
|
| 390 |
+
base_model_prefix = "model"
|
| 391 |
+
supports_gradient_checkpointing = True
|
| 392 |
+
_no_split_modules = ["GemmaDecoderLayer"]
|
| 393 |
+
_skip_keys_device_placement = ["past_key_values"]
|
| 394 |
+
_supports_flash_attn_3 = True
|
| 395 |
+
_supports_flash_attn_2 = True
|
| 396 |
+
_supports_sdpa = True
|
| 397 |
+
_supports_flex_attn = True
|
| 398 |
+
_supports_cache_class = True
|
| 399 |
+
_supports_quantized_cache = True
|
| 400 |
+
_supports_static_cache = True
|
| 401 |
+
_supports_attention_backend = True
|
| 402 |
+
|
| 403 |
+
def _init_weights(self, module):
|
| 404 |
+
std = self.config.initializer_range
|
| 405 |
+
if isinstance(module, nn.Linear):
|
| 406 |
+
module.weight.data.normal_(mean=0.0, std=std)
|
| 407 |
+
if module.bias is not None:
|
| 408 |
+
module.bias.data.zero_()
|
| 409 |
+
elif isinstance(module, nn.Embedding):
|
| 410 |
+
module.weight.data.normal_(mean=0.0, std=std)
|
| 411 |
+
if module.padding_idx is not None:
|
| 412 |
+
module.weight.data[module.padding_idx].zero_()
|
| 413 |
+
elif isinstance(module, GemmaRMSNorm):
|
| 414 |
+
if hasattr(module, 'weight'):
|
| 415 |
+
module.weight.data.fill_(1.0)
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
@auto_docstring
|
| 419 |
+
class GemmaModel(GemmaPreTrainedModel):
|
| 420 |
+
def __init__(self, config: GemmaConfig):
|
| 421 |
+
super().__init__(config)
|
| 422 |
+
self.padding_idx = config.pad_token_id
|
| 423 |
+
self.vocab_size = config.vocab_size
|
| 424 |
+
|
| 425 |
+
self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
|
| 426 |
+
self.layers = nn.ModuleList(
|
| 427 |
+
[GemmaDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
|
| 428 |
+
)
|
| 429 |
+
|
| 430 |
+
cond_dim = getattr(config, 'adarms_cond_dim', None) if getattr(config, 'use_adarms', False) else None
|
| 431 |
+
self.norm = GemmaRMSNorm(config.hidden_size, eps=config.rms_norm_eps, cond_dim=cond_dim)
|
| 432 |
+
self.rotary_emb = GemmaRotaryEmbedding(config=config)
|
| 433 |
+
self.gradient_checkpointing = False
|
| 434 |
+
|
| 435 |
+
# Initialize weights and apply final processing
|
| 436 |
+
self.post_init()
|
| 437 |
+
|
| 438 |
+
def get_input_embeddings(self):
|
| 439 |
+
return self.embed_tokens
|
| 440 |
+
|
| 441 |
+
def set_input_embeddings(self, value):
|
| 442 |
+
self.embed_tokens = value
|
| 443 |
+
|
| 444 |
+
@can_return_tuple
|
| 445 |
+
@auto_docstring
|
| 446 |
+
def forward(
|
| 447 |
+
self,
|
| 448 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 449 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 450 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 451 |
+
past_key_values: Optional[Cache] = None,
|
| 452 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 453 |
+
use_cache: Optional[bool] = None,
|
| 454 |
+
output_attentions: Optional[bool] = None,
|
| 455 |
+
output_hidden_states: Optional[bool] = None,
|
| 456 |
+
cache_position: Optional[torch.LongTensor] = None,
|
| 457 |
+
adarms_cond: Optional[torch.Tensor] = None,
|
| 458 |
+
**kwargs: Unpack[FlashAttentionKwargs],
|
| 459 |
+
) -> BaseModelOutputWithPast:
|
| 460 |
+
"""
|
| 461 |
+
adarms_cond (`torch.Tensor` of shape `(batch_size, cond_dim)`, *optional*):
|
| 462 |
+
Condition for ADARMS.
|
| 463 |
+
"""
|
| 464 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 465 |
+
output_hidden_states = (
|
| 466 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 467 |
+
)
|
| 468 |
+
use_cache = use_cache if use_cache is not None else self.config.use_cache
|
| 469 |
+
|
| 470 |
+
if (input_ids is None) ^ (inputs_embeds is not None):
|
| 471 |
+
raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
|
| 472 |
+
|
| 473 |
+
if self.gradient_checkpointing and self.training and use_cache:
|
| 474 |
+
logger.warning_once(
|
| 475 |
+
"`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`."
|
| 476 |
+
)
|
| 477 |
+
use_cache = False
|
| 478 |
+
|
| 479 |
+
if inputs_embeds is None:
|
| 480 |
+
inputs_embeds = self.embed_tokens(input_ids)
|
| 481 |
+
|
| 482 |
+
if use_cache and past_key_values is None:
|
| 483 |
+
past_key_values = DynamicCache()
|
| 484 |
+
|
| 485 |
+
if cache_position is None:
|
| 486 |
+
past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
|
| 487 |
+
cache_position = torch.arange(
|
| 488 |
+
past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device
|
| 489 |
+
)
|
| 490 |
+
|
| 491 |
+
if position_ids is None:
|
| 492 |
+
position_ids = cache_position.unsqueeze(0)
|
| 493 |
+
|
| 494 |
+
causal_mask = create_causal_mask(
|
| 495 |
+
config=self.config,
|
| 496 |
+
input_embeds=inputs_embeds,
|
| 497 |
+
attention_mask=attention_mask,
|
| 498 |
+
cache_position=cache_position,
|
| 499 |
+
past_key_values=past_key_values,
|
| 500 |
+
position_ids=position_ids,
|
| 501 |
+
)
|
| 502 |
+
|
| 503 |
+
# embed positions
|
| 504 |
+
hidden_states = inputs_embeds
|
| 505 |
+
# Convert to bfloat16 if the first layer uses bfloat16
|
| 506 |
+
if len(self.layers) > 0 and self.layers[0].self_attn.q_proj.weight.dtype == torch.bfloat16:
|
| 507 |
+
hidden_states = hidden_states.to(torch.bfloat16)
|
| 508 |
+
|
| 509 |
+
# create position embeddings to be shared across the decoder layers
|
| 510 |
+
position_embeddings = self.rotary_emb(hidden_states, position_ids)
|
| 511 |
+
|
| 512 |
+
# normalized
|
| 513 |
+
# Gemma downcasts the below to float16, causing sqrt(3072)=55.4256 to become 55.5
|
| 514 |
+
# See https://github.com/huggingface/transformers/pull/29402
|
| 515 |
+
normalizer = torch.tensor(self.config.hidden_size**0.5, dtype=hidden_states.dtype)
|
| 516 |
+
#hidden_states = hidden_states * normalizer
|
| 517 |
+
|
| 518 |
+
# decoder layers
|
| 519 |
+
all_hidden_states = () if output_hidden_states else None
|
| 520 |
+
all_self_attns = () if output_attentions else None
|
| 521 |
+
|
| 522 |
+
for decoder_layer in self.layers[: self.config.num_hidden_layers]:
|
| 523 |
+
if output_hidden_states:
|
| 524 |
+
all_hidden_states += (hidden_states,)
|
| 525 |
+
|
| 526 |
+
layer_outputs = decoder_layer(
|
| 527 |
+
hidden_states,
|
| 528 |
+
attention_mask=causal_mask,
|
| 529 |
+
position_ids=position_ids,
|
| 530 |
+
past_key_value=past_key_values,
|
| 531 |
+
output_attentions=output_attentions,
|
| 532 |
+
use_cache=use_cache,
|
| 533 |
+
cache_position=cache_position,
|
| 534 |
+
position_embeddings=position_embeddings,
|
| 535 |
+
adarms_cond=adarms_cond,
|
| 536 |
+
**kwargs,
|
| 537 |
+
)
|
| 538 |
+
|
| 539 |
+
hidden_states = layer_outputs[0]
|
| 540 |
+
|
| 541 |
+
if output_attentions:
|
| 542 |
+
all_self_attns += (layer_outputs[1],)
|
| 543 |
+
|
| 544 |
+
hidden_states, _ = self.norm(hidden_states, adarms_cond)
|
| 545 |
+
|
| 546 |
+
# add hidden states from the last decoder layer
|
| 547 |
+
if output_hidden_states:
|
| 548 |
+
all_hidden_states += (hidden_states,)
|
| 549 |
+
|
| 550 |
+
return BaseModelOutputWithPast(
|
| 551 |
+
last_hidden_state=hidden_states,
|
| 552 |
+
past_key_values=past_key_values if use_cache else None,
|
| 553 |
+
hidden_states=all_hidden_states,
|
| 554 |
+
attentions=all_self_attns,
|
| 555 |
+
)
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
class KwargsForCausalLM(FlashAttentionKwargs, LossKwargs): ...
|
| 559 |
+
|
| 560 |
+
|
| 561 |
+
@auto_docstring
|
| 562 |
+
class GemmaForCausalLM(GemmaPreTrainedModel, GenerationMixin):
|
| 563 |
+
_tied_weights_keys = ["lm_head.weight"]
|
| 564 |
+
_tp_plan = {"lm_head": "colwise_rep"}
|
| 565 |
+
_pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
|
| 566 |
+
|
| 567 |
+
def __init__(self, config):
|
| 568 |
+
super().__init__(config)
|
| 569 |
+
self.model = GemmaModel(config)
|
| 570 |
+
self.vocab_size = config.vocab_size
|
| 571 |
+
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
|
| 572 |
+
|
| 573 |
+
# Initialize weights and apply final processing
|
| 574 |
+
self.post_init()
|
| 575 |
+
|
| 576 |
+
def get_input_embeddings(self):
|
| 577 |
+
return self.model.embed_tokens
|
| 578 |
+
|
| 579 |
+
def set_input_embeddings(self, value):
|
| 580 |
+
self.model.embed_tokens = value
|
| 581 |
+
|
| 582 |
+
def get_output_embeddings(self):
|
| 583 |
+
return self.lm_head
|
| 584 |
+
|
| 585 |
+
def set_output_embeddings(self, new_embeddings):
|
| 586 |
+
self.lm_head = new_embeddings
|
| 587 |
+
|
| 588 |
+
def set_decoder(self, decoder):
|
| 589 |
+
self.model = decoder
|
| 590 |
+
|
| 591 |
+
def get_decoder(self):
|
| 592 |
+
return self.model
|
| 593 |
+
|
| 594 |
+
@can_return_tuple
|
| 595 |
+
@auto_docstring
|
| 596 |
+
def forward(
|
| 597 |
+
self,
|
| 598 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 599 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 600 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 601 |
+
past_key_values: Optional[Cache] = None,
|
| 602 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 603 |
+
labels: Optional[torch.LongTensor] = None,
|
| 604 |
+
use_cache: Optional[bool] = None,
|
| 605 |
+
output_attentions: Optional[bool] = None,
|
| 606 |
+
output_hidden_states: Optional[bool] = None,
|
| 607 |
+
cache_position: Optional[torch.LongTensor] = None,
|
| 608 |
+
logits_to_keep: Union[int, torch.Tensor] = 0,
|
| 609 |
+
adarms_cond: Optional[torch.Tensor] = None,
|
| 610 |
+
**kwargs: Unpack[KwargsForCausalLM],
|
| 611 |
+
) -> CausalLMOutputWithPast:
|
| 612 |
+
r"""
|
| 613 |
+
labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
|
| 614 |
+
Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
|
| 615 |
+
config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
|
| 616 |
+
(masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
|
| 617 |
+
|
| 618 |
+
adarms_cond (`torch.Tensor` of shape `(batch_size, cond_dim)`, *optional*):
|
| 619 |
+
Condition for ADARMS.
|
| 620 |
+
|
| 621 |
+
Example:
|
| 622 |
+
|
| 623 |
+
```python
|
| 624 |
+
>>> from transformers import AutoTokenizer, GemmaForCausalLM
|
| 625 |
+
|
| 626 |
+
>>> model = GemmaForCausalLM.from_pretrained("google/gemma-7b")
|
| 627 |
+
>>> tokenizer = AutoTokenizer.from_pretrained("google/gemma-7b")
|
| 628 |
+
|
| 629 |
+
>>> prompt = "What is your favorite condiment?"
|
| 630 |
+
>>> inputs = tokenizer(prompt, return_tensors="pt")
|
| 631 |
+
|
| 632 |
+
>>> # Generate
|
| 633 |
+
>>> generate_ids = model.generate(inputs.input_ids, max_length=30)
|
| 634 |
+
>>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
|
| 635 |
+
"What is your favorite condiment?"
|
| 636 |
+
```"""
|
| 637 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 638 |
+
output_hidden_states = (
|
| 639 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 640 |
+
)
|
| 641 |
+
|
| 642 |
+
# decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
|
| 643 |
+
outputs: BaseModelOutputWithPast = self.model(
|
| 644 |
+
input_ids=input_ids,
|
| 645 |
+
attention_mask=attention_mask,
|
| 646 |
+
position_ids=position_ids,
|
| 647 |
+
past_key_values=past_key_values,
|
| 648 |
+
inputs_embeds=inputs_embeds,
|
| 649 |
+
use_cache=use_cache,
|
| 650 |
+
output_attentions=output_attentions,
|
| 651 |
+
output_hidden_states=output_hidden_states,
|
| 652 |
+
cache_position=cache_position,
|
| 653 |
+
adarms_cond=adarms_cond,
|
| 654 |
+
**kwargs,
|
| 655 |
+
)
|
| 656 |
+
|
| 657 |
+
hidden_states = outputs.last_hidden_state
|
| 658 |
+
# Only compute necessary logits, and do not upcast them to float if we are not computing the loss
|
| 659 |
+
slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
|
| 660 |
+
logits = self.lm_head(hidden_states[:, slice_indices, :])
|
| 661 |
+
|
| 662 |
+
loss = None
|
| 663 |
+
if labels is not None:
|
| 664 |
+
loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs)
|
| 665 |
+
|
| 666 |
+
return CausalLMOutputWithPast(
|
| 667 |
+
loss=loss,
|
| 668 |
+
logits=logits,
|
| 669 |
+
past_key_values=outputs.past_key_values,
|
| 670 |
+
hidden_states=outputs.hidden_states,
|
| 671 |
+
attentions=outputs.attentions,
|
| 672 |
+
)
|
| 673 |
+
|
| 674 |
+
|
| 675 |
+
@auto_docstring(
|
| 676 |
+
custom_intro="""
|
| 677 |
+
The Gemma Model transformer with a sequence classification head on top (linear layer).
|
| 678 |
+
|
| 679 |
+
[`GemmaForSequenceClassification`] uses the last token in order to do the classification, as other causal models
|
| 680 |
+
(e.g. GPT-2) do.
|
| 681 |
+
|
| 682 |
+
Since it does classification on the last token, it requires to know the position of the last token. If a
|
| 683 |
+
`pad_token_id` is defined in the configuration, it finds the last token that is not a padding token in each row. If
|
| 684 |
+
no `pad_token_id` is defined, it simply takes the last value in each row of the batch. Since it cannot guess the
|
| 685 |
+
padding tokens when `inputs_embeds` are passed instead of `input_ids`, it does the same (take the last value in
|
| 686 |
+
each row of the batch).
|
| 687 |
+
"""
|
| 688 |
+
)
|
| 689 |
+
class GemmaForSequenceClassification(GemmaPreTrainedModel):
|
| 690 |
+
def __init__(self, config):
|
| 691 |
+
super().__init__(config)
|
| 692 |
+
self.num_labels = config.num_labels
|
| 693 |
+
self.model = GemmaModel(config)
|
| 694 |
+
self.score = nn.Linear(config.hidden_size, self.num_labels, bias=False)
|
| 695 |
+
|
| 696 |
+
# Initialize weights and apply final processing
|
| 697 |
+
self.post_init()
|
| 698 |
+
|
| 699 |
+
def get_input_embeddings(self):
|
| 700 |
+
return self.model.embed_tokens
|
| 701 |
+
|
| 702 |
+
def set_input_embeddings(self, value):
|
| 703 |
+
self.model.embed_tokens = value
|
| 704 |
+
|
| 705 |
+
@can_return_tuple
|
| 706 |
+
@auto_docstring
|
| 707 |
+
def forward(
|
| 708 |
+
self,
|
| 709 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 710 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 711 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 712 |
+
past_key_values: Optional[Cache] = None,
|
| 713 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 714 |
+
labels: Optional[torch.LongTensor] = None,
|
| 715 |
+
use_cache: Optional[bool] = None,
|
| 716 |
+
output_attentions: Optional[bool] = None,
|
| 717 |
+
output_hidden_states: Optional[bool] = None,
|
| 718 |
+
adarms_cond: Optional[torch.Tensor] = None,
|
| 719 |
+
) -> SequenceClassifierOutputWithPast:
|
| 720 |
+
r"""
|
| 721 |
+
labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
|
| 722 |
+
Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
|
| 723 |
+
config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
|
| 724 |
+
`config.num_labels > 1` a classification loss is computed (Cross-Entropy).
|
| 725 |
+
|
| 726 |
+
adarms_cond (`torch.Tensor` of shape `(batch_size, cond_dim)`, *optional*):
|
| 727 |
+
Condition for ADARMS.
|
| 728 |
+
"""
|
| 729 |
+
|
| 730 |
+
transformer_outputs: BaseModelOutputWithPast = self.model(
|
| 731 |
+
input_ids,
|
| 732 |
+
attention_mask=attention_mask,
|
| 733 |
+
position_ids=position_ids,
|
| 734 |
+
past_key_values=past_key_values,
|
| 735 |
+
inputs_embeds=inputs_embeds,
|
| 736 |
+
use_cache=use_cache,
|
| 737 |
+
output_attentions=output_attentions,
|
| 738 |
+
output_hidden_states=output_hidden_states,
|
| 739 |
+
adarms_cond=adarms_cond,
|
| 740 |
+
)
|
| 741 |
+
hidden_states = transformer_outputs.last_hidden_state
|
| 742 |
+
logits = self.score(hidden_states)
|
| 743 |
+
|
| 744 |
+
if input_ids is not None:
|
| 745 |
+
batch_size = input_ids.shape[0]
|
| 746 |
+
else:
|
| 747 |
+
batch_size = inputs_embeds.shape[0]
|
| 748 |
+
|
| 749 |
+
if self.config.pad_token_id is None and batch_size != 1:
|
| 750 |
+
raise ValueError("Cannot handle batch sizes > 1 if no padding token is defined.")
|
| 751 |
+
if self.config.pad_token_id is None:
|
| 752 |
+
last_non_pad_token = -1
|
| 753 |
+
elif input_ids is not None:
|
| 754 |
+
# To handle both left- and right- padding, we take the rightmost token that is not equal to pad_token_id
|
| 755 |
+
non_pad_mask = (input_ids != self.config.pad_token_id).to(logits.device, torch.int32)
|
| 756 |
+
token_indices = torch.arange(input_ids.shape[-1], device=logits.device, dtype=torch.int32)
|
| 757 |
+
last_non_pad_token = (token_indices * non_pad_mask).argmax(-1)
|
| 758 |
+
else:
|
| 759 |
+
last_non_pad_token = -1
|
| 760 |
+
logger.warning_once(
|
| 761 |
+
f"{self.__class__.__name__} will not detect padding tokens in `inputs_embeds`. Results may be "
|
| 762 |
+
"unexpected if using padding tokens in conjunction with `inputs_embeds.`"
|
| 763 |
+
)
|
| 764 |
+
|
| 765 |
+
pooled_logits = logits[torch.arange(batch_size, device=logits.device), last_non_pad_token]
|
| 766 |
+
|
| 767 |
+
loss = None
|
| 768 |
+
if labels is not None:
|
| 769 |
+
loss = self.loss_function(logits=logits, labels=labels, pooled_logits=pooled_logits, config=self.config)
|
| 770 |
+
|
| 771 |
+
return SequenceClassifierOutputWithPast(
|
| 772 |
+
loss=loss,
|
| 773 |
+
logits=pooled_logits,
|
| 774 |
+
past_key_values=transformer_outputs.past_key_values,
|
| 775 |
+
hidden_states=transformer_outputs.hidden_states,
|
| 776 |
+
attentions=transformer_outputs.attentions,
|
| 777 |
+
)
|
| 778 |
+
|
| 779 |
+
|
| 780 |
+
@auto_docstring
|
| 781 |
+
class GemmaForTokenClassification(GemmaPreTrainedModel):
|
| 782 |
+
def __init__(self, config):
|
| 783 |
+
super().__init__(config)
|
| 784 |
+
self.num_labels = config.num_labels
|
| 785 |
+
self.model = GemmaModel(config)
|
| 786 |
+
if getattr(config, "classifier_dropout", None) is not None:
|
| 787 |
+
classifier_dropout = config.classifier_dropout
|
| 788 |
+
elif getattr(config, "hidden_dropout", None) is not None:
|
| 789 |
+
classifier_dropout = config.hidden_dropout
|
| 790 |
+
else:
|
| 791 |
+
classifier_dropout = 0.1
|
| 792 |
+
self.dropout = nn.Dropout(classifier_dropout)
|
| 793 |
+
self.score = nn.Linear(config.hidden_size, config.num_labels)
|
| 794 |
+
|
| 795 |
+
# Initialize weights and apply final processing
|
| 796 |
+
self.post_init()
|
| 797 |
+
|
| 798 |
+
def get_input_embeddings(self):
|
| 799 |
+
return self.model.embed_tokens
|
| 800 |
+
|
| 801 |
+
def set_input_embeddings(self, value):
|
| 802 |
+
self.model.embed_tokens = value
|
| 803 |
+
|
| 804 |
+
@can_return_tuple
|
| 805 |
+
@auto_docstring
|
| 806 |
+
def forward(
|
| 807 |
+
self,
|
| 808 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 809 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 810 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 811 |
+
past_key_values: Optional[Cache] = None,
|
| 812 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 813 |
+
labels: Optional[torch.LongTensor] = None,
|
| 814 |
+
use_cache: Optional[bool] = None,
|
| 815 |
+
output_attentions: Optional[bool] = None,
|
| 816 |
+
output_hidden_states: Optional[bool] = None,
|
| 817 |
+
adarms_cond: Optional[torch.Tensor] = None,
|
| 818 |
+
) -> TokenClassifierOutput:
|
| 819 |
+
r"""
|
| 820 |
+
labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
|
| 821 |
+
Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
|
| 822 |
+
config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
|
| 823 |
+
`config.num_labels > 1` a classification loss is computed (Cross-Entropy).
|
| 824 |
+
|
| 825 |
+
adarms_cond (`torch.Tensor` of shape `(batch_size, cond_dim)`, *optional*):
|
| 826 |
+
Condition for ADARMS.
|
| 827 |
+
"""
|
| 828 |
+
|
| 829 |
+
outputs: BaseModelOutputWithPast = self.model(
|
| 830 |
+
input_ids,
|
| 831 |
+
attention_mask=attention_mask,
|
| 832 |
+
position_ids=position_ids,
|
| 833 |
+
past_key_values=past_key_values,
|
| 834 |
+
inputs_embeds=inputs_embeds,
|
| 835 |
+
use_cache=use_cache,
|
| 836 |
+
output_attentions=output_attentions,
|
| 837 |
+
output_hidden_states=output_hidden_states,
|
| 838 |
+
adarms_cond=adarms_cond,
|
| 839 |
+
)
|
| 840 |
+
sequence_output = outputs.last_hidden_state
|
| 841 |
+
sequence_output = self.dropout(sequence_output)
|
| 842 |
+
logits = self.score(sequence_output)
|
| 843 |
+
|
| 844 |
+
loss = None
|
| 845 |
+
if labels is not None:
|
| 846 |
+
loss = self.loss_function(logits, labels, self.config)
|
| 847 |
+
|
| 848 |
+
return TokenClassifierOutput(
|
| 849 |
+
loss=loss,
|
| 850 |
+
logits=logits,
|
| 851 |
+
hidden_states=outputs.hidden_states,
|
| 852 |
+
attentions=outputs.attentions,
|
| 853 |
+
)
|
| 854 |
+
|
| 855 |
+
|
| 856 |
+
__all__ = [
|
| 857 |
+
"GemmaModel",
|
| 858 |
+
"GemmaForCausalLM",
|
| 859 |
+
"GemmaForSequenceClassification",
|
| 860 |
+
"GemmaForTokenClassification",
|
| 861 |
+
"GemmaPreTrainedModel",
|
| 862 |
+
]
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/paligemma/modeling_paligemma.py
ADDED
|
@@ -0,0 +1,622 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2024 the HuggingFace Inc. team. All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""PyTorch PaliGemmamodel."""
|
| 16 |
+
|
| 17 |
+
from dataclasses import dataclass
|
| 18 |
+
from typing import Optional, Union
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
import torch.utils.checkpoint
|
| 22 |
+
from torch import nn
|
| 23 |
+
|
| 24 |
+
from ...cache_utils import Cache, HybridCache, StaticCache
|
| 25 |
+
from ...generation import GenerationMixin
|
| 26 |
+
from ...modeling_flash_attention_utils import FlashAttentionKwargs
|
| 27 |
+
from ...modeling_outputs import BaseModelOutputWithPast
|
| 28 |
+
from ...modeling_utils import PreTrainedModel
|
| 29 |
+
from ...processing_utils import Unpack
|
| 30 |
+
from ...utils import LossKwargs, ModelOutput, auto_docstring, can_return_tuple, is_torchdynamo_compiling, logging
|
| 31 |
+
from ..auto import AutoModel
|
| 32 |
+
from .configuration_paligemma import PaliGemmaConfig
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
logger = logging.get_logger(__name__)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@dataclass
|
| 39 |
+
@auto_docstring(
|
| 40 |
+
custom_intro="""
|
| 41 |
+
Base class for Paligemma outputs, with hidden states and attentions.
|
| 42 |
+
"""
|
| 43 |
+
)
|
| 44 |
+
class PaligemmaModelOutputWithPast(BaseModelOutputWithPast):
|
| 45 |
+
r"""
|
| 46 |
+
past_key_values (`tuple(tuple(torch.FloatTensor))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
|
| 47 |
+
Tuple of `tuple(torch.FloatTensor)` of length `config.n_layers`, with each tuple having 2 tensors of shape
|
| 48 |
+
`(batch_size, num_heads, sequence_length, embed_size_per_head)`)
|
| 49 |
+
|
| 50 |
+
Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
|
| 51 |
+
`past_key_values` input) to speed up sequential decoding.
|
| 52 |
+
image_hidden_states (`torch.FloatTensor`, *optional*):
|
| 53 |
+
A `torch.FloatTensor` of size `(batch_size, num_images, sequence_length, hidden_size)`.
|
| 54 |
+
image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
image_hidden_states: Optional[torch.FloatTensor] = None
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
@dataclass
|
| 61 |
+
@auto_docstring(
|
| 62 |
+
custom_intro="""
|
| 63 |
+
Base class for PaliGemma causal language model (or autoregressive) outputs.
|
| 64 |
+
"""
|
| 65 |
+
)
|
| 66 |
+
class PaliGemmaCausalLMOutputWithPast(ModelOutput):
|
| 67 |
+
r"""
|
| 68 |
+
loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
|
| 69 |
+
Language modeling loss (for next-token prediction).
|
| 70 |
+
logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.text_config.vocab_size)`):
|
| 71 |
+
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
|
| 72 |
+
past_key_values (`tuple(tuple(torch.FloatTensor))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
|
| 73 |
+
Tuple of `tuple(torch.FloatTensor)` of length `config.n_layers`, with each tuple having 2 tensors of shape
|
| 74 |
+
`(batch_size, num_heads, sequence_length, embed_size_per_head)`)
|
| 75 |
+
|
| 76 |
+
Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
|
| 77 |
+
`past_key_values` input) to speed up sequential decoding.
|
| 78 |
+
image_hidden_states (`torch.FloatTensor`, *optional*):
|
| 79 |
+
A `torch.FloatTensor` of size `(batch_size, num_images, sequence_length, hidden_size)`.
|
| 80 |
+
image_hidden_states of the model produced by the vision encoder after projecting last hidden state.
|
| 81 |
+
"""
|
| 82 |
+
|
| 83 |
+
loss: Optional[torch.FloatTensor] = None
|
| 84 |
+
logits: Optional[torch.FloatTensor] = None
|
| 85 |
+
past_key_values: Optional[Union[list[torch.FloatTensor], Cache]] = None
|
| 86 |
+
hidden_states: Optional[tuple[torch.FloatTensor]] = None
|
| 87 |
+
attentions: Optional[tuple[torch.FloatTensor]] = None
|
| 88 |
+
image_hidden_states: Optional[torch.FloatTensor] = None
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class PaliGemmaMultiModalProjector(nn.Module):
|
| 92 |
+
def __init__(self, config: PaliGemmaConfig):
|
| 93 |
+
super().__init__()
|
| 94 |
+
self.linear = nn.Linear(config.vision_config.hidden_size, config.vision_config.projection_dim, bias=True)
|
| 95 |
+
|
| 96 |
+
def forward(self, image_features):
|
| 97 |
+
hidden_states = self.linear(image_features)
|
| 98 |
+
|
| 99 |
+
return hidden_states
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
@auto_docstring
|
| 103 |
+
class PaliGemmaPreTrainedModel(PreTrainedModel):
|
| 104 |
+
config_class = PaliGemmaConfig
|
| 105 |
+
base_model_prefix = ""
|
| 106 |
+
supports_gradient_checkpointing = True
|
| 107 |
+
_no_split_modules = ["PaliGemmaMultiModalProjector"]
|
| 108 |
+
_skip_keys_device_placement = "past_key_values"
|
| 109 |
+
_supports_cache_class = True
|
| 110 |
+
_supports_quantized_cache = True
|
| 111 |
+
_supports_static_cache = True
|
| 112 |
+
_supports_flash_attn_2 = True
|
| 113 |
+
_supports_sdpa = True
|
| 114 |
+
_supports_flex_attn = True
|
| 115 |
+
_supports_attention_backend = True
|
| 116 |
+
|
| 117 |
+
def _init_weights(self, module):
|
| 118 |
+
# important: this ported version of PaliGemmaisn't meant for training from scratch - only
|
| 119 |
+
# inference and fine-tuning
|
| 120 |
+
std = getattr(self.config, "initializer_range", self.config.get_text_config().initializer_range)
|
| 121 |
+
|
| 122 |
+
if isinstance(module, nn.Linear):
|
| 123 |
+
module.weight.data.normal_(mean=0.0, std=std)
|
| 124 |
+
if module.bias is not None:
|
| 125 |
+
module.bias.data.zero_()
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
@auto_docstring(
|
| 129 |
+
custom_intro="""
|
| 130 |
+
The Base Paligemma model which consists of a vision backbone and a language model withou language modeling head.,
|
| 131 |
+
"""
|
| 132 |
+
)
|
| 133 |
+
class PaliGemmaModel(PaliGemmaPreTrainedModel):
|
| 134 |
+
_checkpoint_conversion_mapping = {"language_model.model": "language_model"}
|
| 135 |
+
# we are filtering the logits/labels so we shouldn't divide the loss based on num_items_in_batch
|
| 136 |
+
accepts_loss_kwargs = False
|
| 137 |
+
|
| 138 |
+
def __init__(self, config: PaliGemmaConfig):
|
| 139 |
+
super().__init__(config)
|
| 140 |
+
self.vision_tower = AutoModel.from_config(config=config.vision_config)
|
| 141 |
+
self.multi_modal_projector = PaliGemmaMultiModalProjector(config)
|
| 142 |
+
self.vocab_size = config.text_config.vocab_size
|
| 143 |
+
|
| 144 |
+
language_model = AutoModel.from_config(config=config.text_config)
|
| 145 |
+
self.language_model = language_model
|
| 146 |
+
|
| 147 |
+
self.pad_token_id = self.config.pad_token_id if self.config.pad_token_id is not None else -1
|
| 148 |
+
self.post_init()
|
| 149 |
+
|
| 150 |
+
# Copied from transformers.models.llava.modeling_llava.LlavaModel.get_input_embeddings with Llava->PaliGemma
|
| 151 |
+
def get_input_embeddings(self):
|
| 152 |
+
return self.language_model.get_input_embeddings()
|
| 153 |
+
|
| 154 |
+
# Copied from transformers.models.llava.modeling_llava.LlavaModel.set_input_embeddings with Llava->PaliGemma
|
| 155 |
+
def set_input_embeddings(self, value):
|
| 156 |
+
self.language_model.set_input_embeddings(value)
|
| 157 |
+
|
| 158 |
+
def set_decoder(self, decoder):
|
| 159 |
+
self.language_model = decoder
|
| 160 |
+
|
| 161 |
+
def get_decoder(self):
|
| 162 |
+
return self.language_model
|
| 163 |
+
|
| 164 |
+
def _update_causal_mask(
|
| 165 |
+
self,
|
| 166 |
+
attention_mask,
|
| 167 |
+
token_type_ids=None,
|
| 168 |
+
past_key_values=None,
|
| 169 |
+
cache_position=None,
|
| 170 |
+
input_tensor=None,
|
| 171 |
+
is_training: Optional[bool] = None,
|
| 172 |
+
):
|
| 173 |
+
if self.config.text_config._attn_implementation == "flash_attention_2":
|
| 174 |
+
if attention_mask is not None and 0.0 in attention_mask:
|
| 175 |
+
return attention_mask
|
| 176 |
+
return None
|
| 177 |
+
is_training = is_training if is_training is not None else self.training
|
| 178 |
+
using_static_cache = isinstance(past_key_values, StaticCache)
|
| 179 |
+
min_dtype = torch.finfo(self.dtype).min
|
| 180 |
+
if input_tensor is None:
|
| 181 |
+
input_tensor = attention_mask
|
| 182 |
+
|
| 183 |
+
inputs_lead_dim, sequence_length = input_tensor.shape[:2]
|
| 184 |
+
if using_static_cache:
|
| 185 |
+
target_length = past_key_values.get_max_cache_shape()
|
| 186 |
+
elif isinstance(past_key_values, HybridCache):
|
| 187 |
+
target_length = past_key_values.get_max_cache_shape()
|
| 188 |
+
else:
|
| 189 |
+
target_length = (
|
| 190 |
+
attention_mask.shape[-1]
|
| 191 |
+
if isinstance(attention_mask, torch.Tensor)
|
| 192 |
+
else cache_position[0] + sequence_length + 1
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
if attention_mask is not None and attention_mask.dim() == 4:
|
| 196 |
+
# In this case we assume that the mask comes already in inverted form and requires no inversion or slicing.
|
| 197 |
+
return attention_mask
|
| 198 |
+
|
| 199 |
+
causal_mask = torch.full(
|
| 200 |
+
(sequence_length, target_length), fill_value=min_dtype, dtype=self.dtype, device=cache_position.device
|
| 201 |
+
)
|
| 202 |
+
# Causal diagonal mask only if training, otherwise attend to the whole prefix. Training-specific attn for prefix is handled below
|
| 203 |
+
if sequence_length != 1:
|
| 204 |
+
if is_training:
|
| 205 |
+
causal_mask = torch.triu(causal_mask, diagonal=1)
|
| 206 |
+
else:
|
| 207 |
+
causal_mask[:, :sequence_length] = 0.0
|
| 208 |
+
|
| 209 |
+
causal_mask *= torch.arange(target_length, device=cache_position.device) > cache_position.reshape(-1, 1)
|
| 210 |
+
causal_mask = causal_mask[None, None, :, :].expand(inputs_lead_dim, 1, -1, -1)
|
| 211 |
+
if attention_mask is not None:
|
| 212 |
+
causal_mask = causal_mask.clone() # copy to contiguous memory for in-place edit
|
| 213 |
+
mask_length = attention_mask.shape[-1]
|
| 214 |
+
|
| 215 |
+
# First unmask prefix tokens during training
|
| 216 |
+
if is_training:
|
| 217 |
+
if token_type_ids is None:
|
| 218 |
+
raise ValueError("Token type ids must be provided during training")
|
| 219 |
+
causal_mask[:, :, :, :mask_length] = causal_mask[:, :, :, :mask_length].masked_fill(
|
| 220 |
+
token_type_ids[:, None, None, :].to(causal_mask.device) == 0, 0
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
# Then apply padding mask (will mask pad tokens)
|
| 224 |
+
padding_mask = causal_mask[:, :, :, :mask_length] + attention_mask[:, None, None, :].to(causal_mask.device)
|
| 225 |
+
padding_mask = padding_mask == 0
|
| 226 |
+
causal_mask[:, :, :, :mask_length] = causal_mask[:, :, :, :mask_length].masked_fill(
|
| 227 |
+
padding_mask, min_dtype
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
return causal_mask
|
| 231 |
+
|
| 232 |
+
def get_image_features(self, pixel_values: torch.FloatTensor):
|
| 233 |
+
"""
|
| 234 |
+
Obtains image last hidden states from the vision tower and apply multimodal projection.
|
| 235 |
+
|
| 236 |
+
Args:
|
| 237 |
+
pixel_values (`torch.FloatTensor]` of shape `(batch_size, channels, height, width)`)
|
| 238 |
+
The tensors corresponding to the input images.
|
| 239 |
+
Returns:
|
| 240 |
+
image_features (`torch.Tensor`): Image feature tensor of shape `(num_images, image_length, embed_dim)`).
|
| 241 |
+
"""
|
| 242 |
+
image_outputs = self.vision_tower(pixel_values)
|
| 243 |
+
selected_image_feature = image_outputs.last_hidden_state
|
| 244 |
+
image_features = self.multi_modal_projector(selected_image_feature)
|
| 245 |
+
return image_features
|
| 246 |
+
|
| 247 |
+
@can_return_tuple
|
| 248 |
+
@auto_docstring
|
| 249 |
+
def forward(
|
| 250 |
+
self,
|
| 251 |
+
input_ids: torch.LongTensor = None,
|
| 252 |
+
pixel_values: torch.FloatTensor = None,
|
| 253 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 254 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 255 |
+
past_key_values: Optional[Union[list[torch.FloatTensor], Cache]] = None,
|
| 256 |
+
token_type_ids: Optional[torch.LongTensor] = None,
|
| 257 |
+
cache_position: Optional[torch.LongTensor] = None,
|
| 258 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 259 |
+
labels: Optional[torch.LongTensor] = None,
|
| 260 |
+
use_cache: Optional[bool] = None,
|
| 261 |
+
output_attentions: Optional[bool] = None,
|
| 262 |
+
output_hidden_states: Optional[bool] = None,
|
| 263 |
+
return_dict: Optional[bool] = None,
|
| 264 |
+
**kwargs: Unpack[FlashAttentionKwargs],
|
| 265 |
+
) -> Union[tuple, PaligemmaModelOutputWithPast]:
|
| 266 |
+
r"""
|
| 267 |
+
labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
|
| 268 |
+
Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
|
| 269 |
+
config.text_config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
|
| 270 |
+
(masked), the loss is only computed for the tokens with labels in `[0, ..., config.text_config.vocab_size]`.
|
| 271 |
+
|
| 272 |
+
Example:
|
| 273 |
+
|
| 274 |
+
```python
|
| 275 |
+
>>> from PIL import Image
|
| 276 |
+
>>> import requests
|
| 277 |
+
>>> from transformers import AutoProcessor, PaliGemmaForConditionalGeneration
|
| 278 |
+
|
| 279 |
+
>>> model = PaliGemmaForConditionalGeneration.from_pretrained("google/paligemma2-3b-mix-224")
|
| 280 |
+
>>> processor = AutoProcessor.from_pretrained("google/paligemma2-3b-mix-224")
|
| 281 |
+
|
| 282 |
+
>>> prompt = "Where is the cat standing?"
|
| 283 |
+
>>> url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"
|
| 284 |
+
>>> image = Image.open(requests.get(url, stream=True).raw)
|
| 285 |
+
|
| 286 |
+
>>> inputs = processor(images=image, text=prompt, return_tensors="pt")
|
| 287 |
+
|
| 288 |
+
>>> # Generate
|
| 289 |
+
>>> generate_ids = model.generate(**inputs,)
|
| 290 |
+
>>> processor.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
|
| 291 |
+
"Where is the cat standing?\nsnow"
|
| 292 |
+
```"""
|
| 293 |
+
|
| 294 |
+
if (input_ids is None) ^ (inputs_embeds is not None):
|
| 295 |
+
raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
|
| 296 |
+
|
| 297 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 298 |
+
output_hidden_states = (
|
| 299 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 300 |
+
)
|
| 301 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 302 |
+
|
| 303 |
+
is_training = token_type_ids is not None and labels is not None
|
| 304 |
+
|
| 305 |
+
# Replace image id woth PAD if the image token if OOV, to avoid index-errors
|
| 306 |
+
if input_ids is not None and self.config.image_token_id >= self.vocab_size:
|
| 307 |
+
special_image_mask = input_ids == self.config.image_token_id
|
| 308 |
+
llm_input_ids = input_ids.clone()
|
| 309 |
+
llm_input_ids[special_image_mask] = 0
|
| 310 |
+
else:
|
| 311 |
+
llm_input_ids = input_ids
|
| 312 |
+
|
| 313 |
+
if inputs_embeds is None:
|
| 314 |
+
inputs_embeds = self.get_input_embeddings()(llm_input_ids)
|
| 315 |
+
|
| 316 |
+
if cache_position is None:
|
| 317 |
+
past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
|
| 318 |
+
cache_position = torch.arange(
|
| 319 |
+
past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device
|
| 320 |
+
)
|
| 321 |
+
|
| 322 |
+
if position_ids is None:
|
| 323 |
+
position_ids = cache_position.unsqueeze(0) + 1 # Paligemma positions are 1-indexed
|
| 324 |
+
|
| 325 |
+
# Merge text and images
|
| 326 |
+
if pixel_values is not None:
|
| 327 |
+
image_features = self.get_image_features(pixel_values)
|
| 328 |
+
|
| 329 |
+
if input_ids is None:
|
| 330 |
+
special_image_mask = inputs_embeds == self.get_input_embeddings()(
|
| 331 |
+
torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
|
| 332 |
+
)
|
| 333 |
+
else:
|
| 334 |
+
special_image_mask = (input_ids == self.config.image_token_id).unsqueeze(-1)
|
| 335 |
+
special_image_mask = special_image_mask.expand_as(inputs_embeds).to(inputs_embeds.device)
|
| 336 |
+
|
| 337 |
+
if not is_torchdynamo_compiling() and inputs_embeds[special_image_mask].numel() != image_features.numel():
|
| 338 |
+
image_tokens_in_text = (special_image_mask).sum(dim=1).sum(dim=0)[0]
|
| 339 |
+
raise ValueError(
|
| 340 |
+
f"Number of images does not match number of special image tokens in the input text. "
|
| 341 |
+
f"Got {image_tokens_in_text} image tokens in the text but {image_features.shape[0] * image_features.shape[1]} "
|
| 342 |
+
"tokens from image embeddings."
|
| 343 |
+
)
|
| 344 |
+
image_features = image_features.to(inputs_embeds.device, inputs_embeds.dtype)
|
| 345 |
+
inputs_embeds = inputs_embeds.masked_scatter(special_image_mask, image_features)
|
| 346 |
+
|
| 347 |
+
causal_mask = self._update_causal_mask(
|
| 348 |
+
attention_mask, token_type_ids, past_key_values, cache_position, inputs_embeds, is_training
|
| 349 |
+
)
|
| 350 |
+
outputs = self.language_model(
|
| 351 |
+
attention_mask=causal_mask,
|
| 352 |
+
position_ids=position_ids,
|
| 353 |
+
past_key_values=past_key_values,
|
| 354 |
+
inputs_embeds=inputs_embeds,
|
| 355 |
+
use_cache=use_cache,
|
| 356 |
+
output_attentions=output_attentions,
|
| 357 |
+
output_hidden_states=output_hidden_states,
|
| 358 |
+
return_dict=True,
|
| 359 |
+
cache_position=cache_position,
|
| 360 |
+
**kwargs,
|
| 361 |
+
)
|
| 362 |
+
|
| 363 |
+
return PaligemmaModelOutputWithPast(
|
| 364 |
+
last_hidden_state=outputs.last_hidden_state,
|
| 365 |
+
past_key_values=outputs.past_key_values,
|
| 366 |
+
hidden_states=outputs.hidden_states,
|
| 367 |
+
attentions=outputs.attentions,
|
| 368 |
+
image_hidden_states=image_features if pixel_values is not None else None,
|
| 369 |
+
)
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
class KwargsForCausalLM(FlashAttentionKwargs, LossKwargs): ...
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
@auto_docstring(
|
| 376 |
+
custom_intro="""
|
| 377 |
+
The Base Paligemma model which consists of a vision backbone and a language model without language modeling head.,
|
| 378 |
+
"""
|
| 379 |
+
)
|
| 380 |
+
class PaliGemmaForConditionalGeneration(PaliGemmaPreTrainedModel, GenerationMixin):
|
| 381 |
+
_checkpoint_conversion_mapping = {
|
| 382 |
+
"^language_model.model": "model.language_model",
|
| 383 |
+
"^vision_tower": "model.vision_tower",
|
| 384 |
+
"^multi_modal_projector": "model.multi_modal_projector",
|
| 385 |
+
"^language_model.lm_head": "lm_head",
|
| 386 |
+
}
|
| 387 |
+
_tied_weights_keys = ["lm_head.weight"]
|
| 388 |
+
|
| 389 |
+
def __init__(self, config: PaliGemmaConfig):
|
| 390 |
+
super().__init__(config)
|
| 391 |
+
self.model = PaliGemmaModel(config)
|
| 392 |
+
self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
|
| 393 |
+
self.post_init()
|
| 394 |
+
|
| 395 |
+
def get_input_embeddings(self):
|
| 396 |
+
return self.model.get_input_embeddings()
|
| 397 |
+
|
| 398 |
+
def set_input_embeddings(self, value):
|
| 399 |
+
self.model.set_input_embeddings(value)
|
| 400 |
+
|
| 401 |
+
def get_output_embeddings(self):
|
| 402 |
+
return self.lm_head
|
| 403 |
+
|
| 404 |
+
def set_output_embeddings(self, new_embeddings):
|
| 405 |
+
self.lm_head = new_embeddings
|
| 406 |
+
|
| 407 |
+
def set_decoder(self, decoder):
|
| 408 |
+
self.model.set_decoder(decoder)
|
| 409 |
+
|
| 410 |
+
def get_decoder(self):
|
| 411 |
+
return self.model.get_decoder()
|
| 412 |
+
|
| 413 |
+
def get_image_features(self, pixel_values):
|
| 414 |
+
return self.model.get_image_features(pixel_values)
|
| 415 |
+
|
| 416 |
+
# Make modules available throught conditional class for BC
|
| 417 |
+
@property
|
| 418 |
+
def language_model(self):
|
| 419 |
+
return self.model.language_model
|
| 420 |
+
|
| 421 |
+
@property
|
| 422 |
+
def vision_tower(self):
|
| 423 |
+
return self.model.vision_tower
|
| 424 |
+
|
| 425 |
+
@property
|
| 426 |
+
def multi_modal_projector(self):
|
| 427 |
+
return self.model.multi_modal_projector
|
| 428 |
+
|
| 429 |
+
@can_return_tuple
|
| 430 |
+
@auto_docstring
|
| 431 |
+
def forward(
|
| 432 |
+
self,
|
| 433 |
+
input_ids: torch.LongTensor = None,
|
| 434 |
+
pixel_values: torch.FloatTensor = None,
|
| 435 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 436 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 437 |
+
past_key_values: Optional[Union[list[torch.FloatTensor], Cache]] = None,
|
| 438 |
+
token_type_ids: Optional[torch.LongTensor] = None,
|
| 439 |
+
cache_position: Optional[torch.LongTensor] = None,
|
| 440 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 441 |
+
labels: Optional[torch.LongTensor] = None,
|
| 442 |
+
use_cache: Optional[bool] = None,
|
| 443 |
+
output_attentions: Optional[bool] = None,
|
| 444 |
+
output_hidden_states: Optional[bool] = None,
|
| 445 |
+
return_dict: Optional[bool] = None,
|
| 446 |
+
logits_to_keep: Union[int, torch.Tensor] = 0,
|
| 447 |
+
**kwargs: Unpack[KwargsForCausalLM],
|
| 448 |
+
) -> Union[tuple, PaliGemmaCausalLMOutputWithPast]:
|
| 449 |
+
r"""
|
| 450 |
+
labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
|
| 451 |
+
Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
|
| 452 |
+
config.text_config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
|
| 453 |
+
(masked), the loss is only computed for the tokens with labels in `[0, ..., config.text_config.vocab_size]`.
|
| 454 |
+
|
| 455 |
+
Example:
|
| 456 |
+
|
| 457 |
+
```python
|
| 458 |
+
>>> from PIL import Image
|
| 459 |
+
>>> import requests
|
| 460 |
+
>>> from transformers import AutoProcessor, PaliGemmaForConditionalGeneration
|
| 461 |
+
|
| 462 |
+
>>> model = PaliGemmaForConditionalGeneration.from_pretrained("google/paligemma2-3b-mix-224")
|
| 463 |
+
>>> processor = AutoProcessor.from_pretrained("google/paligemma2-3b-mix-224")
|
| 464 |
+
|
| 465 |
+
>>> prompt = "Where is the cat standing?"
|
| 466 |
+
>>> url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"
|
| 467 |
+
>>> image = Image.open(requests.get(url, stream=True).raw)
|
| 468 |
+
|
| 469 |
+
>>> inputs = processor(images=image, text=prompt, return_tensors="pt")
|
| 470 |
+
|
| 471 |
+
>>> # Generate
|
| 472 |
+
>>> generate_ids = model.generate(**inputs,)
|
| 473 |
+
>>> processor.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
|
| 474 |
+
"Where is the cat standing?\nsnow"
|
| 475 |
+
```"""
|
| 476 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 477 |
+
output_hidden_states = (
|
| 478 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 479 |
+
)
|
| 480 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 481 |
+
|
| 482 |
+
outputs = self.model(
|
| 483 |
+
input_ids=input_ids,
|
| 484 |
+
pixel_values=pixel_values,
|
| 485 |
+
token_type_ids=token_type_ids,
|
| 486 |
+
attention_mask=attention_mask,
|
| 487 |
+
position_ids=position_ids,
|
| 488 |
+
past_key_values=past_key_values,
|
| 489 |
+
inputs_embeds=inputs_embeds,
|
| 490 |
+
use_cache=use_cache,
|
| 491 |
+
labels=labels,
|
| 492 |
+
output_attentions=output_attentions,
|
| 493 |
+
output_hidden_states=output_hidden_states,
|
| 494 |
+
return_dict=True,
|
| 495 |
+
cache_position=cache_position,
|
| 496 |
+
**kwargs,
|
| 497 |
+
)
|
| 498 |
+
|
| 499 |
+
hidden_states = outputs[0]
|
| 500 |
+
# Only compute necessary logits, and do not upcast them to float if we are not computing the loss
|
| 501 |
+
slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
|
| 502 |
+
logits = self.lm_head(hidden_states[:, slice_indices, :])
|
| 503 |
+
|
| 504 |
+
loss = None
|
| 505 |
+
if labels is not None:
|
| 506 |
+
loss = self.loss_function(
|
| 507 |
+
logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
|
| 508 |
+
)
|
| 509 |
+
|
| 510 |
+
return PaliGemmaCausalLMOutputWithPast(
|
| 511 |
+
loss=loss,
|
| 512 |
+
logits=logits,
|
| 513 |
+
past_key_values=outputs.past_key_values,
|
| 514 |
+
hidden_states=outputs.hidden_states,
|
| 515 |
+
attentions=outputs.attentions,
|
| 516 |
+
image_hidden_states=outputs.image_hidden_states,
|
| 517 |
+
)
|
| 518 |
+
|
| 519 |
+
def prepare_inputs_for_generation(
|
| 520 |
+
self,
|
| 521 |
+
input_ids,
|
| 522 |
+
past_key_values=None,
|
| 523 |
+
inputs_embeds=None,
|
| 524 |
+
cache_position=None,
|
| 525 |
+
position_ids=None,
|
| 526 |
+
pixel_values=None,
|
| 527 |
+
attention_mask=None,
|
| 528 |
+
token_type_ids=None,
|
| 529 |
+
use_cache=True,
|
| 530 |
+
logits_to_keep=None,
|
| 531 |
+
labels=None,
|
| 532 |
+
**kwargs,
|
| 533 |
+
):
|
| 534 |
+
# Overwritten -- custom `position_ids` and `pixel_values` handling
|
| 535 |
+
model_inputs = super().prepare_inputs_for_generation(
|
| 536 |
+
input_ids,
|
| 537 |
+
past_key_values=past_key_values,
|
| 538 |
+
inputs_embeds=inputs_embeds,
|
| 539 |
+
attention_mask=attention_mask,
|
| 540 |
+
position_ids=position_ids,
|
| 541 |
+
cache_position=cache_position,
|
| 542 |
+
use_cache=use_cache,
|
| 543 |
+
logits_to_keep=logits_to_keep,
|
| 544 |
+
token_type_ids=token_type_ids,
|
| 545 |
+
**kwargs,
|
| 546 |
+
)
|
| 547 |
+
|
| 548 |
+
# position_ids in Paligemma are 1-indexed
|
| 549 |
+
if model_inputs.get("position_ids") is not None:
|
| 550 |
+
model_inputs["position_ids"] += 1
|
| 551 |
+
# If we're in cached decoding stage, pixel values should be None because input ids do not contain special image token anymore
|
| 552 |
+
# Otherwise we need pixel values to be passed to model. NOTE: use_cache=False needs pixel_values always
|
| 553 |
+
if cache_position[0] == 0:
|
| 554 |
+
model_inputs["pixel_values"] = pixel_values
|
| 555 |
+
is_training = token_type_ids is not None and labels is not None
|
| 556 |
+
if cache_position[0] == 0 and isinstance(past_key_values, HybridCache):
|
| 557 |
+
input_tensor = inputs_embeds if inputs_embeds is not None else input_ids
|
| 558 |
+
causal_mask = self.model._update_causal_mask(
|
| 559 |
+
attention_mask, token_type_ids, past_key_values, cache_position, input_tensor, is_training
|
| 560 |
+
)
|
| 561 |
+
model_inputs["attention_mask"] = causal_mask
|
| 562 |
+
|
| 563 |
+
return model_inputs
|
| 564 |
+
|
| 565 |
+
@staticmethod
|
| 566 |
+
# Copied from transformers.models.gptj.modeling_gptj.GPTJModel._prepare_4d_causal_attention_mask_with_cache_position
|
| 567 |
+
def _prepare_4d_causal_attention_mask_with_cache_position(
|
| 568 |
+
attention_mask: torch.Tensor,
|
| 569 |
+
sequence_length: int,
|
| 570 |
+
target_length: int,
|
| 571 |
+
dtype: torch.dtype,
|
| 572 |
+
cache_position: torch.Tensor,
|
| 573 |
+
batch_size: int,
|
| 574 |
+
**kwargs,
|
| 575 |
+
):
|
| 576 |
+
"""
|
| 577 |
+
Creates a causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape
|
| 578 |
+
`(batch_size, key_value_length)`, or if the input `attention_mask` is already 4D, do nothing.
|
| 579 |
+
|
| 580 |
+
Args:
|
| 581 |
+
attention_mask (`torch.Tensor`):
|
| 582 |
+
A 2D attention mask of shape `(batch_size, key_value_length)` or a 4D attention mask of shape
|
| 583 |
+
`(batch_size, 1, query_length, key_value_length)`.
|
| 584 |
+
sequence_length (`int`):
|
| 585 |
+
The sequence length being processed.
|
| 586 |
+
target_length (`int`):
|
| 587 |
+
The target length: when generating with static cache, the mask should be as long as the static cache,
|
| 588 |
+
to account for the 0 padding, the part of the cache that is not filled yet.
|
| 589 |
+
dtype (`torch.dtype`):
|
| 590 |
+
The dtype to use for the 4D attention mask.
|
| 591 |
+
cache_position (`torch.Tensor`):
|
| 592 |
+
Indices depicting the position of the input sequence tokens in the sequence.
|
| 593 |
+
batch_size (`torch.Tensor`):
|
| 594 |
+
Batch size.
|
| 595 |
+
"""
|
| 596 |
+
if attention_mask is not None and attention_mask.dim() == 4:
|
| 597 |
+
# In this case we assume that the mask comes already in inverted form and requires no inversion or slicing.
|
| 598 |
+
causal_mask = attention_mask
|
| 599 |
+
else:
|
| 600 |
+
min_dtype = torch.finfo(dtype).min
|
| 601 |
+
causal_mask = torch.full(
|
| 602 |
+
(sequence_length, target_length), fill_value=min_dtype, dtype=dtype, device=cache_position.device
|
| 603 |
+
)
|
| 604 |
+
if sequence_length != 1:
|
| 605 |
+
causal_mask = torch.triu(causal_mask, diagonal=1)
|
| 606 |
+
causal_mask *= torch.arange(target_length, device=cache_position.device) > cache_position.reshape(-1, 1)
|
| 607 |
+
causal_mask = causal_mask[None, None, :, :].expand(batch_size, 1, -1, -1)
|
| 608 |
+
if attention_mask is not None:
|
| 609 |
+
causal_mask = causal_mask.clone() # copy to contiguous memory for in-place edit
|
| 610 |
+
mask_length = attention_mask.shape[-1]
|
| 611 |
+
padding_mask = causal_mask[:, :, :, :mask_length] + attention_mask[:, None, None, :].to(
|
| 612 |
+
causal_mask.device
|
| 613 |
+
)
|
| 614 |
+
padding_mask = padding_mask == 0
|
| 615 |
+
causal_mask[:, :, :, :mask_length] = causal_mask[:, :, :, :mask_length].masked_fill(
|
| 616 |
+
padding_mask, min_dtype
|
| 617 |
+
)
|
| 618 |
+
|
| 619 |
+
return causal_mask
|
| 620 |
+
|
| 621 |
+
|
| 622 |
+
__all__ = ["PaliGemmaForConditionalGeneration", "PaliGemmaPreTrainedModel", "PaliGemmaModel"]
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/siglip/check.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import transformers
|
| 2 |
+
|
| 3 |
+
def check_whether_transformers_replace_is_installed_correctly():
|
| 4 |
+
return transformers.__version__ == "4.53.2"
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/models_pytorch/transformers_replace/models/siglip/modeling_siglip.py
ADDED
|
@@ -0,0 +1,1237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2024 Google AI and The HuggingFace Team. All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""PyTorch Siglip model."""
|
| 16 |
+
|
| 17 |
+
import math
|
| 18 |
+
import warnings
|
| 19 |
+
from dataclasses import dataclass
|
| 20 |
+
from typing import Any, Callable, Optional, Union
|
| 21 |
+
|
| 22 |
+
import numpy as np
|
| 23 |
+
import torch
|
| 24 |
+
import torch.utils.checkpoint
|
| 25 |
+
from torch import nn
|
| 26 |
+
from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
|
| 27 |
+
from torch.nn.init import _calculate_fan_in_and_fan_out
|
| 28 |
+
|
| 29 |
+
from ...activations import ACT2FN
|
| 30 |
+
from ...modeling_attn_mask_utils import _prepare_4d_attention_mask
|
| 31 |
+
from ...modeling_layers import GradientCheckpointingLayer
|
| 32 |
+
from ...modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling, ImageClassifierOutput
|
| 33 |
+
from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
|
| 34 |
+
from ...utils import ModelOutput, auto_docstring, can_return_tuple, logging, torch_int
|
| 35 |
+
from .configuration_siglip import SiglipConfig, SiglipTextConfig, SiglipVisionConfig
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
logger = logging.get_logger(__name__)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _trunc_normal_(tensor, mean, std, a, b):
|
| 42 |
+
# Cut & paste from PyTorch official master until it's in a few official releases - RW
|
| 43 |
+
# Method based on https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf
|
| 44 |
+
def norm_cdf(x):
|
| 45 |
+
# Computes standard normal cumulative distribution function
|
| 46 |
+
return (1.0 + math.erf(x / math.sqrt(2.0))) / 2.0
|
| 47 |
+
|
| 48 |
+
if (mean < a - 2 * std) or (mean > b + 2 * std):
|
| 49 |
+
warnings.warn(
|
| 50 |
+
"mean is more than 2 std from [a, b] in nn.init.trunc_normal_. "
|
| 51 |
+
"The distribution of values may be incorrect.",
|
| 52 |
+
stacklevel=2,
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
# Values are generated by using a truncated uniform distribution and
|
| 56 |
+
# then using the inverse CDF for the normal distribution.
|
| 57 |
+
# Get upper and lower cdf values
|
| 58 |
+
l = norm_cdf((a - mean) / std)
|
| 59 |
+
u = norm_cdf((b - mean) / std)
|
| 60 |
+
|
| 61 |
+
# Uniformly fill tensor with values from [l, u], then translate to
|
| 62 |
+
# [2l-1, 2u-1].
|
| 63 |
+
tensor.uniform_(2 * l - 1, 2 * u - 1)
|
| 64 |
+
|
| 65 |
+
# Use inverse cdf transform for normal distribution to get truncated
|
| 66 |
+
# standard normal
|
| 67 |
+
tensor.erfinv_()
|
| 68 |
+
|
| 69 |
+
# Transform to proper mean, std
|
| 70 |
+
tensor.mul_(std * math.sqrt(2.0))
|
| 71 |
+
tensor.add_(mean)
|
| 72 |
+
|
| 73 |
+
# Clamp to ensure it's in the proper range
|
| 74 |
+
tensor.clamp_(min=a, max=b)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def trunc_normal_tf_(
|
| 78 |
+
tensor: torch.Tensor, mean: float = 0.0, std: float = 1.0, a: float = -2.0, b: float = 2.0
|
| 79 |
+
) -> torch.Tensor:
|
| 80 |
+
"""Fills the input Tensor with values drawn from a truncated
|
| 81 |
+
normal distribution. The values are effectively drawn from the
|
| 82 |
+
normal distribution :math:`\\mathcal{N}(\text{mean}, \text{std}^2)`
|
| 83 |
+
with values outside :math:`[a, b]` redrawn until they are within
|
| 84 |
+
the bounds. The method used for generating the random values works
|
| 85 |
+
best when :math:`a \\leq \text{mean} \\leq b`.
|
| 86 |
+
|
| 87 |
+
NOTE: this 'tf' variant behaves closer to Tensorflow / JAX impl where the
|
| 88 |
+
bounds [a, b] are applied when sampling the normal distribution with mean=0, std=1.0
|
| 89 |
+
and the result is subsequently scaled and shifted by the mean and std args.
|
| 90 |
+
|
| 91 |
+
Args:
|
| 92 |
+
tensor: an n-dimensional `torch.Tensor`
|
| 93 |
+
mean: the mean of the normal distribution
|
| 94 |
+
std: the standard deviation of the normal distribution
|
| 95 |
+
a: the minimum cutoff value
|
| 96 |
+
b: the maximum cutoff value
|
| 97 |
+
"""
|
| 98 |
+
with torch.no_grad():
|
| 99 |
+
_trunc_normal_(tensor, 0, 1.0, a, b)
|
| 100 |
+
tensor.mul_(std).add_(mean)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def variance_scaling_(tensor, scale=1.0, mode="fan_in", distribution="normal"):
|
| 104 |
+
fan_in, fan_out = _calculate_fan_in_and_fan_out(tensor)
|
| 105 |
+
if mode == "fan_in":
|
| 106 |
+
denom = fan_in
|
| 107 |
+
elif mode == "fan_out":
|
| 108 |
+
denom = fan_out
|
| 109 |
+
elif mode == "fan_avg":
|
| 110 |
+
denom = (fan_in + fan_out) / 2
|
| 111 |
+
|
| 112 |
+
variance = scale / denom
|
| 113 |
+
|
| 114 |
+
if distribution == "truncated_normal":
|
| 115 |
+
# constant is stddev of standard normal truncated to (-2, 2)
|
| 116 |
+
trunc_normal_tf_(tensor, std=math.sqrt(variance) / 0.87962566103423978)
|
| 117 |
+
elif distribution == "normal":
|
| 118 |
+
with torch.no_grad():
|
| 119 |
+
tensor.normal_(std=math.sqrt(variance))
|
| 120 |
+
elif distribution == "uniform":
|
| 121 |
+
bound = math.sqrt(3 * variance)
|
| 122 |
+
with torch.no_grad():
|
| 123 |
+
tensor.uniform_(-bound, bound)
|
| 124 |
+
else:
|
| 125 |
+
raise ValueError(f"invalid distribution {distribution}")
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def lecun_normal_(tensor):
|
| 129 |
+
variance_scaling_(tensor, mode="fan_in", distribution="truncated_normal")
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def default_flax_embed_init(tensor):
|
| 133 |
+
variance_scaling_(tensor, mode="fan_in", distribution="normal")
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
@dataclass
|
| 137 |
+
@auto_docstring(
|
| 138 |
+
custom_intro="""
|
| 139 |
+
Base class for vision model's outputs that also contains image embeddings of the pooling of the last hidden states.
|
| 140 |
+
"""
|
| 141 |
+
)
|
| 142 |
+
# Copied from transformers.models.clip.modeling_clip.CLIPVisionModelOutput with CLIP->Siglip
|
| 143 |
+
class SiglipVisionModelOutput(ModelOutput):
|
| 144 |
+
r"""
|
| 145 |
+
image_embeds (`torch.FloatTensor` of shape `(batch_size, output_dim)` *optional* returned when model is initialized with `with_projection=True`):
|
| 146 |
+
The image embeddings obtained by applying the projection layer to the pooler_output.
|
| 147 |
+
"""
|
| 148 |
+
|
| 149 |
+
image_embeds: Optional[torch.FloatTensor] = None
|
| 150 |
+
last_hidden_state: Optional[torch.FloatTensor] = None
|
| 151 |
+
hidden_states: Optional[tuple[torch.FloatTensor, ...]] = None
|
| 152 |
+
attentions: Optional[tuple[torch.FloatTensor, ...]] = None
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
@dataclass
|
| 156 |
+
@auto_docstring(
|
| 157 |
+
custom_intro="""
|
| 158 |
+
Base class for text model's outputs that also contains a pooling of the last hidden states.
|
| 159 |
+
"""
|
| 160 |
+
)
|
| 161 |
+
# Copied from transformers.models.clip.modeling_clip.CLIPTextModelOutput with CLIP->Siglip
|
| 162 |
+
class SiglipTextModelOutput(ModelOutput):
|
| 163 |
+
r"""
|
| 164 |
+
text_embeds (`torch.FloatTensor` of shape `(batch_size, output_dim)` *optional* returned when model is initialized with `with_projection=True`):
|
| 165 |
+
The text embeddings obtained by applying the projection layer to the pooler_output.
|
| 166 |
+
"""
|
| 167 |
+
|
| 168 |
+
text_embeds: Optional[torch.FloatTensor] = None
|
| 169 |
+
last_hidden_state: Optional[torch.FloatTensor] = None
|
| 170 |
+
hidden_states: Optional[tuple[torch.FloatTensor, ...]] = None
|
| 171 |
+
attentions: Optional[tuple[torch.FloatTensor, ...]] = None
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
@dataclass
|
| 175 |
+
@auto_docstring
|
| 176 |
+
# Copied from transformers.models.clip.modeling_clip.CLIPOutput with CLIP->Siglip
|
| 177 |
+
class SiglipOutput(ModelOutput):
|
| 178 |
+
r"""
|
| 179 |
+
loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `return_loss` is `True`):
|
| 180 |
+
Contrastive loss for image-text similarity.
|
| 181 |
+
logits_per_image (`torch.FloatTensor` of shape `(image_batch_size, text_batch_size)`):
|
| 182 |
+
The scaled dot product scores between `image_embeds` and `text_embeds`. This represents the image-text
|
| 183 |
+
similarity scores.
|
| 184 |
+
logits_per_text (`torch.FloatTensor` of shape `(text_batch_size, image_batch_size)`):
|
| 185 |
+
The scaled dot product scores between `text_embeds` and `image_embeds`. This represents the text-image
|
| 186 |
+
similarity scores.
|
| 187 |
+
text_embeds (`torch.FloatTensor` of shape `(batch_size, output_dim`):
|
| 188 |
+
The text embeddings obtained by applying the projection layer to the pooled output of [`SiglipTextModel`].
|
| 189 |
+
image_embeds (`torch.FloatTensor` of shape `(batch_size, output_dim`):
|
| 190 |
+
The image embeddings obtained by applying the projection layer to the pooled output of [`SiglipVisionModel`].
|
| 191 |
+
text_model_output (`BaseModelOutputWithPooling`):
|
| 192 |
+
The output of the [`SiglipTextModel`].
|
| 193 |
+
vision_model_output (`BaseModelOutputWithPooling`):
|
| 194 |
+
The output of the [`SiglipVisionModel`].
|
| 195 |
+
"""
|
| 196 |
+
|
| 197 |
+
loss: Optional[torch.FloatTensor] = None
|
| 198 |
+
logits_per_image: Optional[torch.FloatTensor] = None
|
| 199 |
+
logits_per_text: Optional[torch.FloatTensor] = None
|
| 200 |
+
text_embeds: Optional[torch.FloatTensor] = None
|
| 201 |
+
image_embeds: Optional[torch.FloatTensor] = None
|
| 202 |
+
text_model_output: BaseModelOutputWithPooling = None
|
| 203 |
+
vision_model_output: BaseModelOutputWithPooling = None
|
| 204 |
+
|
| 205 |
+
def to_tuple(self) -> tuple[Any]:
|
| 206 |
+
return tuple(
|
| 207 |
+
self[k] if k not in ["text_model_output", "vision_model_output"] else getattr(self, k).to_tuple()
|
| 208 |
+
for k in self.keys()
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
class SiglipVisionEmbeddings(nn.Module):
|
| 213 |
+
def __init__(self, config: SiglipVisionConfig):
|
| 214 |
+
super().__init__()
|
| 215 |
+
self.config = config
|
| 216 |
+
self.embed_dim = config.hidden_size
|
| 217 |
+
self.image_size = config.image_size
|
| 218 |
+
self.patch_size = config.patch_size
|
| 219 |
+
|
| 220 |
+
self.patch_embedding = nn.Conv2d(
|
| 221 |
+
in_channels=config.num_channels,
|
| 222 |
+
out_channels=self.embed_dim,
|
| 223 |
+
kernel_size=self.patch_size,
|
| 224 |
+
stride=self.patch_size,
|
| 225 |
+
padding="valid",
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
+
self.num_patches = (self.image_size // self.patch_size) ** 2
|
| 229 |
+
self.num_positions = self.num_patches
|
| 230 |
+
self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim)
|
| 231 |
+
self.register_buffer("position_ids", torch.arange(self.num_positions).expand((1, -1)), persistent=False)
|
| 232 |
+
|
| 233 |
+
def interpolate_pos_encoding(self, embeddings: torch.Tensor, height: int, width: int) -> torch.Tensor:
|
| 234 |
+
"""
|
| 235 |
+
This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher resolution
|
| 236 |
+
images. This method is also adapted to support torch.jit tracing and no class embeddings.
|
| 237 |
+
|
| 238 |
+
Adapted from:
|
| 239 |
+
- https://github.com/facebookresearch/dino/blob/de9ee3df6cf39fac952ab558447af1fa1365362a/vision_transformer.py#L174-L194, and
|
| 240 |
+
- https://github.com/facebookresearch/dinov2/blob/e1277af2ba9496fbadf7aec6eba56e8d882d1e35/dinov2/models/vision_transformer.py#L179-L211
|
| 241 |
+
"""
|
| 242 |
+
|
| 243 |
+
num_patches = embeddings.shape[1]
|
| 244 |
+
num_positions = self.position_embedding.weight.shape[0]
|
| 245 |
+
|
| 246 |
+
# always interpolate when tracing to ensure the exported model works for dynamic input shapes
|
| 247 |
+
if not torch.jit.is_tracing() and num_patches == num_positions and height == width:
|
| 248 |
+
return self.position_embedding(self.position_ids)
|
| 249 |
+
|
| 250 |
+
patch_pos_embed = self.position_embedding.weight.unsqueeze(0)
|
| 251 |
+
|
| 252 |
+
dim = embeddings.shape[-1]
|
| 253 |
+
|
| 254 |
+
new_height = height // self.patch_size
|
| 255 |
+
new_width = width // self.patch_size
|
| 256 |
+
|
| 257 |
+
sqrt_num_positions = torch_int(num_positions**0.5)
|
| 258 |
+
patch_pos_embed = patch_pos_embed.reshape(1, sqrt_num_positions, sqrt_num_positions, dim)
|
| 259 |
+
patch_pos_embed = patch_pos_embed.permute(0, 3, 1, 2)
|
| 260 |
+
|
| 261 |
+
patch_pos_embed = nn.functional.interpolate(
|
| 262 |
+
patch_pos_embed,
|
| 263 |
+
size=(new_height, new_width),
|
| 264 |
+
mode="bicubic",
|
| 265 |
+
align_corners=False,
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
+
patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
|
| 269 |
+
return patch_pos_embed
|
| 270 |
+
|
| 271 |
+
def forward(self, pixel_values: torch.FloatTensor, interpolate_pos_encoding=False) -> torch.Tensor:
|
| 272 |
+
_, _, height, width = pixel_values.shape
|
| 273 |
+
target_dtype = self.patch_embedding.weight.dtype
|
| 274 |
+
patch_embeds = self.patch_embedding(pixel_values.to(dtype=target_dtype)) # shape = [*, width, grid, grid]
|
| 275 |
+
embeddings = patch_embeds.flatten(2).transpose(1, 2)
|
| 276 |
+
|
| 277 |
+
if interpolate_pos_encoding:
|
| 278 |
+
embeddings = embeddings + self.interpolate_pos_encoding(embeddings, height, width)
|
| 279 |
+
else:
|
| 280 |
+
embeddings = embeddings + self.position_embedding(self.position_ids)
|
| 281 |
+
return embeddings
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
# Copied from transformers.models.clip.modeling_clip.CLIPTextEmbeddings with CLIP->Siglip
|
| 285 |
+
class SiglipTextEmbeddings(nn.Module):
|
| 286 |
+
def __init__(self, config: SiglipTextConfig):
|
| 287 |
+
super().__init__()
|
| 288 |
+
embed_dim = config.hidden_size
|
| 289 |
+
|
| 290 |
+
self.token_embedding = nn.Embedding(config.vocab_size, embed_dim)
|
| 291 |
+
self.position_embedding = nn.Embedding(config.max_position_embeddings, embed_dim)
|
| 292 |
+
|
| 293 |
+
# position_ids (1, len position emb) is contiguous in memory and exported when serialized
|
| 294 |
+
self.register_buffer(
|
| 295 |
+
"position_ids", torch.arange(config.max_position_embeddings).expand((1, -1)), persistent=False
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
def forward(
|
| 299 |
+
self,
|
| 300 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 301 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 302 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 303 |
+
) -> torch.Tensor:
|
| 304 |
+
seq_length = input_ids.shape[-1] if input_ids is not None else inputs_embeds.shape[-2]
|
| 305 |
+
max_position_embedding = self.position_embedding.weight.shape[0]
|
| 306 |
+
|
| 307 |
+
if seq_length > max_position_embedding:
|
| 308 |
+
raise ValueError(
|
| 309 |
+
f"Sequence length must be less than max_position_embeddings (got `sequence length`: "
|
| 310 |
+
f"{seq_length} and max_position_embeddings: {max_position_embedding}"
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
if position_ids is None:
|
| 314 |
+
position_ids = self.position_ids[:, :seq_length]
|
| 315 |
+
|
| 316 |
+
if inputs_embeds is None:
|
| 317 |
+
inputs_embeds = self.token_embedding(input_ids)
|
| 318 |
+
|
| 319 |
+
position_embeddings = self.position_embedding(position_ids)
|
| 320 |
+
embeddings = inputs_embeds + position_embeddings
|
| 321 |
+
|
| 322 |
+
return embeddings
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
def eager_attention_forward(
|
| 326 |
+
module: nn.Module,
|
| 327 |
+
query: torch.Tensor,
|
| 328 |
+
key: torch.Tensor,
|
| 329 |
+
value: torch.Tensor,
|
| 330 |
+
attention_mask: Optional[torch.Tensor],
|
| 331 |
+
scaling: float,
|
| 332 |
+
dropout: float = 0.0,
|
| 333 |
+
**kwargs,
|
| 334 |
+
):
|
| 335 |
+
attn_weights = torch.matmul(query, key.transpose(-1, -2)) * scaling
|
| 336 |
+
if attention_mask is not None:
|
| 337 |
+
attn_weights = attn_weights + attention_mask
|
| 338 |
+
|
| 339 |
+
attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
|
| 340 |
+
attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
|
| 341 |
+
|
| 342 |
+
attn_output = torch.matmul(attn_weights, value)
|
| 343 |
+
attn_output = attn_output.transpose(1, 2).contiguous()
|
| 344 |
+
|
| 345 |
+
return attn_output, attn_weights
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
class SiglipAttention(nn.Module):
|
| 349 |
+
"""Multi-headed attention from 'Attention Is All You Need' paper"""
|
| 350 |
+
|
| 351 |
+
def __init__(self, config):
|
| 352 |
+
super().__init__()
|
| 353 |
+
self.config = config
|
| 354 |
+
self.embed_dim = config.hidden_size
|
| 355 |
+
self.num_heads = config.num_attention_heads
|
| 356 |
+
self.head_dim = self.embed_dim // self.num_heads
|
| 357 |
+
if self.head_dim * self.num_heads != self.embed_dim:
|
| 358 |
+
raise ValueError(
|
| 359 |
+
f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
|
| 360 |
+
f" {self.num_heads})."
|
| 361 |
+
)
|
| 362 |
+
self.scale = self.head_dim**-0.5
|
| 363 |
+
self.dropout = config.attention_dropout
|
| 364 |
+
self.is_causal = False
|
| 365 |
+
|
| 366 |
+
self.k_proj = nn.Linear(self.embed_dim, self.embed_dim)
|
| 367 |
+
self.v_proj = nn.Linear(self.embed_dim, self.embed_dim)
|
| 368 |
+
self.q_proj = nn.Linear(self.embed_dim, self.embed_dim)
|
| 369 |
+
self.out_proj = nn.Linear(self.embed_dim, self.embed_dim)
|
| 370 |
+
|
| 371 |
+
def forward(
|
| 372 |
+
self,
|
| 373 |
+
hidden_states: torch.Tensor,
|
| 374 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 375 |
+
output_attentions: Optional[bool] = False,
|
| 376 |
+
) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
|
| 377 |
+
"""Input shape: Batch x Time x Channel"""
|
| 378 |
+
|
| 379 |
+
batch_size, seq_length, embed_dim = hidden_states.shape
|
| 380 |
+
|
| 381 |
+
queries = self.q_proj(hidden_states)
|
| 382 |
+
keys = self.k_proj(hidden_states)
|
| 383 |
+
values = self.v_proj(hidden_states)
|
| 384 |
+
|
| 385 |
+
queries = queries.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
|
| 386 |
+
keys = keys.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
|
| 387 |
+
values = values.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)
|
| 388 |
+
|
| 389 |
+
attention_interface: Callable = eager_attention_forward
|
| 390 |
+
if self.config._attn_implementation != "eager":
|
| 391 |
+
if self.config._attn_implementation == "sdpa" and output_attentions:
|
| 392 |
+
logger.warning_once(
|
| 393 |
+
"`torch.nn.functional.scaled_dot_product_attention` does not support `output_attentions=True`. Falling back to "
|
| 394 |
+
'eager attention. This warning can be removed using the argument `attn_implementation="eager"` when loading the model.'
|
| 395 |
+
)
|
| 396 |
+
else:
|
| 397 |
+
attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
|
| 398 |
+
|
| 399 |
+
attn_output, attn_weights = attention_interface(
|
| 400 |
+
self,
|
| 401 |
+
queries,
|
| 402 |
+
keys,
|
| 403 |
+
values,
|
| 404 |
+
attention_mask,
|
| 405 |
+
is_causal=self.is_causal,
|
| 406 |
+
scaling=self.scale,
|
| 407 |
+
dropout=0.0 if not self.training else self.dropout,
|
| 408 |
+
)
|
| 409 |
+
|
| 410 |
+
attn_output = attn_output.reshape(batch_size, seq_length, embed_dim).contiguous()
|
| 411 |
+
attn_output = self.out_proj(attn_output)
|
| 412 |
+
|
| 413 |
+
if not output_attentions:
|
| 414 |
+
attn_weights = None
|
| 415 |
+
|
| 416 |
+
return attn_output, attn_weights
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
# Copied from transformers.models.clip.modeling_clip.CLIPMLP with CLIP->Siglip
|
| 420 |
+
class SiglipMLP(nn.Module):
|
| 421 |
+
def __init__(self, config):
|
| 422 |
+
super().__init__()
|
| 423 |
+
self.config = config
|
| 424 |
+
self.activation_fn = ACT2FN[config.hidden_act]
|
| 425 |
+
self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size)
|
| 426 |
+
self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size)
|
| 427 |
+
|
| 428 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 429 |
+
hidden_states = self.fc1(hidden_states)
|
| 430 |
+
hidden_states = self.activation_fn(hidden_states)
|
| 431 |
+
hidden_states = self.fc2(hidden_states)
|
| 432 |
+
return hidden_states
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
class SiglipEncoderLayer(GradientCheckpointingLayer):
|
| 436 |
+
def __init__(self, config: Union[SiglipVisionConfig, SiglipTextConfig]):
|
| 437 |
+
super().__init__()
|
| 438 |
+
self.embed_dim = config.hidden_size
|
| 439 |
+
self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
|
| 440 |
+
self.self_attn = SiglipAttention(config)
|
| 441 |
+
self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
|
| 442 |
+
self.mlp = SiglipMLP(config)
|
| 443 |
+
|
| 444 |
+
def forward(
|
| 445 |
+
self,
|
| 446 |
+
hidden_states: torch.Tensor,
|
| 447 |
+
attention_mask: torch.Tensor,
|
| 448 |
+
output_attentions: Optional[bool] = False,
|
| 449 |
+
) -> tuple[torch.FloatTensor]:
|
| 450 |
+
"""
|
| 451 |
+
Args:
|
| 452 |
+
hidden_states (`torch.FloatTensor`):
|
| 453 |
+
Input to the layer of shape `(batch, seq_len, embed_dim)`.
|
| 454 |
+
attention_mask (`torch.FloatTensor`):
|
| 455 |
+
Attention mask of shape `(batch, 1, q_len, k_v_seq_len)` where padding elements are indicated by very large negative values.
|
| 456 |
+
output_attentions (`bool`, *optional*, defaults to `False`):
|
| 457 |
+
Whether or not to return the attentions tensors of all attention layers. See `attentions` under
|
| 458 |
+
returned tensors for more detail.
|
| 459 |
+
"""
|
| 460 |
+
residual = hidden_states
|
| 461 |
+
|
| 462 |
+
hidden_states = self.layer_norm1(hidden_states)
|
| 463 |
+
hidden_states, attn_weights = self.self_attn(
|
| 464 |
+
hidden_states=hidden_states,
|
| 465 |
+
attention_mask=attention_mask,
|
| 466 |
+
output_attentions=output_attentions,
|
| 467 |
+
)
|
| 468 |
+
hidden_states = residual + hidden_states
|
| 469 |
+
|
| 470 |
+
residual = hidden_states
|
| 471 |
+
hidden_states = self.layer_norm2(hidden_states)
|
| 472 |
+
hidden_states = self.mlp(hidden_states)
|
| 473 |
+
hidden_states = residual + hidden_states
|
| 474 |
+
|
| 475 |
+
outputs = (hidden_states,)
|
| 476 |
+
|
| 477 |
+
if output_attentions:
|
| 478 |
+
outputs += (attn_weights,)
|
| 479 |
+
|
| 480 |
+
return outputs
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
@auto_docstring
|
| 484 |
+
class SiglipPreTrainedModel(PreTrainedModel):
|
| 485 |
+
config_class = SiglipConfig
|
| 486 |
+
base_model_prefix = "siglip"
|
| 487 |
+
supports_gradient_checkpointing = True
|
| 488 |
+
|
| 489 |
+
_no_split_modules = [
|
| 490 |
+
"SiglipTextEmbeddings",
|
| 491 |
+
"SiglipEncoderLayer",
|
| 492 |
+
"SiglipVisionEmbeddings",
|
| 493 |
+
"SiglipEncoderLayer",
|
| 494 |
+
"SiglipMultiheadAttentionPoolingHead",
|
| 495 |
+
]
|
| 496 |
+
_supports_flash_attn_2 = True
|
| 497 |
+
_supports_sdpa = True
|
| 498 |
+
_supports_flex_attn = True
|
| 499 |
+
_supports_attention_backend = True
|
| 500 |
+
|
| 501 |
+
def _init_weights(self, module):
|
| 502 |
+
"""Initialize the weights"""
|
| 503 |
+
if isinstance(module, SiglipVisionEmbeddings):
|
| 504 |
+
width = (
|
| 505 |
+
self.config.vision_config.hidden_size
|
| 506 |
+
if isinstance(self.config, SiglipConfig)
|
| 507 |
+
else self.config.hidden_size
|
| 508 |
+
)
|
| 509 |
+
nn.init.normal_(module.position_embedding.weight, std=1 / np.sqrt(width))
|
| 510 |
+
elif isinstance(module, nn.Embedding):
|
| 511 |
+
default_flax_embed_init(module.weight)
|
| 512 |
+
elif isinstance(module, SiglipAttention):
|
| 513 |
+
nn.init.xavier_uniform_(module.q_proj.weight)
|
| 514 |
+
nn.init.xavier_uniform_(module.k_proj.weight)
|
| 515 |
+
nn.init.xavier_uniform_(module.v_proj.weight)
|
| 516 |
+
nn.init.xavier_uniform_(module.out_proj.weight)
|
| 517 |
+
nn.init.zeros_(module.q_proj.bias)
|
| 518 |
+
nn.init.zeros_(module.k_proj.bias)
|
| 519 |
+
nn.init.zeros_(module.v_proj.bias)
|
| 520 |
+
nn.init.zeros_(module.out_proj.bias)
|
| 521 |
+
elif isinstance(module, SiglipMLP):
|
| 522 |
+
nn.init.xavier_uniform_(module.fc1.weight)
|
| 523 |
+
nn.init.xavier_uniform_(module.fc2.weight)
|
| 524 |
+
nn.init.normal_(module.fc1.bias, std=1e-6)
|
| 525 |
+
nn.init.normal_(module.fc2.bias, std=1e-6)
|
| 526 |
+
elif isinstance(module, SiglipMultiheadAttentionPoolingHead):
|
| 527 |
+
nn.init.xavier_uniform_(module.probe.data)
|
| 528 |
+
nn.init.xavier_uniform_(module.attention.in_proj_weight.data)
|
| 529 |
+
nn.init.zeros_(module.attention.in_proj_bias.data)
|
| 530 |
+
elif isinstance(module, SiglipModel):
|
| 531 |
+
logit_scale_init = torch.log(torch.tensor(1.0))
|
| 532 |
+
module.logit_scale.data.fill_(logit_scale_init)
|
| 533 |
+
module.logit_bias.data.zero_()
|
| 534 |
+
elif isinstance(module, SiglipForImageClassification):
|
| 535 |
+
nn.init.normal_(
|
| 536 |
+
module.classifier.weight,
|
| 537 |
+
std=self.config.vision_config.hidden_size**-0.5 * self.config.initializer_factor,
|
| 538 |
+
)
|
| 539 |
+
elif isinstance(module, (nn.Linear, nn.Conv2d)):
|
| 540 |
+
lecun_normal_(module.weight)
|
| 541 |
+
if module.bias is not None:
|
| 542 |
+
nn.init.zeros_(module.bias)
|
| 543 |
+
elif isinstance(module, nn.LayerNorm):
|
| 544 |
+
module.bias.data.zero_()
|
| 545 |
+
module.weight.data.fill_(1.0)
|
| 546 |
+
|
| 547 |
+
|
| 548 |
+
# Copied from transformers.models.altclip.modeling_altclip.AltCLIPEncoder with AltCLIP->Siglip
|
| 549 |
+
class SiglipEncoder(nn.Module):
|
| 550 |
+
"""
|
| 551 |
+
Transformer encoder consisting of `config.num_hidden_layers` self attention layers. Each layer is a
|
| 552 |
+
[`SiglipEncoderLayer`].
|
| 553 |
+
|
| 554 |
+
Args:
|
| 555 |
+
config: SiglipConfig
|
| 556 |
+
"""
|
| 557 |
+
|
| 558 |
+
def __init__(self, config: SiglipConfig):
|
| 559 |
+
super().__init__()
|
| 560 |
+
self.config = config
|
| 561 |
+
self.layers = nn.ModuleList([SiglipEncoderLayer(config) for _ in range(config.num_hidden_layers)])
|
| 562 |
+
self.gradient_checkpointing = False
|
| 563 |
+
|
| 564 |
+
# Ignore copy
|
| 565 |
+
@can_return_tuple
|
| 566 |
+
def forward(
|
| 567 |
+
self,
|
| 568 |
+
inputs_embeds,
|
| 569 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 570 |
+
output_attentions: Optional[bool] = None,
|
| 571 |
+
output_hidden_states: Optional[bool] = None,
|
| 572 |
+
) -> BaseModelOutput:
|
| 573 |
+
r"""
|
| 574 |
+
Args:
|
| 575 |
+
inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
|
| 576 |
+
Optionally, instead of passing `input_ids` you can choose to directly pass an embedded representation.
|
| 577 |
+
This is useful if you want more control over how to convert `input_ids` indices into associated vectors
|
| 578 |
+
than the model's internal embedding lookup matrix.
|
| 579 |
+
attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*):
|
| 580 |
+
Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`:
|
| 581 |
+
|
| 582 |
+
- 1 for tokens that are **not masked**,
|
| 583 |
+
- 0 for tokens that are **masked**.
|
| 584 |
+
|
| 585 |
+
[What are attention masks?](../glossary#attention-mask)
|
| 586 |
+
output_attentions (`bool`, *optional*):
|
| 587 |
+
Whether or not to return the attentions tensors of all attention layers. See `attentions` under
|
| 588 |
+
returned tensors for more detail.
|
| 589 |
+
output_hidden_states (`bool`, *optional*):
|
| 590 |
+
Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors
|
| 591 |
+
for more detail.
|
| 592 |
+
return_dict (`bool`, *optional*):
|
| 593 |
+
Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple.
|
| 594 |
+
"""
|
| 595 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 596 |
+
output_hidden_states = (
|
| 597 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 598 |
+
)
|
| 599 |
+
|
| 600 |
+
encoder_states = () if output_hidden_states else None
|
| 601 |
+
all_attentions = () if output_attentions else None
|
| 602 |
+
|
| 603 |
+
hidden_states = inputs_embeds
|
| 604 |
+
for encoder_layer in self.layers:
|
| 605 |
+
if output_hidden_states:
|
| 606 |
+
encoder_states = encoder_states + (hidden_states,)
|
| 607 |
+
|
| 608 |
+
layer_outputs = encoder_layer(
|
| 609 |
+
hidden_states,
|
| 610 |
+
attention_mask,
|
| 611 |
+
output_attentions=output_attentions,
|
| 612 |
+
)
|
| 613 |
+
|
| 614 |
+
hidden_states = layer_outputs[0]
|
| 615 |
+
|
| 616 |
+
if output_attentions:
|
| 617 |
+
all_attentions = all_attentions + (layer_outputs[1],)
|
| 618 |
+
|
| 619 |
+
if output_hidden_states:
|
| 620 |
+
encoder_states = encoder_states + (hidden_states,)
|
| 621 |
+
|
| 622 |
+
return BaseModelOutput(
|
| 623 |
+
last_hidden_state=hidden_states,
|
| 624 |
+
hidden_states=encoder_states,
|
| 625 |
+
attentions=all_attentions,
|
| 626 |
+
)
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
class SiglipTextTransformer(nn.Module):
|
| 630 |
+
def __init__(self, config: SiglipTextConfig):
|
| 631 |
+
super().__init__()
|
| 632 |
+
self.config = config
|
| 633 |
+
embed_dim = config.hidden_size
|
| 634 |
+
self.embeddings = SiglipTextEmbeddings(config)
|
| 635 |
+
self.encoder = SiglipEncoder(config)
|
| 636 |
+
self.final_layer_norm = nn.LayerNorm(embed_dim, eps=config.layer_norm_eps)
|
| 637 |
+
|
| 638 |
+
self.head = nn.Linear(embed_dim, config.projection_size)
|
| 639 |
+
self._use_flash_attention_2 = config._attn_implementation == "flash_attention_2"
|
| 640 |
+
|
| 641 |
+
@can_return_tuple
|
| 642 |
+
@auto_docstring
|
| 643 |
+
def forward(
|
| 644 |
+
self,
|
| 645 |
+
input_ids: Optional[torch.Tensor] = None,
|
| 646 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 647 |
+
position_ids: Optional[torch.Tensor] = None,
|
| 648 |
+
output_attentions: Optional[bool] = None,
|
| 649 |
+
output_hidden_states: Optional[bool] = None,
|
| 650 |
+
) -> BaseModelOutputWithPooling:
|
| 651 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 652 |
+
output_hidden_states = (
|
| 653 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 654 |
+
)
|
| 655 |
+
|
| 656 |
+
if input_ids is None:
|
| 657 |
+
raise ValueError("You have to specify input_ids")
|
| 658 |
+
|
| 659 |
+
input_shape = input_ids.size()
|
| 660 |
+
input_ids = input_ids.view(-1, input_shape[-1])
|
| 661 |
+
|
| 662 |
+
hidden_states = self.embeddings(input_ids=input_ids, position_ids=position_ids)
|
| 663 |
+
|
| 664 |
+
# note: SigLIP's text model does not use a causal mask, unlike the original CLIP model.
|
| 665 |
+
# expand attention_mask
|
| 666 |
+
if attention_mask is not None and not self._use_flash_attention_2:
|
| 667 |
+
# [batch_size, seq_len] -> [batch_size, 1, tgt_seq_len, src_seq_len]
|
| 668 |
+
attention_mask = _prepare_4d_attention_mask(attention_mask, hidden_states.dtype)
|
| 669 |
+
|
| 670 |
+
encoder_outputs: BaseModelOutput = self.encoder(
|
| 671 |
+
inputs_embeds=hidden_states,
|
| 672 |
+
attention_mask=attention_mask,
|
| 673 |
+
output_attentions=output_attentions,
|
| 674 |
+
output_hidden_states=output_hidden_states,
|
| 675 |
+
)
|
| 676 |
+
|
| 677 |
+
last_hidden_state = encoder_outputs.last_hidden_state
|
| 678 |
+
last_hidden_state = self.final_layer_norm(last_hidden_state)
|
| 679 |
+
|
| 680 |
+
# Assuming "sticky" EOS tokenization, last token is always EOS.
|
| 681 |
+
pooled_output = last_hidden_state[:, -1, :]
|
| 682 |
+
pooled_output = self.head(pooled_output)
|
| 683 |
+
|
| 684 |
+
return BaseModelOutputWithPooling(
|
| 685 |
+
last_hidden_state=last_hidden_state,
|
| 686 |
+
pooler_output=pooled_output,
|
| 687 |
+
hidden_states=encoder_outputs.hidden_states,
|
| 688 |
+
attentions=encoder_outputs.attentions,
|
| 689 |
+
)
|
| 690 |
+
|
| 691 |
+
|
| 692 |
+
@auto_docstring(
|
| 693 |
+
custom_intro="""
|
| 694 |
+
The text model from SigLIP without any head or projection on top.
|
| 695 |
+
"""
|
| 696 |
+
)
|
| 697 |
+
class SiglipTextModel(SiglipPreTrainedModel):
|
| 698 |
+
config_class = SiglipTextConfig
|
| 699 |
+
|
| 700 |
+
def __init__(self, config: SiglipTextConfig):
|
| 701 |
+
super().__init__(config)
|
| 702 |
+
self.text_model = SiglipTextTransformer(config)
|
| 703 |
+
# Initialize weights and apply final processing
|
| 704 |
+
self.post_init()
|
| 705 |
+
|
| 706 |
+
def get_input_embeddings(self) -> nn.Module:
|
| 707 |
+
return self.text_model.embeddings.token_embedding
|
| 708 |
+
|
| 709 |
+
def set_input_embeddings(self, value):
|
| 710 |
+
self.text_model.embeddings.token_embedding = value
|
| 711 |
+
|
| 712 |
+
@can_return_tuple
|
| 713 |
+
@auto_docstring
|
| 714 |
+
def forward(
|
| 715 |
+
self,
|
| 716 |
+
input_ids: Optional[torch.Tensor] = None,
|
| 717 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 718 |
+
position_ids: Optional[torch.Tensor] = None,
|
| 719 |
+
output_attentions: Optional[bool] = None,
|
| 720 |
+
output_hidden_states: Optional[bool] = None,
|
| 721 |
+
) -> BaseModelOutputWithPooling:
|
| 722 |
+
r"""
|
| 723 |
+
Examples:
|
| 724 |
+
|
| 725 |
+
```python
|
| 726 |
+
>>> from transformers import AutoTokenizer, SiglipTextModel
|
| 727 |
+
|
| 728 |
+
>>> model = SiglipTextModel.from_pretrained("google/siglip-base-patch16-224")
|
| 729 |
+
>>> tokenizer = AutoTokenizer.from_pretrained("google/siglip-base-patch16-224")
|
| 730 |
+
|
| 731 |
+
>>> # important: make sure to set padding="max_length" as that's how the model was trained
|
| 732 |
+
>>> inputs = tokenizer(["a photo of a cat", "a photo of a dog"], padding="max_length", return_tensors="pt")
|
| 733 |
+
|
| 734 |
+
>>> outputs = model(**inputs)
|
| 735 |
+
>>> last_hidden_state = outputs.last_hidden_state
|
| 736 |
+
>>> pooled_output = outputs.pooler_output # pooled (EOS token) states
|
| 737 |
+
```"""
|
| 738 |
+
|
| 739 |
+
return self.text_model(
|
| 740 |
+
input_ids=input_ids,
|
| 741 |
+
attention_mask=attention_mask,
|
| 742 |
+
position_ids=position_ids,
|
| 743 |
+
output_attentions=output_attentions,
|
| 744 |
+
output_hidden_states=output_hidden_states,
|
| 745 |
+
)
|
| 746 |
+
|
| 747 |
+
|
| 748 |
+
class SiglipVisionTransformer(nn.Module):
|
| 749 |
+
def __init__(self, config: SiglipVisionConfig):
|
| 750 |
+
super().__init__()
|
| 751 |
+
self.config = config
|
| 752 |
+
embed_dim = config.hidden_size
|
| 753 |
+
|
| 754 |
+
self.embeddings = SiglipVisionEmbeddings(config)
|
| 755 |
+
self.encoder = SiglipEncoder(config)
|
| 756 |
+
self.post_layernorm = nn.LayerNorm(embed_dim, eps=config.layer_norm_eps)
|
| 757 |
+
self.use_head = True if not hasattr(config, "vision_use_head") else config.vision_use_head
|
| 758 |
+
if self.use_head:
|
| 759 |
+
self.head = SiglipMultiheadAttentionPoolingHead(config)
|
| 760 |
+
|
| 761 |
+
@can_return_tuple
|
| 762 |
+
@auto_docstring
|
| 763 |
+
def forward(
|
| 764 |
+
self,
|
| 765 |
+
pixel_values,
|
| 766 |
+
output_attentions: Optional[bool] = None,
|
| 767 |
+
output_hidden_states: Optional[bool] = None,
|
| 768 |
+
interpolate_pos_encoding: Optional[bool] = False,
|
| 769 |
+
) -> BaseModelOutputWithPooling:
|
| 770 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 771 |
+
output_hidden_states = (
|
| 772 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 773 |
+
)
|
| 774 |
+
|
| 775 |
+
hidden_states = self.embeddings(pixel_values, interpolate_pos_encoding=interpolate_pos_encoding)
|
| 776 |
+
# Convert to bfloat16 if the encoder uses bfloat16
|
| 777 |
+
if len(self.encoder.layers) > 0 and self.encoder.layers[0].self_attn.q_proj.weight.dtype == torch.bfloat16:
|
| 778 |
+
hidden_states = hidden_states.to(torch.bfloat16)
|
| 779 |
+
|
| 780 |
+
encoder_outputs: BaseModelOutput = self.encoder(
|
| 781 |
+
inputs_embeds=hidden_states,
|
| 782 |
+
output_attentions=output_attentions,
|
| 783 |
+
output_hidden_states=output_hidden_states,
|
| 784 |
+
)
|
| 785 |
+
|
| 786 |
+
last_hidden_state = encoder_outputs.last_hidden_state
|
| 787 |
+
last_hidden_state = self.post_layernorm(last_hidden_state)
|
| 788 |
+
|
| 789 |
+
pooler_output = self.head(last_hidden_state) if self.use_head else None
|
| 790 |
+
|
| 791 |
+
return BaseModelOutputWithPooling(
|
| 792 |
+
last_hidden_state=last_hidden_state,
|
| 793 |
+
pooler_output=pooler_output,
|
| 794 |
+
hidden_states=encoder_outputs.hidden_states,
|
| 795 |
+
attentions=encoder_outputs.attentions,
|
| 796 |
+
)
|
| 797 |
+
|
| 798 |
+
|
| 799 |
+
class SiglipMultiheadAttentionPoolingHead(nn.Module):
|
| 800 |
+
"""Multihead Attention Pooling."""
|
| 801 |
+
|
| 802 |
+
def __init__(self, config: SiglipVisionConfig):
|
| 803 |
+
super().__init__()
|
| 804 |
+
|
| 805 |
+
self.probe = nn.Parameter(torch.randn(1, 1, config.hidden_size))
|
| 806 |
+
self.attention = torch.nn.MultiheadAttention(config.hidden_size, config.num_attention_heads, batch_first=True)
|
| 807 |
+
self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
|
| 808 |
+
self.mlp = SiglipMLP(config)
|
| 809 |
+
|
| 810 |
+
def forward(self, hidden_state):
|
| 811 |
+
batch_size = hidden_state.shape[0]
|
| 812 |
+
probe = self.probe.repeat(batch_size, 1, 1)
|
| 813 |
+
|
| 814 |
+
hidden_state = self.attention(probe, hidden_state, hidden_state)[0]
|
| 815 |
+
|
| 816 |
+
residual = hidden_state
|
| 817 |
+
hidden_state = self.layernorm(hidden_state)
|
| 818 |
+
hidden_state = residual + self.mlp(hidden_state)
|
| 819 |
+
|
| 820 |
+
return hidden_state[:, 0]
|
| 821 |
+
|
| 822 |
+
|
| 823 |
+
@auto_docstring(
|
| 824 |
+
custom_intro="""
|
| 825 |
+
The vision model from SigLIP without any head or projection on top.
|
| 826 |
+
"""
|
| 827 |
+
)
|
| 828 |
+
class SiglipVisionModel(SiglipPreTrainedModel):
|
| 829 |
+
config_class = SiglipVisionConfig
|
| 830 |
+
main_input_name = "pixel_values"
|
| 831 |
+
|
| 832 |
+
def __init__(self, config: SiglipVisionConfig):
|
| 833 |
+
super().__init__(config)
|
| 834 |
+
|
| 835 |
+
self.vision_model = SiglipVisionTransformer(config)
|
| 836 |
+
|
| 837 |
+
# Initialize weights and apply final processing
|
| 838 |
+
self.post_init()
|
| 839 |
+
|
| 840 |
+
def get_input_embeddings(self) -> nn.Module:
|
| 841 |
+
return self.vision_model.embeddings.patch_embedding
|
| 842 |
+
|
| 843 |
+
@can_return_tuple
|
| 844 |
+
@auto_docstring
|
| 845 |
+
def forward(
|
| 846 |
+
self,
|
| 847 |
+
pixel_values,
|
| 848 |
+
output_attentions: Optional[bool] = None,
|
| 849 |
+
output_hidden_states: Optional[bool] = None,
|
| 850 |
+
interpolate_pos_encoding: bool = False,
|
| 851 |
+
) -> BaseModelOutputWithPooling:
|
| 852 |
+
r"""
|
| 853 |
+
Examples:
|
| 854 |
+
|
| 855 |
+
```python
|
| 856 |
+
>>> from PIL import Image
|
| 857 |
+
>>> import requests
|
| 858 |
+
>>> from transformers import AutoProcessor, SiglipVisionModel
|
| 859 |
+
|
| 860 |
+
>>> model = SiglipVisionModel.from_pretrained("google/siglip-base-patch16-224")
|
| 861 |
+
>>> processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-224")
|
| 862 |
+
|
| 863 |
+
>>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
|
| 864 |
+
>>> image = Image.open(requests.get(url, stream=True).raw)
|
| 865 |
+
|
| 866 |
+
>>> inputs = processor(images=image, return_tensors="pt")
|
| 867 |
+
|
| 868 |
+
>>> outputs = model(**inputs)
|
| 869 |
+
>>> last_hidden_state = outputs.last_hidden_state
|
| 870 |
+
>>> pooled_output = outputs.pooler_output # pooled features
|
| 871 |
+
```"""
|
| 872 |
+
|
| 873 |
+
return self.vision_model(
|
| 874 |
+
pixel_values=pixel_values,
|
| 875 |
+
output_attentions=output_attentions,
|
| 876 |
+
output_hidden_states=output_hidden_states,
|
| 877 |
+
interpolate_pos_encoding=interpolate_pos_encoding,
|
| 878 |
+
)
|
| 879 |
+
|
| 880 |
+
|
| 881 |
+
@auto_docstring
|
| 882 |
+
class SiglipModel(SiglipPreTrainedModel):
|
| 883 |
+
config_class = SiglipConfig
|
| 884 |
+
|
| 885 |
+
def __init__(self, config: SiglipConfig):
|
| 886 |
+
super().__init__(config)
|
| 887 |
+
|
| 888 |
+
if not isinstance(config.text_config, SiglipTextConfig):
|
| 889 |
+
raise TypeError(
|
| 890 |
+
"config.text_config is expected to be of type SiglipTextConfig but is of type"
|
| 891 |
+
f" {type(config.text_config)}."
|
| 892 |
+
)
|
| 893 |
+
|
| 894 |
+
if not isinstance(config.vision_config, SiglipVisionConfig):
|
| 895 |
+
raise TypeError(
|
| 896 |
+
"config.vision_config is expected to be of type SiglipVisionConfig but is of type"
|
| 897 |
+
f" {type(config.vision_config)}."
|
| 898 |
+
)
|
| 899 |
+
|
| 900 |
+
text_config = config.text_config
|
| 901 |
+
vision_config = config.vision_config
|
| 902 |
+
|
| 903 |
+
# First, initialize the text and vision models with proper attention implementation
|
| 904 |
+
text_model = SiglipTextModel._from_config(text_config)
|
| 905 |
+
vision_model = SiglipVisionModel._from_config(vision_config)
|
| 906 |
+
|
| 907 |
+
# Second, get the text and vision submodules (for backward compatibility)
|
| 908 |
+
self.text_model = text_model.text_model
|
| 909 |
+
self.vision_model = vision_model.vision_model
|
| 910 |
+
|
| 911 |
+
self.logit_scale = nn.Parameter(torch.randn(1))
|
| 912 |
+
self.logit_bias = nn.Parameter(torch.randn(1))
|
| 913 |
+
|
| 914 |
+
# Initialize weights and apply final processing
|
| 915 |
+
self.post_init()
|
| 916 |
+
|
| 917 |
+
@auto_docstring
|
| 918 |
+
def get_text_features(
|
| 919 |
+
self,
|
| 920 |
+
input_ids: Optional[torch.Tensor] = None,
|
| 921 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 922 |
+
position_ids: Optional[torch.Tensor] = None,
|
| 923 |
+
output_attentions: Optional[bool] = None,
|
| 924 |
+
output_hidden_states: Optional[bool] = None,
|
| 925 |
+
) -> torch.FloatTensor:
|
| 926 |
+
r"""
|
| 927 |
+
Returns:
|
| 928 |
+
text_features (`torch.FloatTensor` of shape `(batch_size, output_dim`): The text embeddings obtained by
|
| 929 |
+
applying the projection layer to the pooled output of [`SiglipTextModel`].
|
| 930 |
+
|
| 931 |
+
Examples:
|
| 932 |
+
|
| 933 |
+
```python
|
| 934 |
+
>>> from transformers import AutoTokenizer, AutoModel
|
| 935 |
+
>>> import torch
|
| 936 |
+
|
| 937 |
+
>>> model = AutoModel.from_pretrained("google/siglip-base-patch16-224")
|
| 938 |
+
>>> tokenizer = AutoTokenizer.from_pretrained("google/siglip-base-patch16-224")
|
| 939 |
+
|
| 940 |
+
>>> # important: make sure to set padding="max_length" as that's how the model was trained
|
| 941 |
+
>>> inputs = tokenizer(["a photo of a cat", "a photo of a dog"], padding="max_length", return_tensors="pt")
|
| 942 |
+
>>> with torch.no_grad():
|
| 943 |
+
... text_features = model.get_text_features(**inputs)
|
| 944 |
+
```"""
|
| 945 |
+
# Use SigLIP model's config for some fields (if specified) instead of those of vision & text components.
|
| 946 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 947 |
+
output_hidden_states = (
|
| 948 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 949 |
+
)
|
| 950 |
+
|
| 951 |
+
text_outputs: BaseModelOutputWithPooling = self.text_model(
|
| 952 |
+
input_ids=input_ids,
|
| 953 |
+
attention_mask=attention_mask,
|
| 954 |
+
position_ids=position_ids,
|
| 955 |
+
output_attentions=output_attentions,
|
| 956 |
+
output_hidden_states=output_hidden_states,
|
| 957 |
+
)
|
| 958 |
+
|
| 959 |
+
pooled_output = text_outputs.pooler_output
|
| 960 |
+
|
| 961 |
+
return pooled_output
|
| 962 |
+
|
| 963 |
+
@auto_docstring
|
| 964 |
+
def get_image_features(
|
| 965 |
+
self,
|
| 966 |
+
pixel_values: Optional[torch.FloatTensor] = None,
|
| 967 |
+
output_attentions: Optional[bool] = None,
|
| 968 |
+
output_hidden_states: Optional[bool] = None,
|
| 969 |
+
interpolate_pos_encoding: bool = False,
|
| 970 |
+
) -> torch.FloatTensor:
|
| 971 |
+
r"""
|
| 972 |
+
Returns:
|
| 973 |
+
image_features (`torch.FloatTensor` of shape `(batch_size, output_dim`): The image embeddings obtained by
|
| 974 |
+
applying the projection layer to the pooled output of [`SiglipVisionModel`].
|
| 975 |
+
|
| 976 |
+
Examples:
|
| 977 |
+
|
| 978 |
+
```python
|
| 979 |
+
>>> from PIL import Image
|
| 980 |
+
>>> import requests
|
| 981 |
+
>>> from transformers import AutoProcessor, AutoModel
|
| 982 |
+
>>> import torch
|
| 983 |
+
|
| 984 |
+
>>> model = AutoModel.from_pretrained("google/siglip-base-patch16-224")
|
| 985 |
+
>>> processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-224")
|
| 986 |
+
|
| 987 |
+
>>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
|
| 988 |
+
>>> image = Image.open(requests.get(url, stream=True).raw)
|
| 989 |
+
|
| 990 |
+
>>> inputs = processor(images=image, return_tensors="pt")
|
| 991 |
+
|
| 992 |
+
>>> with torch.no_grad():
|
| 993 |
+
... image_features = model.get_image_features(**inputs)
|
| 994 |
+
```"""
|
| 995 |
+
# Use SiglipModel's config for some fields (if specified) instead of those of vision & text components.
|
| 996 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 997 |
+
output_hidden_states = (
|
| 998 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 999 |
+
)
|
| 1000 |
+
|
| 1001 |
+
vision_outputs: BaseModelOutputWithPooling = self.vision_model(
|
| 1002 |
+
pixel_values=pixel_values,
|
| 1003 |
+
output_attentions=output_attentions,
|
| 1004 |
+
output_hidden_states=output_hidden_states,
|
| 1005 |
+
interpolate_pos_encoding=interpolate_pos_encoding,
|
| 1006 |
+
)
|
| 1007 |
+
|
| 1008 |
+
pooled_output = vision_outputs.pooler_output
|
| 1009 |
+
|
| 1010 |
+
return pooled_output
|
| 1011 |
+
|
| 1012 |
+
@can_return_tuple
|
| 1013 |
+
@auto_docstring
|
| 1014 |
+
def forward(
|
| 1015 |
+
self,
|
| 1016 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 1017 |
+
pixel_values: Optional[torch.FloatTensor] = None,
|
| 1018 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 1019 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 1020 |
+
return_loss: Optional[bool] = None,
|
| 1021 |
+
output_attentions: Optional[bool] = None,
|
| 1022 |
+
output_hidden_states: Optional[bool] = None,
|
| 1023 |
+
interpolate_pos_encoding: bool = False,
|
| 1024 |
+
) -> SiglipOutput:
|
| 1025 |
+
r"""
|
| 1026 |
+
return_loss (`bool`, *optional*):
|
| 1027 |
+
Whether or not to return the contrastive loss.
|
| 1028 |
+
|
| 1029 |
+
Examples:
|
| 1030 |
+
|
| 1031 |
+
```python
|
| 1032 |
+
>>> from PIL import Image
|
| 1033 |
+
>>> import requests
|
| 1034 |
+
>>> from transformers import AutoProcessor, AutoModel
|
| 1035 |
+
>>> import torch
|
| 1036 |
+
|
| 1037 |
+
>>> model = AutoModel.from_pretrained("google/siglip-base-patch16-224")
|
| 1038 |
+
>>> processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-224")
|
| 1039 |
+
|
| 1040 |
+
>>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
|
| 1041 |
+
>>> image = Image.open(requests.get(url, stream=True).raw)
|
| 1042 |
+
|
| 1043 |
+
>>> texts = ["a photo of 2 cats", "a photo of 2 dogs"]
|
| 1044 |
+
>>> # important: we pass `padding=max_length` since the model was trained with this
|
| 1045 |
+
>>> inputs = processor(text=texts, images=image, padding="max_length", return_tensors="pt")
|
| 1046 |
+
|
| 1047 |
+
>>> with torch.no_grad():
|
| 1048 |
+
... outputs = model(**inputs)
|
| 1049 |
+
|
| 1050 |
+
>>> logits_per_image = outputs.logits_per_image
|
| 1051 |
+
>>> probs = torch.sigmoid(logits_per_image) # these are the probabilities
|
| 1052 |
+
>>> print(f"{probs[0][0]:.1%} that image 0 is '{texts[0]}'")
|
| 1053 |
+
31.9% that image 0 is 'a photo of 2 cats'
|
| 1054 |
+
```"""
|
| 1055 |
+
# Use SigLIP model's config for some fields (if specified) instead of those of vision & text components.
|
| 1056 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 1057 |
+
output_hidden_states = (
|
| 1058 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 1059 |
+
)
|
| 1060 |
+
|
| 1061 |
+
vision_outputs: BaseModelOutputWithPooling = self.vision_model(
|
| 1062 |
+
pixel_values=pixel_values,
|
| 1063 |
+
output_attentions=output_attentions,
|
| 1064 |
+
output_hidden_states=output_hidden_states,
|
| 1065 |
+
interpolate_pos_encoding=interpolate_pos_encoding,
|
| 1066 |
+
)
|
| 1067 |
+
|
| 1068 |
+
text_outputs: BaseModelOutputWithPooling = self.text_model(
|
| 1069 |
+
input_ids=input_ids,
|
| 1070 |
+
attention_mask=attention_mask,
|
| 1071 |
+
position_ids=position_ids,
|
| 1072 |
+
output_attentions=output_attentions,
|
| 1073 |
+
output_hidden_states=output_hidden_states,
|
| 1074 |
+
)
|
| 1075 |
+
|
| 1076 |
+
image_embeds = vision_outputs.pooler_output
|
| 1077 |
+
text_embeds = text_outputs.pooler_output
|
| 1078 |
+
|
| 1079 |
+
# normalized features
|
| 1080 |
+
image_embeds = image_embeds / image_embeds.norm(p=2, dim=-1, keepdim=True)
|
| 1081 |
+
text_embeds = text_embeds / text_embeds.norm(p=2, dim=-1, keepdim=True)
|
| 1082 |
+
|
| 1083 |
+
# cosine similarity as logits
|
| 1084 |
+
logits_per_text = torch.matmul(text_embeds, image_embeds.t().to(text_embeds.device))
|
| 1085 |
+
|
| 1086 |
+
logit_scale, logit_bias = self.logit_scale.to(text_embeds.device), self.logit_bias.to(text_embeds.device)
|
| 1087 |
+
logits_per_text = logits_per_text * logit_scale.exp() + logit_bias
|
| 1088 |
+
|
| 1089 |
+
logits_per_image = logits_per_text.t()
|
| 1090 |
+
|
| 1091 |
+
loss = None
|
| 1092 |
+
if return_loss:
|
| 1093 |
+
# Adapted from https://github.com/google-research/big_vision/blob/01edb81a4716f93a48be43b3a4af14e29cdb3a7f/big_vision/trainers/proj/image_text/siglip.py#L287
|
| 1094 |
+
eye = torch.eye(logits_per_text.size(0), device=logits_per_text.device)
|
| 1095 |
+
m1_diag1 = -torch.ones_like(logits_per_text) + 2 * eye
|
| 1096 |
+
loglik = torch.nn.functional.logsigmoid(m1_diag1 * logits_per_text)
|
| 1097 |
+
nll = -torch.sum(loglik, dim=-1)
|
| 1098 |
+
loss = nll.mean()
|
| 1099 |
+
|
| 1100 |
+
return SiglipOutput(
|
| 1101 |
+
loss=loss,
|
| 1102 |
+
logits_per_image=logits_per_image,
|
| 1103 |
+
logits_per_text=logits_per_text,
|
| 1104 |
+
text_embeds=text_embeds,
|
| 1105 |
+
image_embeds=image_embeds,
|
| 1106 |
+
text_model_output=text_outputs,
|
| 1107 |
+
vision_model_output=vision_outputs,
|
| 1108 |
+
)
|
| 1109 |
+
|
| 1110 |
+
|
| 1111 |
+
@auto_docstring(
|
| 1112 |
+
custom_intro="""
|
| 1113 |
+
SigLIP vision encoder with an image classification head on top (a linear layer on top of the pooled final hidden states of
|
| 1114 |
+
the patch tokens) e.g. for ImageNet.
|
| 1115 |
+
"""
|
| 1116 |
+
)
|
| 1117 |
+
class SiglipForImageClassification(SiglipPreTrainedModel):
|
| 1118 |
+
main_input_name = "pixel_values"
|
| 1119 |
+
|
| 1120 |
+
def __init__(self, config: SiglipConfig) -> None:
|
| 1121 |
+
super().__init__(config)
|
| 1122 |
+
|
| 1123 |
+
self.num_labels = config.num_labels
|
| 1124 |
+
|
| 1125 |
+
# Create the vision model with proper attention
|
| 1126 |
+
# and take only vision_model submodule (for backward compatibility)
|
| 1127 |
+
vision_model = SiglipVisionModel._from_config(config.vision_config)
|
| 1128 |
+
self.vision_model = vision_model.vision_model
|
| 1129 |
+
|
| 1130 |
+
# Classifier head
|
| 1131 |
+
self.classifier = (
|
| 1132 |
+
nn.Linear(config.vision_config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
|
| 1133 |
+
)
|
| 1134 |
+
|
| 1135 |
+
# Initialize weights and apply final processing
|
| 1136 |
+
self.post_init()
|
| 1137 |
+
|
| 1138 |
+
@can_return_tuple
|
| 1139 |
+
@auto_docstring
|
| 1140 |
+
def forward(
|
| 1141 |
+
self,
|
| 1142 |
+
pixel_values: Optional[torch.Tensor] = None,
|
| 1143 |
+
labels: Optional[torch.Tensor] = None,
|
| 1144 |
+
output_attentions: Optional[bool] = None,
|
| 1145 |
+
output_hidden_states: Optional[bool] = None,
|
| 1146 |
+
interpolate_pos_encoding: bool = False,
|
| 1147 |
+
) -> ImageClassifierOutput:
|
| 1148 |
+
r"""
|
| 1149 |
+
labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
|
| 1150 |
+
Labels for computing the image classification/regression loss. Indices should be in `[0, ...,
|
| 1151 |
+
config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
|
| 1152 |
+
`config.num_labels > 1` a classification loss is computed (Cross-Entropy).
|
| 1153 |
+
|
| 1154 |
+
Examples:
|
| 1155 |
+
|
| 1156 |
+
```python
|
| 1157 |
+
>>> from transformers import AutoImageProcessor, SiglipForImageClassification
|
| 1158 |
+
>>> import torch
|
| 1159 |
+
>>> from PIL import Image
|
| 1160 |
+
>>> import requests
|
| 1161 |
+
|
| 1162 |
+
>>> torch.manual_seed(3) # doctest: +IGNORE_RESULT
|
| 1163 |
+
>>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
|
| 1164 |
+
>>> image = Image.open(requests.get(url, stream=True).raw)
|
| 1165 |
+
|
| 1166 |
+
>>> # note: we are loading a `SiglipModel` from the hub here,
|
| 1167 |
+
>>> # so the head will be randomly initialized, hence the predictions will be random if seed is not set above.
|
| 1168 |
+
>>> image_processor = AutoImageProcessor.from_pretrained("google/siglip-base-patch16-224")
|
| 1169 |
+
>>> model = SiglipForImageClassification.from_pretrained("google/siglip-base-patch16-224")
|
| 1170 |
+
|
| 1171 |
+
>>> inputs = image_processor(images=image, return_tensors="pt")
|
| 1172 |
+
>>> outputs = model(**inputs)
|
| 1173 |
+
>>> logits = outputs.logits
|
| 1174 |
+
>>> # model predicts one of the two classes
|
| 1175 |
+
>>> predicted_class_idx = logits.argmax(-1).item()
|
| 1176 |
+
>>> print("Predicted class:", model.config.id2label[predicted_class_idx])
|
| 1177 |
+
Predicted class: LABEL_1
|
| 1178 |
+
```"""
|
| 1179 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 1180 |
+
output_hidden_states = (
|
| 1181 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 1182 |
+
)
|
| 1183 |
+
|
| 1184 |
+
outputs: BaseModelOutputWithPooling = self.vision_model(
|
| 1185 |
+
pixel_values,
|
| 1186 |
+
output_attentions=output_attentions,
|
| 1187 |
+
output_hidden_states=output_hidden_states,
|
| 1188 |
+
interpolate_pos_encoding=interpolate_pos_encoding,
|
| 1189 |
+
)
|
| 1190 |
+
|
| 1191 |
+
sequence_output = outputs.last_hidden_state
|
| 1192 |
+
|
| 1193 |
+
# average pool the patch tokens
|
| 1194 |
+
sequence_output = torch.mean(sequence_output, dim=1)
|
| 1195 |
+
# apply classifier
|
| 1196 |
+
logits = self.classifier(sequence_output)
|
| 1197 |
+
|
| 1198 |
+
loss = None
|
| 1199 |
+
if labels is not None:
|
| 1200 |
+
# move labels to correct device to enable model parallelism
|
| 1201 |
+
labels = labels.to(logits.device)
|
| 1202 |
+
if self.config.problem_type is None:
|
| 1203 |
+
if self.num_labels == 1:
|
| 1204 |
+
self.config.problem_type = "regression"
|
| 1205 |
+
elif self.num_labels > 1 and (labels.dtype == torch.long or labels.dtype == torch.int):
|
| 1206 |
+
self.config.problem_type = "single_label_classification"
|
| 1207 |
+
else:
|
| 1208 |
+
self.config.problem_type = "multi_label_classification"
|
| 1209 |
+
|
| 1210 |
+
if self.config.problem_type == "regression":
|
| 1211 |
+
loss_fct = MSELoss()
|
| 1212 |
+
if self.num_labels == 1:
|
| 1213 |
+
loss = loss_fct(logits.squeeze(), labels.squeeze())
|
| 1214 |
+
else:
|
| 1215 |
+
loss = loss_fct(logits, labels)
|
| 1216 |
+
elif self.config.problem_type == "single_label_classification":
|
| 1217 |
+
loss_fct = CrossEntropyLoss()
|
| 1218 |
+
loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
|
| 1219 |
+
elif self.config.problem_type == "multi_label_classification":
|
| 1220 |
+
loss_fct = BCEWithLogitsLoss()
|
| 1221 |
+
loss = loss_fct(logits, labels)
|
| 1222 |
+
|
| 1223 |
+
return ImageClassifierOutput(
|
| 1224 |
+
loss=loss,
|
| 1225 |
+
logits=logits,
|
| 1226 |
+
hidden_states=outputs.hidden_states,
|
| 1227 |
+
attentions=outputs.attentions,
|
| 1228 |
+
)
|
| 1229 |
+
|
| 1230 |
+
|
| 1231 |
+
__all__ = [
|
| 1232 |
+
"SiglipModel",
|
| 1233 |
+
"SiglipPreTrainedModel",
|
| 1234 |
+
"SiglipTextModel",
|
| 1235 |
+
"SiglipVisionModel",
|
| 1236 |
+
"SiglipForImageClassification",
|
| 1237 |
+
]
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/policies/__pycache__/aloha_policy.cpython-311.pyc
ADDED
|
Binary file (15.6 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/policies/__pycache__/droid_policy.cpython-311.pyc
ADDED
|
Binary file (5.1 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/policies/__pycache__/franka_ee_policy.cpython-310.pyc
ADDED
|
Binary file (4.08 kB). View file
|
|
|
pi05_twotasks_pytorch/code/openpi-main/src/openpi/policies/__pycache__/franka_ee_policy.cpython-311.pyc
ADDED
|
Binary file (8.01 kB). View file
|
|
|