From fb1bd51046456097958fb12bd416f31c28f03a43 Mon Sep 17 00:00:00 2001 From: seeker Date: Sat, 11 Jul 2026 13:08:42 +0800 Subject: [PATCH] Add 0.5B Alpamayo validation --- .gitignore | 22 + LICENSE | 201 +++ README.md | 341 ++++ THIRD_PARTY_NOTICES.md | 26 + configs/local_overfit.yaml | 56 + pyproject.toml | 47 + results/complete_comparison/comparison.json | 1381 +++++++++++++++++ .../trajectory_comparison.png | Bin 0 -> 345595 bytes results/complete_comparison/waypoints.csv | 17 + scripts/build_complete_comparison.py | 722 +++++++++ scripts/download_model.py | 48 + scripts/evaluate_pipeline.py | 244 +++ scripts/materialize_pai_subset.py | 204 +++ scripts/train_stage1_vlm.py | 136 ++ scripts/train_stage2_expert.py | 451 ++++++ scripts/train_vlm_trajectory_logits.py | 662 ++++++++ scripts/verify_connection.py | 106 ++ scripts/verify_structured_vlm_handoff.py | 226 +++ src/alpamayo_lite/__init__.py | 5 + src/alpamayo_lite/action_space.py | 56 + src/alpamayo_lite/config.py | 112 ++ src/alpamayo_lite/data.py | 54 + src/alpamayo_lite/diffusion.py | 57 + src/alpamayo_lite/evaluation.py | 72 + src/alpamayo_lite/expert.py | 237 +++ src/alpamayo_lite/prompts.py | 44 + src/alpamayo_lite/runtime.py | 60 + src/alpamayo_lite/trajectory_tokens.py | 179 +++ src/alpamayo_lite/vlm.py | 181 +++ tests/test_action_space.py | 13 + tests/test_complete_comparison.py | 46 + tests/test_diffusion.py | 11 + tests/test_expert_contract.py | 69 + tests/test_trajectory_tokens.py | 39 + 34 files changed, 6125 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 THIRD_PARTY_NOTICES.md create mode 100644 configs/local_overfit.yaml create mode 100644 pyproject.toml create mode 100644 results/complete_comparison/comparison.json create mode 100644 results/complete_comparison/trajectory_comparison.png create mode 100644 results/complete_comparison/waypoints.csv create mode 100644 scripts/build_complete_comparison.py create mode 100644 scripts/download_model.py create mode 100644 scripts/evaluate_pipeline.py create mode 100644 scripts/materialize_pai_subset.py create mode 100644 scripts/train_stage1_vlm.py create mode 100644 scripts/train_stage2_expert.py create mode 100644 scripts/train_vlm_trajectory_logits.py create mode 100644 scripts/verify_connection.py create mode 100644 scripts/verify_structured_vlm_handoff.py create mode 100644 src/alpamayo_lite/__init__.py create mode 100644 src/alpamayo_lite/action_space.py create mode 100644 src/alpamayo_lite/config.py create mode 100644 src/alpamayo_lite/data.py create mode 100644 src/alpamayo_lite/diffusion.py create mode 100644 src/alpamayo_lite/evaluation.py create mode 100644 src/alpamayo_lite/expert.py create mode 100644 src/alpamayo_lite/prompts.py create mode 100644 src/alpamayo_lite/runtime.py create mode 100644 src/alpamayo_lite/trajectory_tokens.py create mode 100644 src/alpamayo_lite/vlm.py create mode 100644 tests/test_action_space.py create mode 100644 tests/test_complete_comparison.py create mode 100644 tests/test_diffusion.py create mode 100644 tests/test_expert_contract.py create mode 100644 tests/test_trajectory_tokens.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57e9b87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +.venv/ +.cache/ +.ruff_cache/ +models/ +data/raw/ +data/processed/ +outputs/ +references/ +*.egg-info/ +*.pyc +__pycache__/ +.pytest_cache/ +.DS_Store +.env +.env.* +wandb/ +*.safetensors +*.pt +*.pth +*.parquet +*.npz +*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, 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 or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..91c82ac --- /dev/null +++ b/README.md @@ -0,0 +1,341 @@ +# alpamayo-0.5B + +`alpamayo-0.5B` 是一个单机、单样本的 Alpamayo 接口验证工程。它把一个带 +Qwen2.5-0.5B 文本骨干的 VLM 接到缩小版 diffusion expert,验证以下链路能够实际 +训练、保存、重新加载和推理: + +1. 图像、自然语言和 ego-relative 历史坐标进入 VLM; +2. VLM 生成驾驶文字,也可以在未截断验证分支中生成结构化 CoT、meta-action 和 + 离散轨迹 token; +3. VLM 的 24 层 past-KV 作为 expert 的前缀; +4. flow-matching diffusion 预测连续 action,再积分回未来 XY 位置。 + +这是针对连接方式和输出契约的最小过拟合实验,不是 NVIDIA Alpamayo-R1 的完整复现, +也不是可部署的驾驶模型。 + +## 架构 + +```mermaid +flowchart LR + A["前视图像"] --> V["LLaVA VLM"] + B["驾驶问题与历史 XY"] --> V + Q["CLIP vision tower"] --> V + L["Qwen2.5-0.5B text backbone"] --> V + V --> T["驾驶文字 / structured tokens"] + V --> K["24-layer past-KV"] + N["noisy normalized XY-delta"] --> E["105.6M Qwen2 expert"] + S["diffusion timestep"] --> E + K --> E + E --> F["flow-matching vector field"] + F --> D["10-step denoising"] + D --> X["16 x 2 continuous action"] + X --> P["ego-relative future XY"] +``` + +连接契约不是按 hidden size 复制 VLM。expert 采用自己的 `hidden_size=512`,但保持与 +VLM KV cache 相同的 24 层、2 个 KV heads 和 `head_dim=64`,因此可以直接消费缓存。 +expert 没有词嵌入层;noisy action 和 timestep 经 Fourier/MLP 投影成 token embeddings, +最后由线性层输出二维 vector field。 + +| 组件 | 本地配置 | +| --- | --- | +| VLM checkpoint | `thkim0305/qwen2.5_0.5B_vl`,revision `c318c4b464753d86ddffe065ba6df255ae354f41` | +| VLM 类型 | `LlavaForConditionalGeneration`;CLIP vision tower + Qwen2.5-0.5B text backbone | +| VLM 参数量 | 799,262,336(“0.5B”指文本骨干;加入视觉塔后完整 VLM 约 0.80B) | +| VLM text cache | 24 层,2 KV heads,`head_dim=64` | +| Expert | 105,575,170 参数;24 层,hidden 512,intermediate 2432,8 query heads,2 KV heads,non-causal | +| Diffusion 输出 | 16 个二维 normalized XY-delta action;10 个 flow-matching inference steps | +| 运行精度 | 当前产物为 MPS + float16;代码也可选择可用的 CUDA 或 CPU | + +主配置位于 [`configs/local_overfit.yaml`](configs/local_overfit.yaml)。 + +## 单样本 PAI 数据 + +实验只使用 `nvidia/PhysicalAI-Autonomous-Vehicles` 的一个 `train` 事件。PAI 是 gated +dataset,使用前需要在 Hugging Face 接受其许可。 + +| 字段 | 值 | +| --- | --- | +| Clip ID | `314f59c2-e420-4fcd-9df2-a51d572e0c20` | +| `t0_us` | `1503899` | +| Camera | `camera_front_wide_120fov` | +| Event cluster | `WORK_ZONES_TEMP_TRAFFIC_CONTROL` | +| Reasoning source | `reasoning/ood_reasoning.parquet` 中的 `coc` 字段 | +| Reasoning | “Steer left following the temporary lane delineated by traffic cones through the construction zone.” | +| History | 8 个点,最后一个点为当前 ego-local 原点 | +| Future source | egomotion 的 64 个未来点,时间步 0.1 s | +| Expert target | 从 64 点以索引 `[0,4,8,12,16,21,25,29,33,37,42,46,50,54,58,63]` 取 16 点 | + +[`scripts/materialize_pai_subset.py`](scripts/materialize_pai_subset.py) 将世界坐标平移、旋转到 +`t0` ego-relative frame,并把单样本 manifest、图像和轨迹写入本地 `data/processed/`。 +原始/处理后的 PAI 数据都被 `.gitignore` 排除,不应提交到仓库。 + +## 训练阶段 + +### Stage 1:自然语言驾驶输出 + +[`scripts/train_stage1_vlm.py`](scripts/train_stage1_vlm.py) 冻结 vision tower、multimodal +projector 和 LM head,只训练最后 8 个 Qwen2 language layers 及 final norm。当前 checkpoint +训练 80 steps,共 119,299,968 个可训练参数:loss 从 `3.9338069` 降至 +`0.0000216427`,greedy 输出与数据集 reasoning 逐字一致。 + +Stage 1 输出到本地 `outputs/stage1_vlm/`,供后续两个分支使用: + +- plain-text KV 分支训练现有 diffusion expert; +- structured VLM 分支继续训练轨迹 logits。 + +### VLM structured 未截断轨迹-token 分支 + +[`scripts/train_vlm_trajectory_logits.py`](scripts/train_vlm_trajectory_logits.py) 从 Stage 1 +checkpoint 继续训练,并添加: + +- 128 个连续 token:`` … ``; +- `<|cot_start|>`、`<|cot_end|>`; +- `<|meta_action_start|>`、`<|meta_action_end|>`; +- `<|traj_future_start|>`、`<|traj_future_end|>`。 + +当前 Qwen LM head 已预留 151,936 行,而原 tokenizer 长度为 151,666;扩展后的 151,800 +个 token 不需要增大权重矩阵。每个 16 x 2 normalized XY-delta action 被均匀量化到 +`[-4, 4]` 的 128 bins,形成 32 个监督 token。 + +当前结构化 checkpoint 训练 160 steps,loss 从 `14.17349` 降至 `0.0000347825`,最终 +训练 accuracy 为 1.0。未截断 greedy generation 生成 32/32 个正确轨迹 token,其完整 +目标/输出格式为: + +```text +<|cot_start|>Steer left following the temporary lane delineated by traffic cones through the construction zone.<|cot_end|> +<|meta_action_start|>STEER_LEFT<|meta_action_end|> +<|traj_future_start|><|traj_future_end|><|im_end|> +``` + +另有 source-style handoff 验证:generation 到 `<|traj_future_start|>` 后屏蔽全部 128 个 +trajectory logits,并把 exact generation cache 交给 diffusion。这个模式验证了 KV 接口, +但不会把 VLM 离散轨迹 token 作为 diffusion 分支的可见输出。 + +### Stage 2:KV-conditioned diffusion expert + +[`scripts/train_stage2_expert.py`](scripts/train_stage2_expert.py) 使用 Stage 1 plain-text prefix +的 past-KV,训练 105.6M expert 预测 flow-matching vector field。目标 action 是按单样本统计量 +标准化的相邻 waypoint XY delta,预测 action 经反标准化和累积求和恢复未来轨迹。 + +当前产物记录为 1,000 total steps:早期 250-step 权重缺少 optimizer state,之后以 legacy +warm start 运行了剩余 750 steps。因此它是 1,000-step 参数训练记录,但不是严格连续、 +optimizer 完整恢复的 1,000-step run。新 checkpoint 已保存 expert、optimizer、step 和 RNG, +后续中断可用 `--resume` 精确恢复。 + +普通 reload evaluation 仍使用训练 expert 时的旧 plain-text prefix(sequence length 770)。 +structured exact-cache handoff 的 prefix 长度为 777;接口验证成功,但旧 expert 没有在这个 +新 prefix 分布上联合训练,所以该分支的轨迹质量只作诊断。 + +## 目录 + +```text +alpamayo-0.5B/ +├── configs/ # 单样本实验配置 +├── scripts/ # 下载、数据准备、训练、评估和比较入口 +├── src/alpamayo_lite/ # VLM、action space、expert、diffusion 实现 +├── tests/ # 单元测试和产物一致性测试 +├── results/complete_comparison/ # 仓库中保留的轻量比较 JSON、CSV 和三方图 +├── references/alpamayo-upstream/# 可选的本地只读上游 checkout;gitignored +├── models/ # 本地模型;gitignored +├── data/ # gated/raw/processed 数据;gitignored +├── outputs/ # checkpoint 和评估产物;gitignored +└── .venv/ # Python 3.12 虚拟环境;gitignored +``` + +## 安装 + +需要 Python 3.12。以下命令把环境和所有运行产物留在仓库目录内: + +```bash +git clone alpamayo-0.5B +cd alpamayo-0.5B +python3.12 -m venv .venv +source .venv/bin/activate +python -m pip install --upgrade pip +python -m pip install -e '.[dev]' +``` + +登录 Hugging Face 时使用交互式认证,不要把 access token 写进配置、脚本、README、日志或 +shell history: + +```bash +hf auth login +``` + +## 运行 + +所有命令均从仓库根目录执行。 + +下载 0.5B VLM,并记录 checkpoint provenance: + +```bash +python scripts/download_model.py --config configs/local_overfit.yaml +``` + +下载并物化一个 PAI 样本,然后验证 VLM KV 与 expert 的形状契约: + +```bash +python scripts/materialize_pai_subset.py --config configs/local_overfit.yaml +python scripts/verify_connection.py --config configs/local_overfit.yaml +``` + +训练 Stage 1 自然语言 VLM: + +```bash +python scripts/train_stage1_vlm.py \ + --config configs/local_overfit.yaml \ + --steps 80 +``` + +训练并验证 VLM 的 structured/untruncated trajectory logits: + +```bash +python scripts/train_vlm_trajectory_logits.py \ + --config configs/local_overfit.yaml \ + --output-dir outputs/stage1_vlm_trajectory \ + --steps 160 \ + --trajectory-vocab-size 128 \ + --action-min -4 \ + --action-max 4 \ + --max-new-tokens 96 +``` + +从头训练 Stage 2 到 1,000 total steps: + +```bash +python scripts/train_stage2_expert.py \ + --config configs/local_overfit.yaml \ + --steps 1000 +``` + +若 `outputs/stage2_expert/checkpoints/latest/` 中已有完整 checkpoint,则精确续训到总计 +1,000 steps: + +```bash +python scripts/train_stage2_expert.py \ + --config configs/local_overfit.yaml \ + --steps 1000 \ + --resume +``` + +重新加载 VLM/expert,固定从 seed 12 开始做 16 个 diffusion rollouts: + +```bash +python scripts/evaluate_pipeline.py \ + --config configs/local_overfit.yaml \ + --stage2-dir outputs/stage2_expert \ + --num-rollouts 16 \ + --base-seed 12 \ + --output outputs/reload_evaluation.json +``` + +验证 source-style structured VLM exact cache 能直接进入旧 expert: + +```bash +python scripts/verify_structured_vlm_handoff.py \ + --config configs/local_overfit.yaml \ + --vlm-dir outputs/stage1_vlm_trajectory \ + --stage2-dir outputs/stage2_expert \ + --output-dir outputs/structured_vlm_handoff +``` + +汇总 GT、VLM direct trajectory 和 diffusion seed 12: + +```bash +python scripts/build_complete_comparison.py \ + --diffusion-seed 12 \ + --output-dir outputs/complete_comparison +``` + +## 当前结果 + +两条预测使用逐字节相同的 GT action 和 GT XY。下表由保存的 NPZ 重新计算;action MSE 的 +单位是 normalized-action squared,轨迹指标单位是米。 + +| 输出 | 解码协议 | Action MSE | ADE | FDE | Max error | +| --- | --- | ---: | ---: | ---: | ---: | +| Ground truth | GT 对自身 | 0 | 0 m | 0 m | 0 m | +| VLM trajectory tokens | greedy、未截断、128-bin | 0.0003439452 | 0.026104664 m | 0.045444489 m | 0.054956669 m | +| Diffusion | flow matching、seed 12、10 steps | 0.7453363983 | 1.125946622 m | 0.258275141 m | 2.925430649 m | + +VLM 的 32 个生成 token 与量化 GT 全部匹配,因此其非零轨迹误差完全来自 128-bin +反量化。Diffusion 是随机采样;seed 12 只代表一个 rollout。16 个 seed 的平均 ADE 为 +`1.530302633 m`,不应把 seed 12 当作总体均值。 + +![GT、VLM 与 diffusion 三方比较](results/complete_comparison/trajectory_comparison.png) + +### 文字对比 + +| 字段 | Ground truth | 模型输出 | Exact match | +| --- | --- | --- | ---: | +| Reasoning / CoT | Steer left following the temporary lane delineated by traffic cones through the construction zone. | Steer left following the temporary lane delineated by traffic cones through the construction zone. | 是 | +| Meta-action | `STEER_LEFT`(由 reasoning 规则派生) | `STEER_LEFT` | 是 | +| Diffusion conditioning text | 同上方 reasoning | 同上方 reasoning | 是 | + +## 本地产物 + +仓库保留以下不含权重或 gated 样本的轻量结果,可直接审计完整三方对比: + +- [GT/VLM/diffusion 完整比较 JSON](results/complete_comparison/comparison.json) +- [16 个 waypoint 的完整 CSV](results/complete_comparison/waypoints.csv) +- [GT/VLM/diffusion 三方比较图](results/complete_comparison/trajectory_comparison.png) + +`models/`、`data/` 和 `outputs/` 默认不进入 Git。运行完成后,以下链接仅在本地工作区存在; +不要为了让它们在 GitHub 页面可用而提交 gated data 或 checkpoint 权重。 + +- [Stage 1 文字训练指标](outputs/stage1_vlm/stage1_metrics.json) +- [Structured VLM 训练、logits、cache 与生成指标](outputs/stage1_vlm_trajectory/trajectory_logits_metrics.json) +- [Structured VLM trajectory vocabulary](outputs/stage1_vlm_trajectory/trajectory_vocabulary.json) +- [Structured VLM 轨迹图](outputs/stage1_vlm_trajectory/vlm_discrete_trajectory.png) +- [Stage 2 训练指标](outputs/stage2_expert/stage2_metrics.json) +- [Stage 2 expert 配置](outputs/stage2_expert/expert_config.json) +- [Stage 2 seed-12 reload 轨迹图](outputs/stage2_expert/reload_trajectory.png) +- [16-seed reload 评估](outputs/reload_evaluation.json) +- [Structured exact-cache handoff 指标](outputs/structured_vlm_handoff/handoff_metrics.json) +- [Structured exact-cache handoff 轨迹图](outputs/structured_vlm_handoff/handoff_trajectory.png) +- [本地 GT/VLM/diffusion 完整比较 JSON](outputs/complete_comparison/comparison.json) +- [本地 16 个 waypoint CSV](outputs/complete_comparison/waypoints.csv) +- [本地 GT/VLM/diffusion 比较图](outputs/complete_comparison/trajectory_comparison.png) + +没有列出或链接任何 checkpoint 权重文件。 + +## 测试状态 + +发布或提交结果前运行: + +```bash +pytest -q +ruff check src scripts tests +``` + +| 检查 | 最近结果 | +| --- | --- | +| `pytest -q` | `11 passed` | +| `ruff check src scripts tests` | `All checks passed` | + +## 已知限制与安全边界 + +- 这是一个 PAI `train` 样本的显式过拟合实验,没有验证跨 clip、跨场景或真实驾驶泛化。 +- 本地 VLM 轨迹词表只有 128 bins,action 是 normalized per-waypoint XY delta;它不是上游 + 使用的 768-token vocabulary 或约 4,000-bin delta-tokenizer 配置,也不是上游 + unicycle acceleration/curvature action space。 +- action mean/scale 由同一个单样本目标轨迹拟合;VLM direct trajectory 的 32 个监督 token + 也来自该目标,因此其近零误差只证明记忆和输出链路。 +- `STEER_LEFT` 是 [`_infer_meta_action`](scripts/train_vlm_trajectory_logits.py) 从 reasoning + 文字规则派生的标签,不是 PAI 中独立提供的 meta-action ground truth。 +- 当前 expert 用旧 plain-text `"Continuous action:"` prefix 训练,并未与 structured VLM + prefix 或 exact generation cache 联合训练。structured handoff 证明 24-layer KV 兼容, + 不代表该 prefix 下的轨迹质量已经收敛。 +- source-style handoff 会在 `<|traj_future_start|>` 屏蔽 VLM 离散轨迹 logits;未截断 VLM + 轨迹输出是一个单独的能力验证分支。 +- 当前 1,000-step expert 包含一次缺少 optimizer state 的 legacy warm start;需要全新、严格 + 连续的训练曲线时应删除本地旧产物并从头运行。 +- 模型输出和本项目都不能用于控制真实车辆或做安全决策。 +- 不要提交或发布 Hugging Face access token、认证缓存、gated PAI 原始/处理数据、下载的模型 + 权重、训练 checkpoint 或可能包含私有路径/凭据的日志。公开前应再次检查 `git status` 和 + Git 历史;仅提交有权再分发的代码与文档。 + +本项目代码按 [Apache-2.0](LICENSE) 发布。上游归属、修改说明及不随仓库分发的模型/数据 +声明见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。本地工作区可另行检出 +`references/alpamayo-upstream/` 进行接口研究,但该目录不会随本仓库分发。 diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..fc9807e --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,26 @@ +# Third-party notices + +This repository is an unofficial, Alpamayo-inspired local validation project. It is not +affiliated with, endorsed by, or a release of NVIDIA Corporation. + +## NVlabs/alpamayo + +Parts of the local expert connection pattern and flow-matching implementation were derived +from or informed by [`NVlabs/alpamayo`](https://github.com/NVlabs/alpamayo), inspected at +commit `4b96c546ce3416e7b1e4d86e7649662852c3c1ac`. + +- Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +- Licensed under the Apache License, Version 2.0. +- Locally modified files retain their SPDX copyright and license headers. + +The upstream source checkout is not redistributed in this repository. + +## Models and datasets + +The code references the Hugging Face checkpoint `thkim0305/qwen2.5_0.5B_vl` and the gated +dataset `nvidia/PhysicalAI-Autonomous-Vehicles`. Neither downloaded model weights nor raw or +processed dataset samples are redistributed here. Their respective repository terms and +licenses apply independently from this project's Apache-2.0 code license. + +Users must obtain their own authorized access, review the applicable terms, and avoid using +this research smoke test for real-vehicle control or safety decisions. diff --git a/configs/local_overfit.yaml b/configs/local_overfit.yaml new file mode 100644 index 0000000..2ae5af6 --- /dev/null +++ b/configs/local_overfit.yaml @@ -0,0 +1,56 @@ +seed: 11 + +paths: + model_dir: models/qwen2.5_0.5B_vl + pai_raw_dir: data/raw/pai + manifest: data/processed/overfit_manifest.jsonl + stage1_dir: outputs/stage1_vlm + stage2_dir: outputs/stage2_expert + +vlm: + repo_id: thkim0305/qwen2.5_0.5B_vl + dtype: float16 + attn_implementation: eager + max_reasoning_tokens: 48 + num_additional_image_tokens: 1 + +data: + repo_id: nvidia/PhysicalAI-Autonomous-Vehicles + num_clips: 1 + camera: camera_front_wide_120fov + num_frames: 1 + history_steps: 8 + source_future_steps: 64 + expert_waypoints: 16 + time_step: 0.1 + +stage1: + steps: 80 + learning_rate: 0.0001 + weight_decay: 0.0 + adam_epsilon: 0.0001 + freeze_vision_tower: true + freeze_multimodal_projector: true + train_last_n_language_layers: 8 + train_final_norm: true + train_lm_head: false + gradient_checkpointing: true + log_every: 5 + +stage2: + steps: 1000 + learning_rate: 0.0001 + weight_decay: 0.0 + adam_epsilon: 0.0001 + expert_hidden_size: 512 + expert_intermediate_size: 2432 + expert_num_layers: 24 + expert_num_attention_heads: 8 + expert_num_key_value_heads: 2 + expert_head_dim: 64 + expert_non_causal_attention: true + action_projection_hidden_size: 256 + action_fourier_features: 20 + inference_steps: 10 + log_every: 50 + checkpoint_every: 50 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9e25413 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,47 @@ +[build-system] +requires = ["setuptools>=75", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "alpamayo-qwen05b-validation" +version = "0.1.0" +description = "Two-stage local Alpamayo validation with a 0.5B Qwen VLM and a KV-conditioned flow-matching expert." +readme = "README.md" +license = "Apache-2.0" +requires-python = ">=3.12,<3.13" +dependencies = [ + "accelerate==1.12.0", + "av>=16.0.1", + "einops>=0.8.1", + "huggingface-hub==0.36.0", + "matplotlib>=3.9", + "numpy>=2.1,<3", + "pandas>=2.2", + "physical-ai-av>=0.2.0", + "pillow>=11", + "psutil>=6", + "pyarrow>=18", + "pyyaml>=6", + "safetensors>=0.5", + "scipy>=1.14", + "torch==2.8.0", + "torchvision==0.23.0", + "tqdm>=4.67", + "transformers==4.57.1", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.3", + "ruff>=0.9", +] + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests"] + +[tool.ruff] +line-length = 100 +extend-exclude = ["references"] diff --git a/results/complete_comparison/comparison.json b/results/complete_comparison/comparison.json new file mode 100644 index 0000000..95a0c7c --- /dev/null +++ b/results/complete_comparison/comparison.json @@ -0,0 +1,1381 @@ +{ + "schema_version": 1, + "sample": { + "manifest_index": 0, + "clip_id": "314f59c2-e420-4fcd-9df2-a51d572e0c20", + "t0_us": 1503899, + "camera": "camera_front_wide_120fov", + "history_steps": 8, + "expert_waypoints": 16, + "source_time_step_s": 0.1, + "image_path": "data/processed/images/sample_00_314f59c2-e420-4fcd-9df2-a51d572e0c20_1503899.jpg", + "trajectory_path": "data/processed/trajectories/sample_00_314f59c2-e420-4fcd-9df2-a51d572e0c20_1503899.npz" + }, + "prediction_protocol": { + "vlm": { + "decoding": "greedy untruncated structured generation", + "trajectory_representation": "32 discrete normalized-XY-delta tokens", + "trajectory_vocabulary_bins": 128 + }, + "diffusion": { + "decoding": "flow-matching sample", + "seed": 12, + "inference_steps": 10 + }, + "units": { + "action_mse": "normalized_action_squared", + "trajectory": "metres" + } + }, + "text": { + "ground_truth_reasoning": "Steer left following the temporary lane delineated by traffic cones through the construction zone.", + "vlm_reasoning": "Steer left following the temporary lane delineated by traffic cones through the construction zone.", + "vlm_reasoning_exact_match": true, + "vlm_meta_action": "STEER_LEFT", + "target_meta_action": "STEER_LEFT", + "vlm_meta_action_exact_match": true, + "vlm_structured_output": "<|cot_start|>Steer left following the temporary lane delineated by traffic cones through the construction zone.<|cot_end|>\n<|meta_action_start|>STEER_LEFT<|meta_action_end|>\n<|traj_future_start|><|traj_future_end|><|im_end|>", + "target_structured_output": "<|cot_start|>Steer left following the temporary lane delineated by traffic cones through the construction zone.<|cot_end|>\n<|meta_action_start|>STEER_LEFT<|meta_action_end|>\n<|traj_future_start|><|traj_future_end|><|im_end|>", + "vlm_structured_exact_match": true, + "diffusion_conditioning_reasoning": "Steer left following the temporary lane delineated by traffic cones through the construction zone.", + "diffusion_reasoning_exact_match": true + }, + "arrays": { + "ground_truth": { + "action": [ + [ + -3.4397106170654297, + -1.6662895679473877 + ], + [ + 0.46647655963897705, + -1.6230756044387817 + ], + [ + 0.3931064307689667, + -1.3917449712753296 + ], + [ + 0.26794740557670593, + -1.1304054260253906 + ], + [ + 0.1750280112028122, + -0.931678295135498 + ], + [ + 1.3138797283172607, + -0.017029158771038055 + ], + [ + -0.0019493416184559464, + -0.05992039293050766 + ], + [ + 0.07472366094589233, + 0.23706550896167755 + ], + [ + 0.13519389927387238, + 0.3722502589225769 + ], + [ + 0.06780000776052475, + 0.21779732406139374 + ], + [ + 1.1512386798858643, + 1.035483717918396 + ], + [ + -0.15684714913368225, + 0.8499115109443665 + ], + [ + -0.2395608276128769, + 0.9665660858154297 + ], + [ + -0.2766667306423187, + 0.9057282209396362 + ], + [ + -0.44114238023757935, + 0.7915734648704529 + ], + [ + 0.5104826092720032, + 1.4437676668167114 + ] + ], + "xy_m": [ + [ + 0.9489696025848389, + -0.00039592807297594845 + ], + [ + 4.71621561050415, + 0.002558943582698703 + ], + [ + 8.430525779724121, + 0.023451128974556923 + ], + [ + 12.054534912109375, + 0.06460751593112946 + ], + [ + 15.611503601074219, + 0.12117315828800201 + ], + [ + 19.990142822265625, + 0.24866046011447906 + ], + [ + 23.419424057006836, + 0.37282198667526245 + ], + [ + 26.904024124145508, + 0.5200117230415344 + ], + [ + 30.432252883911133, + 0.6776836514472961 + ], + [ + 33.91185760498047, + 0.8233793377876282 + ], + [ + 38.173152923583984, + 1.0324782133102417 + ], + [ + 41.49067687988281, + 1.2271878719329834 + ], + [ + 44.7485237121582, + 1.4309428930282593 + ], + [ + 47.97959899902344, + 1.6299805641174316 + ], + [ + 51.09200668334961, + 1.8201667070388794 + ], + [ + 54.8910026550293, + 2.0609238147735596 + ] + ], + "discrete_token_values": [ + 9, + 37, + 71, + 38, + 70, + 41, + 68, + 46, + 66, + 49, + 84, + 63, + 63, + 63, + 65, + 67, + 66, + 69, + 65, + 67, + 82, + 80, + 61, + 77, + 60, + 79, + 59, + 78, + 56, + 76, + 72, + 86 + ] + }, + "vlm": { + "action": [ + [ + -3.4330708980560303, + -1.6692912578582764 + ], + [ + 0.4724407196044922, + -1.6062991619110107 + ], + [ + 0.40944862365722656, + -1.4173228740692139 + ], + [ + 0.2834644317626953, + -1.1023621559143066 + ], + [ + 0.15748023986816406, + -0.9133858680725098 + ], + [ + 1.2913384437561035, + -0.03149604797363281 + ], + [ + -0.03149604797363281, + -0.03149604797363281 + ], + [ + 0.09448814392089844, + 0.2204723358154297 + ], + [ + 0.15748023986816406, + 0.34645652770996094 + ], + [ + 0.09448814392089844, + 0.2204723358154297 + ], + [ + 1.1653542518615723, + 1.039370059967041 + ], + [ + -0.15748023986816406, + 0.8503937721252441 + ], + [ + -0.2204723358154297, + 0.9763779640197754 + ], + [ + -0.2834646701812744, + 0.9133858680725098 + ], + [ + -0.4724409580230713, + 0.7874016761779785 + ], + [ + 0.535433292388916, + 1.4173226356506348 + ] + ], + "xy_m": [ + [ + 0.9537601470947266, + -0.0006286799907684326 + ], + [ + 4.725309371948242, + 0.0036270394921302795 + ], + [ + 8.451410293579102, + 0.02253592759370804 + ], + [ + 12.086614608764648, + 0.06586679071187973 + ], + [ + 15.630922317504883, + 0.12385082244873047 + ], + [ + 19.993297576904297, + 0.25021636486053467 + ], + [ + 23.401260375976562, + 0.37658190727233887 + ], + [ + 26.90011978149414, + 0.5224850177764893 + ], + [ + 30.444427490234375, + 0.6781569123268127 + ], + [ + 33.94328689575195, + 0.8240600228309631 + ], + [ + 38.21476745605469, + 1.033460259437561 + ], + [ + 41.53183364868164, + 1.2282073497772217 + ], + [ + 44.80345153808594, + 1.4327231645584106 + ], + [ + 48.029624938964844, + 1.6323546171188354 + ], + [ + 51.11945343017578, + 1.8222172260284424 + ], + [ + 54.93644714355469, + 2.0609238147735596 + ] + ], + "discrete_token_values": [ + 9, + 37, + 71, + 38, + 70, + 41, + 68, + 46, + 66, + 49, + 84, + 63, + 63, + 63, + 65, + 67, + 66, + 69, + 65, + 67, + 82, + 80, + 61, + 77, + 60, + 79, + 59, + 78, + 56, + 76, + 72, + 86 + ] + }, + "diffusion": { + "action": [ + [ + -3.84375, + -0.443359375 + ], + [ + -0.68017578125, + -1.453125 + ], + [ + -0.204345703125, + -2.953125 + ], + [ + 1.0224609375, + -0.82275390625 + ], + [ + -0.126953125, + -0.8857421875 + ], + [ + 2.5078125, + -0.9423828125 + ], + [ + 0.2435302734375, + -0.045867919921875 + ], + [ + 1.005859375, + 0.17138671875 + ], + [ + 0.12469482421875, + 0.52001953125 + ], + [ + 0.432373046875, + -0.6748046875 + ], + [ + 1.95703125, + 0.78125 + ], + [ + 2.060546875, + 1.314453125 + ], + [ + -0.9697265625, + 2.46875 + ], + [ + -0.52783203125, + 1.48046875 + ], + [ + -1.5810546875, + 0.258056640625 + ], + [ + -1.0625, + 1.236328125 + ] + ], + "xy_m": [ + [ + 0.657459020614624, + 0.09442976862192154 + ], + [ + 3.5974063873291016, + 0.11056257039308548 + ], + [ + 6.880660533905029, + 0.010385759174823761 + ], + [ + 11.049043655395508, + 0.07539737224578857 + ], + [ + 14.38813591003418, + 0.1355248987674713 + ], + [ + 19.628185272216797, + 0.19126051664352417 + ], + [ + 23.234577178955078, + 0.31651169061660767 + ], + [ + 27.39098358154297, + 0.4586087167263031 + ], + [ + 30.911636352539062, + 0.6277386546134949 + ], + [ + 34.65427780151367, + 0.7042222023010254 + ], + [ + 39.496944427490234, + 0.8936078548431396 + ], + [ + 44.41429901123047, + 1.124337911605835 + ], + [ + 47.14533615112305, + 1.4445719718933105 + ], + [ + 50.19519805908203, + 1.688174843788147 + ], + [ + 52.48516845703125, + 1.8369922637939453 + ], + [ + 55.14927673339844, + 2.061664581298828 + ] + ], + "seed": 12 + }, + "history_xy_m": [ + [ + -6.5544657707214355, + 0.035949498414993286 + ], + [ + -5.636332988739014, + 0.025562627241015434 + ], + [ + -4.717358112335205, + 0.019754931330680847 + ], + [ + -3.7914953231811523, + 0.012032918632030487 + ], + [ + -2.853987693786621, + 0.008905102498829365 + ], + [ + -1.9065805673599243, + 0.006025215145200491 + ], + [ + -0.9533587694168091, + 0.0025723553262650967 + ], + [ + 0.0, + 0.0 + ] + ], + "source_future_indices": [ + 0, + 4, + 8, + 12, + 16, + 21, + 25, + 29, + 33, + 37, + 42, + 46, + 50, + 54, + 58, + 63 + ], + "waypoint_times_s": [ + 0.1, + 0.5, + 0.9, + 1.3, + 1.7000000000000002, + 2.2, + 2.6, + 3.0, + 3.4000000000000004, + 3.8000000000000003, + 4.3, + 4.7, + 5.1000000000000005, + 5.5, + 5.9, + 6.4 + ] + }, + "waypoints": [ + { + "index": 1, + "source_future_index": 0, + "time_s": 0.1, + "action": { + "ground_truth": [ + -3.4397106170654297, + -1.6662895679473877 + ], + "vlm": [ + -3.4330708980560303, + -1.6692912578582764 + ], + "diffusion": [ + -3.84375, + -0.443359375 + ] + }, + "action_error_l2": { + "vlm": 0.00728670095756034, + "diffusion": 1.2879464584308167 + }, + "xy_m": { + "ground_truth": [ + 0.9489696025848389, + -0.00039592807297594845 + ], + "vlm": [ + 0.9537601470947266, + -0.0006286799907684326 + ], + "diffusion": [ + 0.657459020614624, + 0.09442976862192154 + ] + }, + "position_error_m": { + "vlm": 0.004796195383473365, + "diffusion": 0.30654580759535116 + } + }, + { + "index": 2, + "source_future_index": 4, + "time_s": 0.5, + "action": { + "ground_truth": [ + 0.46647655963897705, + -1.6230756044387817 + ], + "vlm": [ + 0.4724407196044922, + -1.6062991619110107 + ], + "diffusion": [ + -0.68017578125, + -1.453125 + ] + }, + "action_error_l2": { + "vlm": 0.01780506186402779, + "diffusion": 1.1591785017050988 + }, + "xy_m": { + "ground_truth": [ + 4.71621561050415, + 0.002558943582698703 + ], + "vlm": [ + 4.725309371948242, + 0.0036270394921302795 + ], + "diffusion": [ + 3.5974063873291016, + 0.11056257039308548 + ] + }, + "position_error_m": { + "vlm": 0.009156272498882664, + "diffusion": 1.1240101695561981 + } + }, + { + "index": 3, + "source_future_index": 8, + "time_s": 0.9, + "action": { + "ground_truth": [ + 0.3931064307689667, + -1.3917449712753296 + ], + "vlm": [ + 0.40944862365722656, + -1.4173228740692139 + ], + "diffusion": [ + -0.204345703125, + -2.953125 + ] + }, + "action_error_l2": { + "vlm": 0.03035286444028776, + "diffusion": 1.6717824758008162 + }, + "xy_m": { + "ground_truth": [ + 8.430525779724121, + 0.023451128974556923 + ], + "vlm": [ + 8.451410293579102, + 0.02253592759370804 + ], + "diffusion": [ + 6.880660533905029, + 0.010385759174823761 + ] + }, + "position_error_m": { + "vlm": 0.02090455721909409, + "diffusion": 1.5499203153987877 + } + }, + { + "index": 4, + "source_future_index": 12, + "time_s": 1.3, + "action": { + "ground_truth": [ + 0.26794740557670593, + -1.1304054260253906 + ], + "vlm": [ + 0.2834644317626953, + -1.1023621559143066 + ], + "diffusion": [ + 1.0224609375, + -0.82275390625 + ] + }, + "action_error_l2": { + "vlm": 0.032050009363179546, + "diffusion": 0.8148252128373736 + }, + "xy_m": { + "ground_truth": [ + 12.054534912109375, + 0.06460751593112946 + ], + "vlm": [ + 12.086614608764648, + 0.06586679071187973 + ], + "diffusion": [ + 11.049043655395508, + 0.07539737224578857 + ] + }, + "position_error_m": { + "vlm": 0.03210440328783258, + "diffusion": 1.0055491476438747 + } + }, + { + "index": 5, + "source_future_index": 16, + "time_s": 1.7000000000000002, + "action": { + "ground_truth": [ + 0.1750280112028122, + -0.931678295135498 + ], + "vlm": [ + 0.15748023986816406, + -0.9133858680725098 + ], + "diffusion": [ + -0.126953125, + -0.8857421875 + ] + }, + "action_error_l2": { + "vlm": 0.025348316840923476, + "diffusion": 0.3054549600301843 + }, + "xy_m": { + "ground_truth": [ + 15.611503601074219, + 0.12117315828800201 + ], + "vlm": [ + 15.630922317504883, + 0.12385082244873047 + ], + "diffusion": [ + 14.38813591003418, + 0.1355248987674713 + ] + }, + "position_error_m": { + "vlm": 0.019602459875540927, + "diffusion": 1.2234518707065785 + } + }, + { + "index": 6, + "source_future_index": 21, + "time_s": 2.2, + "action": { + "ground_truth": [ + 1.3138797283172607, + -0.017029158771038055 + ], + "vlm": [ + 1.2913384437561035, + -0.03149604797363281 + ], + "diffusion": [ + 2.5078125, + -0.9423828125 + ] + }, + "action_error_l2": { + "vlm": 0.02678433110733247, + "diffusion": 1.5105478634480827 + }, + "xy_m": { + "ground_truth": [ + 19.990142822265625, + 0.24866046011447906 + ], + "vlm": [ + 19.993297576904297, + 0.25021636486053467 + ], + "diffusion": [ + 19.628185272216797, + 0.19126051664352417 + ] + }, + "position_error_m": { + "vlm": 0.003517572516526143, + "diffusion": 0.36648058822783336 + } + }, + { + "index": 7, + "source_future_index": 25, + "time_s": 2.6, + "action": { + "ground_truth": [ + -0.0019493416184559464, + -0.05992039293050766 + ], + "vlm": [ + -0.03149604797363281, + -0.03149604797363281 + ], + "diffusion": [ + 0.2435302734375, + -0.045867919921875 + ] + }, + "action_error_l2": { + "vlm": 0.04099940539405987, + "diffusion": 0.24588150277253198 + }, + "xy_m": { + "ground_truth": [ + 23.419424057006836, + 0.37282198667526245 + ], + "vlm": [ + 23.401260375976562, + 0.37658190727233887 + ], + "diffusion": [ + 23.234577178955078, + 0.31651169061660767 + ] + }, + "position_error_m": { + "vlm": 0.018548754984252572, + "diffusion": 0.19323358343645852 + } + }, + { + "index": 8, + "source_future_index": 29, + "time_s": 3.0, + "action": { + "ground_truth": [ + 0.07472366094589233, + 0.23706550896167755 + ], + "vlm": [ + 0.09448814392089844, + 0.2204723358154297 + ], + "diffusion": [ + 1.005859375, + 0.17138671875 + ] + }, + "action_error_l2": { + "vlm": 0.025806359338943326, + "diffusion": 0.9334492066902851 + }, + "xy_m": { + "ground_truth": [ + 26.904024124145508, + 0.5200117230415344 + ], + "vlm": [ + 26.90011978149414, + 0.5224850177764893 + ], + "diffusion": [ + 27.39098358154297, + 0.4586087167263031 + ] + }, + "position_error_m": { + "vlm": 0.004621804667577402, + "diffusion": 0.49081548705534744 + } + }, + { + "index": 9, + "source_future_index": 33, + "time_s": 3.4000000000000004, + "action": { + "ground_truth": [ + 0.13519389927387238, + 0.3722502589225769 + ], + "vlm": [ + 0.15748023986816406, + 0.34645652770996094 + ], + "diffusion": [ + 0.12469482421875, + 0.52001953125 + ] + }, + "action_error_l2": { + "vlm": 0.034088085117141044, + "diffusion": 0.14814178485892907 + }, + "xy_m": { + "ground_truth": [ + 30.432252883911133, + 0.6776836514472961 + ], + "vlm": [ + 30.444427490234375, + 0.6781569123268127 + ], + "diffusion": [ + 30.911636352539062, + 0.6277386546134949 + ] + }, + "position_error_m": { + "vlm": 0.01218380133562631, + "diffusion": 0.4819782284527732 + } + }, + { + "index": 10, + "source_future_index": 37, + "time_s": 3.8000000000000003, + "action": { + "ground_truth": [ + 0.06780000776052475, + 0.21779732406139374 + ], + "vlm": [ + 0.09448814392089844, + 0.2204723358154297 + ], + "diffusion": [ + 0.432373046875, + -0.6748046875 + ] + }, + "action_error_l2": { + "vlm": 0.026821862343970004, + "diffusion": 0.9641845528178778 + }, + "xy_m": { + "ground_truth": [ + 33.91185760498047, + 0.8233793377876282 + ], + "vlm": [ + 33.94328689575195, + 0.8240600228309631 + ], + "diffusion": [ + 34.65427780151367, + 0.7042222023010254 + ] + }, + "position_error_m": { + "vlm": 0.03143666093157371, + "diffusion": 0.7519216522735415 + } + }, + { + "index": 11, + "source_future_index": 42, + "time_s": 4.3, + "action": { + "ground_truth": [ + 1.1512386798858643, + 1.035483717918396 + ], + "vlm": [ + 1.1653542518615723, + 1.039370059967041 + ], + "diffusion": [ + 1.95703125, + 0.78125 + ] + }, + "action_error_l2": { + "vlm": 0.014640800071050068, + "diffusion": 0.84494760155749 + }, + "xy_m": { + "ground_truth": [ + 38.173152923583984, + 1.0324782133102417 + ], + "vlm": [ + 38.21476745605469, + 1.033460259437561 + ], + "diffusion": [ + 39.496944427490234, + 0.8936078548431396 + ] + }, + "position_error_m": { + "vlm": 0.04162611833154021, + "diffusion": 1.331055566937441 + } + }, + { + "index": 12, + "source_future_index": 46, + "time_s": 4.7, + "action": { + "ground_truth": [ + -0.15684714913368225, + 0.8499115109443665 + ], + "vlm": [ + -0.15748023986816406, + 0.8503937721252441 + ], + "diffusion": [ + 2.060546875, + 1.314453125 + ] + }, + "action_error_l2": { + "vlm": 0.0007958515720084112, + "diffusion": 2.2655319837630143 + }, + "xy_m": { + "ground_truth": [ + 41.49067687988281, + 1.2271878719329834 + ], + "vlm": [ + 41.53183364868164, + 1.2282073497772217 + ], + "diffusion": [ + 44.41429901123047, + 1.124337911605835 + ] + }, + "position_error_m": { + "vlm": 0.04116939340134956, + "diffusion": 2.925430648852423 + } + }, + { + "index": 13, + "source_future_index": 50, + "time_s": 5.1000000000000005, + "action": { + "ground_truth": [ + -0.2395608276128769, + 0.9665660858154297 + ], + "vlm": [ + -0.2204723358154297, + 0.9763779640197754 + ], + "diffusion": [ + -0.9697265625, + 2.46875 + ] + }, + "action_error_l2": { + "vlm": 0.021462606388743272, + "diffusion": 1.6702390584698135 + }, + "xy_m": { + "ground_truth": [ + 44.7485237121582, + 1.4309428930282593 + ], + "vlm": [ + 44.80345153808594, + 1.4327231645584106 + ], + "diffusion": [ + 47.14533615112305, + 1.4445719718933105 + ] + }, + "position_error_m": { + "vlm": 0.054956668638742615, + "diffusion": 2.3968511884068464 + } + }, + { + "index": 14, + "source_future_index": 54, + "time_s": 5.5, + "action": { + "ground_truth": [ + -0.2766667306423187, + 0.9057282209396362 + ], + "vlm": [ + -0.2834646701812744, + 0.9133858680725098 + ], + "diffusion": [ + -0.52783203125, + 1.48046875 + ] + }, + "action_error_l2": { + "vlm": 0.010239704174774942, + "diffusion": 0.6272245881452143 + }, + "xy_m": { + "ground_truth": [ + 47.97959899902344, + 1.6299805641174316 + ], + "vlm": [ + 48.029624938964844, + 1.6323546171188354 + ], + "diffusion": [ + 50.19519805908203, + 1.688174843788147 + ] + }, + "position_error_m": { + "vlm": 0.050082240312057325, + "diffusion": 2.2163631852922747 + } + }, + { + "index": 15, + "source_future_index": 58, + "time_s": 5.9, + "action": { + "ground_truth": [ + -0.44114238023757935, + 0.7915734648704529 + ], + "vlm": [ + -0.4724409580230713, + 0.7874016761779785 + ], + "diffusion": [ + -1.5810546875, + 0.258056640625 + ] + }, + "action_error_l2": { + "vlm": 0.03157538269426274, + "diffusion": 1.2585866160107093 + }, + "xy_m": { + "ground_truth": [ + 51.09200668334961, + 1.8201667070388794 + ], + "vlm": [ + 51.11945343017578, + 1.8222172260284424 + ], + "diffusion": [ + 52.48516845703125, + 1.8369922637939453 + ] + }, + "position_error_m": { + "vlm": 0.027523236355242352, + "diffusion": 1.393263373166715 + } + }, + { + "index": 16, + "source_future_index": 63, + "time_s": 6.4, + "action": { + "ground_truth": [ + 0.5104826092720032, + 1.4437676668167114 + ], + "vlm": [ + 0.535433292388916, + 1.4173226356506348 + ], + "diffusion": [ + -1.0625, + 1.236328125 + ] + }, + "action_error_l2": { + "vlm": 0.03635761627740968, + "diffusion": 1.5866018569828055 + }, + "xy_m": { + "ground_truth": [ + 54.8910026550293, + 2.0609238147735596 + ], + "vlm": [ + 54.93644714355469, + 2.0609238147735596 + ], + "diffusion": [ + 55.14927673339844, + 2.061664581298828 + ] + }, + "position_error_m": { + "vlm": 0.045444488525390625, + "diffusion": 0.2582751406784516 + } + } + ], + "metrics": { + "vlm": { + "computed_from_npz": { + "action_mse": 0.0003439451570161356, + "ade_m": 0.026104664266543905, + "fde_m": 0.045444488525390625, + "max_error_m": 0.054956668638742615 + }, + "reported_in_metrics_json": { + "action_mse": 0.0003439451684243977, + "ade_m": 0.02610466256737709, + "fde_m": 0.045444488525390625, + "max_error_m": 0.05495666712522507 + }, + "trajectory_token_matches": 32, + "trajectory_token_count": 32, + "trajectory_token_accuracy": 1.0 + }, + "diffusion": { + "computed_from_npz": { + "action_mse": 0.745336398308135, + "ade_m": 1.125946622105056, + "fde_m": 0.2582751406784516, + "max_error_m": 2.925430648852423 + }, + "reported_in_metrics_json": { + "action_mse": 0.7453364133834839, + "ade_m": 1.1259467601776123, + "fde_m": 0.2582751512527466, + "max_error_m": 2.9254307746887207 + }, + "seed": 12 + }, + "diffusion_minus_vlm": { + "action_mse": 0.7449924531511188, + "ade_m": 1.0998419578385121, + "fde_m": 0.21283065215306096, + "max_error_m": 2.87047398021368 + } + }, + "validation": { + "ground_truth_action_exact_match": true, + "ground_truth_action_max_abs_difference": 0.0, + "ground_truth_xy_exact_match": true, + "ground_truth_xy_max_abs_difference": 0.0, + "dataset_target_xy_exact_match": true, + "dataset_target_xy_max_abs_difference": 0.0, + "dataset_history_xy_exact_match": true, + "dataset_history_xy_max_abs_difference": 0.0, + "ground_truth_text_matches_manifest": true, + "vlm_discrete_tokens_exact_match": true, + "action_decode_max_abs_differences": { + "ground_truth": 6.767095328541473e-08, + "vlm": 4.464556582206569e-06, + "diffusion": 2.836197381839156e-06 + }, + "vlm_reported_metrics_match_npz": { + "consistent": true, + "absolute_tolerance": 1e-05, + "absolute_differences": { + "action_mse": 1.1408262084577209e-11, + "ade_m": 1.699166814195241e-09, + "fde_m": 0.0, + "max_error_m": 1.5135175474023654e-09 + } + }, + "diffusion_reported_metrics_match_npz": { + "consistent": true, + "absolute_tolerance": 1e-05, + "absolute_differences": { + "action_mse": 1.5075348858140103e-08, + "ade_m": 1.3807255627718007e-07, + "fde_m": 1.0574294995180367e-08, + "max_error_m": 1.2583629782625394e-07 + } + } + }, + "data_sources": { + "manifest": { + "path": "data/processed/overfit_manifest.jsonl", + "sha256": "3af0fe4e1b1315b83ae7214dd923950c5aa2b5a6a14918bc3f940013c431da97" + }, + "vlm_npz": { + "path": "outputs/stage1_vlm_trajectory/trajectory_logits.npz", + "sha256": "1570bb17b39821b5e73900f6b1e9332dd0ea3f765770fbe6a282799945adf2ab", + "keys": [ + "boundary_masked_trajectory_logits", + "boundary_raw_trajectory_logits", + "generated_action", + "generated_values", + "generated_xy", + "quantized_target_values", + "target_action", + "target_xy", + "teacher_target_values", + "teacher_trajectory_logits" + ] + }, + "vlm_metrics": { + "path": "outputs/stage1_vlm_trajectory/trajectory_logits_metrics.json", + "sha256": "5bd5033ce99b2ba20fb1933d3dc997af7b44183d6f22735de549a4f5c71f88f6" + }, + "diffusion_npz": { + "path": "outputs/stage2_expert/reload_prediction.npz", + "sha256": "21005dbece8064e3a01b5c9e314f576d90b11934e789ed094ad46eee1d160030", + "keys": [ + "history_xy", + "predicted_action", + "predicted_xy", + "target_action", + "target_xy" + ] + }, + "diffusion_metrics": { + "path": "outputs/stage2_expert/stage2_metrics.json", + "sha256": "fc0ee1da1d0bc512f42e4530caabd3f4c738294e905e401a96807abb7bcc7698" + }, + "expert_config": { + "path": "outputs/stage2_expert/expert_config.json", + "sha256": "60b83a95c5883cc65aeda9ff1dd9faa652dcf50fa9968640306ed6305b46a38d" + }, + "trajectory_npz": { + "path": "data/processed/trajectories/sample_00_314f59c2-e420-4fcd-9df2-a51d572e0c20_1503899.npz", + "sha256": "145e480d94c06fd968acbf066cf512595ab2b6d02170ed2f570b4a93ada88336", + "keys": [ + "future_xyz", + "history_xyz", + "source_future_xyz", + "waypoint_indices" + ] + } + }, + "artifacts": { + "comparison_json": "results/complete_comparison/comparison.json", + "waypoints_csv": "results/complete_comparison/waypoints.csv", + "trajectory_plot": "results/complete_comparison/trajectory_comparison.png" + } +} diff --git a/results/complete_comparison/trajectory_comparison.png b/results/complete_comparison/trajectory_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..03f7f433e2fc4c73567d0c33c67303c4bc47a0c0 GIT binary patch literal 345595 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%0D~X%sUG}$s8A0N@A4K;usYIdv! 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

