Publish TaskAnchor source; model transfer in progress
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- README.md +12 -0
- code/.gitignore +23 -0
- code/LICENSE +201 -0
- code/LICENSES/LICENSE_GEMMA.txt +77 -0
- code/NOTICE +22 -0
- code/README.md +96 -0
- code/SOURCE_MANIFEST.json +654 -0
- code/assets/pi05_libero/norm_stats.json +84 -0
- code/docs/PORTABILITY.md +108 -0
- code/docs/PUBLISHING_ZH.md +46 -0
- code/docs/REPRODUCING.md +283 -0
- code/evidence/environment_pi05.json +19 -0
- code/evidence/packaging_checks.json +17 -0
- code/evidence/pipeline_restart_10000.json +37 -0
- code/evidence/robomem50k_adapter_numerical_parity.json +24 -0
- code/evidence/task25_seed_candidates.json +208 -0
- code/pi05/LICENSE +201 -0
- code/pi05/LICENSE_GEMMA.txt +77 -0
- code/pi05/README.md +323 -0
- code/pi05/packages/openpi-client/pyproject.toml +22 -0
- code/pi05/packages/openpi-client/src/openpi_client/__init__.py +1 -0
- code/pi05/packages/openpi-client/src/openpi_client/action_chunk_broker.py +50 -0
- code/pi05/packages/openpi-client/src/openpi_client/base_policy.py +12 -0
- code/pi05/packages/openpi-client/src/openpi_client/image_tools.py +58 -0
- code/pi05/packages/openpi-client/src/openpi_client/image_tools_test.py +37 -0
- code/pi05/packages/openpi-client/src/openpi_client/msgpack_numpy.py +57 -0
- code/pi05/packages/openpi-client/src/openpi_client/msgpack_numpy_test.py +45 -0
- code/pi05/packages/openpi-client/src/openpi_client/runtime/agent.py +17 -0
- code/pi05/packages/openpi-client/src/openpi_client/runtime/agents/policy_agent.py +18 -0
- code/pi05/packages/openpi-client/src/openpi_client/runtime/environment.py +32 -0
- code/pi05/packages/openpi-client/src/openpi_client/runtime/runtime.py +92 -0
- code/pi05/packages/openpi-client/src/openpi_client/runtime/subscriber.py +20 -0
- code/pi05/packages/openpi-client/src/openpi_client/websocket_client_policy.py +58 -0
- code/pi05/pyproject.toml +140 -0
- code/pi05/scripts/__init__.py +0 -0
- code/pi05/scripts/check_robomem_memory_graph.py +59 -0
- code/pi05/scripts/evaluate_robomem_memory.py +271 -0
- code/pi05/scripts/extract_robomem_memory.py +169 -0
- code/pi05/scripts/robomem_eval_paths.py +20 -0
- code/pi05/scripts/robomem_memory_eval_adapter.py +93 -0
- code/pi05/scripts/robomem_seed_search.py +57 -0
- code/pi05/scripts/run_robomem_eval_task.py +103 -0
- code/pi05/scripts/run_robomem_task25_seed.py +36 -0
- code/pi05/scripts/serve_robomem_memory.py +16 -0
- code/pi05/scripts/test_robomem_discrete_progress.py +58 -0
- code/pi05/scripts/test_robomem_memory.py +163 -0
- code/pi05/scripts/test_robomem_memory_inference.py +54 -0
- code/pi05/scripts/test_robomem_memory_loader.py +82 -0
- code/pi05/scripts/train_robomem_memory.py +206 -0
- code/pi05/src/openpi/__init__.py +0 -0
README.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
| 3 |
+
tags:
|
| 4 |
+
- taskanchor
|
| 5 |
+
- pi05
|
| 6 |
+
- robomemarena
|
| 7 |
---
|
| 8 |
+
|
| 9 |
+
# TaskAnchor for RoboMemArena
|
| 10 |
+
|
| 11 |
+
The training, inference, and evaluation source is available in [code/](code/README.md).
|
| 12 |
+
|
| 13 |
+
**Model upload is in progress.** The complete 50k checkpoint is not available yet; do not attempt model loading until `50000/params/` and the final release manifest have been published.
|
| 14 |
+
|
| 15 |
+
Source code is Apache-2.0. The full pretrained-model release will retain applicable upstream Gemma terms. No benchmark results are published here.
|
code/.gitignore
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.py[cod]
|
| 3 |
+
.pytest_cache/
|
| 4 |
+
.ruff_cache/
|
| 5 |
+
.venv/
|
| 6 |
+
.env
|
| 7 |
+
.env.*
|
| 8 |
+
!.env.example
|
| 9 |
+
.cache/
|
| 10 |
+
.wandb/
|
| 11 |
+
wandb/
|
| 12 |
+
logs/
|
| 13 |
+
/checkpoints/
|
| 14 |
+
/pretrained/
|
| 15 |
+
/data/
|
| 16 |
+
*.egg-info/
|
| 17 |
+
build/
|
| 18 |
+
dist/
|
| 19 |
+
/reports/
|
| 20 |
+
auth.json
|
| 21 |
+
*token*.txt
|
| 22 |
+
|
| 23 |
+
/local/
|
code/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
code/LICENSES/LICENSE_GEMMA.txt
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Gemma Terms of Use
|
| 2 |
+
|
| 3 |
+
Last modified: February 21, 2024
|
| 4 |
+
|
| 5 |
+
By using, reproducing, modifying, distributing, performing or displaying any portion or element of Gemma, Model Derivatives including via any Hosted Service, (each as defined below) (collectively, the "Gemma Services") or otherwise accepting the terms of this Agreement, you agree to be bound by this Agreement.
|
| 6 |
+
|
| 7 |
+
Section 1: DEFINITIONS
|
| 8 |
+
1.1 Definitions
|
| 9 |
+
(a) "Agreement" or "Gemma Terms of Use" means these terms and conditions that govern the use, reproduction, Distribution or modification of the Gemma Services and any terms and conditions incorporated by reference.
|
| 10 |
+
|
| 11 |
+
(b) "Distribution" or "Distribute" means any transmission, publication, or other sharing of Gemma or Model Derivatives to a third party, including by providing or making Gemma or its functionality available as a hosted service via API, web access, or any other electronic or remote means ("Hosted Service").
|
| 12 |
+
|
| 13 |
+
(c) "Gemma" means the set of machine learning language models, trained model weights and parameters identified at ai.google.dev/gemma, regardless of the source that you obtained it from.
|
| 14 |
+
|
| 15 |
+
(d) "Google" means Google LLC.
|
| 16 |
+
|
| 17 |
+
(e) "Model Derivatives" means all (i) modifications to Gemma, (ii) works based on Gemma, or (iii) any other machine learning model which is created by transfer of patterns of the weights, parameters, operations, or Output of Gemma, to that model in order to cause that model to perform similarly to Gemma, including distillation methods that use intermediate data representations or methods based on the generation of synthetic data Outputs by Gemma for training that model. For clarity, Outputs are not deemed Model Derivatives.
|
| 18 |
+
|
| 19 |
+
(f) "Output" means the information content output of Gemma or a Model Derivative that results from operating or otherwise using Gemma or the Model Derivative, including via a Hosted Service.
|
| 20 |
+
|
| 21 |
+
1.2
|
| 22 |
+
As used in this Agreement, "including" means "including without limitation".
|
| 23 |
+
|
| 24 |
+
Section 2: ELIGIBILITY AND USAGE
|
| 25 |
+
2.1 Eligibility
|
| 26 |
+
You represent and warrant that you have the legal capacity to enter into this Agreement (including being of sufficient age of consent). If you are accessing or using any of the Gemma Services for or on behalf of a legal entity, (a) you are entering into this Agreement on behalf of yourself and that legal entity, (b) you represent and warrant that you have the authority to act on behalf of and bind that entity to this Agreement and (c) references to "you" or "your" in the remainder of this Agreement refers to both you (as an individual) and that entity.
|
| 27 |
+
|
| 28 |
+
2.2 Use
|
| 29 |
+
You may use, reproduce, modify, Distribute, perform or display any of the Gemma Services only in accordance with the terms of this Agreement, and must not violate (or encourage or permit anyone else to violate) any term of this Agreement.
|
| 30 |
+
|
| 31 |
+
Section 3: DISTRIBUTION AND RESTRICTIONS
|
| 32 |
+
3.1 Distribution and Redistribution
|
| 33 |
+
You may reproduce or Distribute copies of Gemma or Model Derivatives if you meet all of the following conditions:
|
| 34 |
+
|
| 35 |
+
You must include the use restrictions referenced in Section 3.2 as an enforceable provision in any agreement (e.g., license agreement, terms of use, etc.) governing the use and/or distribution of Gemma or Model Derivatives and you must provide notice to subsequent users you Distribute to that Gemma or Model Derivatives are subject to the use restrictions in Section 3.2.
|
| 36 |
+
You must provide all third party recipients of Gemma or Model Derivatives a copy of this Agreement.
|
| 37 |
+
You must cause any modified files to carry prominent notices stating that you modified the files.
|
| 38 |
+
All Distributions (other than through a Hosted Service) must be accompanied by a "Notice" text file that contains the following notice: "Gemma is provided under and subject to the Gemma Terms of Use found at ai.google.dev/gemma/terms".
|
| 39 |
+
You may add your own intellectual property statement to your modifications and, except as set forth in this Section, may provide additional or different terms and conditions for use, reproduction, or Distribution of your modifications, or for any such Model Derivatives as a whole, provided your use, reproduction, modification, Distribution, performance, and display of Gemma otherwise complies with the terms and conditions of this Agreement. Any additional or different terms and conditions you impose must not conflict with the terms of this Agreement.
|
| 40 |
+
|
| 41 |
+
3.2 Use Restrictions
|
| 42 |
+
You must not use any of the Gemma Services:
|
| 43 |
+
|
| 44 |
+
for the restricted uses set forth in the Gemma Prohibited Use Policy at ai.google.dev/gemma/prohibited_use_policy ("Prohibited Use Policy"), which is hereby incorporated by reference into this Agreement; or
|
| 45 |
+
in violation of applicable laws and regulations.
|
| 46 |
+
To the maximum extent permitted by law, Google reserves the right to restrict (remotely or otherwise) usage of any of the Gemma Services that Google reasonably believes are in violation of this Agreement.
|
| 47 |
+
|
| 48 |
+
3.3 Generated Output
|
| 49 |
+
Google claims no rights in Outputs you generate using Gemma. You and your users are solely responsible for Outputs and their subsequent uses.
|
| 50 |
+
|
| 51 |
+
Section 4: ADDITIONAL PROVISIONS
|
| 52 |
+
4.1 Updates
|
| 53 |
+
Google may update Gemma from time to time, and you must make reasonable efforts to use the latest version of Gemma.
|
| 54 |
+
|
| 55 |
+
4.2 Trademarks
|
| 56 |
+
Nothing in this Agreement grants you any rights to use Google's trademarks, trade names, logos or to otherwise suggest endorsement or misrepresent the relationship between you and Google. Google reserves any rights not expressly granted herein.
|
| 57 |
+
|
| 58 |
+
4.3 DISCLAIMER OF WARRANTY
|
| 59 |
+
UNLESS REQUIRED BY APPLICABLE LAW, THE GEMMA SERVICES, AND OUTPUTS, ARE PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING, REPRODUCING, MODIFYING, PERFORMING, DISPLAYING OR OR DISTRIBUTING ANY OF THE GEMMA SERVICES OR OUTPUTS AND ASSUME ANY AND ALL RISKS ASSOCIATED WITH YOUR USE OR DISTRIBUTION OF ANY OF THE GEMMA SERVICES OR OUTPUTS AND YOUR EXERCISE OF RIGHTS AND PERMISSIONS UNDER THIS AGREEMENT.
|
| 60 |
+
|
| 61 |
+
4.4 LIMITATION OF LIABILITY
|
| 62 |
+
TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY, CONTRACT, OR OTHERWISE, UNLESS REQUIRED BY APPLICABLE LAW, SHALL GOOGLE OR ITS AFFILIATES BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY, CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR LOST PROFITS OF ANY KIND ARISING FROM THIS AGREEMENT OR RELATED TO, ANY OF THE GEMMA SERVICES OR OUTPUTS EVEN IF GOOGLE OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
| 63 |
+
|
| 64 |
+
4.5 Term, Termination, and Survival
|
| 65 |
+
The term of this Agreement will commence upon your acceptance of this Agreement (including acceptance by your use, modification, or Distribution, reproduction, performance or display of any portion or element of the Gemma Services) and will continue in full force and effect until terminated in accordance with the terms of this Agreement. Google may terminate this Agreement if you are in breach of any term of this Agreement. Upon termination of this Agreement, you must delete and cease use and Distribution of all copies of Gemma and Model Derivatives in your possession or control. Sections 1, 2.1, 3.3, 4.2 to 4.9 shall survive the termination of this Agreement.
|
| 66 |
+
|
| 67 |
+
4.6 Governing Law and Jurisdiction
|
| 68 |
+
This Agreement will be governed by the laws of the State of California without regard to choice of law principles. The UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement. The state and federal courts of Santa Clara County, California shall have exclusive jurisdiction of any dispute arising out of this Agreement.
|
| 69 |
+
|
| 70 |
+
4.7 Severability
|
| 71 |
+
If any provision of this Agreement is held to be invalid, illegal or unenforceable, the remaining provisions shall be unaffected thereby and remain valid as if such provision had not been set forth herein.
|
| 72 |
+
|
| 73 |
+
4.8 Entire Agreement
|
| 74 |
+
This Agreement states all the terms agreed between the parties and supersedes all other agreements between the parties as of the date of acceptance relating to its subject matter.
|
| 75 |
+
|
| 76 |
+
4.9 No Waiver
|
| 77 |
+
Google will not be treated as having waived any rights by not exercising (or delaying the exercise of) any rights under this Agreement.
|
code/NOTICE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TaskAnchor RoboMemArena π0.5 release
|
| 2 |
+
|
| 3 |
+
Source code in this package is distributed under Apache License 2.0, except
|
| 4 |
+
where an individual file or upstream component states otherwise. Retain the
|
| 5 |
+
existing copyright and license notices when redistributing it.
|
| 6 |
+
|
| 7 |
+
This distribution includes modified OpenPI code:
|
| 8 |
+
https://github.com/Physical-Intelligence/openpi
|
| 9 |
+
Modifications add TaskAnchor, full-task history sampling, progress
|
| 10 |
+
conditioning, RoboMemArena training/evaluation, and portable file locations.
|
| 11 |
+
The original model computations in the selected release profile are retained.
|
| 12 |
+
|
| 13 |
+
The accompanying full model includes pretrained π0.5/PaliGemma-derived
|
| 14 |
+
components. The Apache source license does not override terms that apply
|
| 15 |
+
to the underlying pretrained model. The included Gemma terms continue to
|
| 16 |
+
apply to Gemma-derived material.
|
| 17 |
+
|
| 18 |
+
Gemma is provided under and subject to the Gemma Terms of Use found at ai.google.dev/gemma/terms
|
| 19 |
+
|
| 20 |
+
The official RoboMemArena simulator, assets, and training dataset are not
|
| 21 |
+
redistributed in this source package; obtain them from their upstream
|
| 22 |
+
locations and retain their respective terms.
|
code/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# TaskAnchor for RoboMemArena (π0.5, 50k)
|
| 2 |
+
|
| 3 |
+
Training and evaluation source for the released **TaskAnchor–π0.5 50,000-step checkpoint** on RoboMemArena. The supported public profile is `robomemarena/pi05`. This package contains the native OpenPI backend, the TaskAnchor adapter, the benchmark interface, normalization assets, and evaluation tools.
|
| 4 |
+
|
| 5 |
+
**Weights:** [henry13522/Taskanchor](https://huggingface.co/henry13522/Taskanchor). The complete source is also included there under `code/`; no separate GitHub repository is required to use this release.
|
| 6 |
+
|
| 7 |
+
## Architecture and checkpoint contract
|
| 8 |
+
|
| 9 |
+
The frozen SigLIP encoder produces 1152-dimensional tokens. TaskAnchor projects the main-camera tokens to a 512-dimensional latent space, applies the eight-layer, eight-head temporal adapter, and restores the visual residual to 1152 dimensions. The writeback is **`refined = current + offset`**, with no additional scalar gate. Adapter dropout is 0.10; the MLP ratio is 4. The native π0.5 backbone and action decoder remain present in the full checkpoint.
|
| 10 |
+
|
| 11 |
+
The main camera and wrist camera are real inputs; the third native image slot is masked padding. Only the main camera receives the history-conditioned visual residual. Images are resized to 224 × 224. Each cached history frame contains 4 × 4 spatial tokens, bilinearly pooled from the native 16 × 16 SigLIP grid. The recorded protocol retains **a separate permanent frame0 plus up to 191 history frames**, sampled with stride 10. This is the 50k checkpoint's protocol, not the FIFO-only configuration used in some other experiments.
|
| 12 |
+
|
| 13 |
+
The task-state head predicts a continuous scalar. Training targets are `round(subtask_id / num_subtasks, 2)`. At evaluation, the prediction is mapped to the nearest task-specific coordinate (for four stages: 0.00, 0.25, 0.50, 0.75) before conditioning the policy. These are predicted stages, not oracle labels. The model predicts 20 actions and executes 10 before replanning.
|
| 14 |
+
|
| 15 |
+
There are **26,930,049 adapter parameters** and **3,380,363,921 parameters in the complete checkpoint**. The full tensor inventory and payload hashes are provided with the weights.
|
| 16 |
+
|
| 17 |
+
## Download and run
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
hf download henry13522/Taskanchor --local-dir /path/to/Taskanchor
|
| 21 |
+
cd /path/to/Taskanchor/code
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
Follow [REPRODUCING.md](docs/REPRODUCING.md) to install the model and simulator environments, pin the official benchmark, and run extraction, training, serving, or evaluation. [PORTABILITY.md](docs/PORTABILITY.md) lists the supported path overrides.
|
| 25 |
+
|
| 26 |
+
After environment and benchmark setup, the evaluation entry point is shown below. The evaluator retains the original trial and seed schedule; a study requiring a different schedule must configure and record its evaluation protocol before collecting results.
|
| 27 |
+
|
| 28 |
+
```bash
|
| 29 |
+
export TASKANCHOR_TOKENIZER_PATH=/path/to/Taskanchor/tokenizer/paligemma_tokenizer.model
|
| 30 |
+
export TASKANCHOR_PI05_PYTHON=/path/to/Taskanchor/code/pi05/.venv/bin/python
|
| 31 |
+
export TASKANCHOR_ROBOMEM_EVAL_PYTHON=/path/to/simulator-env/bin/python
|
| 32 |
+
export TASKANCHOR_LIBERO_CONFIG=/path/to/robomem_libero_config
|
| 33 |
+
|
| 34 |
+
python -m taskanchor --benchmark robomemarena --model pi05 --stage eval \
|
| 35 |
+
--benchmark-root /path/to/RoboMemArena/evaluation_benchmark \
|
| 36 |
+
--python "$TASKANCHOR_PI05_PYTHON" -- \
|
| 37 |
+
--checkpoint /path/to/Taskanchor/50000 \
|
| 38 |
+
--output /path/to/reports/taskanchor_50k \
|
| 39 |
+
--workers 8 --base-port 8800
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
Keep `recipe.json` one level above `50000/`. Inference also needs `50000/assets/` and the supplied tokenizer. No training data, feature cache, base-model weight download, or optimizer state is required to load the complete released checkpoint.
|
| 43 |
+
|
| 44 |
+
## Training settings
|
| 45 |
+
|
| 46 |
+
The recorded run was configured for **55,000 total optimizer steps**, with 5,000 adapter-only steps followed by 50,000 joint steps. The **released 50k snapshot contains 5k adapter-only + 45k joint steps**. The visual encoder stays frozen throughout; the remaining backbone parameters participate after the warm-up. This is one training run with a freeze schedule, not the older four-stage pipeline.
|
| 47 |
+
|
| 48 |
+
| Setting | Recorded value |
|
| 49 |
+
| --- | --- |
|
| 50 |
+
| Initialization | OpenPI `pi05_base` |
|
| 51 |
+
| Global batch / devices | 256 / 8 GPUs, FSDP size 8 |
|
| 52 |
+
| Optimizer | AdamW, β=(0.9, 0.95), gradient clipping 1.0 |
|
| 53 |
+
| Adapter learning rate / weight decay | 2.5e-5 / 0.05 |
|
| 54 |
+
| Backbone learning rate / weight decay | 2.5e-5 / 0 |
|
| 55 |
+
| Image projection learning rate | 1e-5 |
|
| 56 |
+
| Schedule | Adapter constant for the first 5k; cosine decay to 10% over the 50k joint phase |
|
| 57 |
+
| Loss | `flow + 0.01 * (100 * MSE + 1 - Pearson)` |
|
| 58 |
+
| Action prediction / execution | 20 / 10 |
|
| 59 |
+
| History | stride 10, 191 frames + separate frame0, 16 tokens/frame |
|
| 60 |
+
| Dataset | 2,600 full-task trajectories, 2,811,755 frames |
|
| 61 |
+
| Seed / workers | 42 / 8 |
|
| 62 |
+
|
| 63 |
+
The recorded run resumed at 10k after a data-loading change. Its initial recipe and saved checkpoint metadata retain this provenance; they are not claimed to be identical restart states. See `evidence/pipeline_restart_10000.json`. Training from scratch uses the same numerical settings, but exact retraining reproducibility is not asserted.
|
| 64 |
+
|
| 65 |
+
## Source layout
|
| 66 |
+
|
| 67 |
+
| Path | Responsibility |
|
| 68 |
+
| --- | --- |
|
| 69 |
+
| `taskanchor/pi05/taskanchor.py` | Adapter, task-state head, native π0.5 integration |
|
| 70 |
+
| `taskanchor/pi05/ttt.py` | History-dependent temporary fast-weight computation |
|
| 71 |
+
| `taskanchor/pi05/training.py` | Parameter groups, freeze schedule, optimization |
|
| 72 |
+
| `taskanchor/data/` | Full-task indexing and historical token selection |
|
| 73 |
+
| `taskanchor/__main__.py` | Native extraction/train/serve/evaluation launcher |
|
| 74 |
+
| `taskanchor/paths.py` | Portable asset and data locations |
|
| 75 |
+
| `pi05/src/openpi/` | Native model, transforms, checkpoint and serving utilities |
|
| 76 |
+
| `pi05/scripts/*robomem*` | Feature extraction, training, inference, official evaluation bridge |
|
| 77 |
+
| `pi05/packages/openpi-client/` | Policy client/server protocol support |
|
| 78 |
+
| `tools/` | Benchmark setup and result comparison |
|
| 79 |
+
| `assets/pi05_libero/` | The original normalization statistics |
|
| 80 |
+
| `evidence/` | Configuration provenance and environment records |
|
| 81 |
+
|
| 82 |
+
Some native OpenPI compatibility modules retain historical configuration names because they are imported by the backend. Only the RoboMemArena π0.5 entry points documented here are supported in this release.
|
| 83 |
+
|
| 84 |
+
## Verification and scope
|
| 85 |
+
|
| 86 |
+
During packaging, the complete checkpoint was restored on CPU: all 176 tensors are finite, and copied parameter/tokenizer/normalization files match the originals by SHA-256. Core adapter, TTT, history, and training implementations are retained without numerical changes. Packaging checks do not substitute for closed-loop benchmark evaluation. This repository does not publish a benchmark score. The packaged checkpoint omits optimizer state and therefore cannot be used as an exact training resume checkpoint.
|
| 87 |
+
|
| 88 |
+
```bash
|
| 89 |
+
python -m unittest discover -s tests -p test_portability.py
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
## License and attribution
|
| 93 |
+
|
| 94 |
+
The code is distributed under [Apache-2.0](LICENSE), retaining upstream notices. The full checkpoint includes pretrained π0.5/PaliGemma-derived components and remains subject to upstream terms, including [Gemma Terms of Use](LICENSES/LICENSE_GEMMA.txt); Apache-2.0 for this source does not replace those weight terms. See [NOTICE](NOTICE).
|
| 95 |
+
|
| 96 |
+
This work builds on [OpenPI](https://github.com/Physical-Intelligence/openpi) and [RoboMemArena](https://github.com/OpenHelix-Team/RoboMemArena). The benchmark, its assets, and the training dataset are obtained separately under their respective terms. Public model weights and source: [henry13522/Taskanchor](https://huggingface.co/henry13522/Taskanchor).
|
code/SOURCE_MANIFEST.json
ADDED
|
@@ -0,0 +1,654 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"files": [
|
| 3 |
+
{
|
| 4 |
+
"path": ".gitignore",
|
| 5 |
+
"size_bytes": 213,
|
| 6 |
+
"sha256": "281dcbe819391034b619056c0d6ae4fac7b69ba751600f6864d6ae035d54e967"
|
| 7 |
+
},
|
| 8 |
+
{
|
| 9 |
+
"path": "LICENSE",
|
| 10 |
+
"size_bytes": 11356,
|
| 11 |
+
"sha256": "43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1"
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"path": "LICENSES/LICENSE_GEMMA.txt",
|
| 15 |
+
"size_bytes": 8431,
|
| 16 |
+
"sha256": "3e2c24001f9ef57bf7ec959a3658fbb49cdad113cdf394c264da9d16f9bdd132"
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"path": "NOTICE",
|
| 20 |
+
"size_bytes": 1114,
|
| 21 |
+
"sha256": "38e5a788a2ca6eb7d7fca20c19cbfdc12cc22cb54df32ecd5d82587dcce6b7c9"
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"path": "README.md",
|
| 25 |
+
"size_bytes": 7644,
|
| 26 |
+
"sha256": "2224078f9713aedf4e746b16f538d3cb1882a53911d4f7d7d774a7a30e0a2ace"
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"path": "assets/pi05_libero/norm_stats.json",
|
| 30 |
+
"size_bytes": 1874,
|
| 31 |
+
"sha256": "0c0d329a3345d2ea2e1348dac0edf4ae175085fac200cca1cab967aae1ae1767"
|
| 32 |
+
},
|
| 33 |
+
{
|
| 34 |
+
"path": "docs/PORTABILITY.md",
|
| 35 |
+
"size_bytes": 5477,
|
| 36 |
+
"sha256": "b9b39576f3d23c1edfd42f28f8d4c00a0e803e771b4856bd6bdf4cc7922765e2"
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"path": "docs/PUBLISHING_ZH.md",
|
| 40 |
+
"size_bytes": 2346,
|
| 41 |
+
"sha256": "72579bf3257fb1ae7b6e72d58e3fb3e31584fb73ad23da93bfc31770f2279eb9"
|
| 42 |
+
},
|
| 43 |
+
{
|
| 44 |
+
"path": "docs/REPRODUCING.md",
|
| 45 |
+
"size_bytes": 12827,
|
| 46 |
+
"sha256": "7630f8f14f2d232ff49509fe54bca7dc33d20ed1fab3bcc22b7d0fca556692d2"
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"path": "evidence/environment_pi05.json",
|
| 50 |
+
"size_bytes": 393,
|
| 51 |
+
"sha256": "463b318c2fcc9b4374bdf177e55211a134fad79be7fa4f8818d69237208a6a47"
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
"path": "evidence/packaging_checks.json",
|
| 55 |
+
"size_bytes": 465,
|
| 56 |
+
"sha256": "cdd294d1e4c23b38f42e37e2f15ca0f43d7600b7e6e76d4feebde61fa0f11de0"
|
| 57 |
+
},
|
| 58 |
+
{
|
| 59 |
+
"path": "evidence/pipeline_restart_10000.json",
|
| 60 |
+
"size_bytes": 1027,
|
| 61 |
+
"sha256": "55576107d7239b76ad608ea21a43307bdb72886b951ec2a214e006218a554984"
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
"path": "evidence/robomem50k_adapter_numerical_parity.json",
|
| 65 |
+
"size_bytes": 628,
|
| 66 |
+
"sha256": "d0067e72d6c768996c64d4ed66eb1d9351cdf94a232ce3985b8314fb5c76c14a"
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"path": "evidence/task25_seed_candidates.json",
|
| 70 |
+
"size_bytes": 1930,
|
| 71 |
+
"sha256": "2c2c859aa60c0c58e04e9f8c7e7ec54cbb3a23a34d463d8432ab10080e7f4444"
|
| 72 |
+
},
|
| 73 |
+
{
|
| 74 |
+
"path": "pi05/LICENSE",
|
| 75 |
+
"size_bytes": 11356,
|
| 76 |
+
"sha256": "43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1"
|
| 77 |
+
},
|
| 78 |
+
{
|
| 79 |
+
"path": "pi05/LICENSE_GEMMA.txt",
|
| 80 |
+
"size_bytes": 8431,
|
| 81 |
+
"sha256": "3e2c24001f9ef57bf7ec959a3658fbb49cdad113cdf394c264da9d16f9bdd132"
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"path": "pi05/README.md",
|
| 85 |
+
"size_bytes": 23254,
|
| 86 |
+
"sha256": "567fc57766bd736664f50126cab8145af4071ca16cc5e238f2b11e369169c3b5"
|
| 87 |
+
},
|
| 88 |
+
{
|
| 89 |
+
"path": "pi05/packages/openpi-client/pyproject.toml",
|
| 90 |
+
"size_bytes": 388,
|
| 91 |
+
"sha256": "cacd55394787338bef9d6e7c2e4ecaefd0bc90b3362ac48fe20d9a7f201147d6"
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/__init__.py",
|
| 95 |
+
"size_bytes": 22,
|
| 96 |
+
"sha256": "91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed"
|
| 97 |
+
},
|
| 98 |
+
{
|
| 99 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/action_chunk_broker.py",
|
| 100 |
+
"size_bytes": 1404,
|
| 101 |
+
"sha256": "019b9d69cb26cc681331a2f95dbe60a2cc7b02d6d309c5d15b6457ab39798234"
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/base_policy.py",
|
| 105 |
+
"size_bytes": 271,
|
| 106 |
+
"sha256": "88e98e0f06293f3c4767db675061664c4bba12f87242ba1e64377d1b8de8069e"
|
| 107 |
+
},
|
| 108 |
+
{
|
| 109 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/image_tools.py",
|
| 110 |
+
"size_bytes": 2426,
|
| 111 |
+
"sha256": "d48b4bd7f44e79fe6db8a8e07c9161144fa250be686e1245014a8b47e6171977"
|
| 112 |
+
},
|
| 113 |
+
{
|
| 114 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/image_tools_test.py",
|
| 115 |
+
"size_bytes": 1411,
|
| 116 |
+
"sha256": "4df4b0ba0b95ba5e47160039c3e837a64d50c802b419a588211dffbaa732589c"
|
| 117 |
+
},
|
| 118 |
+
{
|
| 119 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/msgpack_numpy.py",
|
| 120 |
+
"size_bytes": 1944,
|
| 121 |
+
"sha256": "c04568948fcee52b691e3be4b6cffb759f7e79ad67530fcd5d23095a0d13c057"
|
| 122 |
+
},
|
| 123 |
+
{
|
| 124 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/msgpack_numpy_test.py",
|
| 125 |
+
"size_bytes": 1643,
|
| 126 |
+
"sha256": "2c6dd236720dae716d7bdf842b7d506a165e9563bf9d4376c95289269adcb973"
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/runtime/agent.py",
|
| 130 |
+
"size_bytes": 465,
|
| 131 |
+
"sha256": "b559885afd9645a91b9b6ee12a28513295474f987d11b16610fc0479dbe6de5b"
|
| 132 |
+
},
|
| 133 |
+
{
|
| 134 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/runtime/agents/policy_agent.py",
|
| 135 |
+
"size_bytes": 509,
|
| 136 |
+
"sha256": "c4d63952d1954bd4b72a3122223df36794812fea74cf1c7023433b5a90473c1c"
|
| 137 |
+
},
|
| 138 |
+
{
|
| 139 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/runtime/environment.py",
|
| 140 |
+
"size_bytes": 1044,
|
| 141 |
+
"sha256": "1c12e5054b3401dec5f1f8b71eef0f625cb60e2b50784dcf19afa32c77107a7e"
|
| 142 |
+
},
|
| 143 |
+
{
|
| 144 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/runtime/runtime.py",
|
| 145 |
+
"size_bytes": 3049,
|
| 146 |
+
"sha256": "3daa242a9ce61ffb210789ef3ba6b1e3cade9de757fcd36af7b174c530345ea8"
|
| 147 |
+
},
|
| 148 |
+
{
|
| 149 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/runtime/subscriber.py",
|
| 150 |
+
"size_bytes": 500,
|
| 151 |
+
"sha256": "d269a2af8ec4056beb310e5c3c97d4468af01549c7ef80dd905652d8ac64f85b"
|
| 152 |
+
},
|
| 153 |
+
{
|
| 154 |
+
"path": "pi05/packages/openpi-client/src/openpi_client/websocket_client_policy.py",
|
| 155 |
+
"size_bytes": 2131,
|
| 156 |
+
"sha256": "36557cb0b91ccf31cd4fb4b508306850d76ed0feb4028dac5182d0f5a5d88005"
|
| 157 |
+
},
|
| 158 |
+
{
|
| 159 |
+
"path": "pi05/pyproject.toml",
|
| 160 |
+
"size_bytes": 3110,
|
| 161 |
+
"sha256": "e1f457bd81622a47a4c4de17b92815977ac5fcd40116bec92398eb400bf76483"
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"path": "pi05/scripts/__init__.py",
|
| 165 |
+
"size_bytes": 0,
|
| 166 |
+
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
| 167 |
+
},
|
| 168 |
+
{
|
| 169 |
+
"path": "pi05/scripts/check_robomem_memory_graph.py",
|
| 170 |
+
"size_bytes": 2521,
|
| 171 |
+
"sha256": "1100021fb2ae25cc85f3e1c0da2cd95fa78086da0a673fd606be8652adcab752"
|
| 172 |
+
},
|
| 173 |
+
{
|
| 174 |
+
"path": "pi05/scripts/evaluate_robomem_memory.py",
|
| 175 |
+
"size_bytes": 13126,
|
| 176 |
+
"sha256": "47fe434454e7db74d834016621f6ae61d12097ad270dd7fb155567604b8ab53d"
|
| 177 |
+
},
|
| 178 |
+
{
|
| 179 |
+
"path": "pi05/scripts/extract_robomem_memory.py",
|
| 180 |
+
"size_bytes": 7229,
|
| 181 |
+
"sha256": "4bf8a66484188ee321ee75b605e80cbfc55b9785c3332cf36dce15c45c186342"
|
| 182 |
+
},
|
| 183 |
+
{
|
| 184 |
+
"path": "pi05/scripts/robomem_eval_paths.py",
|
| 185 |
+
"size_bytes": 843,
|
| 186 |
+
"sha256": "f82cda8a7fecbe3def79e5c013cab15083d19e53634b80b41f984534c25d487e"
|
| 187 |
+
},
|
| 188 |
+
{
|
| 189 |
+
"path": "pi05/scripts/robomem_memory_eval_adapter.py",
|
| 190 |
+
"size_bytes": 4093,
|
| 191 |
+
"sha256": "771cc4492686bce2d81d6f320301df3dcd619e3e9eba8f61d70800f9274e84ce"
|
| 192 |
+
},
|
| 193 |
+
{
|
| 194 |
+
"path": "pi05/scripts/robomem_seed_search.py",
|
| 195 |
+
"size_bytes": 2742,
|
| 196 |
+
"sha256": "c48b3bfc0b30fc1ed7eb986925c42f4821fe52a856d9102a3181fd3e9dd6b2be"
|
| 197 |
+
},
|
| 198 |
+
{
|
| 199 |
+
"path": "pi05/scripts/run_robomem_eval_task.py",
|
| 200 |
+
"size_bytes": 4547,
|
| 201 |
+
"sha256": "18c698ec0bda3510816390778d1412a96a90f7d2bf098ee8224033a9ea1708f3"
|
| 202 |
+
},
|
| 203 |
+
{
|
| 204 |
+
"path": "pi05/scripts/run_robomem_task25_seed.py",
|
| 205 |
+
"size_bytes": 1225,
|
| 206 |
+
"sha256": "ca32a0bdb52376e0937bced4f87f41b87f18e5fb3f75cd6c975bc71dde166321"
|
| 207 |
+
},
|
| 208 |
+
{
|
| 209 |
+
"path": "pi05/scripts/serve_robomem_memory.py",
|
| 210 |
+
"size_bytes": 702,
|
| 211 |
+
"sha256": "e560e83a556a16b620c854140fb2a736ca1c003d8031a531801a71ef507af86e"
|
| 212 |
+
},
|
| 213 |
+
{
|
| 214 |
+
"path": "pi05/scripts/test_robomem_discrete_progress.py",
|
| 215 |
+
"size_bytes": 2915,
|
| 216 |
+
"sha256": "1f9e66c70775fac7933b7de3bc15f33aa434532fdad79165269f311c077b29a5"
|
| 217 |
+
},
|
| 218 |
+
{
|
| 219 |
+
"path": "pi05/scripts/test_robomem_memory.py",
|
| 220 |
+
"size_bytes": 8216,
|
| 221 |
+
"sha256": "e1b71aee89412650a99b4489961450c36d188ce654db182ce2605befcad8c893"
|
| 222 |
+
},
|
| 223 |
+
{
|
| 224 |
+
"path": "pi05/scripts/test_robomem_memory_inference.py",
|
| 225 |
+
"size_bytes": 2430,
|
| 226 |
+
"sha256": "d26aadc408ef8300a879c86cf1fe3fc32df7de3d56f1ff19c3ee7803a9d2ca57"
|
| 227 |
+
},
|
| 228 |
+
{
|
| 229 |
+
"path": "pi05/scripts/test_robomem_memory_loader.py",
|
| 230 |
+
"size_bytes": 3000,
|
| 231 |
+
"sha256": "80f421f909c0d3373171dccd013c46fbf8b2b9a67597b81a6198e6163cdd2ba7"
|
| 232 |
+
},
|
| 233 |
+
{
|
| 234 |
+
"path": "pi05/scripts/train_robomem_memory.py",
|
| 235 |
+
"size_bytes": 8168,
|
| 236 |
+
"sha256": "6e7d4a8ed7b6686437ed85101a0b90518fe555fd5dadb2d8d8d681d5beb25fb0"
|
| 237 |
+
},
|
| 238 |
+
{
|
| 239 |
+
"path": "pi05/src/openpi/__init__.py",
|
| 240 |
+
"size_bytes": 0,
|
| 241 |
+
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
| 242 |
+
},
|
| 243 |
+
{
|
| 244 |
+
"path": "pi05/src/openpi/conftest.py",
|
| 245 |
+
"size_bytes": 339,
|
| 246 |
+
"sha256": "626672e385fb276396c119e2220421065c58f8ec602401e8bf2ae509f9fffaf3"
|
| 247 |
+
},
|
| 248 |
+
{
|
| 249 |
+
"path": "pi05/src/openpi/models/__init__.py",
|
| 250 |
+
"size_bytes": 0,
|
| 251 |
+
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
| 252 |
+
},
|
| 253 |
+
{
|
| 254 |
+
"path": "pi05/src/openpi/models/gemma.py",
|
| 255 |
+
"size_bytes": 17146,
|
| 256 |
+
"sha256": "7e42ada4ae7e9995f0ef3e33a0c224758323c2015e2d9d72ec15726a8a001d2a"
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"path": "pi05/src/openpi/models/gemma_fast.py",
|
| 260 |
+
"size_bytes": 15643,
|
| 261 |
+
"sha256": "563c6e1ffd8afa31c406bcb8894a605e87df88f6c075d398d97a57af6bc3aefa"
|
| 262 |
+
},
|
| 263 |
+
{
|
| 264 |
+
"path": "pi05/src/openpi/models/lora.py",
|
| 265 |
+
"size_bytes": 5342,
|
| 266 |
+
"sha256": "ced28716950c34d84a1a6d0b03b1d9550000732721df884818080b6e78cb3289"
|
| 267 |
+
},
|
| 268 |
+
{
|
| 269 |
+
"path": "pi05/src/openpi/models/lora_test.py",
|
| 270 |
+
"size_bytes": 3138,
|
| 271 |
+
"sha256": "a0f10525de7773310402b3ffa636e88ffff7035f3179b140aacc6809c122f085"
|
| 272 |
+
},
|
| 273 |
+
{
|
| 274 |
+
"path": "pi05/src/openpi/models/model.py",
|
| 275 |
+
"size_bytes": 15120,
|
| 276 |
+
"sha256": "386dd172e1f561ba31d54a0ec3eb92921a51f58e4ee520b12789c9b8e3a62d07"
|
| 277 |
+
},
|
| 278 |
+
{
|
| 279 |
+
"path": "pi05/src/openpi/models/model_test.py",
|
| 280 |
+
"size_bytes": 2966,
|
| 281 |
+
"sha256": "f8e71f195045d4e3ef1ddfafb2337fa52e5aa2f89bce1fde62f5cbfb67121f21"
|
| 282 |
+
},
|
| 283 |
+
{
|
| 284 |
+
"path": "pi05/src/openpi/models/pi0.py",
|
| 285 |
+
"size_bytes": 14318,
|
| 286 |
+
"sha256": "00df716074704c22ab4fccd88146bf73ed6ef7edcb4c9e6a9f92486ad030c89c"
|
| 287 |
+
},
|
| 288 |
+
{
|
| 289 |
+
"path": "pi05/src/openpi/models/pi0_config.py",
|
| 290 |
+
"size_bytes": 4435,
|
| 291 |
+
"sha256": "d321858c30b398126035ea2b6da09e1c422eeeebb5ebf8df631daf9dc77647fa"
|
| 292 |
+
},
|
| 293 |
+
{
|
| 294 |
+
"path": "pi05/src/openpi/models/pi0_fast.py",
|
| 295 |
+
"size_bytes": 13209,
|
| 296 |
+
"sha256": "627332a555046f2744ef144c45f8bd3361a9820cf68854a4528d0fdbba7e9aa3"
|
| 297 |
+
},
|
| 298 |
+
{
|
| 299 |
+
"path": "pi05/src/openpi/models/pi0_test.py",
|
| 300 |
+
"size_bytes": 1631,
|
| 301 |
+
"sha256": "5082873014a7fc45fb8414f517909e1c24cdafea980a1fb612e0aea6058ed0fe"
|
| 302 |
+
},
|
| 303 |
+
{
|
| 304 |
+
"path": "pi05/src/openpi/models/robomem_memory.py",
|
| 305 |
+
"size_bytes": 189,
|
| 306 |
+
"sha256": "cff45c0afe162f062ca54dfb52078483e64cb04db268746f5ea570e5268e1ca5"
|
| 307 |
+
},
|
| 308 |
+
{
|
| 309 |
+
"path": "pi05/src/openpi/models/siglip.py",
|
| 310 |
+
"size_bytes": 12471,
|
| 311 |
+
"sha256": "f129a7e1d5b8bf101d73f27ccf9b0005167c3e208ac934ffcdc36a886662d36f"
|
| 312 |
+
},
|
| 313 |
+
{
|
| 314 |
+
"path": "pi05/src/openpi/models/tokenizer.py",
|
| 315 |
+
"size_bytes": 17558,
|
| 316 |
+
"sha256": "0d604dfde16c3d00fe056ff7559efd4e5d95a042f4fe09e1c2ec7e77dfc47b0a"
|
| 317 |
+
},
|
| 318 |
+
{
|
| 319 |
+
"path": "pi05/src/openpi/models/tokenizer_test.py",
|
| 320 |
+
"size_bytes": 807,
|
| 321 |
+
"sha256": "5abd179ce883420e39d4d18b33d0123290af752da243003aa48a3bc06dbb9517"
|
| 322 |
+
},
|
| 323 |
+
{
|
| 324 |
+
"path": "pi05/src/openpi/models/utils/fsq_tokenizer.py",
|
| 325 |
+
"size_bytes": 15928,
|
| 326 |
+
"sha256": "88866638ee4775f46a222561a957bd35e20600fe38c5888b587016ad77b1065f"
|
| 327 |
+
},
|
| 328 |
+
{
|
| 329 |
+
"path": "pi05/src/openpi/models/vit.py",
|
| 330 |
+
"size_bytes": 10266,
|
| 331 |
+
"sha256": "d285d057636c3cba2d1fd3b37693b18075ad4b9c3b47bbde807c34c6c376ac93"
|
| 332 |
+
},
|
| 333 |
+
{
|
| 334 |
+
"path": "pi05/src/openpi/models_pytorch/gemma_pytorch.py",
|
| 335 |
+
"size_bytes": 13480,
|
| 336 |
+
"sha256": "225092e06509da0ae37bb9dca55c00d2804255bb1496f7c8700304b4c52a95eb"
|
| 337 |
+
},
|
| 338 |
+
{
|
| 339 |
+
"path": "pi05/src/openpi/models_pytorch/pi0_pytorch.py",
|
| 340 |
+
"size_bytes": 22558,
|
| 341 |
+
"sha256": "97dd290d699bb63719627e3933a414500b35ded4ef24c88faa0685f0acdc9e75"
|
| 342 |
+
},
|
| 343 |
+
{
|
| 344 |
+
"path": "pi05/src/openpi/models_pytorch/preprocessing_pytorch.py",
|
| 345 |
+
"size_bytes": 8098,
|
| 346 |
+
"sha256": "f8fb26173ecfe83b7f7832f8d6ca4104d377af08ebb7352dc68ad51b63a0eb43"
|
| 347 |
+
},
|
| 348 |
+
{
|
| 349 |
+
"path": "pi05/src/openpi/models_pytorch/transformers_replace/models/gemma/configuration_gemma.py",
|
| 350 |
+
"size_bytes": 8958,
|
| 351 |
+
"sha256": "26fd0d0f52730ab32b6f337eb73ae9a26cb71ae0915bc49eced1d0e2ed8d0ec6"
|
| 352 |
+
},
|
| 353 |
+
{
|
| 354 |
+
"path": "pi05/src/openpi/models_pytorch/transformers_replace/models/gemma/modeling_gemma.py",
|
| 355 |
+
"size_bytes": 36392,
|
| 356 |
+
"sha256": "17eeb54e277939e58cd6f8a92719a275e36baf0c9f463b2754227d42f87aedde"
|
| 357 |
+
},
|
| 358 |
+
{
|
| 359 |
+
"path": "pi05/src/openpi/models_pytorch/transformers_replace/models/paligemma/modeling_paligemma.py",
|
| 360 |
+
"size_bytes": 27688,
|
| 361 |
+
"sha256": "c945d330b829264f927f3ee5339f977238631772de07a28e855e265bb85fd53c"
|
| 362 |
+
},
|
| 363 |
+
{
|
| 364 |
+
"path": "pi05/src/openpi/models_pytorch/transformers_replace/models/siglip/check.py",
|
| 365 |
+
"size_bytes": 133,
|
| 366 |
+
"sha256": "466e8eb7887ac5ccc98118a3169298d6ab65a284d834bae74751e3854dbc4245"
|
| 367 |
+
},
|
| 368 |
+
{
|
| 369 |
+
"path": "pi05/src/openpi/models_pytorch/transformers_replace/models/siglip/modeling_siglip.py",
|
| 370 |
+
"size_bytes": 50231,
|
| 371 |
+
"sha256": "ef2e99500f263fdd78db9d4d9a255e29d844eb92f53c25ad640f8056700ef84b"
|
| 372 |
+
},
|
| 373 |
+
{
|
| 374 |
+
"path": "pi05/src/openpi/policies/aloha_policy.py",
|
| 375 |
+
"size_bytes": 7543,
|
| 376 |
+
"sha256": "50d7ad972d80142ecb0f03b0780dacfd3af212946f4c9aac31a204b02db4543d"
|
| 377 |
+
},
|
| 378 |
+
{
|
| 379 |
+
"path": "pi05/src/openpi/policies/droid_policy.py",
|
| 380 |
+
"size_bytes": 3186,
|
| 381 |
+
"sha256": "7d0893555964d485e231f70084125084a99495705ddd74b29461229d524fff26"
|
| 382 |
+
},
|
| 383 |
+
{
|
| 384 |
+
"path": "pi05/src/openpi/policies/libero_policy.py",
|
| 385 |
+
"size_bytes": 3461,
|
| 386 |
+
"sha256": "8606b38a3efeb8b97c1af18b33a873eb3c63f5359a661c9b4319cdb87fb66276"
|
| 387 |
+
},
|
| 388 |
+
{
|
| 389 |
+
"path": "pi05/src/openpi/policies/rmbench_memory_inference.py",
|
| 390 |
+
"size_bytes": 4643,
|
| 391 |
+
"sha256": "9335930d0da0efe1c64b6c06a58820c7f7eb522b21933a2cc4895508b5f65ff0"
|
| 392 |
+
},
|
| 393 |
+
{
|
| 394 |
+
"path": "pi05/src/openpi/policies/robomem_memory_inference.py",
|
| 395 |
+
"size_bytes": 12111,
|
| 396 |
+
"sha256": "3ff7d6ac0a2e9a54b011ecf557c4d2e4810c2ab144781b6ce95b1093103a02de"
|
| 397 |
+
},
|
| 398 |
+
{
|
| 399 |
+
"path": "pi05/src/openpi/policies/robomem_progress.py",
|
| 400 |
+
"size_bytes": 1371,
|
| 401 |
+
"sha256": "43b8afbdb6f96400f65b8bcb74e4b077856ede5e874d1513d6b16ef674ff98c3"
|
| 402 |
+
},
|
| 403 |
+
{
|
| 404 |
+
"path": "pi05/src/openpi/py.typed",
|
| 405 |
+
"size_bytes": 0,
|
| 406 |
+
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
| 407 |
+
},
|
| 408 |
+
{
|
| 409 |
+
"path": "pi05/src/openpi/rmbench_cover_blocks_10_full_prompts.py",
|
| 410 |
+
"size_bytes": 4524,
|
| 411 |
+
"sha256": "65e27c9cd1d89238e26005e10525cca8af2faec5837c0221dc3a9b02f2d8304f"
|
| 412 |
+
},
|
| 413 |
+
{
|
| 414 |
+
"path": "pi05/src/openpi/rmbench_eval_prompts.py",
|
| 415 |
+
"size_bytes": 1406,
|
| 416 |
+
"sha256": "c3df981e542ecb1215fb10ee4a56aa3fec7ea3af6ec998e096e0e29c5580d4f5"
|
| 417 |
+
},
|
| 418 |
+
{
|
| 419 |
+
"path": "pi05/src/openpi/rmbench_m1_5tasks_10_full_prompts.py",
|
| 420 |
+
"size_bytes": 14008,
|
| 421 |
+
"sha256": "488b3a0b0854a71d4a192bd9c02718ea7df307eb6d335276244178820cc0ae4c"
|
| 422 |
+
},
|
| 423 |
+
{
|
| 424 |
+
"path": "pi05/src/openpi/robomem_task6_100_instruction.py",
|
| 425 |
+
"size_bytes": 21358,
|
| 426 |
+
"sha256": "e6ae6b788d90b206187550b61d115d10a076cd4dde02afe2c2e5e5cc6dc95a97"
|
| 427 |
+
},
|
| 428 |
+
{
|
| 429 |
+
"path": "pi05/src/openpi/robomemarena_26tasks_10_full_prompts.py",
|
| 430 |
+
"size_bytes": 40410,
|
| 431 |
+
"sha256": "8f8c71573de9701321f62b9cbb334829b97199e6b01831f6d07fb990dcf92906"
|
| 432 |
+
},
|
| 433 |
+
{
|
| 434 |
+
"path": "pi05/src/openpi/robomemarena_26tasks_subtask_prompts.py",
|
| 435 |
+
"size_bytes": 5087,
|
| 436 |
+
"sha256": "8e4e2b3e7d4e88d927c5d4147ff41f91db423231072e302b4bc02fd77a45035e"
|
| 437 |
+
},
|
| 438 |
+
{
|
| 439 |
+
"path": "pi05/src/openpi/serving/websocket_policy_server.py",
|
| 440 |
+
"size_bytes": 3480,
|
| 441 |
+
"sha256": "001f06365933bd11386298905c9125ac28db9a8985f35d10ae090d174cc8e1ae"
|
| 442 |
+
},
|
| 443 |
+
{
|
| 444 |
+
"path": "pi05/src/openpi/shared/__init__.py",
|
| 445 |
+
"size_bytes": 0,
|
| 446 |
+
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
| 447 |
+
},
|
| 448 |
+
{
|
| 449 |
+
"path": "pi05/src/openpi/shared/array_typing.py",
|
| 450 |
+
"size_bytes": 3402,
|
| 451 |
+
"sha256": "6524945566eab3eb97b75a6280ba4724617b4d028c55f05172b283c3de68a026"
|
| 452 |
+
},
|
| 453 |
+
{
|
| 454 |
+
"path": "pi05/src/openpi/shared/download.py",
|
| 455 |
+
"size_bytes": 8862,
|
| 456 |
+
"sha256": "a12054a6ddaab4efa833c4ff49a1aba00c63759e4e88d6be21c1e0b3307fd9b2"
|
| 457 |
+
},
|
| 458 |
+
{
|
| 459 |
+
"path": "pi05/src/openpi/shared/download_test.py",
|
| 460 |
+
"size_bytes": 1490,
|
| 461 |
+
"sha256": "234ee70ab2501a1421f6c44cbd83c1e16747597023319ae1446bed9d44f17072"
|
| 462 |
+
},
|
| 463 |
+
{
|
| 464 |
+
"path": "pi05/src/openpi/shared/image_tools.py",
|
| 465 |
+
"size_bytes": 4665,
|
| 466 |
+
"sha256": "0f8fcb355288119e56acb56d550c1cc1f3a9edddceb0dc5b319f955943ef7767"
|
| 467 |
+
},
|
| 468 |
+
{
|
| 469 |
+
"path": "pi05/src/openpi/shared/image_tools_test.py",
|
| 470 |
+
"size_bytes": 1418,
|
| 471 |
+
"sha256": "842374579dc9f4c2907da5e8bb555efe5a3f4902b659720c8e90115863dd59fe"
|
| 472 |
+
},
|
| 473 |
+
{
|
| 474 |
+
"path": "pi05/src/openpi/shared/nnx_utils.py",
|
| 475 |
+
"size_bytes": 2844,
|
| 476 |
+
"sha256": "4da7111ba3011c4d1c381c9ed17257771df6a50ed89182647c3fb6f79208005e"
|
| 477 |
+
},
|
| 478 |
+
{
|
| 479 |
+
"path": "pi05/src/openpi/shared/normalize.py",
|
| 480 |
+
"size_bytes": 5529,
|
| 481 |
+
"sha256": "6c2cea4946fb07e51801530400d2b2fd94730e195cd290d6b6960114eca9739d"
|
| 482 |
+
},
|
| 483 |
+
{
|
| 484 |
+
"path": "pi05/src/openpi/shared/normalize_test.py",
|
| 485 |
+
"size_bytes": 1521,
|
| 486 |
+
"sha256": "fda27a21cf37f337b12bcab71229407c2a349c5965c33f278bdc468ed03f58af"
|
| 487 |
+
},
|
| 488 |
+
{
|
| 489 |
+
"path": "pi05/src/openpi/training/checkpoints.py",
|
| 490 |
+
"size_bytes": 6696,
|
| 491 |
+
"sha256": "ea420c6a816933ef96966a092801f388997800530aef7a29f7b42807cc3e5103"
|
| 492 |
+
},
|
| 493 |
+
{
|
| 494 |
+
"path": "pi05/src/openpi/training/config.py",
|
| 495 |
+
"size_bytes": 35031,
|
| 496 |
+
"sha256": "e1cde1a9906848460cd92a3c82155a83047610d983cb933e8f6f15bb93c1ac22"
|
| 497 |
+
},
|
| 498 |
+
{
|
| 499 |
+
"path": "pi05/src/openpi/training/data_loader.py",
|
| 500 |
+
"size_bytes": 23482,
|
| 501 |
+
"sha256": "7ccd627788901c9c6d1db222404f22e6cec76b54dab5a29586c21472262d945a"
|
| 502 |
+
},
|
| 503 |
+
{
|
| 504 |
+
"path": "pi05/src/openpi/training/data_loader_test.py",
|
| 505 |
+
"size_bytes": 2487,
|
| 506 |
+
"sha256": "a0cf1bc293e8b0eb9bc5516bc05d30d9940da06c94fcfa1addca7267f8358815"
|
| 507 |
+
},
|
| 508 |
+
{
|
| 509 |
+
"path": "pi05/src/openpi/training/droid_rlds_dataset.py",
|
| 510 |
+
"size_bytes": 10886,
|
| 511 |
+
"sha256": "1d3a057a040240218234d7dbc6680ee4fe8256f2058531a0f9fefd3f7ee995a4"
|
| 512 |
+
},
|
| 513 |
+
{
|
| 514 |
+
"path": "pi05/src/openpi/training/memory_training.py",
|
| 515 |
+
"size_bytes": 208,
|
| 516 |
+
"sha256": "bc17fe9f002a94ab38a09f46caeff75b5baeef2a4a6edd20b3381b611105b14b"
|
| 517 |
+
},
|
| 518 |
+
{
|
| 519 |
+
"path": "pi05/src/openpi/training/misc/polaris_config.py",
|
| 520 |
+
"size_bytes": 9624,
|
| 521 |
+
"sha256": "c2ab92efb5e0d675118ad812113fd1c3cb3a62cdab5d79066014266e82df12d8"
|
| 522 |
+
},
|
| 523 |
+
{
|
| 524 |
+
"path": "pi05/src/openpi/training/misc/roboarena_config.py",
|
| 525 |
+
"size_bytes": 4804,
|
| 526 |
+
"sha256": "914acdd74ccf90e6779c13a5093140595c7e4f687e9e0b167439446a81d74c01"
|
| 527 |
+
},
|
| 528 |
+
{
|
| 529 |
+
"path": "pi05/src/openpi/training/optimizer.py",
|
| 530 |
+
"size_bytes": 3202,
|
| 531 |
+
"sha256": "cfb5d6f6aec61fbe961287b4015c652260628a0ed4682a0dbdc271b8e65431fa"
|
| 532 |
+
},
|
| 533 |
+
{
|
| 534 |
+
"path": "pi05/src/openpi/training/rmbench_memory_data.py",
|
| 535 |
+
"size_bytes": 10093,
|
| 536 |
+
"sha256": "14923395d2d9395e98f3b5571b25cfc1a2d7ddb7f792521bb5a6620952403387"
|
| 537 |
+
},
|
| 538 |
+
{
|
| 539 |
+
"path": "pi05/src/openpi/training/robomem_memory_data.py",
|
| 540 |
+
"size_bytes": 7135,
|
| 541 |
+
"sha256": "6fd26b38cfb373630611e1cf1c54ef4af2b4992321c225ca1d73cb2d5830c0ec"
|
| 542 |
+
},
|
| 543 |
+
{
|
| 544 |
+
"path": "pi05/src/openpi/training/robomem_memory_loader.py",
|
| 545 |
+
"size_bytes": 3911,
|
| 546 |
+
"sha256": "84528f678e54805b2f3819ec596595086f115b1971ea893cb7c1ae3279ed41fc"
|
| 547 |
+
},
|
| 548 |
+
{
|
| 549 |
+
"path": "pi05/src/openpi/training/sharding.py",
|
| 550 |
+
"size_bytes": 4099,
|
| 551 |
+
"sha256": "c67c4112a126ab097571c8a882fd1ee3f106b15b6e608e7205ac7f29b44ca985"
|
| 552 |
+
},
|
| 553 |
+
{
|
| 554 |
+
"path": "pi05/src/openpi/training/utils.py",
|
| 555 |
+
"size_bytes": 1217,
|
| 556 |
+
"sha256": "b684e674343f86ef5e976f279a586d3fe2f885e901f396ac899ed136abc7eca6"
|
| 557 |
+
},
|
| 558 |
+
{
|
| 559 |
+
"path": "pi05/src/openpi/training/weight_loaders.py",
|
| 560 |
+
"size_bytes": 3738,
|
| 561 |
+
"sha256": "a54ac3222f81f8c6233241bc16b6f369c9416cdaa83f7058f96f7ddcfde1a3b4"
|
| 562 |
+
},
|
| 563 |
+
{
|
| 564 |
+
"path": "pi05/src/openpi/transforms.py",
|
| 565 |
+
"size_bytes": 46152,
|
| 566 |
+
"sha256": "ed9673a1f140f188cb6a0657f6938ce69ea6783d9f42c3ed3e6b6b46c1fc7339"
|
| 567 |
+
},
|
| 568 |
+
{
|
| 569 |
+
"path": "pi05/uv.lock",
|
| 570 |
+
"size_bytes": 795910,
|
| 571 |
+
"sha256": "793488b5a55bb87200db90a61fd0af51922b686d94e1da4f4c587ab119b37d74"
|
| 572 |
+
},
|
| 573 |
+
{
|
| 574 |
+
"path": "pyproject.toml",
|
| 575 |
+
"size_bytes": 365,
|
| 576 |
+
"sha256": "e21069c1bb24a3846579748d05fdbef49dda21c2df1b0d4af0fb028be475ce41"
|
| 577 |
+
},
|
| 578 |
+
{
|
| 579 |
+
"path": "taskanchor/__init__.py",
|
| 580 |
+
"size_bytes": 82,
|
| 581 |
+
"sha256": "efe209ecb3e663212784c22fb1891bf52261c241e069d6d90b5a85f2557a6956"
|
| 582 |
+
},
|
| 583 |
+
{
|
| 584 |
+
"path": "taskanchor/__main__.py",
|
| 585 |
+
"size_bytes": 4924,
|
| 586 |
+
"sha256": "efc77b76efe98c00c291de317a6e9822d58f908744b75df1a569a4aa2a08d690"
|
| 587 |
+
},
|
| 588 |
+
{
|
| 589 |
+
"path": "taskanchor/configs/__init__.py",
|
| 590 |
+
"size_bytes": 61,
|
| 591 |
+
"sha256": "a9ecb5d1640dd38980236cdb84878df8c498c3c1d05b291faea19d9e77428f5f"
|
| 592 |
+
},
|
| 593 |
+
{
|
| 594 |
+
"path": "taskanchor/configs/robomemarena_pi05.json",
|
| 595 |
+
"size_bytes": 634,
|
| 596 |
+
"sha256": "fd7d0e623817c60446b85250dd5fc3c6bb36fbc20bf057a30ddef2277d451099"
|
| 597 |
+
},
|
| 598 |
+
{
|
| 599 |
+
"path": "taskanchor/data/__init__.py",
|
| 600 |
+
"size_bytes": 58,
|
| 601 |
+
"sha256": "f40c70863322679971b4c9eaa17158d1ddee26fe194d3f0e9864906a494f5cb0"
|
| 602 |
+
},
|
| 603 |
+
{
|
| 604 |
+
"path": "taskanchor/data/history.py",
|
| 605 |
+
"size_bytes": 3157,
|
| 606 |
+
"sha256": "f8d4c155eaf1d636c7596795b33f28d9fad606f7b7d4b6c421e595ac4a274511"
|
| 607 |
+
},
|
| 608 |
+
{
|
| 609 |
+
"path": "taskanchor/data/robomemarena_metadata.py",
|
| 610 |
+
"size_bytes": 6932,
|
| 611 |
+
"sha256": "d8026e5cd225a0c22edc05004d921298ed8ce5d88f94c4c0110c6ba76e2dde48"
|
| 612 |
+
},
|
| 613 |
+
{
|
| 614 |
+
"path": "taskanchor/paths.py",
|
| 615 |
+
"size_bytes": 1190,
|
| 616 |
+
"sha256": "b829fba7bef5fa450a2621c6d4758e9dac951af5482ae3d4b34552cc9c6b2760"
|
| 617 |
+
},
|
| 618 |
+
{
|
| 619 |
+
"path": "taskanchor/pi05/__init__.py",
|
| 620 |
+
"size_bytes": 58,
|
| 621 |
+
"sha256": "4fe691e5c85a023c697680cb0f7faa1fdf4e5e21feb3bbc5c061403836d075af"
|
| 622 |
+
},
|
| 623 |
+
{
|
| 624 |
+
"path": "taskanchor/pi05/taskanchor.py",
|
| 625 |
+
"size_bytes": 13666,
|
| 626 |
+
"sha256": "6543c6114add32135786c20db971246b18e66a183f1bfe134e2bee7b5cf3992e"
|
| 627 |
+
},
|
| 628 |
+
{
|
| 629 |
+
"path": "taskanchor/pi05/training.py",
|
| 630 |
+
"size_bytes": 9393,
|
| 631 |
+
"sha256": "668be3f93ed82290d66319eb67ba77e85a34119b20685a36d4a20abced2f84f7"
|
| 632 |
+
},
|
| 633 |
+
{
|
| 634 |
+
"path": "taskanchor/pi05/ttt.py",
|
| 635 |
+
"size_bytes": 6106,
|
| 636 |
+
"sha256": "7cc97cd49dce55cee1bd60a95e07f095b47bad5d28f19d7d823ae1864f5159d0"
|
| 637 |
+
},
|
| 638 |
+
{
|
| 639 |
+
"path": "tests/test_portability.py",
|
| 640 |
+
"size_bytes": 3601,
|
| 641 |
+
"sha256": "236c1119e7d4868c26a04fcf8b650723e61bd191e4925b87e6e9ec65283bc685"
|
| 642 |
+
},
|
| 643 |
+
{
|
| 644 |
+
"path": "tools/compare_robomem_reports.py",
|
| 645 |
+
"size_bytes": 3875,
|
| 646 |
+
"sha256": "9398c81f25f9f7ab3eb3f50d792c28ebc0ab136fd8fc841969d5cc309d70945b"
|
| 647 |
+
},
|
| 648 |
+
{
|
| 649 |
+
"path": "tools/prepare_benchmark.py",
|
| 650 |
+
"size_bytes": 2786,
|
| 651 |
+
"sha256": "5eb7efdbb55d851c49b49d4c336d4675d20c894fdd93d70d637d60a8300049d9"
|
| 652 |
+
}
|
| 653 |
+
]
|
| 654 |
+
}
|
code/assets/pi05_libero/norm_stats.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"norm_stats": {
|
| 3 |
+
"state": {
|
| 4 |
+
"mean": [
|
| 5 |
+
0.048380594700574875,
|
| 6 |
+
0.11262303590774536,
|
| 7 |
+
0.6594910025596619,
|
| 8 |
+
3.370551347732544,
|
| 9 |
+
-0.30113735795021057,
|
| 10 |
+
-0.24474778771400452,
|
| 11 |
+
0.02991946041584015,
|
| 12 |
+
-0.029522070661187172
|
| 13 |
+
],
|
| 14 |
+
"std": [
|
| 15 |
+
0.11915058642625809,
|
| 16 |
+
0.2559613585472107,
|
| 17 |
+
0.1345514953136444,
|
| 18 |
+
0.6972067952156067,
|
| 19 |
+
0.75095134973526,
|
| 20 |
+
0.7611529231071472,
|
| 21 |
+
0.011234981939196587,
|
| 22 |
+
0.01164152380079031
|
| 23 |
+
],
|
| 24 |
+
"q01": [
|
| 25 |
+
-0.2685799194574356,
|
| 26 |
+
-0.33133132414221766,
|
| 27 |
+
0.4330671876549721,
|
| 28 |
+
2.4110534167289734,
|
| 29 |
+
-2.4456029770374297,
|
| 30 |
+
-2.43879587931633,
|
| 31 |
+
0.0009645143877714873,
|
| 32 |
+
-0.04009675695095211
|
| 33 |
+
],
|
| 34 |
+
"q99": [
|
| 35 |
+
0.23303380120396622,
|
| 36 |
+
0.5012647942483425,
|
| 37 |
+
1.0027180651426315,
|
| 38 |
+
4.715920171499253,
|
| 39 |
+
0.03176737618446346,
|
| 40 |
+
1.2033550313949584,
|
| 41 |
+
0.04003552807592787,
|
| 42 |
+
0.00003360225446522036
|
| 43 |
+
]
|
| 44 |
+
},
|
| 45 |
+
"actions": {
|
| 46 |
+
"mean": [
|
| 47 |
+
0.010760927572846413,
|
| 48 |
+
0.02506864257156849,
|
| 49 |
+
0.01419717539101839,
|
| 50 |
+
0.0020873930770903826,
|
| 51 |
+
0.0005215497221797705,
|
| 52 |
+
-0.00006810902414144948,
|
| 53 |
+
0.031027423217892647
|
| 54 |
+
],
|
| 55 |
+
"std": [
|
| 56 |
+
0.20257413387298584,
|
| 57 |
+
0.363967627286911,
|
| 58 |
+
0.30624639987945557,
|
| 59 |
+
0.12264969944953918,
|
| 60 |
+
0.11749287694692612,
|
| 61 |
+
0.08579856157302856,
|
| 62 |
+
0.9992039203643799
|
| 63 |
+
],
|
| 64 |
+
"q01": [
|
| 65 |
+
-1.0,
|
| 66 |
+
-1.0,
|
| 67 |
+
-1.0,
|
| 68 |
+
-0.5,
|
| 69 |
+
-0.5001200099349021,
|
| 70 |
+
-0.5003199966907501,
|
| 71 |
+
-1.0
|
| 72 |
+
],
|
| 73 |
+
"q99": [
|
| 74 |
+
0.8684000000000001,
|
| 75 |
+
0.9996,
|
| 76 |
+
0.9996,
|
| 77 |
+
0.5,
|
| 78 |
+
0.4999599966883661,
|
| 79 |
+
0.4997600099325181,
|
| 80 |
+
0.9997999999999998
|
| 81 |
+
]
|
| 82 |
+
}
|
| 83 |
+
}
|
| 84 |
+
}
|
code/docs/PORTABILITY.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Paths, assets, and the evaluation protocol
|
| 2 |
+
|
| 3 |
+
The release resolves paths relative to the checked-out source or explicit user
|
| 4 |
+
arguments. These changes do not change model dimensions, parameter keys,
|
| 5 |
+
normalization numbers, prompts, sampling, optimization, or scoring.
|
| 6 |
+
|
| 7 |
+
## Training and feature extraction
|
| 8 |
+
|
| 9 |
+
Run from the release root. Existing `--dataset`, `--base-params`, `--cache`, and
|
| 10 |
+
`--checkpoint-root` arguments override these defaults; training also accepts
|
| 11 |
+
`--assets-dir` (the parent of `pi05_libero/norm_stats.json`). Environment
|
| 12 |
+
variables are read at process startup.
|
| 13 |
+
|
| 14 |
+
| Environment variable | Default relative to the release root |
|
| 15 |
+
| --- | --- |
|
| 16 |
+
| `TASKANCHOR_ROBOMEM_DATASET` | `data/robomemarena_pi05_task_all_with_subtask_seed` |
|
| 17 |
+
| `TASKANCHOR_BASE_PARAMS` | `pretrained/pi05_base/params` |
|
| 18 |
+
| `TASKANCHOR_FEATURE_CACHE` | `data/robomem_siglip_base_main_spatial_v3` |
|
| 19 |
+
| `TASKANCHOR_ASSETS_DIR` | `assets` |
|
| 20 |
+
| `TASKANCHOR_CHECKPOINT_ROOT` | `checkpoints` |
|
| 21 |
+
|
| 22 |
+
The packaged normalization file is the original `pi05_libero` file. Do not
|
| 23 |
+
replace it with newly computed statistics when reproducing the provided model.
|
| 24 |
+
During inference, normalization is loaded directly from the selected
|
| 25 |
+
checkpoint's `assets/pi05_libero/norm_stats.json`; training-server paths are
|
| 26 |
+
not consulted.
|
| 27 |
+
|
| 28 |
+
Training requires the LeRobot Parquet dataset with embedded RGB `image` and
|
| 29 |
+
`wrist_image`, `state`, `actions`, and metadata in `meta/episodes_stats.jsonl`.
|
| 30 |
+
The metadata must preserve `task_id`, `subtask_id`, and `seed_rank`, so subtasks
|
| 31 |
+
can be joined into the original full-task history. The existing loader does
|
| 32 |
+
not require an additional predecoded observation cache. Feature extraction
|
| 33 |
+
uses frozen base-model SigLIP weights and writes the main-camera 4×4 cache.
|
| 34 |
+
The training dataset and feature cache are not required for evaluation.
|
| 35 |
+
|
| 36 |
+
Feature-cache provenance intentionally retains its original strict check,
|
| 37 |
+
including absolute dataset/base-parameter locations and metadata SHA-256.
|
| 38 |
+
If moving a preexisting cache, extraction/training rejects a path-mismatched
|
| 39 |
+
manifest. The supported portable route is to extract a new cache at the new
|
| 40 |
+
location; do not silently rewrite provenance or relabel different features.
|
| 41 |
+
Fresh training is not guaranteed to reproduce the checkpoint bit for bit,
|
| 42 |
+
including the recorded restart at step 10,000.
|
| 43 |
+
|
| 44 |
+
## Offline inference tokenizer
|
| 45 |
+
|
| 46 |
+
`TASKANCHOR_TOKENIZER_PATH=/absolute/path/to/paligemma_tokenizer.model` selects
|
| 47 |
+
the provided SentencePiece file. It changes the file location only. Without
|
| 48 |
+
that setting, the original `gs://big_vision/paligemma_tokenizer.model` source is
|
| 49 |
+
downloaded through OpenPI, whose cache can be relocated with `OPENPI_DATA_HOME`.
|
| 50 |
+
The public full checkpoint contains the backbone and adapter; inference does
|
| 51 |
+
not require downloading an additional `pi05_base` checkpoint.
|
| 52 |
+
|
| 53 |
+
## Benchmark installation and paths
|
| 54 |
+
|
| 55 |
+
Install RoboMemArena and its simulator dependencies separately, using commit
|
| 56 |
+
`d09f63b671f362985e1e0d038d2014c2d2519488`. The release does not vendor the
|
| 57 |
+
benchmark, MuJoCo/OSMesa libraries, or visual assets. Its evaluation process
|
| 58 |
+
must import the pinned checkout's `libero_fork` and the official scorer.
|
| 59 |
+
|
| 60 |
+
After checking out that commit, generate configuration without editing the
|
| 61 |
+
official scorer:
|
| 62 |
+
|
| 63 |
+
```bash
|
| 64 |
+
python tools/prepare_benchmark.py \
|
| 65 |
+
--benchmark-root /path/to/RoboMemArena \
|
| 66 |
+
--config-dir /path/to/robomem_libero_config
|
| 67 |
+
|
| 68 |
+
export TASKANCHOR_ROBOMEM_BENCHMARK=/path/to/RoboMemArena/evaluation_benchmark
|
| 69 |
+
export TASKANCHOR_ROBOMEM_EVAL_PYTHON=/path/to/simulator-env/bin/python
|
| 70 |
+
export TASKANCHOR_LIBERO_CONFIG=/path/to/robomem_libero_config
|
| 71 |
+
export TASKANCHOR_PI05_PYTHON=/path/to/pi05-env/bin/python
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
The setup utility verifies the pinned Git revision and unchanged tracked
|
| 75 |
+
benchmark files, then writes `OFFICIAL_SOURCE.json` and a local LIBERO
|
| 76 |
+
`config.yaml`. It requires `libero_fork/libero/assets` and `bddl_files`.
|
| 77 |
+
Evaluation uses seeded resets and does not read LIBERO stored `init_files`.
|
| 78 |
+
Keep the simulator virtual environment's executable path; resolving its
|
| 79 |
+
symlink to a base Python loses that environment's packages.
|
| 80 |
+
|
| 81 |
+
OSMesa remains the renderer. If OSMesa libraries are installed outside system
|
| 82 |
+
library paths, set `TASKANCHOR_RENDER_LIBS` to their directory. The original
|
| 83 |
+
runtime used Mesa libraries under its `robomem_render_libs/usr/lib/x86_64-linux-gnu`
|
| 84 |
+
directory; these operating-system binaries are not part of the code package.
|
| 85 |
+
|
| 86 |
+
The original Task25 candidate list is retained in
|
| 87 |
+
`evidence/task25_seed_candidates.json`. Only initialization failures before any
|
| 88 |
+
policy action may advance to the next candidate. Policy failures are scored.
|
| 89 |
+
Both main and wrist observations are still passed to the VLA; only the main
|
| 90 |
+
view receives the TaskAnchor residual.
|
| 91 |
+
|
| 92 |
+
## Preserved numerical settings
|
| 93 |
+
|
| 94 |
+
The recorded 50k model uses separate permanent frame0, 191 history frames,
|
| 95 |
+
16 tokens per frame, stride 10, action chunk 20, execution length 10, and
|
| 96 |
+
nearest-stage predicted progress. Evaluation uses all 26 tasks and the original 2500-step budget.
|
| 97 |
+
The evaluator retains the original trial and seed schedule; configure and record
|
| 98 |
+
any different study protocol before collecting results. Paths do not select a
|
| 99 |
+
different memory protocol or change these values.
|
| 100 |
+
|
| 101 |
+
CPU portability checks can be run without JAX or simulator installation:
|
| 102 |
+
|
| 103 |
+
```bash
|
| 104 |
+
python -m unittest discover -s tests -p test_portability.py
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
These checks validate path relocation and benchmark setup only. They do not
|
| 108 |
+
replace closed-loop benchmark evaluation.
|
code/docs/PUBLISHING_ZH.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 发布和后续维护
|
| 2 |
+
|
| 3 |
+
Hugging Face 目标:<https://huggingface.co/henry13522/Taskanchor>。
|
| 4 |
+
代码许可证为 Apache-2.0;完整权重保留 π0.5 / PaliGemma / Gemma 上游条款。模型卡明确区分代码和权重许可。
|
| 5 |
+
|
| 6 |
+
## 本次目录
|
| 7 |
+
|
| 8 |
+
`model/` 是 Hugging Face 仓库的上传根目录:含 `50000/`、`recipe.json`、tokenizer、模型卡、校验清单和 `code/`。`code/` 同时作为独立 GitHub 源码仓库发布目录。HF 下载后直接按 `code/docs/REPRODUCING.md` 使用。
|
| 9 |
+
|
| 10 |
+
只发布 50k 完整推理 checkpoint;不包含 optimizer/train_state、训练数据、特征缓存、机器环境、凭据或训练日志。不要将原始工作区整体推送到公开仓库。
|
| 11 |
+
|
| 12 |
+
## Hugging Face 上传
|
| 13 |
+
|
| 14 |
+
在终端安全登录有目标仓库写权限的 Hugging Face 账号:
|
| 15 |
+
|
| 16 |
+
```bash
|
| 17 |
+
hf auth login
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
使用官方 Hugging Face CLI 上传已检查的模型目录(目录内包含 `code/`):
|
| 21 |
+
|
| 22 |
+
```bash
|
| 23 |
+
hf upload henry13522/Taskanchor /path/to/release/model .
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
上传使用 Hugging Face 官方 SDK/Xet,无需转换 Orbax checkpoint。若使用 Git 路线,应先安装 Git Xet,并保留发布包 `.gitattributes` 对**无扩展名** Orbax 分片的追踪规则,不能只追踪 `*.safetensors`。
|
| 27 |
+
|
| 28 |
+
官方文档:[上传文件](https://huggingface.co/docs/huggingface_hub/guides/upload)、[Git Xet](https://huggingface.co/docs/hub/git-xet)。
|
| 29 |
+
|
| 30 |
+
## GitHub 源码仓库
|
| 31 |
+
|
| 32 |
+
账号/组织确定后,在 `code/` 内执行以下命令。将 `OWNER` 替换为实际账号;不要在 `model/` 内创建 GitHub 源码仓库。
|
| 33 |
+
|
| 34 |
+
```bash
|
| 35 |
+
cd /path/to/release/code
|
| 36 |
+
git init -b main
|
| 37 |
+
git add .
|
| 38 |
+
git commit -m 'Release TaskAnchor RoboMemArena pi05 training and evaluation'
|
| 39 |
+
gh repo create OWNER/TaskAnchor --public --source=. --remote=origin --push
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
在 GitHub About 中链接 Hugging Face 权重;在模型卡中添加 GitHub 仓库链接。后续对外报告结果时记录代码 commit、模型仓库 revision、benchmark commit 和完整评测命令。
|
| 43 |
+
|
| 44 |
+
## 本次验证范围
|
| 45 |
+
|
| 46 |
+
本次执行 CPU 权重恢复、有限性和文件校验、路径测试。公开包不提供历史小批次评测指标或复现结论;评测次数由正式研究协议确定;现有入口保留原试验次数和种子调度,数量不同时须先调整协议实现。原始记录仅保留在上传目录之外的本地审计目录。
|
code/docs/REPRODUCING.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Reproducing the RoboMemArena 50k model
|
| 2 |
+
|
| 3 |
+
This guide uses the downloaded Hugging Face repository, which contains both
|
| 4 |
+
source and complete inference weights:
|
| 5 |
+
|
| 6 |
+
```text
|
| 7 |
+
Taskanchor/
|
| 8 |
+
├── code/ # source directory
|
| 9 |
+
│ ├── taskanchor/
|
| 10 |
+
│ ├── pi05/
|
| 11 |
+
│ └── assets/pi05_libero/norm_stats.json
|
| 12 |
+
├── recipe.json
|
| 13 |
+
├── tokenizer/paligemma_tokenizer.model
|
| 14 |
+
└── 50000/
|
| 15 |
+
├── params/ # complete Orbax parameter directory
|
| 16 |
+
└── assets/
|
| 17 |
+
├── robomem_memory.json
|
| 18 |
+
└── pi05_libero/norm_stats.json
|
| 19 |
+
```
|
| 20 |
+
|
| 21 |
+
Set these absolute paths for the commands below:
|
| 22 |
+
|
| 23 |
+
```bash
|
| 24 |
+
export TASKANCHOR_MODEL=/absolute/path/to/Taskanchor
|
| 25 |
+
export TASKANCHOR_CODE="$TASKANCHOR_MODEL/code"
|
| 26 |
+
export TASKANCHOR_BENCHMARK_REPO=/absolute/path/to/RoboMemArena
|
| 27 |
+
export TASKANCHOR_SIM_ENV=/absolute/path/to/robomem-sim-env
|
| 28 |
+
cd "$TASKANCHOR_CODE"
|
| 29 |
+
```
|
| 30 |
+
|
| 31 |
+
## 1. Policy environment: Python 3.11 and JAX
|
| 32 |
+
|
| 33 |
+
Use the checked-in `pi05/uv.lock`; do not replace it with an unpinned OpenPI
|
| 34 |
+
installation. This environment runs feature extraction, training, model
|
| 35 |
+
servers, and the evaluation scheduler. The simulator runs in a second
|
| 36 |
+
environment. Install `uv` first if it is unavailable.
|
| 37 |
+
|
| 38 |
+
```bash
|
| 39 |
+
cd "$TASKANCHOR_CODE/pi05"
|
| 40 |
+
uv python install 3.11
|
| 41 |
+
uv sync --frozen --python 3.11
|
| 42 |
+
uv pip install --python .venv/bin/python --no-deps -e "$TASKANCHOR_CODE"
|
| 43 |
+
|
| 44 |
+
export TASKANCHOR_PI05_PYTHON="$TASKANCHOR_CODE/pi05/.venv/bin/python"
|
| 45 |
+
export TASKANCHOR_TOKENIZER_PATH="$TASKANCHOR_MODEL/tokenizer/paligemma_tokenizer.model"
|
| 46 |
+
cd "$TASKANCHOR_CODE"
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
The measured policy stack used Python 3.11.13, JAX/JAXlib 0.5.3, Flax 0.10.2,
|
| 50 |
+
Optax 0.2.4, and Orbax 0.11.13. A compatible NVIDIA driver is required for the
|
| 51 |
+
CUDA 12 JAX build. Do not force `JAX_PLATFORMS=cpu` for training or inference.
|
| 52 |
+
The local tokenizer setting avoids a network download during model startup.
|
| 53 |
+
The full 50k checkpoint already contains backbone weights.
|
| 54 |
+
|
| 55 |
+
## 2. Simulator environment: Python 3.10 and MuJoCo
|
| 56 |
+
|
| 57 |
+
Check out the exact benchmark revision. Use a fresh checkout or a clean
|
| 58 |
+
worktree; `tools/prepare_benchmark.py` rejects modified tracked benchmark code.
|
| 59 |
+
|
| 60 |
+
```bash
|
| 61 |
+
git clone https://github.com/OpenHelix-Team/RoboMemArena.git "$TASKANCHOR_BENCHMARK_REPO"
|
| 62 |
+
git -C "$TASKANCHOR_BENCHMARK_REPO" checkout d09f63b671f362985e1e0d038d2014c2d2519488
|
| 63 |
+
|
| 64 |
+
uv python install 3.10
|
| 65 |
+
uv venv --python 3.10 "$TASKANCHOR_SIM_ENV"
|
| 66 |
+
uv pip install --python "$TASKANCHOR_SIM_ENV/bin/python" \
|
| 67 |
+
'numpy==1.26.4' 'mujoco==2.3.7' 'robosuite==1.4.1' \
|
| 68 |
+
'scipy==1.15.3' 'numba==0.68.0' 'llvmlite==0.50.0' \
|
| 69 |
+
'torch==2.4.1' 'torchvision==0.19.1' \
|
| 70 |
+
'opencv-python==4.11.0.86' 'Pillow==11.3.0' \
|
| 71 |
+
'imageio==2.34.2' 'imageio-ffmpeg==0.6.0' \
|
| 72 |
+
'PyOpenGL==3.1.10' 'PyYAML==6.0.3' 'h5py==3.16.0' \
|
| 73 |
+
'easydict==1.13' 'gym==0.26.2' 'bddl==3.6.0' \
|
| 74 |
+
'termcolor==3.3.0' 'cloudpickle==3.1.2' 'matplotlib==3.10.9' \
|
| 75 |
+
'websockets==16.1.1' 'msgpack==1.2.3' tqdm
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
These pins are read from the working evaluation environment. That environment
|
| 79 |
+
inherited system packages; the clean-environment installation above has not
|
| 80 |
+
been replayed as part of this release. Use the pinned benchmark's own setup
|
| 81 |
+
instructions for any platform-specific missing dependency. Do not substitute
|
| 82 |
+
a different MuJoCo/robosuite pair within a controlled evaluation comparison.
|
| 83 |
+
|
| 84 |
+
The measured renderer is OSMesa. On Debian/Ubuntu, install the system OSMesa
|
| 85 |
+
runtime if it is absent:
|
| 86 |
+
|
| 87 |
+
```bash
|
| 88 |
+
sudo apt-get update
|
| 89 |
+
sudo apt-get install -y libosmesa6 libgl1 libegl1 ffmpeg
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
The benchmark must contain its meshes/textures and BDDL files. Follow its
|
| 93 |
+
asset instructions if Git checkout alone does not supply these files.
|
| 94 |
+
Create local LIBERO paths without importing its interactive setup:
|
| 95 |
+
|
| 96 |
+
```bash
|
| 97 |
+
"$TASKANCHOR_PI05_PYTHON" tools/prepare_benchmark.py \
|
| 98 |
+
--benchmark-root "$TASKANCHOR_BENCHMARK_REPO" \
|
| 99 |
+
--config-dir "$TASKANCHOR_CODE/local/robomem_libero_config"
|
| 100 |
+
|
| 101 |
+
export TASKANCHOR_ROBOMEM_BENCHMARK="$TASKANCHOR_BENCHMARK_REPO/evaluation_benchmark"
|
| 102 |
+
export TASKANCHOR_ROBOMEM_EVAL_PYTHON="$TASKANCHOR_SIM_ENV/bin/python"
|
| 103 |
+
export TASKANCHOR_LIBERO_CONFIG="$TASKANCHOR_CODE/local/robomem_libero_config"
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
If OSMesa is outside the system linker paths, additionally set
|
| 107 |
+
`TASKANCHOR_RENDER_LIBS=/absolute/path/to/osmesa/lib`. Keep the simulator
|
| 108 |
+
virtualenv executable path as written; do not resolve its symlink to base
|
| 109 |
+
Python. The launcher sets `MUJOCO_GL=osmesa`, `PYOPENGL_PLATFORM=osmesa`, and
|
| 110 |
+
the local `libero_fork` import path for each simulator subprocess.
|
| 111 |
+
|
| 112 |
+
## 3. Evaluate the released checkpoint
|
| 113 |
+
|
| 114 |
+
The unified command starts its own policy servers and terminates only those
|
| 115 |
+
servers when evaluation ends. Do not start a separate server first. The
|
| 116 |
+
`--python` option below is the policy interpreter; the simulator interpreter
|
| 117 |
+
comes from `TASKANCHOR_ROBOMEM_EVAL_PYTHON`.
|
| 118 |
+
|
| 119 |
+
The evaluator preserves its original trial-count and seed schedule. A study
|
| 120 |
+
requiring a different schedule must configure and validate that protocol before
|
| 121 |
+
collecting results. No benchmark score is provided by this release.
|
| 122 |
+
|
| 123 |
+
First inspect the command without allocating GPUs:
|
| 124 |
+
|
| 125 |
+
```bash
|
| 126 |
+
"$TASKANCHOR_PI05_PYTHON" -m taskanchor \
|
| 127 |
+
--benchmark robomemarena --model pi05 --stage eval \
|
| 128 |
+
--benchmark-root "$TASKANCHOR_ROBOMEM_BENCHMARK" \
|
| 129 |
+
--python "$TASKANCHOR_PI05_PYTHON" --dry-run -- \
|
| 130 |
+
--checkpoint "$TASKANCHOR_MODEL/50000" \
|
| 131 |
+
--output "$TASKANCHOR_CODE/reports/robomem50k_evaluation" \
|
| 132 |
+
--workers 8 --base-port 8800
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
Remove `--dry-run` to execute the supported task/trial configuration. The output directory
|
| 136 |
+
must not already exist. `--workers 8` uses GPUs 0–7, with one model server per
|
| 137 |
+
GPU. `--workers 1` uses GPU 0 and retains the same task/seed coverage, taking
|
| 138 |
+
longer. The current scheduler assigns physical GPU indices itself; an outer
|
| 139 |
+
`CUDA_VISIBLE_DEVICES` remapping is not an arbitrary GPU-selection interface.
|
| 140 |
+
|
| 141 |
+
Inspect `final_report.json`, `status.json`, and
|
| 142 |
+
per-task logs for complete coverage and runtime failures. Per-task folders
|
| 143 |
+
contain benchmark summaries/videos and a `progress.jsonl` trace containing
|
| 144 |
+
both raw and discretized predictions. Initialization failures are recorded;
|
| 145 |
+
Task25 may advance its recorded candidate list only before any policy action.
|
| 146 |
+
Failed policies are not discarded or retried as successful replacements.
|
| 147 |
+
|
| 148 |
+
## 4. Exact evaluation contract
|
| 149 |
+
|
| 150 |
+
| Setting | Released 50k protocol |
|
| 151 |
+
| --- | --- |
|
| 152 |
+
| Backbone | π0.5, Gemma 2B VLM / Gemma 300M action expert |
|
| 153 |
+
| Observations | Main and wrist views; 224×224 model inputs |
|
| 154 |
+
| Adapter insertion | Main view only; `current + offset`, 1152→512→1152 |
|
| 155 |
+
| Temporal module | 8 layers, width 512, 8 heads; dropout 0.10, DropPath 0 |
|
| 156 |
+
| History frame | 4×4 = 16 SigLIP tokens |
|
| 157 |
+
| Memory protocol | Separate permanent frame0 + up to 191 history frames |
|
| 158 |
+
| History stride | 10 environment steps |
|
| 159 |
+
| Action chunk / replan | Predict 20; execute 10, then replan |
|
| 160 |
+
| Progress | Continuous prediction mapped to nearest valid task stage |
|
| 161 |
+
| Language | Fixed full-task rewrite 0 + `Progress:%.2f` |
|
| 162 |
+
| Task coverage | Tasks 1–26; trial count and accepted seeds must be checked against the research protocol |
|
| 163 |
+
| Episode budget | 2500 steps |
|
| 164 |
+
| Rendering | OSMesa |
|
| 165 |
+
|
| 166 |
+
There is no oracle progress input. For a four-stage task, conditioning values
|
| 167 |
+
are 0.00, 0.25, 0.50, and 0.75. The separate frame0 is part of this model's
|
| 168 |
+
training and evaluation contract; replacing it with FIFO frame0 changes the
|
| 169 |
+
experiment even if the weight keys still load.
|
| 170 |
+
|
| 171 |
+
This package provides runnable model and evaluation code. It does not publish
|
| 172 |
+
benchmark scores or assert that a new evaluation with the sample size required
|
| 173 |
+
by a paper has been completed. Record and report the actual task/seed coverage
|
| 174 |
+
of each evaluation run.
|
| 175 |
+
|
| 176 |
+
## 5. Extract training features
|
| 177 |
+
|
| 178 |
+
Training starts from the converted LeRobot dataset, not the public 50k
|
| 179 |
+
checkpoint. The dataset must contain embedded main/wrist RGB images, real
|
| 180 |
+
states/actions, and the original subtask/seed metadata. This package does not
|
| 181 |
+
include the raw-HDF5-to-LeRobot converter or the dataset. Preserve the original
|
| 182 |
+
conversion and ordering when reproducing training. The reference manifest has
|
| 183 |
+
2600 full-task groups, 2,811,755 frames, and a maximum 1918 frames per full task.
|
| 184 |
+
Evaluation does not need any of these training files.
|
| 185 |
+
|
| 186 |
+
Provide the original pretrained `pi05_base/params` and data locations:
|
| 187 |
+
|
| 188 |
+
```bash
|
| 189 |
+
export TASKANCHOR_ROBOMEM_DATASET=/absolute/path/to/robomemarena_pi05_task_all_with_subtask_seed
|
| 190 |
+
export TASKANCHOR_BASE_PARAMS=/absolute/path/to/pi05_base/params
|
| 191 |
+
export TASKANCHOR_FEATURE_CACHE=/absolute/path/to/new_robomem_spatial_cache
|
| 192 |
+
export TASKANCHOR_CHECKPOINT_ROOT=/absolute/path/to/new_training_checkpoints
|
| 193 |
+
|
| 194 |
+
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
| 195 |
+
"$TASKANCHOR_PI05_PYTHON" -m taskanchor \
|
| 196 |
+
--benchmark robomemarena --model pi05 --stage extract \
|
| 197 |
+
--python "$TASKANCHOR_PI05_PYTHON" -- \
|
| 198 |
+
--dataset "$TASKANCHOR_ROBOMEM_DATASET" \
|
| 199 |
+
--base-params "$TASKANCHOR_BASE_PARAMS" \
|
| 200 |
+
--cache "$TASKANCHOR_FEATURE_CACHE" --batch-size 256 --workers 8
|
| 201 |
+
```
|
| 202 |
+
|
| 203 |
+
Extraction validates every completed feature file and writes `VERIFIED.json`.
|
| 204 |
+
It stores FP16 values derived from BF16 main-camera SigLIP features, using the
|
| 205 |
+
recorded 16×16→4×4 bilinear pooling without antialiasing. Existing cache
|
| 206 |
+
provenance is checked strictly, including source paths; see [PORTABILITY](PORTABILITY.md)
|
| 207 |
+
before relocating an old cache.
|
| 208 |
+
|
| 209 |
+
## 6. Train one run with the 5k freeze prefix
|
| 210 |
+
|
| 211 |
+
Use a new run name. The schedule is 55,000 total updates, with 5,000
|
| 212 |
+
adapter-only updates followed by 50,000 joint updates. The released checkpoint
|
| 213 |
+
is taken at update 50,000: 5,000 adapter-only + 45,000 joint updates. Setting
|
| 214 |
+
`--steps 50000` would change the learning-rate schedule and is not the recorded
|
| 215 |
+
experiment.
|
| 216 |
+
|
| 217 |
+
```bash
|
| 218 |
+
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
| 219 |
+
"$TASKANCHOR_PI05_PYTHON" -m taskanchor \
|
| 220 |
+
--benchmark robomemarena --model pi05 --stage train \
|
| 221 |
+
--python "$TASKANCHOR_PI05_PYTHON" -- \
|
| 222 |
+
--dataset "$TASKANCHOR_ROBOMEM_DATASET" \
|
| 223 |
+
--base-params "$TASKANCHOR_BASE_PARAMS" \
|
| 224 |
+
--cache "$TASKANCHOR_FEATURE_CACHE" \
|
| 225 |
+
--assets-dir "$TASKANCHOR_CODE/assets" \
|
| 226 |
+
--checkpoint-root "$TASKANCHOR_CHECKPOINT_ROOT" \
|
| 227 |
+
--run-name robomem50k_reproduction \
|
| 228 |
+
--steps 55000 --freeze-steps 5000 --batch-size 256 \
|
| 229 |
+
--action-horizon 20 --execution-horizon 10 \
|
| 230 |
+
--fsdp-devices 8 --workers 8 --save-interval 1000 --log-interval 20
|
| 231 |
+
```
|
| 232 |
+
|
| 233 |
+
The measured run restarted after a complete 10,000-step checkpoint to enable
|
| 234 |
+
the shared-memory loader. To mirror that history, retain the **55k schedule**
|
| 235 |
+
for the first leg, stop the first process after that checkpoint is complete,
|
| 236 |
+
then rerun the exact command above with:
|
| 237 |
+
|
| 238 |
+
```text
|
| 239 |
+
--resume --resume-step 10000 --shared-memory-loader --device-prefetch 2 --prefetch-factor 2
|
| 240 |
+
```
|
| 241 |
+
|
| 242 |
+
Do not launch the resumed command while the original process is still running.
|
| 243 |
+
The recorded restart arguments are retained in
|
| 244 |
+
`evidence/pipeline_restart_10000.json`. The sampler restarts with seed
|
| 245 |
+
`42 + restored_step`; it does not restore an exact input cursor. A fresh run
|
| 246 |
+
therefore is not promised to reproduce identical parameter bytes.
|
| 247 |
+
|
| 248 |
+
The training output is
|
| 249 |
+
`$TASKANCHOR_CHECKPOINT_ROOT/pi05_robomemarena_memory_v5_main/robomem50k_reproduction/`.
|
| 250 |
+
It contains the parent `recipe.json` and numbered checkpoints. Training saves
|
| 251 |
+
every 1000 updates and retains 5000-step milestones under the original manager
|
| 252 |
+
policy. Keep optimizer/train-state files for resuming; the public inference
|
| 253 |
+
package intentionally omits them and cannot resume the original 50k optimizer.
|
| 254 |
+
|
| 255 |
+
| Training setting | Value |
|
| 256 |
+
| --- | --- |
|
| 257 |
+
| Global batch / FSDP devices | 256 / 8 |
|
| 258 |
+
| Visual encoder | Frozen throughout; image projection head joins after 5k |
|
| 259 |
+
| Optimizer | AdamW, β=(0.9, 0.95), global gradient clip 1.0 |
|
| 260 |
+
| Adapter learning rate | 2.5e-5 for first 5k; cosine decay afterward |
|
| 261 |
+
| Backbone / image-head LR after unfreezing | 2.5e-5 / 1e-5, cosine decay |
|
| 262 |
+
| Final cosine multiplier | 0.1 |
|
| 263 |
+
| Weight decay | Adapter 0.05; backbone/image head 0 |
|
| 264 |
+
| Progress target | `round(subtask_id / number_of_subtasks, 2)` |
|
| 265 |
+
| Loss | Flow action loss + 0.01 × (100 × progress MSE + 1 − Pearson) |
|
| 266 |
+
|
| 267 |
+
Frozen backbone Adam states do not advance during the first 5000 updates.
|
| 268 |
+
Neither LoRA nor a separate planner is introduced. Numerical implementation
|
| 269 |
+
and optimizer groups are in `taskanchor/pi05/taskanchor.py` and
|
| 270 |
+
`taskanchor/pi05/training.py`.
|
| 271 |
+
|
| 272 |
+
## 7. Validation scope
|
| 273 |
+
|
| 274 |
+
```bash
|
| 275 |
+
cd "$TASKANCHOR_CODE"
|
| 276 |
+
python -m unittest discover -s tests -p test_portability.py
|
| 277 |
+
```
|
| 278 |
+
|
| 279 |
+
The packaging checks passed source parsing, CPU imports, path tests, and
|
| 280 |
+
unchanged numerical source checks. Adapter/TTT/training-loop/history core
|
| 281 |
+
files remain byte-identical to the previously tested release. Data sampling,
|
| 282 |
+
online history, action sampling, and extraction computations are unchanged.
|
| 283 |
+
No new training or GPU rollout was started during this packaging pass.
|
code/evidence/environment_pi05.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"python": "3.11.13",
|
| 3 |
+
"jax": "0.5.3",
|
| 4 |
+
"jaxlib": "0.5.3",
|
| 5 |
+
"flax": "0.10.2",
|
| 6 |
+
"optax": "0.2.4",
|
| 7 |
+
"orbax-checkpoint": "0.11.13",
|
| 8 |
+
"numpy": "1.26.4",
|
| 9 |
+
"torch": "2.7.1",
|
| 10 |
+
"transformers": "4.53.2",
|
| 11 |
+
"lerobot": "0.1.0",
|
| 12 |
+
"pyarrow": "20.0.0",
|
| 13 |
+
"Pillow": "11.2.1",
|
| 14 |
+
"sentencepiece": "0.2.0",
|
| 15 |
+
"ruff": "0.11.12",
|
| 16 |
+
"pytest": "8.3.5",
|
| 17 |
+
"openpi": "0.1.0",
|
| 18 |
+
"openpi-client": "0.1.0"
|
| 19 |
+
}
|
code/evidence/packaging_checks.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ast_files": 107,
|
| 3 |
+
"portability_tests": {
|
| 4 |
+
"passed": 3
|
| 5 |
+
},
|
| 6 |
+
"adapter_and_history_cpu_tests": {
|
| 7 |
+
"passed": 6
|
| 8 |
+
},
|
| 9 |
+
"no_gpu_or_rollouts_in_this_packaging_pass": true,
|
| 10 |
+
"numerical_model_logic_modified": false,
|
| 11 |
+
"changes": [
|
| 12 |
+
"portable asset and data paths",
|
| 13 |
+
"documentation and license notices",
|
| 14 |
+
"packaging"
|
| 15 |
+
],
|
| 16 |
+
"training_data_requirement": "Previously converted LeRobot dataset with subtask/seed metadata; raw conversion not included."
|
| 17 |
+
}
|
code/evidence/pipeline_restart_10000.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"command": [
|
| 3 |
+
"<OPENPI>/.venv/bin/python",
|
| 4 |
+
"-u",
|
| 5 |
+
"scripts/train_robomem_memory.py",
|
| 6 |
+
"--run-name",
|
| 7 |
+
"main_v5_spatial4x4_h191_a20_exec10_bs256_5k50k",
|
| 8 |
+
"--batch-size",
|
| 9 |
+
"256",
|
| 10 |
+
"--steps",
|
| 11 |
+
"55000",
|
| 12 |
+
"--freeze-steps",
|
| 13 |
+
"5000",
|
| 14 |
+
"--action-horizon",
|
| 15 |
+
"20",
|
| 16 |
+
"--execution-horizon",
|
| 17 |
+
"10",
|
| 18 |
+
"--fsdp-devices",
|
| 19 |
+
"8",
|
| 20 |
+
"--workers",
|
| 21 |
+
"8",
|
| 22 |
+
"--resume",
|
| 23 |
+
"--resume-step",
|
| 24 |
+
"10000",
|
| 25 |
+
"--shared-memory-loader",
|
| 26 |
+
"--device-prefetch",
|
| 27 |
+
"2",
|
| 28 |
+
"--prefetch-factor",
|
| 29 |
+
"2"
|
| 30 |
+
],
|
| 31 |
+
"source_sha256": {
|
| 32 |
+
"scripts/train_robomem_memory.py": "218d6f44812d0043438b3c515bf8ca4d8e1fdd34a86dac1d6431398fb7aef9fb",
|
| 33 |
+
"src/openpi/training/robomem_memory_loader.py": "8924b7cb7146ae8f5a46230050f18938fc83338ab5382efb5cbe5e2ad6b9d038",
|
| 34 |
+
"scripts/test_robomem_memory_loader.py": "80f421f909c0d3373171dccd013c46fbf8b2b9a67597b81a6198e6163cdd2ba7",
|
| 35 |
+
"scripts/resume_robomem_pipeline_at_10k.py": "0d9dbfa45ce3a279878a203ff8ca147ff1189dfdaf166e137ac27459951165ea"
|
| 36 |
+
}
|
| 37 |
+
}
|
code/evidence/robomem50k_adapter_numerical_parity.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"checkpoint": "<RUN>/50000/params",
|
| 3 |
+
"adapter_parameters": 26930049,
|
| 4 |
+
"adapter_arrays_sha256": "52573dc7d0ac6fb4373382bed7430ca1c0f30af1474cceab42c785ab6bae8a10",
|
| 5 |
+
"cases": [
|
| 6 |
+
{
|
| 7 |
+
"valid_history_frames": 0,
|
| 8 |
+
"refined_exact": true,
|
| 9 |
+
"progress_exact": true
|
| 10 |
+
},
|
| 11 |
+
{
|
| 12 |
+
"valid_history_frames": 17,
|
| 13 |
+
"refined_exact": true,
|
| 14 |
+
"progress_exact": true
|
| 15 |
+
},
|
| 16 |
+
{
|
| 17 |
+
"valid_history_frames": 191,
|
| 18 |
+
"refined_exact": true,
|
| 19 |
+
"progress_exact": true
|
| 20 |
+
}
|
| 21 |
+
],
|
| 22 |
+
"device": "CPU",
|
| 23 |
+
"limitation": "Synthetic visual features; full VLA action inference and closed-loop episodes not rerun"
|
| 24 |
+
}
|
code/evidence/task25_seed_candidates.json
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"candidates": [
|
| 3 |
+
50,
|
| 4 |
+
51,
|
| 5 |
+
52,
|
| 6 |
+
53,
|
| 7 |
+
54,
|
| 8 |
+
55,
|
| 9 |
+
56,
|
| 10 |
+
57,
|
| 11 |
+
58,
|
| 12 |
+
59,
|
| 13 |
+
60,
|
| 14 |
+
61,
|
| 15 |
+
62,
|
| 16 |
+
63,
|
| 17 |
+
64,
|
| 18 |
+
65,
|
| 19 |
+
66,
|
| 20 |
+
67,
|
| 21 |
+
68,
|
| 22 |
+
69,
|
| 23 |
+
70,
|
| 24 |
+
71,
|
| 25 |
+
72,
|
| 26 |
+
73,
|
| 27 |
+
74,
|
| 28 |
+
75,
|
| 29 |
+
76,
|
| 30 |
+
77,
|
| 31 |
+
78,
|
| 32 |
+
79,
|
| 33 |
+
80,
|
| 34 |
+
81,
|
| 35 |
+
82,
|
| 36 |
+
83,
|
| 37 |
+
84,
|
| 38 |
+
85,
|
| 39 |
+
86,
|
| 40 |
+
87,
|
| 41 |
+
88,
|
| 42 |
+
89,
|
| 43 |
+
90,
|
| 44 |
+
91,
|
| 45 |
+
92,
|
| 46 |
+
93,
|
| 47 |
+
94,
|
| 48 |
+
95,
|
| 49 |
+
96,
|
| 50 |
+
97,
|
| 51 |
+
98,
|
| 52 |
+
99,
|
| 53 |
+
216,
|
| 54 |
+
241,
|
| 55 |
+
247,
|
| 56 |
+
248,
|
| 57 |
+
250,
|
| 58 |
+
252,
|
| 59 |
+
253,
|
| 60 |
+
257,
|
| 61 |
+
258,
|
| 62 |
+
262,
|
| 63 |
+
265,
|
| 64 |
+
266,
|
| 65 |
+
267,
|
| 66 |
+
269,
|
| 67 |
+
271,
|
| 68 |
+
279,
|
| 69 |
+
280,
|
| 70 |
+
283,
|
| 71 |
+
289,
|
| 72 |
+
290,
|
| 73 |
+
293,
|
| 74 |
+
294,
|
| 75 |
+
295,
|
| 76 |
+
296,
|
| 77 |
+
300,
|
| 78 |
+
303,
|
| 79 |
+
308,
|
| 80 |
+
310,
|
| 81 |
+
313,
|
| 82 |
+
315,
|
| 83 |
+
316,
|
| 84 |
+
318,
|
| 85 |
+
322,
|
| 86 |
+
325,
|
| 87 |
+
326,
|
| 88 |
+
328,
|
| 89 |
+
329,
|
| 90 |
+
332,
|
| 91 |
+
333,
|
| 92 |
+
335,
|
| 93 |
+
336,
|
| 94 |
+
337,
|
| 95 |
+
338,
|
| 96 |
+
339,
|
| 97 |
+
343,
|
| 98 |
+
344,
|
| 99 |
+
345,
|
| 100 |
+
346,
|
| 101 |
+
350,
|
| 102 |
+
352,
|
| 103 |
+
360,
|
| 104 |
+
361,
|
| 105 |
+
362,
|
| 106 |
+
364,
|
| 107 |
+
367,
|
| 108 |
+
368,
|
| 109 |
+
369,
|
| 110 |
+
370,
|
| 111 |
+
371,
|
| 112 |
+
372,
|
| 113 |
+
373,
|
| 114 |
+
374,
|
| 115 |
+
375,
|
| 116 |
+
377,
|
| 117 |
+
378,
|
| 118 |
+
381,
|
| 119 |
+
382,
|
| 120 |
+
383,
|
| 121 |
+
384,
|
| 122 |
+
385,
|
| 123 |
+
386,
|
| 124 |
+
387,
|
| 125 |
+
388,
|
| 126 |
+
389,
|
| 127 |
+
390,
|
| 128 |
+
391,
|
| 129 |
+
392,
|
| 130 |
+
393,
|
| 131 |
+
394,
|
| 132 |
+
395,
|
| 133 |
+
396,
|
| 134 |
+
397,
|
| 135 |
+
398,
|
| 136 |
+
399,
|
| 137 |
+
401,
|
| 138 |
+
402,
|
| 139 |
+
403,
|
| 140 |
+
404,
|
| 141 |
+
405,
|
| 142 |
+
406,
|
| 143 |
+
407,
|
| 144 |
+
408,
|
| 145 |
+
409,
|
| 146 |
+
410,
|
| 147 |
+
411,
|
| 148 |
+
413,
|
| 149 |
+
414,
|
| 150 |
+
415,
|
| 151 |
+
416,
|
| 152 |
+
418,
|
| 153 |
+
419,
|
| 154 |
+
420,
|
| 155 |
+
421,
|
| 156 |
+
422,
|
| 157 |
+
423,
|
| 158 |
+
424,
|
| 159 |
+
425,
|
| 160 |
+
426,
|
| 161 |
+
427,
|
| 162 |
+
428,
|
| 163 |
+
429,
|
| 164 |
+
430,
|
| 165 |
+
431,
|
| 166 |
+
432,
|
| 167 |
+
433,
|
| 168 |
+
434,
|
| 169 |
+
435,
|
| 170 |
+
436,
|
| 171 |
+
437,
|
| 172 |
+
438,
|
| 173 |
+
440,
|
| 174 |
+
442,
|
| 175 |
+
446,
|
| 176 |
+
447,
|
| 177 |
+
448,
|
| 178 |
+
449,
|
| 179 |
+
450,
|
| 180 |
+
451,
|
| 181 |
+
453,
|
| 182 |
+
454,
|
| 183 |
+
455,
|
| 184 |
+
457,
|
| 185 |
+
458,
|
| 186 |
+
459,
|
| 187 |
+
460,
|
| 188 |
+
461,
|
| 189 |
+
462,
|
| 190 |
+
463,
|
| 191 |
+
465,
|
| 192 |
+
466,
|
| 193 |
+
468,
|
| 194 |
+
469,
|
| 195 |
+
470,
|
| 196 |
+
472,
|
| 197 |
+
473,
|
| 198 |
+
474,
|
| 199 |
+
475,
|
| 200 |
+
476,
|
| 201 |
+
477,
|
| 202 |
+
478
|
| 203 |
+
],
|
| 204 |
+
"max_attempts": 200,
|
| 205 |
+
"selection": "ascending from 50, excluding all training seed_rank values",
|
| 206 |
+
"training_seed_min": 100,
|
| 207 |
+
"training_seed_max": 813
|
| 208 |
+
}
|
code/pi05/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
code/pi05/LICENSE_GEMMA.txt
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Gemma Terms of Use
|
| 2 |
+
|
| 3 |
+
Last modified: February 21, 2024
|
| 4 |
+
|
| 5 |
+
By using, reproducing, modifying, distributing, performing or displaying any portion or element of Gemma, Model Derivatives including via any Hosted Service, (each as defined below) (collectively, the "Gemma Services") or otherwise accepting the terms of this Agreement, you agree to be bound by this Agreement.
|
| 6 |
+
|
| 7 |
+
Section 1: DEFINITIONS
|
| 8 |
+
1.1 Definitions
|
| 9 |
+
(a) "Agreement" or "Gemma Terms of Use" means these terms and conditions that govern the use, reproduction, Distribution or modification of the Gemma Services and any terms and conditions incorporated by reference.
|
| 10 |
+
|
| 11 |
+
(b) "Distribution" or "Distribute" means any transmission, publication, or other sharing of Gemma or Model Derivatives to a third party, including by providing or making Gemma or its functionality available as a hosted service via API, web access, or any other electronic or remote means ("Hosted Service").
|
| 12 |
+
|
| 13 |
+
(c) "Gemma" means the set of machine learning language models, trained model weights and parameters identified at ai.google.dev/gemma, regardless of the source that you obtained it from.
|
| 14 |
+
|
| 15 |
+
(d) "Google" means Google LLC.
|
| 16 |
+
|
| 17 |
+
(e) "Model Derivatives" means all (i) modifications to Gemma, (ii) works based on Gemma, or (iii) any other machine learning model which is created by transfer of patterns of the weights, parameters, operations, or Output of Gemma, to that model in order to cause that model to perform similarly to Gemma, including distillation methods that use intermediate data representations or methods based on the generation of synthetic data Outputs by Gemma for training that model. For clarity, Outputs are not deemed Model Derivatives.
|
| 18 |
+
|
| 19 |
+
(f) "Output" means the information content output of Gemma or a Model Derivative that results from operating or otherwise using Gemma or the Model Derivative, including via a Hosted Service.
|
| 20 |
+
|
| 21 |
+
1.2
|
| 22 |
+
As used in this Agreement, "including" means "including without limitation".
|
| 23 |
+
|
| 24 |
+
Section 2: ELIGIBILITY AND USAGE
|
| 25 |
+
2.1 Eligibility
|
| 26 |
+
You represent and warrant that you have the legal capacity to enter into this Agreement (including being of sufficient age of consent). If you are accessing or using any of the Gemma Services for or on behalf of a legal entity, (a) you are entering into this Agreement on behalf of yourself and that legal entity, (b) you represent and warrant that you have the authority to act on behalf of and bind that entity to this Agreement and (c) references to "you" or "your" in the remainder of this Agreement refers to both you (as an individual) and that entity.
|
| 27 |
+
|
| 28 |
+
2.2 Use
|
| 29 |
+
You may use, reproduce, modify, Distribute, perform or display any of the Gemma Services only in accordance with the terms of this Agreement, and must not violate (or encourage or permit anyone else to violate) any term of this Agreement.
|
| 30 |
+
|
| 31 |
+
Section 3: DISTRIBUTION AND RESTRICTIONS
|
| 32 |
+
3.1 Distribution and Redistribution
|
| 33 |
+
You may reproduce or Distribute copies of Gemma or Model Derivatives if you meet all of the following conditions:
|
| 34 |
+
|
| 35 |
+
You must include the use restrictions referenced in Section 3.2 as an enforceable provision in any agreement (e.g., license agreement, terms of use, etc.) governing the use and/or distribution of Gemma or Model Derivatives and you must provide notice to subsequent users you Distribute to that Gemma or Model Derivatives are subject to the use restrictions in Section 3.2.
|
| 36 |
+
You must provide all third party recipients of Gemma or Model Derivatives a copy of this Agreement.
|
| 37 |
+
You must cause any modified files to carry prominent notices stating that you modified the files.
|
| 38 |
+
All Distributions (other than through a Hosted Service) must be accompanied by a "Notice" text file that contains the following notice: "Gemma is provided under and subject to the Gemma Terms of Use found at ai.google.dev/gemma/terms".
|
| 39 |
+
You may add your own intellectual property statement to your modifications and, except as set forth in this Section, may provide additional or different terms and conditions for use, reproduction, or Distribution of your modifications, or for any such Model Derivatives as a whole, provided your use, reproduction, modification, Distribution, performance, and display of Gemma otherwise complies with the terms and conditions of this Agreement. Any additional or different terms and conditions you impose must not conflict with the terms of this Agreement.
|
| 40 |
+
|
| 41 |
+
3.2 Use Restrictions
|
| 42 |
+
You must not use any of the Gemma Services:
|
| 43 |
+
|
| 44 |
+
for the restricted uses set forth in the Gemma Prohibited Use Policy at ai.google.dev/gemma/prohibited_use_policy ("Prohibited Use Policy"), which is hereby incorporated by reference into this Agreement; or
|
| 45 |
+
in violation of applicable laws and regulations.
|
| 46 |
+
To the maximum extent permitted by law, Google reserves the right to restrict (remotely or otherwise) usage of any of the Gemma Services that Google reasonably believes are in violation of this Agreement.
|
| 47 |
+
|
| 48 |
+
3.3 Generated Output
|
| 49 |
+
Google claims no rights in Outputs you generate using Gemma. You and your users are solely responsible for Outputs and their subsequent uses.
|
| 50 |
+
|
| 51 |
+
Section 4: ADDITIONAL PROVISIONS
|
| 52 |
+
4.1 Updates
|
| 53 |
+
Google may update Gemma from time to time, and you must make reasonable efforts to use the latest version of Gemma.
|
| 54 |
+
|
| 55 |
+
4.2 Trademarks
|
| 56 |
+
Nothing in this Agreement grants you any rights to use Google's trademarks, trade names, logos or to otherwise suggest endorsement or misrepresent the relationship between you and Google. Google reserves any rights not expressly granted herein.
|
| 57 |
+
|
| 58 |
+
4.3 DISCLAIMER OF WARRANTY
|
| 59 |
+
UNLESS REQUIRED BY APPLICABLE LAW, THE GEMMA SERVICES, AND OUTPUTS, ARE PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING, REPRODUCING, MODIFYING, PERFORMING, DISPLAYING OR OR DISTRIBUTING ANY OF THE GEMMA SERVICES OR OUTPUTS AND ASSUME ANY AND ALL RISKS ASSOCIATED WITH YOUR USE OR DISTRIBUTION OF ANY OF THE GEMMA SERVICES OR OUTPUTS AND YOUR EXERCISE OF RIGHTS AND PERMISSIONS UNDER THIS AGREEMENT.
|
| 60 |
+
|
| 61 |
+
4.4 LIMITATION OF LIABILITY
|
| 62 |
+
TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY, CONTRACT, OR OTHERWISE, UNLESS REQUIRED BY APPLICABLE LAW, SHALL GOOGLE OR ITS AFFILIATES BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY, CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR LOST PROFITS OF ANY KIND ARISING FROM THIS AGREEMENT OR RELATED TO, ANY OF THE GEMMA SERVICES OR OUTPUTS EVEN IF GOOGLE OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
| 63 |
+
|
| 64 |
+
4.5 Term, Termination, and Survival
|
| 65 |
+
The term of this Agreement will commence upon your acceptance of this Agreement (including acceptance by your use, modification, or Distribution, reproduction, performance or display of any portion or element of the Gemma Services) and will continue in full force and effect until terminated in accordance with the terms of this Agreement. Google may terminate this Agreement if you are in breach of any term of this Agreement. Upon termination of this Agreement, you must delete and cease use and Distribution of all copies of Gemma and Model Derivatives in your possession or control. Sections 1, 2.1, 3.3, 4.2 to 4.9 shall survive the termination of this Agreement.
|
| 66 |
+
|
| 67 |
+
4.6 Governing Law and Jurisdiction
|
| 68 |
+
This Agreement will be governed by the laws of the State of California without regard to choice of law principles. The UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement. The state and federal courts of Santa Clara County, California shall have exclusive jurisdiction of any dispute arising out of this Agreement.
|
| 69 |
+
|
| 70 |
+
4.7 Severability
|
| 71 |
+
If any provision of this Agreement is held to be invalid, illegal or unenforceable, the remaining provisions shall be unaffected thereby and remain valid as if such provision had not been set forth herein.
|
| 72 |
+
|
| 73 |
+
4.8 Entire Agreement
|
| 74 |
+
This Agreement states all the terms agreed between the parties and supersedes all other agreements between the parties as of the date of acceptance relating to its subject matter.
|
| 75 |
+
|
| 76 |
+
4.9 No Waiver
|
| 77 |
+
Google will not be treated as having waived any rights by not exercising (or delaying the exercise of) any rights under this Agreement.
|
code/pi05/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. |
|
code/pi05/packages/openpi-client/pyproject.toml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "openpi-client"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
requires-python = ">=3.7"
|
| 5 |
+
dependencies = [
|
| 6 |
+
"dm-tree>=0.1.8",
|
| 7 |
+
"msgpack>=1.0.5",
|
| 8 |
+
"numpy>=1.22.4,<2.0.0",
|
| 9 |
+
"pillow>=9.0.0",
|
| 10 |
+
"websockets>=11.0",
|
| 11 |
+
]
|
| 12 |
+
|
| 13 |
+
[build-system]
|
| 14 |
+
requires = ["hatchling"]
|
| 15 |
+
build-backend = "hatchling.build"
|
| 16 |
+
|
| 17 |
+
[dependency-groups]
|
| 18 |
+
dev = ["pytest>=8.3.4"]
|
| 19 |
+
|
| 20 |
+
[tool.ruff]
|
| 21 |
+
line-length = 120
|
| 22 |
+
target-version = "py37"
|
code/pi05/packages/openpi-client/src/openpi_client/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
__version__ = "0.1.0"
|
code/pi05/packages/openpi-client/src/openpi_client/action_chunk_broker.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import tree
|
| 5 |
+
from typing_extensions import override
|
| 6 |
+
|
| 7 |
+
from openpi_client import base_policy as _base_policy
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class ActionChunkBroker(_base_policy.BasePolicy):
|
| 11 |
+
"""Wraps a policy to return action chunks one-at-a-time.
|
| 12 |
+
|
| 13 |
+
Assumes that the first dimension of all action fields is the chunk size.
|
| 14 |
+
|
| 15 |
+
A new inference call to the inner policy is only made when the current
|
| 16 |
+
list of chunks is exhausted.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
def __init__(self, policy: _base_policy.BasePolicy, action_horizon: int):
|
| 20 |
+
self._policy = policy
|
| 21 |
+
self._action_horizon = action_horizon
|
| 22 |
+
self._cur_step: int = 0
|
| 23 |
+
|
| 24 |
+
self._last_results: Dict[str, np.ndarray] | None = None
|
| 25 |
+
|
| 26 |
+
@override
|
| 27 |
+
def infer(self, obs: Dict) -> Dict: # noqa: UP006
|
| 28 |
+
if self._last_results is None:
|
| 29 |
+
self._last_results = self._policy.infer(obs)
|
| 30 |
+
self._cur_step = 0
|
| 31 |
+
|
| 32 |
+
def slicer(x):
|
| 33 |
+
if isinstance(x, np.ndarray):
|
| 34 |
+
return x[self._cur_step, ...]
|
| 35 |
+
else:
|
| 36 |
+
return x
|
| 37 |
+
|
| 38 |
+
results = tree.map_structure(slicer, self._last_results)
|
| 39 |
+
self._cur_step += 1
|
| 40 |
+
|
| 41 |
+
if self._cur_step >= self._action_horizon:
|
| 42 |
+
self._last_results = None
|
| 43 |
+
|
| 44 |
+
return results
|
| 45 |
+
|
| 46 |
+
@override
|
| 47 |
+
def reset(self) -> None:
|
| 48 |
+
self._policy.reset()
|
| 49 |
+
self._last_results = None
|
| 50 |
+
self._cur_step = 0
|
code/pi05/packages/openpi-client/src/openpi_client/base_policy.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
from typing import Dict
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class BasePolicy(abc.ABC):
|
| 6 |
+
@abc.abstractmethod
|
| 7 |
+
def infer(self, obs: Dict) -> Dict:
|
| 8 |
+
"""Infer actions from observations."""
|
| 9 |
+
|
| 10 |
+
def reset(self) -> None:
|
| 11 |
+
"""Reset the policy to its initial state."""
|
| 12 |
+
pass
|
code/pi05/packages/openpi-client/src/openpi_client/image_tools.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from PIL import Image
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def convert_to_uint8(img: np.ndarray) -> np.ndarray:
|
| 6 |
+
"""Converts an image to uint8 if it is a float image.
|
| 7 |
+
|
| 8 |
+
This is important for reducing the size of the image when sending it over the network.
|
| 9 |
+
"""
|
| 10 |
+
if np.issubdtype(img.dtype, np.floating):
|
| 11 |
+
img = (255 * img).astype(np.uint8)
|
| 12 |
+
return img
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def resize_with_pad(images: np.ndarray, height: int, width: int, method=Image.BILINEAR) -> np.ndarray:
|
| 16 |
+
"""Replicates tf.image.resize_with_pad for multiple images using PIL. Resizes a batch of images to a target height.
|
| 17 |
+
|
| 18 |
+
Args:
|
| 19 |
+
images: A batch of images in [..., height, width, channel] format.
|
| 20 |
+
height: The target height of the image.
|
| 21 |
+
width: The target width of the image.
|
| 22 |
+
method: The interpolation method to use. Default is bilinear.
|
| 23 |
+
|
| 24 |
+
Returns:
|
| 25 |
+
The resized images in [..., height, width, channel].
|
| 26 |
+
"""
|
| 27 |
+
# If the images are already the correct size, return them as is.
|
| 28 |
+
if images.shape[-3:-1] == (height, width):
|
| 29 |
+
return images
|
| 30 |
+
|
| 31 |
+
original_shape = images.shape
|
| 32 |
+
|
| 33 |
+
images = images.reshape(-1, *original_shape[-3:])
|
| 34 |
+
resized = np.stack([_resize_with_pad_pil(Image.fromarray(im), height, width, method=method) for im in images])
|
| 35 |
+
return resized.reshape(*original_shape[:-3], *resized.shape[-3:])
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _resize_with_pad_pil(image: Image.Image, height: int, width: int, method: int) -> Image.Image:
|
| 39 |
+
"""Replicates tf.image.resize_with_pad for one image using PIL. Resizes an image to a target height and
|
| 40 |
+
width without distortion by padding with zeros.
|
| 41 |
+
|
| 42 |
+
Unlike the jax version, note that PIL uses [width, height, channel] ordering instead of [batch, h, w, c].
|
| 43 |
+
"""
|
| 44 |
+
cur_width, cur_height = image.size
|
| 45 |
+
if cur_width == width and cur_height == height:
|
| 46 |
+
return image # No need to resize if the image is already the correct size.
|
| 47 |
+
|
| 48 |
+
ratio = max(cur_width / width, cur_height / height)
|
| 49 |
+
resized_height = int(cur_height / ratio)
|
| 50 |
+
resized_width = int(cur_width / ratio)
|
| 51 |
+
resized_image = image.resize((resized_width, resized_height), resample=method)
|
| 52 |
+
|
| 53 |
+
zero_image = Image.new(resized_image.mode, (width, height), 0)
|
| 54 |
+
pad_height = max(0, int((height - resized_height) / 2))
|
| 55 |
+
pad_width = max(0, int((width - resized_width) / 2))
|
| 56 |
+
zero_image.paste(resized_image, (pad_width, pad_height))
|
| 57 |
+
assert zero_image.size == (width, height)
|
| 58 |
+
return zero_image
|
code/pi05/packages/openpi-client/src/openpi_client/image_tools_test.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
import openpi_client.image_tools as image_tools
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def test_resize_with_pad_shapes():
|
| 7 |
+
# Test case 1: Resize image with larger dimensions
|
| 8 |
+
images = np.zeros((2, 10, 10, 3), dtype=np.uint8) # Input images of shape (batch_size, height, width, channels)
|
| 9 |
+
height = 20
|
| 10 |
+
width = 20
|
| 11 |
+
resized_images = image_tools.resize_with_pad(images, height, width)
|
| 12 |
+
assert resized_images.shape == (2, height, width, 3)
|
| 13 |
+
assert np.all(resized_images == 0)
|
| 14 |
+
|
| 15 |
+
# Test case 2: Resize image with smaller dimensions
|
| 16 |
+
images = np.zeros((3, 30, 30, 3), dtype=np.uint8)
|
| 17 |
+
height = 15
|
| 18 |
+
width = 15
|
| 19 |
+
resized_images = image_tools.resize_with_pad(images, height, width)
|
| 20 |
+
assert resized_images.shape == (3, height, width, 3)
|
| 21 |
+
assert np.all(resized_images == 0)
|
| 22 |
+
|
| 23 |
+
# Test case 3: Resize image with the same dimensions
|
| 24 |
+
images = np.zeros((1, 50, 50, 3), dtype=np.uint8)
|
| 25 |
+
height = 50
|
| 26 |
+
width = 50
|
| 27 |
+
resized_images = image_tools.resize_with_pad(images, height, width)
|
| 28 |
+
assert resized_images.shape == (1, height, width, 3)
|
| 29 |
+
assert np.all(resized_images == 0)
|
| 30 |
+
|
| 31 |
+
# Test case 3: Resize image with odd-numbered padding
|
| 32 |
+
images = np.zeros((1, 256, 320, 3), dtype=np.uint8)
|
| 33 |
+
height = 60
|
| 34 |
+
width = 80
|
| 35 |
+
resized_images = image_tools.resize_with_pad(images, height, width)
|
| 36 |
+
assert resized_images.shape == (1, height, width, 3)
|
| 37 |
+
assert np.all(resized_images == 0)
|
code/pi05/packages/openpi-client/src/openpi_client/msgpack_numpy.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Adds NumPy array support to msgpack.
|
| 2 |
+
|
| 3 |
+
msgpack is good for (de)serializing data over a network for multiple reasons:
|
| 4 |
+
- msgpack is secure (as opposed to pickle/dill/etc which allow for arbitrary code execution)
|
| 5 |
+
- msgpack is widely used and has good cross-language support
|
| 6 |
+
- msgpack does not require a schema (as opposed to protobuf/flatbuffers/etc) which is convenient in dynamically typed
|
| 7 |
+
languages like Python and JavaScript
|
| 8 |
+
- msgpack is fast and efficient (as opposed to readable formats like JSON/YAML/etc); I found that msgpack was ~4x faster
|
| 9 |
+
than pickle for serializing large arrays using the below strategy
|
| 10 |
+
|
| 11 |
+
The code below is adapted from https://github.com/lebedov/msgpack-numpy. The reason not to use that library directly is
|
| 12 |
+
that it falls back to pickle for object arrays.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
import functools
|
| 16 |
+
|
| 17 |
+
import msgpack
|
| 18 |
+
import numpy as np
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def pack_array(obj):
|
| 22 |
+
if (isinstance(obj, (np.ndarray, np.generic))) and obj.dtype.kind in ("V", "O", "c"):
|
| 23 |
+
raise ValueError(f"Unsupported dtype: {obj.dtype}")
|
| 24 |
+
|
| 25 |
+
if isinstance(obj, np.ndarray):
|
| 26 |
+
return {
|
| 27 |
+
b"__ndarray__": True,
|
| 28 |
+
b"data": obj.tobytes(),
|
| 29 |
+
b"dtype": obj.dtype.str,
|
| 30 |
+
b"shape": obj.shape,
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
if isinstance(obj, np.generic):
|
| 34 |
+
return {
|
| 35 |
+
b"__npgeneric__": True,
|
| 36 |
+
b"data": obj.item(),
|
| 37 |
+
b"dtype": obj.dtype.str,
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
return obj
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def unpack_array(obj):
|
| 44 |
+
if b"__ndarray__" in obj:
|
| 45 |
+
return np.ndarray(buffer=obj[b"data"], dtype=np.dtype(obj[b"dtype"]), shape=obj[b"shape"])
|
| 46 |
+
|
| 47 |
+
if b"__npgeneric__" in obj:
|
| 48 |
+
return np.dtype(obj[b"dtype"]).type(obj[b"data"])
|
| 49 |
+
|
| 50 |
+
return obj
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
Packer = functools.partial(msgpack.Packer, default=pack_array)
|
| 54 |
+
packb = functools.partial(msgpack.packb, default=pack_array)
|
| 55 |
+
|
| 56 |
+
Unpacker = functools.partial(msgpack.Unpacker, object_hook=unpack_array)
|
| 57 |
+
unpackb = functools.partial(msgpack.unpackb, object_hook=unpack_array)
|
code/pi05/packages/openpi-client/src/openpi_client/msgpack_numpy_test.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pytest
|
| 3 |
+
import tree
|
| 4 |
+
|
| 5 |
+
from openpi_client import msgpack_numpy
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def _check(expected, actual):
|
| 9 |
+
if isinstance(expected, np.ndarray):
|
| 10 |
+
assert expected.shape == actual.shape
|
| 11 |
+
assert expected.dtype == actual.dtype
|
| 12 |
+
assert np.array_equal(expected, actual, equal_nan=expected.dtype.kind == "f")
|
| 13 |
+
else:
|
| 14 |
+
assert expected == actual
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@pytest.mark.parametrize(
|
| 18 |
+
"data",
|
| 19 |
+
[
|
| 20 |
+
1, # int
|
| 21 |
+
1.0, # float
|
| 22 |
+
"hello", # string
|
| 23 |
+
np.bool_(True), # boolean scalar
|
| 24 |
+
np.array([1, 2, 3])[0], # int scalar
|
| 25 |
+
np.str_("asdf"), # string scalar
|
| 26 |
+
[1, 2, 3], # list
|
| 27 |
+
{"key": "value"}, # dict
|
| 28 |
+
{"key": [1, 2, 3]}, # nested dict
|
| 29 |
+
np.array(1.0), # 0D array
|
| 30 |
+
np.array([1, 2, 3], dtype=np.int32), # 1D integer array
|
| 31 |
+
np.array(["asdf", "qwer"]), # string array
|
| 32 |
+
np.array([True, False]), # boolean array
|
| 33 |
+
np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32), # 2D float array
|
| 34 |
+
np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], dtype=np.int16), # 3D integer array
|
| 35 |
+
np.array([np.nan, np.inf, -np.inf]), # special float values
|
| 36 |
+
{"arr": np.array([1, 2, 3]), "nested": {"arr": np.array([4, 5, 6])}}, # nested dict with arrays
|
| 37 |
+
[np.array([1, 2]), np.array([3, 4])], # list of arrays
|
| 38 |
+
np.zeros((3, 4, 5), dtype=np.float32), # 3D zeros
|
| 39 |
+
np.ones((2, 3), dtype=np.float64), # 2D ones with double precision
|
| 40 |
+
],
|
| 41 |
+
)
|
| 42 |
+
def test_pack_unpack(data):
|
| 43 |
+
packed = msgpack_numpy.packb(data)
|
| 44 |
+
unpacked = msgpack_numpy.unpackb(packed)
|
| 45 |
+
tree.map_structure(_check, data, unpacked)
|
code/pi05/packages/openpi-client/src/openpi_client/runtime/agent.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class Agent(abc.ABC):
|
| 5 |
+
"""An Agent is the thing with agency, i.e. the entity that makes decisions.
|
| 6 |
+
|
| 7 |
+
Agents receive observations about the state of the world, and return actions
|
| 8 |
+
to take in response.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
@abc.abstractmethod
|
| 12 |
+
def get_action(self, observation: dict) -> dict:
|
| 13 |
+
"""Query the agent for the next action."""
|
| 14 |
+
|
| 15 |
+
@abc.abstractmethod
|
| 16 |
+
def reset(self) -> None:
|
| 17 |
+
"""Reset the agent to its initial state."""
|
code/pi05/packages/openpi-client/src/openpi_client/runtime/agents/policy_agent.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing_extensions import override
|
| 2 |
+
|
| 3 |
+
from openpi_client import base_policy as _base_policy
|
| 4 |
+
from openpi_client.runtime import agent as _agent
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class PolicyAgent(_agent.Agent):
|
| 8 |
+
"""An agent that uses a policy to determine actions."""
|
| 9 |
+
|
| 10 |
+
def __init__(self, policy: _base_policy.BasePolicy) -> None:
|
| 11 |
+
self._policy = policy
|
| 12 |
+
|
| 13 |
+
@override
|
| 14 |
+
def get_action(self, observation: dict) -> dict:
|
| 15 |
+
return self._policy.infer(observation)
|
| 16 |
+
|
| 17 |
+
def reset(self) -> None:
|
| 18 |
+
self._policy.reset()
|
code/pi05/packages/openpi-client/src/openpi_client/runtime/environment.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class Environment(abc.ABC):
|
| 5 |
+
"""An Environment represents the robot and the environment it inhabits.
|
| 6 |
+
|
| 7 |
+
The primary contract of environments is that they can be queried for observations
|
| 8 |
+
about their state, and have actions applied to them to change that state.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
@abc.abstractmethod
|
| 12 |
+
def reset(self) -> None:
|
| 13 |
+
"""Reset the environment to its initial state.
|
| 14 |
+
|
| 15 |
+
This will be called once before starting each episode.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
@abc.abstractmethod
|
| 19 |
+
def is_episode_complete(self) -> bool:
|
| 20 |
+
"""Allow the environment to signal that the episode is complete.
|
| 21 |
+
|
| 22 |
+
This will be called after each step. It should return `True` if the episode is
|
| 23 |
+
complete (either successfully or unsuccessfully), and `False` otherwise.
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
@abc.abstractmethod
|
| 27 |
+
def get_observation(self) -> dict:
|
| 28 |
+
"""Query the environment for the current state."""
|
| 29 |
+
|
| 30 |
+
@abc.abstractmethod
|
| 31 |
+
def apply_action(self, action: dict) -> None:
|
| 32 |
+
"""Take an action in the environment."""
|
code/pi05/packages/openpi-client/src/openpi_client/runtime/runtime.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import threading
|
| 3 |
+
import time
|
| 4 |
+
|
| 5 |
+
from openpi_client.runtime import agent as _agent
|
| 6 |
+
from openpi_client.runtime import environment as _environment
|
| 7 |
+
from openpi_client.runtime import subscriber as _subscriber
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class Runtime:
|
| 11 |
+
"""The core module orchestrating interactions between key components of the system."""
|
| 12 |
+
|
| 13 |
+
def __init__(
|
| 14 |
+
self,
|
| 15 |
+
environment: _environment.Environment,
|
| 16 |
+
agent: _agent.Agent,
|
| 17 |
+
subscribers: list[_subscriber.Subscriber],
|
| 18 |
+
max_hz: float = 0,
|
| 19 |
+
num_episodes: int = 1,
|
| 20 |
+
max_episode_steps: int = 0,
|
| 21 |
+
) -> None:
|
| 22 |
+
self._environment = environment
|
| 23 |
+
self._agent = agent
|
| 24 |
+
self._subscribers = subscribers
|
| 25 |
+
self._max_hz = max_hz
|
| 26 |
+
self._num_episodes = num_episodes
|
| 27 |
+
self._max_episode_steps = max_episode_steps
|
| 28 |
+
|
| 29 |
+
self._in_episode = False
|
| 30 |
+
self._episode_steps = 0
|
| 31 |
+
|
| 32 |
+
def run(self) -> None:
|
| 33 |
+
"""Runs the runtime loop continuously until stop() is called or the environment is done."""
|
| 34 |
+
for _ in range(self._num_episodes):
|
| 35 |
+
self._run_episode()
|
| 36 |
+
|
| 37 |
+
# Final reset, this is important for real environments to move the robot to its home position.
|
| 38 |
+
self._environment.reset()
|
| 39 |
+
|
| 40 |
+
def run_in_new_thread(self) -> threading.Thread:
|
| 41 |
+
"""Runs the runtime loop in a new thread."""
|
| 42 |
+
thread = threading.Thread(target=self.run)
|
| 43 |
+
thread.start()
|
| 44 |
+
return thread
|
| 45 |
+
|
| 46 |
+
def mark_episode_complete(self) -> None:
|
| 47 |
+
"""Marks the end of an episode."""
|
| 48 |
+
self._in_episode = False
|
| 49 |
+
|
| 50 |
+
def _run_episode(self) -> None:
|
| 51 |
+
"""Runs a single episode."""
|
| 52 |
+
logging.info("Starting episode...")
|
| 53 |
+
self._environment.reset()
|
| 54 |
+
self._agent.reset()
|
| 55 |
+
for subscriber in self._subscribers:
|
| 56 |
+
subscriber.on_episode_start()
|
| 57 |
+
|
| 58 |
+
self._in_episode = True
|
| 59 |
+
self._episode_steps = 0
|
| 60 |
+
step_time = 1 / self._max_hz if self._max_hz > 0 else 0
|
| 61 |
+
last_step_time = time.time()
|
| 62 |
+
|
| 63 |
+
while self._in_episode:
|
| 64 |
+
self._step()
|
| 65 |
+
self._episode_steps += 1
|
| 66 |
+
|
| 67 |
+
# Sleep to maintain the desired frame rate
|
| 68 |
+
now = time.time()
|
| 69 |
+
dt = now - last_step_time
|
| 70 |
+
if dt < step_time:
|
| 71 |
+
time.sleep(step_time - dt)
|
| 72 |
+
last_step_time = time.time()
|
| 73 |
+
else:
|
| 74 |
+
last_step_time = now
|
| 75 |
+
|
| 76 |
+
logging.info("Episode completed.")
|
| 77 |
+
for subscriber in self._subscribers:
|
| 78 |
+
subscriber.on_episode_end()
|
| 79 |
+
|
| 80 |
+
def _step(self) -> None:
|
| 81 |
+
"""A single step of the runtime loop."""
|
| 82 |
+
observation = self._environment.get_observation()
|
| 83 |
+
action = self._agent.get_action(observation)
|
| 84 |
+
self._environment.apply_action(action)
|
| 85 |
+
|
| 86 |
+
for subscriber in self._subscribers:
|
| 87 |
+
subscriber.on_step(observation, action)
|
| 88 |
+
|
| 89 |
+
if self._environment.is_episode_complete() or (
|
| 90 |
+
self._max_episode_steps > 0 and self._episode_steps >= self._max_episode_steps
|
| 91 |
+
):
|
| 92 |
+
self.mark_episode_complete()
|
code/pi05/packages/openpi-client/src/openpi_client/runtime/subscriber.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class Subscriber(abc.ABC):
|
| 5 |
+
"""Subscribes to events in the runtime.
|
| 6 |
+
|
| 7 |
+
Subscribers can be used to save data, visualize, etc.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
@abc.abstractmethod
|
| 11 |
+
def on_episode_start(self) -> None:
|
| 12 |
+
"""Called when an episode starts."""
|
| 13 |
+
|
| 14 |
+
@abc.abstractmethod
|
| 15 |
+
def on_step(self, observation: dict, action: dict) -> None:
|
| 16 |
+
"""Append a step to the episode."""
|
| 17 |
+
|
| 18 |
+
@abc.abstractmethod
|
| 19 |
+
def on_episode_end(self) -> None:
|
| 20 |
+
"""Called when an episode ends."""
|
code/pi05/packages/openpi-client/src/openpi_client/websocket_client_policy.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import time
|
| 3 |
+
from typing import Dict, Optional, Tuple
|
| 4 |
+
|
| 5 |
+
from typing_extensions import override
|
| 6 |
+
import websockets.sync.client
|
| 7 |
+
|
| 8 |
+
from openpi_client import base_policy as _base_policy
|
| 9 |
+
from openpi_client import msgpack_numpy
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class WebsocketClientPolicy(_base_policy.BasePolicy):
|
| 13 |
+
"""Implements the Policy interface by communicating with a server over websocket.
|
| 14 |
+
|
| 15 |
+
See WebsocketPolicyServer for a corresponding server implementation.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
def __init__(self, host: str = "0.0.0.0", port: Optional[int] = None, api_key: Optional[str] = None) -> None:
|
| 19 |
+
if host.startswith("ws"):
|
| 20 |
+
self._uri = host
|
| 21 |
+
else:
|
| 22 |
+
self._uri = f"ws://{host}"
|
| 23 |
+
if port is not None:
|
| 24 |
+
self._uri += f":{port}"
|
| 25 |
+
self._packer = msgpack_numpy.Packer()
|
| 26 |
+
self._api_key = api_key
|
| 27 |
+
self._ws, self._server_metadata = self._wait_for_server()
|
| 28 |
+
|
| 29 |
+
def get_server_metadata(self) -> Dict:
|
| 30 |
+
return self._server_metadata
|
| 31 |
+
|
| 32 |
+
def _wait_for_server(self) -> Tuple[websockets.sync.client.ClientConnection, Dict]:
|
| 33 |
+
logging.info(f"Waiting for server at {self._uri}...")
|
| 34 |
+
while True:
|
| 35 |
+
try:
|
| 36 |
+
headers = {"Authorization": f"Api-Key {self._api_key}"} if self._api_key else None
|
| 37 |
+
conn = websockets.sync.client.connect(
|
| 38 |
+
self._uri, compression=None, max_size=None, additional_headers=headers
|
| 39 |
+
)
|
| 40 |
+
metadata = msgpack_numpy.unpackb(conn.recv())
|
| 41 |
+
return conn, metadata
|
| 42 |
+
except ConnectionRefusedError:
|
| 43 |
+
logging.info("Still waiting for server...")
|
| 44 |
+
time.sleep(5)
|
| 45 |
+
|
| 46 |
+
@override
|
| 47 |
+
def infer(self, obs: Dict) -> Dict: # noqa: UP006
|
| 48 |
+
data = self._packer.pack(obs)
|
| 49 |
+
self._ws.send(data)
|
| 50 |
+
response = self._ws.recv()
|
| 51 |
+
if isinstance(response, str):
|
| 52 |
+
# we're expecting bytes; if the server sends a string, it's an error.
|
| 53 |
+
raise RuntimeError(f"Error in inference server:\n{response}")
|
| 54 |
+
return msgpack_numpy.unpackb(response)
|
| 55 |
+
|
| 56 |
+
@override
|
| 57 |
+
def reset(self) -> None:
|
| 58 |
+
pass
|
code/pi05/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>=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.19.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"]
|
code/pi05/scripts/__init__.py
ADDED
|
File without changes
|
code/pi05/scripts/check_robomem_memory_graph.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compile both phases without loading weights or executing training updates."""
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import logging
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import time
|
| 7 |
+
|
| 8 |
+
import jax
|
| 9 |
+
import numpy as np
|
| 10 |
+
|
| 11 |
+
import train_robomem_memory as trainer
|
| 12 |
+
from openpi.shared import array_typing as at
|
| 13 |
+
from openpi.training import data_loader, sharding
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def main():
|
| 17 |
+
logging.basicConfig(level=logging.INFO, force=True)
|
| 18 |
+
args = trainer.build_argparser().parse_args()
|
| 19 |
+
cfg = trainer.recipe(args)
|
| 20 |
+
mesh = sharding.make_mesh(args.fsdp_devices)
|
| 21 |
+
state, state_sharding = trainer.init_state(cfg, args, mesh, True)
|
| 22 |
+
index = trainer.EpisodeIndex(args.dataset)
|
| 23 |
+
dataset = trainer.MemoryDataset(index, cfg.data.create(cfg.assets_dirs, cfg.model), cfg.model, args.cache)
|
| 24 |
+
ep = index.groups[sorted(index.groups)[0]][0]
|
| 25 |
+
pos = index.episodes.index(ep)
|
| 26 |
+
sample_index = int(index.ends[pos - 1]) if pos else 0
|
| 27 |
+
batch = data_loader._collate_fn([dataset[sample_index]] * args.batch_size)
|
| 28 |
+
batch = jax.tree.map(lambda a: jax.ShapeDtypeStruct(a.shape, a.dtype), batch)
|
| 29 |
+
data_sharding = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec(sharding.DATA_AXIS))
|
| 30 |
+
replicated = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec())
|
| 31 |
+
report = {}
|
| 32 |
+
with sharding.set_mesh(mesh):
|
| 33 |
+
for warmup in (True, False):
|
| 34 |
+
name = "adapter_only" if warmup else "joint_frozen_vision"
|
| 35 |
+
started = time.monotonic()
|
| 36 |
+
fn = jax.jit(
|
| 37 |
+
lambda s, b: trainer.train_step(s, b, warmup=warmup),
|
| 38 |
+
in_shardings=(state_sharding, data_sharding),
|
| 39 |
+
out_shardings=(state_sharding, replicated),
|
| 40 |
+
donate_argnums=(0,),
|
| 41 |
+
)
|
| 42 |
+
with at.disable_typechecking():
|
| 43 |
+
executable = fn.lower(state, batch).compile()
|
| 44 |
+
memory = executable.memory_analysis()
|
| 45 |
+
report[name] = dict(
|
| 46 |
+
seconds=time.monotonic() - started,
|
| 47 |
+
arguments_bytes=memory.argument_size_in_bytes,
|
| 48 |
+
output_bytes=memory.output_size_in_bytes,
|
| 49 |
+
temporary_bytes=memory.temp_size_in_bytes,
|
| 50 |
+
alias_bytes=memory.alias_size_in_bytes,
|
| 51 |
+
)
|
| 52 |
+
print(name, json.dumps(report[name]), flush=True)
|
| 53 |
+
report["action_horizon"] = cfg.model.action_horizon
|
| 54 |
+
report["execution_horizon"] = args.execution_horizon
|
| 55 |
+
Path(f"logs/robomem_memory_spatial_a{args.action_horizon}_compile_check.json").write_text(json.dumps(report, indent=2))
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
if __name__ == "__main__":
|
| 59 |
+
main()
|
code/pi05/scripts/evaluate_robomem_memory.py
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Run selected official tasks and persist coverage, exclusions, failures and CSR/TSR."""
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import concurrent.futures
|
| 5 |
+
import hashlib
|
| 6 |
+
import json
|
| 7 |
+
import os
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
import subprocess
|
| 10 |
+
import threading
|
| 11 |
+
import time
|
| 12 |
+
import urllib.request
|
| 13 |
+
|
| 14 |
+
from openpi_client import msgpack_numpy
|
| 15 |
+
from openpi.policies.robomem_progress import PROGRESS_MODE
|
| 16 |
+
from robomem_eval_paths import ROOT, BENCHMARK, POLICY_PYTHON
|
| 17 |
+
from taskanchor.data.history import validate_robomem_recipe
|
| 18 |
+
from run_robomem_eval_task import run_task
|
| 19 |
+
from robomem_seed_search import run_task25
|
| 20 |
+
import websockets.sync.client
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def verify_server_checkpoint(port, checkpoint):
|
| 24 |
+
"""Reject a stale policy server before sending any evaluation observations."""
|
| 25 |
+
with websockets.sync.client.connect(
|
| 26 |
+
f"ws://127.0.0.1:{port}", compression=None, max_size=None, open_timeout=10
|
| 27 |
+
) as connection:
|
| 28 |
+
metadata = msgpack_numpy.unpackb(connection.recv(timeout=10))
|
| 29 |
+
actual = metadata.get("checkpoint")
|
| 30 |
+
if not actual or Path(actual).resolve() != Path(checkpoint).resolve():
|
| 31 |
+
raise RuntimeError(f"Policy server {port} checkpoint mismatch: expected {checkpoint}, got {actual}")
|
| 32 |
+
return metadata
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def main(args):
|
| 36 |
+
tasks = [task for task in range(1, 27) if task not in args.skip_tasks]
|
| 37 |
+
if not tasks or args.trials < 1:
|
| 38 |
+
raise ValueError("Evaluation requires at least one task and one trial")
|
| 39 |
+
task25_candidates = json.loads(Path(args.task25_seeds).read_text())["candidates"] if args.task25_seeds else None
|
| 40 |
+
if task25_candidates is not None and (not task25_candidates or len(task25_candidates) > 200
|
| 41 |
+
or len(task25_candidates) != len(set(task25_candidates))):
|
| 42 |
+
raise ValueError("Task25 requires 1 to 200 distinct candidate seeds")
|
| 43 |
+
if args.wait_smoke:
|
| 44 |
+
smoke = Path(args.wait_smoke).resolve()
|
| 45 |
+
print(f"Waiting for closed-loop smoke result: {smoke}", flush=True)
|
| 46 |
+
deadline = time.monotonic() + 3600
|
| 47 |
+
while not (smoke / "aggregate.json").exists():
|
| 48 |
+
log = (smoke / "run.log").read_text() if (smoke / "run.log").exists() else ""
|
| 49 |
+
if "Traceback (most recent call last)" in log or "Episode failed:" in log:
|
| 50 |
+
raise RuntimeError("Closed-loop smoke encountered a runtime error; refusing full evaluation")
|
| 51 |
+
if time.monotonic() > deadline:
|
| 52 |
+
raise TimeoutError("Closed-loop smoke did not finish within one hour")
|
| 53 |
+
time.sleep(5)
|
| 54 |
+
if json.loads((smoke / "aggregate.json").read_text())["num_episodes"] != 1:
|
| 55 |
+
raise RuntimeError("Incomplete closed-loop smoke")
|
| 56 |
+
log = (smoke / "run.log").read_text()
|
| 57 |
+
if "Traceback (most recent call last)" in log or "Episode failed:" in log:
|
| 58 |
+
raise RuntimeError("Closed-loop smoke failed")
|
| 59 |
+
print("Closed-loop smoke passed; starting 26-task evaluation", flush=True)
|
| 60 |
+
checkpoint, output = Path(args.checkpoint).resolve(), Path(args.output).resolve()
|
| 61 |
+
recipe = json.loads((checkpoint.parent / "recipe.json").read_text())
|
| 62 |
+
validate_robomem_recipe(recipe)
|
| 63 |
+
action_horizon = recipe["model"]["action_horizon"]
|
| 64 |
+
replan_steps = recipe.get("execution_horizon", 5)
|
| 65 |
+
if not 0 < replan_steps <= action_horizon or recipe["history_stride"] % replan_steps:
|
| 66 |
+
raise ValueError("Checkpoint has incompatible execution and history settings")
|
| 67 |
+
allowed_trials = (3, 10) if args.reference_prefix else (10,)
|
| 68 |
+
if args.trials not in allowed_trials or args.skip_tasks:
|
| 69 |
+
raise ValueError("Use all26 tasks and10 trials, or --reference-prefix --trials 3 for seeds50–52")
|
| 70 |
+
output.mkdir(parents=True, exist_ok=False)
|
| 71 |
+
metadata = dict(
|
| 72 |
+
checkpoint=str(checkpoint),
|
| 73 |
+
tasks=tasks,
|
| 74 |
+
skipped_tasks=sorted(set(args.skip_tasks)),
|
| 75 |
+
task25_seed_candidates=task25_candidates,
|
| 76 |
+
seeds=list(range(50, 50 + args.trials)),
|
| 77 |
+
expected_episodes=len(tasks) * args.trials,
|
| 78 |
+
reference_prefix_check=args.reference_prefix,
|
| 79 |
+
action_horizon=action_horizon,
|
| 80 |
+
replan_steps=replan_steps,
|
| 81 |
+
history_stride=recipe["history_stride"],
|
| 82 |
+
max_steps=2500,
|
| 83 |
+
renderer="osmesa",
|
| 84 |
+
instruction="fixed full-task rewrite 0 from training instruction table",
|
| 85 |
+
progress=PROGRESS_MODE,
|
| 86 |
+
base_port=args.base_port,
|
| 87 |
+
launch_gpu0=args.launch_gpu0,
|
| 88 |
+
benchmark=json.loads((BENCHMARK.parent / "OFFICIAL_SOURCE.json").read_text()),
|
| 89 |
+
code_sha256={
|
| 90 |
+
str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest()
|
| 91 |
+
for p in [
|
| 92 |
+
ROOT / "src/openpi/policies/robomem_memory_inference.py",
|
| 93 |
+
ROOT / "src/openpi/policies/robomem_progress.py",
|
| 94 |
+
ROOT / "src/openpi/robomemarena_26tasks_subtask_prompts.py",
|
| 95 |
+
ROOT / "scripts/robomem_memory_eval_adapter.py",
|
| 96 |
+
ROOT / "scripts/run_robomem_eval_task.py",
|
| 97 |
+
ROOT / "scripts/run_robomem_task25_seed.py",
|
| 98 |
+
ROOT / "scripts/robomem_seed_search.py",
|
| 99 |
+
ROOT / "scripts/robomem_eval_paths.py",
|
| 100 |
+
Path(__file__).resolve(),
|
| 101 |
+
]
|
| 102 |
+
},
|
| 103 |
+
)
|
| 104 |
+
metadata["recipe_sha256"] = hashlib.sha256((checkpoint.parent / "recipe.json").read_bytes()).hexdigest()
|
| 105 |
+
metadata["norm_stats_sha256"] = hashlib.sha256(
|
| 106 |
+
(checkpoint / "assets/pi05_libero/norm_stats.json").read_bytes()
|
| 107 |
+
).hexdigest()
|
| 108 |
+
metadata["taskanchor_code_sha256"] = {
|
| 109 |
+
rel: hashlib.sha256((ROOT.parent / rel).read_bytes()).hexdigest()
|
| 110 |
+
for rel in ("taskanchor/pi05/taskanchor.py", "taskanchor/pi05/ttt.py",
|
| 111 |
+
"taskanchor/data/history.py")
|
| 112 |
+
}
|
| 113 |
+
(output / "evaluation_config.json").write_text(json.dumps(metadata, indent=2))
|
| 114 |
+
lock = threading.Lock()
|
| 115 |
+
completed, failures, running = {}, {}, {}
|
| 116 |
+
|
| 117 |
+
def write_status():
|
| 118 |
+
result = dict(
|
| 119 |
+
expected_tasks=len(tasks),
|
| 120 |
+
expected_episodes=len(tasks) * args.trials,
|
| 121 |
+
skipped_tasks=metadata["skipped_tasks"],
|
| 122 |
+
completed_tasks=sorted(completed),
|
| 123 |
+
failed_tasks=failures,
|
| 124 |
+
running_tasks=running,
|
| 125 |
+
completed_episodes=sum(r["num_episodes"] for r in completed.values()),
|
| 126 |
+
)
|
| 127 |
+
scored = [r for r in completed.values() if r["num_episodes"] > 0]
|
| 128 |
+
if scored:
|
| 129 |
+
result.update(
|
| 130 |
+
TSR=sum(r["TSR"] for r in scored) / len(scored),
|
| 131 |
+
CSR=sum(r["CSR"] for r in scored) / len(scored),
|
| 132 |
+
)
|
| 133 |
+
temp = output / "status.tmp.json"
|
| 134 |
+
temp.write_text(json.dumps(result, indent=2))
|
| 135 |
+
temp.replace(output / "status.json")
|
| 136 |
+
|
| 137 |
+
def worker(gpu):
|
| 138 |
+
assigned_tasks = [task for task in range(gpu + 1, 27, args.workers) if task in tasks]
|
| 139 |
+
if not assigned_tasks:
|
| 140 |
+
return
|
| 141 |
+
port, server = args.base_port + gpu, None
|
| 142 |
+
if gpu != 0 or args.launch_gpu0:
|
| 143 |
+
env = dict(
|
| 144 |
+
os.environ,
|
| 145 |
+
CUDA_VISIBLE_DEVICES=str(gpu),
|
| 146 |
+
JAX_PLATFORMS="cuda",
|
| 147 |
+
XLA_PYTHON_CLIENT_PREALLOCATE="false",
|
| 148 |
+
OMP_NUM_THREADS="1",
|
| 149 |
+
OPENBLAS_NUM_THREADS="1",
|
| 150 |
+
PYTHONPYCACHEPREFIX="/tmp/robomem_memory_pycache",
|
| 151 |
+
)
|
| 152 |
+
with (output / f"server_gpu{gpu}.log").open("w") as log:
|
| 153 |
+
server = subprocess.Popen(
|
| 154 |
+
[
|
| 155 |
+
str(POLICY_PYTHON),
|
| 156 |
+
"-u",
|
| 157 |
+
"scripts/serve_robomem_memory.py",
|
| 158 |
+
"--checkpoint",
|
| 159 |
+
str(checkpoint),
|
| 160 |
+
"--port",
|
| 161 |
+
str(port),
|
| 162 |
+
],
|
| 163 |
+
cwd=ROOT,
|
| 164 |
+
env=env,
|
| 165 |
+
stdout=log,
|
| 166 |
+
stderr=subprocess.STDOUT,
|
| 167 |
+
stdin=subprocess.DEVNULL,
|
| 168 |
+
)
|
| 169 |
+
(output / f"server_gpu{gpu}.pid").write_text(str(server.pid))
|
| 170 |
+
try:
|
| 171 |
+
deadline = time.monotonic() + 300
|
| 172 |
+
while True:
|
| 173 |
+
try:
|
| 174 |
+
with urllib.request.urlopen(f"http://127.0.0.1:{port}/healthz", timeout=2) as response:
|
| 175 |
+
if response.status == 200:
|
| 176 |
+
break
|
| 177 |
+
except OSError:
|
| 178 |
+
if (server and server.poll() is not None) or time.monotonic() > deadline:
|
| 179 |
+
raise RuntimeError(f"GPU {gpu} policy server unavailable")
|
| 180 |
+
time.sleep(2)
|
| 181 |
+
server_metadata = verify_server_checkpoint(port, checkpoint)
|
| 182 |
+
if server_metadata["action_horizon"] != action_horizon:
|
| 183 |
+
raise RuntimeError("Server action horizon does not match the checkpoint recipe")
|
| 184 |
+
if server_metadata.get("progress") != PROGRESS_MODE:
|
| 185 |
+
raise RuntimeError("Server progress mode does not match evaluation protocol")
|
| 186 |
+
(output / f"server_gpu{gpu}_metadata.json").write_text(json.dumps(server_metadata, indent=2))
|
| 187 |
+
for task in assigned_tasks:
|
| 188 |
+
with lock:
|
| 189 |
+
running[gpu] = task
|
| 190 |
+
write_status()
|
| 191 |
+
print(f"START gpu={gpu} task={task}", flush=True)
|
| 192 |
+
try:
|
| 193 |
+
if task == 25 and task25_candidates is not None:
|
| 194 |
+
result = run_task25(gpu, port, args.trials, task25_candidates,
|
| 195 |
+
output / "task25", replan_steps)
|
| 196 |
+
else:
|
| 197 |
+
result = run_task(task, gpu, port, args.trials, 50, output / f"task{task:02d}",
|
| 198 |
+
replan_steps=replan_steps)
|
| 199 |
+
with lock:
|
| 200 |
+
completed[task] = result
|
| 201 |
+
print(f"COMPLETE task={task} {json.dumps(result)}", flush=True)
|
| 202 |
+
except Exception as exc:
|
| 203 |
+
with lock:
|
| 204 |
+
failures[task] = str(exc)
|
| 205 |
+
print(f"ERROR task={task}: {exc}", flush=True)
|
| 206 |
+
finally:
|
| 207 |
+
with lock:
|
| 208 |
+
running.pop(gpu, None)
|
| 209 |
+
write_status()
|
| 210 |
+
except Exception as exc:
|
| 211 |
+
with lock:
|
| 212 |
+
for task in assigned_tasks:
|
| 213 |
+
if task not in completed:
|
| 214 |
+
failures[task] = str(exc)
|
| 215 |
+
write_status()
|
| 216 |
+
finally:
|
| 217 |
+
if server:
|
| 218 |
+
server.terminate()
|
| 219 |
+
try:
|
| 220 |
+
server.wait(timeout=30)
|
| 221 |
+
except subprocess.TimeoutExpired:
|
| 222 |
+
server.kill()
|
| 223 |
+
server.wait()
|
| 224 |
+
|
| 225 |
+
write_status()
|
| 226 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=args.workers) as pool:
|
| 227 |
+
futures = [pool.submit(worker, gpu) for gpu in range(args.workers)]
|
| 228 |
+
for future in futures:
|
| 229 |
+
future.result()
|
| 230 |
+
episodes, task_results = [], []
|
| 231 |
+
for task in sorted(completed):
|
| 232 |
+
rows = json.loads((output / f"task{task:02d}/summary.json").read_text())
|
| 233 |
+
task_results.extend(rows)
|
| 234 |
+
for row in rows:
|
| 235 |
+
episodes.extend(dict(task_id=task, **ep) for ep in row["episodes"])
|
| 236 |
+
scored = [r for r in completed.values() if r["num_episodes"] > 0]
|
| 237 |
+
report = dict(
|
| 238 |
+
metadata=metadata,
|
| 239 |
+
complete=set(completed) == set(tasks) and not failures,
|
| 240 |
+
failures=failures,
|
| 241 |
+
task_results=task_results,
|
| 242 |
+
episodes=episodes,
|
| 243 |
+
actual_episodes=len(episodes),
|
| 244 |
+
shortfalls={task: args.trials - r["num_episodes"] for task, r in completed.items()
|
| 245 |
+
if r["num_episodes"] < args.trials},
|
| 246 |
+
metric_tasks=[task for task, r in sorted(completed.items()) if r["num_episodes"] > 0],
|
| 247 |
+
TSR=sum(r["TSR"] for r in scored) / len(scored) if scored else None,
|
| 248 |
+
CSR=sum(r["CSR"] for r in scored) / len(scored) if scored else None,
|
| 249 |
+
)
|
| 250 |
+
(output / "final_report.json").write_text(json.dumps(report, indent=2))
|
| 251 |
+
print(f"EVALUATION FINISHED complete={report['complete']} TSR={report['TSR']} CSR={report['CSR']}", flush=True)
|
| 252 |
+
if not report["complete"]:
|
| 253 |
+
raise RuntimeError("Some tasks failed; see final_report.json")
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
if __name__ == "__main__":
|
| 257 |
+
parser = argparse.ArgumentParser()
|
| 258 |
+
parser.add_argument("--checkpoint", required=True)
|
| 259 |
+
parser.add_argument("--output", required=True)
|
| 260 |
+
parser.add_argument("--workers", type=int, default=8, choices=range(1, 9))
|
| 261 |
+
parser.add_argument("--trials", type=int, default=10)
|
| 262 |
+
parser.add_argument("--reference-prefix", action="store_true",
|
| 263 |
+
help="Allow3 trials per task to compare the seeds50–52 prefix of the recorded50k run")
|
| 264 |
+
parser.add_argument("--wait-smoke")
|
| 265 |
+
parser.add_argument("--base-port", type=int, default=8600)
|
| 266 |
+
parser.add_argument("--launch-gpu0", action="store_true")
|
| 267 |
+
parser.add_argument("--skip-tasks", type=int, nargs="*", default=[], choices=range(1, 27))
|
| 268 |
+
parser.add_argument("--task25-seeds", default=str(
|
| 269 |
+
ROOT.parent / "evidence/task25_seed_candidates.json"
|
| 270 |
+
), help="Reference initialization seed list; policy failures are never retried")
|
| 271 |
+
main(parser.parse_args())
|
code/pi05/scripts/extract_robomem_memory.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Extract frozen pi05_base main-camera features before memory training."""
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import json
|
| 5 |
+
import logging
|
| 6 |
+
import os
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import time
|
| 9 |
+
|
| 10 |
+
import jax
|
| 11 |
+
import jax.numpy as jnp
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pyarrow.parquet as pq
|
| 14 |
+
import torch
|
| 15 |
+
|
| 16 |
+
from taskanchor.data.history import same_feature_provenance
|
| 17 |
+
from openpi.models import siglip
|
| 18 |
+
from openpi.models.model import restore_params
|
| 19 |
+
from openpi_client.image_tools import resize_with_pad
|
| 20 |
+
from openpi.training.robomem_memory_data import (
|
| 21 |
+
BASE_PARAMS,
|
| 22 |
+
CACHE_ROOT,
|
| 23 |
+
DATA_ROOT,
|
| 24 |
+
EpisodeIndex,
|
| 25 |
+
cache_file,
|
| 26 |
+
decode_image,
|
| 27 |
+
episode_path,
|
| 28 |
+
validate_cache,
|
| 29 |
+
validate_feature_file,
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class ExtractionImages(torch.utils.data.IterableDataset):
|
| 34 |
+
def __init__(self, index, groups):
|
| 35 |
+
self.index, self.groups = index, groups
|
| 36 |
+
|
| 37 |
+
def __iter__(self):
|
| 38 |
+
torch.set_num_threads(1)
|
| 39 |
+
worker = torch.utils.data.get_worker_info()
|
| 40 |
+
groups = self.groups if worker is None else self.groups[worker.id :: worker.num_workers]
|
| 41 |
+
for task, seed in groups:
|
| 42 |
+
for ep in self.index.groups[(task, seed)]:
|
| 43 |
+
record = self.index.records[ep]
|
| 44 |
+
table = pq.read_table(episode_path(self.index.root, ep), columns=["image"])
|
| 45 |
+
for local in range(len(table)):
|
| 46 |
+
yield dict(
|
| 47 |
+
image=resize_with_pad(decode_image(table["image"][local].as_py()), 224, 224),
|
| 48 |
+
task=task,
|
| 49 |
+
seed=seed,
|
| 50 |
+
frame=record.prefix_frames + local,
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def main(args):
|
| 55 |
+
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s", force=True)
|
| 56 |
+
index = EpisodeIndex(args.dataset)
|
| 57 |
+
cache = Path(args.cache)
|
| 58 |
+
cache.mkdir(parents=True, exist_ok=True)
|
| 59 |
+
expected = index.manifest(args.base_params)
|
| 60 |
+
manifest = cache / "manifest.json"
|
| 61 |
+
if manifest.exists() and not same_feature_provenance(json.loads(manifest.read_text()), expected):
|
| 62 |
+
raise ValueError("Existing cache has different provenance")
|
| 63 |
+
if not manifest.exists():
|
| 64 |
+
manifest.write_text(json.dumps(expected, indent=2))
|
| 65 |
+
if args.verify_only:
|
| 66 |
+
validate_cache(index, cache, args.base_params)
|
| 67 |
+
logging.info("CACHE VERIFIED: %s", expected)
|
| 68 |
+
return
|
| 69 |
+
pending = []
|
| 70 |
+
for key in sorted(index.groups):
|
| 71 |
+
path = cache_file(cache, key)
|
| 72 |
+
if path.exists():
|
| 73 |
+
length = index.records[index.groups[key][0]].total_frames
|
| 74 |
+
validate_feature_file(path, length)
|
| 75 |
+
else:
|
| 76 |
+
pending.append(key)
|
| 77 |
+
if args.limit_tasks:
|
| 78 |
+
pending = pending[: args.limit_tasks]
|
| 79 |
+
if not pending:
|
| 80 |
+
validate_cache(index, cache, args.base_params)
|
| 81 |
+
return
|
| 82 |
+
devices = jax.devices()
|
| 83 |
+
if any(d.platform != "gpu" for d in devices):
|
| 84 |
+
raise RuntimeError("Extraction requires GPUs; refusing a silent CPU fallback")
|
| 85 |
+
if args.batch_size % len(devices):
|
| 86 |
+
raise ValueError("Extraction batch size must be divisible by GPU count")
|
| 87 |
+
logging.info("Loading pi05_base encoder; devices=%s pending_tasks=%d", devices, len(pending))
|
| 88 |
+
params = restore_params(args.base_params, restore_type=np.ndarray)["PaliGemma"]["img"]
|
| 89 |
+
mesh = jax.sharding.Mesh(np.asarray(devices), ("batch",))
|
| 90 |
+
replicated = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec())
|
| 91 |
+
data_sharding = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec("batch"))
|
| 92 |
+
params = jax.device_put(jax.tree.map(lambda x: x.astype(jnp.bfloat16), params), replicated)
|
| 93 |
+
encoder = siglip.Module(num_classes=2048, variant="So400m/14", pool_type="none", scan=True, dtype_mm="bfloat16")
|
| 94 |
+
|
| 95 |
+
def encode(p, images):
|
| 96 |
+
images = images.astype(jnp.float32) / 255 * 2 - 1
|
| 97 |
+
tokens, _ = encoder.apply({"params": p}, images, train=False, return_encoded=True)
|
| 98 |
+
# Pool in FP32 then quantize to BF16; float16 storage preserves these values.
|
| 99 |
+
spatial = jax.image.resize(
|
| 100 |
+
tokens.reshape(-1, 16, 16, 1152).astype(jnp.float32),
|
| 101 |
+
(images.shape[0], 4, 4, 1152),
|
| 102 |
+
"linear",
|
| 103 |
+
antialias=False,
|
| 104 |
+
)
|
| 105 |
+
return spatial.reshape(-1, 16, 1152).astype(jnp.bfloat16).astype(jnp.float16)
|
| 106 |
+
|
| 107 |
+
encode = jax.jit(encode, in_shardings=(replicated, data_sharding), out_shardings=data_sharding)
|
| 108 |
+
loader = torch.utils.data.DataLoader(
|
| 109 |
+
ExtractionImages(index, pending),
|
| 110 |
+
batch_size=args.batch_size,
|
| 111 |
+
num_workers=args.workers,
|
| 112 |
+
multiprocessing_context="spawn" if args.workers else None,
|
| 113 |
+
prefetch_factor=2 if args.workers else None,
|
| 114 |
+
)
|
| 115 |
+
buffers, completed, frames = {}, 0, 0
|
| 116 |
+
started = time.monotonic()
|
| 117 |
+
for batch in loader:
|
| 118 |
+
images = batch["image"].numpy()
|
| 119 |
+
count = len(images)
|
| 120 |
+
if count < args.batch_size:
|
| 121 |
+
images = np.pad(images, ((0, args.batch_size - count), (0, 0), (0, 0), (0, 0)), mode="edge")
|
| 122 |
+
history = jax.device_get(encode(params, jax.device_put(images, data_sharding)))
|
| 123 |
+
for i in range(count):
|
| 124 |
+
key = int(batch["task"][i]), int(batch["seed"][i])
|
| 125 |
+
frame = int(batch["frame"][i])
|
| 126 |
+
if key not in buffers:
|
| 127 |
+
total = index.records[index.groups[key][0]].total_frames
|
| 128 |
+
buffers[key] = dict(history=np.empty((total, 16, 1152), np.float16), seen=np.zeros(total, bool))
|
| 129 |
+
buf = buffers[key]
|
| 130 |
+
if buf["seen"][frame]:
|
| 131 |
+
raise ValueError(f"Duplicate frame {key} {frame}")
|
| 132 |
+
buf["history"][frame] = history[i]
|
| 133 |
+
buf["seen"][frame] = True
|
| 134 |
+
if buf["seen"].all():
|
| 135 |
+
if not np.isfinite(buf["history"]).all():
|
| 136 |
+
raise ValueError(f"Nonfinite encoder output {key}")
|
| 137 |
+
path = cache_file(cache, key)
|
| 138 |
+
temp = path.with_suffix(".tmp.npy")
|
| 139 |
+
np.save(temp, buf["history"], allow_pickle=False)
|
| 140 |
+
os.replace(temp, path)
|
| 141 |
+
del buffers[key]
|
| 142 |
+
completed += 1
|
| 143 |
+
frames += count
|
| 144 |
+
if frames == count or frames % (args.batch_size * 20) == 0:
|
| 145 |
+
logging.info(
|
| 146 |
+
"extracted_frames=%d tasks=%d/%d fps=%.1f",
|
| 147 |
+
frames,
|
| 148 |
+
completed,
|
| 149 |
+
len(pending),
|
| 150 |
+
frames / (time.monotonic() - started),
|
| 151 |
+
)
|
| 152 |
+
if buffers or completed != len(pending):
|
| 153 |
+
raise RuntimeError("Extraction ended with incomplete task buffers")
|
| 154 |
+
if not args.limit_tasks:
|
| 155 |
+
validate_cache(index, cache, args.base_params)
|
| 156 |
+
(cache / "VERIFIED.json").write_text(json.dumps(expected, indent=2))
|
| 157 |
+
logging.info("EXTRACTION COMPLETE: tasks=%d frames=%d", completed, frames)
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
if __name__ == "__main__":
|
| 161 |
+
parser = argparse.ArgumentParser()
|
| 162 |
+
parser.add_argument("--dataset", default=str(DATA_ROOT))
|
| 163 |
+
parser.add_argument("--base-params", default=str(BASE_PARAMS))
|
| 164 |
+
parser.add_argument("--cache", default=str(CACHE_ROOT))
|
| 165 |
+
parser.add_argument("--batch-size", type=int, default=256)
|
| 166 |
+
parser.add_argument("--workers", type=int, default=8)
|
| 167 |
+
parser.add_argument("--limit-tasks", type=int, default=0)
|
| 168 |
+
parser.add_argument("--verify-only", action="store_true")
|
| 169 |
+
main(parser.parse_args())
|
code/pi05/scripts/robomem_eval_paths.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Portable filesystem locations; numerical evaluation settings live elsewhere."""
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import sys
|
| 6 |
+
|
| 7 |
+
ROOT = Path(__file__).resolve().parents[1]
|
| 8 |
+
BENCHMARK = Path(os.environ.get(
|
| 9 |
+
"TASKANCHOR_ROBOMEM_BENCHMARK", ROOT.parent / "RoboMemArena/evaluation_benchmark"
|
| 10 |
+
)).resolve()
|
| 11 |
+
PYTHON = Path(os.environ.get(
|
| 12 |
+
"TASKANCHOR_ROBOMEM_EVAL_PYTHON", ROOT.parent / "robomem_eval_env/bin/python"
|
| 13 |
+
)).absolute() # Keep the virtualenv entry point; resolving its symlink loses its packages.
|
| 14 |
+
POLICY_PYTHON = os.environ.get("TASKANCHOR_PI05_PYTHON", sys.executable)
|
| 15 |
+
LIBERO_CONFIG = Path(os.environ.get(
|
| 16 |
+
"TASKANCHOR_LIBERO_CONFIG", ROOT / "data/robomem_eval_libero_config"
|
| 17 |
+
)).resolve()
|
| 18 |
+
RENDER_LIBS = Path(os.environ.get(
|
| 19 |
+
"TASKANCHOR_RENDER_LIBS", ROOT / "data/robomem_render_libs/usr/lib/x86_64-linux-gnu"
|
| 20 |
+
)).resolve()
|
code/pi05/scripts/robomem_memory_eval_adapter.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""RoboMemArena adapter: whole-task instruction, predicted progress, causal memory."""
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import sys
|
| 6 |
+
import time
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
|
| 10 |
+
ROOT = Path(__file__).resolve().parents[1]
|
| 11 |
+
sys.path.insert(0, str(ROOT / "packages/openpi-client/src"))
|
| 12 |
+
sys.path.insert(0, str(ROOT / "src"))
|
| 13 |
+
from openpi_client.websocket_client_policy import WebsocketClientPolicy
|
| 14 |
+
from openpi.robomemarena_26tasks_10_full_prompts import ROBOMEMARENA_26_10_FULL_PROMPTS
|
| 15 |
+
from openpi.policies.robomem_progress import PROGRESS_MODE, stage_values
|
| 16 |
+
from policy_adapter import BasePolicyAdapter
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class MemoryEvalAdapter(BasePolicyAdapter):
|
| 20 |
+
def __init__(self, host="127.0.0.1", port=8600, replan_steps=None, trace_path=None, task_id=None):
|
| 21 |
+
self.client = WebsocketClientPolicy(host=host, port=int(port))
|
| 22 |
+
metadata = self.client.get_server_metadata()
|
| 23 |
+
if metadata.get("progress") != PROGRESS_MODE:
|
| 24 |
+
self.client._ws.close()
|
| 25 |
+
raise ValueError("Evaluation requires a nearest-stage progress policy server")
|
| 26 |
+
replan_steps = metadata.get("execution_horizon", 5) if replan_steps is None else replan_steps
|
| 27 |
+
if not 0 < replan_steps <= metadata["action_horizon"] or metadata["history_stride"] % replan_steps:
|
| 28 |
+
self.client._ws.close()
|
| 29 |
+
raise ValueError("Execution length must divide history stride and not exceed prediction horizon")
|
| 30 |
+
self.replan_steps = replan_steps
|
| 31 |
+
self.task_id = task_id
|
| 32 |
+
self.episode_id = -1
|
| 33 |
+
self.trace_path = Path(trace_path) if trace_path else None
|
| 34 |
+
self.frame = 0
|
| 35 |
+
|
| 36 |
+
def set_task_context(self, *, task_id=None, episode_id=None, **kwargs):
|
| 37 |
+
del kwargs
|
| 38 |
+
self.task_id, self.episode_id = task_id, episode_id
|
| 39 |
+
|
| 40 |
+
def reset(self):
|
| 41 |
+
self.frame = 0
|
| 42 |
+
self.episode_id += 1
|
| 43 |
+
|
| 44 |
+
def infer_actions(self, obs, prompt, resize_size):
|
| 45 |
+
del resize_size
|
| 46 |
+
task = self.task_id if self.task_id is not None else obs.get("task_id")
|
| 47 |
+
stage_values(task)
|
| 48 |
+
# A fixed full-task rewrite is the commanded instruction, never an oracle subtask.
|
| 49 |
+
if task is not None:
|
| 50 |
+
prompt = ROBOMEMARENA_26_10_FULL_PROMPTS[int(task)][0]
|
| 51 |
+
state = np.asarray(obs["observation/state"], np.float32).reshape(-1)
|
| 52 |
+
state = np.pad(state, (0, max(0, 8 - len(state))))[:8]
|
| 53 |
+
payload = {
|
| 54 |
+
"observation/image": obs["observation/image"],
|
| 55 |
+
"observation/wrist_image": obs["observation/wrist_image"],
|
| 56 |
+
"observation/state": state,
|
| 57 |
+
"prompt": prompt,
|
| 58 |
+
"task_id": int(task),
|
| 59 |
+
"environment_frame": self.frame,
|
| 60 |
+
"reset": self.frame == 0,
|
| 61 |
+
}
|
| 62 |
+
infer_started = time.perf_counter()
|
| 63 |
+
result = self.client.infer(payload)
|
| 64 |
+
round_trip_ms = (time.perf_counter() - infer_started) * 1000
|
| 65 |
+
if self.trace_path:
|
| 66 |
+
self.trace_path.parent.mkdir(parents=True, exist_ok=True)
|
| 67 |
+
with self.trace_path.open("a") as stream:
|
| 68 |
+
stream.write(
|
| 69 |
+
json.dumps(
|
| 70 |
+
dict(
|
| 71 |
+
task_id=int(task) if task is not None else None,
|
| 72 |
+
episode_id=self.episode_id,
|
| 73 |
+
frame=self.frame,
|
| 74 |
+
progress=float(result["progress"]),
|
| 75 |
+
progress_raw=float(result["progress_raw"]),
|
| 76 |
+
progress_stage=int(result["progress_stage"]),
|
| 77 |
+
progress_num_stages=int(result["progress_num_stages"]),
|
| 78 |
+
history_frames=int(result["history_frames"]),
|
| 79 |
+
server_infer_ms=result.get("server_timing", {}).get("infer_ms"),
|
| 80 |
+
round_trip_ms=round_trip_ms,
|
| 81 |
+
)
|
| 82 |
+
)
|
| 83 |
+
+ "\n"
|
| 84 |
+
)
|
| 85 |
+
self.frame += self.replan_steps
|
| 86 |
+
return np.asarray(result["actions"], np.float32)
|
| 87 |
+
|
| 88 |
+
def close(self):
|
| 89 |
+
self.client._ws.close()
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def build_adapter(**kwargs):
|
| 93 |
+
return MemoryEvalAdapter(**kwargs)
|
code/pi05/scripts/robomem_seed_search.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Bounded Task25 seed enumeration; unsuccessful policies remain scored episodes."""
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from run_robomem_eval_task import TaskInitializationError, run_task
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def run_task25(gpu, port, trials, candidates, output, replan_steps):
|
| 10 |
+
output = Path(output)
|
| 11 |
+
output.mkdir(parents=True, exist_ok=False)
|
| 12 |
+
if len(candidates) > 200 or len(candidates) != len(set(candidates)):
|
| 13 |
+
raise ValueError("At most 200 distinct seed candidates are allowed")
|
| 14 |
+
audit = dict(target_episodes=trials, max_seed_attempts=len(candidates), attempts=[], accepted_seeds=[])
|
| 15 |
+
|
| 16 |
+
def save():
|
| 17 |
+
temporary = output / "seed_search.tmp.json"
|
| 18 |
+
temporary.write_text(json.dumps(audit, indent=2))
|
| 19 |
+
temporary.replace(output / "seed_search.json")
|
| 20 |
+
|
| 21 |
+
rows, episodes = [], []
|
| 22 |
+
save()
|
| 23 |
+
for seed in candidates:
|
| 24 |
+
case = output / f"seed{seed:04d}"
|
| 25 |
+
try:
|
| 26 |
+
run_task(25, gpu, port, 1, seed, case, replan_steps=replan_steps, enumerate_seed=True)
|
| 27 |
+
except TaskInitializationError as exc:
|
| 28 |
+
audit["attempts"].append(dict(seed=seed, status="initialization_error", error=str(exc)))
|
| 29 |
+
save()
|
| 30 |
+
continue
|
| 31 |
+
except Exception as exc:
|
| 32 |
+
audit["attempts"].append(dict(seed=seed, status="runtime_error", error=str(exc)))
|
| 33 |
+
save()
|
| 34 |
+
raise
|
| 35 |
+
row = json.loads((case / "summary.json").read_text())[0]
|
| 36 |
+
if len(row["episodes"]) != 1 or row["episodes"][0]["seed"] != seed:
|
| 37 |
+
raise RuntimeError("Single-seed scorer returned inconsistent coverage")
|
| 38 |
+
ep = dict(row["episodes"][0], ep=len(episodes), video_dir=row["video_dir"])
|
| 39 |
+
episodes.append(ep)
|
| 40 |
+
rows.append(row)
|
| 41 |
+
audit["accepted_seeds"].append(seed)
|
| 42 |
+
audit["attempts"].append(dict(seed=seed, status="scored", TSR=ep["TSR"], CSR=ep["CSR"]))
|
| 43 |
+
save()
|
| 44 |
+
if len(episodes) == trials:
|
| 45 |
+
break
|
| 46 |
+
audit.update(actual_episodes=len(episodes), exhausted=len(audit["attempts"]) == len(candidates),
|
| 47 |
+
shortfall=trials - len(episodes))
|
| 48 |
+
save()
|
| 49 |
+
tsr = sum(ep["TSR"] for ep in episodes) / len(episodes) if episodes else None
|
| 50 |
+
csr = sum(ep["CSR"] for ep in episodes) / len(episodes) if episodes else None
|
| 51 |
+
summary = dict(rows[0]) if rows else dict(task_id=25, task_key="task25")
|
| 52 |
+
summary.update(episodes=episodes, TSR=tsr, CSR=csr, seed_search=audit,
|
| 53 |
+
video_dir=str(output), num_episodes=len(episodes))
|
| 54 |
+
aggregate = dict(num_tasks=1, num_episodes=len(episodes), TSR=tsr, CSR=csr, seed_search=audit)
|
| 55 |
+
(output / "summary.json").write_text(json.dumps([summary], indent=2))
|
| 56 |
+
(output / "aggregate.json").write_text(json.dumps(aggregate, indent=2))
|
| 57 |
+
return aggregate
|
code/pi05/scripts/run_robomem_eval_task.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Launch the unmodified official scorer for one task."""
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import json
|
| 5 |
+
import os
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import subprocess
|
| 8 |
+
|
| 9 |
+
from robomem_eval_paths import ROOT, BENCHMARK, PYTHON, LIBERO_CONFIG, RENDER_LIBS
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class TaskInitializationError(RuntimeError):
|
| 13 |
+
"""An enumerated seed failed before any policy action was produced."""
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def run_task(task, gpu, port, trials, seed, output, replan_steps=5, enumerate_seed=False):
|
| 17 |
+
if replan_steps <= 0 or 10 % replan_steps:
|
| 18 |
+
raise ValueError("Replan steps must divide history stride 10")
|
| 19 |
+
output = Path(output).resolve()
|
| 20 |
+
output.mkdir(parents=True, exist_ok=True)
|
| 21 |
+
env = dict(
|
| 22 |
+
os.environ,
|
| 23 |
+
MUJOCO_GL="osmesa",
|
| 24 |
+
PYOPENGL_PLATFORM="osmesa",
|
| 25 |
+
MUJOCO_EGL_DEVICE_ID=str(gpu),
|
| 26 |
+
OMP_NUM_THREADS="1",
|
| 27 |
+
OPENBLAS_NUM_THREADS="1",
|
| 28 |
+
NUMBA_CACHE_DIR="/tmp/robomem_numba",
|
| 29 |
+
LIBERO_CONFIG_PATH=str(LIBERO_CONFIG),
|
| 30 |
+
PYTHONPATH=str(BENCHMARK / "libero_fork"),
|
| 31 |
+
LD_LIBRARY_PATH=str(RENDER_LIBS)
|
| 32 |
+
+ ":"
|
| 33 |
+
+ os.environ.get("LD_LIBRARY_PATH", ""),
|
| 34 |
+
LP_NUM_THREADS="2",
|
| 35 |
+
)
|
| 36 |
+
kwargs = dict(port=port, replan_steps=replan_steps, task_id=task, trace_path=str(output / "progress.jsonl"))
|
| 37 |
+
command = [
|
| 38 |
+
str(PYTHON),
|
| 39 |
+
"-u",
|
| 40 |
+
str(ROOT / "scripts/run_robomem_task25_seed.py") if enumerate_seed else "scripts/run_all_tasks1_26.py",
|
| 41 |
+
"--adapter-spec",
|
| 42 |
+
str(ROOT / "scripts/robomem_memory_eval_adapter.py") + ":build_adapter",
|
| 43 |
+
"--adapter-kwargs",
|
| 44 |
+
json.dumps(kwargs),
|
| 45 |
+
"--task-start",
|
| 46 |
+
str(task),
|
| 47 |
+
"--task-end",
|
| 48 |
+
str(task),
|
| 49 |
+
"--num-trials-per-task",
|
| 50 |
+
str(trials),
|
| 51 |
+
"--seed",
|
| 52 |
+
str(seed),
|
| 53 |
+
"--replan-steps",
|
| 54 |
+
str(replan_steps),
|
| 55 |
+
"--max-steps",
|
| 56 |
+
"2500",
|
| 57 |
+
"--out-root",
|
| 58 |
+
str(output),
|
| 59 |
+
]
|
| 60 |
+
(output / "command.json").write_text(json.dumps(command, indent=2))
|
| 61 |
+
for attempt in range(1, 2 if enumerate_seed else 4):
|
| 62 |
+
with (output / "run.log").open("w", buffering=1) as log:
|
| 63 |
+
process = subprocess.run(command, cwd=BENCHMARK, env=env, stdout=log, stderr=subprocess.STDOUT)
|
| 64 |
+
log_text = (output / "run.log").read_text()
|
| 65 |
+
trace = output / "progress.jsonl"
|
| 66 |
+
if (enumerate_seed and process.returncode == 86 and "TASK25_INITIALIZATION_ERROR:" in log_text
|
| 67 |
+
and (not trace.exists() or trace.stat().st_size == 0)):
|
| 68 |
+
raise TaskInitializationError(f"Task25 seed {seed}: initialization failed; see {output / 'run.log'}")
|
| 69 |
+
# The official constructor samples before any episode seed is set. Its
|
| 70 |
+
# reset() already retries invalid placements, but its constructor does not.
|
| 71 |
+
# Retry only this startup failure, never an episode or a policy failure.
|
| 72 |
+
startup_placement_error = (
|
| 73 |
+
process.returncode != 0
|
| 74 |
+
and "robosuite.utils.errors.RandomizationError: Cannot place all objects" in log_text
|
| 75 |
+
and "env = OffScreenRenderEnv(" in log_text
|
| 76 |
+
and "Episode " not in log_text
|
| 77 |
+
and not (output / "progress.jsonl").exists()
|
| 78 |
+
and not (output / "summary.json").exists()
|
| 79 |
+
)
|
| 80 |
+
if not startup_placement_error or attempt == 3:
|
| 81 |
+
break
|
| 82 |
+
(output / f"startup_failure_attempt{attempt}.log").write_text(log_text)
|
| 83 |
+
print(f"Task {task}: constructor placement failed before episode 0; retry {attempt}/2", flush=True)
|
| 84 |
+
if process.returncode or "Traceback (most recent call last)" in log_text or "Episode failed:" in log_text:
|
| 85 |
+
raise RuntimeError(f"Task {task} evaluation encountered a runtime error; see {output / 'run.log'}")
|
| 86 |
+
result = json.loads((output / "aggregate.json").read_text())
|
| 87 |
+
if result["num_episodes"] != trials:
|
| 88 |
+
raise RuntimeError(f"Task {task}: incomplete episode coverage")
|
| 89 |
+
return result
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
if __name__ == "__main__":
|
| 93 |
+
parser = argparse.ArgumentParser()
|
| 94 |
+
parser.add_argument("--task", type=int, required=True)
|
| 95 |
+
parser.add_argument("--gpu", type=int, default=0)
|
| 96 |
+
parser.add_argument("--port", type=int, default=8600)
|
| 97 |
+
parser.add_argument("--trials", type=int, default=1)
|
| 98 |
+
parser.add_argument("--seed", type=int, default=50)
|
| 99 |
+
parser.add_argument("--output", required=True)
|
| 100 |
+
parser.add_argument("--replan-steps", type=int, default=5)
|
| 101 |
+
args = parser.parse_args()
|
| 102 |
+
print(json.dumps(run_task(args.task, args.gpu, args.port, args.trials, args.seed, args.output,
|
| 103 |
+
replan_steps=args.replan_steps)), flush=True)
|
code/pi05/scripts/run_robomem_task25_seed.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Official scorer with deterministic, single-attempt Task25 initialization."""
|
| 2 |
+
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
import sys
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
from robomem_eval_paths import BENCHMARK
|
| 9 |
+
sys.path.insert(0, str(BENCHMARK / "scripts"))
|
| 10 |
+
import run_all_tasks1_26 as official
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def main():
|
| 14 |
+
args = official.build_argparser().parse_args()
|
| 15 |
+
from robosuite.utils.errors import RandomizationError
|
| 16 |
+
if args.task_start != 25 or args.task_end != 25 or args.num_trials_per_task != 1:
|
| 17 |
+
raise ValueError("This entry point is only for one Task25 seed")
|
| 18 |
+
np.random.seed(args.seed) # Also seed the constructor, which precedes the episode loop.
|
| 19 |
+
base = official.ec._get_env_class()
|
| 20 |
+
|
| 21 |
+
class SingleAttemptEnvironment(base):
|
| 22 |
+
def reset(self):
|
| 23 |
+
# Original ControlEnv.reset retries placement errors indefinitely.
|
| 24 |
+
# Return the same observation on success; reject this seed on failure.
|
| 25 |
+
return self.env.reset()
|
| 26 |
+
|
| 27 |
+
official.ec._get_env_class = lambda: SingleAttemptEnvironment
|
| 28 |
+
try:
|
| 29 |
+
official.main()
|
| 30 |
+
except RandomizationError as exc:
|
| 31 |
+
print(f"TASK25_INITIALIZATION_ERROR: {exc}", flush=True)
|
| 32 |
+
raise SystemExit(86) from exc
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
if __name__ == "__main__":
|
| 36 |
+
main()
|
code/pi05/scripts/serve_robomem_memory.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import logging
|
| 3 |
+
|
| 4 |
+
from openpi.policies.robomem_memory_inference import RoboMemMemoryPolicy
|
| 5 |
+
from openpi.serving.websocket_policy_server import WebsocketPolicyServer
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
if __name__ == "__main__":
|
| 9 |
+
parser = argparse.ArgumentParser()
|
| 10 |
+
parser.add_argument("--checkpoint", required=True)
|
| 11 |
+
parser.add_argument("--port", type=int, default=8600)
|
| 12 |
+
args = parser.parse_args()
|
| 13 |
+
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s", force=True)
|
| 14 |
+
policy = RoboMemMemoryPolicy(args.checkpoint)
|
| 15 |
+
logging.info("Memory policy loaded: %s", policy.metadata)
|
| 16 |
+
WebsocketPolicyServer(policy, host="127.0.0.1", port=args.port, metadata=policy.metadata).serve_forever()
|
code/pi05/scripts/test_robomem_discrete_progress.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CPU checks for legal progress values and the action-conditioning path."""
|
| 2 |
+
import unittest
|
| 3 |
+
import numpy as np
|
| 4 |
+
from openpi.policies.robomem_progress import discretize_progress, stage_values
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class ProgressTests(unittest.TestCase):
|
| 8 |
+
def test_four_stages_and_boundaries(self):
|
| 9 |
+
self.assertEqual(stage_values(1), (0.0, 0.25, 0.5, 0.75))
|
| 10 |
+
for raw, expected in [(0, 0), (.124, 0), (.125, 0), (.126, .25),
|
| 11 |
+
(.375, .25), (.62, .5), (.625, .5), (.626, .75),
|
| 12 |
+
(.86, .75), (1, .75), (-.1, 0), (1.1, .75)]:
|
| 13 |
+
self.assertEqual(discretize_progress(raw, 1)[0], expected)
|
| 14 |
+
|
| 15 |
+
def test_all_tasks_match_training_labels(self):
|
| 16 |
+
from openpi.robomemarena_26tasks_subtask_prompts import ROBOMEMARENA_26_SUBTASK_PROMPTS
|
| 17 |
+
for task, stages in ROBOMEMARENA_26_SUBTASK_PROMPTS.items():
|
| 18 |
+
expected = tuple(round(i / len(stages), 2) for i in range(len(stages)))
|
| 19 |
+
self.assertEqual(stage_values(task), expected)
|
| 20 |
+
for i, value in enumerate(expected):
|
| 21 |
+
self.assertEqual(discretize_progress(value, task), (value, i))
|
| 22 |
+
for value in np.linspace(0, 1, 101):
|
| 23 |
+
self.assertIn(discretize_progress(value, task)[0], expected)
|
| 24 |
+
|
| 25 |
+
def test_reject_invalid_inputs(self):
|
| 26 |
+
for task in [None, 0, 27, True, "1"]:
|
| 27 |
+
with self.assertRaises(ValueError):
|
| 28 |
+
discretize_progress(.5, task)
|
| 29 |
+
for raw in [float("nan"), float("inf"), -float("inf")]:
|
| 30 |
+
with self.assertRaises(FloatingPointError):
|
| 31 |
+
discretize_progress(raw, 1)
|
| 32 |
+
|
| 33 |
+
def test_action_sampler_receives_discrete_prompt(self):
|
| 34 |
+
from types import SimpleNamespace
|
| 35 |
+
import jax
|
| 36 |
+
from openpi.policies.robomem_memory_inference import RoboMemMemoryPolicy
|
| 37 |
+
policy = object.__new__(RoboMemMemoryPolicy)
|
| 38 |
+
policy.action_horizon = 20
|
| 39 |
+
policy.rng = jax.random.key(42)
|
| 40 |
+
policy._observation = lambda raw, prompt: SimpleNamespace(state=np.zeros((1, 8)), prompt=prompt)
|
| 41 |
+
policy.encode = lambda obs: (None, None, np.zeros((1, 16, 1152)))
|
| 42 |
+
policy.history = SimpleNamespace(add_and_read=lambda frame, spatial:
|
| 43 |
+
(np.zeros((16, 1152)), np.zeros((1, 16, 1152)), np.ones(1, bool)))
|
| 44 |
+
policy.refine = lambda *args: (None, np.array([.86]))
|
| 45 |
+
def sample(key, obs, refined, mask):
|
| 46 |
+
self.assertEqual(obs.prompt, "test task.Progress:0.75")
|
| 47 |
+
return np.zeros((1, 20, 7))
|
| 48 |
+
policy.sample = sample
|
| 49 |
+
policy.outputs = lambda data: data
|
| 50 |
+
result = policy.infer(dict(task_id=1, environment_frame=0, prompt="test task"))
|
| 51 |
+
self.assertEqual(result["progress"], .75)
|
| 52 |
+
self.assertEqual(result["progress_raw"], .86)
|
| 53 |
+
self.assertEqual(result["progress_stage"], 3)
|
| 54 |
+
self.assertEqual(result["actions"].shape, (20, 7))
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
if __name__ == "__main__":
|
| 58 |
+
unittest.main()
|
code/pi05/scripts/test_robomem_memory.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import importlib.util
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import unittest
|
| 4 |
+
import tempfile
|
| 5 |
+
from flax import nnx
|
| 6 |
+
import jax
|
| 7 |
+
import jax.numpy as jnp
|
| 8 |
+
import numpy as np
|
| 9 |
+
import optax
|
| 10 |
+
from taskanchor.pi05.taskanchor import PrimaryMemoryAdapter, MemoryTTT
|
| 11 |
+
from openpi.training.robomem_memory_data import (
|
| 12 |
+
EpisodeIndex,
|
| 13 |
+
MemoryDataset,
|
| 14 |
+
history_indices,
|
| 15 |
+
cache_file,
|
| 16 |
+
validate_feature_file,
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
spec = importlib.util.spec_from_file_location("memory_trainer", Path(__file__).with_name("train_robomem_memory.py"))
|
| 20 |
+
trainer = importlib.util.module_from_spec(spec)
|
| 21 |
+
spec.loader.exec_module(trainer)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class MemoryTests(unittest.TestCase):
|
| 25 |
+
def test_primary_residual_and_masked_history(self):
|
| 26 |
+
adapter = PrimaryMemoryAdapter(rngs=nnx.Rngs(0), input_dim=32, hidden_dim=32, depth=2, heads=4)
|
| 27 |
+
current = jax.random.normal(jax.random.key(1), (2, 3, 4, 32)).astype(jnp.bfloat16)
|
| 28 |
+
frame0 = jax.random.normal(jax.random.key(2), (2, 16, 32)).astype(jnp.bfloat16)
|
| 29 |
+
history = jax.random.normal(jax.random.key(3), (2, 5, 16, 32)).astype(jnp.bfloat16)
|
| 30 |
+
mask = jnp.array([[1, 1, 0, 0, 0]] * 2, bool)
|
| 31 |
+
cmask = jnp.ones((2, 3, 4), bool)
|
| 32 |
+
refined, progress = adapter(current, cmask, history, mask, frame0=frame0)
|
| 33 |
+
np.testing.assert_array_equal(refined[:, 1:], current[:, 1:])
|
| 34 |
+
self.assertGreater(float(jnp.linalg.norm((refined[:, 0] - current[:, 0]).astype(jnp.float32))), 0)
|
| 35 |
+
altered, p2 = adapter(current, cmask, history.at[:, 2:].set(100), mask, frame0=frame0)
|
| 36 |
+
np.testing.assert_array_equal(refined, altered)
|
| 37 |
+
np.testing.assert_array_equal(progress, p2)
|
| 38 |
+
grad = jax.grad(lambda h: adapter(current, cmask, h, mask, frame0=frame0)[0][:, 0, 0, 0].astype(jnp.float32).sum())(
|
| 39 |
+
history
|
| 40 |
+
)
|
| 41 |
+
self.assertGreater(float(jnp.linalg.norm(grad[:, :2].astype(jnp.float32))), 0)
|
| 42 |
+
self.assertTrue(np.all(np.linalg.norm(np.asarray(grad[:, :2], np.float32), axis=-1) > 0))
|
| 43 |
+
np.testing.assert_array_equal(grad[:, 2:], jnp.zeros_like(grad[:, 2:]))
|
| 44 |
+
masked, _ = adapter(current, cmask.at[:, 0].set(False), history, mask, frame0=frame0)
|
| 45 |
+
np.testing.assert_array_equal(masked, current)
|
| 46 |
+
|
| 47 |
+
def test_current_never_writes_fast_weights(self):
|
| 48 |
+
ttt = MemoryTTT(32, 4, nnx.Rngs(5))
|
| 49 |
+
context = jax.random.normal(jax.random.key(6), (1, 6, 32)).astype(jnp.bfloat16)
|
| 50 |
+
write = jnp.array([[0, 0, 1, 1, 1, 1]], bool)
|
| 51 |
+
positions = jnp.arange(6)[None].astype(jnp.float32)
|
| 52 |
+
original = ttt(context, write, positions, 2)
|
| 53 |
+
changed = ttt(context.at[:, 0].multiply(10), write, positions, 2)
|
| 54 |
+
np.testing.assert_array_equal(original[:, 1], changed[:, 1])
|
| 55 |
+
|
| 56 |
+
def test_history_strictly_past_and_across_subtasks(self):
|
| 57 |
+
for t, expected in [(0, []), (10, []), (11, [1]), (31, [1, 11, 21])]:
|
| 58 |
+
self.assertEqual(history_indices(t).tolist(), expected)
|
| 59 |
+
self.assertEqual(len(history_indices(1917)), 191)
|
| 60 |
+
index = EpisodeIndex()
|
| 61 |
+
self.assertEqual(len(index.groups), 2600)
|
| 62 |
+
self.assertEqual(index.max_task_frames, 1918)
|
| 63 |
+
self.assertEqual(index.history_frames, 191)
|
| 64 |
+
for episodes in index.groups.values():
|
| 65 |
+
previous_end = 0
|
| 66 |
+
for ep in episodes:
|
| 67 |
+
r = index.records[ep]
|
| 68 |
+
self.assertEqual(r.prefix_frames, previous_end)
|
| 69 |
+
previous_end += r.length
|
| 70 |
+
self.assertTrue(np.all(history_indices(r.prefix_frames) < r.prefix_frames))
|
| 71 |
+
self.assertEqual(previous_end, r.total_frames)
|
| 72 |
+
for t in (0, previous_end // 2, previous_end - 1):
|
| 73 |
+
expected = list(reversed(range(t - 10, 0, -10)))
|
| 74 |
+
self.assertEqual(history_indices(t, capacity=index.history_frames).tolist(), expected)
|
| 75 |
+
|
| 76 |
+
def test_spatial_cache_dataset_preserves_frames_and_patches(self):
|
| 77 |
+
index = EpisodeIndex()
|
| 78 |
+
key = sorted(index.groups)[0]
|
| 79 |
+
episodes = index.groups[key]
|
| 80 |
+
total = index.records[episodes[0]].total_frames
|
| 81 |
+
cfg = trainer.config.get_config("pi05_robomemarena_26tasks_subtask_only_full_sft_eval")
|
| 82 |
+
with tempfile.TemporaryDirectory() as directory:
|
| 83 |
+
path = cache_file(directory, key)
|
| 84 |
+
spatial = np.broadcast_to(np.arange(total, dtype=np.float16)[:, None, None], (total, 16, 1152)).copy()
|
| 85 |
+
spatial[:, :, 0] = np.arange(16, dtype=np.float16)
|
| 86 |
+
np.save(path, spatial)
|
| 87 |
+
validate_feature_file(path, total)
|
| 88 |
+
dataset = MemoryDataset(index, cfg.data.create(cfg.assets_dirs, cfg.model), cfg.model, directory)
|
| 89 |
+
ep = episodes[-1]
|
| 90 |
+
pos = index.episodes.index(ep)
|
| 91 |
+
sample = dataset[int(index.ends[pos]) - 1]
|
| 92 |
+
selected = history_indices(total - 1)
|
| 93 |
+
self.assertEqual(sample["history"].shape, (191, 16, 1152))
|
| 94 |
+
np.testing.assert_array_equal(sample["history_mask"], np.arange(191) < len(selected))
|
| 95 |
+
np.testing.assert_array_equal(sample["history"][: len(selected)], spatial[selected])
|
| 96 |
+
np.testing.assert_array_equal(sample["history"][len(selected) :], 0)
|
| 97 |
+
np.testing.assert_array_equal(sample["frame0"], spatial[0])
|
| 98 |
+
self.assertIsInstance(dataset.features[key], np.memmap)
|
| 99 |
+
np.save(path, spatial.mean(axis=1))
|
| 100 |
+
with self.assertRaisesRegex(ValueError, "shape/dtype"):
|
| 101 |
+
validate_feature_file(path, total)
|
| 102 |
+
|
| 103 |
+
def test_siglip_head_recomposition(self):
|
| 104 |
+
from openpi.models.siglip import Module
|
| 105 |
+
|
| 106 |
+
encoder = Module(num_classes=16, variant="mu/14", pool_type="none", scan=True, head_zeroinit=False)
|
| 107 |
+
images = jax.random.normal(jax.random.key(7), (1, 28, 28, 3))
|
| 108 |
+
params = encoder.init(jax.random.key(8), images, train=False)
|
| 109 |
+
original, _ = encoder.apply(params, images, train=False)
|
| 110 |
+
native, _ = encoder.apply(params, images, train=False, return_encoded=True)
|
| 111 |
+
recomposed = encoder.apply(params, native, method="project_encoded")
|
| 112 |
+
self.assertGreater(float(jnp.linalg.norm(original)), 0)
|
| 113 |
+
np.testing.assert_array_equal(original, recomposed)
|
| 114 |
+
|
| 115 |
+
def test_repeated_initialization_has_identical_static_graph(self):
|
| 116 |
+
def create():
|
| 117 |
+
return PrimaryMemoryAdapter(rngs=nnx.Rngs(0), input_dim=32, hidden_dim=32, depth=2, heads=4)
|
| 118 |
+
|
| 119 |
+
self.assertEqual(nnx.graphdef(create()), nnx.graphdef(create()))
|
| 120 |
+
|
| 121 |
+
def test_exact_optimizer_phase_boundary(self):
|
| 122 |
+
class Tiny(nnx.Module):
|
| 123 |
+
def __init__(self):
|
| 124 |
+
self.adapter = nnx.Dict(weight=nnx.Param(jnp.ones(2)))
|
| 125 |
+
self.backbone = nnx.Dict(
|
| 126 |
+
weight=nnx.Param(jnp.ones(2)),
|
| 127 |
+
PaliGemma=nnx.Dict(
|
| 128 |
+
img=nnx.Dict(head=nnx.Dict(kernel=nnx.Param(jnp.ones(2))), encoder=nnx.Param(jnp.ones(2)))
|
| 129 |
+
),
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
params = nnx.state(Tiny()).filter(trainer.trainable)
|
| 133 |
+
self.assertNotIn("encoder", params["backbone"]["PaliGemma"]["img"])
|
| 134 |
+
tx = trainer.make_optimizer()
|
| 135 |
+
state = tx.init(params)
|
| 136 |
+
grads = jax.tree.map(jnp.ones_like, params)
|
| 137 |
+
|
| 138 |
+
@jax.jit
|
| 139 |
+
def update(p, s):
|
| 140 |
+
updates, s = tx.update(grads, s, p)
|
| 141 |
+
return optax.apply_updates(p, updates), s
|
| 142 |
+
|
| 143 |
+
initial = params.to_pure_dict()
|
| 144 |
+
for _ in range(5000):
|
| 145 |
+
params, state = update(params, state)
|
| 146 |
+
np.testing.assert_array_equal(params["backbone"]["weight"].value, initial["backbone"]["weight"])
|
| 147 |
+
np.testing.assert_array_equal(
|
| 148 |
+
params["backbone"]["PaliGemma"]["img"]["head"]["kernel"].value,
|
| 149 |
+
initial["backbone"]["PaliGemma"]["img"]["head"]["kernel"],
|
| 150 |
+
)
|
| 151 |
+
self.assertFalse(np.array_equal(params["adapter"]["weight"].value, initial["adapter"]["weight"]))
|
| 152 |
+
params, _ = update(params, state)
|
| 153 |
+
self.assertFalse(np.array_equal(params["backbone"]["weight"].value, initial["backbone"]["weight"]))
|
| 154 |
+
self.assertFalse(
|
| 155 |
+
np.array_equal(
|
| 156 |
+
params["backbone"]["PaliGemma"]["img"]["head"]["kernel"].value,
|
| 157 |
+
initial["backbone"]["PaliGemma"]["img"]["head"]["kernel"],
|
| 158 |
+
)
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
if __name__ == "__main__":
|
| 163 |
+
unittest.main()
|
code/pi05/scripts/test_robomem_memory_inference.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
import numpy as np
|
| 3 |
+
from openpi.policies.robomem_memory_inference import FIFOHistory
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class OnlineHistoryTests(unittest.TestCase):
|
| 7 |
+
def test_environment_stride_and_rollover(self):
|
| 8 |
+
bank = FIFOHistory()
|
| 9 |
+
for t in range(0, 2051, 5):
|
| 10 |
+
spatial = np.full((16, 1152), t, np.float16)
|
| 11 |
+
spatial[:, 0] = np.arange(16)
|
| 12 |
+
memory, mask = bank.add_and_read(t, spatial)
|
| 13 |
+
expected = ([0] + list(reversed(range(t - 10, 0, -10))))[-191:]
|
| 14 |
+
np.testing.assert_array_equal(memory[mask, 0, 1], expected)
|
| 15 |
+
np.testing.assert_array_equal(memory[mask, :, 0], np.broadcast_to(np.arange(16), (len(expected), 16)))
|
| 16 |
+
self.assertNotIn(0, bank.frames)
|
| 17 |
+
self.assertEqual(int(mask.sum()), 191)
|
| 18 |
+
bank.reset()
|
| 19 |
+
self.assertFalse(bank.frames)
|
| 20 |
+
_, mask = bank.add_and_read(0, np.zeros((16, 1152)))
|
| 21 |
+
self.assertEqual(int(mask.sum()), 1)
|
| 22 |
+
|
| 23 |
+
def test_rmbench_fifo25_bootstrap_and_eviction(self):
|
| 24 |
+
bank = FIFOHistory(capacity=25, stride=30)
|
| 25 |
+
for t in range(0, 1201, 30):
|
| 26 |
+
memory, mask = bank.add_and_read(t, np.full((16, 1152), t, np.float16))
|
| 27 |
+
expected = [0] if t == 0 else list(range(0, t, 30))[-25:]
|
| 28 |
+
np.testing.assert_array_equal(memory[mask, 0, 0], expected)
|
| 29 |
+
self.assertEqual(memory.shape, (25, 16, 1152))
|
| 30 |
+
if t == 750:
|
| 31 |
+
self.assertEqual(memory[mask, 0, 0].tolist(), list(range(0, 750, 30)))
|
| 32 |
+
if t == 780:
|
| 33 |
+
self.assertEqual(memory[mask, 0, 0].tolist(), list(range(30, 780, 30)))
|
| 34 |
+
self.assertNotIn(0, bank.frames)
|
| 35 |
+
self.assertFalse(hasattr(bank, "frame0"))
|
| 36 |
+
|
| 37 |
+
def test_invalid_first_frame_does_not_mutate_bank(self):
|
| 38 |
+
bank = FIFOHistory(capacity=25, stride=30)
|
| 39 |
+
with self.assertRaisesRegex(ValueError, "frame zero"):
|
| 40 |
+
bank.add_and_read(30, np.zeros((16, 1152)))
|
| 41 |
+
self.assertEqual(bank.last_frame, -1)
|
| 42 |
+
self.assertFalse(bank.frames)
|
| 43 |
+
|
| 44 |
+
def test_missing_frames_and_duplicate_indices_rejected(self):
|
| 45 |
+
bank = FIFOHistory()
|
| 46 |
+
bank.add_and_read(0, np.zeros((16, 1152)))
|
| 47 |
+
with self.assertRaises(ValueError):
|
| 48 |
+
bank.add_and_read(0, np.zeros((16, 1152)))
|
| 49 |
+
with self.assertRaisesRegex(ValueError, "Missing historical"):
|
| 50 |
+
bank.add_and_read(25, np.zeros((16, 1152)))
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
if __name__ == "__main__":
|
| 54 |
+
unittest.main()
|
code/pi05/scripts/test_robomem_memory_loader.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Verify batch equality, shared storage, epoch order, and producer failures."""
|
| 2 |
+
import unittest
|
| 3 |
+
import jax
|
| 4 |
+
import numpy as np
|
| 5 |
+
import torch
|
| 6 |
+
from openpi.training.robomem_memory_loader import DevicePrefetch, make_host_loader
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Samples:
|
| 10 |
+
def __len__(self):
|
| 11 |
+
return 12
|
| 12 |
+
|
| 13 |
+
def __getitem__(self, index):
|
| 14 |
+
return {"history": np.full((3, 4, 8), index, np.float16),
|
| 15 |
+
"mask": np.array([True, False, True]),
|
| 16 |
+
"data": {"ids": np.array([index, index + 1], np.int32)},
|
| 17 |
+
"progress": np.float32(index / 12)}
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class BrokenSamples(Samples):
|
| 21 |
+
def __getitem__(self, index):
|
| 22 |
+
raise ValueError("intentional producer failure")
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def numpy_collate(items):
|
| 26 |
+
return jax.tree.map(lambda *xs: np.stack([np.asarray(x) for x in xs]), *items)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class LoaderTests(unittest.TestCase):
|
| 30 |
+
def setUp(self):
|
| 31 |
+
self.shard = jax.sharding.NamedSharding(
|
| 32 |
+
jax.sharding.Mesh(np.array(jax.devices()), ("batch",)), jax.sharding.PartitionSpec("batch")
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
def test_same_batches_two_epochs(self):
|
| 36 |
+
expected = torch.utils.data.DataLoader(
|
| 37 |
+
Samples(), batch_size=4, num_workers=2, shuffle=True,
|
| 38 |
+
generator=torch.Generator().manual_seed(7), drop_last=True,
|
| 39 |
+
persistent_workers=True, multiprocessing_context="spawn", collate_fn=numpy_collate,
|
| 40 |
+
)
|
| 41 |
+
source = make_host_loader(Samples(), 4, workers=2, seed=7)
|
| 42 |
+
prefetch = DevicePrefetch(source, self.shard, depth=2)
|
| 43 |
+
try:
|
| 44 |
+
for _ in range(2):
|
| 45 |
+
for host in expected:
|
| 46 |
+
actual = next(prefetch)
|
| 47 |
+
for a, b in zip(jax.tree.leaves(actual), jax.tree.leaves(host), strict=True):
|
| 48 |
+
np.testing.assert_array_equal(np.asarray(a), np.asarray(b))
|
| 49 |
+
self.assertEqual(a.dtype, np.asarray(b).dtype)
|
| 50 |
+
finally:
|
| 51 |
+
prefetch.close()
|
| 52 |
+
expected._iterator._shutdown_workers()
|
| 53 |
+
|
| 54 |
+
def test_worker_output_is_shared(self):
|
| 55 |
+
source = make_host_loader(Samples(), 4, workers=1, seed=7)
|
| 56 |
+
it = iter(source)
|
| 57 |
+
try:
|
| 58 |
+
for batch in it:
|
| 59 |
+
for value in jax.tree.leaves(batch):
|
| 60 |
+
self.assertIsInstance(value, torch.Tensor)
|
| 61 |
+
self.assertTrue(value.is_shared())
|
| 62 |
+
finally:
|
| 63 |
+
it._shutdown_workers()
|
| 64 |
+
|
| 65 |
+
def test_exception_reaches_trainer(self):
|
| 66 |
+
prefetch = DevicePrefetch(make_host_loader(BrokenSamples(), 4, workers=0, seed=7), self.shard)
|
| 67 |
+
try:
|
| 68 |
+
with self.assertRaisesRegex(ValueError, "intentional producer failure"):
|
| 69 |
+
next(prefetch)
|
| 70 |
+
finally:
|
| 71 |
+
prefetch.close()
|
| 72 |
+
|
| 73 |
+
def test_finite_iterator(self):
|
| 74 |
+
prefetch = DevicePrefetch(make_host_loader(Samples(), 4, workers=0, seed=7), self.shard, repeat=False)
|
| 75 |
+
try:
|
| 76 |
+
self.assertEqual(len(list(prefetch)), 3)
|
| 77 |
+
finally:
|
| 78 |
+
prefetch.close()
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
if __name__ == "__main__":
|
| 82 |
+
unittest.main()
|
code/pi05/scripts/train_robomem_memory.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""pi0.5: 5k adapter-only, then 50k joint updates with frozen SigLIP."""
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import dataclasses
|
| 5 |
+
import json
|
| 6 |
+
import logging
|
| 7 |
+
import hashlib
|
| 8 |
+
import shutil
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
|
| 11 |
+
import jax
|
| 12 |
+
from taskanchor.paths import ASSETS_DIR, CHECKPOINT_ROOT
|
| 13 |
+
|
| 14 |
+
from openpi.training import checkpoints, config, sharding, weight_loaders
|
| 15 |
+
from openpi.training.robomem_memory_data import (
|
| 16 |
+
BASE_PARAMS,
|
| 17 |
+
CACHE_ROOT,
|
| 18 |
+
DATA_ROOT,
|
| 19 |
+
EpisodeIndex,
|
| 20 |
+
MemoryDataset,
|
| 21 |
+
validate_cache,
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
from taskanchor.pi05.training import (
|
| 26 |
+
label as label,
|
| 27 |
+
trainable as trainable,
|
| 28 |
+
adapter_only as adapter_only,
|
| 29 |
+
make_optimizer as make_optimizer,
|
| 30 |
+
init_state,
|
| 31 |
+
train_step as train_step,
|
| 32 |
+
CheckpointData,
|
| 33 |
+
run_training_loop,
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def recipe(args):
|
| 38 |
+
original = config.get_config("pi05_robomemarena_26tasks_subtask_only_full_sft_eval")
|
| 39 |
+
return dataclasses.replace(
|
| 40 |
+
original,
|
| 41 |
+
name="pi05_robomemarena_memory_v5_main",
|
| 42 |
+
exp_name=args.run_name,
|
| 43 |
+
model=dataclasses.replace(original.model, action_horizon=args.action_horizon),
|
| 44 |
+
data=dataclasses.replace(original.data, assets=dataclasses.replace(original.data.assets, assets_dir=args.assets_dir)),
|
| 45 |
+
batch_size=args.batch_size,
|
| 46 |
+
num_workers=args.workers,
|
| 47 |
+
num_train_steps=args.steps,
|
| 48 |
+
fsdp_devices=args.fsdp_devices,
|
| 49 |
+
ema_decay=None,
|
| 50 |
+
wandb_enabled=False,
|
| 51 |
+
save_interval=args.save_interval,
|
| 52 |
+
keep_period=5000,
|
| 53 |
+
checkpoint_base_dir=args.checkpoint_root,
|
| 54 |
+
weight_loader=weight_loaders.CheckpointWeightLoader(args.base_params),
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def main(args):
|
| 59 |
+
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s", force=True)
|
| 60 |
+
if not 0 < args.freeze_steps < args.steps:
|
| 61 |
+
raise ValueError("Require 0 < freeze_steps < steps")
|
| 62 |
+
if not 0 < args.execution_horizon <= args.action_horizon or 10 % args.execution_horizon:
|
| 63 |
+
raise ValueError("Execution horizon must divide history stride 10 and not exceed action horizon")
|
| 64 |
+
if any(d.platform != "gpu" for d in jax.devices()):
|
| 65 |
+
raise RuntimeError("Training requires GPUs; refusing a silent CPU fallback")
|
| 66 |
+
if args.batch_size % jax.device_count():
|
| 67 |
+
raise ValueError("Batch size must be divisible by GPU count")
|
| 68 |
+
index = EpisodeIndex(args.dataset)
|
| 69 |
+
logging.info("Verifying every cache file before training")
|
| 70 |
+
manifest = validate_cache(index, args.cache, args.base_params)
|
| 71 |
+
cfg = recipe(args)
|
| 72 |
+
dc = cfg.data.create(cfg.assets_dirs, cfg.model)
|
| 73 |
+
dataset = MemoryDataset(index, dc, cfg.model, args.cache)
|
| 74 |
+
mesh = sharding.make_mesh(args.fsdp_devices)
|
| 75 |
+
data_sharding = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec(sharding.DATA_AXIS))
|
| 76 |
+
replicated = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec())
|
| 77 |
+
manager, resume = checkpoints.initialize_checkpoint_dir(
|
| 78 |
+
cfg.checkpoint_dir, keep_period=5000, overwrite=False, resume=args.resume
|
| 79 |
+
)
|
| 80 |
+
metadata = dict(
|
| 81 |
+
metadata_filename="robomem_memory.json",
|
| 82 |
+
**vars(args),
|
| 83 |
+
cache_manifest=manifest,
|
| 84 |
+
model=dataclasses.asdict(cfg.model),
|
| 85 |
+
architecture="primary_camera_v5_spatial_history_offset_512_1152",
|
| 86 |
+
memory_layout="frame0_and_history",
|
| 87 |
+
frame0="separate",
|
| 88 |
+
history_stride=10,
|
| 89 |
+
history_capacity=index.history_frames,
|
| 90 |
+
max_task_frames=index.max_task_frames,
|
| 91 |
+
history_tokens_per_frame=16,
|
| 92 |
+
freeze_vision=True,
|
| 93 |
+
progress_target="round(subtask_id/num_subtasks,2)",
|
| 94 |
+
loss="flow+0.01*(100*MSE+1-Pearson)",
|
| 95 |
+
)
|
| 96 |
+
metadata_path = Path(cfg.checkpoint_dir) / "recipe.json"
|
| 97 |
+
if resume:
|
| 98 |
+
old = json.loads(metadata_path.read_text())
|
| 99 |
+
# Original50k recipes predate these explicit fields. Architecture identifies them.
|
| 100 |
+
from taskanchor.data.history import validate_robomem_recipe
|
| 101 |
+
validate_robomem_recipe(old)
|
| 102 |
+
old.setdefault("memory_layout", "frame0_and_history")
|
| 103 |
+
old.setdefault("frame0", "separate")
|
| 104 |
+
for key in (
|
| 105 |
+
"base_params",
|
| 106 |
+
"dataset",
|
| 107 |
+
"cache_manifest",
|
| 108 |
+
"freeze_steps",
|
| 109 |
+
"steps",
|
| 110 |
+
"batch_size",
|
| 111 |
+
"model",
|
| 112 |
+
"architecture",
|
| 113 |
+
"memory_layout",
|
| 114 |
+
"frame0",
|
| 115 |
+
"history_capacity",
|
| 116 |
+
"history_stride",
|
| 117 |
+
"history_tokens_per_frame",
|
| 118 |
+
):
|
| 119 |
+
if old.get(key) != metadata[key]:
|
| 120 |
+
raise ValueError(f"Resume recipe changed: {key}")
|
| 121 |
+
else:
|
| 122 |
+
root = Path(__file__).resolve().parents[1]
|
| 123 |
+
snapshot = Path(cfg.checkpoint_dir) / "source_snapshot"
|
| 124 |
+
shutil.copytree(
|
| 125 |
+
root / "src", snapshot / "src", ignore=shutil.ignore_patterns("__pycache__", "*.pyc"), dirs_exist_ok=True
|
| 126 |
+
)
|
| 127 |
+
shutil.copytree(
|
| 128 |
+
root / "scripts",
|
| 129 |
+
snapshot / "scripts",
|
| 130 |
+
ignore=shutil.ignore_patterns("__pycache__", "*.pyc"),
|
| 131 |
+
dirs_exist_ok=True,
|
| 132 |
+
)
|
| 133 |
+
shutil.copytree(
|
| 134 |
+
root.parent / "taskanchor", snapshot / "taskanchor",
|
| 135 |
+
ignore=shutil.ignore_patterns("__pycache__", "*.pyc"), dirs_exist_ok=True,
|
| 136 |
+
)
|
| 137 |
+
metadata["source_sha256"] = {
|
| 138 |
+
str(path.relative_to(snapshot)): hashlib.sha256(path.read_bytes()).hexdigest()
|
| 139 |
+
for path in sorted(snapshot.rglob("*.py"))
|
| 140 |
+
}
|
| 141 |
+
metadata_path.write_text(json.dumps(metadata, indent=2))
|
| 142 |
+
logging.info(
|
| 143 |
+
"Spatial memory: max_task_frames=%d history_frames=%d tokens_per_frame=16 memory_tokens=%d",
|
| 144 |
+
index.max_task_frames,
|
| 145 |
+
index.history_frames,
|
| 146 |
+
(index.history_frames + 1) * 16,
|
| 147 |
+
)
|
| 148 |
+
logging.info("Initializing sharded pi0.5 + adapter")
|
| 149 |
+
state, state_sharding = init_state(cfg, args, mesh, resume)
|
| 150 |
+
checkpoint_data = CheckpointData(dc)
|
| 151 |
+
if resume:
|
| 152 |
+
state = checkpoints.restore_state(manager, state, checkpoint_data, step=args.resume_step)
|
| 153 |
+
step0 = int(state.step)
|
| 154 |
+
if args.resume_step is not None and (not resume or step0 != args.resume_step):
|
| 155 |
+
raise ValueError(f"Expected resume step {args.resume_step}, restored {step0}")
|
| 156 |
+
logging.info(
|
| 157 |
+
"Model initialized; start_step=%d total_steps=%d freeze_steps=%d", step0, args.steps, args.freeze_steps
|
| 158 |
+
)
|
| 159 |
+
run_training_loop(
|
| 160 |
+
args,
|
| 161 |
+
cfg,
|
| 162 |
+
dataset,
|
| 163 |
+
data_sharding,
|
| 164 |
+
replicated,
|
| 165 |
+
state_sharding,
|
| 166 |
+
state,
|
| 167 |
+
step0,
|
| 168 |
+
mesh,
|
| 169 |
+
manager,
|
| 170 |
+
checkpoint_data,
|
| 171 |
+
metadata,
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def build_argparser():
|
| 176 |
+
parser = argparse.ArgumentParser()
|
| 177 |
+
parser.add_argument("--dataset", default=str(DATA_ROOT))
|
| 178 |
+
parser.add_argument("--base-params", default=str(BASE_PARAMS))
|
| 179 |
+
parser.add_argument("--cache", default=str(CACHE_ROOT))
|
| 180 |
+
parser.add_argument("--assets-dir", default=str(ASSETS_DIR), help="Directory containing pi05_libero/norm_stats.json")
|
| 181 |
+
parser.add_argument("--run-name", default="main_v5_spatial4x4_h191_a20_exec10_bs256_5k50k")
|
| 182 |
+
parser.add_argument("--checkpoint-root", default=str(CHECKPOINT_ROOT))
|
| 183 |
+
parser.add_argument("--steps", type=int, default=55000)
|
| 184 |
+
parser.add_argument("--freeze-steps", type=int, default=5000)
|
| 185 |
+
parser.add_argument("--batch-size", type=int, default=256)
|
| 186 |
+
parser.add_argument("--action-horizon", type=int, default=20)
|
| 187 |
+
parser.add_argument(
|
| 188 |
+
"--execution-horizon",
|
| 189 |
+
type=int,
|
| 190 |
+
default=10,
|
| 191 |
+
help="Execution length for future deployment; training supervises the full action horizon",
|
| 192 |
+
)
|
| 193 |
+
parser.add_argument("--fsdp-devices", type=int, default=8)
|
| 194 |
+
parser.add_argument("--workers", type=int, default=8)
|
| 195 |
+
parser.add_argument("--save-interval", type=int, default=1000)
|
| 196 |
+
parser.add_argument("--log-interval", type=int, default=20)
|
| 197 |
+
parser.add_argument("--resume", action="store_true")
|
| 198 |
+
parser.add_argument("--resume-step", type=int, default=None)
|
| 199 |
+
parser.add_argument("--shared-memory-loader", action="store_true")
|
| 200 |
+
parser.add_argument("--prefetch-factor", type=int, default=2)
|
| 201 |
+
parser.add_argument("--device-prefetch", type=int, default=2)
|
| 202 |
+
return parser
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
if __name__ == "__main__":
|
| 206 |
+
main(build_argparser().parse_args())
|
code/pi05/src/openpi/__init__.py
ADDED
|
File without changes
|