-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 Path: + return path.expanduser().resolve() if path.is_absolute() else (PROJECT_ROOT / path).resolve() + + +def _require_file(path: Path) -> Path: + resolved = _resolve(path) + if not resolved.is_file(): + raise FileNotFoundError(f"Required input does not exist: {resolved}") + return resolved + + +def _read_json(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError(f"Expected a JSON object in {path}") + return value + + +def _read_manifest_row(path: Path, index: int) -> dict[str, Any]: + rows = [line for line in path.read_text(encoding="utf-8").splitlines() if line.strip()] + if not 0 <= index < len(rows): + raise IndexError(f"Manifest index {index} is outside 0..{len(rows) - 1}: {path}") + value = json.loads(rows[index]) + if not isinstance(value, dict): + raise ValueError(f"Expected a JSON object at manifest row {index}: {path}") + return value + + +def _load_npz(path: Path) -> dict[str, np.ndarray]: + with np.load(path, allow_pickle=False) as archive: + return {key: np.asarray(archive[key]) for key in archive.files} + + +def _require_array( + arrays: dict[str, np.ndarray], key: str, source: Path, *, allow_batch: bool = True +) -> np.ndarray: + if key not in arrays: + raise KeyError(f"Missing NPZ key {key!r} in {source}") + value = arrays[key] + if allow_batch and value.ndim == 3 and value.shape[0] == 1: + value = value[0] + if value.ndim != 2 or value.shape[1] != 2: + raise ValueError(f"{source}:{key} must have shape (N, 2) or (1, N, 2), got {value.shape}") + if not np.issubdtype(value.dtype, np.number) or not np.isfinite(value).all(): + raise ValueError(f"{source}:{key} must contain only finite numeric values") + return value.astype(np.float64, copy=False) + + +def _require_xy_prefix(arrays: dict[str, np.ndarray], key: str, source: Path) -> np.ndarray: + if key not in arrays: + raise KeyError(f"Missing NPZ key {key!r} in {source}") + value = arrays[key] + if value.ndim != 2 or value.shape[1] < 2: + raise ValueError(f"{source}:{key} must have shape (N, >=2), got {value.shape}") + if not np.issubdtype(value.dtype, np.number) or not np.isfinite(value).all(): + raise ValueError(f"{source}:{key} must contain only finite numeric values") + return value[:, :2].astype(np.float64, copy=False) + + +def _require_token_values(arrays: dict[str, np.ndarray], key: str, source: Path) -> np.ndarray: + if key not in arrays: + raise KeyError(f"Missing NPZ key {key!r} in {source}") + value = np.asarray(arrays[key]).reshape(-1) + if not np.issubdtype(value.dtype, np.integer): + raise ValueError(f"{source}:{key} must contain integer token values") + return value.astype(np.int64, copy=False) + + +def _assert_same_truth( + name: str, + vlm_truth: np.ndarray, + diffusion_truth: np.ndarray, + vlm_path: Path, + diffusion_path: Path, +) -> float: + if vlm_truth.shape != diffusion_truth.shape: + raise ValueError( + f"Ground-truth {name} shape mismatch: {vlm_path} has {vlm_truth.shape}, " + f"{diffusion_path} has {diffusion_truth.shape}" + ) + max_abs_difference = float(np.max(np.abs(vlm_truth - diffusion_truth), initial=0.0)) + if not np.array_equal(vlm_truth, diffusion_truth): + raise ValueError( + f"Ground-truth {name} mismatch between {vlm_path} and {diffusion_path}; " + f"max_abs_difference={max_abs_difference:.9g}" + ) + return max_abs_difference + + +def _extract_component(text: str, component: str) -> str: + start = f"<|{component}_start|>" + end = f"<|{component}_end|>" + if text.count(start) != 1 or text.count(end) != 1: + raise ValueError(f"Structured VLM text must contain exactly one {start} and one {end}") + start_index = text.find(start) + content_start = start_index + len(start) + end_index = text.find(end, content_start) + if start_index < 0 or end_index < content_start: + raise ValueError(f"Structured VLM markers are out of order for {component}") + value = text[content_start:end_index].strip() + if not value: + raise ValueError(f"Structured VLM component {component!r} is empty") + return value + + +def _prediction_metrics( + predicted_action: np.ndarray, + predicted_xy: np.ndarray, + target_action: np.ndarray, + target_xy: np.ndarray, +) -> dict[str, float]: + position_errors = np.linalg.norm(predicted_xy - target_xy, axis=-1) + return { + "action_mse": float(np.mean(np.square(predicted_action - target_action))), + "ade_m": float(np.mean(position_errors)), + "fde_m": float(position_errors[-1]), + "max_error_m": float(np.max(position_errors)), + } + + +def _reported_metrics(metrics: dict[str, Any], section: str | None = None) -> dict[str, float]: + source: Any = metrics if section is None else metrics.get(section) + if not isinstance(source, dict): + raise KeyError(f"Missing metrics section {section!r}") + result: dict[str, float] = {} + for name in METRIC_NAMES: + if name not in source: + raise KeyError(f"Missing reported metric {name!r} in section {section!r}") + result[name] = float(source[name]) + return result + + +def _metric_validation( + computed: dict[str, float], reported: dict[str, float], tolerance: float = 1e-5 +) -> dict[str, Any]: + absolute_differences = {name: abs(computed[name] - reported[name]) for name in METRIC_NAMES} + consistent = all(difference <= tolerance for difference in absolute_differences.values()) + if not consistent: + raise ValueError( + "Computed metrics do not match the metrics JSON: " + f"absolute_differences={absolute_differences}, tolerance={tolerance}" + ) + return { + "consistent": True, + "absolute_tolerance": tolerance, + "absolute_differences": absolute_differences, + } + + +def _sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def _display_path(path: Path) -> str: + try: + return str(path.relative_to(PROJECT_ROOT)) + except ValueError: + return str(path) + + +def _source(path: Path, *, keys: list[str] | None = None) -> dict[str, Any]: + result: dict[str, Any] = {"path": _display_path(path), "sha256": _sha256(path)} + if keys is not None: + result["keys"] = keys + return result + + +def _decode_action( + action: np.ndarray, + history_xy: np.ndarray, + mean: np.ndarray, + scale: np.ndarray, +) -> np.ndarray: + deltas = action * scale + mean + return history_xy[-1] + np.cumsum(deltas, axis=0) + + +def _validate_decoded_xy( + name: str, + decoded_xy: np.ndarray, + stored_xy: np.ndarray, + tolerance: float = 1e-5, +) -> float: + max_abs_difference = float(np.max(np.abs(decoded_xy - stored_xy), initial=0.0)) + if not np.allclose(decoded_xy, stored_xy, rtol=0.0, atol=tolerance): + raise ValueError( + f"Decoded {name} action does not reproduce stored XY; " + f"max_abs_difference={max_abs_difference:.9g}, tolerance={tolerance}" + ) + return max_abs_difference + + +def _write_waypoints(path: Path, waypoints: list[dict[str, Any]]) -> None: + fieldnames = [ + "waypoint_index", + "source_future_index", + "time_s", + "gt_action_x", + "gt_action_y", + "vlm_action_x", + "vlm_action_y", + "diffusion_action_x", + "diffusion_action_y", + "vlm_action_error_l2", + "diffusion_action_error_l2", + "gt_x_m", + "gt_y_m", + "vlm_x_m", + "vlm_y_m", + "diffusion_x_m", + "diffusion_y_m", + "vlm_position_error_m", + "diffusion_position_error_m", + ] + with path.open("w", encoding="utf-8", newline="") as stream: + writer = csv.DictWriter(stream, fieldnames=fieldnames, lineterminator="\n") + writer.writeheader() + for waypoint in waypoints: + writer.writerow( + { + "waypoint_index": waypoint["index"], + "source_future_index": waypoint["source_future_index"], + "time_s": waypoint["time_s"], + "gt_action_x": waypoint["action"]["ground_truth"][0], + "gt_action_y": waypoint["action"]["ground_truth"][1], + "vlm_action_x": waypoint["action"]["vlm"][0], + "vlm_action_y": waypoint["action"]["vlm"][1], + "diffusion_action_x": waypoint["action"]["diffusion"][0], + "diffusion_action_y": waypoint["action"]["diffusion"][1], + "vlm_action_error_l2": waypoint["action_error_l2"]["vlm"], + "diffusion_action_error_l2": waypoint["action_error_l2"]["diffusion"], + "gt_x_m": waypoint["xy_m"]["ground_truth"][0], + "gt_y_m": waypoint["xy_m"]["ground_truth"][1], + "vlm_x_m": waypoint["xy_m"]["vlm"][0], + "vlm_y_m": waypoint["xy_m"]["vlm"][1], + "diffusion_x_m": waypoint["xy_m"]["diffusion"][0], + "diffusion_y_m": waypoint["xy_m"]["diffusion"][1], + "vlm_position_error_m": waypoint["position_error_m"]["vlm"], + "diffusion_position_error_m": waypoint["position_error_m"]["diffusion"], + } + ) + + +def _save_plot( + path: Path, + history_xy: np.ndarray, + target_xy: np.ndarray, + vlm_xy: np.ndarray, + diffusion_xy: np.ndarray, + target_action: np.ndarray, + vlm_action: np.ndarray, + diffusion_action: np.ndarray, + waypoint_times_s: np.ndarray, + vlm_errors: np.ndarray, + diffusion_errors: np.ndarray, + ground_truth_reasoning: str, + vlm_reasoning: str, +) -> None: + figure, axes = plt.subplots(2, 2, figsize=(14, 10)) + trajectory_axis, action_x_axis, action_y_axis, error_axis = axes.reshape(-1) + trajectory_axis.plot(history_xy[:, 0], history_xy[:, 1], "o-", color="#555555", label="history") + trajectory_axis.plot( + target_xy[:, 0], target_xy[:, 1], "o-", color="#d62728", label="ground truth" + ) + trajectory_axis.plot( + vlm_xy[:, 0], + vlm_xy[:, 1], + "o--", + color="#1f77b4", + label=f"VLM discrete (ADE {vlm_errors.mean():.3f} m)", + ) + trajectory_axis.plot( + diffusion_xy[:, 0], + diffusion_xy[:, 1], + "o-.", + color="#ff7f0e", + label=f"diffusion (ADE {diffusion_errors.mean():.3f} m)", + ) + trajectory_axis.scatter([0.0], [0.0], marker="x", color="black", label="current ego") + trajectory_axis.set_xlabel("ego-relative x (m)") + trajectory_axis.set_ylabel("ego-relative y (m)") + trajectory_axis.set_title("XY trajectory (axes independently scaled)") + trajectory_axis.grid(alpha=0.25) + trajectory_axis.legend() + + action_series = ( + ("ground truth", target_action, "#d62728", "o-"), + ("VLM discrete", vlm_action, "#1f77b4", "o--"), + ("diffusion", diffusion_action, "#ff7f0e", "o-."), + ) + for label, action, color, style in action_series: + action_x_axis.plot(waypoint_times_s, action[:, 0], style, color=color, label=label) + action_y_axis.plot(waypoint_times_s, action[:, 1], style, color=color, label=label) + for axis, component in ((action_x_axis, "x"), (action_y_axis, "y")): + axis.set_xlabel("future time (s)") + axis.set_ylabel(f"normalized action {component}") + axis.set_title(f"Normalized action {component}") + axis.grid(alpha=0.25) + action_x_axis.legend() + + error_axis.plot(waypoint_times_s, vlm_errors, "o--", color="#1f77b4", label="VLM discrete") + error_axis.plot(waypoint_times_s, diffusion_errors, "o-.", color="#ff7f0e", label="diffusion") + error_axis.set_xlabel("future time (s)") + error_axis.set_ylabel("position error (m)") + error_axis.set_title("Per-waypoint position error") + error_axis.grid(alpha=0.25) + error_axis.legend() + + match = "yes" if vlm_reasoning == ground_truth_reasoning else "no" + text_block = ( + f"Ground truth text: {ground_truth_reasoning}\n" + f"VLM text: {vlm_reasoning}\n" + f"Exact match: {match}" + ) + figure.suptitle("Ground truth vs VLM logits vs diffusion") + figure.text(0.02, 0.015, textwrap.fill(text_block, width=150), va="bottom") + figure.tight_layout(rect=(0.0, 0.10, 1.0, 0.97)) + figure.savefig(path, dpi=180) + plt.close(figure) + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--vlm-npz", + type=Path, + default=Path("outputs/stage1_vlm_trajectory/trajectory_logits.npz"), + ) + parser.add_argument( + "--vlm-metrics", + type=Path, + default=Path("outputs/stage1_vlm_trajectory/trajectory_logits_metrics.json"), + ) + parser.add_argument( + "--diffusion-npz", + type=Path, + default=Path("outputs/stage2_expert/reload_prediction.npz"), + ) + parser.add_argument( + "--diffusion-metrics", + type=Path, + default=Path("outputs/stage2_expert/stage2_metrics.json"), + ) + parser.add_argument( + "--expert-config", + type=Path, + default=Path("outputs/stage2_expert/expert_config.json"), + ) + parser.add_argument( + "--manifest", type=Path, default=Path("data/processed/overfit_manifest.jsonl") + ) + parser.add_argument("--manifest-index", type=int, default=0) + parser.add_argument("--diffusion-seed", type=int, default=12) + parser.add_argument("--time-step-s", type=float, default=0.1) + parser.add_argument("--output-dir", type=Path, default=Path("outputs/complete_comparison")) + args = parser.parse_args() + if args.time_step_s <= 0: + raise ValueError("--time-step-s must be positive") + + vlm_npz_path = _require_file(args.vlm_npz) + vlm_metrics_path = _require_file(args.vlm_metrics) + diffusion_npz_path = _require_file(args.diffusion_npz) + diffusion_metrics_path = _require_file(args.diffusion_metrics) + expert_config_path = _require_file(args.expert_config) + manifest_path = _require_file(args.manifest) + output_dir = _resolve(args.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + vlm_arrays = _load_npz(vlm_npz_path) + diffusion_arrays = _load_npz(diffusion_npz_path) + vlm_metrics = _read_json(vlm_metrics_path) + diffusion_metrics = _read_json(diffusion_metrics_path) + expert_config = _read_json(expert_config_path) + manifest = _read_manifest_row(manifest_path, args.manifest_index) + trajectory_value = manifest.get("trajectory_path") + if not isinstance(trajectory_value, str): + raise KeyError("Manifest row lacks string field 'trajectory_path'") + trajectory_path = _require_file(Path(trajectory_value)) + trajectory_arrays = _load_npz(trajectory_path) + + vlm_target_action = _require_array(vlm_arrays, "target_action", vlm_npz_path) + vlm_target_xy = _require_array(vlm_arrays, "target_xy", vlm_npz_path) + vlm_action = _require_array(vlm_arrays, "generated_action", vlm_npz_path) + vlm_xy = _require_array(vlm_arrays, "generated_xy", vlm_npz_path) + diffusion_target_action = _require_array(diffusion_arrays, "target_action", diffusion_npz_path) + diffusion_target_xy = _require_array(diffusion_arrays, "target_xy", diffusion_npz_path) + diffusion_action = _require_array(diffusion_arrays, "predicted_action", diffusion_npz_path) + diffusion_xy = _require_array(diffusion_arrays, "predicted_xy", diffusion_npz_path) + history_xy = _require_array( + diffusion_arrays, "history_xy", diffusion_npz_path, allow_batch=False + ) + target_token_values = _require_token_values(vlm_arrays, "quantized_target_values", vlm_npz_path) + generated_token_values = _require_token_values(vlm_arrays, "generated_values", vlm_npz_path) + dataset_history_xy = _require_xy_prefix(trajectory_arrays, "history_xyz", trajectory_path) + dataset_target_xy = _require_xy_prefix(trajectory_arrays, "future_xyz", trajectory_path) + if "waypoint_indices" not in trajectory_arrays: + raise KeyError(f"Missing NPZ key 'waypoint_indices' in {trajectory_path}") + source_future_indices = np.asarray(trajectory_arrays["waypoint_indices"]).reshape(-1) + if not np.issubdtype(source_future_indices.dtype, np.integer): + raise ValueError(f"{trajectory_path}:waypoint_indices must contain integers") + + action_truth_difference = _assert_same_truth( + "action", vlm_target_action, diffusion_target_action, vlm_npz_path, diffusion_npz_path + ) + xy_truth_difference = _assert_same_truth( + "XY", vlm_target_xy, diffusion_target_xy, vlm_npz_path, diffusion_npz_path + ) + dataset_xy_difference = _assert_same_truth( + "dataset XY", vlm_target_xy, dataset_target_xy, vlm_npz_path, trajectory_path + ) + history_xy_difference = _assert_same_truth( + "history XY", history_xy, dataset_history_xy, diffusion_npz_path, trajectory_path + ) + target_action = vlm_target_action + target_xy = vlm_target_xy + expected_shape = target_action.shape + for name, value in { + "target_xy": target_xy, + "vlm_action": vlm_action, + "vlm_xy": vlm_xy, + "diffusion_action": diffusion_action, + "diffusion_xy": diffusion_xy, + }.items(): + if value.shape != expected_shape: + raise ValueError(f"{name} shape {value.shape} does not match target {expected_shape}") + if source_future_indices.shape != (target_xy.shape[0],): + raise ValueError( + "waypoint_indices length does not match the compared trajectory: " + f"{source_future_indices.shape} vs {target_xy.shape[0]}" + ) + if target_token_values.shape != generated_token_values.shape: + raise ValueError( + "VLM generated and target trajectory token shapes differ: " + f"{generated_token_values.shape} vs {target_token_values.shape}" + ) + if target_token_values.size != target_action.size: + raise ValueError( + f"Expected {target_action.size} trajectory tokens, got {target_token_values.size}" + ) + token_matches = generated_token_values == target_token_values + waypoint_times_s = (source_future_indices.astype(np.float64) + 1.0) * args.time_step_s + + action_mean = np.asarray(expert_config.get("action_mean"), dtype=np.float64) + action_scale = np.asarray(expert_config.get("action_scale"), dtype=np.float64) + if action_mean.shape != (2,) or action_scale.shape != (2,): + raise ValueError("expert_config action_mean and action_scale must both have shape (2,)") + if not np.isfinite(action_mean).all() or not np.isfinite(action_scale).all(): + raise ValueError("expert_config action statistics must be finite") + if np.any(action_scale <= 0): + raise ValueError("expert_config action_scale must be positive") + action_decode_differences = { + "ground_truth": _validate_decoded_xy( + "ground-truth", + _decode_action(target_action, history_xy, action_mean, action_scale), + target_xy, + ), + "vlm": _validate_decoded_xy( + "VLM", + _decode_action(vlm_action, history_xy, action_mean, action_scale), + vlm_xy, + ), + "diffusion": _validate_decoded_xy( + "diffusion", + _decode_action(diffusion_action, history_xy, action_mean, action_scale), + diffusion_xy, + ), + } + + untruncated = vlm_metrics.get("untruncated_vlm_generation") + if not isinstance(untruncated, dict) or not isinstance(untruncated.get("raw_text"), str): + raise KeyError("VLM metrics lack untruncated_vlm_generation.raw_text") + vlm_structured_text = untruncated["raw_text"] + target_structured_text = vlm_metrics.get("structured_target") + if not isinstance(target_structured_text, str): + raise KeyError("VLM metrics lack structured_target") + vlm_reasoning = _extract_component(vlm_structured_text, "cot") + vlm_meta_action = _extract_component(vlm_structured_text, "meta_action") + vlm_target_reasoning = _extract_component(target_structured_text, "cot") + vlm_target_meta_action = _extract_component(target_structured_text, "meta_action") + ground_truth_reasoning = manifest.get("reasoning") + if not isinstance(ground_truth_reasoning, str): + raise KeyError("Manifest row lacks string field 'reasoning'") + diffusion_target_reasoning = diffusion_metrics.get("target_reasoning") + if vlm_target_reasoning != ground_truth_reasoning: + raise ValueError("VLM target reasoning does not match the manifest ground truth") + if diffusion_target_reasoning != ground_truth_reasoning: + raise ValueError("Diffusion target reasoning does not match the manifest ground truth") + if vlm_metrics.get("target_meta_action") != vlm_target_meta_action: + raise ValueError("Structured target meta-action does not match VLM metrics") + + vlm_position_errors = np.linalg.norm(vlm_xy - target_xy, axis=-1) + diffusion_position_errors = np.linalg.norm(diffusion_xy - target_xy, axis=-1) + vlm_action_errors = np.linalg.norm(vlm_action - target_action, axis=-1) + diffusion_action_errors = np.linalg.norm(diffusion_action - target_action, axis=-1) + + vlm_computed_metrics = _prediction_metrics(vlm_action, vlm_xy, target_action, target_xy) + diffusion_computed_metrics = _prediction_metrics( + diffusion_action, diffusion_xy, target_action, target_xy + ) + vlm_reported_metrics = _reported_metrics(vlm_metrics, section="untruncated_vlm_generation") + diffusion_reported_metrics = _reported_metrics(diffusion_metrics) + vlm_metric_validation = _metric_validation(vlm_computed_metrics, vlm_reported_metrics) + diffusion_metric_validation = _metric_validation( + diffusion_computed_metrics, diffusion_reported_metrics + ) + + waypoints: list[dict[str, Any]] = [] + for index in range(target_xy.shape[0]): + waypoints.append( + { + "index": index + 1, + "source_future_index": int(source_future_indices[index]), + "time_s": float(waypoint_times_s[index]), + "action": { + "ground_truth": target_action[index].tolist(), + "vlm": vlm_action[index].tolist(), + "diffusion": diffusion_action[index].tolist(), + }, + "action_error_l2": { + "vlm": float(vlm_action_errors[index]), + "diffusion": float(diffusion_action_errors[index]), + }, + "xy_m": { + "ground_truth": target_xy[index].tolist(), + "vlm": vlm_xy[index].tolist(), + "diffusion": diffusion_xy[index].tolist(), + }, + "position_error_m": { + "vlm": float(vlm_position_errors[index]), + "diffusion": float(diffusion_position_errors[index]), + }, + } + ) + + comparison = { + "schema_version": 1, + "sample": { + "manifest_index": args.manifest_index, + "clip_id": manifest.get("clip_id"), + "t0_us": manifest.get("t0_us"), + "camera": manifest.get("camera"), + "history_steps": manifest.get("history_steps"), + "expert_waypoints": manifest.get("expert_waypoints"), + "source_time_step_s": args.time_step_s, + "image_path": _display_path(_resolve(Path(str(manifest.get("image_path"))))), + "trajectory_path": _display_path(trajectory_path), + }, + "prediction_protocol": { + "vlm": { + "decoding": "greedy untruncated structured generation", + "trajectory_representation": "32 discrete normalized-XY-delta tokens", + "trajectory_vocabulary_bins": int(vlm_metrics["vocabulary"]["size"]), + }, + "diffusion": { + "decoding": "flow-matching sample", + "seed": args.diffusion_seed, + "inference_steps": expert_config.get("inference_steps"), + }, + "units": { + "action_mse": "normalized_action_squared", + "trajectory": "metres", + }, + }, + "text": { + "ground_truth_reasoning": ground_truth_reasoning, + "vlm_reasoning": vlm_reasoning, + "vlm_reasoning_exact_match": vlm_reasoning == ground_truth_reasoning, + "vlm_meta_action": vlm_meta_action, + "target_meta_action": vlm_target_meta_action, + "vlm_meta_action_exact_match": vlm_meta_action == vlm_target_meta_action, + "vlm_structured_output": vlm_structured_text, + "target_structured_output": target_structured_text, + "vlm_structured_exact_match": vlm_structured_text == target_structured_text, + "diffusion_conditioning_reasoning": diffusion_metrics.get("generated_reasoning"), + "diffusion_reasoning_exact_match": diffusion_metrics.get("generated_reasoning") + == ground_truth_reasoning, + }, + "arrays": { + "ground_truth": { + "action": target_action.tolist(), + "xy_m": target_xy.tolist(), + "discrete_token_values": target_token_values.tolist(), + }, + "vlm": { + "action": vlm_action.tolist(), + "xy_m": vlm_xy.tolist(), + "discrete_token_values": generated_token_values.tolist(), + }, + "diffusion": { + "action": diffusion_action.tolist(), + "xy_m": diffusion_xy.tolist(), + "seed": args.diffusion_seed, + }, + "history_xy_m": history_xy.tolist(), + "source_future_indices": source_future_indices.tolist(), + "waypoint_times_s": waypoint_times_s.tolist(), + }, + "waypoints": waypoints, + "metrics": { + "vlm": { + "computed_from_npz": vlm_computed_metrics, + "reported_in_metrics_json": vlm_reported_metrics, + "trajectory_token_matches": int(token_matches.sum()), + "trajectory_token_count": int(token_matches.size), + "trajectory_token_accuracy": float(token_matches.mean()), + }, + "diffusion": { + "computed_from_npz": diffusion_computed_metrics, + "reported_in_metrics_json": diffusion_reported_metrics, + "seed": args.diffusion_seed, + }, + "diffusion_minus_vlm": { + name: diffusion_computed_metrics[name] - vlm_computed_metrics[name] + for name in METRIC_NAMES + }, + }, + "validation": { + "ground_truth_action_exact_match": True, + "ground_truth_action_max_abs_difference": action_truth_difference, + "ground_truth_xy_exact_match": True, + "ground_truth_xy_max_abs_difference": xy_truth_difference, + "dataset_target_xy_exact_match": True, + "dataset_target_xy_max_abs_difference": dataset_xy_difference, + "dataset_history_xy_exact_match": True, + "dataset_history_xy_max_abs_difference": history_xy_difference, + "ground_truth_text_matches_manifest": True, + "vlm_discrete_tokens_exact_match": bool(token_matches.all()), + "action_decode_max_abs_differences": action_decode_differences, + "vlm_reported_metrics_match_npz": vlm_metric_validation, + "diffusion_reported_metrics_match_npz": diffusion_metric_validation, + }, + "data_sources": { + "manifest": _source(manifest_path), + "vlm_npz": _source(vlm_npz_path, keys=sorted(vlm_arrays)), + "vlm_metrics": _source(vlm_metrics_path), + "diffusion_npz": _source(diffusion_npz_path, keys=sorted(diffusion_arrays)), + "diffusion_metrics": _source(diffusion_metrics_path), + "expert_config": _source(expert_config_path), + "trajectory_npz": _source(trajectory_path, keys=sorted(trajectory_arrays)), + }, + "artifacts": { + "comparison_json": _display_path(output_dir / "comparison.json"), + "waypoints_csv": _display_path(output_dir / "waypoints.csv"), + "trajectory_plot": _display_path(output_dir / "trajectory_comparison.png"), + }, + } + + comparison_path = output_dir / "comparison.json" + waypoints_path = output_dir / "waypoints.csv" + plot_path = output_dir / "trajectory_comparison.png" + comparison_path.write_text( + json.dumps(comparison, indent=2, ensure_ascii=False, allow_nan=False) + "\n", + encoding="utf-8", + ) + _write_waypoints(waypoints_path, waypoints) + _save_plot( + plot_path, + history_xy, + target_xy, + vlm_xy, + diffusion_xy, + target_action, + vlm_action, + diffusion_action, + waypoint_times_s, + vlm_position_errors, + diffusion_position_errors, + ground_truth_reasoning, + vlm_reasoning, + ) + print( + json.dumps( + { + "comparison_json": str(comparison_path), + "waypoints_csv": str(waypoints_path), + "trajectory_plot": str(plot_path), + "vlm_metrics": vlm_computed_metrics, + "diffusion_metrics": diffusion_computed_metrics, + "diffusion_seed": args.diffusion_seed, + "vlm_trajectory_tokens": { + "matches": int(token_matches.sum()), + "count": int(token_matches.size), + }, + "vlm_reasoning_exact_match": vlm_reasoning == ground_truth_reasoning, + "ground_truth_consistent": True, + }, + indent=2, + ensure_ascii=False, + ) + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/download_model.py b/scripts/download_model.py new file mode 100644 index 0000000..415bde4 --- /dev/null +++ b/scripts/download_model.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json + +from huggingface_hub import model_info, snapshot_download + +from alpamayo_lite.config import load_config + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--config", default="configs/local_overfit.yaml") + args = parser.parse_args() + cfg = load_config(args.config) + output_dir = cfg.resolve(cfg.paths.model_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + info = model_info(cfg.vlm.repo_id, files_metadata=True) + total = info.safetensors.total if info.safetensors is not None else None + if total is not None and not (700_000_000 <= total <= 900_000_000): + raise RuntimeError(f"Unexpected model size for {cfg.vlm.repo_id}: {total:,} parameters") + + snapshot_download( + repo_id=cfg.vlm.repo_id, + local_dir=output_dir, + allow_patterns=[ + "*.json", + "*.txt", + "*.safetensors", + ], + ) + provenance = { + "repo_id": cfg.vlm.repo_id, + "revision": info.sha, + "safetensors_parameters": total, + "pipeline_tag": info.pipeline_tag, + "library_name": info.library_name, + } + (output_dir / "download_provenance.json").write_text( + json.dumps(provenance, indent=2, ensure_ascii=False) + "\n", encoding="utf-8" + ) + print(json.dumps(provenance, indent=2, ensure_ascii=False)) + + +if __name__ == "__main__": + main() diff --git a/scripts/evaluate_pipeline.py b/scripts/evaluate_pipeline.py new file mode 100644 index 0000000..75c5350 --- /dev/null +++ b/scripts/evaluate_pipeline.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import statistics +from functools import partial +from pathlib import Path +from typing import Any + +import torch +from safetensors.torch import load_file + +from alpamayo_lite.action_space import RelativeDeltaActionSpace +from alpamayo_lite.config import ExperimentConfig, load_config +from alpamayo_lite.data import OverfitSample, load_manifest +from alpamayo_lite.diffusion import FlowMatching +from alpamayo_lite.evaluation import ( + save_prediction_arrays, + save_trajectory_plot, + trajectory_metrics, +) +from alpamayo_lite.expert import ExpertArchitecture, KVCachedDiffusionExpert, PrefixKV +from alpamayo_lite.prompts import build_expert_prefix, build_vlm_prefix +from alpamayo_lite.runtime import ( + empty_device_cache, + parameter_counts, + resolve_dtype, + seed_everything, + select_device, + write_json, +) +from alpamayo_lite.vlm import ( + extract_prefix_kv, + generate_reasoning, + load_vlm, + vlm_text_config, +) + + +def _resolve(cfg: ExperimentConfig, value: str) -> Path: + path = Path(value).expanduser() + return path.resolve() if path.is_absolute() else (cfg.root / path).resolve() + + +def _read_json(path: Path) -> dict[str, Any]: + return json.loads(path.read_text(encoding="utf-8")) + + +def _summary(values: list[float]) -> dict[str, float]: + return { + "mean": statistics.fmean(values), + "std": statistics.pstdev(values), + "min": min(values), + "max": max(values), + } + + +def _evaluate_expert( + stage2_dir: Path, + vlm: Any, + prefix: PrefixKV, + sample: OverfitSample, + device: torch.device, + dtype: torch.dtype, + num_rollouts: int, + base_seed: int, +) -> dict[str, Any]: + expert_config = _read_json(stage2_dir / "expert_config.json") + metrics_path = stage2_dir / "stage2_metrics.json" + saved_metrics = _read_json(metrics_path) if metrics_path.exists() else {} + architecture = ExpertArchitecture(**expert_config["architecture"]) + expert = KVCachedDiffusionExpert(vlm_text_config(vlm), architecture).to( + device=device, dtype=dtype + ) + expert.load_state_dict(load_file(stage2_dir / "expert.safetensors"), strict=True) + expert.requires_grad_(False) + expert.eval() + + action_shape = tuple(int(value) for value in expert_config["action_shape"]) + if len(action_shape) != 2: + raise ValueError(f"Invalid action shape in {stage2_dir}: {action_shape}") + flow = FlowMatching( + action_shape=action_shape, + inference_steps=int(expert_config["inference_steps"]), + ) + action_space = RelativeDeltaActionSpace( + torch.tensor(expert_config["action_mean"], device=device), + torch.tensor(expert_config["action_scale"], device=device), + ).to(device) + history, future = sample.load_trajectory() + history_xy = history[None, :, :2].to(device) + target_xy = future[None, :, :2].to(device) + target_action = action_space.trajectory_to_action(history_xy, target_xy).float() + + rollouts: list[dict[str, float | int]] = [] + first_prediction: tuple[torch.Tensor, torch.Tensor] | None = None + for index in range(num_rollouts): + seed = base_seed + index + seed_everything(seed) + predicted_action = flow.sample( + batch_size=1, + step_fn=partial(expert, prefix=prefix), + device=device, + dtype=dtype, + ).float() + predicted_xy = action_space.action_to_trajectory(predicted_action, history_xy) + record: dict[str, float | int] = { + "seed": seed, + "action_mse": float( + torch.nn.functional.mse_loss(predicted_action, target_action).cpu() + ), + **trajectory_metrics(predicted_xy, target_xy), + } + if not torch.isfinite(predicted_action).all() or not torch.isfinite(predicted_xy).all(): + raise RuntimeError(f"Non-finite reload prediction for seed {seed}") + rollouts.append(record) + if first_prediction is None: + first_prediction = (predicted_action, predicted_xy) + + assert first_prediction is not None + first_action, first_xy = first_prediction + save_prediction_arrays( + stage2_dir / "reload_prediction.npz", + history[:, :2], + target_action[0], + first_action[0], + target_xy[0], + first_xy[0], + ) + save_trajectory_plot( + stage2_dir / "reload_trajectory.png", + history[:, :2], + target_xy[0], + first_xy[0], + ) + + metric_names = ("action_mse", "ade_m", "fde_m", "max_error_m") + result = { + "stage2_dir": str(stage2_dir), + "checkpoint_steps": int( + expert_config.get("completed_steps", saved_metrics.get("steps", 0)) + ), + "expert_parameters": parameter_counts(expert), + "architecture": expert_config["architecture"], + "action_mean": expert_config["action_mean"], + "action_scale": expert_config["action_scale"], + "action_shape": list(action_shape), + "inference_steps": flow.inference_steps, + "num_rollouts": num_rollouts, + "rollouts": rollouts, + "summary": { + name: _summary([float(record[name]) for record in rollouts]) + for name in metric_names + }, + } + del expert + empty_device_cache(device) + return result + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--config", default="configs/local_overfit.yaml") + parser.add_argument( + "--stage2-dir", + action="append", + dest="stage2_dirs", + help="Stage-2 directory to reload; repeat to compare checkpoints.", + ) + parser.add_argument("--num-rollouts", type=int, default=16) + parser.add_argument("--base-seed", type=int, default=None) + parser.add_argument("--output", default="outputs/reload_evaluation.json") + args = parser.parse_args() + if args.num_rollouts <= 0: + raise ValueError("--num-rollouts must be positive") + + cfg = load_config(args.config) + seed_everything(cfg.seed) + device = select_device() + dtype = resolve_dtype(cfg.vlm.dtype, device) + stage2_values = args.stage2_dirs or [cfg.paths.stage2_dir] + stage2_dirs = [_resolve(cfg, value) for value in stage2_values] + sample = load_manifest(cfg.resolve(cfg.paths.manifest))[0] + history, _ = sample.load_trajectory() + + stage1_dir = cfg.resolve(cfg.paths.stage1_dir) + processor, vlm = load_vlm(stage1_dir, cfg.vlm, device, dtype) + vlm.requires_grad_(False) + vlm.eval() + generated_reasoning = generate_reasoning( + vlm, + processor, + sample.load_image(), + build_vlm_prefix(history), + device, + dtype, + cfg.vlm.max_reasoning_tokens, + ) + prefix = extract_prefix_kv( + vlm, + processor, + sample.load_image(), + build_expert_prefix(history, generated_reasoning), + device, + dtype, + ) + base_seed = args.base_seed if args.base_seed is not None else cfg.seed + 1 + models = [ + _evaluate_expert( + stage2_dir, + vlm, + prefix, + sample, + device, + dtype, + args.num_rollouts, + base_seed, + ) + for stage2_dir in stage2_dirs + ] + result = { + "stage": "reloaded_pipeline_evaluation", + "device": str(device), + "dtype": str(dtype), + "stage1_dir": str(stage1_dir), + "vlm_parameters": parameter_counts(vlm), + "generated_reasoning": generated_reasoning, + "target_reasoning": sample.reasoning, + "reasoning_exact_match": generated_reasoning == sample.reasoning, + "prefix_layers": prefix.num_layers, + "prefix_sequence_length": prefix.sequence_length, + "prefix_key_shape": list(prefix.keys[0].shape), + "models": models, + } + output = _resolve(cfg, args.output) + write_json(output, result) + print(json.dumps(result, indent=2, ensure_ascii=False)) + del vlm + empty_device_cache(device) + + +if __name__ == "__main__": + main() diff --git a/scripts/materialize_pai_subset.py b/scripts/materialize_pai_subset.py new file mode 100644 index 0000000..23be009 --- /dev/null +++ b/scripts/materialize_pai_subset.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import os +from pathlib import Path +from typing import Any + +import numpy as np +import pandas as pd +from huggingface_hub import get_token, hf_hub_download +from physical_ai_av import PhysicalAIAVDatasetInterface +from PIL import Image +from scipy.spatial.transform import Rotation + +from alpamayo_lite.config import load_config +from alpamayo_lite.prompts import clean_reasoning_text + + +CLIP_DURATION_US = 20_000_000 + + +def parse_events(cell: object) -> list[dict[str, Any]]: + if cell is None or (np.isscalar(cell) and pd.isna(cell)): + return [] + parsed = json.loads(cell) if isinstance(cell, str) else cell + if not isinstance(parsed, (list, tuple)): + return [] + return [event for event in parsed if isinstance(event, dict)] + + +def collect_candidates( + reasoning_path: Path, + history_seconds: float, + future_seconds: float, +) -> list[dict[str, Any]]: + frame = pd.read_parquet(reasoning_path) + candidates: list[dict[str, Any]] = [] + for clip_id, events_cell in frame["events"].items(): + for event in parse_events(events_cell): + if "event_start_timestamp" not in event: + continue + t0_us = int(event["event_start_timestamp"]) + if t0_us < int(history_seconds * 1_000_000): + continue + if t0_us + int(future_seconds * 1_000_000) > CLIP_DURATION_US: + continue + # Current PAI releases call the driving rationale/command `coc`. + # Keep `cot` as a compatibility fallback for older metadata shards. + reasoning = clean_reasoning_text(str(event.get("coc") or event.get("cot") or "")) + candidates.append({"clip_id": str(clip_id), "t0_us": t0_us, "reasoning": reasoning}) + if not candidates: + raise RuntimeError("No valid reasoning events remain after trajectory safety filtering") + return candidates + + +def load_local_trajectory( + interface: PhysicalAIAVDatasetInterface, + clip_id: str, + t0_us: int, + history_steps: int, + future_steps: int, + time_step: float, +) -> tuple[np.ndarray, np.ndarray]: + egomotion = interface.get_clip_feature( + clip_id, interface.features.LABELS.EGOMOTION, maybe_stream=True + ) + history_offsets = np.arange(-(history_steps - 1), 1, dtype=np.int64) + history_timestamps = t0_us + history_offsets * int(time_step * 1_000_000) + future_offsets = np.arange(1, future_steps + 1, dtype=np.int64) + future_timestamps = t0_us + future_offsets * int(time_step * 1_000_000) + + history_state = egomotion(history_timestamps) + future_state = egomotion(future_timestamps) + history_xyz = history_state.pose.translation + future_xyz = future_state.pose.translation + t0_rotation = Rotation.from_quat(history_state.pose.rotation.as_quat()[-1]) + inverse = t0_rotation.inv() + origin = history_xyz[-1].copy() + history_local = inverse.apply(history_xyz - origin).astype(np.float32) + future_local = inverse.apply(future_xyz - origin).astype(np.float32) + return history_local, future_local + + +def load_camera_frame( + interface: PhysicalAIAVDatasetInterface, + clip_id: str, + t0_us: int, + camera_name: str, +) -> np.ndarray: + camera_feature = getattr(interface.features.CAMERA, camera_name.upper()) + reader = interface.get_clip_feature(clip_id, camera_feature, maybe_stream=True) + frames, _ = reader.decode_images_from_timestamps(np.asarray([t0_us], dtype=np.int64)) + return frames[0] + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--config", default="configs/local_overfit.yaml") + args = parser.parse_args() + cfg = load_config(args.config) + token = os.environ.get("HF_TOKEN") or get_token() + if not token: + raise SystemExit( + "PAI is gated. Set HF_TOKEN for an account that accepted the dataset license." + ) + + raw_dir = cfg.resolve(cfg.paths.pai_raw_dir) + raw_dir.mkdir(parents=True, exist_ok=True) + cache_dir = cfg.root / ".cache" / "huggingface" + reasoning_file = Path( + hf_hub_download( + repo_id=cfg.data.repo_id, + repo_type="dataset", + filename="reasoning/ood_reasoning.parquet", + token=token, + cache_dir=cache_dir, + local_dir=raw_dir, + ) + ) + candidates = collect_candidates( + reasoning_file, + history_seconds=(cfg.data.history_steps - 1) * cfg.data.time_step, + future_seconds=cfg.data.source_future_steps * cfg.data.time_step, + ) + rng = np.random.default_rng(cfg.seed) + selected_indices = rng.choice( + len(candidates), size=min(cfg.data.num_clips, len(candidates)), replace=False + ) + selected = [candidates[int(index)] for index in selected_indices] + + interface = PhysicalAIAVDatasetInterface( + token=token, + cache_dir=cache_dir, + local_dir=raw_dir, + confirm_download_threshold_gb=1000.0, + ) + processed_dir = cfg.resolve(cfg.paths.manifest).parent + image_dir = processed_dir / "images" + trajectory_dir = processed_dir / "trajectories" + image_dir.mkdir(parents=True, exist_ok=True) + trajectory_dir.mkdir(parents=True, exist_ok=True) + + rows: list[dict[str, Any]] = [] + for sample_index, event in enumerate(selected): + clip_id = event["clip_id"] + t0_us = event["t0_us"] + history, future_full = load_local_trajectory( + interface=interface, + clip_id=clip_id, + t0_us=t0_us, + history_steps=cfg.data.history_steps, + future_steps=cfg.data.source_future_steps, + time_step=cfg.data.time_step, + ) + waypoint_indices = np.linspace( + 0, + cfg.data.source_future_steps - 1, + cfg.data.expert_waypoints, + dtype=np.int64, + ) + future = future_full[waypoint_indices] + frame = load_camera_frame( + interface=interface, + clip_id=clip_id, + t0_us=t0_us, + camera_name=cfg.data.camera, + ) + + stem = f"sample_{sample_index:02d}_{clip_id}_{t0_us}" + image_path = image_dir / f"{stem}.jpg" + trajectory_path = trajectory_dir / f"{stem}.npz" + Image.fromarray(frame).save(image_path, quality=95) + np.savez_compressed( + trajectory_path, + history_xyz=history, + future_xyz=future, + source_future_xyz=future_full, + waypoint_indices=waypoint_indices, + ) + rows.append( + { + "clip_id": clip_id, + "t0_us": t0_us, + "reasoning": event["reasoning"], + "image_path": str(image_path.relative_to(cfg.root)), + "trajectory_path": str(trajectory_path.relative_to(cfg.root)), + "camera": cfg.data.camera, + "history_steps": cfg.data.history_steps, + "expert_waypoints": cfg.data.expert_waypoints, + } + ) + + manifest_path = cfg.resolve(cfg.paths.manifest) + manifest_path.write_text( + "".join(json.dumps(row, ensure_ascii=False) + "\n" for row in rows), + encoding="utf-8", + ) + print(json.dumps({"manifest": str(manifest_path), "samples": rows}, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/train_stage1_vlm.py b/scripts/train_stage1_vlm.py new file mode 100644 index 0000000..4483f20 --- /dev/null +++ b/scripts/train_stage1_vlm.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import time + +import torch +from tqdm import trange + +from alpamayo_lite.config import load_config +from alpamayo_lite.data import load_manifest +from alpamayo_lite.prompts import build_stage1_text, build_vlm_prefix +from alpamayo_lite.runtime import ( + empty_device_cache, + resolve_dtype, + seed_everything, + select_device, + write_json, +) +from alpamayo_lite.vlm import ( + configure_stage1_trainability, + enforce_frozen_module_eval, + generate_reasoning, + load_vlm, + prepare_stage1_batch, +) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--config", default="configs/local_overfit.yaml") + parser.add_argument("--steps", type=int, default=None) + args = parser.parse_args() + cfg = load_config(args.config) + seed_everything(cfg.seed) + device = select_device() + dtype = resolve_dtype(cfg.vlm.dtype, device) + samples = load_manifest(cfg.resolve(cfg.paths.manifest)) + + processor, model = load_vlm(cfg.resolve(cfg.paths.model_dir), cfg.vlm, device, dtype) + first = samples[0] + first_history, _ = first.load_trajectory() + baseline = generate_reasoning( + model, + processor, + first.load_image(), + build_vlm_prefix(first_history), + device, + dtype, + cfg.vlm.max_reasoning_tokens, + ) + trainability = configure_stage1_trainability(model, cfg.stage1) + print({"device": str(device), "dtype": str(dtype), "trainability": trainability}) + + batches = [] + for sample in samples: + history, _ = sample.load_trajectory() + prefix, full_text = build_stage1_text(history, sample.reasoning) + inputs, labels, prefix_length = prepare_stage1_batch( + processor, + sample.load_image(), + prefix, + full_text, + device, + dtype, + ) + batches.append((inputs, labels, prefix_length)) + + optimizer = torch.optim.AdamW( + [parameter for parameter in model.parameters() if parameter.requires_grad], + lr=cfg.stage1.learning_rate, + weight_decay=cfg.stage1.weight_decay, + eps=cfg.stage1.adam_epsilon, + ) + steps = args.steps if args.steps is not None else cfg.stage1.steps + losses: list[float] = [] + started = time.perf_counter() + model.train() + enforce_frozen_module_eval(model) + for step in trange(steps, desc="stage1-vlm"): + inputs, labels, _ = batches[step % len(batches)] + optimizer.zero_grad(set_to_none=True) + outputs = model(**inputs, labels=labels, use_cache=False, return_dict=True) + loss = outputs.loss + if loss is None or not torch.isfinite(loss): + raise RuntimeError(f"Non-finite Stage-1 loss at step {step}: {loss}") + loss.backward() + torch.nn.utils.clip_grad_norm_( + [parameter for parameter in model.parameters() if parameter.requires_grad], 1.0 + ) + optimizer.step() + loss_value = float(loss.detach().cpu()) + losses.append(loss_value) + if step == 0 or (step + 1) % cfg.stage1.log_every == 0: + print(f"stage1 step={step + 1} loss={loss_value:.6f}") + + elapsed = time.perf_counter() - started + model.gradient_checkpointing_disable() + model.config.use_cache = True + model.model.language_model.config.use_cache = True + output_dir = cfg.resolve(cfg.paths.stage1_dir) + output_dir.mkdir(parents=True, exist_ok=True) + model.save_pretrained(output_dir, safe_serialization=True, max_shard_size="2GB") + processor.save_pretrained(output_dir) + trained = generate_reasoning( + model, + processor, + first.load_image(), + build_vlm_prefix(first_history), + device, + dtype, + cfg.vlm.max_reasoning_tokens, + ) + metrics = { + "stage": "stage1_vlm", + "device": str(device), + "dtype": str(dtype), + "trainability": trainability, + "steps": steps, + "elapsed_seconds": elapsed, + "initial_loss": losses[0], + "final_loss": losses[-1], + "minimum_loss": min(losses), + "losses": losses, + "baseline_text": baseline, + "trained_text": trained, + "target_text": first.reasoning, + } + write_json(output_dir / "stage1_metrics.json", metrics) + print(metrics) + del optimizer, model + empty_device_cache(device) + + +if __name__ == "__main__": + main() diff --git a/scripts/train_stage2_expert.py b/scripts/train_stage2_expert.py new file mode 100644 index 0000000..7bee022 --- /dev/null +++ b/scripts/train_stage2_expert.py @@ -0,0 +1,451 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import os +import time +from dataclasses import asdict +from functools import partial +from pathlib import Path +from typing import Any + +import torch +from safetensors.torch import load_file, save_file +from tqdm import trange + +from alpamayo_lite.action_space import RelativeDeltaActionSpace +from alpamayo_lite.config import load_config +from alpamayo_lite.data import load_manifest +from alpamayo_lite.diffusion import FlowMatching +from alpamayo_lite.evaluation import ( + save_prediction_arrays, + save_trajectory_plot, + trajectory_metrics, +) +from alpamayo_lite.expert import ExpertArchitecture, KVCachedDiffusionExpert +from alpamayo_lite.prompts import build_expert_prefix, build_vlm_prefix +from alpamayo_lite.runtime import ( + empty_device_cache, + parameter_counts, + resolve_dtype, + seed_everything, + select_device, + write_json, +) +from alpamayo_lite.vlm import ( + extract_prefix_kv, + generate_reasoning, + load_vlm, + vlm_text_config, +) + + +def _cpu_copy(value: Any) -> Any: + if isinstance(value, torch.Tensor): + return value.detach().cpu() + if isinstance(value, dict): + return {key: _cpu_copy(item) for key, item in value.items()} + if isinstance(value, list): + return [_cpu_copy(item) for item in value] + if isinstance(value, tuple): + return tuple(_cpu_copy(item) for item in value) + return value + + +def _optimizer_to(optimizer: torch.optim.Optimizer, device: torch.device) -> None: + for state in optimizer.state.values(): + for key, value in state.items(): + if isinstance(value, torch.Tensor): + state[key] = value.to(device) + + +def _atomic_save_safetensors(module: torch.nn.Module, path: Path) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + temporary = path.with_name(f".{path.name}.tmp") + save_file( + {key: value.detach().cpu().contiguous() for key, value in module.state_dict().items()}, + temporary, + ) + os.replace(temporary, path) + + +def _atomic_torch_save(value: Any, path: Path) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + temporary = path.with_name(f".{path.name}.tmp") + torch.save(value, temporary) + os.replace(temporary, path) + + +def _read_json(path: Path) -> dict[str, Any]: + return json.loads(path.read_text(encoding="utf-8")) + + +def _save_training_checkpoint( + checkpoint_dir: Path, + expert: KVCachedDiffusionExpert, + optimizer: torch.optim.Optimizer, + architecture: ExpertArchitecture, + completed_steps: int, + losses: list[float], + elapsed_seconds: float, + device: torch.device, +) -> None: + checkpoint_dir.mkdir(parents=True, exist_ok=True) + _atomic_save_safetensors(expert, checkpoint_dir / "expert.safetensors") + state: dict[str, Any] = { + "completed_steps": completed_steps, + "losses": losses, + "elapsed_seconds": elapsed_seconds, + "architecture": asdict(architecture), + "optimizer": _cpu_copy(optimizer.state_dict()), + "torch_rng_state": torch.get_rng_state(), + } + if device.type == "mps" and hasattr(torch.mps, "get_rng_state"): + state["device_rng_state"] = torch.mps.get_rng_state().cpu() + elif device.type == "cuda": + state["device_rng_state"] = torch.cuda.get_rng_state(device).cpu() + _atomic_torch_save(state, checkpoint_dir / "training_state.pt") + write_json( + checkpoint_dir / "progress.json", + { + "completed_steps": completed_steps, + "latest_loss": losses[-1], + "minimum_loss": min(losses), + "elapsed_seconds": elapsed_seconds, + "architecture": asdict(architecture), + }, + ) + + +def _load_resume_state( + output_dir: Path, + expert: KVCachedDiffusionExpert, + optimizer: torch.optim.Optimizer, + architecture: ExpertArchitecture, + device: torch.device, +) -> tuple[int, list[float], float, bool, str]: + checkpoint_dir = output_dir / "checkpoints" / "latest" + checkpoint_weights = checkpoint_dir / "expert.safetensors" + checkpoint_state = checkpoint_dir / "training_state.pt" + if checkpoint_weights.exists() and checkpoint_state.exists(): + state = torch.load(checkpoint_state, map_location="cpu", weights_only=True) + if state.get("architecture") != asdict(architecture): + raise RuntimeError("Checkpoint architecture does not match the current config") + expert.load_state_dict(load_file(checkpoint_weights), strict=True) + optimizer.load_state_dict(state["optimizer"]) + _optimizer_to(optimizer, device) + torch.set_rng_state(state["torch_rng_state"]) + device_rng_state = state.get("device_rng_state") + if device_rng_state is not None and device.type == "mps": + torch.mps.set_rng_state(device_rng_state) + elif device_rng_state is not None and device.type == "cuda": + torch.cuda.set_rng_state(device_rng_state, device) + return ( + int(state["completed_steps"]), + [float(value) for value in state["losses"]], + float(state.get("elapsed_seconds", 0.0)), + True, + str(checkpoint_dir), + ) + + final_weights = output_dir / "expert.safetensors" + final_metrics = output_dir / "stage2_metrics.json" + final_config = output_dir / "expert_config.json" + if not (final_weights.exists() and final_metrics.exists() and final_config.exists()): + raise FileNotFoundError( + "No resumable Stage-2 checkpoint found. Expected either checkpoints/latest or " + "the legacy expert.safetensors + stage2_metrics.json + expert_config.json files." + ) + saved_config = _read_json(final_config) + if saved_config.get("architecture") != asdict(architecture): + raise RuntimeError("Legacy expert architecture does not match the current config") + metrics = _read_json(final_metrics) + expert.load_state_dict(load_file(final_weights), strict=True) + completed_steps = int(metrics["steps"]) + losses = [float(value) for value in metrics["losses"]] + if completed_steps != len(losses): + raise RuntimeError( + f"Legacy metrics are inconsistent: steps={completed_steps}, losses={len(losses)}" + ) + return ( + completed_steps, + losses, + float(metrics.get("elapsed_seconds", 0.0)), + False, + str(final_weights), + ) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--config", default="configs/local_overfit.yaml") + parser.add_argument( + "--steps", + type=int, + default=None, + help="Total target steps. With --resume, only the missing steps are run.", + ) + parser.add_argument( + "--resume", + action="store_true", + help="Resume from checkpoints/latest, or warm-start from the legacy final expert.", + ) + args = parser.parse_args() + cfg = load_config(args.config) + seed_everything(cfg.seed) + device = select_device() + dtype = resolve_dtype(cfg.vlm.dtype, device) + samples = load_manifest(cfg.resolve(cfg.paths.manifest)) + + stage1_dir = cfg.resolve(cfg.paths.stage1_dir) + if not (stage1_dir / "stage1_metrics.json").exists(): + raise SystemExit("Stage-1 checkpoint is missing; run train_stage1_vlm.py first") + processor, vlm = load_vlm(stage1_dir, cfg.vlm, device, dtype) + vlm.requires_grad_(False) + vlm.eval() + vlm_counts = parameter_counts(vlm) + if vlm_counts["trainable"] != 0: + raise RuntimeError(f"Stage-2 VLM must be fully frozen: {vlm_counts}") + + histories = [] + futures = [] + images = [] + prefixes = [] + for sample in samples: + history, future = sample.load_trajectory() + histories.append(history[:, :2]) + futures.append(future[:, :2]) + images.append(sample.load_image()) + prefixes.append( + extract_prefix_kv( + vlm, + processor, + images[-1], + build_expert_prefix(history, sample.reasoning), + device, + dtype, + ) + ) + + history_tensor = torch.stack(histories).to(device=device, dtype=torch.float32) + future_tensor = torch.stack(futures).to(device=device, dtype=torch.float32) + statistics = RelativeDeltaActionSpace.fit(history_tensor, future_tensor) + action_space = RelativeDeltaActionSpace(statistics.mean, statistics.scale).to(device) + target_actions = action_space.trajectory_to_action(history_tensor, future_tensor).to(dtype) + + stage2 = cfg.stage2 + architecture = ExpertArchitecture( + hidden_size=stage2.expert_hidden_size, + intermediate_size=stage2.expert_intermediate_size, + num_layers=stage2.expert_num_layers, + num_attention_heads=stage2.expert_num_attention_heads, + num_key_value_heads=stage2.expert_num_key_value_heads, + head_dim=stage2.expert_head_dim, + action_projection_hidden_size=stage2.action_projection_hidden_size, + action_fourier_features=stage2.action_fourier_features, + non_causal_attention=stage2.expert_non_causal_attention, + ) + expert = KVCachedDiffusionExpert(vlm_text_config(vlm), architecture).to( + device=device, dtype=dtype + ) + expert_counts = parameter_counts(expert) + if not (70_000_000 <= expert_counts["total"] <= 120_000_000): + raise RuntimeError( + f"Scaled expert parameter count is outside the expected range: {expert_counts}" + ) + flow = FlowMatching( + action_shape=(cfg.data.expert_waypoints, 2), + inference_steps=stage2.inference_steps, + ) + optimizer = torch.optim.AdamW( + expert.parameters(), + lr=stage2.learning_rate, + weight_decay=stage2.weight_decay, + eps=stage2.adam_epsilon, + ) + target_steps = args.steps if args.steps is not None else stage2.steps + if target_steps <= 0: + raise ValueError("Stage-2 target steps must be positive") + if stage2.checkpoint_every <= 0: + raise ValueError("stage2.checkpoint_every must be positive") + + output_dir = cfg.resolve(cfg.paths.stage2_dir) + output_dir.mkdir(parents=True, exist_ok=True) + checkpoint_dir = output_dir / "checkpoints" / "latest" + completed_steps = 0 + losses: list[float] = [] + previous_elapsed = 0.0 + optimizer_state_restored = False + resume_source: str | None = None + if args.resume: + ( + completed_steps, + losses, + previous_elapsed, + optimizer_state_restored, + resume_source, + ) = _load_resume_state(output_dir, expert, optimizer, architecture, device) + if completed_steps > target_steps: + raise ValueError( + f"Checkpoint already has {completed_steps} steps, beyond target {target_steps}" + ) + mode = "exact checkpoint resume" if optimizer_state_restored else "legacy warm start" + print( + f"stage2 {mode}: source={resume_source} completed={completed_steps} " + f"remaining={target_steps - completed_steps}" + ) + + new_losses: list[float] = [] + started = time.perf_counter() + expert.train() + progress = trange( + completed_steps, + target_steps, + desc="stage2-expert", + initial=completed_steps, + total=target_steps, + ) + for step in progress: + sample_index = step % len(samples) + target = target_actions[sample_index : sample_index + 1] + training_data = flow.construct_training_data(target) + optimizer.zero_grad(set_to_none=True) + prediction = expert( + noisy_action=training_data["noisy_action"], + timestep=training_data["timestep"], + prefix=prefixes[sample_index], + ) + loss = flow.loss(training_data, prediction) + if not torch.isfinite(loss): + raise RuntimeError(f"Non-finite Stage-2 loss at step {step}: {loss}") + loss.backward() + torch.nn.utils.clip_grad_norm_(expert.parameters(), 1.0) + optimizer.step() + value = float(loss.detach().cpu()) + losses.append(value) + new_losses.append(value) + if step == 0 or (step + 1) % stage2.log_every == 0: + print(f"stage2 step={step + 1} loss={value:.6f}") + if (step + 1) % stage2.checkpoint_every == 0: + elapsed_so_far = previous_elapsed + (time.perf_counter() - started) + _save_training_checkpoint( + checkpoint_dir, + expert, + optimizer, + architecture, + step + 1, + losses, + elapsed_so_far, + device, + ) + elapsed_this_run = time.perf_counter() - started + elapsed = previous_elapsed + elapsed_this_run + + _save_training_checkpoint( + checkpoint_dir, + expert, + optimizer, + architecture, + target_steps, + losses, + elapsed, + device, + ) + _atomic_save_safetensors(expert, output_dir / "expert.safetensors") + write_json( + output_dir / "expert_config.json", + { + "architecture": asdict(architecture), + "action_mean": action_space.mean.detach().cpu().tolist(), + "action_scale": action_space.scale.detach().cpu().tolist(), + "action_shape": [cfg.data.expert_waypoints, 2], + "inference_steps": stage2.inference_steps, + "stage1_checkpoint": str(stage1_dir), + "completed_steps": target_steps, + }, + ) + + first = samples[0] + first_history, first_future = first.load_trajectory() + generated_reasoning = generate_reasoning( + vlm, + processor, + first.load_image(), + build_vlm_prefix(first_history), + device, + dtype, + cfg.vlm.max_reasoning_tokens, + ) + generated_prefix = extract_prefix_kv( + vlm, + processor, + first.load_image(), + build_expert_prefix(first_history, generated_reasoning), + device, + dtype, + ) + expert.eval() + seed_everything(cfg.seed + 1) + predicted_action = flow.sample( + batch_size=1, + step_fn=partial(expert, prefix=generated_prefix), + device=device, + dtype=dtype, + ).float() + predicted_xy = action_space.action_to_trajectory( + predicted_action, first_history[None, :, :2].to(device) + ) + target_xy = first_future[None, :, :2].to(device) + target_action = action_space.trajectory_to_action( + first_history[None, :, :2].to(device), target_xy + ) + metrics = { + "stage": "stage2_expert", + "device": str(device), + "dtype": str(dtype), + "vlm_parameters": vlm_counts, + "expert_parameters": expert_counts, + "architecture": asdict(architecture), + "steps": target_steps, + "additional_steps": len(new_losses), + "resumed_from_step": completed_steps, + "resume_source": resume_source, + "optimizer_state_restored": optimizer_state_restored, + "learning_rate": stage2.learning_rate, + "elapsed_seconds": elapsed, + "elapsed_seconds_this_run": elapsed_this_run, + "initial_loss": losses[0], + "resume_initial_loss": new_losses[0] if new_losses else None, + "final_loss": losses[-1], + "minimum_loss": min(losses), + "losses": losses, + "generated_reasoning": generated_reasoning, + "target_reasoning": first.reasoning, + "action_mse": float( + torch.nn.functional.mse_loss(predicted_action, target_action.float()).cpu() + ), + **trajectory_metrics(predicted_xy, target_xy), + } + write_json(output_dir / "stage2_metrics.json", metrics) + save_prediction_arrays( + output_dir / "prediction.npz", + first_history[:, :2], + target_action[0], + predicted_action[0], + target_xy[0], + predicted_xy[0], + ) + save_trajectory_plot( + output_dir / "trajectory.png", + first_history[:, :2], + target_xy[0], + predicted_xy[0], + ) + print(metrics) + del optimizer, expert, vlm + empty_device_cache(device) + + +if __name__ == "__main__": + main() diff --git a/scripts/train_vlm_trajectory_logits.py b/scripts/train_vlm_trajectory_logits.py new file mode 100644 index 0000000..eb61160 --- /dev/null +++ b/scripts/train_vlm_trajectory_logits.py @@ -0,0 +1,662 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import time +from dataclasses import asdict +from pathlib import Path +from typing import Any + +import numpy as np +import torch +from transformers import LogitsProcessorList, StoppingCriteriaList +from tqdm import trange + +from alpamayo_lite.action_space import RelativeDeltaActionSpace +from alpamayo_lite.config import load_config +from alpamayo_lite.data import load_manifest +from alpamayo_lite.evaluation import save_trajectory_plot, trajectory_metrics +from alpamayo_lite.expert import PrefixKV +from alpamayo_lite.prompts import build_vlm_prefix +from alpamayo_lite.runtime import ( + empty_device_cache, + parameter_counts, + resolve_dtype, + seed_everything, + select_device, + write_json, +) +from alpamayo_lite.trajectory_tokens import ( + DiscreteTrajectoryMask, + StopOneTokenAfterBoundary, + TrajectoryVocabulary, + add_trajectory_vocabulary, + build_structured_target, + dequantize_action, + extract_trajectory_values, + mask_after_boundary, + quantize_action, +) +from alpamayo_lite.vlm import ( + configure_stage1_trainability, + enforce_frozen_module_eval, + load_vlm, + prepare_stage1_batch, + process_image_text, +) + + +def _resolve(root: Path, value: str) -> Path: + path = Path(value).expanduser() + return path.resolve() if path.is_absolute() else (root / path).resolve() + + +def _read_json(path: Path) -> dict[str, Any]: + return json.loads(path.read_text(encoding="utf-8")) + + +def _infer_meta_action(reasoning: str) -> str: + lowered = reasoning.lower() + for phrase, label in ( + ("steer left", "STEER_LEFT"), + ("turn left", "TURN_LEFT"), + ("steer right", "STEER_RIGHT"), + ("turn right", "TURN_RIGHT"), + ("stop", "STOP"), + ("slow", "SLOW_DOWN"), + ): + if phrase in lowered: + return label + return "CONTINUE" + + +def _vocabulary_from_tokenizer(tokenizer: Any, size: int) -> TrajectoryVocabulary: + start_id = int(tokenizer.convert_tokens_to_ids("")) + end_id = int(tokenizer.convert_tokens_to_ids(f"")) + if end_id - start_id + 1 != size: + raise RuntimeError("Reloaded trajectory vocabulary is not contiguous") + special_tokens = ( + "<|cot_start|>", + "<|cot_end|>", + "<|meta_action_start|>", + "<|meta_action_end|>", + "<|traj_future_start|>", + "<|traj_future_end|>", + ) + special_token_ids = { + token: int(tokenizer.convert_tokens_to_ids(token)) for token in special_tokens + } + return TrajectoryVocabulary(size, start_id, end_id, special_token_ids) + + +def _cache_summary(cache: object) -> dict[str, Any]: + prefix = PrefixKV.from_transformers_cache(cache) + first_key = prefix.keys[0] + sample = first_key[..., : min(8, first_key.shape[-2]), : min(8, first_key.shape[-1])] + return { + "layers": prefix.num_layers, + "sequence_length": prefix.sequence_length, + "first_key_shape": list(first_key.shape), + "first_key_sample_sum": float(sample.float().sum().cpu()), + "all_finite": bool( + all(torch.isfinite(key).all() and torch.isfinite(value).all() for key, value in zip(prefix.keys, prefix.values)) + ), + } + + +def _top_tokens(logits: torch.Tensor, tokenizer: Any, count: int = 5) -> list[dict[str, Any]]: + values, ids = torch.topk(logits.float(), k=count) + return [ + { + "id": int(token_id), + "token": tokenizer.convert_ids_to_tokens(int(token_id)), + "logit": float(value), + } + for value, token_id in zip(values.cpu(), ids.cpu()) + ] + + +@torch.no_grad() +def _generate( + model: Any, + processor: Any, + image: Any, + prefix_text: str, + device: torch.device, + dtype: torch.dtype, + max_new_tokens: int, + logits_processor: LogitsProcessorList | None = None, + stopping_criteria: StoppingCriteriaList | None = None, +) -> tuple[dict[str, Any], torch.Tensor, Any]: + inputs = process_image_text(processor, image, prefix_text, device, dtype) + prompt_length = inputs["input_ids"].shape[1] + generated = model.generate( + **inputs, + max_new_tokens=max_new_tokens, + do_sample=False, + use_cache=True, + return_dict_in_generate=True, + output_logits=True, + output_scores=True, + logits_processor=logits_processor, + stopping_criteria=stopping_criteria, + pad_token_id=processor.tokenizer.pad_token_id, + eos_token_id=processor.tokenizer.eos_token_id, + ) + new_ids = generated.sequences[:, prompt_length:] + step_records = [] + returned_logits = generated.logits or () + for index, token_id in enumerate(new_ids[0].tolist()): + if index >= len(returned_logits): + break + logits = returned_logits[index][0] + log_probability = torch.log_softmax(logits.float(), dim=-1)[token_id] + step_records.append( + { + "index": index, + "id": int(token_id), + "token": processor.tokenizer.convert_ids_to_tokens(int(token_id)), + "selected_logit": float(logits[token_id].float().cpu()), + "selected_log_probability": float(log_probability.cpu()), + "top5": _top_tokens(logits, processor.tokenizer), + } + ) + result = { + "prompt_length": prompt_length, + "new_token_ids": new_ids[0].detach().cpu().tolist(), + "raw_text": processor.tokenizer.decode(new_ids[0], skip_special_tokens=False), + "clean_text": processor.tokenizer.decode(new_ids[0], skip_special_tokens=True), + "steps": step_records, + "cache": _cache_summary(generated.past_key_values), + } + return result, new_ids, generated + + +@torch.no_grad() +def _teacher_forcing_probe( + model: Any, + inputs: dict[str, torch.Tensor], + labels: torch.Tensor, + vocabulary: TrajectoryVocabulary, + tokenizer: Any, +) -> tuple[dict[str, Any], np.ndarray, np.ndarray]: + outputs = model(**inputs, use_cache=False, return_dict=True) + logits = outputs.logits[:, :-1] + next_ids = inputs["input_ids"][:, 1:] + supervised = labels[:, 1:] != -100 + trajectory = ( + (next_ids >= vocabulary.start_id) + & (next_ids <= vocabulary.end_id) + & supervised + ) + supervised_logits = logits[supervised].float() + supervised_ids = next_ids[supervised] + trajectory_logits = logits[trajectory].float() + trajectory_ids = next_ids[trajectory] + if trajectory_logits.shape[0] == 0: + raise RuntimeError("No supervised trajectory tokens were found") + + target_logits = trajectory_logits.gather(1, trajectory_ids[:, None]).squeeze(1) + ranks = (trajectory_logits > target_logits[:, None]).sum(dim=1) + 1 + probabilities = torch.softmax(trajectory_logits, dim=-1).gather( + 1, trajectory_ids[:, None] + ).squeeze(1) + top_values, top_ids = torch.topk(trajectory_logits, k=5, dim=-1) + records = [] + for index in range(trajectory_logits.shape[0]): + records.append( + { + "index": index, + "target_id": int(trajectory_ids[index]), + "target_token": tokenizer.convert_ids_to_tokens(int(trajectory_ids[index])), + "target_logit": float(target_logits[index].cpu()), + "target_probability": float(probabilities[index].cpu()), + "target_rank": int(ranks[index].cpu()), + "top5": [ + { + "id": int(token_id), + "token": tokenizer.convert_ids_to_tokens(int(token_id)), + "logit": float(value), + } + for value, token_id in zip(top_values[index].cpu(), top_ids[index].cpu()) + ], + } + ) + + discrete_slice = trajectory_logits[:, vocabulary.start_id : vocabulary.end_id + 1] + result = { + "supervised_token_count": int(supervised.sum()), + "trajectory_supervised_token_count": int(trajectory.sum()), + "next_token_accuracy": float( + (supervised_logits.argmax(dim=-1) == supervised_ids).float().mean().cpu() + ), + "trajectory_next_token_accuracy": float( + (trajectory_logits.argmax(dim=-1) == trajectory_ids).float().mean().cpu() + ), + "trajectory_mean_target_probability": float(probabilities.mean().cpu()), + "trajectory_max_target_rank": int(ranks.max().cpu()), + "trajectory_records": records, + } + return ( + result, + discrete_slice.detach().cpu().numpy(), + (trajectory_ids - vocabulary.start_id).detach().cpu().numpy(), + ) + + +@torch.no_grad() +def _handoff_probe( + model: Any, + processor: Any, + image: Any, + handoff_text: str, + first_target_id: int, + vocabulary: TrajectoryVocabulary, + device: torch.device, + dtype: torch.dtype, +) -> tuple[dict[str, Any], np.ndarray, np.ndarray]: + inputs = process_image_text(processor, image, handoff_text, device, dtype) + outputs = model( + **inputs, + use_cache=True, + return_dict=True, + logits_to_keep=1, + ) + raw = outputs.logits[0, -1].float() + masked = DiscreteTrajectoryMask(vocabulary.start_id, vocabulary.size)( + inputs["input_ids"], raw[None].clone() + )[0] + target_logit = raw[first_target_id] + target_rank = int((raw > target_logit).sum().cpu()) + 1 + outside = torch.ones_like(raw, dtype=torch.bool) + outside[vocabulary.start_id : vocabulary.end_id + 1] = False + result = { + "first_target_id": first_target_id, + "first_target_token": processor.tokenizer.convert_ids_to_tokens(first_target_id), + "raw_first_target_logit": float(target_logit.cpu()), + "raw_first_target_rank": target_rank, + "raw_top5": _top_tokens(raw, processor.tokenizer), + "processed_top5": _top_tokens(masked, processor.tokenizer), + "raw_trajectory_finite_count": int( + torch.isfinite(raw[vocabulary.start_id : vocabulary.end_id + 1]).sum().cpu() + ), + "processed_trajectory_negative_infinity_count": int( + torch.isneginf(masked[vocabulary.start_id : vocabulary.end_id + 1]).sum().cpu() + ), + "outside_range_max_absolute_change": float((masked[outside] - raw[outside]).abs().max().cpu()), + "cache": _cache_summary(outputs.past_key_values), + } + return ( + result, + raw[vocabulary.start_id : vocabulary.end_id + 1].detach().cpu().numpy(), + masked[vocabulary.start_id : vocabulary.end_id + 1].detach().cpu().numpy(), + ) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--config", default="configs/local_overfit.yaml") + parser.add_argument("--output-dir", default="outputs/stage1_vlm_trajectory") + parser.add_argument("--steps", type=int, default=120) + parser.add_argument("--learning-rate", type=float, default=1e-4) + parser.add_argument("--trajectory-vocab-size", type=int, default=128) + parser.add_argument("--action-min", type=float, default=-4.0) + parser.add_argument("--action-max", type=float, default=4.0) + parser.add_argument("--max-new-tokens", type=int, default=96) + parser.add_argument("--early-stop-loss", type=float, default=1e-4) + parser.add_argument("--early-stop-patience", type=int, default=5) + args = parser.parse_args() + if args.steps <= 0: + raise ValueError("--steps must be positive") + if args.early_stop_loss < 0: + raise ValueError("--early-stop-loss must be non-negative") + if args.early_stop_patience <= 0: + raise ValueError("--early-stop-patience must be positive") + + cfg = load_config(args.config) + seed_everything(cfg.seed) + device = select_device() + dtype = resolve_dtype(cfg.vlm.dtype, device) + samples = load_manifest(cfg.resolve(cfg.paths.manifest)) + if len(samples) != 1: + raise ValueError("This local trajectory-logit validation expects exactly one sample") + sample = samples[0] + history, future = sample.load_trajectory() + image = sample.load_image() + + processor, model = load_vlm(cfg.resolve(cfg.paths.stage1_dir), cfg.vlm, device, dtype) + tokenizer = processor.tokenizer + original_tokenizer_length = len(tokenizer) + vocabulary = add_trajectory_vocabulary(tokenizer, args.trajectory_vocab_size) + model_vocab_size = model.get_output_embeddings().weight.shape[0] + if len(tokenizer) > model_vocab_size: + raise RuntimeError( + f"Expanded tokenizer ({len(tokenizer)}) exceeds preallocated LM head ({model_vocab_size})" + ) + if model.get_input_embeddings().weight.data_ptr() != model.get_output_embeddings().weight.data_ptr(): + raise RuntimeError("Trajectory validation expects tied input/output embeddings") + + expert_config = _read_json(cfg.resolve(cfg.paths.stage2_dir) / "expert_config.json") + action_space = RelativeDeltaActionSpace( + torch.tensor(expert_config["action_mean"], device=device), + torch.tensor(expert_config["action_scale"], device=device), + ).to(device) + target_xy = future[None, :, :2].to(device) + history_xy = history[None, :, :2].to(device) + target_action = action_space.trajectory_to_action(history_xy, target_xy) + quantized = quantize_action( + target_action, + args.trajectory_vocab_size, + args.action_min, + args.action_max, + ) + meta_action = _infer_meta_action(sample.reasoning) + structured_target = build_structured_target(sample.reasoning, meta_action, quantized) + prefix_text = build_vlm_prefix(history) + full_text = prefix_text + structured_target + + baseline, _, baseline_output = _generate( + model, + processor, + image, + prefix_text, + device, + dtype, + args.max_new_tokens, + ) + del baseline_output + + trainability = configure_stage1_trainability(model, cfg.stage1) + embedding_weight = model.get_input_embeddings().weight + embedding_weight.requires_grad_(True) + trainable_rows = list(range(vocabulary.start_id, vocabulary.end_id + 1)) + list( + vocabulary.special_token_ids.values() + ) + gradient_mask = torch.zeros( + embedding_weight.shape[0], 1, device=embedding_weight.device, dtype=embedding_weight.dtype + ) + gradient_mask[trainable_rows] = 1 + gradient_hook = embedding_weight.register_hook(lambda gradient: gradient * gradient_mask) + + inputs, labels, prefix_length = prepare_stage1_batch( + processor, + image, + prefix_text, + full_text, + device, + dtype, + ) + target_ids = inputs["input_ids"][:, prefix_length:] + trajectory_supervision_count = int( + ( + (target_ids >= vocabulary.start_id) + & (target_ids <= vocabulary.end_id) + ).sum().cpu() + ) + if trajectory_supervision_count != quantized.numel(): + raise RuntimeError( + f"Expected {quantized.numel()} trajectory labels, found {trajectory_supervision_count}" + ) + + optimizer = torch.optim.AdamW( + [parameter for parameter in model.parameters() if parameter.requires_grad], + lr=args.learning_rate, + weight_decay=cfg.stage1.weight_decay, + eps=cfg.stage1.adam_epsilon, + ) + losses: list[float] = [] + accuracies: list[float] = [] + converged_steps = 0 + started = time.perf_counter() + model.train() + enforce_frozen_module_eval(model) + for step in trange(args.steps, desc="vlm-trajectory-logits"): + optimizer.zero_grad(set_to_none=True) + outputs = model(**inputs, labels=labels, use_cache=False, return_dict=True) + loss = outputs.loss + if loss is None or not torch.isfinite(loss): + raise RuntimeError(f"Non-finite trajectory-logit loss at step {step}: {loss}") + loss.backward() + torch.nn.utils.clip_grad_norm_( + [parameter for parameter in model.parameters() if parameter.requires_grad], 1.0 + ) + optimizer.step() + value = float(loss.detach().cpu()) + next_predictions = outputs.logits[:, :-1].argmax(dim=-1) + shifted_labels = labels[:, 1:] + supervised = shifted_labels != -100 + accuracy = float( + (next_predictions[supervised] == shifted_labels[supervised]).float().mean().cpu() + ) + losses.append(value) + accuracies.append(accuracy) + if step == 0 or (step + 1) % cfg.stage1.log_every == 0: + print(f"trajectory-vlm step={step + 1} loss={value:.6f} accuracy={accuracy:.4f}") + del outputs, next_predictions + if value <= args.early_stop_loss and accuracy == 1.0: + converged_steps += 1 + else: + converged_steps = 0 + if converged_steps >= args.early_stop_patience: + print( + f"trajectory-vlm converged at step={step + 1}: " + f"loss={value:.6f} accuracy={accuracy:.4f}" + ) + break + elapsed = time.perf_counter() - started + + model.gradient_checkpointing_disable() + model.config.use_cache = True + model.model.language_model.config.use_cache = True + output_dir = _resolve(cfg.root, args.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + model.save_pretrained(output_dir, safe_serialization=True, max_shard_size="2GB") + processor.save_pretrained(output_dir) + write_json( + output_dir / "trajectory_vocabulary.json", + { + **asdict(vocabulary), + "original_tokenizer_length": original_tokenizer_length, + "expanded_tokenizer_length": len(tokenizer), + "model_vocab_size": model_vocab_size, + "preallocated_rows_used": True, + "action_min": args.action_min, + "action_max": args.action_max, + "action_mean": expert_config["action_mean"], + "action_scale": expert_config["action_scale"], + "action_shape": expert_config["action_shape"], + }, + ) + gradient_hook.remove() + del optimizer, model, processor + empty_device_cache(device) + + processor, model = load_vlm(output_dir, cfg.vlm, device, dtype) + model.requires_grad_(False) + model.eval() + vocabulary = _vocabulary_from_tokenizer(processor.tokenizer, args.trajectory_vocab_size) + inputs, labels, prefix_length = prepare_stage1_batch( + processor, + image, + prefix_text, + full_text, + device, + dtype, + ) + teacher, teacher_logits, teacher_values = _teacher_forcing_probe( + model, inputs, labels, vocabulary, processor.tokenizer + ) + + cot_and_meta = ( + f"<|cot_start|>{sample.reasoning}<|cot_end|>\n" + f"<|meta_action_start|>{meta_action}<|meta_action_end|>\n" + ) + handoff_text = prefix_text + cot_and_meta + "<|traj_future_start|>" + first_target_id = vocabulary.start_id + int(quantized.reshape(-1)[0].cpu()) + handoff, boundary_raw, boundary_masked = _handoff_probe( + model, + processor, + image, + handoff_text, + first_target_id, + vocabulary, + device, + dtype, + ) + + full_generation, full_ids, full_output = _generate( + model, + processor, + image, + prefix_text, + device, + dtype, + args.max_new_tokens, + ) + generated_values = extract_trajectory_values(full_ids[0], vocabulary) + expected_values = quantized.detach().cpu().reshape(-1) + generated_token_accuracy = ( + float((generated_values.cpu() == expected_values).float().mean()) + if generated_values.numel() == expected_values.numel() + else 0.0 + ) + if generated_values.numel() != target_action.numel(): + raise RuntimeError( + f"Generated {generated_values.numel()} trajectory tokens, expected {target_action.numel()}" + ) + generated_action = dequantize_action( + generated_values.reshape_as(target_action).to(device), + args.trajectory_vocab_size, + args.action_min, + args.action_max, + ) + generated_xy = action_space.action_to_trajectory(generated_action, history_xy) + quantized_target_action = dequantize_action( + quantized, + args.trajectory_vocab_size, + args.action_min, + args.action_max, + ) + quantized_target_xy = action_space.action_to_trajectory(quantized_target_action, history_xy) + del full_output + + boundary_id = vocabulary.special_token_ids["<|traj_future_start|>"] + source_mask = DiscreteTrajectoryMask(vocabulary.start_id, vocabulary.size) + source_stop = StopOneTokenAfterBoundary(boundary_id) + truncated_generation, truncated_ids, truncated_output = _generate( + model, + processor, + image, + prefix_text, + device, + dtype, + args.max_new_tokens, + logits_processor=LogitsProcessorList([source_mask]), + stopping_criteria=StoppingCriteriaList([source_stop]), + ) + visible_ids = mask_after_boundary( + truncated_output.sequences, + boundary_id, + processor.tokenizer.pad_token_id, + ) + truncated_generation["visible_new_token_ids"] = visible_ids[ + :, truncated_generation["prompt_length"] : + ][0].cpu().tolist() + truncated_generation["visible_raw_text"] = processor.tokenizer.decode( + visible_ids[:, truncated_generation["prompt_length"] :][0], + skip_special_tokens=False, + ) + truncated_generation["boundary_generated"] = bool((truncated_ids == boundary_id).any()) + truncated_generation["trajectory_tokens_visible"] = int( + ( + (visible_ids >= vocabulary.start_id) + & (visible_ids <= vocabulary.end_id) + ).sum().cpu() + ) + boundary_positions = torch.nonzero(truncated_ids[0] == boundary_id, as_tuple=False).flatten() + post_boundary_logit_index = ( + int(boundary_positions[0]) + 1 if boundary_positions.numel() else -1 + ) + if 0 <= post_boundary_logit_index < len(truncated_output.logits or ()): + post_boundary_logits = truncated_output.logits[post_boundary_logit_index][0] + truncated_generation["returned_post_boundary_trajectory_negative_infinity_count"] = int( + torch.isneginf( + post_boundary_logits[vocabulary.start_id : vocabulary.end_id + 1] + ).sum().cpu() + ) + del truncated_output + + save_trajectory_plot( + output_dir / "vlm_discrete_trajectory.png", + history[:, :2], + target_xy[0], + generated_xy[0], + prediction_label="VLM discrete tokens", + title="VLM discrete-trajectory logits", + ) + np.savez_compressed( + output_dir / "trajectory_logits.npz", + teacher_trajectory_logits=teacher_logits, + teacher_target_values=teacher_values, + boundary_raw_trajectory_logits=boundary_raw, + boundary_masked_trajectory_logits=boundary_masked, + target_action=target_action.detach().cpu().numpy(), + quantized_target_values=quantized.detach().cpu().numpy(), + generated_values=generated_values.detach().cpu().numpy(), + generated_action=generated_action.detach().cpu().numpy(), + target_xy=target_xy.detach().cpu().numpy(), + generated_xy=generated_xy.detach().cpu().numpy(), + ) + + metrics = { + "stage": "vlm_discrete_trajectory_logits", + "device": str(device), + "dtype": str(dtype), + "model_parameters": parameter_counts(model), + "vocabulary": asdict(vocabulary), + "original_tokenizer_length": original_tokenizer_length, + "expanded_tokenizer_length": len(processor.tokenizer), + "model_vocab_size": model_vocab_size, + "semantic_trajectory_supervision_count": trajectory_supervision_count, + "structured_target": structured_target, + "target_meta_action": meta_action, + "training": { + "steps": len(losses), + "requested_steps": args.steps, + "learning_rate": args.learning_rate, + "elapsed_seconds": elapsed, + "initial_loss": losses[0], + "final_loss": losses[-1], + "minimum_loss": min(losses), + "final_training_accuracy": accuracies[-1], + "losses": losses, + "accuracies": accuracies, + "base_trainability": trainability, + "trainable_token_rows": len(trainable_rows), + }, + "baseline_generation_before_structured_training": baseline, + "teacher_forcing": teacher, + "raw_handoff_logits_and_cache": handoff, + "untruncated_vlm_generation": { + **full_generation, + "trajectory_token_count": int(generated_values.numel()), + "trajectory_token_accuracy": generated_token_accuracy, + "generated_values": generated_values.cpu().tolist(), + "target_values": expected_values.tolist(), + "action_mse": float( + torch.nn.functional.mse_loss(generated_action, target_action).cpu() + ), + **trajectory_metrics(generated_xy, target_xy), + }, + "quantization_only": trajectory_metrics(quantized_target_xy, target_xy), + "source_style_diffusion_handoff_generation": truncated_generation, + } + write_json(output_dir / "trajectory_logits_metrics.json", metrics) + print(json.dumps(metrics, indent=2, ensure_ascii=False)) + del model, processor + empty_device_cache(device) + + +if __name__ == "__main__": + main() diff --git a/scripts/verify_connection.py b/scripts/verify_connection.py new file mode 100644 index 0000000..49b96d7 --- /dev/null +++ b/scripts/verify_connection.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +from dataclasses import asdict + +import torch + +from alpamayo_lite.config import load_config +from alpamayo_lite.data import load_manifest +from alpamayo_lite.expert import ExpertArchitecture, KVCachedDiffusionExpert +from alpamayo_lite.prompts import build_expert_prefix, build_vlm_prefix +from alpamayo_lite.runtime import ( + empty_device_cache, + parameter_counts, + resolve_dtype, + seed_everything, + select_device, + write_json, +) +from alpamayo_lite.vlm import ( + configure_stage1_trainability, + extract_prefix_kv, + generate_reasoning, + load_vlm, + vlm_text_config, +) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--config", default="configs/local_overfit.yaml") + args = parser.parse_args() + cfg = load_config(args.config) + seed_everything(cfg.seed) + device = select_device() + dtype = resolve_dtype(cfg.vlm.dtype, device) + sample = load_manifest(cfg.resolve(cfg.paths.manifest))[0] + history, _ = sample.load_trajectory() + + processor, vlm = load_vlm(cfg.resolve(cfg.paths.model_dir), cfg.vlm, device, dtype) + vlm.eval() + baseline_text = generate_reasoning( + vlm, + processor, + sample.load_image(), + build_vlm_prefix(history), + device, + dtype, + cfg.vlm.max_reasoning_tokens, + ) + prefix = extract_prefix_kv( + vlm, + processor, + sample.load_image(), + build_expert_prefix(history, sample.reasoning), + device, + dtype, + ) + + stage2 = cfg.stage2 + architecture = ExpertArchitecture( + hidden_size=stage2.expert_hidden_size, + intermediate_size=stage2.expert_intermediate_size, + num_layers=stage2.expert_num_layers, + num_attention_heads=stage2.expert_num_attention_heads, + num_key_value_heads=stage2.expert_num_key_value_heads, + head_dim=stage2.expert_head_dim, + action_projection_hidden_size=stage2.action_projection_hidden_size, + action_fourier_features=stage2.action_fourier_features, + non_causal_attention=stage2.expert_non_causal_attention, + ) + expert = KVCachedDiffusionExpert(vlm_text_config(vlm), architecture).to( + device=device, dtype=dtype + ) + with torch.no_grad(): + predicted_velocity = expert( + noisy_action=torch.randn(1, cfg.data.expert_waypoints, 2, device=device, dtype=dtype), + timestep=torch.full((1, 1, 1), 0.5, device=device, dtype=dtype), + prefix=prefix, + ) + + trainability = configure_stage1_trainability(vlm, cfg.stage1) + result = { + "device": str(device), + "dtype": str(dtype), + "vlm_parameters": parameter_counts(vlm), + "stage1_trainability": trainability, + "expert_parameters": parameter_counts(expert), + "expert_architecture": asdict(architecture), + "prefix_layers": prefix.num_layers, + "prefix_sequence_length": prefix.sequence_length, + "prefix_key_shape": list(prefix.keys[0].shape), + "expert_output_shape": list(predicted_velocity.shape), + "expert_output_finite": bool(torch.isfinite(predicted_velocity).all().item()), + "baseline_text": baseline_text, + "target_text": sample.reasoning, + } + write_json(cfg.root / "outputs" / "connection_test.json", result) + print(result) + del expert, vlm + empty_device_cache(device) + + +if __name__ == "__main__": + main() diff --git a/scripts/verify_structured_vlm_handoff.py b/scripts/verify_structured_vlm_handoff.py new file mode 100644 index 0000000..d827f24 --- /dev/null +++ b/scripts/verify_structured_vlm_handoff.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +from dataclasses import asdict +from functools import partial +from pathlib import Path +from typing import Any + +import torch +from safetensors.torch import load_file +from transformers import LogitsProcessorList, StoppingCriteriaList + +from alpamayo_lite.action_space import RelativeDeltaActionSpace +from alpamayo_lite.config import load_config +from alpamayo_lite.data import load_manifest +from alpamayo_lite.diffusion import FlowMatching +from alpamayo_lite.evaluation import ( + save_prediction_arrays, + save_trajectory_plot, + trajectory_metrics, +) +from alpamayo_lite.expert import ExpertArchitecture, KVCachedDiffusionExpert, PrefixKV +from alpamayo_lite.prompts import build_vlm_prefix +from alpamayo_lite.runtime import ( + empty_device_cache, + parameter_counts, + resolve_dtype, + seed_everything, + select_device, + write_json, +) +from alpamayo_lite.trajectory_tokens import ( + DiscreteTrajectoryMask, + StopOneTokenAfterBoundary, + mask_after_boundary, +) +from alpamayo_lite.vlm import load_vlm, process_image_text, vlm_text_config + + +def _resolve(root: Path, value: str) -> Path: + path = Path(value).expanduser() + return path.resolve() if path.is_absolute() else (root / path).resolve() + + +def _read_json(path: Path) -> dict[str, Any]: + return json.loads(path.read_text(encoding="utf-8")) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--config", default="configs/local_overfit.yaml") + parser.add_argument("--vlm-dir", default="outputs/stage1_vlm_trajectory") + parser.add_argument("--stage2-dir", default="outputs/stage2_expert") + parser.add_argument("--output-dir", default="outputs/structured_vlm_handoff") + parser.add_argument("--max-new-tokens", type=int, default=96) + args = parser.parse_args() + + cfg = load_config(args.config) + seed_everything(cfg.seed) + device = select_device() + dtype = resolve_dtype(cfg.vlm.dtype, device) + vlm_dir = _resolve(cfg.root, args.vlm_dir) + stage2_dir = _resolve(cfg.root, args.stage2_dir) + output_dir = _resolve(cfg.root, args.output_dir) + sample = load_manifest(cfg.resolve(cfg.paths.manifest))[0] + history, future = sample.load_trajectory() + + vocabulary = _read_json(vlm_dir / "trajectory_vocabulary.json") + trajectory_start = int(vocabulary["start_id"]) + trajectory_size = int(vocabulary["size"]) + boundary_id = int(vocabulary["special_token_ids"]["<|traj_future_start|>"]) + + processor, vlm = load_vlm(vlm_dir, cfg.vlm, device, dtype) + vlm.requires_grad_(False) + vlm.eval() + inputs = process_image_text( + processor, + sample.load_image(), + build_vlm_prefix(history), + device, + dtype, + ) + prompt_length = inputs["input_ids"].shape[1] + generated = vlm.generate( + **inputs, + max_new_tokens=args.max_new_tokens, + do_sample=False, + use_cache=True, + return_dict_in_generate=True, + output_logits=True, + logits_processor=LogitsProcessorList( + [DiscreteTrajectoryMask(trajectory_start, trajectory_size)] + ), + stopping_criteria=StoppingCriteriaList([StopOneTokenAfterBoundary(boundary_id)]), + pad_token_id=processor.tokenizer.pad_token_id, + eos_token_id=processor.tokenizer.eos_token_id, + ) + new_ids = generated.sequences[:, prompt_length:] + visible_sequences = mask_after_boundary( + generated.sequences, + boundary_id, + processor.tokenizer.pad_token_id, + ) + visible_new_ids = visible_sequences[:, prompt_length:] + prefix = PrefixKV.from_transformers_cache(generated.past_key_values) + boundary_positions = torch.nonzero(new_ids[0] == boundary_id, as_tuple=False).flatten() + if boundary_positions.numel() == 0: + raise RuntimeError("Structured VLM did not generate the diffusion handoff boundary") + boundary_index = int(boundary_positions[0]) + expected_prefix_length = prompt_length + boundary_index + 1 + if prefix.sequence_length != expected_prefix_length: + raise RuntimeError( + f"Generation cache does not end at the handoff boundary: " + f"cache={prefix.sequence_length}, expected={expected_prefix_length}" + ) + + expert_config = _read_json(stage2_dir / "expert_config.json") + architecture = ExpertArchitecture(**expert_config["architecture"]) + expert = KVCachedDiffusionExpert(vlm_text_config(vlm), architecture).to( + device=device, dtype=dtype + ) + expert.load_state_dict(load_file(stage2_dir / "expert.safetensors"), strict=True) + expert.requires_grad_(False) + expert.eval() + flow = FlowMatching( + action_shape=tuple(expert_config["action_shape"]), + inference_steps=int(expert_config["inference_steps"]), + ) + action_space = RelativeDeltaActionSpace( + torch.tensor(expert_config["action_mean"], device=device), + torch.tensor(expert_config["action_scale"], device=device), + ).to(device) + seed_everything(cfg.seed + 1) + predicted_action = flow.sample( + batch_size=1, + step_fn=partial(expert, prefix=prefix), + device=device, + dtype=dtype, + ).float() + history_xy = history[None, :, :2].to(device) + target_xy = future[None, :, :2].to(device) + target_action = action_space.trajectory_to_action(history_xy, target_xy) + predicted_xy = action_space.action_to_trajectory(predicted_action, history_xy) + + post_boundary_index = boundary_index + 1 + returned_masked_count = None + if 0 <= post_boundary_index < len(generated.logits or ()): + post_boundary_logits = generated.logits[post_boundary_index][0] + returned_masked_count = int( + torch.isneginf( + post_boundary_logits[trajectory_start : trajectory_start + trajectory_size] + ).sum().cpu() + ) + + result = { + "stage": "structured_vlm_to_diffusion_handoff", + "device": str(device), + "dtype": str(dtype), + "vlm_dir": str(vlm_dir), + "stage2_dir": str(stage2_dir), + "prompt_length": prompt_length, + "generated_token_count_including_one_cache_update_token": int(new_ids.shape[1]), + "boundary_index_in_generated_tokens": boundary_index, + "expected_cache_length_through_boundary": expected_prefix_length, + "visible_vlm_output": processor.tokenizer.decode( + visible_new_ids[0], skip_special_tokens=False + ), + "boundary_generated": bool((new_ids == boundary_id).any()), + "visible_discrete_trajectory_tokens": int( + ( + (visible_new_ids >= trajectory_start) + & (visible_new_ids < trajectory_start + trajectory_size) + ).sum().cpu() + ), + "returned_post_boundary_masked_logits": returned_masked_count, + "handoff_prefix": { + "layers": prefix.num_layers, + "sequence_length": prefix.sequence_length, + "first_key_shape": list(prefix.keys[0].shape), + "all_finite": bool( + all( + torch.isfinite(key).all() and torch.isfinite(value).all() + for key, value in zip(prefix.keys, prefix.values) + ) + ), + }, + "expert_architecture": asdict(architecture), + "expert_parameters": parameter_counts(expert), + "diffusion_output_shape": list(predicted_action.shape), + "diffusion_output_finite": bool(torch.isfinite(predicted_action).all()), + "action_mse": float( + torch.nn.functional.mse_loss(predicted_action, target_action).cpu() + ), + **trajectory_metrics(predicted_xy, target_xy), + "quality_note": ( + "This verifies exact structured-generation KV compatibility. The expert was trained " + "with the earlier plain-text Stage-1 prefix, so trajectory quality is diagnostic only." + ), + } + output_dir.mkdir(parents=True, exist_ok=True) + write_json(output_dir / "handoff_metrics.json", result) + save_prediction_arrays( + output_dir / "handoff_prediction.npz", + history[:, :2], + target_action[0], + predicted_action[0], + target_xy[0], + predicted_xy[0], + ) + save_trajectory_plot( + output_dir / "handoff_trajectory.png", + history[:, :2], + target_xy[0], + predicted_xy[0], + prediction_label="diffusion from structured VLM KV", + title="Structured VLM handoff to diffusion", + ) + print(json.dumps(result, indent=2, ensure_ascii=False)) + del expert, vlm + empty_device_cache(device) + + +if __name__ == "__main__": + main() diff --git a/src/alpamayo_lite/__init__.py b/src/alpamayo_lite/__init__.py new file mode 100644 index 0000000..ee681dc --- /dev/null +++ b/src/alpamayo_lite/__init__.py @@ -0,0 +1,5 @@ +"""Small, local two-stage Alpamayo architecture validation.""" + +from .config import ExperimentConfig, load_config + +__all__ = ["ExperimentConfig", "load_config"] diff --git a/src/alpamayo_lite/action_space.py b/src/alpamayo_lite/action_space.py new file mode 100644 index 0000000..60c0c5c --- /dev/null +++ b/src/alpamayo_lite/action_space.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +from dataclasses import dataclass + +import torch +from torch import nn + + +@dataclass(frozen=True) +class ActionStatistics: + mean: torch.Tensor + scale: torch.Tensor + + +class RelativeDeltaActionSpace(nn.Module): + """Invertible local validation action space. + + Alpamayo's release model predicts normalized acceleration and curvature. For the + architecture smoke test we use normalized per-waypoint XY deltas. It preserves + the important contract: diffusion predicts continuous actions, and a deterministic + decoder integrates those actions back to relative positions. + """ + + def __init__(self, mean: torch.Tensor | None = None, scale: torch.Tensor | None = None): + super().__init__() + if mean is None: + mean = torch.zeros(2, dtype=torch.float32) + if scale is None: + scale = torch.ones(2, dtype=torch.float32) + self.register_buffer("mean", mean.float().reshape(2)) + self.register_buffer("scale", scale.float().reshape(2).clamp_min(1e-3)) + + @staticmethod + def fit(history_xy: torch.Tensor, future_xy: torch.Tensor) -> ActionStatistics: + if history_xy.shape[-1] != 2 or future_xy.shape[-1] != 2: + raise ValueError("history_xy and future_xy must end in XY coordinates") + start = history_xy[..., -1:, :] + previous = torch.cat([start, future_xy[..., :-1, :]], dim=-2) + deltas = future_xy - previous + flat = deltas.reshape(-1, 2).float() + mean = flat.mean(dim=0) + # A floor keeps a one-clip overfit target numerically well conditioned. + scale = flat.std(dim=0, unbiased=False).clamp_min(0.05) + return ActionStatistics(mean=mean, scale=scale) + + def trajectory_to_action( + self, history_xy: torch.Tensor, future_xy: torch.Tensor + ) -> torch.Tensor: + start = history_xy[..., -1:, :] + previous = torch.cat([start, future_xy[..., :-1, :]], dim=-2) + deltas = future_xy - previous + return (deltas - self.mean) / self.scale + + def action_to_trajectory(self, action: torch.Tensor, history_xy: torch.Tensor) -> torch.Tensor: + deltas = action * self.scale + self.mean + return history_xy[..., -1:, :] + torch.cumsum(deltas, dim=-2) diff --git a/src/alpamayo_lite/config.py b/src/alpamayo_lite/config.py new file mode 100644 index 0000000..64b96e9 --- /dev/null +++ b/src/alpamayo_lite/config.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +from dataclasses import dataclass, fields +from pathlib import Path +from typing import Any, TypeVar + +import yaml + + +@dataclass(frozen=True) +class PathsConfig: + model_dir: str + pai_raw_dir: str + manifest: str + stage1_dir: str + stage2_dir: str + + +@dataclass(frozen=True) +class VLMConfig: + repo_id: str + dtype: str = "float16" + attn_implementation: str = "eager" + max_reasoning_tokens: int = 48 + num_additional_image_tokens: int = 1 + + +@dataclass(frozen=True) +class DataConfig: + repo_id: str + num_clips: int = 1 + camera: str = "camera_front_wide_120fov" + num_frames: int = 1 + history_steps: int = 8 + source_future_steps: int = 64 + expert_waypoints: int = 16 + time_step: float = 0.1 + + +@dataclass(frozen=True) +class Stage1Config: + steps: int = 80 + learning_rate: float = 1e-4 + weight_decay: float = 0.0 + adam_epsilon: float = 1e-4 + freeze_vision_tower: bool = True + freeze_multimodal_projector: bool = True + train_last_n_language_layers: int = 8 + train_final_norm: bool = True + train_lm_head: bool = False + gradient_checkpointing: bool = True + log_every: int = 5 + + +@dataclass(frozen=True) +class Stage2Config: + steps: int = 250 + learning_rate: float = 3e-4 + weight_decay: float = 0.0 + adam_epsilon: float = 1e-4 + expert_hidden_size: int = 448 + expert_intermediate_size: int = 2432 + expert_num_layers: int = 24 + expert_num_attention_heads: int = 7 + expert_num_key_value_heads: int = 2 + expert_head_dim: int = 64 + expert_non_causal_attention: bool = True + action_projection_hidden_size: int = 256 + action_fourier_features: int = 20 + inference_steps: int = 10 + log_every: int = 10 + checkpoint_every: int = 50 + + +@dataclass(frozen=True) +class ExperimentConfig: + seed: int + paths: PathsConfig + vlm: VLMConfig + data: DataConfig + stage1: Stage1Config + stage2: Stage2Config + root: Path + + def resolve(self, value: str) -> Path: + path = Path(value) + return path if path.is_absolute() else self.root / path + + +T = TypeVar("T") + + +def _construct(cls: type[T], values: dict[str, Any]) -> T: + known = {item.name for item in fields(cls)} + unknown = set(values) - known + if unknown: + raise ValueError(f"Unknown {cls.__name__} keys: {sorted(unknown)}") + return cls(**values) + + +def load_config(path: str | Path) -> ExperimentConfig: + config_path = Path(path).expanduser().resolve() + raw = yaml.safe_load(config_path.read_text(encoding="utf-8")) + return ExperimentConfig( + seed=int(raw["seed"]), + paths=_construct(PathsConfig, raw["paths"]), + vlm=_construct(VLMConfig, raw["vlm"]), + data=_construct(DataConfig, raw["data"]), + stage1=_construct(Stage1Config, raw["stage1"]), + stage2=_construct(Stage2Config, raw["stage2"]), + root=config_path.parent.parent, + ) diff --git a/src/alpamayo_lite/data.py b/src/alpamayo_lite/data.py new file mode 100644 index 0000000..effb872 --- /dev/null +++ b/src/alpamayo_lite/data.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +import json +from dataclasses import dataclass +from pathlib import Path + +import numpy as np +import torch +from PIL import Image + + +@dataclass(frozen=True) +class OverfitSample: + clip_id: str + t0_us: int + reasoning: str + image_path: Path + trajectory_path: Path + + def load_image(self) -> Image.Image: + with Image.open(self.image_path) as image: + return image.convert("RGB") + + def load_trajectory(self) -> tuple[torch.Tensor, torch.Tensor]: + with np.load(self.trajectory_path) as arrays: + history = torch.from_numpy(arrays["history_xyz"]).float() + future = torch.from_numpy(arrays["future_xyz"]).float() + return history, future + + +def load_manifest(path: str | Path) -> list[OverfitSample]: + manifest_path = Path(path).expanduser().resolve() + root = manifest_path.parent.parent.parent + samples: list[OverfitSample] = [] + for line in manifest_path.read_text(encoding="utf-8").splitlines(): + if not line.strip(): + continue + row = json.loads(line) + image_path = Path(row["image_path"]) + trajectory_path = Path(row["trajectory_path"]) + samples.append( + OverfitSample( + clip_id=str(row["clip_id"]), + t0_us=int(row["t0_us"]), + reasoning=str(row["reasoning"]), + image_path=image_path if image_path.is_absolute() else root / image_path, + trajectory_path=( + trajectory_path if trajectory_path.is_absolute() else root / trajectory_path + ), + ) + ) + if not samples: + raise ValueError(f"No samples found in {manifest_path}") + return samples diff --git a/src/alpamayo_lite/diffusion.py b/src/alpamayo_lite/diffusion.py new file mode 100644 index 0000000..ae7c001 --- /dev/null +++ b/src/alpamayo_lite/diffusion.py @@ -0,0 +1,57 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +# SPDX-License-Identifier: Apache-2.0 +"""Small flow-matching implementation derived from NVlabs/alpamayo.""" + +from __future__ import annotations + +from collections.abc import Callable + +import torch +from torch import nn + + +class FlowMatching(nn.Module): + def __init__(self, action_shape: tuple[int, int], inference_steps: int = 10): + super().__init__() + self.action_shape = action_shape + self.inference_steps = inference_steps + + def construct_training_data(self, target_action: torch.Tensor) -> dict[str, torch.Tensor]: + batch_size = target_action.shape[0] + t = torch.rand(batch_size, 1, 1, device=target_action.device) + noise = torch.randn_like(target_action) + noisy_action = t * target_action + (1.0 - t) * noise + return { + "target_action": target_action, + "noisy_action": noisy_action, + "timestep": t, + "noise": noise, + } + + @staticmethod + def loss(training_data: dict[str, torch.Tensor], prediction: torch.Tensor) -> torch.Tensor: + target_velocity = training_data["target_action"] - training_data["noise"] + return torch.nn.functional.mse_loss(prediction.float(), target_velocity.float()) + + @torch.no_grad() + def sample( + self, + batch_size: int, + step_fn: Callable[[torch.Tensor, torch.Tensor], torch.Tensor], + device: torch.device, + dtype: torch.dtype, + generator: torch.Generator | None = None, + ) -> torch.Tensor: + x = torch.randn( + batch_size, + *self.action_shape, + device=device, + dtype=dtype, + generator=generator, + ) + time_steps = torch.linspace(0.0, 1.0, self.inference_steps + 1, device=device) + for index in range(self.inference_steps): + t = time_steps[index].expand(batch_size).reshape(batch_size, 1, 1) + dt = time_steps[index + 1] - time_steps[index] + x = x + dt * step_fn(x, t) + return x diff --git a/src/alpamayo_lite/evaluation.py b/src/alpamayo_lite/evaluation.py new file mode 100644 index 0000000..a8078c9 --- /dev/null +++ b/src/alpamayo_lite/evaluation.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +import torch + + +def trajectory_metrics(predicted_xy: torch.Tensor, target_xy: torch.Tensor) -> dict[str, float]: + distances = torch.linalg.vector_norm(predicted_xy.float() - target_xy.float(), dim=-1) + return { + "ade_m": float(distances.mean().cpu()), + "fde_m": float(distances[..., -1].mean().cpu()), + "max_error_m": float(distances.max().cpu()), + } + + +def save_trajectory_plot( + path: str | Path, + history_xy: torch.Tensor, + target_xy: torch.Tensor, + predicted_xy: torch.Tensor, + *, + prediction_label: str = "diffusion", + title: str = "PAI overfit trajectory", +) -> None: + output = Path(path) + output.parent.mkdir(parents=True, exist_ok=True) + history = history_xy.detach().cpu().numpy() + target = target_xy.detach().cpu().numpy() + predicted = predicted_xy.detach().cpu().numpy() + figure, axis = plt.subplots(figsize=(6, 5)) + axis.plot(history[:, 0], history[:, 1], "o-", label="history", color="#555555") + axis.plot(target[:, 0], target[:, 1], "o-", label="target", color="#d62728") + axis.plot( + predicted[:, 0], + predicted[:, 1], + "o-", + label=prediction_label, + color="#1f77b4", + ) + axis.scatter([0], [0], marker="x", color="black", label="current ego") + axis.set_xlabel("ego-relative x (m)") + axis.set_ylabel("ego-relative y (m)") + axis.set_title(title) + axis.axis("equal") + axis.grid(alpha=0.25) + axis.legend() + figure.tight_layout() + figure.savefig(output, dpi=160) + plt.close(figure) + + +def save_prediction_arrays( + path: str | Path, + history_xy: torch.Tensor, + target_action: torch.Tensor, + predicted_action: torch.Tensor, + target_xy: torch.Tensor, + predicted_xy: torch.Tensor, +) -> None: + output = Path(path) + output.parent.mkdir(parents=True, exist_ok=True) + np.savez_compressed( + output, + history_xy=history_xy.detach().cpu().numpy(), + target_action=target_action.detach().cpu().numpy(), + predicted_action=predicted_action.detach().cpu().numpy(), + target_xy=target_xy.detach().cpu().numpy(), + predicted_xy=predicted_xy.detach().cpu().numpy(), + ) diff --git a/src/alpamayo_lite/expert.py b/src/alpamayo_lite/expert.py new file mode 100644 index 0000000..66a3952 --- /dev/null +++ b/src/alpamayo_lite/expert.py @@ -0,0 +1,237 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +# SPDX-License-Identifier: Apache-2.0 +"""Past-KV-conditioned Qwen expert, following the Alpamayo connection pattern.""" + +from __future__ import annotations + +import copy +import math +from dataclasses import dataclass + +import torch +from torch import nn +from transformers import Qwen2Config +from transformers.cache_utils import DynamicCache +from transformers.models.qwen2.modeling_qwen2 import Qwen2Model + + +class FourierEncoder(nn.Module): + def __init__(self, dim: int, max_frequency: float = 100.0): + super().__init__() + if dim % 2: + raise ValueError("Fourier feature dimension must be even") + frequencies = torch.logspace(0, math.log10(max_frequency), steps=dim // 2) + self.register_buffer("frequencies", frequencies) + + @property + def output_dim(self) -> int: + return self.frequencies.numel() * 2 + + def forward(self, values: torch.Tensor) -> torch.Tensor: + angles = values[..., None].float() * self.frequencies * (2.0 * torch.pi) + return torch.cat([torch.sin(angles), torch.cos(angles)], dim=-1) * math.sqrt(2.0) + + +class ActionTimeProjection(nn.Module): + def __init__( + self, + action_dim: int, + output_dim: int, + hidden_size: int, + fourier_features: int, + ): + super().__init__() + self.action_encoders = nn.ModuleList( + [FourierEncoder(fourier_features) for _ in range(action_dim)] + ) + self.time_encoder = FourierEncoder(fourier_features) + input_dim = (action_dim + 1) * fourier_features + self.network = nn.Sequential( + nn.Linear(input_dim, hidden_size), + nn.SiLU(), + nn.LayerNorm(hidden_size), + nn.Linear(hidden_size, output_dim), + nn.LayerNorm(output_dim), + ) + + def forward(self, action: torch.Tensor, timestep: torch.Tensor) -> torch.Tensor: + batch_size, num_waypoints, _ = action.shape + action_features = torch.cat( + [encoder(action[..., index]) for index, encoder in enumerate(self.action_encoders)], + dim=-1, + ) + scalar_timestep = timestep.reshape(batch_size, -1)[:, 0] + time_features = self.time_encoder(scalar_timestep)[:, None, :] + time_features = time_features.expand(batch_size, num_waypoints, -1) + features = torch.cat([action_features, time_features], dim=-1) + return self.network(features.to(dtype=self.network[0].weight.dtype)) + + +@dataclass(frozen=True) +class PrefixKV: + keys: tuple[torch.Tensor, ...] + values: tuple[torch.Tensor, ...] + + @property + def num_layers(self) -> int: + return len(self.keys) + + @property + def sequence_length(self) -> int: + return int(self.keys[0].shape[-2]) + + @classmethod + def from_transformers_cache(cls, cache: object) -> "PrefixKV": + if hasattr(cache, "layers"): + keys = tuple(layer.keys.detach() for layer in cache.layers) + values = tuple(layer.values.detach() for layer in cache.layers) + else: + legacy = tuple(cache) + keys = tuple(item[0].detach() for item in legacy) + values = tuple(item[1].detach() for item in legacy) + return cls(keys=keys, values=values) + + def dynamic_copy(self) -> DynamicCache: + entries = [(key.clone(), value.clone()) for key, value in zip(self.keys, self.values)] + try: + return DynamicCache(ddp_cache_data=entries) + except TypeError: + cache = DynamicCache() + for layer_index, (key, value) in enumerate(entries): + cache.update(key, value, layer_index) + return cache + + +@dataclass(frozen=True) +class ExpertArchitecture: + hidden_size: int + intermediate_size: int + num_layers: int + num_attention_heads: int + num_key_value_heads: int + head_dim: int + action_projection_hidden_size: int + action_fourier_features: int + non_causal_attention: bool = True + + +class KVCachedDiffusionExpert(nn.Module): + def __init__( + self, + vlm_text_config: Qwen2Config, + architecture: ExpertArchitecture, + action_dim: int = 2, + ): + super().__init__() + if architecture.hidden_size != architecture.num_attention_heads * architecture.head_dim: + raise ValueError("expert hidden_size must equal num_attention_heads * head_dim") + if architecture.num_layers != vlm_text_config.num_hidden_layers: + raise ValueError("direct past-KV reuse requires the same number of layers") + if architecture.num_key_value_heads != vlm_text_config.num_key_value_heads: + raise ValueError("direct past-KV reuse requires the same number of KV heads") + vlm_head_dim = getattr( + vlm_text_config, + "head_dim", + vlm_text_config.hidden_size // vlm_text_config.num_attention_heads, + ) + if architecture.head_dim != vlm_head_dim: + raise ValueError("direct past-KV reuse requires the same attention head dimension") + + expert_config = copy.deepcopy(vlm_text_config) + expert_config.hidden_size = architecture.hidden_size + expert_config.intermediate_size = architecture.intermediate_size + expert_config.num_hidden_layers = architecture.num_layers + expert_config.num_attention_heads = architecture.num_attention_heads + expert_config.num_key_value_heads = architecture.num_key_value_heads + expert_config.head_dim = architecture.head_dim + expert_config.vocab_size = 1 + expert_config.tie_word_embeddings = False + expert_config.use_cache = True + expert_config._attn_implementation = "eager" + + self.architecture = architecture + self.expert = Qwen2Model(expert_config) + # Inputs are always continuous action embeddings, exactly as in Alpamayo. + self.expert.embed_tokens = None + self.action_projection = ActionTimeProjection( + action_dim=action_dim, + output_dim=architecture.hidden_size, + hidden_size=architecture.action_projection_hidden_size, + fourier_features=architecture.action_fourier_features, + ) + self.action_output = nn.Linear(architecture.hidden_size, action_dim) + + def validate_prefix(self, prefix: PrefixKV) -> None: + if prefix.num_layers != self.architecture.num_layers: + raise ValueError( + f"KV layer mismatch: prefix={prefix.num_layers}, expert={self.architecture.num_layers}" + ) + first_key = prefix.keys[0] + if first_key.shape[-3] != self.architecture.num_key_value_heads: + raise ValueError( + f"KV-head mismatch: prefix={first_key.shape[-3]}, " + f"expert={self.architecture.num_key_value_heads}" + ) + if first_key.shape[-1] != self.architecture.head_dim: + raise ValueError( + f"head-dim mismatch: prefix={first_key.shape[-1]}, " + f"expert={self.architecture.head_dim}" + ) + + def forward( + self, + noisy_action: torch.Tensor, + timestep: torch.Tensor, + prefix: PrefixKV, + ) -> torch.Tensor: + self.validate_prefix(prefix) + batch_size, num_waypoints, _ = noisy_action.shape + if prefix.keys[0].shape[0] != batch_size: + raise ValueError("Prefix KV batch size does not match action batch size") + + action_embeddings = self.action_projection(noisy_action, timestep) + prefix_length = prefix.sequence_length + position_ids = torch.arange( + prefix_length, + prefix_length + num_waypoints, + device=noisy_action.device, + )[None, :].expand(batch_size, -1) + if self.architecture.non_causal_attention: + # A prepared 4-D additive mask is returned unchanged by Transformers' + # mask builder. This lets all diffusion tokens attend to one another, + # while still giving every query access to the frozen VLM prefix KV. + attention_mask = torch.zeros( + batch_size, + 1, + num_waypoints, + prefix_length + num_waypoints, + dtype=action_embeddings.dtype, + device=noisy_action.device, + ) + else: + attention_mask = torch.ones( + batch_size, + prefix_length + num_waypoints, + dtype=torch.long, + device=noisy_action.device, + ) + cache_position = torch.arange( + prefix_length, + prefix_length + num_waypoints, + device=noisy_action.device, + ) + + kwargs: dict[str, object] = {} + if self.architecture.non_causal_attention: + kwargs["is_causal"] = False + outputs = self.expert( + inputs_embeds=action_embeddings, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=prefix.dynamic_copy(), + cache_position=cache_position, + use_cache=False, + **kwargs, + ) + hidden = outputs.last_hidden_state[:, -num_waypoints:, :] + return self.action_output(hidden) diff --git a/src/alpamayo_lite/prompts.py b/src/alpamayo_lite/prompts.py new file mode 100644 index 0000000..53608df --- /dev/null +++ b/src/alpamayo_lite/prompts.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +import re + +import torch + + +def format_history(history_xyz: torch.Tensor) -> str: + history = history_xyz.detach().cpu().float() + return "; ".join( + f"t-{history.shape[-2] - 1 - index}:({point[0]:.2f},{point[1]:.2f})" + for index, point in enumerate(history) + ) + + +def clean_reasoning_text(text: str, max_characters: int = 320) -> str: + cleaned = re.sub(r"\s+", " ", text).strip() + if not cleaned: + return "Continue safely while following the visible lane." + return cleaned[:max_characters].rstrip() + + +def build_vlm_prefix(history_xyz: torch.Tensor) -> str: + history_text = format_history(history_xyz) + return ( + "<|im_start|>system\n" + "You are a driving vision-language model. Describe the immediate driving decision " + "briefly and concretely.<|im_end|>\n" + "<|im_start|>user\n" + "\n" + f"Recent ego positions in the current ego-relative XY frame: {history_text}.\n" + "What should the vehicle do next based on the scene?<|im_end|>\n" + "<|im_start|>assistant\n" + ) + + +def build_stage1_text(history_xyz: torch.Tensor, reasoning: str) -> tuple[str, str]: + prefix = build_vlm_prefix(history_xyz) + target = clean_reasoning_text(reasoning) + "<|im_end|>" + return prefix, prefix + target + + +def build_expert_prefix(history_xyz: torch.Tensor, reasoning: str) -> str: + return build_vlm_prefix(history_xyz) + clean_reasoning_text(reasoning) + "\nContinuous action:" diff --git a/src/alpamayo_lite/runtime.py b/src/alpamayo_lite/runtime.py new file mode 100644 index 0000000..7b6a768 --- /dev/null +++ b/src/alpamayo_lite/runtime.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +import json +import random +from pathlib import Path +from typing import Any + +import numpy as np +import torch + + +def seed_everything(seed: int) -> None: + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + if torch.backends.mps.is_available(): + torch.mps.manual_seed(seed) + + +def select_device() -> torch.device: + if torch.backends.mps.is_available(): + return torch.device("mps") + if torch.cuda.is_available(): + return torch.device("cuda") + return torch.device("cpu") + + +def resolve_dtype(name: str, device: torch.device) -> torch.dtype: + mapping = { + "float32": torch.float32, + "float16": torch.float16, + "bfloat16": torch.bfloat16, + } + if name not in mapping: + raise ValueError(f"Unsupported dtype: {name}") + dtype = mapping[name] + if device.type == "cpu" and dtype == torch.float16: + return torch.float32 + return dtype + + +def parameter_counts(module: torch.nn.Module) -> dict[str, int]: + total = sum(parameter.numel() for parameter in module.parameters()) + trainable = sum( + parameter.numel() for parameter in module.parameters() if parameter.requires_grad + ) + return {"total": total, "trainable": trainable, "frozen": total - trainable} + + +def write_json(path: str | Path, value: Any) -> None: + output = Path(path) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(json.dumps(value, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") + + +def empty_device_cache(device: torch.device) -> None: + if device.type == "mps": + torch.mps.empty_cache() + elif device.type == "cuda": + torch.cuda.empty_cache() diff --git a/src/alpamayo_lite/trajectory_tokens.py b/src/alpamayo_lite/trajectory_tokens.py new file mode 100644 index 0000000..103ded0 --- /dev/null +++ b/src/alpamayo_lite/trajectory_tokens.py @@ -0,0 +1,179 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +import torch +from transformers import LogitsProcessor, StoppingCriteria + + +STRUCTURED_SPECIAL_TOKENS = ( + "<|cot_start|>", + "<|cot_end|>", + "<|meta_action_start|>", + "<|meta_action_end|>", + "<|traj_future_start|>", + "<|traj_future_end|>", +) + + +@dataclass(frozen=True) +class TrajectoryVocabulary: + size: int + start_id: int + end_id: int + special_token_ids: dict[str, int] + + def token_id(self, value: int) -> int: + if not 0 <= value < self.size: + raise ValueError(f"Trajectory token value is outside 0..{self.size - 1}: {value}") + return self.start_id + value + + +def trajectory_token(value: int) -> str: + return f"" + + +def add_trajectory_vocabulary(tokenizer: Any, size: int) -> TrajectoryVocabulary: + if size < 2: + raise ValueError("Trajectory vocabulary must contain at least two bins") + discrete_tokens = [trajectory_token(value) for value in range(size)] + existing = set(tokenizer.get_vocab()) + collisions = [token for token in (*discrete_tokens, *STRUCTURED_SPECIAL_TOKENS) if token in existing] + if collisions: + raise ValueError(f"Trajectory tokens already exist in tokenizer: {collisions[:4]}") + + added_discrete = tokenizer.add_tokens(discrete_tokens) + if added_discrete != size: + raise RuntimeError(f"Expected to add {size} trajectory tokens, added {added_discrete}") + tokenizer.add_tokens(list(STRUCTURED_SPECIAL_TOKENS), special_tokens=True) + + discrete_ids = tokenizer.convert_tokens_to_ids(discrete_tokens) + start_id = int(discrete_ids[0]) + expected_ids = list(range(start_id, start_id + size)) + if list(discrete_ids) != expected_ids: + raise RuntimeError("Trajectory token IDs are not contiguous") + special_token_ids = { + token: int(tokenizer.convert_tokens_to_ids(token)) for token in STRUCTURED_SPECIAL_TOKENS + } + return TrajectoryVocabulary( + size=size, + start_id=start_id, + end_id=start_id + size - 1, + special_token_ids=special_token_ids, + ) + + +def quantize_action( + action: torch.Tensor, + size: int, + minimum: float, + maximum: float, +) -> torch.LongTensor: + if maximum <= minimum: + raise ValueError("maximum must be greater than minimum") + normalized = (action.float().clamp(minimum, maximum) - minimum) / (maximum - minimum) + return (normalized * (size - 1)).round().long() + + +def dequantize_action( + tokens: torch.Tensor, + size: int, + minimum: float, + maximum: float, +) -> torch.Tensor: + if maximum <= minimum: + raise ValueError("maximum must be greater than minimum") + if torch.any(tokens < 0) or torch.any(tokens >= size): + raise ValueError("Trajectory token values are outside the configured vocabulary") + return tokens.float() / (size - 1) * (maximum - minimum) + minimum + + +def build_structured_target( + reasoning: str, + meta_action: str, + token_values: torch.Tensor, +) -> str: + flattened = token_values.detach().cpu().reshape(-1).tolist() + trajectory = "".join(trajectory_token(int(value)) for value in flattened) + return ( + f"<|cot_start|>{reasoning}<|cot_end|>\n" + f"<|meta_action_start|>{meta_action}<|meta_action_end|>\n" + f"<|traj_future_start|>{trajectory}<|traj_future_end|><|im_end|>" + ) + + +def extract_trajectory_values( + token_ids: torch.Tensor, + vocabulary: TrajectoryVocabulary, +) -> torch.LongTensor: + sequence = token_ids.detach().reshape(-1) + start_token_id = vocabulary.special_token_ids["<|traj_future_start|>"] + end_token_id = vocabulary.special_token_ids["<|traj_future_end|>"] + start_positions = torch.nonzero(sequence == start_token_id, as_tuple=False).flatten() + if start_positions.numel() == 0: + raise ValueError("Generated sequence does not contain <|traj_future_start|>") + start = int(start_positions[-1]) + 1 + end_positions = torch.nonzero(sequence[start:] == end_token_id, as_tuple=False).flatten() + if end_positions.numel() == 0: + raise ValueError("Generated sequence does not contain <|traj_future_end|>") + end = start + int(end_positions[0]) + values = sequence[start:end] - vocabulary.start_id + if torch.any(values < 0) or torch.any(values >= vocabulary.size): + raise ValueError("Generated trajectory block contains non-trajectory tokens") + return values.long() + + +class DiscreteTrajectoryMask(LogitsProcessor): + """Source-style mask that prevents the VLM from sampling discrete trajectory tokens.""" + + def __init__(self, start_id: int, size: int): + self.start_id = start_id + self.size = size + + def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor: + del input_ids + scores[:, self.start_id : self.start_id + self.size] = float("-inf") + return scores + + +class StopOneTokenAfterBoundary(StoppingCriteria): + """Match Alpamayo's handoff behavior so generation updates the cache once after the marker.""" + + def __init__(self, boundary_token_id: int): + self.boundary_token_id = boundary_token_id + self.boundary_found: torch.BoolTensor | None = None + + def __call__( + self, + input_ids: torch.LongTensor, + scores: torch.FloatTensor, + **kwargs: Any, + ) -> torch.BoolTensor: + del scores, kwargs + if self.boundary_found is None: + self.boundary_found = torch.zeros( + input_ids.shape[0], dtype=torch.bool, device=input_ids.device + ) + if self.boundary_found.all(): + return torch.ones_like(self.boundary_found) + self.boundary_found |= input_ids[:, -1] == self.boundary_token_id + return torch.zeros_like(self.boundary_found) + + +def mask_after_boundary( + token_ids: torch.Tensor, + boundary_token_id: int, + pad_token_id: int, +) -> torch.Tensor: + result = token_ids.clone() + boundary_mask = result == boundary_token_id + sequence_length = result.shape[-1] + positions = torch.arange(sequence_length, device=result.device).expand_as(result) + first_boundary = torch.where( + boundary_mask, + positions, + torch.full_like(positions, sequence_length), + ).min(dim=-1, keepdim=True).values + result[positions > first_boundary] = pad_token_id + return result diff --git a/src/alpamayo_lite/vlm.py b/src/alpamayo_lite/vlm.py new file mode 100644 index 0000000..2a8a16b --- /dev/null +++ b/src/alpamayo_lite/vlm.py @@ -0,0 +1,181 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Any + +import torch +from PIL import Image +from transformers import AutoProcessor, LlavaForConditionalGeneration + +from .config import Stage1Config, VLMConfig +from .expert import PrefixKV +from .runtime import parameter_counts + + +def load_vlm( + model_path: str | Path, + config: VLMConfig, + device: torch.device, + dtype: torch.dtype, +) -> tuple[Any, LlavaForConditionalGeneration]: + processor = AutoProcessor.from_pretrained(model_path, use_fast=False) + # The selected third-party checkpoint was saved with this value set to 0, + # although its CLIP tower has one CLS token. LLaVA's "default" feature + # selection removes CLS after counting it, so the processor must start at 1 + # to expand into the same 576 tokens produced by the vision tower. + processor.num_additional_image_tokens = config.num_additional_image_tokens + model = LlavaForConditionalGeneration.from_pretrained( + model_path, + dtype=dtype, + attn_implementation=config.attn_implementation, + low_cpu_mem_usage=True, + ) + model.to(device) + return processor, model + + +def _core_modules(model: LlavaForConditionalGeneration) -> tuple[Any, Any, Any, Any]: + core = model.model + vision_tower = core.vision_tower + projector = core.multi_modal_projector + language_model = core.language_model + language_backbone = language_model + return vision_tower, projector, language_model, language_backbone + + +def configure_stage1_trainability( + model: LlavaForConditionalGeneration, config: Stage1Config +) -> dict[str, Any]: + model.requires_grad_(False) + vision_tower, projector, language_model, backbone = _core_modules(model) + layers = backbone.layers + train_last = config.train_last_n_language_layers + if not (1 <= train_last <= len(layers)): + raise ValueError(f"train_last_n_language_layers must be within 1..{len(layers)}") + first_trainable = len(layers) - train_last + for layer in layers[first_trainable:]: + layer.requires_grad_(True) + if config.train_final_norm: + backbone.norm.requires_grad_(True) + if config.train_lm_head: + model.lm_head.requires_grad_(True) + + if config.freeze_vision_tower: + vision_tower.requires_grad_(False) + if config.freeze_multimodal_projector: + projector.requires_grad_(False) + + if config.gradient_checkpointing: + model.gradient_checkpointing_enable(gradient_checkpointing_kwargs={"use_reentrant": False}) + model.config.use_cache = False + language_model.config.use_cache = False + + counts = parameter_counts(model) + counts.update( + { + "language_layers_total": len(layers), + "language_layers_trainable": train_last, + "first_trainable_language_layer": first_trainable, + "vision_trainable": any(p.requires_grad for p in vision_tower.parameters()), + "projector_trainable": any(p.requires_grad for p in projector.parameters()), + "lm_head_trainable": any(p.requires_grad for p in model.lm_head.parameters()), + } + ) + return counts + + +def enforce_frozen_module_eval(model: LlavaForConditionalGeneration) -> None: + vision_tower, projector, _, _ = _core_modules(model) + vision_tower.eval() + projector.eval() + + +def _move_inputs( + inputs: dict[str, torch.Tensor], device: torch.device, dtype: torch.dtype +) -> dict[str, torch.Tensor]: + moved: dict[str, torch.Tensor] = {} + for key, value in inputs.items(): + if not isinstance(value, torch.Tensor): + moved[key] = value + elif value.is_floating_point(): + moved[key] = value.to(device=device, dtype=dtype) + else: + moved[key] = value.to(device=device) + return moved + + +def process_image_text( + processor: Any, + image: Image.Image, + text: str, + device: torch.device, + dtype: torch.dtype, +) -> dict[str, torch.Tensor]: + encoded = processor(text=text, images=image, return_tensors="pt") + return _move_inputs(dict(encoded), device=device, dtype=dtype) + + +def prepare_stage1_batch( + processor: Any, + image: Image.Image, + prefix: str, + full_text: str, + device: torch.device, + dtype: torch.dtype, +) -> tuple[dict[str, torch.Tensor], torch.Tensor, int]: + prefix_inputs = process_image_text(processor, image, prefix, device, dtype) + full_inputs = process_image_text(processor, image, full_text, device, dtype) + prefix_length = prefix_inputs["input_ids"].shape[1] + labels = full_inputs["input_ids"].clone() + labels[:, :prefix_length] = -100 + if "attention_mask" in full_inputs: + labels[full_inputs["attention_mask"] == 0] = -100 + return full_inputs, labels, prefix_length + + +@torch.no_grad() +def generate_reasoning( + model: LlavaForConditionalGeneration, + processor: Any, + image: Image.Image, + prefix: str, + device: torch.device, + dtype: torch.dtype, + max_new_tokens: int, +) -> str: + model.eval() + inputs = process_image_text(processor, image, prefix, device, dtype) + prompt_length = inputs["input_ids"].shape[1] + generated = model.generate( + **inputs, + max_new_tokens=max_new_tokens, + do_sample=False, + use_cache=True, + pad_token_id=processor.tokenizer.pad_token_id, + eos_token_id=processor.tokenizer.eos_token_id, + ) + new_tokens = generated[:, prompt_length:] + return processor.batch_decode(new_tokens, skip_special_tokens=True)[0].strip() + + +@torch.no_grad() +def extract_prefix_kv( + model: LlavaForConditionalGeneration, + processor: Any, + image: Image.Image, + text: str, + device: torch.device, + dtype: torch.dtype, +) -> PrefixKV: + model.eval() + model.config.use_cache = True + model.model.language_model.config.use_cache = True + inputs = process_image_text(processor, image, text, device, dtype) + outputs = model(**inputs, use_cache=True, return_dict=True) + if outputs.past_key_values is None: + raise RuntimeError("VLM did not return past_key_values") + return PrefixKV.from_transformers_cache(outputs.past_key_values) + + +def vlm_text_config(model: LlavaForConditionalGeneration) -> Any: + return model.config.text_config diff --git a/tests/test_action_space.py b/tests/test_action_space.py new file mode 100644 index 0000000..3b9241b --- /dev/null +++ b/tests/test_action_space.py @@ -0,0 +1,13 @@ +import torch + +from alpamayo_lite.action_space import RelativeDeltaActionSpace + + +def test_action_round_trip() -> None: + history = torch.tensor([[[-1.0, 0.0], [0.0, 0.0]]]) + future = torch.tensor([[[1.0, 0.2], [2.1, 0.5], [3.2, 0.9]]]) + statistics = RelativeDeltaActionSpace.fit(history, future) + action_space = RelativeDeltaActionSpace(statistics.mean, statistics.scale) + action = action_space.trajectory_to_action(history, future) + reconstructed = action_space.action_to_trajectory(action, history) + torch.testing.assert_close(reconstructed, future) diff --git a/tests/test_complete_comparison.py b/tests/test_complete_comparison.py new file mode 100644 index 0000000..772b575 --- /dev/null +++ b/tests/test_complete_comparison.py @@ -0,0 +1,46 @@ +import runpy +from pathlib import Path + +import numpy as np +import pytest + +SCRIPT = runpy.run_path( + str(Path(__file__).resolve().parents[1] / "scripts" / "build_complete_comparison.py") +) +_decode_action = SCRIPT["_decode_action"] +_extract_component = SCRIPT["_extract_component"] +_prediction_metrics = SCRIPT["_prediction_metrics"] + + +def test_decode_action_uses_statistics_and_nonzero_history_anchor() -> None: + history_xy = np.array([[8.0, -4.0], [10.0, -3.0]]) + action = np.array([[1.0, -1.0], [0.0, 2.0]]) + decoded = _decode_action( + action, + history_xy, + mean=np.array([0.5, 0.25]), + scale=np.array([2.0, 0.5]), + ) + np.testing.assert_allclose(decoded, np.array([[12.5, -3.25], [13.0, -2.0]])) + + +def test_extract_component_requires_one_ordered_nonempty_pair() -> None: + text = "<|cot_start|>keep left<|cot_end|>" + assert _extract_component(text, "cot") == "keep left" + with pytest.raises(ValueError): + _extract_component(text + text, "cot") + with pytest.raises(ValueError): + _extract_component("<|cot_start|><|cot_end|>", "cot") + + +def test_prediction_metrics_use_normalized_action_and_euclidean_xy() -> None: + target_action = np.zeros((2, 2)) + predicted_action = np.ones((2, 2)) + target_xy = np.zeros((2, 2)) + predicted_xy = np.array([[3.0, 4.0], [0.0, 0.0]]) + assert _prediction_metrics(predicted_action, predicted_xy, target_action, target_xy) == { + "action_mse": 1.0, + "ade_m": 2.5, + "fde_m": 0.0, + "max_error_m": 5.0, + } diff --git a/tests/test_diffusion.py b/tests/test_diffusion.py new file mode 100644 index 0000000..00a3b6a --- /dev/null +++ b/tests/test_diffusion.py @@ -0,0 +1,11 @@ +import torch + +from alpamayo_lite.diffusion import FlowMatching + + +def test_flow_matching_target_velocity() -> None: + flow = FlowMatching((4, 2), inference_steps=2) + target = torch.ones(1, 4, 2) + data = flow.construct_training_data(target) + exact_velocity = data["target_action"] - data["noise"] + assert float(flow.loss(data, exact_velocity)) == 0.0 diff --git a/tests/test_expert_contract.py b/tests/test_expert_contract.py new file mode 100644 index 0000000..84e1470 --- /dev/null +++ b/tests/test_expert_contract.py @@ -0,0 +1,69 @@ +import torch +from transformers import Qwen2Config + +from alpamayo_lite.expert import ExpertArchitecture, KVCachedDiffusionExpert, PrefixKV + + +def tiny_expert(non_causal_attention: bool = False) -> KVCachedDiffusionExpert: + vlm_config = Qwen2Config( + vocab_size=32, + hidden_size=64, + intermediate_size=128, + num_hidden_layers=2, + num_attention_heads=4, + num_key_value_heads=2, + max_position_embeddings=128, + ) + architecture = ExpertArchitecture( + hidden_size=32, + intermediate_size=64, + num_layers=2, + num_attention_heads=2, + num_key_value_heads=2, + head_dim=16, + action_projection_hidden_size=16, + action_fourier_features=4, + non_causal_attention=non_causal_attention, + ) + return KVCachedDiffusionExpert(vlm_config, architecture) + + +def test_expert_consumes_compatible_past_kv() -> None: + expert = tiny_expert() + prefix = PrefixKV( + keys=tuple(torch.randn(1, 2, 5, 16) for _ in range(2)), + values=tuple(torch.randn(1, 2, 5, 16) for _ in range(2)), + ) + output = expert(torch.randn(1, 3, 2), torch.rand(1, 1, 1), prefix) + assert output.shape == (1, 3, 2) + + +def test_expert_rejects_incompatible_kv_heads() -> None: + expert = tiny_expert() + prefix = PrefixKV( + keys=tuple(torch.randn(1, 1, 5, 16) for _ in range(2)), + values=tuple(torch.randn(1, 1, 5, 16) for _ in range(2)), + ) + try: + expert.validate_prefix(prefix) + except ValueError as error: + assert "KV-head mismatch" in str(error) + else: + raise AssertionError("Expected incompatible KV heads to be rejected") + + +def test_non_causal_expert_allows_future_action_attention() -> None: + torch.manual_seed(3) + expert = tiny_expert(non_causal_attention=True).eval() + prefix = PrefixKV( + keys=tuple(torch.randn(1, 2, 5, 16) for _ in range(2)), + values=tuple(torch.randn(1, 2, 5, 16) for _ in range(2)), + ) + first = torch.randn(1, 3, 2) + changed_future = first.clone() + changed_future[:, -1] += 0.137 + timestep = torch.rand(1, 1, 1) + with torch.no_grad(): + first_output = expert(first, timestep, prefix) + second_output = expert(changed_future, timestep, prefix) + assert not torch.allclose(first_output[:, 0], second_output[:, 0]) diff --git a/tests/test_trajectory_tokens.py b/tests/test_trajectory_tokens.py new file mode 100644 index 0000000..05d4791 --- /dev/null +++ b/tests/test_trajectory_tokens.py @@ -0,0 +1,39 @@ +import torch + +from alpamayo_lite.trajectory_tokens import ( + DiscreteTrajectoryMask, + TrajectoryVocabulary, + dequantize_action, + extract_trajectory_values, + quantize_action, +) + + +def test_quantized_action_round_trip_is_bounded() -> None: + action = torch.tensor([[-4.0, -1.25], [0.0, 1.5], [3.9, 4.0]]) + tokens = quantize_action(action, size=128, minimum=-4.0, maximum=4.0) + reconstructed = dequantize_action(tokens, size=128, minimum=-4.0, maximum=4.0) + assert float((reconstructed - action).abs().max()) <= 8.0 / 127.0 + + +def test_extract_trajectory_values_between_markers() -> None: + vocabulary = TrajectoryVocabulary( + size=8, + start_id=100, + end_id=107, + special_token_ids={"<|traj_future_start|>": 200, "<|traj_future_end|>": 201}, + ) + sequence = torch.tensor([1, 200, 103, 107, 100, 201, 2]) + torch.testing.assert_close( + extract_trajectory_values(sequence, vocabulary), torch.tensor([3, 7, 0]) + ) + + +def test_discrete_trajectory_mask_changes_only_trajectory_range() -> None: + input_ids = torch.tensor([[1, 2]]) + scores = torch.randn(1, 16) + original = scores.clone() + masked = DiscreteTrajectoryMask(start_id=5, size=4)(input_ids, scores) + assert torch.isneginf(masked[:, 5:9]).all() + torch.testing.assert_close(masked[:, :5], original[:, :5]) + torch.testing.assert_close(masked[:, 9:], original[:, 9:])