Kuavo original action: multi_task_dit checkpoint 10K
Browse files- v2/multi_task_dit/checkpoints/checkpoint-10K/README.md +7 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/checkpoint_manifest.json +302 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/config.json +110 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/model.safetensors +3 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_postprocessor.json +32 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_postprocessor_step_0_unnormalizer_processor.safetensors +3 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_preprocessor.json +83 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_preprocessor_step_4_normalizer_processor.safetensors +3 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/train_config.json +249 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/DATA_CHANGES.md +7 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/dataset.yaml +31 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/prepare.py +8 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/verify.py +8 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/CONTRACT.md +19 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/compat.py +4 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/gr00t_n15.yaml +55 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/gr00t_n17.yaml +57 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/hf_watcher.py +7 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/launch.sh +18 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/load_n17.py +4 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/loss_mask.py +4 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/multi_task_dit.yaml +33 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/native_assets.py +4 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/orchestrate.py +7 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/run.py +7 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/run_n17.py +7 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/total/gr00t_n15_total.yaml +104 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/total/multi_task_dit_total.yaml +118 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/__init__.py +1 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/__init__.py +1 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/action_validity.py +98 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/groot_v3.py +86 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/language.py +56 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/__init__.py +1 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/prepare.py +18 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/validate.py +67 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/view.py +166 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/tests/test_contract.py +65 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/__init__.py +1 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/checkpoints.py +35 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/hf_watcher.py +363 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/language_guard.py +13 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/load_n17.py +20 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/native_assets.py +48 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/orchestrate.py +92 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/run_lerobot.py +312 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/run_n17.py +170 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/start_watchers.py +26 -0
- v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/validate_checkpoint.py +95 -0
v2/multi_task_dit/checkpoints/checkpoint-10K/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# multi_task_dit: original-action Kuavo, checkpoint 10000 of 30000 updates
|
| 2 |
+
|
| 3 |
+
Original v3 data; 30Hz to 10Hz logical view; state20/action23; seed42; actual batch128; accumulation1.
|
| 4 |
+
Torso targets are the original action commands. Invalid action elements are masked.
|
| 5 |
+
N1.5/DiT use LeTools-pinned LeRobot; N1.7 uses bundled native model, processor, sharded loader and Trainer.
|
| 6 |
+
LeTools revision: 6d2002613241ae2a32c5d8fd753f80caa1383fba. See provenance for adapter and YAML.
|
| 7 |
+
N1.7 uses native checkpoint layout. Optimizer/RNG states stay on the training server.
|
v2/multi_task_dit/checkpoints/checkpoint-10K/checkpoint_manifest.json
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema": 2,
|
| 3 |
+
"policy": "multi_task_dit",
|
| 4 |
+
"step": 10000,
|
| 5 |
+
"repo_id": "RwH-Lab/Kuavo_ckpt",
|
| 6 |
+
"remote_prefix": "v2/multi_task_dit/checkpoints/checkpoint-10K",
|
| 7 |
+
"source_checkpoint": "/home/work/mntvol/RwH-Kuavo_ckpt/v3_rawaction/multi_task_dit/checkpoints/010000",
|
| 8 |
+
"source_signature": {
|
| 9 |
+
"pretrained_model/config.json": [
|
| 10 |
+
2964,
|
| 11 |
+
1793104970876621208
|
| 12 |
+
],
|
| 13 |
+
"pretrained_model/model.safetensors": [
|
| 14 |
+
1110874532,
|
| 15 |
+
1793104972611445238
|
| 16 |
+
],
|
| 17 |
+
"pretrained_model/policy_postprocessor.json": [
|
| 18 |
+
659,
|
| 19 |
+
1793104972670128375
|
| 20 |
+
],
|
| 21 |
+
"pretrained_model/policy_postprocessor_step_0_unnormalizer_processor.safetensors": [
|
| 22 |
+
2860,
|
| 23 |
+
1793104934154675628
|
| 24 |
+
],
|
| 25 |
+
"pretrained_model/policy_preprocessor.json": [
|
| 26 |
+
1896,
|
| 27 |
+
1793104939229702410
|
| 28 |
+
],
|
| 29 |
+
"pretrained_model/policy_preprocessor_step_4_normalizer_processor.safetensors": [
|
| 30 |
+
2860,
|
| 31 |
+
1793104939226303274
|
| 32 |
+
],
|
| 33 |
+
"pretrained_model/train_config.json": [
|
| 34 |
+
7322,
|
| 35 |
+
1793104939222364217
|
| 36 |
+
],
|
| 37 |
+
"training_state/optimizer_param_groups.json": [
|
| 38 |
+
5342,
|
| 39 |
+
1793104936859016090
|
| 40 |
+
],
|
| 41 |
+
"training_state/optimizer_state.safetensors": [
|
| 42 |
+
1716334020,
|
| 43 |
+
1793104977322473307
|
| 44 |
+
],
|
| 45 |
+
"training_state/rng_state.safetensors": [
|
| 46 |
+
15708,
|
| 47 |
+
1793104974693417186
|
| 48 |
+
],
|
| 49 |
+
"training_state/scheduler_state.json": [
|
| 50 |
+
333,
|
| 51 |
+
1793104977390581314
|
| 52 |
+
],
|
| 53 |
+
"training_state/training_step.json": [
|
| 54 |
+
21,
|
| 55 |
+
1793104974690185169
|
| 56 |
+
]
|
| 57 |
+
},
|
| 58 |
+
"tensor_count": 505,
|
| 59 |
+
"training_state_included": false,
|
| 60 |
+
"files": {
|
| 61 |
+
"README.md": {
|
| 62 |
+
"size": 554,
|
| 63 |
+
"sha256": "6abb178b18332426d2530aff35e55a94bb088ea56fa074414e43741ea5afc520",
|
| 64 |
+
"git_blob_sha1": "9ce80cd130087c6817b6f3ff8ba31c25fe641d40"
|
| 65 |
+
},
|
| 66 |
+
"pretrained_model/config.json": {
|
| 67 |
+
"size": 2964,
|
| 68 |
+
"sha256": "c034fc26d286314eb58b28af9c4cb4f6bb6ffb0983ac642fd50a36a3bcdd50a3",
|
| 69 |
+
"git_blob_sha1": "78836fe1300e29eb2be5395290772c72a62d7469"
|
| 70 |
+
},
|
| 71 |
+
"pretrained_model/model.safetensors": {
|
| 72 |
+
"size": 1110874532,
|
| 73 |
+
"sha256": "08faf5e4134d7620e237f894aa02d2c102b6870adb4c01f8e575b2ca27a03ef0",
|
| 74 |
+
"git_blob_sha1": "8fc556098dd6ce42e6cc5e73017539bc2a8b85b2"
|
| 75 |
+
},
|
| 76 |
+
"pretrained_model/policy_postprocessor.json": {
|
| 77 |
+
"size": 659,
|
| 78 |
+
"sha256": "f953e66e6857340ed76934257dcf9e00b18a12a865b0f4f2b332d1a285839dad",
|
| 79 |
+
"git_blob_sha1": "4c44db41a739258cef9f98302259f9b0d15a501f"
|
| 80 |
+
},
|
| 81 |
+
"pretrained_model/policy_postprocessor_step_0_unnormalizer_processor.safetensors": {
|
| 82 |
+
"size": 2860,
|
| 83 |
+
"sha256": "e1e9ad65b2902a52587822fa5121e86fadf0c3a2469c4a014f34707b416b1bbd",
|
| 84 |
+
"git_blob_sha1": "b9afb173e9a4ee05a75acd072865d6ceef64da8f"
|
| 85 |
+
},
|
| 86 |
+
"pretrained_model/policy_preprocessor.json": {
|
| 87 |
+
"size": 1896,
|
| 88 |
+
"sha256": "30892a46fe786931cc7b131d7c3b55e227f0284f201b6ee1ce95034cb93ce725",
|
| 89 |
+
"git_blob_sha1": "0a5c7c55adc0ef1bef64d8697a261730e4dfc7de"
|
| 90 |
+
},
|
| 91 |
+
"pretrained_model/policy_preprocessor_step_4_normalizer_processor.safetensors": {
|
| 92 |
+
"size": 2860,
|
| 93 |
+
"sha256": "e1e9ad65b2902a52587822fa5121e86fadf0c3a2469c4a014f34707b416b1bbd",
|
| 94 |
+
"git_blob_sha1": "b9afb173e9a4ee05a75acd072865d6ceef64da8f"
|
| 95 |
+
},
|
| 96 |
+
"pretrained_model/train_config.json": {
|
| 97 |
+
"size": 7322,
|
| 98 |
+
"sha256": "182a15faf8d65a918ac2481eb1ffebac7a0c1b45f0da76393fd2fb5c27908f2a",
|
| 99 |
+
"git_blob_sha1": "e7d64337a1286dddae7e0b8b518a20a80ca0daca"
|
| 100 |
+
},
|
| 101 |
+
"provenance/configs/Bs128_30k/data/DATA_CHANGES.md": {
|
| 102 |
+
"size": 509,
|
| 103 |
+
"sha256": "62b56e19aca04147363650a7ca23cf6bff3b9f9e780978cbc7b9ad58fb5782ea",
|
| 104 |
+
"git_blob_sha1": "09a3c536857a6ef41d7ca8af865fdf1067097232"
|
| 105 |
+
},
|
| 106 |
+
"provenance/configs/Bs128_30k/data/dataset.yaml": {
|
| 107 |
+
"size": 592,
|
| 108 |
+
"sha256": "a95789a682ec84e2e5b596b2d0c5da8a11ad3abca6d1c39ce6dd9b3942203bc6",
|
| 109 |
+
"git_blob_sha1": "24953451806778143fa67a9308fdbff31ac7b51c"
|
| 110 |
+
},
|
| 111 |
+
"provenance/configs/Bs128_30k/data/prepare.py": {
|
| 112 |
+
"size": 361,
|
| 113 |
+
"sha256": "06cef117826af42d15421c0a2c709120f00c181f38f2873f7dc5a0bc17d447de",
|
| 114 |
+
"git_blob_sha1": "176bdbae5d88c8dca293ec71161f657b5c97c237"
|
| 115 |
+
},
|
| 116 |
+
"provenance/configs/Bs128_30k/data/verify.py": {
|
| 117 |
+
"size": 365,
|
| 118 |
+
"sha256": "4df3c512a6c0d810c6d11f6e7fdb99b46407adfe43fdea94939b2bfdf5077a80",
|
| 119 |
+
"git_blob_sha1": "60f2f83e6a01bb0d9274c2d4e49054401dc7d0c3"
|
| 120 |
+
},
|
| 121 |
+
"provenance/configs/Bs128_30k/train/CONTRACT.md": {
|
| 122 |
+
"size": 1481,
|
| 123 |
+
"sha256": "6a6793237591e0ffc8178266b483308fe53e7c5a7f206626bb284a2f6cf9772a",
|
| 124 |
+
"git_blob_sha1": "630bc7d682901a222bb7f7ec09dc0b66541e6db7"
|
| 125 |
+
},
|
| 126 |
+
"provenance/configs/Bs128_30k/train/compat.py": {
|
| 127 |
+
"size": 136,
|
| 128 |
+
"sha256": "ab948478240b23d037e7623711bf3cc0053db72f43ed9af218a04412cecde6af",
|
| 129 |
+
"git_blob_sha1": "55b0ae6c9979f0d329194a99129e248cabc6ea30"
|
| 130 |
+
},
|
| 131 |
+
"provenance/configs/Bs128_30k/train/gr00t_n15.yaml": {
|
| 132 |
+
"size": 1144,
|
| 133 |
+
"sha256": "04686afff2f338dfb2067e7669a8675fd0cc354a1a2f9f7e22d794fadd4d81b5",
|
| 134 |
+
"git_blob_sha1": "f3e3cea3aee9e2532941516482b7c96a3ab3ad2a"
|
| 135 |
+
},
|
| 136 |
+
"provenance/configs/Bs128_30k/train/gr00t_n17.yaml": {
|
| 137 |
+
"size": 1382,
|
| 138 |
+
"sha256": "2620d1aa6e5328f2f6e870ba9179997c5567acb2c31b69f786dc8861551867d8",
|
| 139 |
+
"git_blob_sha1": "58529a1277c2ac7766ea775b80bc38052a1ac79a"
|
| 140 |
+
},
|
| 141 |
+
"provenance/configs/Bs128_30k/train/hf_watcher.py": {
|
| 142 |
+
"size": 346,
|
| 143 |
+
"sha256": "aef5d741bf30a567341651680ec0c89919e3be3ef67b373765ea707dd091e0a6",
|
| 144 |
+
"git_blob_sha1": "d933df64b4fd46d5bd12840ef12fca28671c5f98"
|
| 145 |
+
},
|
| 146 |
+
"provenance/configs/Bs128_30k/train/launch.sh": {
|
| 147 |
+
"size": 811,
|
| 148 |
+
"sha256": "a267aadd48e1ac00c564561e89715406a2d653a7ea4fe3dfa8c23f0c56210e10",
|
| 149 |
+
"git_blob_sha1": "e5ce544f5645597ca960084933aad619f24cba28"
|
| 150 |
+
},
|
| 151 |
+
"provenance/configs/Bs128_30k/train/load_n17.py": {
|
| 152 |
+
"size": 136,
|
| 153 |
+
"sha256": "e509c8ff64e9bfb65e8a0027b9bf6b88ab453b4882f2d7e93a41da63843ec3cd",
|
| 154 |
+
"git_blob_sha1": "85eed0b4356436f2bd2ded2f2241e4ff92fdd065"
|
| 155 |
+
},
|
| 156 |
+
"provenance/configs/Bs128_30k/train/loss_mask.py": {
|
| 157 |
+
"size": 143,
|
| 158 |
+
"sha256": "6b46d48cd5d7075721d44fb5b1fb17e942b2eacb51f0e31020248b77bd6690bb",
|
| 159 |
+
"git_blob_sha1": "5d98223ccfd34385f362ea36487a09c3ae691004"
|
| 160 |
+
},
|
| 161 |
+
"provenance/configs/Bs128_30k/train/multi_task_dit.yaml": {
|
| 162 |
+
"size": 737,
|
| 163 |
+
"sha256": "8c2bbe6729e83e387dc30d919eb156d7da6a4f3dd89720771c1eeea51e985573",
|
| 164 |
+
"git_blob_sha1": "9ed3273a427ee67a94f61a24109fde181bfaa2de"
|
| 165 |
+
},
|
| 166 |
+
"provenance/configs/Bs128_30k/train/native_assets.py": {
|
| 167 |
+
"size": 138,
|
| 168 |
+
"sha256": "acb80a4b8e58348941f0aff6684c9db8d3508665c4e920749cc77d895bc55318",
|
| 169 |
+
"git_blob_sha1": "383fc3571cb09c3fa01c5f2b4a2602e1e8616fd1"
|
| 170 |
+
},
|
| 171 |
+
"provenance/configs/Bs128_30k/train/orchestrate.py": {
|
| 172 |
+
"size": 347,
|
| 173 |
+
"sha256": "960224e8968a3181cf246c6c24c89ffac4fa6b8f1f7b00ada1dbf773d9fffbb6",
|
| 174 |
+
"git_blob_sha1": "3bdadd272cde613eeb9b679f7872b99ee26a34e8"
|
| 175 |
+
},
|
| 176 |
+
"provenance/configs/Bs128_30k/train/run.py": {
|
| 177 |
+
"size": 347,
|
| 178 |
+
"sha256": "01d3c3e053fa01937763eb548ad8ce0e4527a9c19caeb1d41ec7da496f5ef550",
|
| 179 |
+
"git_blob_sha1": "d1105742fba295f481404a112e1f162b22bc0d34"
|
| 180 |
+
},
|
| 181 |
+
"provenance/configs/Bs128_30k/train/run_n17.py": {
|
| 182 |
+
"size": 343,
|
| 183 |
+
"sha256": "4c0724e714c320fc444f9b997c09be2582dc0ce5851259db5aa4eca4a486c522",
|
| 184 |
+
"git_blob_sha1": "61ade41949124d5f2cd9b75eaa269db154d9998e"
|
| 185 |
+
},
|
| 186 |
+
"provenance/configs/Bs128_30k/train/total/gr00t_n15_total.yaml": {
|
| 187 |
+
"size": 5745,
|
| 188 |
+
"sha256": "a16c6a1b7733da06523a2f63f7eb227c84bbddf27c18343a324004db7b2f16aa",
|
| 189 |
+
"git_blob_sha1": "8cfa3cff2322fb2523570037a5790f9bf6168669"
|
| 190 |
+
},
|
| 191 |
+
"provenance/configs/Bs128_30k/train/total/multi_task_dit_total.yaml": {
|
| 192 |
+
"size": 6499,
|
| 193 |
+
"sha256": "45b92a3935b04e13f531839e414e586f195422925d33f02b15c9615dce77c310",
|
| 194 |
+
"git_blob_sha1": "a1cd3d6af83ded29518089384c7840b4ea9e4986"
|
| 195 |
+
},
|
| 196 |
+
"provenance/scripts/__init__.py": {
|
| 197 |
+
"size": 65,
|
| 198 |
+
"sha256": "d5cb3b6372ce5367b0e1c6a3aabcacf5ae0c69c1f123c3fdc768dbdab86615fd",
|
| 199 |
+
"git_blob_sha1": "b60a71c83c559cc4515886759eb03bff532ef809"
|
| 200 |
+
},
|
| 201 |
+
"provenance/scripts/adapters/__init__.py": {
|
| 202 |
+
"size": 85,
|
| 203 |
+
"sha256": "d66d70788ab53bdfd8e0235ba7150604476d3654dece06d0871aa512bbf02db0",
|
| 204 |
+
"git_blob_sha1": "ed130013a85392ebd51771dbb14d18e0f3aa164a"
|
| 205 |
+
},
|
| 206 |
+
"provenance/scripts/adapters/action_validity.py": {
|
| 207 |
+
"size": 4223,
|
| 208 |
+
"sha256": "28284a5c5725a0cf8391c740818a25adf6e946845772db82801533c8784605e8",
|
| 209 |
+
"git_blob_sha1": "96b7d695011f8f41745245ceba576754c3714f44"
|
| 210 |
+
},
|
| 211 |
+
"provenance/scripts/adapters/groot_v3.py": {
|
| 212 |
+
"size": 4834,
|
| 213 |
+
"sha256": "f23c095efaefcaa3bbeb20487ba70141520783ffd820cf39354b9ee1a93463b1",
|
| 214 |
+
"git_blob_sha1": "577fef6bfdb8f6bd13c4b8ee48316fabc5fd1809"
|
| 215 |
+
},
|
| 216 |
+
"provenance/scripts/adapters/language.py": {
|
| 217 |
+
"size": 2378,
|
| 218 |
+
"sha256": "0c2d35f55e8a43f1772bc1f92d1582dc8c8696b330a4c999c8188a440fe7adfa",
|
| 219 |
+
"git_blob_sha1": "693cbaede81dc7ab903d42a3279f01b1b33240a7"
|
| 220 |
+
},
|
| 221 |
+
"provenance/scripts/data/__init__.py": {
|
| 222 |
+
"size": 38,
|
| 223 |
+
"sha256": "d3bc18a39403772a7ab3803cb57fd46fa6b47c10140162291029a1800b62cfc0",
|
| 224 |
+
"git_blob_sha1": "263a31b0589ca2481bfddad6515eb3a86a645c12"
|
| 225 |
+
},
|
| 226 |
+
"provenance/scripts/data/prepare.py": {
|
| 227 |
+
"size": 492,
|
| 228 |
+
"sha256": "f37d9e1c00c0abc08c32851ea50931c5a810a161fb4d0b602a916ac120bd6bb6",
|
| 229 |
+
"git_blob_sha1": "343931e66cea374ec8beb6d1e94830a67c167bb3"
|
| 230 |
+
},
|
| 231 |
+
"provenance/scripts/data/validate.py": {
|
| 232 |
+
"size": 3761,
|
| 233 |
+
"sha256": "ea0a55b480136c519baaae589ad527a4953338ed8894a89fce80c33ffdd1ad93",
|
| 234 |
+
"git_blob_sha1": "564aeb727b01544a83e48f40f48dbe4026e4f75c"
|
| 235 |
+
},
|
| 236 |
+
"provenance/scripts/data/view.py": {
|
| 237 |
+
"size": 10237,
|
| 238 |
+
"sha256": "ca7f03c7fca94575b9e089bed2727981406791b8ddbf382b61059bc3bbf937e6",
|
| 239 |
+
"git_blob_sha1": "7c86c48b6815e79f303edd54a4bf4db45b89f339"
|
| 240 |
+
},
|
| 241 |
+
"provenance/scripts/tests/test_contract.py": {
|
| 242 |
+
"size": 3756,
|
| 243 |
+
"sha256": "c240afb5d8154783c0a86fd5cf26cd330ed5ec56ae4d6e236281eccf78b351c6",
|
| 244 |
+
"git_blob_sha1": "b0d4c7630e4abe15f4fb9b673456714bce7bf450"
|
| 245 |
+
},
|
| 246 |
+
"provenance/scripts/train/__init__.py": {
|
| 247 |
+
"size": 34,
|
| 248 |
+
"sha256": "5f51caf72525efcf81ce8bdfc06321746fd185d4c932f734a3b0ffa141af74ce",
|
| 249 |
+
"git_blob_sha1": "61ef7af826bb64fd194a8cabe53818989f3cbcdb"
|
| 250 |
+
},
|
| 251 |
+
"provenance/scripts/train/checkpoints.py": {
|
| 252 |
+
"size": 1766,
|
| 253 |
+
"sha256": "c5feb6d74497a28945f574fad39846c6d8e16f9798bb7fbe60f23af65c5dcb78",
|
| 254 |
+
"git_blob_sha1": "869f01e29a3cf6fd343df5589718dd1d2f82b2cf"
|
| 255 |
+
},
|
| 256 |
+
"provenance/scripts/train/hf_watcher.py": {
|
| 257 |
+
"size": 18729,
|
| 258 |
+
"sha256": "5bced15077e5d0b63c2baeb5b3a05e5f21e9e69c26fc3ed1363c4140ee0ac5dd",
|
| 259 |
+
"git_blob_sha1": "0cb4da36c3ca84ee20e4f4b9db91ea30a71dddd1"
|
| 260 |
+
},
|
| 261 |
+
"provenance/scripts/train/language_guard.py": {
|
| 262 |
+
"size": 683,
|
| 263 |
+
"sha256": "80bb7e687c84c7120c90c2a1b74ad57ba9c0c724ad42e3dbd62656c94fb321ab",
|
| 264 |
+
"git_blob_sha1": "a6307b5c104e20fadaf96340b1686e5fd8da8164"
|
| 265 |
+
},
|
| 266 |
+
"provenance/scripts/train/load_n17.py": {
|
| 267 |
+
"size": 1110,
|
| 268 |
+
"sha256": "1ce062dcbedc08396abb041d2f33b1c0f249bfcc094898757d504394fa4a089c",
|
| 269 |
+
"git_blob_sha1": "2b24ef4fbf614dca5da4f4a64807faa4440e5a7d"
|
| 270 |
+
},
|
| 271 |
+
"provenance/scripts/train/native_assets.py": {
|
| 272 |
+
"size": 2450,
|
| 273 |
+
"sha256": "01ddf2cbef9fe60131a90b0b4866014c9ad7e617f98bad4cd548065658845b3d",
|
| 274 |
+
"git_blob_sha1": "30b2c0504938f8d9e32bdde5b387687a2aba6e70"
|
| 275 |
+
},
|
| 276 |
+
"provenance/scripts/train/orchestrate.py": {
|
| 277 |
+
"size": 4554,
|
| 278 |
+
"sha256": "7c0111f58ea1031959cbfcd64139df9af34220ac242280fb5da2e883db0b9cb1",
|
| 279 |
+
"git_blob_sha1": "2aac4e118eae079cb0f6d941c6ebdda6223f12e8"
|
| 280 |
+
},
|
| 281 |
+
"provenance/scripts/train/run_lerobot.py": {
|
| 282 |
+
"size": 15482,
|
| 283 |
+
"sha256": "ad58a8ae3091483da73293370efe4234d3e74eb6b2f694a5fa773f1a15d7fb16",
|
| 284 |
+
"git_blob_sha1": "13de7a3d0f3bce212317426443ea94b1647fcda9"
|
| 285 |
+
},
|
| 286 |
+
"provenance/scripts/train/run_n17.py": {
|
| 287 |
+
"size": 9892,
|
| 288 |
+
"sha256": "f76c2e7aa09bcb381b712399b18ea1be76767ba1cc2a4e564b01c2c439cfb313",
|
| 289 |
+
"git_blob_sha1": "968d716454f9fb611686d4fa570d940fe46b7811"
|
| 290 |
+
},
|
| 291 |
+
"provenance/scripts/train/start_watchers.py": {
|
| 292 |
+
"size": 1155,
|
| 293 |
+
"sha256": "7440fea95063139b0867489e3c82d0899fe89b63b30501038563eefa7410d003",
|
| 294 |
+
"git_blob_sha1": "aeb5b7bbc727f3b338b698a64e7a82e7bca0ba75"
|
| 295 |
+
},
|
| 296 |
+
"provenance/scripts/train/validate_checkpoint.py": {
|
| 297 |
+
"size": 6027,
|
| 298 |
+
"sha256": "b3bc7aeb656654b303b65dcb48ae1e2030b6297bfd42cdad69cd364262a378a1",
|
| 299 |
+
"git_blob_sha1": "7d4b92277a4f867b0498f6610d7765819f4b7880"
|
| 300 |
+
}
|
| 301 |
+
}
|
| 302 |
+
}
|
v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/config.json
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"type": "multi_task_dit",
|
| 3 |
+
"n_obs_steps": 2,
|
| 4 |
+
"input_features": {
|
| 5 |
+
"observation.images.head": {
|
| 6 |
+
"type": "VISUAL",
|
| 7 |
+
"shape": [
|
| 8 |
+
3,
|
| 9 |
+
224,
|
| 10 |
+
224
|
| 11 |
+
]
|
| 12 |
+
},
|
| 13 |
+
"observation.images.left_wrist": {
|
| 14 |
+
"type": "VISUAL",
|
| 15 |
+
"shape": [
|
| 16 |
+
3,
|
| 17 |
+
224,
|
| 18 |
+
224
|
| 19 |
+
]
|
| 20 |
+
},
|
| 21 |
+
"observation.images.right_wrist": {
|
| 22 |
+
"type": "VISUAL",
|
| 23 |
+
"shape": [
|
| 24 |
+
3,
|
| 25 |
+
224,
|
| 26 |
+
224
|
| 27 |
+
]
|
| 28 |
+
},
|
| 29 |
+
"observation.state": {
|
| 30 |
+
"type": "STATE",
|
| 31 |
+
"shape": [
|
| 32 |
+
20
|
| 33 |
+
]
|
| 34 |
+
}
|
| 35 |
+
},
|
| 36 |
+
"output_features": {
|
| 37 |
+
"action": {
|
| 38 |
+
"type": "ACTION",
|
| 39 |
+
"shape": [
|
| 40 |
+
23
|
| 41 |
+
]
|
| 42 |
+
}
|
| 43 |
+
},
|
| 44 |
+
"device": "cuda",
|
| 45 |
+
"use_amp": false,
|
| 46 |
+
"use_peft": false,
|
| 47 |
+
"push_to_hub": false,
|
| 48 |
+
"repo_id": null,
|
| 49 |
+
"private": null,
|
| 50 |
+
"tags": null,
|
| 51 |
+
"license": null,
|
| 52 |
+
"pretrained_path": null,
|
| 53 |
+
"horizon": 32,
|
| 54 |
+
"n_action_steps": 24,
|
| 55 |
+
"objective": "diffusion",
|
| 56 |
+
"noise_scheduler_type": "DDPM",
|
| 57 |
+
"num_train_timesteps": 100,
|
| 58 |
+
"beta_schedule": "squaredcos_cap_v2",
|
| 59 |
+
"beta_start": 0.0001,
|
| 60 |
+
"beta_end": 0.02,
|
| 61 |
+
"prediction_type": "epsilon",
|
| 62 |
+
"clip_sample": true,
|
| 63 |
+
"clip_sample_range": 1.0,
|
| 64 |
+
"num_inference_steps": null,
|
| 65 |
+
"sigma_min": 0.0,
|
| 66 |
+
"num_integration_steps": 100,
|
| 67 |
+
"integration_method": "euler",
|
| 68 |
+
"timestep_sampling_strategy": "beta",
|
| 69 |
+
"timestep_sampling_s": 0.999,
|
| 70 |
+
"timestep_sampling_alpha": 1.5,
|
| 71 |
+
"timestep_sampling_beta": 1.0,
|
| 72 |
+
"hidden_dim": 512,
|
| 73 |
+
"num_layers": 6,
|
| 74 |
+
"num_heads": 8,
|
| 75 |
+
"dropout": 0.1,
|
| 76 |
+
"use_positional_encoding": false,
|
| 77 |
+
"timestep_embed_dim": 256,
|
| 78 |
+
"use_rope": true,
|
| 79 |
+
"rope_base": 10000.0,
|
| 80 |
+
"vision_encoder_name": "/home/work/.cache/huggingface/hub/models--openai--clip-vit-base-patch16/snapshots/57c216476eefef5ab752ec549e440a49ae4ae5f3",
|
| 81 |
+
"use_separate_rgb_encoder_per_camera": false,
|
| 82 |
+
"vision_encoder_lr_multiplier": 0.1,
|
| 83 |
+
"image_resize_shape": null,
|
| 84 |
+
"image_crop_shape": [
|
| 85 |
+
224,
|
| 86 |
+
224
|
| 87 |
+
],
|
| 88 |
+
"image_crop_is_random": true,
|
| 89 |
+
"text_encoder_name": "/home/work/.cache/huggingface/hub/models--openai--clip-vit-base-patch16/snapshots/57c216476eefef5ab752ec549e440a49ae4ae5f3",
|
| 90 |
+
"tokenizer_max_length": 77,
|
| 91 |
+
"tokenizer_padding": "max_length",
|
| 92 |
+
"tokenizer_padding_side": "right",
|
| 93 |
+
"tokenizer_truncation": true,
|
| 94 |
+
"normalization_mapping": {
|
| 95 |
+
"VISUAL": "MEAN_STD",
|
| 96 |
+
"STATE": "MIN_MAX",
|
| 97 |
+
"ACTION": "MIN_MAX"
|
| 98 |
+
},
|
| 99 |
+
"optimizer_lr": 2e-05,
|
| 100 |
+
"optimizer_betas": [
|
| 101 |
+
0.95,
|
| 102 |
+
0.999
|
| 103 |
+
],
|
| 104 |
+
"optimizer_eps": 1e-08,
|
| 105 |
+
"optimizer_weight_decay": 0.0,
|
| 106 |
+
"scheduler_name": "cosine",
|
| 107 |
+
"scheduler_warmup_steps": 0,
|
| 108 |
+
"do_mask_loss_for_padding": false,
|
| 109 |
+
"drop_n_last_frames": 7
|
| 110 |
+
}
|
v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:08faf5e4134d7620e237f894aa02d2c102b6870adb4c01f8e575b2ca27a03ef0
|
| 3 |
+
size 1110874532
|
v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_postprocessor.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "policy_postprocessor",
|
| 3 |
+
"steps": [
|
| 4 |
+
{
|
| 5 |
+
"registry_name": "unnormalizer_processor",
|
| 6 |
+
"config": {
|
| 7 |
+
"eps": 1e-08,
|
| 8 |
+
"features": {
|
| 9 |
+
"action": {
|
| 10 |
+
"type": "ACTION",
|
| 11 |
+
"shape": [
|
| 12 |
+
23
|
| 13 |
+
]
|
| 14 |
+
}
|
| 15 |
+
},
|
| 16 |
+
"norm_map": {
|
| 17 |
+
"VISUAL": "MEAN_STD",
|
| 18 |
+
"STATE": "MIN_MAX",
|
| 19 |
+
"ACTION": "MIN_MAX"
|
| 20 |
+
}
|
| 21 |
+
},
|
| 22 |
+
"state_file": "policy_postprocessor_step_0_unnormalizer_processor.safetensors"
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"registry_name": "device_processor",
|
| 26 |
+
"config": {
|
| 27 |
+
"device": "cpu",
|
| 28 |
+
"float_dtype": null
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
]
|
| 32 |
+
}
|
v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_postprocessor_step_0_unnormalizer_processor.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e1e9ad65b2902a52587822fa5121e86fadf0c3a2469c4a014f34707b416b1bbd
|
| 3 |
+
size 2860
|
v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_preprocessor.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "policy_preprocessor",
|
| 3 |
+
"steps": [
|
| 4 |
+
{
|
| 5 |
+
"registry_name": "rename_observations_processor",
|
| 6 |
+
"config": {
|
| 7 |
+
"rename_map": {}
|
| 8 |
+
}
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"registry_name": "to_batch_processor",
|
| 12 |
+
"config": {}
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"registry_name": "tokenizer_processor",
|
| 16 |
+
"config": {
|
| 17 |
+
"max_length": 77,
|
| 18 |
+
"task_key": "task",
|
| 19 |
+
"padding_side": "right",
|
| 20 |
+
"padding": "max_length",
|
| 21 |
+
"truncation": true,
|
| 22 |
+
"tokenizer_name": "/home/work/.cache/huggingface/hub/models--openai--clip-vit-base-patch16/snapshots/57c216476eefef5ab752ec549e440a49ae4ae5f3"
|
| 23 |
+
}
|
| 24 |
+
},
|
| 25 |
+
{
|
| 26 |
+
"registry_name": "device_processor",
|
| 27 |
+
"config": {
|
| 28 |
+
"device": "cuda",
|
| 29 |
+
"float_dtype": null
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"registry_name": "normalizer_processor",
|
| 34 |
+
"config": {
|
| 35 |
+
"eps": 1e-08,
|
| 36 |
+
"features": {
|
| 37 |
+
"observation.images.head": {
|
| 38 |
+
"type": "VISUAL",
|
| 39 |
+
"shape": [
|
| 40 |
+
3,
|
| 41 |
+
224,
|
| 42 |
+
224
|
| 43 |
+
]
|
| 44 |
+
},
|
| 45 |
+
"observation.images.left_wrist": {
|
| 46 |
+
"type": "VISUAL",
|
| 47 |
+
"shape": [
|
| 48 |
+
3,
|
| 49 |
+
224,
|
| 50 |
+
224
|
| 51 |
+
]
|
| 52 |
+
},
|
| 53 |
+
"observation.images.right_wrist": {
|
| 54 |
+
"type": "VISUAL",
|
| 55 |
+
"shape": [
|
| 56 |
+
3,
|
| 57 |
+
224,
|
| 58 |
+
224
|
| 59 |
+
]
|
| 60 |
+
},
|
| 61 |
+
"observation.state": {
|
| 62 |
+
"type": "STATE",
|
| 63 |
+
"shape": [
|
| 64 |
+
20
|
| 65 |
+
]
|
| 66 |
+
},
|
| 67 |
+
"action": {
|
| 68 |
+
"type": "ACTION",
|
| 69 |
+
"shape": [
|
| 70 |
+
23
|
| 71 |
+
]
|
| 72 |
+
}
|
| 73 |
+
},
|
| 74 |
+
"norm_map": {
|
| 75 |
+
"VISUAL": "MEAN_STD",
|
| 76 |
+
"STATE": "MIN_MAX",
|
| 77 |
+
"ACTION": "MIN_MAX"
|
| 78 |
+
}
|
| 79 |
+
},
|
| 80 |
+
"state_file": "policy_preprocessor_step_4_normalizer_processor.safetensors"
|
| 81 |
+
}
|
| 82 |
+
]
|
| 83 |
+
}
|
v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_preprocessor_step_4_normalizer_processor.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e1e9ad65b2902a52587822fa5121e86fadf0c3a2469c4a014f34707b416b1bbd
|
| 3 |
+
size 2860
|
v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/train_config.json
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"dataset": {
|
| 3 |
+
"repo_id": "RwH-Lab/rwh-auto-step",
|
| 4 |
+
"root": "/home/work/mntvol/data/RwH-Kuavo-v2/rwh-auto-step",
|
| 5 |
+
"episodes": null,
|
| 6 |
+
"image_transforms": {
|
| 7 |
+
"enable": false,
|
| 8 |
+
"max_num_transforms": 3,
|
| 9 |
+
"random_order": false,
|
| 10 |
+
"tfs": {
|
| 11 |
+
"brightness": {
|
| 12 |
+
"weight": 1.0,
|
| 13 |
+
"type": "ColorJitter",
|
| 14 |
+
"kwargs": {
|
| 15 |
+
"brightness": [
|
| 16 |
+
0.8,
|
| 17 |
+
1.2
|
| 18 |
+
]
|
| 19 |
+
}
|
| 20 |
+
},
|
| 21 |
+
"contrast": {
|
| 22 |
+
"weight": 1.0,
|
| 23 |
+
"type": "ColorJitter",
|
| 24 |
+
"kwargs": {
|
| 25 |
+
"contrast": [
|
| 26 |
+
0.8,
|
| 27 |
+
1.2
|
| 28 |
+
]
|
| 29 |
+
}
|
| 30 |
+
},
|
| 31 |
+
"saturation": {
|
| 32 |
+
"weight": 1.0,
|
| 33 |
+
"type": "ColorJitter",
|
| 34 |
+
"kwargs": {
|
| 35 |
+
"saturation": [
|
| 36 |
+
0.5,
|
| 37 |
+
1.5
|
| 38 |
+
]
|
| 39 |
+
}
|
| 40 |
+
},
|
| 41 |
+
"hue": {
|
| 42 |
+
"weight": 1.0,
|
| 43 |
+
"type": "ColorJitter",
|
| 44 |
+
"kwargs": {
|
| 45 |
+
"hue": [
|
| 46 |
+
-0.05,
|
| 47 |
+
0.05
|
| 48 |
+
]
|
| 49 |
+
}
|
| 50 |
+
},
|
| 51 |
+
"sharpness": {
|
| 52 |
+
"weight": 1.0,
|
| 53 |
+
"type": "SharpnessJitter",
|
| 54 |
+
"kwargs": {
|
| 55 |
+
"sharpness": [
|
| 56 |
+
0.5,
|
| 57 |
+
1.5
|
| 58 |
+
]
|
| 59 |
+
}
|
| 60 |
+
},
|
| 61 |
+
"affine": {
|
| 62 |
+
"weight": 1.0,
|
| 63 |
+
"type": "RandomAffine",
|
| 64 |
+
"kwargs": {
|
| 65 |
+
"degrees": [
|
| 66 |
+
-5.0,
|
| 67 |
+
5.0
|
| 68 |
+
],
|
| 69 |
+
"translate": [
|
| 70 |
+
0.05,
|
| 71 |
+
0.05
|
| 72 |
+
]
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
}
|
| 76 |
+
},
|
| 77 |
+
"revision": null,
|
| 78 |
+
"use_imagenet_stats": true,
|
| 79 |
+
"video_backend": "pyav",
|
| 80 |
+
"return_uint8": false,
|
| 81 |
+
"streaming": false
|
| 82 |
+
},
|
| 83 |
+
"env": null,
|
| 84 |
+
"policy": {
|
| 85 |
+
"type": "multi_task_dit",
|
| 86 |
+
"n_obs_steps": 2,
|
| 87 |
+
"input_features": {
|
| 88 |
+
"observation.images.head": {
|
| 89 |
+
"type": "VISUAL",
|
| 90 |
+
"shape": [
|
| 91 |
+
3,
|
| 92 |
+
224,
|
| 93 |
+
224
|
| 94 |
+
]
|
| 95 |
+
},
|
| 96 |
+
"observation.images.left_wrist": {
|
| 97 |
+
"type": "VISUAL",
|
| 98 |
+
"shape": [
|
| 99 |
+
3,
|
| 100 |
+
224,
|
| 101 |
+
224
|
| 102 |
+
]
|
| 103 |
+
},
|
| 104 |
+
"observation.images.right_wrist": {
|
| 105 |
+
"type": "VISUAL",
|
| 106 |
+
"shape": [
|
| 107 |
+
3,
|
| 108 |
+
224,
|
| 109 |
+
224
|
| 110 |
+
]
|
| 111 |
+
},
|
| 112 |
+
"observation.state": {
|
| 113 |
+
"type": "STATE",
|
| 114 |
+
"shape": [
|
| 115 |
+
20
|
| 116 |
+
]
|
| 117 |
+
}
|
| 118 |
+
},
|
| 119 |
+
"output_features": {
|
| 120 |
+
"action": {
|
| 121 |
+
"type": "ACTION",
|
| 122 |
+
"shape": [
|
| 123 |
+
23
|
| 124 |
+
]
|
| 125 |
+
}
|
| 126 |
+
},
|
| 127 |
+
"device": "cuda",
|
| 128 |
+
"use_amp": false,
|
| 129 |
+
"use_peft": false,
|
| 130 |
+
"push_to_hub": false,
|
| 131 |
+
"repo_id": null,
|
| 132 |
+
"private": null,
|
| 133 |
+
"tags": null,
|
| 134 |
+
"license": null,
|
| 135 |
+
"pretrained_path": null,
|
| 136 |
+
"horizon": 32,
|
| 137 |
+
"n_action_steps": 24,
|
| 138 |
+
"objective": "diffusion",
|
| 139 |
+
"noise_scheduler_type": "DDPM",
|
| 140 |
+
"num_train_timesteps": 100,
|
| 141 |
+
"beta_schedule": "squaredcos_cap_v2",
|
| 142 |
+
"beta_start": 0.0001,
|
| 143 |
+
"beta_end": 0.02,
|
| 144 |
+
"prediction_type": "epsilon",
|
| 145 |
+
"clip_sample": true,
|
| 146 |
+
"clip_sample_range": 1.0,
|
| 147 |
+
"num_inference_steps": null,
|
| 148 |
+
"sigma_min": 0.0,
|
| 149 |
+
"num_integration_steps": 100,
|
| 150 |
+
"integration_method": "euler",
|
| 151 |
+
"timestep_sampling_strategy": "beta",
|
| 152 |
+
"timestep_sampling_s": 0.999,
|
| 153 |
+
"timestep_sampling_alpha": 1.5,
|
| 154 |
+
"timestep_sampling_beta": 1.0,
|
| 155 |
+
"hidden_dim": 512,
|
| 156 |
+
"num_layers": 6,
|
| 157 |
+
"num_heads": 8,
|
| 158 |
+
"dropout": 0.1,
|
| 159 |
+
"use_positional_encoding": false,
|
| 160 |
+
"timestep_embed_dim": 256,
|
| 161 |
+
"use_rope": true,
|
| 162 |
+
"rope_base": 10000.0,
|
| 163 |
+
"vision_encoder_name": "/home/work/.cache/huggingface/hub/models--openai--clip-vit-base-patch16/snapshots/57c216476eefef5ab752ec549e440a49ae4ae5f3",
|
| 164 |
+
"use_separate_rgb_encoder_per_camera": false,
|
| 165 |
+
"vision_encoder_lr_multiplier": 0.1,
|
| 166 |
+
"image_resize_shape": null,
|
| 167 |
+
"image_crop_shape": [
|
| 168 |
+
224,
|
| 169 |
+
224
|
| 170 |
+
],
|
| 171 |
+
"image_crop_is_random": true,
|
| 172 |
+
"text_encoder_name": "/home/work/.cache/huggingface/hub/models--openai--clip-vit-base-patch16/snapshots/57c216476eefef5ab752ec549e440a49ae4ae5f3",
|
| 173 |
+
"tokenizer_max_length": 77,
|
| 174 |
+
"tokenizer_padding": "max_length",
|
| 175 |
+
"tokenizer_padding_side": "right",
|
| 176 |
+
"tokenizer_truncation": true,
|
| 177 |
+
"normalization_mapping": {
|
| 178 |
+
"VISUAL": "MEAN_STD",
|
| 179 |
+
"STATE": "MIN_MAX",
|
| 180 |
+
"ACTION": "MIN_MAX"
|
| 181 |
+
},
|
| 182 |
+
"optimizer_lr": 2e-05,
|
| 183 |
+
"optimizer_betas": [
|
| 184 |
+
0.95,
|
| 185 |
+
0.999
|
| 186 |
+
],
|
| 187 |
+
"optimizer_eps": 1e-08,
|
| 188 |
+
"optimizer_weight_decay": 0.0,
|
| 189 |
+
"scheduler_name": "cosine",
|
| 190 |
+
"scheduler_warmup_steps": 0,
|
| 191 |
+
"do_mask_loss_for_padding": false,
|
| 192 |
+
"drop_n_last_frames": 7
|
| 193 |
+
},
|
| 194 |
+
"output_dir": "/home/work/mntvol/RwH-Kuavo_ckpt/v3_rawaction/multi_task_dit",
|
| 195 |
+
"job_name": "rawaction_bs128_30k_multi_task_dit",
|
| 196 |
+
"resume": false,
|
| 197 |
+
"seed": 42,
|
| 198 |
+
"cudnn_deterministic": false,
|
| 199 |
+
"num_workers": 4,
|
| 200 |
+
"batch_size": 128,
|
| 201 |
+
"prefetch_factor": 4,
|
| 202 |
+
"persistent_workers": true,
|
| 203 |
+
"steps": 30000,
|
| 204 |
+
"eval_freq": 0,
|
| 205 |
+
"log_freq": 100,
|
| 206 |
+
"tolerance_s": 0.0001,
|
| 207 |
+
"save_checkpoint": true,
|
| 208 |
+
"save_freq": 10000,
|
| 209 |
+
"use_policy_training_preset": true,
|
| 210 |
+
"optimizer": {
|
| 211 |
+
"type": "adam",
|
| 212 |
+
"lr": 2e-05,
|
| 213 |
+
"weight_decay": 0.0,
|
| 214 |
+
"grad_clip_norm": 10.0,
|
| 215 |
+
"betas": [
|
| 216 |
+
0.95,
|
| 217 |
+
0.999
|
| 218 |
+
],
|
| 219 |
+
"eps": 1e-08
|
| 220 |
+
},
|
| 221 |
+
"scheduler": {
|
| 222 |
+
"type": "diffuser",
|
| 223 |
+
"num_warmup_steps": 0,
|
| 224 |
+
"name": "cosine"
|
| 225 |
+
},
|
| 226 |
+
"eval": {
|
| 227 |
+
"n_episodes": 50,
|
| 228 |
+
"batch_size": 50,
|
| 229 |
+
"use_async_envs": true
|
| 230 |
+
},
|
| 231 |
+
"wandb": {
|
| 232 |
+
"enable": true,
|
| 233 |
+
"disable_artifact": true,
|
| 234 |
+
"project": "kuavo",
|
| 235 |
+
"entity": "minje227_hyu-hanyang-university",
|
| 236 |
+
"notes": null,
|
| 237 |
+
"run_id": "bee4vl8k",
|
| 238 |
+
"mode": "online",
|
| 239 |
+
"add_tags": true
|
| 240 |
+
},
|
| 241 |
+
"peft": null,
|
| 242 |
+
"use_rabc": false,
|
| 243 |
+
"rabc_progress_path": null,
|
| 244 |
+
"rabc_kappa": 0.01,
|
| 245 |
+
"rabc_epsilon": 1e-06,
|
| 246 |
+
"rabc_head_mode": "sparse",
|
| 247 |
+
"rename_map": {},
|
| 248 |
+
"checkpoint_path": null
|
| 249 |
+
}
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/DATA_CHANGES.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Original v3 logical view
|
| 2 |
+
|
| 3 |
+
`dataset.yaml` is shared by N1.5,N1.7,MultiTaskDiT. No data.output.root and no v3->v2 conversion.
|
| 4 |
+
Rows/timestamps, task episodes and normalization statistics are prepared once per process.
|
| 5 |
+
Videos stay original; each backend decodes requested timestamps and uses its native prefetch.
|
| 6 |
+
Torso action and validity come from original action/validity.action. No future-state substitution.
|
| 7 |
+
Reports/indices/statistics are stored under the training output _audit, not in the source dataset.
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/dataset.yaml
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
source:
|
| 2 |
+
repo_id: RwH-Lab/rwh-auto-step
|
| 3 |
+
revision: 55e6893084d2a1dd1906e09c6561cc29edc420bf
|
| 4 |
+
root: /home/work/mntvol/data/RwH-Kuavo-v2/rwh-auto-step
|
| 5 |
+
sampling:
|
| 6 |
+
source_fps: 30
|
| 7 |
+
fps: 10
|
| 8 |
+
split_at_task_change: true
|
| 9 |
+
state:
|
| 10 |
+
source: observation.state
|
| 11 |
+
indices:
|
| 12 |
+
- 0
|
| 13 |
+
- 20
|
| 14 |
+
invalid_stored_zero: keep_as_input
|
| 15 |
+
action:
|
| 16 |
+
source: action
|
| 17 |
+
indices:
|
| 18 |
+
- 0
|
| 19 |
+
- 23
|
| 20 |
+
validity_feature: validity.action
|
| 21 |
+
invalid: mask_loss
|
| 22 |
+
statistics:
|
| 23 |
+
state: all_inputs
|
| 24 |
+
action: valid_targets
|
| 25 |
+
video:
|
| 26 |
+
mode: original
|
| 27 |
+
alignment: timestamp
|
| 28 |
+
adapters:
|
| 29 |
+
gr00t_n17:
|
| 30 |
+
enabled: true
|
| 31 |
+
type: letools_native_v3
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/prepare.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compatibility entry point; audit the original-data view without conversion."""
|
| 2 |
+
import runpy
|
| 3 |
+
import sys
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
|
| 6 |
+
if '--config' not in sys.argv:
|
| 7 |
+
sys.argv += ['--config', str(Path(__file__).with_name('dataset.yaml'))]
|
| 8 |
+
runpy.run_module('scripts.data.prepare', run_name='__main__')
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/verify.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compatibility entry point for exhaustive source/view and sampled-video checks."""
|
| 2 |
+
import runpy
|
| 3 |
+
import sys
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
|
| 6 |
+
if '--config' not in sys.argv:
|
| 7 |
+
sys.argv += ['--config', str(Path(__file__).with_name('dataset.yaml'))]
|
| 8 |
+
runpy.run_module('scripts.data.validate', run_name='__main__')
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/CONTRACT.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Original-action training contract
|
| 2 |
+
|
| 3 |
+
All three policies reference `../data/dataset.yaml`. Original v3 files stay read-only.
|
| 4 |
+
30Hz -> 10Hz logical sampling, split task changes before subsampling, state20/action23.
|
| 5 |
+
Torso uses original action[:4]. Invalid action elements only are excluded from loss and statistics.
|
| 6 |
+
Actual batch128, accumulation1, seed42, 30000 updates, no evaluation split.
|
| 7 |
+
N1.5: observation1/action16. N1.7: observation1/action40. DiT: observation2/horizon32/execution24.
|
| 8 |
+
Full action/history chunks must remain within each logical task episode.
|
| 9 |
+
|
| 10 |
+
N1.5/DiT call the LeTools config converter and pinned LeRobot Trainer.
|
| 11 |
+
N1.7 calls LeTools bundled native pipeline, processor, shard cache, collator, and HF Trainer.
|
| 12 |
+
Runtime adapters only: original-v3 metadata/timestamps, source validity masks, per-sample N1.5 language.
|
| 13 |
+
N1.5/DiT letterbox images to 224px before native preprocessing; N1.7 keeps native image augmentation.
|
| 14 |
+
N1.7 runs in the dedicated Python3.10/Torch2.7.1 conda environment required by LeTools. No model, loss, Trainer-class, or CUDA-layout patch is applied.
|
| 15 |
+
Checkpoints: 10k,20k,30k locally. HF watcher publishes verified 10k/20k/30k per policy under v2 after the run succeeds, excluding optimizer/RNG.
|
| 16 |
+
DiT starts exactly once on the first successfully completed GPU0/1.
|
| 17 |
+
|
| 18 |
+
LeTools:6d2002613241ae2a32c5d8fd753f80caa1383fba; LeRobot:a07f22e22ce88cddff1f6eddced9ea008fbfc37c.
|
| 19 |
+
Runtime and reusable code are in `RwH/scripts`, not a private N1.7 checkout copy.
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/compat.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
import sys
|
| 3 |
+
sys.path.insert(0,str(Path(__file__).resolve().parents[3]))
|
| 4 |
+
from scripts.adapters.language import *
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/gr00t_n15.yaml
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset:
|
| 2 |
+
config: ../data/dataset.yaml
|
| 3 |
+
training:
|
| 4 |
+
resume: false
|
| 5 |
+
output_dir: /home/work/mntvol/RwH-Kuavo_ckpt/v3_rawaction/gr00t_n15
|
| 6 |
+
job_name: rawaction_bs128_30k_gr00t_n15
|
| 7 |
+
batch_size: 128
|
| 8 |
+
steps: 30000
|
| 9 |
+
save_freq: 10000
|
| 10 |
+
log_freq: 100
|
| 11 |
+
eval_freq: 0
|
| 12 |
+
num_workers: 4
|
| 13 |
+
seed: 42
|
| 14 |
+
use_policy_training_preset: false
|
| 15 |
+
optimizer:
|
| 16 |
+
type: adamw
|
| 17 |
+
lr: 0.0001
|
| 18 |
+
betas:
|
| 19 |
+
- 0.95
|
| 20 |
+
- 0.999
|
| 21 |
+
eps: 1.0e-08
|
| 22 |
+
weight_decay: 1.0e-05
|
| 23 |
+
grad_clip_norm: 10.0
|
| 24 |
+
scheduler:
|
| 25 |
+
type: cosine_decay_with_warmup
|
| 26 |
+
num_warmup_steps: 1500
|
| 27 |
+
num_decay_steps: 30000
|
| 28 |
+
peak_lr: 0.0001
|
| 29 |
+
decay_lr: 1.0e-05
|
| 30 |
+
wandb:
|
| 31 |
+
enable: true
|
| 32 |
+
project: kuavo
|
| 33 |
+
entity: minje227_hyu-hanyang-university
|
| 34 |
+
mode: online
|
| 35 |
+
disable_artifact: true
|
| 36 |
+
policy:
|
| 37 |
+
type: groot
|
| 38 |
+
pretrained_path: null
|
| 39 |
+
base_model_path: nvidia/GR00T-N1.5-3B
|
| 40 |
+
embodiment_tag: new_embodiment
|
| 41 |
+
use_bf16: true
|
| 42 |
+
chunk_size: 16
|
| 43 |
+
n_action_steps: 16
|
| 44 |
+
max_state_dim: 64
|
| 45 |
+
max_action_dim: 32
|
| 46 |
+
image_size:
|
| 47 |
+
- 224
|
| 48 |
+
- 224
|
| 49 |
+
tune_llm: false
|
| 50 |
+
tune_visual: false
|
| 51 |
+
tune_projector: true
|
| 52 |
+
tune_diffusion_model: true
|
| 53 |
+
optimizer_lr: 0.0001
|
| 54 |
+
warmup_ratio: 0.05
|
| 55 |
+
dataloader_num_workers: 8
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/gr00t_n17.yaml
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset:
|
| 2 |
+
config: ../data/dataset.yaml
|
| 3 |
+
training:
|
| 4 |
+
resume: false
|
| 5 |
+
output_dir: /home/work/mntvol/RwH-Kuavo_ckpt/v3_rawaction/gr00t_n17
|
| 6 |
+
job_name: rawaction_bs128_30k_gr00t_n17
|
| 7 |
+
batch_size: 128
|
| 8 |
+
global_batch_size: 128
|
| 9 |
+
gradient_accumulation_steps: 1
|
| 10 |
+
steps: 30000
|
| 11 |
+
save_freq: 10000
|
| 12 |
+
log_freq: 100
|
| 13 |
+
num_workers: 6
|
| 14 |
+
seed: 42
|
| 15 |
+
optimizer:
|
| 16 |
+
lr: 0.0001
|
| 17 |
+
betas:
|
| 18 |
+
- 0.9
|
| 19 |
+
- 0.999
|
| 20 |
+
weight_decay: 1.0e-05
|
| 21 |
+
grad_clip_norm: 1.0
|
| 22 |
+
scheduler:
|
| 23 |
+
type: cosine
|
| 24 |
+
warmup_ratio: 0.05
|
| 25 |
+
decay_lr: 0.0
|
| 26 |
+
shard_size: 1024
|
| 27 |
+
episode_sampling_rate: 0.1
|
| 28 |
+
num_shards_per_epoch: 100000
|
| 29 |
+
policy:
|
| 30 |
+
type: groot_n17
|
| 31 |
+
base_model_path: nvidia/GR00T-N1.7-3B
|
| 32 |
+
base_revision: 2fc962b973bccdd5d8ce4f67cc63b264d6886495
|
| 33 |
+
runtime_root: /home/work/workspace/RwH-Kuavo_V2/LeTools-Learning/kuavo_model/external_models/gr00tn1d7
|
| 34 |
+
embodiment_tag: new_embodiment
|
| 35 |
+
chunk_size: 40
|
| 36 |
+
n_action_steps: 40
|
| 37 |
+
max_state_dim: 132
|
| 38 |
+
max_action_dim: 132
|
| 39 |
+
tune_llm: false
|
| 40 |
+
tune_visual: false
|
| 41 |
+
tune_projector: true
|
| 42 |
+
tune_diffusion_model: true
|
| 43 |
+
tune_vlln: true
|
| 44 |
+
use_bf16: true
|
| 45 |
+
use_relative_action: false
|
| 46 |
+
state_dropout_prob: 0.2
|
| 47 |
+
loss:
|
| 48 |
+
validity_feature: validity.action
|
| 49 |
+
dimension_mask: true
|
| 50 |
+
exclude_action_padding: true
|
| 51 |
+
reduction: sum_valid_elements_div_valid_count
|
| 52 |
+
wandb:
|
| 53 |
+
enable: true
|
| 54 |
+
project: kuavo
|
| 55 |
+
entity: minje227_hyu-hanyang-university
|
| 56 |
+
mode: online
|
| 57 |
+
disable_artifact: true
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/hf_watcher.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compatibility entry point; implementation lives in RwH/scripts."""
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import os,runpy,sys
|
| 4 |
+
root=Path(__file__).resolve().parents[3]
|
| 5 |
+
sys.path.insert(0,str(root))
|
| 6 |
+
os.environ['RWH_TRAIN_CONFIG_DIR']=str(Path(__file__).resolve().parent)
|
| 7 |
+
if __name__=='__main__':runpy.run_module('scripts.train.hf_watcher',run_name='__main__')
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/launch.sh
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
source /home/work/workspace/session-bootstrap.sh >/dev/null
|
| 3 |
+
set -euo pipefail
|
| 4 |
+
task_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
| 5 |
+
project="$(cd "$task_dir/../../.." && pwd)"
|
| 6 |
+
export RWH_TRAIN_CONFIG_DIR="$task_dir"
|
| 7 |
+
export PYTHONPATH="$project:$project/../LeTools-Learning/third_party/lerobot/src"
|
| 8 |
+
export WANDB_API_KEY="$(cat /home/work/.local/share/wandb/kuavo_api_key)"
|
| 9 |
+
export WANDB__SERVICE_WAIT=120
|
| 10 |
+
export TOKENIZERS_PARALLELISM=false OMP_NUM_THREADS=8 MKL_NUM_THREADS=8 HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1
|
| 11 |
+
policy="$1"
|
| 12 |
+
shift
|
| 13 |
+
cd "$project"
|
| 14 |
+
if [ "$policy" = gr00t_n17 ]; then
|
| 15 |
+
exec /home/work/.local/miniconda3/envs/rwh_n17_letools/bin/python -m scripts.train.run_n17 "$@"
|
| 16 |
+
else
|
| 17 |
+
exec /home/work/.local/miniconda3/envs/rwh/bin/python -m scripts.train.run_lerobot "$policy" "$@"
|
| 18 |
+
fi
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/load_n17.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
import sys
|
| 3 |
+
sys.path.insert(0,str(Path(__file__).resolve().parents[3]))
|
| 4 |
+
from scripts.train.load_n17 import load
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/loss_mask.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
import sys
|
| 3 |
+
sys.path.insert(0,str(Path(__file__).resolve().parents[3]))
|
| 4 |
+
from scripts.adapters.action_validity import *
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/multi_task_dit.yaml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset:
|
| 2 |
+
config: ../data/dataset.yaml
|
| 3 |
+
training:
|
| 4 |
+
resume: false
|
| 5 |
+
output_dir: /home/work/mntvol/RwH-Kuavo_ckpt/v3_rawaction/multi_task_dit
|
| 6 |
+
job_name: rawaction_bs128_30k_multi_task_dit
|
| 7 |
+
batch_size: 128
|
| 8 |
+
steps: 30000
|
| 9 |
+
save_freq: 10000
|
| 10 |
+
log_freq: 100
|
| 11 |
+
eval_freq: 0
|
| 12 |
+
num_workers: 4
|
| 13 |
+
seed: 42
|
| 14 |
+
wandb:
|
| 15 |
+
enable: true
|
| 16 |
+
project: kuavo
|
| 17 |
+
entity: minje227_hyu-hanyang-university
|
| 18 |
+
mode: online
|
| 19 |
+
disable_artifact: true
|
| 20 |
+
policy:
|
| 21 |
+
type: multi_task_dit
|
| 22 |
+
pretrained_path: null
|
| 23 |
+
vision_encoder_name: openai/clip-vit-base-patch16
|
| 24 |
+
text_encoder_name: openai/clip-vit-base-patch16
|
| 25 |
+
n_obs_steps: 2
|
| 26 |
+
horizon: 32
|
| 27 |
+
n_action_steps: 24
|
| 28 |
+
objective: diffusion
|
| 29 |
+
image_crop_shape:
|
| 30 |
+
- 224
|
| 31 |
+
- 224
|
| 32 |
+
optimizer_lr: 2.0e-05
|
| 33 |
+
scheduler_name: cosine
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/native_assets.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
import sys
|
| 3 |
+
sys.path.insert(0,str(Path(__file__).resolve().parents[3]))
|
| 4 |
+
from scripts.train.native_assets import *
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/orchestrate.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compatibility entry point; implementation lives in RwH/scripts."""
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import os,runpy,sys
|
| 4 |
+
root=Path(__file__).resolve().parents[3]
|
| 5 |
+
sys.path.insert(0,str(root))
|
| 6 |
+
os.environ['RWH_TRAIN_CONFIG_DIR']=str(Path(__file__).resolve().parent)
|
| 7 |
+
if __name__=='__main__':runpy.run_module('scripts.train.orchestrate',run_name='__main__')
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/run.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compatibility entry point; implementation lives in RwH/scripts."""
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import os,runpy,sys
|
| 4 |
+
root=Path(__file__).resolve().parents[3]
|
| 5 |
+
sys.path.insert(0,str(root))
|
| 6 |
+
os.environ['RWH_TRAIN_CONFIG_DIR']=str(Path(__file__).resolve().parent)
|
| 7 |
+
if __name__=='__main__':runpy.run_module('scripts.train.run_lerobot',run_name='__main__')
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/run_n17.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compatibility entry point; implementation lives in RwH/scripts."""
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import os,runpy,sys
|
| 4 |
+
root=Path(__file__).resolve().parents[3]
|
| 5 |
+
sys.path.insert(0,str(root))
|
| 6 |
+
os.environ['RWH_TRAIN_CONFIG_DIR']=str(Path(__file__).resolve().parent)
|
| 7 |
+
if __name__=='__main__':runpy.run_module('scripts.train.run_n17',run_name='__main__')
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/total/gr00t_n15_total.yaml
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset: # 数据集相关设置。
|
| 2 |
+
repo_id: "lerobot/your_dataset" # 数据集名字,用来做标识。
|
| 3 |
+
root: null # 本地数据路径。
|
| 4 |
+
episodes: null # 只训练指定的 episode;不填就是全量。
|
| 5 |
+
image_transforms: {} # 额外的图像预处理设置。
|
| 6 |
+
revision: null # 远程数据集版本;本地训练一般不用改。
|
| 7 |
+
use_imagenet_stats: true # 图像是否用 ImageNet 的均值方差做归一化。
|
| 8 |
+
video_backend: pyav # 读视频帧用的后端。一般用 pyav。
|
| 9 |
+
return_uint8: false # 是否保留图像为 uint8,而不是转换成 float tensor。
|
| 10 |
+
streaming: false # 是否流式读数据。一般保持 false。
|
| 11 |
+
|
| 12 |
+
training: # 训练流程设置。
|
| 13 |
+
output_dir: "outputs/train/gr00t" # 训练输出目录。
|
| 14 |
+
job_name: "gr00t" # 实验名。
|
| 15 |
+
resume: false # 是否从 output_dir 最新 checkpoint 继续训;续训改为 true,并修改 output_dir 为上阶段 run 的产物路径,如 outputs/train/gr00t_YYYYMMDD_HHMMSS。
|
| 16 |
+
seed: 1000 # 随机种子。
|
| 17 |
+
cudnn_deterministic: false # 更稳定可复现,但会慢一点。
|
| 18 |
+
num_workers: 4 # 读数据用的进程数。
|
| 19 |
+
batch_size: 8 # 每张卡上的 batch size。
|
| 20 |
+
prefetch_factor: 4 # 每个读数据进程提前准备多少个 batch。
|
| 21 |
+
persistent_workers: true # 是否让读数据进程常驻。
|
| 22 |
+
steps: 10000 # 总训练步数。
|
| 23 |
+
eval_freq: 1000 # 每隔多少步做一次评测。
|
| 24 |
+
log_freq: 100 # 每隔多少步打一次日志。
|
| 25 |
+
tolerance_s: 1.0e-4 # 视频帧和标注对齐时允许的时间误差。
|
| 26 |
+
save_checkpoint: true # 是否保存 checkpoint。
|
| 27 |
+
save_freq: 1000 # 每隔多少步存一次权重。
|
| 28 |
+
use_policy_training_preset: true # 是否用模型自带的优化器和调度器默认值。
|
| 29 |
+
optimizer: null # 手动指定优化器;一般留空。
|
| 30 |
+
scheduler: null # 手动指定学习率策略;一般留空。
|
| 31 |
+
use_rabc: false # 是否启用 RABC。一般不用。
|
| 32 |
+
rabc_progress_path: null # RABC 用的额外文件路径。一般不用。
|
| 33 |
+
rabc_kappa: 0.01 # RABC 参数。一般不用改。
|
| 34 |
+
rabc_epsilon: 1.0e-6 # RABC 的稳定性参数。一般不用改。
|
| 35 |
+
rabc_head_mode: "sparse" # RABC 模式。一般不用改。
|
| 36 |
+
rename_map: {} # 特征重命名表。一般不用。
|
| 37 |
+
|
| 38 |
+
eval: # 训练中评测设置。
|
| 39 |
+
n_episodes: 50 # 每次评测跑多少条。
|
| 40 |
+
batch_size: 0 # 评测 batch size。0 表示自动。
|
| 41 |
+
use_async_envs: true # 评测环境是否并行异步跑。
|
| 42 |
+
|
| 43 |
+
wandb: # wandb 设置。
|
| 44 |
+
enable: false # 是否启用 wandb。
|
| 45 |
+
disable_artifact: true # 是否禁用向 wandb 上传权重文件。
|
| 46 |
+
project: "lerobot" # wandb 项目名。
|
| 47 |
+
entity: null # wandb 账号或团队名。
|
| 48 |
+
notes: null # wandb 备注。
|
| 49 |
+
run_id: null # wandb run id。
|
| 50 |
+
mode: null # wandb 模式。
|
| 51 |
+
add_tags: true # 是否自动加 wandb 标签。
|
| 52 |
+
|
| 53 |
+
peft: null # PEFT 设置。一般留空。
|
| 54 |
+
|
| 55 |
+
policy: # 模型相关设置。
|
| 56 |
+
type: "groot" # 模型类型。
|
| 57 |
+
n_obs_steps: 1 # 一次预测会看几帧观测。
|
| 58 |
+
input_features: {} # 手动指定输入特征。一般不用。
|
| 59 |
+
output_features: {} # 手动指定输出特征。一般不用。
|
| 60 |
+
device: "cuda" # 运行设备。
|
| 61 |
+
use_amp: false # 是否启用混合精度。
|
| 62 |
+
use_peft: false # 是否启用 PEFT。
|
| 63 |
+
push_to_hub: false # 是否上传到 Hugging Face。
|
| 64 |
+
repo_id: null # 上传时用的仓库名。
|
| 65 |
+
private: null # 上传仓库是否私有。
|
| 66 |
+
tags: null # 上传时的标签。
|
| 67 |
+
license: null # 上传时的 license。
|
| 68 |
+
pretrained_path: null # 加载 LeRobot 整包 checkpoint;从 NVIDIA 基座微调时保持 null(权重见 base_model_path)。二阶段微调请改为上一 run 的 <policy_name>_YYYYMMDD_HHMMSS/checkpoints/<step>/pretrained_model 路径。
|
| 69 |
+
chunk_size: 50 # 每次预测多长的动作序列。
|
| 70 |
+
n_action_steps: 50 # 每次真正执行多少步动作。
|
| 71 |
+
max_state_dim: 64 # 状态最大维度。
|
| 72 |
+
max_action_dim: 32 # 动作最大维度。
|
| 73 |
+
normalization_mapping: # 不同输入的归一化方式。
|
| 74 |
+
VISUAL: "IDENTITY" # 图像怎么归一化。
|
| 75 |
+
STATE: "MEAN_STD" # 状态怎么归一化。
|
| 76 |
+
ACTION: "MEAN_STD" # 动作怎么归一化。
|
| 77 |
+
image_size: [224, 224] # 模型期望的图像尺寸。
|
| 78 |
+
base_model_path: "nvidia/GR00T-N1.5-3B" # GR00T 基座模型。
|
| 79 |
+
tokenizer_assets_repo: "lerobot/eagle2hg-processor-groot-n1p5" # processor 资源仓库。
|
| 80 |
+
embodiment_tag: "new_embodiment" # 机器人形态标签。
|
| 81 |
+
tune_llm: false # 是否训练语言部分。
|
| 82 |
+
tune_visual: false # 是否训练视觉部分。
|
| 83 |
+
tune_projector: true # 是否训练 projector。
|
| 84 |
+
tune_diffusion_model: true # 是否训练动作头。
|
| 85 |
+
lora_alpha: 16 # LoRA alpha。
|
| 86 |
+
lora_dropout: 0.1 # LoRA dropout。
|
| 87 |
+
lora_full_model: false # 是否全模型加 LoRA。
|
| 88 |
+
optimizer_lr: 1.0e-4 # 主学习率。
|
| 89 |
+
optimizer_betas: [0.95, 0.999] # 优化器 beta 参数。
|
| 90 |
+
optimizer_eps: 1.0e-8 # 优化器 epsilon。
|
| 91 |
+
optimizer_weight_decay: 1.0e-5 # weight decay。
|
| 92 |
+
warmup_ratio: 0.05 # warmup 比例。
|
| 93 |
+
use_bf16: true # 是否用 bf16。
|
| 94 |
+
video_backend: "decord" # 这个模型训练时用的视频后端。
|
| 95 |
+
balance_dataset_weights: true # 多数据集时是否平衡各数据集。
|
| 96 |
+
balance_trajectory_weights: true # 是否平衡不同轨迹。
|
| 97 |
+
dataset_paths: null # 多数据集路径列表。
|
| 98 |
+
output_dir: "./tmp/gr00t" # 这个模型内部使用的输出目录。
|
| 99 |
+
save_steps: 1000 # 模型内部保存步数。
|
| 100 |
+
max_steps: 10000 # 模型内部最大训练步数。
|
| 101 |
+
batch_size: 32 # 模型内部 batch size。
|
| 102 |
+
dataloader_num_workers: 8 # 模型内部读数据进程数。
|
| 103 |
+
report_to: "wandb" # 日志上报后端。
|
| 104 |
+
resume: false # 模型内部是否恢复训练。
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/total/multi_task_dit_total.yaml
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset: # 数据集相关设置。
|
| 2 |
+
repo_id: "lerobot/your_dataset" # 数据集名字,用来做标识。
|
| 3 |
+
root: null # 本地数据路径。
|
| 4 |
+
episodes: null # 只训练指定的 episode;不填就是全量。
|
| 5 |
+
image_transforms: {} # 额外的图像预处理设置。
|
| 6 |
+
revision: null # 远程数据集版本;本地训练一般不用改。
|
| 7 |
+
use_imagenet_stats: true # 图像是否用 ImageNet 的均值方差做归一化。
|
| 8 |
+
video_backend: pyav # 读视频帧用的后端。一般用 pyav。
|
| 9 |
+
return_uint8: false # 是否保留图像为 uint8,而不是转换成 float tensor。
|
| 10 |
+
streaming: false # 是否流式读数据。一般保持 false。
|
| 11 |
+
|
| 12 |
+
training: # 训练流程设置。
|
| 13 |
+
output_dir: "outputs/train/multi_task_dit" # 训练输出目录。
|
| 14 |
+
job_name: "multi_task_dit" # 实验名。
|
| 15 |
+
resume: false # 是否从 output_dir 最新 checkpoint 继续训;续训改为 true,并修改 output_dir 为上阶段 run 的产物路径,如 outputs/train/multi_task_dit_YYYYMMDD_HHMMSS。
|
| 16 |
+
seed: 1000 # 随机种子。
|
| 17 |
+
cudnn_deterministic: false # 更稳定可复现,但会慢一点。
|
| 18 |
+
num_workers: 4 # 读数据用的进程数。
|
| 19 |
+
batch_size: 8 # 每张卡上的 batch size。
|
| 20 |
+
prefetch_factor: 4 # 每个读数据进程提前准备多少个 batch。
|
| 21 |
+
persistent_workers: true # 是否让读数据进程常驻。
|
| 22 |
+
steps: 100000 # 总训练步数。
|
| 23 |
+
eval_freq: 10000 # 每隔多少步做一次评测。
|
| 24 |
+
log_freq: 100 # 每隔多少步打一次日志。
|
| 25 |
+
tolerance_s: 1.0e-4 # 视频帧和标注对齐时允许的时间误差。
|
| 26 |
+
save_checkpoint: true # 是否保存 checkpoint。
|
| 27 |
+
save_freq: 10000 # 每隔多少步存一次权重。
|
| 28 |
+
use_policy_training_preset: true # 是否用模型自带的优化器和调度器默认值。
|
| 29 |
+
optimizer: null # 手动指定优化器;一般留空。
|
| 30 |
+
scheduler: null # 手动指定学习率策略;一般留空。
|
| 31 |
+
use_rabc: false # 是否启用 RABC。一般不用。
|
| 32 |
+
rabc_progress_path: null # RABC 用的额外文件路径。一般不用。
|
| 33 |
+
rabc_kappa: 0.01 # RABC 参数。一般不用改。
|
| 34 |
+
rabc_epsilon: 1.0e-6 # RABC 的稳定性参数。一般不用改。
|
| 35 |
+
rabc_head_mode: "sparse" # RABC 模式。一般不用改。
|
| 36 |
+
rename_map: {} # 特征重命名表。一般不用。
|
| 37 |
+
|
| 38 |
+
eval: # 训练中评测设置。
|
| 39 |
+
n_episodes: 50 # 每次评测跑多少条。
|
| 40 |
+
batch_size: 0 # 评测 batch size。0 表示自动。
|
| 41 |
+
use_async_envs: true # 评测环境是否并行异步跑。
|
| 42 |
+
|
| 43 |
+
wandb: # wandb 设置。
|
| 44 |
+
enable: false # 是否启用 wandb。
|
| 45 |
+
disable_artifact: true # 是否禁用向 wandb 上传权重文件。
|
| 46 |
+
project: "lerobot" # wandb 项目名。
|
| 47 |
+
entity: null # wandb 账号或团队名。
|
| 48 |
+
notes: null # wandb 备注。
|
| 49 |
+
run_id: null # wandb run id。
|
| 50 |
+
mode: null # wandb 模式。
|
| 51 |
+
add_tags: true # 是否自动加 wandb 标签。
|
| 52 |
+
|
| 53 |
+
peft: null # PEFT 设置。一般留空。
|
| 54 |
+
|
| 55 |
+
policy: # 模型相关设置。
|
| 56 |
+
type: "multi_task_dit" # 模型类型。
|
| 57 |
+
n_obs_steps: 2 # 一次预测会看几帧观测。
|
| 58 |
+
input_features: {} # 手动指定输入特征。一般不用。
|
| 59 |
+
output_features: {} # 手动指定输出特征。一般不用。
|
| 60 |
+
device: "cuda" # 运行设备。
|
| 61 |
+
use_amp: false # 是否启用混合精度。
|
| 62 |
+
use_peft: false # 是否启用 PEFT。
|
| 63 |
+
push_to_hub: false # 是否上传到 Hugging Face。
|
| 64 |
+
repo_id: null # 上传时用的仓库名。
|
| 65 |
+
private: null # 上传仓库是否私有。
|
| 66 |
+
tags: null # 上传时的标签。
|
| 67 |
+
license: null # 上传时的 license。
|
| 68 |
+
pretrained_path: null # 通常从头训(填 null)或填本地/LeRobot checkpoint。二阶段微调请改为上一 run 的 <policy_name>_YYYYMMDD_HHMMSS/checkpoints/<step>/pretrained_model 路径。
|
| 69 |
+
horizon: 32 # 模型内部预测的总长度。
|
| 70 |
+
n_action_steps: 24 # 每次真正执行多少步动作。
|
| 71 |
+
objective: "diffusion" # 训练目标类型。
|
| 72 |
+
noise_scheduler_type: "DDPM" # 噪声调度器类型。
|
| 73 |
+
num_train_timesteps: 100 # 训练时的扩散步数。
|
| 74 |
+
beta_schedule: "squaredcos_cap_v2" # beta 调度方式。
|
| 75 |
+
beta_start: 1.0e-4 # beta 起始值。
|
| 76 |
+
beta_end: 0.02 # beta 结束值。
|
| 77 |
+
prediction_type: "epsilon" # 模型预测什么目标。
|
| 78 |
+
clip_sample: true # 是否裁剪去噪结果。
|
| 79 |
+
clip_sample_range: 1.0 # 裁剪范围。
|
| 80 |
+
num_inference_steps: null # 推理时去噪多少步;不填就用默认。
|
| 81 |
+
sigma_min: 0.0 # 最小 sigma。
|
| 82 |
+
num_integration_steps: 100 # 积分步数。
|
| 83 |
+
integration_method: "euler" # 积分方法。
|
| 84 |
+
timestep_sampling_strategy: "beta" # 时间步采样策略。
|
| 85 |
+
timestep_sampling_s: 0.999 # 时间步采样参数 s。
|
| 86 |
+
timestep_sampling_alpha: 1.5 # 时间步采样 alpha。
|
| 87 |
+
timestep_sampling_beta: 1.0 # 时间步采样 beta。
|
| 88 |
+
hidden_dim: 512 # 主隐藏维度。
|
| 89 |
+
num_layers: 6 # 层数。
|
| 90 |
+
num_heads: 8 # 注意力头数。
|
| 91 |
+
dropout: 0.1 # dropout 比例。
|
| 92 |
+
use_positional_encoding: false # 是否启用位置编码。
|
| 93 |
+
timestep_embed_dim: 256 # 时间嵌入维度。
|
| 94 |
+
use_rope: true # 是否启用 RoPE。
|
| 95 |
+
rope_base: 10000.0 # RoPE base。
|
| 96 |
+
vision_encoder_name: "openai/clip-vit-base-patch16" # 视觉编码器模型名。
|
| 97 |
+
use_separate_rgb_encoder_per_camera: false # 每个相机是否单独编码。
|
| 98 |
+
vision_encoder_lr_multiplier: 0.1 # 视觉编码器学习率倍率。
|
| 99 |
+
image_resize_shape: null # 图像先 resize 到多大。
|
| 100 |
+
image_crop_shape: [224, 224] # 图像 crop 尺寸。
|
| 101 |
+
image_crop_is_random: true # 是否随机 crop。
|
| 102 |
+
text_encoder_name: "openai/clip-vit-base-patch16" # 文本编码器模型名。
|
| 103 |
+
tokenizer_max_length: 77 # 文本最大长度。
|
| 104 |
+
tokenizer_padding: "max_length" # padding 模式。
|
| 105 |
+
tokenizer_padding_side: "right" # padding 方向。
|
| 106 |
+
tokenizer_truncation: true # 是否截断过长文本。
|
| 107 |
+
normalization_mapping: # 不同输入的归一化方式。
|
| 108 |
+
VISUAL: "MEAN_STD" # 图像怎么归一化。
|
| 109 |
+
STATE: "MIN_MAX" # 状态怎么归一化。
|
| 110 |
+
ACTION: "MIN_MAX" # 动作怎么归一化。
|
| 111 |
+
optimizer_lr: 2.0e-5 # 主学习率。
|
| 112 |
+
optimizer_betas: [0.95, 0.999] # 优化器 beta 参数。
|
| 113 |
+
optimizer_eps: 1.0e-8 # 优化器 epsilon。
|
| 114 |
+
optimizer_weight_decay: 0.0 # weight decay。
|
| 115 |
+
scheduler_name: "cosine" # 学习率策略。
|
| 116 |
+
scheduler_warmup_steps: 0 # warmup 步数。
|
| 117 |
+
do_mask_loss_for_padding: false # 算 loss 时是否忽略 padding。
|
| 118 |
+
drop_n_last_frames: null # 训练时丢掉最后几帧,减少 padding 影响。
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Reusable RwH data views and LeTools training entry points."""
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Small compatibility adapters; model and trainer implementations stay upstream."""
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/action_validity.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Project-only dimension masks; never rewrite vendored policies."""
|
| 2 |
+
import inspect
|
| 3 |
+
import textwrap
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def masked_mean(error, mask):
|
| 7 |
+
mask = mask.to(device=error.device, dtype=error.dtype).expand_as(error)
|
| 8 |
+
return (error.float() * mask).sum() / mask.sum().clamp(min=1)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class MaskBank:
|
| 12 |
+
def __init__(self, view, policy):
|
| 13 |
+
import torch
|
| 14 |
+
self.mask = torch.as_tensor(view.validity.copy(), dtype=torch.bool)
|
| 15 |
+
self.policy = policy
|
| 16 |
+
self.offsets = torch.as_tensor(view.offsets(policy))
|
| 17 |
+
self.calls = 0
|
| 18 |
+
|
| 19 |
+
def attach(self, processed, raw):
|
| 20 |
+
import torch
|
| 21 |
+
index = raw["index"].detach().cpu().long().reshape(-1)
|
| 22 |
+
mask = self.mask[index[:, None] + self.offsets[None, :]]
|
| 23 |
+
if raw.get("action_is_pad") is not None:
|
| 24 |
+
mask &= ~raw["action_is_pad"].detach().cpu().bool().unsqueeze(-1)
|
| 25 |
+
if self.policy == "gr00t_n15":
|
| 26 |
+
padded = torch.zeros_like(processed["action_mask"], dtype=torch.bool)
|
| 27 |
+
if padded.shape[:2] != mask.shape[:2]:
|
| 28 |
+
raise RuntimeError("N1.5 mask/chunk alignment mismatch")
|
| 29 |
+
padded[..., :23] = mask.to(padded.device)
|
| 30 |
+
processed["action_mask"] = processed["action_mask"].bool() & padded
|
| 31 |
+
else:
|
| 32 |
+
if processed["action"].shape != mask.shape:
|
| 33 |
+
raise RuntimeError("DiT mask/chunk alignment mismatch")
|
| 34 |
+
processed["rwh_action_validity"] = mask.to(processed["action"].device)
|
| 35 |
+
self.calls += 1
|
| 36 |
+
if self.calls == 1:
|
| 37 |
+
print("RWH_MASK_ATTACHED", self.policy, list(mask.shape), "invalid", int((~mask).sum()), flush=True)
|
| 38 |
+
return processed
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def diffusion_loss(self, model, batch, conditioning_vec):
|
| 42 |
+
import torch
|
| 43 |
+
import torch.nn.functional as F
|
| 44 |
+
actions = batch["action"]
|
| 45 |
+
noise = torch.randn_like(actions)
|
| 46 |
+
timesteps = torch.randint(0, self.noise_scheduler.config.num_train_timesteps,
|
| 47 |
+
(actions.shape[0],), device=actions.device).long()
|
| 48 |
+
noisy = self.noise_scheduler.add_noise(actions, noise, timesteps)
|
| 49 |
+
prediction_type = self.noise_scheduler.config.prediction_type
|
| 50 |
+
if prediction_type == "epsilon":
|
| 51 |
+
target = noise
|
| 52 |
+
elif prediction_type == "sample":
|
| 53 |
+
target = actions
|
| 54 |
+
else:
|
| 55 |
+
raise ValueError(prediction_type)
|
| 56 |
+
predicted = model(noisy, timesteps, conditioning_vec=conditioning_vec)
|
| 57 |
+
mask = batch["rwh_action_validity"]
|
| 58 |
+
if "action_is_pad" in batch:
|
| 59 |
+
mask = mask & ~batch["action_is_pad"].unsqueeze(-1)
|
| 60 |
+
return masked_mean(F.mse_loss(predicted.float(), target.float(), reduction="none"), mask)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def install(policy):
|
| 64 |
+
if policy == "multi_task_dit":
|
| 65 |
+
from lerobot.policies.multi_task_dit.modeling_multi_task_dit import DiffusionObjective
|
| 66 |
+
DiffusionObjective.compute_loss = diffusion_loss
|
| 67 |
+
elif policy == "gr00t_n15":
|
| 68 |
+
from lerobot.policies.groot.action_head.flow_matching_action_head import FlowmatchingActionHead
|
| 69 |
+
original = inspect.getsource(FlowmatchingActionHead.forward)
|
| 70 |
+
needle = "loss = loss.sum() / action_mask.sum()"
|
| 71 |
+
if original.count(needle) != 1:
|
| 72 |
+
raise RuntimeError("Unknown upstream N1.5 loss implementation")
|
| 73 |
+
source = textwrap.dedent(original).replace(needle, "loss = loss.float().sum() / action_mask.sum().clamp(min=1)")
|
| 74 |
+
namespace = {}
|
| 75 |
+
exec(compile(source, "<rwh_n15_masked_forward>", "exec"), FlowmatchingActionHead.forward.__globals__, namespace)
|
| 76 |
+
FlowmatchingActionHead.forward = namespace["forward"]
|
| 77 |
+
else:
|
| 78 |
+
raise ValueError(policy)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def verify():
|
| 82 |
+
import torch
|
| 83 |
+
p = torch.randn(2, 3, 23, requires_grad=True)
|
| 84 |
+
target = torch.randn_like(p)
|
| 85 |
+
mask = torch.ones_like(p, dtype=torch.bool)
|
| 86 |
+
mask[0, 1, 20:23] = False
|
| 87 |
+
loss = masked_mean((p - target).square(), mask)
|
| 88 |
+
loss.backward()
|
| 89 |
+
assert torch.equal(p.grad[~mask], torch.zeros_like(p.grad[~mask]))
|
| 90 |
+
assert p.grad[mask].abs().sum() > 0
|
| 91 |
+
assert masked_mean(p.square(), torch.zeros_like(mask)).item() == 0
|
| 92 |
+
return {"dimension_gradient_zero": True, "valid_dimension_gradient_nonzero": True,
|
| 93 |
+
"all_masked_loss_zero": True, "mask_shape": list(mask.shape)}
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
if __name__ == "__main__":
|
| 97 |
+
print(verify())
|
| 98 |
+
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/groot_v3.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Adapt v3 row/timestamp views to the bundled native episode interface."""
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pandas as pd
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def install(view):
|
| 8 |
+
import torch
|
| 9 |
+
from PIL import Image
|
| 10 |
+
import gr00t.data.dataset.sharded_single_step_dataset as single
|
| 11 |
+
import gr00t.data.dataset.factory as factory
|
| 12 |
+
from gr00t.utils.video_utils import get_frames_by_timestamps
|
| 13 |
+
|
| 14 |
+
class EpisodeLoader:
|
| 15 |
+
def __init__(self, dataset_path, modality_configs, video_backend, video_backend_kwargs=None):
|
| 16 |
+
if Path(dataset_path).resolve()!=view.root: raise ValueError('Unexpected source dataset')
|
| 17 |
+
self.modality_configs=modality_configs
|
| 18 |
+
self.video_backend=video_backend
|
| 19 |
+
self.episode_lengths=[s['end']-s['start'] for s in view.segments]
|
| 20 |
+
def get_episode_length(self,index): return self.episode_lengths[index]
|
| 21 |
+
def __len__(self): return len(self.episode_lengths)
|
| 22 |
+
def get_initial_actions(self): return []
|
| 23 |
+
def get_dataset_statistics(self):
|
| 24 |
+
return {kind:{'body':{k:v for k,v in view.stats[feature].items() if k!='count'}}
|
| 25 |
+
for kind,feature in [('state','observation.state'),('action','action')]}
|
| 26 |
+
def __getitem__(self,index):
|
| 27 |
+
s=view.segments[index]; rows=np.arange(s['start'],s['end'])
|
| 28 |
+
df=pd.DataFrame({'state.body':list(view.state[rows]),'action.body':list(view.action[rows]),
|
| 29 |
+
'rwh.validity':list(view.validity[rows]),'rwh.anchor':rows,
|
| 30 |
+
'language.task':[view.tasks[s['task']]]*len(rows)})
|
| 31 |
+
for camera in view.cameras:
|
| 32 |
+
needed=getattr(self,'requested',{}).get(index,np.arange(len(rows)))
|
| 33 |
+
locations=[view.video_location(rows[i],camera) for i in needed]
|
| 34 |
+
if not locations:
|
| 35 |
+
df['video.'+camera]=[None]*len(rows)
|
| 36 |
+
continue
|
| 37 |
+
assert len({str(p) for p,t in locations})==1
|
| 38 |
+
frames=get_frames_by_timestamps(str(locations[0][0]),np.asarray([t for p,t in locations]),
|
| 39 |
+
video_backend=self.video_backend)
|
| 40 |
+
assert len(frames)==len(needed)
|
| 41 |
+
values=[None]*len(rows)
|
| 42 |
+
for i,frame in zip(needed,frames):values[int(i)]=Image.fromarray(np.ascontiguousarray(frame))
|
| 43 |
+
df['video.'+camera]=values
|
| 44 |
+
return df
|
| 45 |
+
|
| 46 |
+
# Scoped to this process and exact source root. Upstream files remain clean.
|
| 47 |
+
single.LeRobotEpisodeLoader=EpisodeLoader
|
| 48 |
+
original_shard=single.ShardedSingleStepDataset.get_shard
|
| 49 |
+
def shard(self,index):
|
| 50 |
+
# Keep native shard scheduling, processing and prefetch. Only v3 I/O gets
|
| 51 |
+
# the exact requested frame indices so unused full episodes aren't decoded.
|
| 52 |
+
deltas=np.asarray(self.modality_configs['video'].delta_indices)
|
| 53 |
+
requested={}
|
| 54 |
+
for ep,steps in self.sharded_episodes[index]:
|
| 55 |
+
needed=(np.asarray(steps)[:,None]+deltas[None,:]).reshape(-1)
|
| 56 |
+
requested.setdefault(int(ep),[]).extend(needed.tolist())
|
| 57 |
+
self.episode_loader.requested={k:np.unique(v).astype(int) for k,v in requested.items()}
|
| 58 |
+
try:return original_shard(self,index)
|
| 59 |
+
finally:self.episode_loader.requested={}
|
| 60 |
+
single.ShardedSingleStepDataset.get_shard=shard
|
| 61 |
+
original=single.ShardedSingleStepDataset.get_datapoint
|
| 62 |
+
def datapoint(self,episode_data,step_index):
|
| 63 |
+
encoded=original(self,episode_data,step_index)
|
| 64 |
+
indices=step_index+np.asarray(self.modality_configs['action'].delta_indices)
|
| 65 |
+
validity=np.stack(episode_data['rwh.validity'].iloc[indices])
|
| 66 |
+
mask=encoded['action_mask']
|
| 67 |
+
padded=torch.zeros_like(mask,dtype=torch.bool)
|
| 68 |
+
padded[:len(indices),:23]=torch.as_tensor(validity,device=mask.device)
|
| 69 |
+
encoded['action_mask']=mask.bool() & padded
|
| 70 |
+
return encoded
|
| 71 |
+
single.ShardedSingleStepDataset.get_datapoint=datapoint
|
| 72 |
+
def validated_stats(path,*args):
|
| 73 |
+
if Path(path).resolve()!=view.root: raise ValueError('Unexpected statistics source')
|
| 74 |
+
return view.stats
|
| 75 |
+
factory.generate_stats=validated_stats
|
| 76 |
+
factory.generate_rel_stats=validated_stats
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def modality(view,horizon):
|
| 80 |
+
from gr00t.data.types import ModalityConfig, ActionConfig, ActionRepresentation, ActionType, ActionFormat
|
| 81 |
+
return {'new_embodiment':{
|
| 82 |
+
'video':ModalityConfig(delta_indices=[0],modality_keys=view.cameras),
|
| 83 |
+
'state':ModalityConfig(delta_indices=[0],modality_keys=['body']),
|
| 84 |
+
'action':ModalityConfig(delta_indices=list(range(horizon)),modality_keys=['body'],
|
| 85 |
+
action_configs=[ActionConfig(rep=ActionRepresentation.ABSOLUTE,type=ActionType.NON_EEF,format=ActionFormat.DEFAULT)]),
|
| 86 |
+
'language':ModalityConfig(delta_indices=[0],modality_keys=['task'])}}
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/language.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Runtime compatibility fixes for this pinned LeTools revision.
|
| 2 |
+
|
| 3 |
+
Import/install this module before loading saved GR00T processors for inference.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def letterbox224(image):
|
| 8 |
+
from torchvision.transforms import functional as F
|
| 9 |
+
height, width = image.shape[-2:]
|
| 10 |
+
scale = 224 / max(height, width)
|
| 11 |
+
new_h, new_w = max(1, round(height * scale)), max(1, round(width * scale))
|
| 12 |
+
image = F.resize(image, [new_h, new_w], antialias=True)
|
| 13 |
+
left, top = (224 - new_w) // 2, (224 - new_h) // 2
|
| 14 |
+
return F.pad(image, [left, top, 224 - new_w - left, 224 - new_h - top])
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def install(policy):
|
| 18 |
+
if policy not in ("gr00t", "gr00t_n15"):
|
| 19 |
+
return
|
| 20 |
+
from lerobot.policies.groot.processor_groot import GrootPackInputsStep, GrootEagleEncodeStep
|
| 21 |
+
from lerobot.types import TransitionKey as K
|
| 22 |
+
if getattr(GrootPackInputsStep, "_rwh_prompts_fixed", False):
|
| 23 |
+
return
|
| 24 |
+
original_pack = GrootPackInputsStep.__call__
|
| 25 |
+
original_encode = GrootEagleEncodeStep.__call__
|
| 26 |
+
|
| 27 |
+
def pack(self, transition):
|
| 28 |
+
prompts = transition.get(K.COMPLEMENTARY_DATA, {}).get(self.language_key)
|
| 29 |
+
result = original_pack(self, transition)
|
| 30 |
+
if isinstance(prompts, list):
|
| 31 |
+
if self.formalize_language:
|
| 32 |
+
prompts = ["".join(c for c in p.lower() if c.isalnum() or c.isspace()) for p in prompts]
|
| 33 |
+
result[K.COMPLEMENTARY_DATA]["language"] = prompts
|
| 34 |
+
return result
|
| 35 |
+
|
| 36 |
+
def encode(self, transition):
|
| 37 |
+
obs = transition.get(K.OBSERVATION, {})
|
| 38 |
+
comp = transition.get(K.COMPLEMENTARY_DATA, {})
|
| 39 |
+
prompts = comp.get("language")
|
| 40 |
+
if not isinstance(prompts, list) or "video" not in obs:
|
| 41 |
+
return original_encode(self, transition)
|
| 42 |
+
if len(prompts) != obs["video"].shape[0]:
|
| 43 |
+
raise ValueError("One task prompt is required for every GR00T batch sample")
|
| 44 |
+
contents = []
|
| 45 |
+
for i, prompt in enumerate(prompts):
|
| 46 |
+
one = {K.OBSERVATION: {"video": obs["video"][i:i + 1]},
|
| 47 |
+
K.COMPLEMENTARY_DATA: {"language": prompt}}
|
| 48 |
+
encoded = original_encode(self, one)
|
| 49 |
+
contents.extend(encoded[K.COMPLEMENTARY_DATA]["eagle_content"])
|
| 50 |
+
comp["eagle_content"] = contents
|
| 51 |
+
return transition
|
| 52 |
+
|
| 53 |
+
GrootPackInputsStep.__call__ = pack
|
| 54 |
+
GrootPackInputsStep._rwh_prompts_fixed = True
|
| 55 |
+
GrootEagleEncodeStep.__call__ = encode
|
| 56 |
+
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Read-only source dataset views."""
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/prepare.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Read and audit the configured logical view without exporting a dataset."""
|
| 2 |
+
import argparse
|
| 3 |
+
import json
|
| 4 |
+
from scripts.data.view import DataView
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def main():
|
| 8 |
+
parser = argparse.ArgumentParser()
|
| 9 |
+
parser.add_argument('--config', required=True)
|
| 10 |
+
parser.add_argument('--audit-dir', required=True)
|
| 11 |
+
args = parser.parse_args()
|
| 12 |
+
view = DataView(args.config)
|
| 13 |
+
view.save_audit(args.audit_dir)
|
| 14 |
+
print(json.dumps(view.manifest(), indent=2))
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
if __name__ == '__main__':
|
| 18 |
+
main()
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/validate.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Exhaustive index/target checks plus sampled native video comparisons."""
|
| 2 |
+
import argparse
|
| 3 |
+
import hashlib
|
| 4 |
+
import json
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import numpy as np
|
| 7 |
+
from scripts.data.view import DataView, statistics
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def source_inventory(root):
|
| 11 |
+
return {str(p.relative_to(root)):[p.stat().st_size,p.stat().st_mtime_ns] for p in root.rglob('*') if p.is_file()}
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def main():
|
| 15 |
+
p=argparse.ArgumentParser();p.add_argument('--config',required=True);p.add_argument('--output',required=True)
|
| 16 |
+
args=p.parse_args();view=DataView(args.config)
|
| 17 |
+
inventory=source_inventory(view.root)
|
| 18 |
+
assert np.array_equal(view.action,view.raw['action'][view.rows,:23])
|
| 19 |
+
assert np.array_equal(view.validity,view.raw['validity.action'][view.rows,:23])
|
| 20 |
+
assert np.array_equal(view.state,view.raw['observation.state'][view.rows,:20])
|
| 21 |
+
assert view.validity[:,:4].all()
|
| 22 |
+
anchor_report={}
|
| 23 |
+
for policy in ['gr00t_n15','gr00t_n17','multi_task_dit']:
|
| 24 |
+
anchors=view.anchors(policy);idx=anchors[:,None]+view.offsets(policy)[None,:]
|
| 25 |
+
assert (view.episode[idx]==view.episode[anchors,None]).all()
|
| 26 |
+
assert (view.task[idx]==view.task[anchors,None]).all()
|
| 27 |
+
assert (np.diff(view.rows[idx],axis=1)==view.stride).all()
|
| 28 |
+
anchor_report[policy]={'count':len(anchors),'first':int(anchors[0]),'last':int(anchors[-1])}
|
| 29 |
+
for j in range(23):
|
| 30 |
+
actual=view.action[view.validity[:,j],j]
|
| 31 |
+
assert np.isclose(view.stats['action']['mean'][j],actual.mean(dtype=np.float64))
|
| 32 |
+
assert view.stats['action']['count'][j]==len(actual)
|
| 33 |
+
# Native LeRobot video decode is compared against an independent PyAV seek.
|
| 34 |
+
import av
|
| 35 |
+
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
| 36 |
+
native=LeRobotDataset(view.config['source']['repo_id'],root=view.root,video_backend='pyav',
|
| 37 |
+
return_uint8=True,tolerance_s=1/30+.001)
|
| 38 |
+
rng=np.random.default_rng(42)
|
| 39 |
+
chosen=set(rng.choice(len(view.rows),24,replace=False).tolist())
|
| 40 |
+
for s in view.segments[::max(1,len(view.segments)//12)]:chosen.update([s['start'],s['end']-1])
|
| 41 |
+
checks=[]
|
| 42 |
+
for i in sorted(chosen):
|
| 43 |
+
sample=native[int(view.rows[i])]
|
| 44 |
+
for camera in view.cameras:
|
| 45 |
+
path,timestamp=view.video_location(i,camera)
|
| 46 |
+
with av.open(str(path)) as container:
|
| 47 |
+
stream=container.streams.video[0];stream.thread_type='NONE';stream.codec_context.thread_count=1
|
| 48 |
+
container.seek(max(0,int(timestamp/stream.time_base)),stream=stream,backward=True)
|
| 49 |
+
decoded=[]
|
| 50 |
+
for frame in container.decode(stream):
|
| 51 |
+
decoded.append((abs(frame.time-timestamp),frame.time,frame.to_ndarray(format='rgb24')))
|
| 52 |
+
if frame.time>=timestamp:break
|
| 53 |
+
distance,pts,image=min(decoded,key=lambda x:x[0])
|
| 54 |
+
assert distance<1/60+.001,(i,camera,timestamp,pts)
|
| 55 |
+
other=sample[camera].permute(1,2,0).numpy()
|
| 56 |
+
assert np.array_equal(image,other),(i,camera,int(np.max(np.abs(image.astype(int)-other))))
|
| 57 |
+
checks.append({'row':i,'source_row':int(view.rows[i]),'camera':camera,
|
| 58 |
+
'timestamp':timestamp,'decoded_pts':pts,'sha256':hashlib.sha256(image.tobytes()).hexdigest()})
|
| 59 |
+
assert inventory==source_inventory(view.root),'Source dataset was mutated'
|
| 60 |
+
out=Path(args.output);view.save_audit(out)
|
| 61 |
+
report={'status':'passed','manifest':view.manifest(),'anchors':anchor_report,'video_checks':checks,
|
| 62 |
+
'all_selected_targets_equal_source':True,'all_anchors_task_safe':True,'source_unchanged':True}
|
| 63 |
+
(out/'data_validation.json').write_text(json.dumps(report,indent=2)+'\n')
|
| 64 |
+
print(json.dumps({**report,'video_checks':len(checks)},indent=2))
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
if __name__=='__main__':main()
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/view.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A small in-memory index over original v3 files; never exports another dataset."""
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import copy
|
| 4 |
+
import hashlib
|
| 5 |
+
import json
|
| 6 |
+
import numpy as np
|
| 7 |
+
import pyarrow.parquet as pq
|
| 8 |
+
import yaml
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def load_config(path):
|
| 12 |
+
path = Path(path).resolve()
|
| 13 |
+
cfg = yaml.safe_load(path.read_text())
|
| 14 |
+
allowed = {'source', 'sampling', 'state', 'action', 'statistics', 'video', 'adapters'}
|
| 15 |
+
if set(cfg) - allowed:
|
| 16 |
+
raise ValueError(f'Unknown data keys: {set(cfg)-allowed}')
|
| 17 |
+
if 'output' in cfg or cfg['action']['source'] != 'action':
|
| 18 |
+
raise ValueError('Expected original actions and a read-only view')
|
| 19 |
+
if cfg['state']['indices'] != [0, 20] or cfg['action']['indices'] != [0, 23]:
|
| 20 |
+
raise ValueError('Expected state20/action23')
|
| 21 |
+
if not cfg['sampling']['split_at_task_change']:
|
| 22 |
+
raise ValueError('Task boundaries are mandatory')
|
| 23 |
+
if cfg['action']['invalid'] != 'mask_loss' or cfg['statistics'] != {'state':'all_inputs', 'action':'valid_targets'}:
|
| 24 |
+
raise ValueError('Unsupported validity/statistics contract')
|
| 25 |
+
if cfg['video'] != {'mode':'original', 'alignment':'timestamp'}:
|
| 26 |
+
raise ValueError('Videos must use original timestamps')
|
| 27 |
+
if cfg['adapters'] != {'gr00t_n17':{'enabled':True,'type':'letools_native_v3'}}:
|
| 28 |
+
raise ValueError('This v3 contract requires the enabled native N1.7 adapter')
|
| 29 |
+
return cfg
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def statistics(values, mask=None):
|
| 33 |
+
result = {k: [] for k in ['mean', 'std', 'min', 'max', 'q01', 'q99', 'count']}
|
| 34 |
+
for d in range(values.shape[1]):
|
| 35 |
+
a = values[:, d] if mask is None else values[mask[:, d], d]
|
| 36 |
+
if not len(a) or not np.isfinite(a).all():
|
| 37 |
+
raise ValueError(f'No finite valid statistics for dimension {d}')
|
| 38 |
+
for k, v in zip(result, [a.mean(dtype=np.float64), a.std(dtype=np.float64), a.min(), a.max(),
|
| 39 |
+
np.quantile(a, .01), np.quantile(a, .99), len(a)]):
|
| 40 |
+
result[k].append(float(v) if k != 'count' else int(v))
|
| 41 |
+
return result
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class DataView:
|
| 45 |
+
def __init__(self, config_path):
|
| 46 |
+
self.config_path = str(Path(config_path).resolve())
|
| 47 |
+
self.config = load_config(config_path)
|
| 48 |
+
self.root = Path(self.config['source']['root']).resolve()
|
| 49 |
+
self.info = json.loads((self.root/'meta/info.json').read_text())
|
| 50 |
+
assert self.info['codebase_version'].startswith('v3')
|
| 51 |
+
sf, fps = self.config['sampling']['source_fps'], self.config['sampling']['fps']
|
| 52 |
+
if self.info['fps'] != sf or sf % fps:
|
| 53 |
+
raise ValueError('This adapter requires an exact integer FPS stride')
|
| 54 |
+
self.stride, self.fps = sf // fps, fps
|
| 55 |
+
if self.stride < 1: raise ValueError('Cannot upsample')
|
| 56 |
+
files = sorted((self.root/'data').rglob('*.parquet'))
|
| 57 |
+
table = pq.read_table(files)
|
| 58 |
+
order = np.argsort(table['index'].to_numpy())
|
| 59 |
+
self.raw = {k: np.asarray(table[k].to_pylist())[order] for k in
|
| 60 |
+
['index', 'episode_index', 'frame_index', 'timestamp', 'task_index',
|
| 61 |
+
self.config['state']['source'], 'action', self.config['action']['validity_feature']]}
|
| 62 |
+
assert np.array_equal(self.raw['index'], np.arange(len(order)))
|
| 63 |
+
self.episode_meta = {int(r['episode_index']): r for r in
|
| 64 |
+
pq.read_table(sorted((self.root/'meta/episodes').rglob('*.parquet'))).to_pylist()}
|
| 65 |
+
tasks = pq.read_table(self.root/'meta/tasks.parquet').to_pandas()
|
| 66 |
+
self.tasks = {int(row.task_index): str(label) for label, row in tasks.iterrows()}
|
| 67 |
+
self.cameras = [k for k,v in self.info['features'].items() if v['dtype']=='video']
|
| 68 |
+
self.segments, selection = [], []
|
| 69 |
+
for ep in sorted(self.episode_meta):
|
| 70 |
+
rows = np.flatnonzero(self.raw['episode_index']==ep)
|
| 71 |
+
assert np.all(np.diff(rows)==1)
|
| 72 |
+
task = self.raw['task_index'][rows]
|
| 73 |
+
edges = np.r_[0, np.flatnonzero(task[1:]!=task[:-1])+1, len(rows)]
|
| 74 |
+
selected = rows[::self.stride]
|
| 75 |
+
for lo, hi in zip(edges[:-1], edges[1:]):
|
| 76 |
+
chosen = selected[(selected>=rows[lo]) & (selected<rows[hi-1]+1)]
|
| 77 |
+
if not len(chosen): continue
|
| 78 |
+
start = len(selection); selection.extend(chosen.tolist())
|
| 79 |
+
self.segments.append(dict(start=start, end=len(selection), source_episode=ep,
|
| 80 |
+
source_from=int(rows[lo]), source_to=int(rows[hi-1])+1,
|
| 81 |
+
task=int(task[lo])))
|
| 82 |
+
self.rows = np.asarray(selection, dtype=np.int64)
|
| 83 |
+
self.state = self.raw[self.config['state']['source']][self.rows, :20].astype(np.float32)
|
| 84 |
+
self.action = self.raw['action'][self.rows, :23].astype(np.float32)
|
| 85 |
+
self.validity = self.raw[self.config['action']['validity_feature']][self.rows,:23].astype(bool)
|
| 86 |
+
self.task = self.raw['task_index'][self.rows].astype(np.int64)
|
| 87 |
+
self.episode = np.empty(len(self.rows), dtype=np.int64)
|
| 88 |
+
for i,s in enumerate(self.segments): self.episode[s['start']:s['end']] = i
|
| 89 |
+
assert np.isfinite(self.state).all() and np.isfinite(self.action).all()
|
| 90 |
+
self.stats = {'observation.state': statistics(self.state), 'action': statistics(self.action,self.validity)}
|
| 91 |
+
signature = [(str(p.relative_to(self.root)),p.stat().st_size,p.stat().st_mtime_ns) for p in files]
|
| 92 |
+
self.fingerprint = hashlib.sha256(json.dumps([self.config,signature],sort_keys=True).encode()).hexdigest()
|
| 93 |
+
|
| 94 |
+
def anchors(self, policy):
|
| 95 |
+
history, future = {'gr00t_n15':(0,15),'gr00t_n17':(0,39),'multi_task_dit':(1,30)}[policy]
|
| 96 |
+
return np.concatenate([np.arange(s['start']+history,s['end']-future,dtype=np.int64)
|
| 97 |
+
for s in self.segments if s['end']-s['start']>history+future])
|
| 98 |
+
|
| 99 |
+
def offsets(self, policy):
|
| 100 |
+
return np.arange(-1,31) if policy=='multi_task_dit' else np.arange(40 if policy=='gr00t_n17' else 16)
|
| 101 |
+
|
| 102 |
+
def video_location(self, row, camera):
|
| 103 |
+
raw = int(self.rows[row]); ep = int(self.raw['episode_index'][raw]); meta=self.episode_meta[ep]
|
| 104 |
+
prefix='videos/'+camera+'/'
|
| 105 |
+
path=self.root/self.info['video_path'].format(video_key=camera,
|
| 106 |
+
chunk_index=int(meta[prefix+'chunk_index']),file_index=int(meta[prefix+'file_index']))
|
| 107 |
+
return path, float(meta[prefix+'from_timestamp'])+float(self.raw['timestamp'][raw])
|
| 108 |
+
|
| 109 |
+
def manifest(self):
|
| 110 |
+
return dict(fingerprint=self.fingerprint, config=self.config, source_rows=len(self.raw['index']),
|
| 111 |
+
selected_rows=len(self.rows), task_episodes=len(self.segments),
|
| 112 |
+
eligible_anchors={p:len(self.anchors(p)) for p in ['gr00t_n15','gr00t_n17','multi_task_dit']},
|
| 113 |
+
invalid_action_elements=int((~self.validity).sum()), original_torso=True)
|
| 114 |
+
|
| 115 |
+
def save_audit(self, directory):
|
| 116 |
+
directory=Path(directory); directory.mkdir(parents=True,exist_ok=True)
|
| 117 |
+
(directory/'data_manifest.json').write_text(json.dumps(self.manifest(),indent=2)+'\n')
|
| 118 |
+
(directory/'statistics.json').write_text(json.dumps(self.stats,indent=2)+'\n')
|
| 119 |
+
np.savez(directory/'indices.npz',source_rows=self.rows,logical_episode=self.episode)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def lerobot_dataset(view, policy):
|
| 123 |
+
import torch
|
| 124 |
+
from lerobot.datasets.lerobot_dataset import LeRobotDataset
|
| 125 |
+
from lerobot.utils.constants import IMAGENET_STATS
|
| 126 |
+
from scripts.adapters.language import letterbox224
|
| 127 |
+
native=LeRobotDataset(view.config['source']['repo_id'],root=view.root,video_backend='pyav',
|
| 128 |
+
return_uint8=True,tolerance_s=1/30+.001)
|
| 129 |
+
meta=copy.copy(native.meta)
|
| 130 |
+
meta.info=copy.deepcopy(meta.info)
|
| 131 |
+
meta.info.update(fps=view.fps,total_frames=len(view.rows),total_episodes=len(view.segments))
|
| 132 |
+
meta.info['features']={k:copy.deepcopy(view.info['features'][k]) for k in
|
| 133 |
+
[*view.cameras,'observation.state','action']}
|
| 134 |
+
meta.info['features']['observation.state']['shape']=[20]
|
| 135 |
+
meta.info['features']['observation.state']['names']=view.info['features']['observation.state']['names'][:20]
|
| 136 |
+
from datasets import Dataset
|
| 137 |
+
meta.episodes=Dataset.from_list([{'episode_index':i,'length':s['end']-s['start'],
|
| 138 |
+
'dataset_from_index':s['start'],'dataset_to_index':s['end'],
|
| 139 |
+
'tasks':[view.tasks[s['task']]]} for i,s in enumerate(view.segments)])
|
| 140 |
+
for camera in view.cameras: meta.info['features'][camera]['shape']=[224,224,3]
|
| 141 |
+
meta.stats={k:{s:torch.tensor(v) for s,v in stats.items()} for k,stats in view.stats.items()}
|
| 142 |
+
for camera in view.cameras:
|
| 143 |
+
meta.stats[camera]={k:torch.tensor(v,dtype=torch.float32) for k,v in IMAGENET_STATS.items()}
|
| 144 |
+
class ViewedDataset(torch.utils.data.Dataset):
|
| 145 |
+
def __init__(self):
|
| 146 |
+
self.meta=meta; self.root=view.root; self.episodes=None
|
| 147 |
+
self.num_frames=len(view.rows); self.num_episodes=len(view.segments)
|
| 148 |
+
self.features=meta.features; self.fps=view.fps; self.repo_id=native.repo_id
|
| 149 |
+
def __len__(self): return self.num_frames
|
| 150 |
+
def __getitem__(self,index):
|
| 151 |
+
index=int(index); offsets=view.offsets(policy); seq=index+offsets
|
| 152 |
+
if seq.min()<0 or seq.max()>=len(view.rows) or np.any(view.episode[seq]!=view.episode[index]):
|
| 153 |
+
raise ValueError('Sampling crossed a task boundary')
|
| 154 |
+
obs_indices=[index-1,index] if policy=='multi_task_dit' else [index]
|
| 155 |
+
frames=[native[int(view.rows[i])] for i in obs_indices]
|
| 156 |
+
result={k:torch.stack([letterbox224(f[k]) for f in frames]) for k in view.cameras}
|
| 157 |
+
states=torch.tensor(view.state[obs_indices])
|
| 158 |
+
if policy!='multi_task_dit':
|
| 159 |
+
result={k:v[0] for k,v in result.items()}; states=states[0]
|
| 160 |
+
result.update({'observation.state':states,'action':torch.tensor(view.action[seq]),
|
| 161 |
+
'action_is_pad':torch.zeros(len(seq),dtype=torch.bool), 'index':torch.tensor(index),
|
| 162 |
+
'episode_index':torch.tensor(view.episode[index]), 'task_index':torch.tensor(view.task[index]),
|
| 163 |
+
'timestamp':torch.tensor(index/view.fps,dtype=torch.float32),
|
| 164 |
+
'task':view.tasks[int(view.task[index])]})
|
| 165 |
+
return result
|
| 166 |
+
return ViewedDataset()
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/tests/test_contract.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CPU tests for missing targets, batch masks and the first-finisher queue."""
|
| 2 |
+
import importlib
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import tempfile
|
| 7 |
+
import unittest
|
| 8 |
+
from unittest.mock import patch
|
| 9 |
+
|
| 10 |
+
class Contract(unittest.TestCase):
|
| 11 |
+
def test_masked_targets_and_all_masked_gradients(self):
|
| 12 |
+
import torch
|
| 13 |
+
from scripts.adapters.action_validity import masked_mean
|
| 14 |
+
p=torch.randn(3,40,132,requires_grad=True)
|
| 15 |
+
mask=torch.zeros_like(p,dtype=torch.bool);mask[...,:23]=True;mask[1,7,20:23]=False
|
| 16 |
+
a=p.square();b=a.clone();b[~mask]=1e8
|
| 17 |
+
torch.testing.assert_close(masked_mean(a,mask),masked_mean(b,mask))
|
| 18 |
+
masked_mean(b,mask).backward()
|
| 19 |
+
self.assertTrue((p.grad[~mask]==0).all());self.assertGreater(p.grad[mask].abs().sum(),0)
|
| 20 |
+
p.grad=None;loss=masked_mean(p.square(),torch.zeros_like(mask));loss.backward()
|
| 21 |
+
self.assertEqual(float(loss.detach()),0);self.assertTrue((p.grad==0).all())
|
| 22 |
+
|
| 23 |
+
def test_real_source_mask_offsets(self):
|
| 24 |
+
import numpy as np
|
| 25 |
+
import torch
|
| 26 |
+
from scripts.data.view import DataView
|
| 27 |
+
from scripts.adapters.action_validity import MaskBank
|
| 28 |
+
v=DataView(os.environ['RWH_DATA_CONFIG'])
|
| 29 |
+
for policy in ['gr00t_n15','multi_task_dit']:
|
| 30 |
+
anchors=v.anchors(policy);seq=anchors[:,None]+v.offsets(policy)[None,:]
|
| 31 |
+
bad=np.flatnonzero((~v.validity[seq]).any(axis=(1,2)))
|
| 32 |
+
self.assertGreater(len(bad),0)
|
| 33 |
+
selected=anchors[np.resize(bad,128)];expected=torch.tensor(v.raw['validity.action'][v.rows[selected[:,None]+v.offsets(policy)],:23])
|
| 34 |
+
processed={'action_mask':torch.ones(128,16,32,dtype=torch.bool)} if policy=='gr00t_n15' else {'action':torch.zeros(128,32,23)}
|
| 35 |
+
actual=MaskBank(v,policy).attach(processed,{'index':torch.tensor(selected)})
|
| 36 |
+
mask=actual['action_mask'] if policy=='gr00t_n15' else actual['rwh_action_validity']
|
| 37 |
+
self.assertTrue(torch.equal(mask[...,:23],expected))
|
| 38 |
+
self.assertFalse(mask[...,23:].any())
|
| 39 |
+
|
| 40 |
+
def test_first_finisher_starts_dit_once(self):
|
| 41 |
+
with tempfile.TemporaryDirectory() as td:
|
| 42 |
+
out=Path(td);os.environ.setdefault('RWH_TRAIN_CONFIG_DIR',td)
|
| 43 |
+
module=importlib.import_module('scripts.train.orchestrate')
|
| 44 |
+
policies=['gr00t_n15','gr00t_n17','multi_task_dit']
|
| 45 |
+
for policy in policies:
|
| 46 |
+
p=out/'_validation'/policy/'result.json';p.parent.mkdir(parents=True)
|
| 47 |
+
p.write_text(json.dumps({'status':'passed','masked_gradient_checks':3,'batch_size':128,'gradient_accumulation_steps':1}))
|
| 48 |
+
p=out/'_audit'/f'reload_{policy}.json';p.parent.mkdir(parents=True,exist_ok=True)
|
| 49 |
+
p.write_text(json.dumps({'status':'passed','separate_process_reload':True}))
|
| 50 |
+
(out/'_audit/data_validation.json').write_text('{"status":"passed"}')
|
| 51 |
+
launched=[]
|
| 52 |
+
class Child:
|
| 53 |
+
pid=999
|
| 54 |
+
def poll(self):return 0
|
| 55 |
+
def launch(command,env,**kw):
|
| 56 |
+
policy=command[2];gpu=int(command[4]);launched.append((policy,gpu))
|
| 57 |
+
p=Path(env['RWH_RESULT_DIR'])/'result.json'
|
| 58 |
+
p.write_text(json.dumps({'status':'passed','completed_at':10 if policy=='gr00t_n17' else 20}))
|
| 59 |
+
return Child()
|
| 60 |
+
with patch.object(module,'OUTPUT',out),patch.object(module.subprocess,'Popen',launch),patch.object(module.time,'sleep',lambda _:None):
|
| 61 |
+
module.main()
|
| 62 |
+
self.assertEqual(launched,[('gr00t_n15',0),('gr00t_n17',1),('multi_task_dit',1)])
|
| 63 |
+
self.assertEqual(json.loads((out/'_runs/controller_status.json').read_text())['state'],'complete')
|
| 64 |
+
|
| 65 |
+
if __name__=='__main__':unittest.main()
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""LeTools training launchers."""
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/checkpoints.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Validate the native Transformers checkpoint layout, including weight shards."""
|
| 2 |
+
import json
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def weight_files(model_dir):
|
| 7 |
+
root=Path(model_dir)
|
| 8 |
+
index=root/'model.safetensors.index.json'
|
| 9 |
+
if index.exists():
|
| 10 |
+
mapping=json.loads(index.read_text())['weight_map']
|
| 11 |
+
names=sorted(set(mapping.values()))
|
| 12 |
+
if not names or any(Path(n).name!=n for n in names):raise ValueError('Unsafe/empty weight index')
|
| 13 |
+
files=[root/n for n in names]
|
| 14 |
+
if not all(p.is_file() and p.stat().st_size>0 for p in files):raise ValueError('Missing model shard')
|
| 15 |
+
from safetensors import safe_open
|
| 16 |
+
for path in files:
|
| 17 |
+
with safe_open(str(path),framework='pt',device='cpu') as sf:
|
| 18 |
+
if set(sf.keys())!={key for key,name in mapping.items() if name==path.name}:
|
| 19 |
+
raise ValueError('Weight index and shard keys differ: '+path.name)
|
| 20 |
+
else:
|
| 21 |
+
files=[root/'model.safetensors']
|
| 22 |
+
if not files[0].is_file():raise ValueError('No safetensors weights or shard index')
|
| 23 |
+
return files
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def validate_native_checkpoint(path,step):
|
| 27 |
+
path=Path(path)
|
| 28 |
+
for name in ['config.json','processor_config.json','statistics.json','embodiment_id.json','trainer_state.json']:
|
| 29 |
+
if not (path/name).exists():
|
| 30 |
+
raise ValueError('Missing native checkpoint file: '+name)
|
| 31 |
+
processor=json.loads((path/'processor_config.json').read_text())
|
| 32 |
+
if not processor['processor_kwargs']['modality_configs']:raise ValueError('Missing saved modalities')
|
| 33 |
+
state=json.loads((path/'trainer_state.json').read_text())
|
| 34 |
+
if state['global_step']!=step:raise ValueError('Wrong native saved step')
|
| 35 |
+
return {'step':step,'weight_files':[p.name for p in weight_files(path)]}
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/hf_watcher.py
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Publish verified 10k/20k/30k checkpoints from successful Kuavo runs.
|
| 2 |
+
|
| 3 |
+
CPU only. Never modifies a trainer checkpoint, last symlink, or optimizer state.
|
| 4 |
+
All paths/revisions are pinned in hf_watcher.json before starting this process.
|
| 5 |
+
"""
|
| 6 |
+
from __future__ import annotations
|
| 7 |
+
|
| 8 |
+
import argparse
|
| 9 |
+
import fcntl
|
| 10 |
+
import hashlib
|
| 11 |
+
import json
|
| 12 |
+
import math
|
| 13 |
+
import os
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
import time
|
| 16 |
+
|
| 17 |
+
POLICIES = ("multi_task_dit", "gr00t_n15", "gr00t_n17")
|
| 18 |
+
STEPS = (10000, 20000, 30000)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def checkpoint_plan(cfg):
|
| 22 |
+
configured = cfg.get("steps_by_policy", {p: list(STEPS) for p in POLICIES})
|
| 23 |
+
if set(configured) != set(POLICIES):
|
| 24 |
+
raise ValueError("Expected checkpoint steps for all three policies")
|
| 25 |
+
for policy, steps in configured.items():
|
| 26 |
+
if not steps or len(steps) != len(set(steps)) or any(s not in STEPS for s in steps):
|
| 27 |
+
raise ValueError("Invalid checkpoint steps: " + policy)
|
| 28 |
+
return [(policy, step) for step in STEPS for policy in POLICIES if step in configured[policy]]
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def remote_prefix(cfg, policy, step):
|
| 32 |
+
root = cfg["remote_root"]
|
| 33 |
+
if root != "v2":
|
| 34 |
+
raise ValueError("The approved HF experiment root is v2")
|
| 35 |
+
return f"{root}/{policy}/checkpoints/checkpoint-{step // 1000}K"
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class NotReady(Exception):
|
| 39 |
+
pass
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def read(path):
|
| 43 |
+
return json.loads(Path(path).read_text())
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def atomic_json(path, value):
|
| 47 |
+
path = Path(path)
|
| 48 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 49 |
+
temporary = path.with_suffix(path.suffix + ".tmp")
|
| 50 |
+
temporary.write_text(json.dumps(value, indent=2, ensure_ascii=False) + "\n")
|
| 51 |
+
temporary.replace(path)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def signature(root):
|
| 55 |
+
"""A stable native snapshot includes training_state, even though it is not uploaded."""
|
| 56 |
+
values = {}
|
| 57 |
+
for f in sorted(root.rglob("*")):
|
| 58 |
+
if f.is_symlink():
|
| 59 |
+
raise ValueError("Checkpoint contains an unexpected symlink")
|
| 60 |
+
if f.is_file():
|
| 61 |
+
s = f.stat()
|
| 62 |
+
values[str(f.relative_to(root))] = [s.st_size, s.st_mtime_ns]
|
| 63 |
+
return values
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def completed_source(cfg, policy, step):
|
| 67 |
+
output = Path(cfg["output_root"]) / policy
|
| 68 |
+
result = read(cfg["results"][policy])
|
| 69 |
+
if result.get("status") != "passed":
|
| 70 |
+
raise NotReady("Waiting for successful final training and inference verification")
|
| 71 |
+
if result["policy"] != policy or result["smoke"] or result["steps_requested"] != 30000:
|
| 72 |
+
raise ValueError("Unexpected production identity")
|
| 73 |
+
if Path(result["output_dir"]) != output:
|
| 74 |
+
raise ValueError("Wrong production output")
|
| 75 |
+
final = Path(result["checkpoint"])
|
| 76 |
+
expected_final = output / ("checkpoint-30000" if policy == "gr00t_n17" else "checkpoints/030000")
|
| 77 |
+
if final.resolve() != expected_final.resolve():
|
| 78 |
+
raise ValueError("Unexpected final checkpoint location")
|
| 79 |
+
checkpoint = output / (f"checkpoint-{step}" if policy == "gr00t_n17" else f"checkpoints/{step:06d}")
|
| 80 |
+
if checkpoint.resolve().parent not in [output.resolve(), (output/"checkpoints").resolve()]:
|
| 81 |
+
raise ValueError("Checkpoint outside this run")
|
| 82 |
+
if policy == "gr00t_n17":
|
| 83 |
+
if read(final/"rwh_save_complete.json")["step"] != 30000:
|
| 84 |
+
raise ValueError("Native save marker step mismatch")
|
| 85 |
+
from scripts.train.checkpoints import validate_native_checkpoint
|
| 86 |
+
validate_native_checkpoint(checkpoint, step)
|
| 87 |
+
model = checkpoint
|
| 88 |
+
required = ["config.json", "processor_config.json", "statistics.json", "embodiment_id.json"]
|
| 89 |
+
else:
|
| 90 |
+
if read(checkpoint/"training_state/training_step.json")["step"] != step:
|
| 91 |
+
raise ValueError("LeRobot training step mismatch")
|
| 92 |
+
model = checkpoint/"pretrained_model"
|
| 93 |
+
required = ["config.json", "train_config.json", "policy_preprocessor.json", "policy_postprocessor.json", "model.safetensors"]
|
| 94 |
+
train = read(model/"train_config.json")
|
| 95 |
+
for key,value in {"steps":30000,"batch_size":128,"seed":42,"save_freq":10000}.items():
|
| 96 |
+
if train[key] != value: raise ValueError("Wrong training contract: "+key)
|
| 97 |
+
for name in required:
|
| 98 |
+
if not (model/name).is_file(): raise ValueError("Missing checkpoint file: "+name)
|
| 99 |
+
from scripts.train.checkpoints import weight_files
|
| 100 |
+
weight_files(model)
|
| 101 |
+
snapshot=signature(checkpoint)
|
| 102 |
+
newest=max(mtime for size,mtime in snapshot.values())/1e9
|
| 103 |
+
if time.time()-newest < cfg["stable_seconds"]: raise NotReady("Waiting for stability interval")
|
| 104 |
+
return checkpoint,snapshot
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def file_info(path):
|
| 108 |
+
size = path.stat().st_size
|
| 109 |
+
sha256 = hashlib.sha256()
|
| 110 |
+
blob = hashlib.sha1(f"blob {size}\0".encode())
|
| 111 |
+
with path.open("rb") as stream:
|
| 112 |
+
for chunk in iter(lambda: stream.read(8 * 1024 * 1024), b""):
|
| 113 |
+
sha256.update(chunk)
|
| 114 |
+
blob.update(chunk)
|
| 115 |
+
return {"size": size, "sha256": sha256.hexdigest(), "git_blob_sha1": blob.hexdigest()}
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def validate_tensors(model):
|
| 119 |
+
import torch
|
| 120 |
+
from safetensors import safe_open
|
| 121 |
+
torch.set_num_threads(2)
|
| 122 |
+
count = 0
|
| 123 |
+
for file in sorted(model.glob("*.safetensors")):
|
| 124 |
+
with safe_open(str(file), framework="pt", device="cpu") as tensors:
|
| 125 |
+
for name in tensors.keys():
|
| 126 |
+
tensor = tensors.get_slice(name)
|
| 127 |
+
shape = tensor.get_shape()
|
| 128 |
+
if not shape:
|
| 129 |
+
chunks = [tensors.get_tensor(name)]
|
| 130 |
+
else:
|
| 131 |
+
rows = max(1, 8_000_000 // max(1, math.prod(shape[1:])))
|
| 132 |
+
chunks = (tensor[i:i + rows] for i in range(0, shape[0], rows))
|
| 133 |
+
for chunk in chunks:
|
| 134 |
+
if not torch.isfinite(chunk).all().item():
|
| 135 |
+
raise ValueError("Nonfinite saved tensor: " + name)
|
| 136 |
+
count += 1
|
| 137 |
+
if not count:
|
| 138 |
+
raise ValueError("No saved tensors")
|
| 139 |
+
return count
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def compare_remote(api, repo, prefix, revision, files):
|
| 143 |
+
"""LFS SHA256 or Git object SHA1 proves actual content, beyond size equality."""
|
| 144 |
+
from huggingface_hub.utils import EntryNotFoundError
|
| 145 |
+
try:
|
| 146 |
+
entries = list(api.list_repo_tree(repo, path_in_repo=prefix, revision=revision,
|
| 147 |
+
repo_type="model", recursive=True))
|
| 148 |
+
except EntryNotFoundError:
|
| 149 |
+
entries = []
|
| 150 |
+
remote = {entry.path[len(prefix) + 1:]: entry for entry in entries if hasattr(entry, "size")}
|
| 151 |
+
missing = sorted(set(files) - set(remote))
|
| 152 |
+
extra = sorted(set(remote) - set(files))
|
| 153 |
+
mismatch = []
|
| 154 |
+
for name in sorted(set(files) & set(remote)):
|
| 155 |
+
expected, actual = files[name], remote[name]
|
| 156 |
+
lfs = actual.lfs
|
| 157 |
+
remote_hash = lfs.sha256 if lfs is not None else actual.blob_id
|
| 158 |
+
expected_hash = expected["sha256"] if lfs is not None else expected["git_blob_sha1"]
|
| 159 |
+
if actual.size != expected["size"] or remote_hash != expected_hash:
|
| 160 |
+
mismatch.append(name)
|
| 161 |
+
return {"missing": missing, "extra": extra, "mismatch": mismatch,
|
| 162 |
+
"file_count": len(remote), "bytes": sum(x.size for x in remote.values())}
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def matches(check):
|
| 166 |
+
return not any(check[k] for k in ("missing", "extra", "mismatch"))
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
def payload(cfg, policy, step, checkpoint, snapshot):
|
| 170 |
+
prefix = remote_prefix(cfg, policy, step)
|
| 171 |
+
work=Path(cfg["state_dir"])/"payloads"/cfg["remote_root"]/policy/str(step)
|
| 172 |
+
manifest_path=work/"checkpoint_manifest.json"
|
| 173 |
+
model=checkpoint if policy=="gr00t_n17" else checkpoint/"pretrained_model"
|
| 174 |
+
def included(f):
|
| 175 |
+
rel=f.relative_to(checkpoint)
|
| 176 |
+
return not any(x in ("training_state",) for x in rel.parts) and not f.name.startswith(("optimizer", "scheduler", "rng_state"))
|
| 177 |
+
if manifest_path.exists():
|
| 178 |
+
manifest=read(manifest_path)
|
| 179 |
+
if manifest["source_signature"] != snapshot or manifest["remote_prefix"] != prefix:
|
| 180 |
+
raise ValueError("Saved checkpoint or destination changed")
|
| 181 |
+
else:
|
| 182 |
+
count=validate_tensors(model)
|
| 183 |
+
provenance=work/"provenance";provenance.mkdir(parents=True,exist_ok=True)
|
| 184 |
+
for source,target in cfg["provenance"][policy].items():
|
| 185 |
+
dest=provenance/target;dest.parent.mkdir(parents=True,exist_ok=True)
|
| 186 |
+
dest.write_bytes(Path(source).read_bytes())
|
| 187 |
+
(work/"README.md").write_text(
|
| 188 |
+
f"# {policy}: original-action Kuavo, checkpoint {step} of 30000 updates\n\n"
|
| 189 |
+
"Original v3 data; 30Hz to 10Hz logical view; state20/action23; seed42; actual batch128; accumulation1.\n"
|
| 190 |
+
"Torso targets are the original action commands. Invalid action elements are masked.\n"
|
| 191 |
+
"N1.5/DiT use LeTools-pinned LeRobot; N1.7 uses bundled native model, processor, sharded loader and Trainer.\n"
|
| 192 |
+
"LeTools revision: 6d2002613241ae2a32c5d8fd753f80caa1383fba. See provenance for adapter and YAML.\n"
|
| 193 |
+
"N1.7 uses native checkpoint layout. Optimizer/RNG states stay on the training server.\n")
|
| 194 |
+
paths={str(f.relative_to(checkpoint)):f for f in checkpoint.rglob("*") if f.is_file() and included(f)}
|
| 195 |
+
paths.update({str(f.relative_to(work)):f for f in work.rglob("*") if f.is_file()})
|
| 196 |
+
manifest={"schema":2,"policy":policy,"step":step,"repo_id":cfg["repo_id"],
|
| 197 |
+
"remote_prefix":prefix,
|
| 198 |
+
"source_checkpoint":str(checkpoint),"source_signature":snapshot,"tensor_count":count,
|
| 199 |
+
"training_state_included":False,"files":{n:file_info(f) for n,f in sorted(paths.items())}}
|
| 200 |
+
if signature(checkpoint)!=snapshot: raise ValueError("Checkpoint changed during validation")
|
| 201 |
+
atomic_json(manifest_path,manifest)
|
| 202 |
+
paths={name:work/name if name.startswith("provenance/") or name=="README.md" else checkpoint/name
|
| 203 |
+
for name in manifest["files"]}
|
| 204 |
+
paths["checkpoint_manifest.json"]=manifest_path
|
| 205 |
+
files=dict(manifest["files"]);files["checkpoint_manifest.json"]=file_info(manifest_path)
|
| 206 |
+
return manifest,paths,files
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def publish(api, cfg, policy, step):
|
| 210 |
+
from huggingface_hub import CommitOperationAdd
|
| 211 |
+
checkpoint, snapshot = completed_source(cfg, policy, step)
|
| 212 |
+
manifest, paths, files = payload(cfg, policy, step, checkpoint, snapshot)
|
| 213 |
+
prefix, repo = manifest["remote_prefix"], cfg["repo_id"]
|
| 214 |
+
print(json.dumps({"mapping": {"local": str(checkpoint), "HF": repo + "/" + prefix},
|
| 215 |
+
"step": step, "files": len(files)}), flush=True)
|
| 216 |
+
head = api.model_info(repo).sha
|
| 217 |
+
before = compare_remote(api, repo, prefix, head, files)
|
| 218 |
+
if before["extra"] or before["mismatch"]:
|
| 219 |
+
raise ValueError("Existing HF checkpoint conflicts with the validated local manifest")
|
| 220 |
+
uploaded = False
|
| 221 |
+
if before["missing"]:
|
| 222 |
+
if signature(checkpoint) != snapshot:
|
| 223 |
+
raise ValueError("Checkpoint changed before upload")
|
| 224 |
+
commit = api.create_commit(
|
| 225 |
+
repo, repo_type="model", parent_commit=head, num_threads=2,
|
| 226 |
+
operations=[CommitOperationAdd(path_in_repo=prefix + "/" + name,
|
| 227 |
+
path_or_fileobj=str(paths[name])) for name in before["missing"]],
|
| 228 |
+
commit_message=f"Kuavo original action: {policy} checkpoint {step // 1000}K")
|
| 229 |
+
head = commit.oid
|
| 230 |
+
uploaded = True
|
| 231 |
+
check = compare_remote(api, repo, prefix, head, files)
|
| 232 |
+
if not matches(check):
|
| 233 |
+
raise ValueError("Remote manifest verification failed: " + json.dumps(check))
|
| 234 |
+
if signature(checkpoint) != snapshot:
|
| 235 |
+
raise ValueError("Source changed during upload; do not mark verified")
|
| 236 |
+
return {"status": "verified", "policy": policy, "step": step, "revision": head,
|
| 237 |
+
"prefix": prefix, "uploaded_this_attempt": uploaded, "verification": check,
|
| 238 |
+
"verified_at": time.time(), "manifest": str(Path(cfg["state_dir"]) / "payloads" /
|
| 239 |
+
cfg["remote_root"] / policy / str(step) / "checkpoint_manifest.json")}
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def once(api, cfg, state, selected=None, selected_step=None, audit=False):
|
| 243 |
+
plan = checkpoint_plan(cfg)
|
| 244 |
+
for step in STEPS:
|
| 245 |
+
for policy in POLICIES:
|
| 246 |
+
if (policy, step) not in plan:
|
| 247 |
+
continue
|
| 248 |
+
if selected and policy != selected or selected_step and step != selected_step:
|
| 249 |
+
continue
|
| 250 |
+
key = f"{policy}/{step}"
|
| 251 |
+
prior = state["checkpoints"].get(key, {})
|
| 252 |
+
same_destination = prior.get("prefix") == remote_prefix(cfg, policy, step)
|
| 253 |
+
if prior.get("status") == "verified" and same_destination and not audit:
|
| 254 |
+
continue
|
| 255 |
+
if prior.get("retry_after", 0) > time.time() and same_destination and not audit:
|
| 256 |
+
continue
|
| 257 |
+
state.update(current=key, heartbeat=time.time(), status="running")
|
| 258 |
+
atomic_json(Path(cfg["state_dir"]) / "status.json", state)
|
| 259 |
+
try:
|
| 260 |
+
record = publish(api, cfg, policy, step)
|
| 261 |
+
except (NotReady, FileNotFoundError, json.JSONDecodeError) as exc:
|
| 262 |
+
record = {"status": "waiting", "reason": str(exc), "checked_at": time.time()}
|
| 263 |
+
except Exception as exc:
|
| 264 |
+
# Never serialize tokens/headers or HTTP exception payloads.
|
| 265 |
+
attempts = prior.get("attempts", 0) + 1
|
| 266 |
+
record = {"status": "error", "error_type": type(exc).__name__,
|
| 267 |
+
"reason": str(exc)[:500] if isinstance(exc, ValueError) else "See service type; retry scheduled",
|
| 268 |
+
"attempts": attempts, "retry_after": time.time() + min(1800, 60 * 2 ** min(attempts, 5))}
|
| 269 |
+
record["prefix"] = remote_prefix(cfg, policy, step)
|
| 270 |
+
state["checkpoints"][key] = record
|
| 271 |
+
state["heartbeat"] = time.time()
|
| 272 |
+
atomic_json(Path(cfg["state_dir"]) / "status.json", state)
|
| 273 |
+
print(json.dumps({"checkpoint": key, **record}), flush=True)
|
| 274 |
+
state["current"] = None
|
| 275 |
+
records = [state["checkpoints"].get(f"{p}/{s}", {}) for p, s in plan]
|
| 276 |
+
state["expected_checkpoints"] = len(plan)
|
| 277 |
+
state["verified_count"] = sum(x.get("status") == "verified" and
|
| 278 |
+
x.get("prefix") == remote_prefix(cfg, p, s) for (p, s), x in zip(plan, records))
|
| 279 |
+
if state["verified_count"] == len(plan):
|
| 280 |
+
# Recheck the entire set against ONE current HF revision before declaring
|
| 281 |
+
# completion, including checkpoints uploaded many hours earlier.
|
| 282 |
+
try:
|
| 283 |
+
revision = api.model_info(cfg["repo_id"]).sha
|
| 284 |
+
for record in records:
|
| 285 |
+
manifest_path = Path(record["manifest"])
|
| 286 |
+
manifest = read(manifest_path)
|
| 287 |
+
files = dict(manifest["files"])
|
| 288 |
+
files["checkpoint_manifest.json"] = file_info(manifest_path)
|
| 289 |
+
check = compare_remote(api, cfg["repo_id"], record["prefix"], revision, files)
|
| 290 |
+
if not matches(check):
|
| 291 |
+
raise ValueError("Completion audit failed: " + record["prefix"])
|
| 292 |
+
state["completion_audit"] = {"status": "verified", "revision": revision,
|
| 293 |
+
"checkpoints": len(plan), "missing": 0, "extra": 0, "mismatch": 0}
|
| 294 |
+
except Exception as exc:
|
| 295 |
+
state["completion_audit"] = {"status": "error", "error_type": type(exc).__name__}
|
| 296 |
+
state["status"] = "complete" if state["verified_count"] == len(plan) and state.get("completion_audit", {}).get("status") == "verified" else "watching"
|
| 297 |
+
state["attention"] = [k for k, v in state["checkpoints"].items() if v.get("status") == "error"]
|
| 298 |
+
if state.get("completion_audit", {}).get("status") == "error":
|
| 299 |
+
state["attention"].append("completion_audit_failed")
|
| 300 |
+
for policy, path in cfg["results"].items():
|
| 301 |
+
try:
|
| 302 |
+
if read(path)["status"] == "failed":
|
| 303 |
+
state["attention"].append(policy + ":training_failed")
|
| 304 |
+
except (FileNotFoundError, json.JSONDecodeError):
|
| 305 |
+
try:
|
| 306 |
+
controller = read(Path(cfg["run_dir"]) / "controller_status.json")
|
| 307 |
+
if controller.get("jobs", {}).get(policy, {}).get("state") == "failed":
|
| 308 |
+
state["attention"].append(policy + ":training_failed")
|
| 309 |
+
except (FileNotFoundError, json.JSONDecodeError):
|
| 310 |
+
pass
|
| 311 |
+
state["heartbeat"] = time.time()
|
| 312 |
+
atomic_json(Path(cfg["state_dir"]) / "status.json", state)
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def main():
|
| 316 |
+
parser = argparse.ArgumentParser(__doc__)
|
| 317 |
+
parser.add_argument("--config", required=True, type=Path)
|
| 318 |
+
parser.add_argument("--once", action="store_true")
|
| 319 |
+
parser.add_argument("--policy", choices=POLICIES)
|
| 320 |
+
parser.add_argument("--step", type=int, choices=STEPS)
|
| 321 |
+
parser.add_argument("--audit-remote", action="store_true")
|
| 322 |
+
args = parser.parse_args()
|
| 323 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = ""
|
| 324 |
+
os.environ["HF_HUB_OFFLINE"] = "0"
|
| 325 |
+
os.environ["TRANSFORMERS_OFFLINE"] = "0"
|
| 326 |
+
os.environ["HF_HUB_DISABLE_PROGRESS_BARS"] = "1"
|
| 327 |
+
os.environ["OMP_NUM_THREADS"] = "2"
|
| 328 |
+
os.environ["MKL_NUM_THREADS"] = "2"
|
| 329 |
+
from huggingface_hub import HfApi
|
| 330 |
+
cfg = read(args.config)
|
| 331 |
+
state_dir = Path(cfg["state_dir"])
|
| 332 |
+
state_dir.mkdir(parents=True, exist_ok=True)
|
| 333 |
+
with (state_dir / "watcher.lock").open("w") as lock:
|
| 334 |
+
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
| 335 |
+
token_file = cfg.get('token_file')
|
| 336 |
+
api = HfApi(token=Path(token_file).read_text().strip() if token_file else None)
|
| 337 |
+
identity = api.whoami()
|
| 338 |
+
if identity["name"] != cfg["hf_account"]:
|
| 339 |
+
raise ValueError("Unexpected HF account")
|
| 340 |
+
if not any(o["name"] == "RwH-Lab" and o.get("roleInOrg") in ("write", "admin")
|
| 341 |
+
for o in identity.get("orgs", [])):
|
| 342 |
+
raise ValueError("Missing HF organization write permission")
|
| 343 |
+
# User authorized a new checkpoint repo; keep private on creation.
|
| 344 |
+
api.create_repo(cfg["repo_id"], repo_type="model", private=True, exist_ok=True)
|
| 345 |
+
if not api.model_info(cfg["repo_id"]).private:
|
| 346 |
+
raise ValueError("Expected a private checkpoint repository")
|
| 347 |
+
status_path = state_dir / "status.json"
|
| 348 |
+
state = read(status_path) if status_path.exists() else {"schema": 1, "checkpoints": {}}
|
| 349 |
+
if state.get("run_dir", cfg["run_dir"]) != cfg["run_dir"]:
|
| 350 |
+
raise ValueError("State belongs to a different run")
|
| 351 |
+
state.update(repo_id=cfg["repo_id"], run_dir=cfg["run_dir"], pid=os.getpid(),
|
| 352 |
+
expected_checkpoints=len(checkpoint_plan(cfg)), remote_root=cfg["remote_root"],
|
| 353 |
+
started_at=state.get("started_at", time.time()))
|
| 354 |
+
while True:
|
| 355 |
+
once(api, cfg, state, args.policy, args.step, args.audit_remote)
|
| 356 |
+
if args.once or state["status"] == "complete":
|
| 357 |
+
break
|
| 358 |
+
time.sleep(cfg["poll_seconds"])
|
| 359 |
+
print("HF_WATCHER_EXIT", state["status"], state["verified_count"], flush=True)
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
if __name__ == "__main__":
|
| 363 |
+
main()
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/language_guard.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Fail before a training update if a sampled state's language is invalid."""
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def validate_training_batch(batch, task_lookup):
|
| 5 |
+
prompts = batch.get("task")
|
| 6 |
+
task_indices = batch["task_index"].tolist()
|
| 7 |
+
if not isinstance(prompts, list) or len(prompts) != 128:
|
| 8 |
+
raise RuntimeError("Every sampled state must have its own task language")
|
| 9 |
+
if any(not isinstance(p, str) or not p.strip() or p != task_lookup[int(i)]
|
| 10 |
+
for p, i in zip(prompts, task_indices, strict=True)):
|
| 11 |
+
raise RuntimeError("Empty or mismatched state/task language")
|
| 12 |
+
if batch["action_is_pad"].any():
|
| 13 |
+
raise RuntimeError("A sampled action chunk crossed its skill boundary")
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/load_n17.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Load a standalone native checkpoint with portable pinned backbone assets."""
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import sys
|
| 4 |
+
|
| 5 |
+
def load(path,device='cuda'):
|
| 6 |
+
project=Path(__file__).resolve().parents[2]
|
| 7 |
+
sys.path.insert(0,str(project.parent/'LeTools-Learning/kuavo_model/external_models/gr00tn1d7'))
|
| 8 |
+
from scripts.train.native_assets import local_cosmos
|
| 9 |
+
from gr00t.configs.model.gr00t_n1d7 import Gr00tN1d7Config
|
| 10 |
+
from gr00t.model.gr00t_n1d7.gr00t_n1d7 import Gr00tN1d7
|
| 11 |
+
from gr00t.model.gr00t_n1d7.processing_gr00t_n1d7 import Gr00tN1d7Processor
|
| 12 |
+
path=Path(path)
|
| 13 |
+
if (path/'pretrained_model').exists():path=path/'pretrained_model'
|
| 14 |
+
cfg=Gr00tN1d7Config.from_pretrained(path);cfg.model_name=local_cosmos()
|
| 15 |
+
model=Gr00tN1d7.from_pretrained(path,config=cfg,
|
| 16 |
+
transformers_loading_kwargs={'local_files_only':True,'trust_remote_code':True}).to(device).eval()
|
| 17 |
+
processor=Gr00tN1d7Processor.from_pretrained(path,model_name=local_cosmos(),
|
| 18 |
+
transformers_loading_kwargs={'local_files_only':True,'trust_remote_code':True})
|
| 19 |
+
processor.eval()
|
| 20 |
+
return model,processor
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/native_assets.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Pin local assets while preserving the native backbone-name routing contract."""
|
| 2 |
+
import os
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def pinned_cosmos_name():
|
| 7 |
+
from huggingface_hub import try_to_load_from_cache
|
| 8 |
+
name = "nvidia/Cosmos-Reason2-2B"
|
| 9 |
+
config = try_to_load_from_cache(name, "config.json", revision="main")
|
| 10 |
+
if not isinstance(config, str) or Path(config).parent.name != "9ce19a195e423419c349abfc86fd07178b230561":
|
| 11 |
+
raise RuntimeError("Cosmos cached main differs from the approved revision")
|
| 12 |
+
return name
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def local_cosmos():
|
| 16 |
+
from huggingface_hub import snapshot_download
|
| 17 |
+
revision = "9ce19a195e423419c349abfc86fd07178b230561"
|
| 18 |
+
snapshot = Path(snapshot_download("nvidia/Cosmos-Reason2-2B", revision=revision, local_files_only=True))
|
| 19 |
+
# Native get_backbone_cls recognizes the nvidia/Cosmos-Reason2 substring.
|
| 20 |
+
alias = Path(os.environ.get("HF_HOME", str(Path.home()/".cache/huggingface"))) / "rwh_pinned/nvidia/Cosmos-Reason2-2B" / revision
|
| 21 |
+
alias.parent.mkdir(parents=True, exist_ok=True)
|
| 22 |
+
if alias.is_symlink():
|
| 23 |
+
if alias.resolve() != snapshot.resolve():
|
| 24 |
+
raise RuntimeError("Pinned Cosmos alias points at another snapshot")
|
| 25 |
+
elif alias.exists():
|
| 26 |
+
raise RuntimeError("Pinned Cosmos alias is occupied")
|
| 27 |
+
else:
|
| 28 |
+
try: alias.symlink_to(snapshot, target_is_directory=True)
|
| 29 |
+
except FileExistsError:
|
| 30 |
+
if alias.resolve() != snapshot.resolve(): raise
|
| 31 |
+
return str(alias)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def verify_legacy_refs(policy):
|
| 35 |
+
from huggingface_hub import try_to_load_from_cache
|
| 36 |
+
assets = {
|
| 37 |
+
"nvidia/GR00T-N1.5-3B": "869830fc749c35f34771aa5209f923ac57e4564e",
|
| 38 |
+
"openai/clip-vit-base-patch16": "57c216476eefef5ab752ec549e440a49ae4ae5f3",
|
| 39 |
+
"lerobot/eagle2hg-processor-groot-n1p5": "baf604d8a5caf26fda5cc545f141bc1814156237",
|
| 40 |
+
}
|
| 41 |
+
repos = ["openai/clip-vit-base-patch16"] if policy == "multi_task_dit" else ["nvidia/GR00T-N1.5-3B"]
|
| 42 |
+
for repo in repos:
|
| 43 |
+
current = try_to_load_from_cache(repo, "config.json", revision="main")
|
| 44 |
+
pinned = try_to_load_from_cache(repo, "config.json", revision=assets[repo])
|
| 45 |
+
# DiT receives the explicit pinned CLIP snapshot path in its config.
|
| 46 |
+
if not isinstance(pinned, str) or (policy != "multi_task_dit" and
|
| 47 |
+
(not isinstance(current, str) or Path(current).parent.name != assets[repo])):
|
| 48 |
+
raise RuntimeError("Cached main is not the approved model revision: " + repo)
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/orchestrate.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Start verified fresh runs; queue DiT on the first successfully completed GPU."""
|
| 2 |
+
import fcntl
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import subprocess
|
| 7 |
+
import time
|
| 8 |
+
|
| 9 |
+
ROOT = Path(os.environ["RWH_TRAIN_CONFIG_DIR"]).resolve()
|
| 10 |
+
OUTPUT = Path("/home/work/mntvol/RwH-Kuavo_ckpt/v3_rawaction")
|
| 11 |
+
POLICIES = ("gr00t_n15", "gr00t_n17", "multi_task_dit")
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def write(path, value):
|
| 15 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 16 |
+
tmp = path.with_suffix(".tmp")
|
| 17 |
+
tmp.write_text(json.dumps(value, indent=2) + "\n")
|
| 18 |
+
tmp.replace(path)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def main():
|
| 22 |
+
OUTPUT.mkdir(parents=True, exist_ok=True)
|
| 23 |
+
lock = (OUTPUT / "launch.lock").open("w")
|
| 24 |
+
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
| 25 |
+
for policy in POLICIES:
|
| 26 |
+
result = json.loads((OUTPUT / "_validation" / policy / "result.json").read_text())
|
| 27 |
+
if result["status"] != "passed" or result.get("masked_gradient_checks", 0) < 3:
|
| 28 |
+
raise RuntimeError("Full-batch/mask/inference validation not passed: " + policy)
|
| 29 |
+
if result["batch_size"] != 128 or result["gradient_accumulation_steps"] != 1:
|
| 30 |
+
raise RuntimeError("Validation used another batch contract")
|
| 31 |
+
reload = json.loads((OUTPUT / '_audit' / f'reload_{policy}.json').read_text())
|
| 32 |
+
if reload.get('status') != 'passed' or not reload.get('separate_process_reload'):
|
| 33 |
+
raise RuntimeError('Separate-process checkpoint reload not passed: '+policy)
|
| 34 |
+
if (OUTPUT / policy).exists():
|
| 35 |
+
raise FileExistsError("Production output already exists: " + policy)
|
| 36 |
+
validation = json.loads((OUTPUT / "_audit/data_validation.json").read_text())
|
| 37 |
+
if validation["status"] != "passed":
|
| 38 |
+
raise RuntimeError("Data validation failed")
|
| 39 |
+
status_path = OUTPUT / "_runs/controller_status.json"
|
| 40 |
+
status = {"state": "running", "started_at": time.time(), "pid": os.getpid(),
|
| 41 |
+
"batch_size": 128, "gradient_accumulation_steps": 1,
|
| 42 |
+
"steps": 30000, "save_every": 10000, "jobs": {}}
|
| 43 |
+
children = {}
|
| 44 |
+
def start(policy, gpu):
|
| 45 |
+
logdir = OUTPUT / "_runs" / policy
|
| 46 |
+
logdir.mkdir(parents=True, exist_ok=True)
|
| 47 |
+
env = os.environ.copy()
|
| 48 |
+
env["RWH_RESULT_DIR"] = str(logdir)
|
| 49 |
+
stream = (logdir / "console.log").open("a")
|
| 50 |
+
child = subprocess.Popen(["bash", str(ROOT / "launch.sh"), policy, "--gpu", str(gpu)],
|
| 51 |
+
env=env, stdout=stream, stderr=subprocess.STDOUT)
|
| 52 |
+
stream.close()
|
| 53 |
+
children[policy] = child
|
| 54 |
+
status["jobs"][policy] = {"state": "running", "gpu": gpu, "pid": child.pid,
|
| 55 |
+
"started_at": time.time(), "result": str(logdir / "result.json")}
|
| 56 |
+
write(status_path, status)
|
| 57 |
+
print("TRAINING_STARTED", policy, gpu, child.pid, flush=True)
|
| 58 |
+
start("gr00t_n15", 0)
|
| 59 |
+
start("gr00t_n17", 1)
|
| 60 |
+
status["jobs"]["multi_task_dit"] = {"state": "queued", "reason": "start once on the first successfully completed GPU 0 or 1"}
|
| 61 |
+
write(status_path, status)
|
| 62 |
+
while True:
|
| 63 |
+
finished=[]
|
| 64 |
+
for policy,child in list(children.items()):
|
| 65 |
+
exit_code=child.poll()
|
| 66 |
+
if exit_code is None or status["jobs"][policy]["state"]!="running":continue
|
| 67 |
+
result_path=OUTPUT/"_runs"/policy/"result.json"
|
| 68 |
+
try:result=json.loads(result_path.read_text())
|
| 69 |
+
except (FileNotFoundError,json.JSONDecodeError):result={}
|
| 70 |
+
finished.append((result.get("completed_at",time.time()),policy,exit_code,result))
|
| 71 |
+
# Both jobs can finish during one poll. Honor actual validated completion time.
|
| 72 |
+
for finished_at,policy,exit_code,result in sorted(finished):
|
| 73 |
+
passed=exit_code==0 and result.get("status")=="passed"
|
| 74 |
+
status["jobs"][policy].update(state="passed" if passed else "failed",
|
| 75 |
+
exit_code=exit_code,finished_at=finished_at)
|
| 76 |
+
if passed and status["jobs"]["multi_task_dit"]["state"]=="queued":
|
| 77 |
+
start("multi_task_dit",status["jobs"][policy]["gpu"])
|
| 78 |
+
write(status_path,status)
|
| 79 |
+
running = any(j["state"] == "running" for j in status["jobs"].values())
|
| 80 |
+
if not running:
|
| 81 |
+
status["state"] = "complete" if all(j["state"] == "passed" for j in status["jobs"].values()) else "failed"
|
| 82 |
+
write(status_path, status)
|
| 83 |
+
if status["state"] != "complete": raise RuntimeError("Production training failure; inspect controller status")
|
| 84 |
+
lock.close()
|
| 85 |
+
break
|
| 86 |
+
status["heartbeat"] = time.time()
|
| 87 |
+
write(status_path, status)
|
| 88 |
+
time.sleep(15)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
if __name__ == "__main__":
|
| 92 |
+
main()
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/run_lerobot.py
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Project-local LeTools runner with policy-specific, skill-safe sampling.
|
| 2 |
+
|
| 3 |
+
The upstream trainer remains untouched. Only its DataLoader reference is wrapped.
|
| 4 |
+
"""
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import argparse
|
| 8 |
+
import hashlib
|
| 9 |
+
import importlib.util
|
| 10 |
+
import json
|
| 11 |
+
import os
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
import statistics
|
| 14 |
+
import sys
|
| 15 |
+
import time
|
| 16 |
+
import traceback
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
ROOT = Path(os.environ["RWH_TRAIN_CONFIG_DIR"]).resolve()
|
| 20 |
+
PROJECT = ROOT.parents[3]
|
| 21 |
+
LETOOLS = PROJECT / "LeTools-Learning"
|
| 22 |
+
OUTPUT_ROOT = Path("/home/work/mntvol/RwH-Kuavo_ckpt/v3_rawaction")
|
| 23 |
+
LOG_ROOT = Path(os.environ.get("RWH_RUN_LOG_ROOT", str(OUTPUT_ROOT / "_runs")))
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def write_json(path, value):
|
| 27 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 28 |
+
path.write_text(json.dumps(value, indent=2, ensure_ascii=False) + "\n")
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def resolve(policy):
|
| 32 |
+
spec = importlib.util.spec_from_file_location("letools_train_config", LETOOLS / "kuavo_model/train.py")
|
| 33 |
+
module = importlib.util.module_from_spec(spec)
|
| 34 |
+
spec.loader.exec_module(module)
|
| 35 |
+
total = module._load_yaml(ROOT / "total" / f"{policy}_total.yaml")
|
| 36 |
+
simple = module._load_yaml(ROOT / f"{policy}.yaml")
|
| 37 |
+
simple["dataset"] = {"repo_id": DATA_VIEW.config["source"]["repo_id"], "root": str(DATA_VIEW.root)}
|
| 38 |
+
return module._convert_to_lerobot_train_config(module._deep_merge(total, simple))
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class Proxy:
|
| 42 |
+
def __init__(self, target, **overrides):
|
| 43 |
+
self.target, self.overrides = target, overrides
|
| 44 |
+
|
| 45 |
+
def __getattr__(self, key):
|
| 46 |
+
return self.overrides[key] if key in self.overrides else getattr(self.target, key)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class CallableProxy(Proxy):
|
| 50 |
+
def __call__(self, *args, **kwargs):
|
| 51 |
+
return self.overrides["call"](*args, **kwargs)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def main():
|
| 55 |
+
parser = argparse.ArgumentParser()
|
| 56 |
+
parser.add_argument("policy", choices=["multi_task_dit", "gr00t_n15"])
|
| 57 |
+
parser.add_argument("--gpu", required=True, choices=["0", "1"])
|
| 58 |
+
parser.add_argument("--smoke-steps", type=int, default=0)
|
| 59 |
+
parser.add_argument("--dry-run", action="store_true")
|
| 60 |
+
args = parser.parse_args()
|
| 61 |
+
if int(os.environ.get("WORLD_SIZE", "1")) != 1:
|
| 62 |
+
raise RuntimeError("This contract uses one GPU per policy and actual batch 128")
|
| 63 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = args.gpu
|
| 64 |
+
os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
|
| 65 |
+
os.environ.setdefault("OMP_NUM_THREADS", "8")
|
| 66 |
+
sys.path.insert(0, str(LETOOLS / "third_party/lerobot/src"))
|
| 67 |
+
from scripts.train.native_assets import verify_legacy_refs
|
| 68 |
+
verify_legacy_refs(args.policy)
|
| 69 |
+
from scripts.data.view import DataView, lerobot_dataset
|
| 70 |
+
import yaml
|
| 71 |
+
global DATA_VIEW
|
| 72 |
+
simple = yaml.safe_load((ROOT/f"{args.policy}.yaml").read_text())
|
| 73 |
+
DATA_VIEW = DataView(ROOT / simple["dataset"]["config"])
|
| 74 |
+
cfg = resolve(args.policy)
|
| 75 |
+
if args.policy == "multi_task_dit":
|
| 76 |
+
from huggingface_hub import snapshot_download
|
| 77 |
+
clip = snapshot_download("openai/clip-vit-base-patch16", revision="57c216476eefef5ab752ec549e440a49ae4ae5f3", allow_patterns=["*.json", "*.txt", "*.safetensors", "pytorch_model.bin"], local_files_only=True)
|
| 78 |
+
cfg["policy"]["vision_encoder_name"] = clip
|
| 79 |
+
cfg["policy"]["text_encoder_name"] = clip
|
| 80 |
+
|
| 81 |
+
stamp = time.strftime("%Y%m%d_%H%M%S")
|
| 82 |
+
tag = f"{args.policy}_{'smoke' if args.smoke_steps else 'train'}_{stamp}"
|
| 83 |
+
log_dir = Path(os.environ.get("RWH_RESULT_DIR", str(LOG_ROOT / tag)))
|
| 84 |
+
if args.smoke_steps:
|
| 85 |
+
if args.smoke_steps < 3:
|
| 86 |
+
raise ValueError("Use at least three updates for a meaningful smoke test")
|
| 87 |
+
cfg.update(steps=args.smoke_steps, save_freq=args.smoke_steps, log_freq=1,
|
| 88 |
+
output_dir=str(OUTPUT_ROOT / "smoke" / tag))
|
| 89 |
+
if args.smoke_steps:
|
| 90 |
+
cfg["wandb"]["enable"] = False
|
| 91 |
+
cfg["dataset"]["video_backend"] = "pyav"
|
| 92 |
+
if cfg["batch_size"] != 128 or cfg["seed"] != 42 or cfg.get("resume"):
|
| 93 |
+
raise ValueError("Expected fresh actual batch=128, seed=42 contract")
|
| 94 |
+
config_path = log_dir / "resolved_train_config.json"
|
| 95 |
+
write_json(config_path, cfg)
|
| 96 |
+
print("RWH_CONFIG", config_path, flush=True)
|
| 97 |
+
import torch
|
| 98 |
+
import draccus
|
| 99 |
+
from lerobot.configs.train import TrainPipelineConfig
|
| 100 |
+
import lerobot.scripts.lerobot_train as trainer
|
| 101 |
+
from scripts.adapters import language as compat
|
| 102 |
+
from scripts.train.language_guard import validate_training_batch
|
| 103 |
+
compat.install(args.policy)
|
| 104 |
+
from scripts.adapters import action_validity as loss_mask
|
| 105 |
+
loss_mask.install(args.policy)
|
| 106 |
+
if args.policy in ("gr00t_n15", "multi_task_dit"):
|
| 107 |
+
def make_dataset(config):
|
| 108 |
+
return lerobot_dataset(DATA_VIEW, args.policy)
|
| 109 |
+
|
| 110 |
+
trainer.make_dataset = make_dataset
|
| 111 |
+
sys.argv = [sys.argv[0], f"--config_path={config_path}"]
|
| 112 |
+
# The native parser also loads registered policy configuration types.
|
| 113 |
+
parsed = draccus.parse(TrainPipelineConfig, config_path=str(config_path), args=[])
|
| 114 |
+
parsed.validate()
|
| 115 |
+
write_json(log_dir / "effective_train_config.json", parsed.to_dict())
|
| 116 |
+
if args.dry_run:
|
| 117 |
+
print("RWH_DRY_RUN_OK", args.policy, flush=True)
|
| 118 |
+
return
|
| 119 |
+
|
| 120 |
+
data_root = Path(cfg["dataset"]["root"])
|
| 121 |
+
mask_bank = loss_mask.MaskBank(DATA_VIEW, args.policy)
|
| 122 |
+
anchors = DATA_VIEW.anchors(args.policy).tolist()
|
| 123 |
+
manifest = DATA_VIEW.manifest()
|
| 124 |
+
task_lookup = DATA_VIEW.tasks
|
| 125 |
+
DATA_VIEW.save_audit(log_dir / "data")
|
| 126 |
+
report = {"status": "running", "policy": args.policy, "gpu": int(args.gpu),
|
| 127 |
+
"batch_size": 128, "gradient_accumulation_steps": 1,
|
| 128 |
+
"steps_requested": cfg["steps"], "smoke": bool(args.smoke_steps),
|
| 129 |
+
"eligible_anchors": len(anchors), "output_dir": cfg["output_dir"],
|
| 130 |
+
"sampling_rule": "full history/action chunks within logical task episodes",
|
| 131 |
+
"config_path": str(config_path), "measurements": [],
|
| 132 |
+
"source_revision": DATA_VIEW.config["source"]["revision"], "data_fingerprint": DATA_VIEW.fingerprint, "trainer_source": trainer.__file__}
|
| 133 |
+
report_path = log_dir / "result.json"
|
| 134 |
+
write_json(report_path, report)
|
| 135 |
+
|
| 136 |
+
class PolicySampler(torch.utils.data.Sampler):
|
| 137 |
+
def __init__(self):
|
| 138 |
+
self.epoch = 0
|
| 139 |
+
|
| 140 |
+
def __len__(self):
|
| 141 |
+
return len(anchors)
|
| 142 |
+
|
| 143 |
+
def __iter__(self):
|
| 144 |
+
generator = torch.Generator().manual_seed(42 + self.epoch)
|
| 145 |
+
self.epoch += 1
|
| 146 |
+
ordered = [anchors[i] for i in torch.randperm(len(anchors), generator=generator).tolist()]
|
| 147 |
+
if args.smoke_steps:
|
| 148 |
+
candidates = torch.tensor(anchors)
|
| 149 |
+
bad = (~mask_bank.mask[candidates[:, None] + mask_bank.offsets[None, :], 20:23]).any(dim=(1, 2))
|
| 150 |
+
ordered[0] = anchors[int(torch.nonzero(bad)[0])]
|
| 151 |
+
return iter(ordered)
|
| 152 |
+
|
| 153 |
+
preview = list(iter(PolicySampler()))[:128]
|
| 154 |
+
report["first_batch_anchors"] = preview
|
| 155 |
+
report["first_batch_sha256"] = hashlib.sha256(json.dumps(preview).encode()).hexdigest()
|
| 156 |
+
original_loader = torch.utils.data.DataLoader
|
| 157 |
+
|
| 158 |
+
def loader(dataset, *positional, **kwargs):
|
| 159 |
+
if Path(dataset.root).resolve() != data_root.resolve():
|
| 160 |
+
raise ValueError("Unexpected dataset; refusing to apply the training sampler")
|
| 161 |
+
kwargs.update(sampler=PolicySampler(), shuffle=False, drop_last=True)
|
| 162 |
+
return original_loader(dataset, *positional, **kwargs)
|
| 163 |
+
|
| 164 |
+
trainer.torch = Proxy(torch, utils=Proxy(torch.utils, data=Proxy(torch.utils.data, DataLoader=loader)))
|
| 165 |
+
original_update = trainer.update_policy
|
| 166 |
+
holder = {}
|
| 167 |
+
|
| 168 |
+
def first_sample(value):
|
| 169 |
+
if isinstance(value, torch.Tensor):
|
| 170 |
+
return value[:1].detach().clone() if value.ndim else value.detach().clone()
|
| 171 |
+
if isinstance(value, dict):
|
| 172 |
+
return {k: first_sample(v) for k, v in value.items()}
|
| 173 |
+
if isinstance(value, list):
|
| 174 |
+
return value[:1]
|
| 175 |
+
return value
|
| 176 |
+
|
| 177 |
+
original_processors = trainer.make_pre_post_processors
|
| 178 |
+
|
| 179 |
+
def make_processors(*positional, **kwargs):
|
| 180 |
+
pre, post = original_processors(*positional, **kwargs)
|
| 181 |
+
holder.update(preprocessor=pre, postprocessor=post)
|
| 182 |
+
|
| 183 |
+
def preprocess(batch):
|
| 184 |
+
validate_training_batch(batch, task_lookup)
|
| 185 |
+
if "raw_batch" not in holder:
|
| 186 |
+
observed = batch["index"].tolist()
|
| 187 |
+
if observed != preview:
|
| 188 |
+
raise RuntimeError("The actual first batch differs from the policy's seeded sampler")
|
| 189 |
+
report["observed_first_batch_anchors"] = observed
|
| 190 |
+
holder["raw_batch"] = first_sample(batch)
|
| 191 |
+
result = pre(batch)
|
| 192 |
+
return mask_bank.attach(result, batch)
|
| 193 |
+
|
| 194 |
+
return CallableProxy(pre, call=preprocess), post
|
| 195 |
+
|
| 196 |
+
trainer.make_pre_post_processors = make_processors
|
| 197 |
+
|
| 198 |
+
def update(metrics, policy, batch, *positional, **kwargs):
|
| 199 |
+
if "policy" not in holder:
|
| 200 |
+
holder.update(policy=policy, accelerator=kwargs["accelerator"])
|
| 201 |
+
if cfg.get("wandb", {}).get("enable"):
|
| 202 |
+
import wandb
|
| 203 |
+
if wandb.run is not None:
|
| 204 |
+
report.update(wandb_url=wandb.run.url, wandb_run_id=wandb.run.id)
|
| 205 |
+
report["parameters"] = sum(p.numel() for p in policy.parameters())
|
| 206 |
+
report["trainable_parameters"] = sum(p.numel() for p in policy.parameters() if p.requires_grad)
|
| 207 |
+
report["batch_shapes"] = {k: list(v.shape) for k, v in batch.items() if isinstance(v, torch.Tensor)}
|
| 208 |
+
if kwargs["accelerator"].gradient_accumulation_steps != 1:
|
| 209 |
+
raise RuntimeError("Gradient accumulation is prohibited")
|
| 210 |
+
report["optimizer_groups"] = [
|
| 211 |
+
{k: v for k, v in group.items() if k != "params"}
|
| 212 |
+
for group in positional[0].param_groups
|
| 213 |
+
]
|
| 214 |
+
report["trainable_parameter_names"] = [n for n, p in policy.named_parameters() if p.requires_grad]
|
| 215 |
+
report["updates_started_unix"] = time.time()
|
| 216 |
+
torch.cuda.synchronize()
|
| 217 |
+
start = time.perf_counter()
|
| 218 |
+
if args.smoke_steps:
|
| 219 |
+
holder["current_mask"] = batch["action_mask"] if args.policy == "gr00t_n15" else batch["rwh_action_validity"]
|
| 220 |
+
if not holder.get("gradient_hook_installed"):
|
| 221 |
+
module = policy._groot_model.action_head.action_decoder if args.policy == "gr00t_n15" else policy.noise_predictor
|
| 222 |
+
def capture(module, inputs, output):
|
| 223 |
+
if not output.requires_grad:
|
| 224 |
+
return
|
| 225 |
+
mask = holder["current_mask"].detach().bool()
|
| 226 |
+
def check(grad):
|
| 227 |
+
tail = grad[:, -mask.shape[1]:]
|
| 228 |
+
if tail[~mask].abs().sum().item() != 0 or tail[mask].abs().sum().item() == 0:
|
| 229 |
+
raise RuntimeError("Real policy dimension-mask gradient verification failed")
|
| 230 |
+
report["masked_gradient_checks"] = report.get("masked_gradient_checks", 0) + 1
|
| 231 |
+
return grad
|
| 232 |
+
output.register_hook(check)
|
| 233 |
+
module.register_forward_hook(capture)
|
| 234 |
+
holder["gradient_hook_installed"] = True
|
| 235 |
+
result = original_update(metrics, policy, batch, *positional, **kwargs)
|
| 236 |
+
torch.cuda.synchronize()
|
| 237 |
+
measurement = {"step": len(report["measurements"]) + 1, "update_s": time.perf_counter() - start,
|
| 238 |
+
"loss": float(result[0].loss.val), "grad_norm": float(result[0].grad_norm.val),
|
| 239 |
+
"lr": float(result[0].lr.val),
|
| 240 |
+
"dataloading_s": float(result[0].dataloading_s.val),
|
| 241 |
+
"max_allocated_gib": torch.cuda.max_memory_allocated() / 2**30,
|
| 242 |
+
"max_reserved_gib": torch.cuda.max_memory_reserved() / 2**30}
|
| 243 |
+
if not all(__import__("math").isfinite(measurement[k]) for k in ("loss", "grad_norm")):
|
| 244 |
+
raise RuntimeError("Nonfinite training loss or gradient norm")
|
| 245 |
+
report["measurements"].append(measurement)
|
| 246 |
+
report["last_update_finished_unix"] = time.time()
|
| 247 |
+
if args.smoke_steps or measurement["step"] == 1 or measurement["step"] % cfg["log_freq"] == 0:
|
| 248 |
+
write_json(report_path, report)
|
| 249 |
+
print("RWH_STEP", json.dumps(measurement), flush=True)
|
| 250 |
+
return result
|
| 251 |
+
|
| 252 |
+
trainer.update_policy = update
|
| 253 |
+
try:
|
| 254 |
+
started = time.perf_counter()
|
| 255 |
+
trainer.train()
|
| 256 |
+
report["training_wall_s"] = time.perf_counter() - started
|
| 257 |
+
policy = holder["accelerator"].unwrap_model(holder["policy"])
|
| 258 |
+
policy.eval()
|
| 259 |
+
raw_batch = holder["raw_batch"]
|
| 260 |
+
if args.policy == "multi_task_dit":
|
| 261 |
+
# Online inference supplies the current frame. select_action fills
|
| 262 |
+
# the two-observation history queues, repeating the first frame.
|
| 263 |
+
raw_batch = {k: v[:, -1] if k.startswith("observation.") and isinstance(v, torch.Tensor)
|
| 264 |
+
and v.ndim >= 3 else v for k, v in raw_batch.items()}
|
| 265 |
+
inference_batch = holder["preprocessor"](raw_batch)
|
| 266 |
+
policy.reset()
|
| 267 |
+
with torch.inference_mode():
|
| 268 |
+
if args.policy == "multi_task_dit":
|
| 269 |
+
first_action = policy.select_action(inference_batch)
|
| 270 |
+
remaining = list(policy._queues["action"])
|
| 271 |
+
actions = torch.stack([first_action, *remaining], dim=1)
|
| 272 |
+
else:
|
| 273 |
+
actions = policy.predict_action_chunk(inference_batch)
|
| 274 |
+
if not torch.isfinite(actions).all() or actions.shape[-1] != 23:
|
| 275 |
+
raise RuntimeError(f"Invalid prediction shape/values: {actions.shape}")
|
| 276 |
+
report["prediction_shape"] = list(actions.shape)
|
| 277 |
+
physical_action = holder["postprocessor"](actions[:, 0])
|
| 278 |
+
if physical_action.shape != (1, 23) or not torch.isfinite(physical_action).all():
|
| 279 |
+
raise RuntimeError("Invalid postprocessed physical action")
|
| 280 |
+
report["postprocessed_action_shape"] = list(physical_action.shape)
|
| 281 |
+
checkpoint = Path(cfg["output_dir"]) / "checkpoints/last"
|
| 282 |
+
if not (checkpoint / "pretrained_model/train_config.json").is_file():
|
| 283 |
+
raise RuntimeError("Missing saved checkpoint training config")
|
| 284 |
+
from safetensors import safe_open
|
| 285 |
+
weights = sorted((checkpoint / "pretrained_model").rglob("*.safetensors"))
|
| 286 |
+
if not weights:
|
| 287 |
+
raise RuntimeError("Missing checkpoint weights")
|
| 288 |
+
tensors = 0
|
| 289 |
+
for weight in weights:
|
| 290 |
+
with safe_open(str(weight), framework="pt", device="cpu") as saved:
|
| 291 |
+
for key in saved.keys():
|
| 292 |
+
if not torch.isfinite(saved.get_tensor(key)).all():
|
| 293 |
+
raise RuntimeError(f"Nonfinite checkpoint tensor: {key}")
|
| 294 |
+
tensors += 1
|
| 295 |
+
report["checkpoint_tensors_verified"] = tensors
|
| 296 |
+
report["checkpoint"] = str(checkpoint.resolve())
|
| 297 |
+
measurements = report["measurements"][2:]
|
| 298 |
+
report["median_update_s_after_warmup"] = statistics.median(x["update_s"] for x in measurements)
|
| 299 |
+
report["median_step_s_after_warmup"] = statistics.median(x["update_s"] + x["dataloading_s"] for x in measurements)
|
| 300 |
+
report["status"] = "passed"
|
| 301 |
+
report["completed_at"] = time.time()
|
| 302 |
+
except BaseException as exc:
|
| 303 |
+
report.update(status="failed", error_type=type(exc).__name__, error=str(exc)[-1800:])
|
| 304 |
+
write_json(report_path, report)
|
| 305 |
+
raise
|
| 306 |
+
finally:
|
| 307 |
+
write_json(report_path, report)
|
| 308 |
+
print("RWH_RESULT", report_path, report["status"], flush=True)
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
if __name__ == "__main__":
|
| 312 |
+
main()
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/run_n17.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Configure and call LeTools' bundled N1.7 pipeline and HF Trainer."""
|
| 2 |
+
import argparse
|
| 3 |
+
import json
|
| 4 |
+
import math
|
| 5 |
+
import os
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import sys
|
| 8 |
+
import time
|
| 9 |
+
|
| 10 |
+
ROOT=Path(os.environ['RWH_TRAIN_CONFIG_DIR']).resolve()
|
| 11 |
+
LETOOLS=ROOT.parents[3]/'LeTools-Learning'
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def write(path,value):
|
| 15 |
+
path=Path(path); path.parent.mkdir(parents=True,exist_ok=True)
|
| 16 |
+
tmp=path.with_suffix('.tmp');tmp.write_text(json.dumps(value,indent=2,ensure_ascii=False)+'\n');tmp.replace(path)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def main():
|
| 20 |
+
parser=argparse.ArgumentParser()
|
| 21 |
+
parser.add_argument('--gpu',required=True,choices=['0','1'])
|
| 22 |
+
parser.add_argument('--smoke-steps',type=int,default=0)
|
| 23 |
+
args=parser.parse_args()
|
| 24 |
+
os.environ['CUDA_VISIBLE_DEVICES']=args.gpu
|
| 25 |
+
os.environ['NO_ALBUMENTATIONS_UPDATE']='1'
|
| 26 |
+
sys.path.insert(0,str(LETOOLS/'kuavo_model/external_models/gr00tn1d7'))
|
| 27 |
+
import yaml
|
| 28 |
+
import numpy as np
|
| 29 |
+
import torch
|
| 30 |
+
torch.set_num_threads(8)
|
| 31 |
+
if args.smoke_steps:
|
| 32 |
+
import faulthandler
|
| 33 |
+
faulthandler.dump_traceback_later(90, repeat=True)
|
| 34 |
+
from huggingface_hub import snapshot_download
|
| 35 |
+
from transformers import TrainerCallback
|
| 36 |
+
from scripts.data.view import DataView
|
| 37 |
+
from scripts.adapters import groot_v3
|
| 38 |
+
from gr00t.configs.base_config import get_default_config
|
| 39 |
+
from gr00t.configs.data.data_config import SingleDatasetConfig
|
| 40 |
+
import gr00t.experiment.experiment as experiment
|
| 41 |
+
from scripts.train.native_assets import pinned_cosmos_name
|
| 42 |
+
|
| 43 |
+
simple=yaml.safe_load((ROOT/'gr00t_n17.yaml').read_text())
|
| 44 |
+
view=DataView(ROOT/simple['dataset']['config'])
|
| 45 |
+
t,p=simple['training'],simple['policy']
|
| 46 |
+
if t['batch_size']!=128 or t['gradient_accumulation_steps']!=1 or t['resume']:
|
| 47 |
+
raise ValueError('Expected fresh actual batch128/accumulation1')
|
| 48 |
+
if p['chunk_size']!=40 or p['n_action_steps']!=40: raise ValueError('Expected 40 steps')
|
| 49 |
+
if int(os.environ.get('WORLD_SIZE','1'))!=1: raise ValueError('One GPU per model required')
|
| 50 |
+
output=Path(t['output_dir'])
|
| 51 |
+
if args.smoke_steps: output=output.parent/'_validation'/('gr00t_n17_'+time.strftime('%Y%m%d_%H%M%S'))
|
| 52 |
+
if output.exists(): raise FileExistsError(str(output))
|
| 53 |
+
log=Path(os.environ.get('RWH_RESULT_DIR',str(output.parent/'_runs/gr00t_n17')))
|
| 54 |
+
report={'status':'loading','policy':'gr00t_n17','gpu':int(args.gpu),'batch_size':128,
|
| 55 |
+
'gradient_accumulation_steps':1,'steps_requested':args.smoke_steps or t['steps'],
|
| 56 |
+
'smoke':bool(args.smoke_steps),'output_dir':str(output),'measurements':[],
|
| 57 |
+
'data_fingerprint':view.fingerprint,'source_revision':view.config['source']['revision'],
|
| 58 |
+
'runtime_root':str(LETOOLS/'kuavo_model/external_models/gr00tn1d7'),
|
| 59 |
+
'trainer_source':experiment.__file__}
|
| 60 |
+
result=log/'result.json';write(result,report);view.save_audit(log/'data')
|
| 61 |
+
base=snapshot_download(p['base_model_path'],revision=p['base_revision'],local_files_only=True)
|
| 62 |
+
cfg=get_default_config(); cfg.load_config_path=None
|
| 63 |
+
cfg.data.datasets=[SingleDatasetConfig(dataset_paths=[str(view.root)],mix_ratio=1.0,embodiment_tag='new_embodiment')]
|
| 64 |
+
cfg.data.modality_configs=groot_v3.modality(view,40)
|
| 65 |
+
cfg.data.seed=42;cfg.data.allow_padding=False;cfg.data.video_backend='torchcodec'
|
| 66 |
+
cfg.data.shard_size=t['shard_size'];cfg.data.episode_sampling_rate=t['episode_sampling_rate']
|
| 67 |
+
cfg.data.num_shards_per_epoch=t['num_shards_per_epoch'];cfg.data.download_cache=False
|
| 68 |
+
cfg.data.multiprocessing_context='fork'
|
| 69 |
+
cfg.model.model_name=pinned_cosmos_name()
|
| 70 |
+
cfg.model.action_horizon=40
|
| 71 |
+
cfg.model.tune_llm=False;cfg.model.tune_visual=False
|
| 72 |
+
cfg.model.tune_projector=True;cfg.model.tune_diffusion_model=True;cfg.model.tune_vlln=True
|
| 73 |
+
cfg.model.load_bf16=True;cfg.model.backbone_trainable_params_fp32=True
|
| 74 |
+
cfg.model.state_dropout_prob=p['state_dropout_prob']
|
| 75 |
+
cfg.model.use_relative_action=False
|
| 76 |
+
cfg.training.output_dir=str(output);cfg.training.experiment_name=None
|
| 77 |
+
cfg.training.start_from_checkpoint=base
|
| 78 |
+
cfg.training.global_batch_size=128;cfg.training.gradient_accumulation_steps=1
|
| 79 |
+
cfg.training.num_gpus=1;cfg.training.max_steps=args.smoke_steps or t['steps']
|
| 80 |
+
cfg.training.save_steps=args.smoke_steps or t['save_freq'];cfg.training.save_total_limit=3
|
| 81 |
+
cfg.training.learning_rate=t['optimizer']['lr'];cfg.training.optim='adamw_torch'
|
| 82 |
+
cfg.training.weight_decay=t['optimizer']['weight_decay']
|
| 83 |
+
cfg.training.max_grad_norm=t['optimizer']['grad_clip_norm']
|
| 84 |
+
cfg.training.warmup_ratio=t['scheduler']['warmup_ratio'];cfg.training.logging_steps=1 if args.smoke_steps else t['log_freq']
|
| 85 |
+
cfg.training.dataloader_num_workers=t['num_workers']
|
| 86 |
+
cfg.training.transformers_local_files_only=True
|
| 87 |
+
cfg.training.use_wandb=bool(simple['wandb']['enable'] and not args.smoke_steps)
|
| 88 |
+
cfg.training.wandb_project=simple['wandb']['project']
|
| 89 |
+
os.environ['WANDB_ENTITY']=simple['wandb']['entity']
|
| 90 |
+
groot_v3.install(view)
|
| 91 |
+
original=experiment.Gr00tTrainer
|
| 92 |
+
holder={}
|
| 93 |
+
|
| 94 |
+
class Progress(TrainerCallback):
|
| 95 |
+
def on_step_end(self,args,state,control,**kw):
|
| 96 |
+
loss=float(holder['last_loss'])
|
| 97 |
+
if not math.isfinite(loss): raise ValueError('Nonfinite loss')
|
| 98 |
+
report.update(status='running',last_step=state.global_step,heartbeat=time.time())
|
| 99 |
+
if state.global_step==1 or state.global_step%args.logging_steps==0 or report['smoke']:
|
| 100 |
+
row={'step':state.global_step,'loss':loss,'elapsed_s':time.time()-holder['started'],
|
| 101 |
+
'max_allocated_gib':torch.cuda.max_memory_allocated()/2**30}
|
| 102 |
+
report['measurements'].append(row);write(result,report)
|
| 103 |
+
print('RWH_STEP',json.dumps(row),flush=True)
|
| 104 |
+
def on_log(self,args,state,control,logs=None,**kw):
|
| 105 |
+
if logs and 'grad_norm' in logs and not math.isfinite(float(logs['grad_norm'])):
|
| 106 |
+
raise ValueError('Nonfinite gradient norm')
|
| 107 |
+
def on_train_end(self,args,state,control,**kw):
|
| 108 |
+
from scripts.train.checkpoints import validate_native_checkpoint
|
| 109 |
+
path=Path(args.output_dir)/f'checkpoint-{state.global_step}'
|
| 110 |
+
validate_native_checkpoint(path,state.global_step)
|
| 111 |
+
write(path/'rwh_save_complete.json',{'step':state.global_step,'fingerprint':view.fingerprint})
|
| 112 |
+
report.update(checkpoint=str(path),saved_step=state.global_step);write(result,report)
|
| 113 |
+
|
| 114 |
+
def create_trainer(*a,**kw):
|
| 115 |
+
trainer=original(*a,**kw)
|
| 116 |
+
# The instance is the unmodified native Gr00tTrainer. Only add a standard
|
| 117 |
+
# progress callback and observational model hooks; no loss/update overrides.
|
| 118 |
+
trainer.add_callback(Progress());holder['trainer']=trainer
|
| 119 |
+
report.update(parameters=sum(p.numel() for p in trainer.model.parameters()),
|
| 120 |
+
trainable_parameters=sum(p.numel() for p in trainer.model.parameters() if p.requires_grad))
|
| 121 |
+
def before(model,inputs,kwargs):
|
| 122 |
+
b=kwargs.get('inputs',inputs[0] if inputs else None)
|
| 123 |
+
if b['action'].shape!=(128,40,132):raise ValueError(f"Invalid batch: {b['action'].shape}")
|
| 124 |
+
if b['action_mask'][...,23:].any():raise ValueError('Padding entered loss')
|
| 125 |
+
holder['mask']=b['action_mask']
|
| 126 |
+
if 'batch_shapes' not in report:
|
| 127 |
+
report['batch_shapes']={k:list(v.shape) for k,v in b.items() if torch.is_tensor(v)}
|
| 128 |
+
report['masked_target_elements']=int((~b['action_mask'][...,:23].bool()).sum())
|
| 129 |
+
write(result,report);print('RWH_NATIVE_FIRST_BATCH',report['batch_shapes'],flush=True)
|
| 130 |
+
def after(model,inputs,output):
|
| 131 |
+
holder['last_loss']=output['loss'].detach().float().cpu().item()
|
| 132 |
+
holder['before_handle']=trainer.model.register_forward_pre_hook(before,with_kwargs=True)
|
| 133 |
+
holder['after_handle']=trainer.model.register_forward_hook(after)
|
| 134 |
+
if args.smoke_steps:
|
| 135 |
+
def capture(module,inputs,output):
|
| 136 |
+
if not output.requires_grad:return
|
| 137 |
+
mask=holder['mask'].bool()
|
| 138 |
+
def check(grad):
|
| 139 |
+
tail=grad[:,-mask.shape[1]:]
|
| 140 |
+
if tail[~mask].abs().sum().item()!=0 or tail[mask].abs().sum().item()==0:
|
| 141 |
+
raise ValueError('Native action-mask gradient check failed')
|
| 142 |
+
report['masked_gradient_checks']=report.get('masked_gradient_checks',0)+1
|
| 143 |
+
return grad
|
| 144 |
+
output.register_hook(check)
|
| 145 |
+
trainer.model.action_head.action_decoder.register_forward_hook(capture)
|
| 146 |
+
return trainer
|
| 147 |
+
experiment.Gr00tTrainer=create_trainer
|
| 148 |
+
try:
|
| 149 |
+
holder['started']=time.time();experiment.run(cfg)
|
| 150 |
+
holder['before_handle'].remove();holder['after_handle'].remove()
|
| 151 |
+
trainer=holder['trainer'];model=trainer.model;model.eval()
|
| 152 |
+
processor=trainer.train_dataset.processor;processor.eval()
|
| 153 |
+
ds=trainer.train_dataset.datasets[0]
|
| 154 |
+
i=next(i for i,s in enumerate(view.segments) if s['end']-s['start']>=40)
|
| 155 |
+
df=ds.episode_loader[i]
|
| 156 |
+
sample=ds.get_datapoint(df,0)
|
| 157 |
+
one=processor.collator([sample])['inputs'];one.pop('action',None);one.pop('action_mask',None)
|
| 158 |
+
one={k:v.to(model.device) for k,v in one.items()}
|
| 159 |
+
with torch.inference_mode(),torch.autocast('cuda',dtype=torch.bfloat16):
|
| 160 |
+
predicted=model.get_action(one)['action_pred']
|
| 161 |
+
if predicted.shape!=(1,40,132) or not torch.isfinite(predicted).all():raise ValueError('Invalid prediction')
|
| 162 |
+
report.update(status='passed',prediction_shape=list(predicted.shape),completed_at=time.time())
|
| 163 |
+
except BaseException as exc:
|
| 164 |
+
report.update(status='failed',error_type=type(exc).__name__,error=str(exc)[-2500:]);raise
|
| 165 |
+
finally:
|
| 166 |
+
write(result,report)
|
| 167 |
+
if args.smoke_steps:faulthandler.cancel_dump_traceback_later()
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
if __name__=='__main__': main()
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/start_watchers.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Start one controller and one HF watcher, detached from the SSH session."""
|
| 2 |
+
import json
|
| 3 |
+
import os
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import subprocess
|
| 6 |
+
import sys
|
| 7 |
+
|
| 8 |
+
def main():
|
| 9 |
+
root=Path(__file__).resolve().parents[2]
|
| 10 |
+
config=root/'configs/Bs128_30k/train'
|
| 11 |
+
out=Path('/home/work/mntvol/RwH-Kuavo_ckpt/v3_rawaction')
|
| 12 |
+
runs=out/'_runs';runs.mkdir(parents=True,exist_ok=True)
|
| 13 |
+
env=os.environ.copy();env['RWH_TRAIN_CONFIG_DIR']=str(config)
|
| 14 |
+
env['PYTHONPATH']=str(root)+':'+str(root.parent/'LeTools-Learning/third_party/lerobot/src')
|
| 15 |
+
jobs={}
|
| 16 |
+
for name,module,args in [
|
| 17 |
+
('controller','scripts.train.orchestrate',[]),
|
| 18 |
+
('hf_watcher','scripts.train.hf_watcher',['--config',str(config/'hf_watcher.json')])]:
|
| 19 |
+
log=(runs/(name+'.log')).open('a')
|
| 20 |
+
child=subprocess.Popen([sys.executable,'-u','-m',module,*args],env=env,cwd=root,
|
| 21 |
+
stdout=log,stderr=subprocess.STDOUT,start_new_session=True)
|
| 22 |
+
jobs[name]={'pid':child.pid,'log':str(runs/(name+'.log'))}
|
| 23 |
+
(runs/'watcher_processes.json').write_text(json.dumps(jobs,indent=2)+'\n')
|
| 24 |
+
print(json.dumps(jobs,indent=2))
|
| 25 |
+
|
| 26 |
+
if __name__=='__main__':main()
|
v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/validate_checkpoint.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Reload a real saved checkpoint in a separate process and predict physical actions."""
|
| 2 |
+
import argparse
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import sys
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def main():
|
| 10 |
+
ap=argparse.ArgumentParser();ap.add_argument('--policy',required=True);ap.add_argument('--checkpoint',required=True)
|
| 11 |
+
ap.add_argument('--gpu',required=True);ap.add_argument('--data-config',required=True);ap.add_argument('--output',required=True)
|
| 12 |
+
args=ap.parse_args();os.environ['CUDA_VISIBLE_DEVICES']=args.gpu
|
| 13 |
+
import torch
|
| 14 |
+
torch.set_num_threads(8)
|
| 15 |
+
import numpy as np
|
| 16 |
+
from scripts.data.view import DataView,lerobot_dataset
|
| 17 |
+
view=DataView(args.data_config);path=Path(args.checkpoint)
|
| 18 |
+
if args.policy=='gr00t_n17':
|
| 19 |
+
project=Path(__file__).resolve().parents[2]
|
| 20 |
+
sys.path.insert(0,str(project.parent/'LeTools-Learning/kuavo_model/external_models/gr00tn1d7'))
|
| 21 |
+
from scripts.train.native_assets import local_cosmos
|
| 22 |
+
from scripts.adapters.groot_v3 import install
|
| 23 |
+
from gr00t.model.gr00t_n1d7.gr00t_n1d7 import Gr00tN1d7
|
| 24 |
+
from gr00t.configs.model.gr00t_n1d7 import Gr00tN1d7Config
|
| 25 |
+
from gr00t.model.gr00t_n1d7.processing_gr00t_n1d7 import Gr00tN1d7Processor
|
| 26 |
+
from gr00t.data.dataset.sharded_single_step_dataset import ShardedSingleStepDataset
|
| 27 |
+
from gr00t.data.embodiment_tags import EmbodimentTag
|
| 28 |
+
cfg=Gr00tN1d7Config.from_pretrained(path);cfg.model_name=local_cosmos()
|
| 29 |
+
model,info=Gr00tN1d7.from_pretrained(path,config=cfg,output_loading_info=True,
|
| 30 |
+
transformers_loading_kwargs={'trust_remote_code':True,'local_files_only':True})
|
| 31 |
+
assert not any(info.get(k) for k in ['missing_keys','unexpected_keys','mismatched_keys'])
|
| 32 |
+
model=model.to('cuda').eval()
|
| 33 |
+
proc=Gr00tN1d7Processor.from_pretrained(path,model_name=local_cosmos(),
|
| 34 |
+
transformers_loading_kwargs={'trust_remote_code':True,'local_files_only':True})
|
| 35 |
+
proc.eval();install(view)
|
| 36 |
+
ds=ShardedSingleStepDataset(dataset_path=view.root,embodiment_tag=EmbodimentTag.NEW_EMBODIMENT,
|
| 37 |
+
modality_configs=proc.modality_configs['new_embodiment'],video_backend='torchcodec',shard_size=256)
|
| 38 |
+
ds.set_processor(proc)
|
| 39 |
+
i=next(i for i,s in enumerate(view.segments) if s['end']-s['start']>=40)
|
| 40 |
+
sample=ds.get_datapoint(ds.episode_loader[i],0)
|
| 41 |
+
masks_checked=0
|
| 42 |
+
anchors=view.anchors('gr00t_n17')
|
| 43 |
+
seq=anchors[:,None]+np.arange(40)[None,:]
|
| 44 |
+
bad=anchors[(~view.validity[seq]).any(axis=(1,2))]
|
| 45 |
+
for anchor in [int(anchors[0]),int(anchors[len(anchors)//2]),int(anchors[-1]),*map(int,bad[:3])]:
|
| 46 |
+
ep=int(view.episode[anchor]);segment=view.segments[ep]
|
| 47 |
+
df=ds.episode_loader[ep];encoded=ds.get_datapoint(df,anchor-segment['start'])
|
| 48 |
+
expected=view.raw['validity.action'][view.rows[anchor:anchor+40],:23]
|
| 49 |
+
assert np.array_equal(encoded['action_mask'][:,:23].numpy(),expected)
|
| 50 |
+
assert not encoded['action_mask'][:,23:].any()
|
| 51 |
+
lo=np.asarray(view.stats['action']['q01']);hi=np.asarray(view.stats['action']['q99'])
|
| 52 |
+
delta=hi-lo;good=~np.isclose(hi,lo)
|
| 53 |
+
normalized=np.zeros((40,23));normalized[:,good]=2*(view.action[anchor:anchor+40,good]-lo[good])/delta[good]-1
|
| 54 |
+
normalized=np.clip(normalized,-1,1)
|
| 55 |
+
np.testing.assert_allclose(encoded['action'][:,:23].numpy(),normalized,rtol=1e-5,atol=1e-5)
|
| 56 |
+
masks_checked+=int((~expected).sum())
|
| 57 |
+
assert masks_checked>0,'No real missing physical target was tested'
|
| 58 |
+
inp=proc.collator([sample])['inputs'];inp.pop('action');inp.pop('action_mask')
|
| 59 |
+
inp={k:v.cuda() for k,v in inp.items()}
|
| 60 |
+
with torch.inference_mode(),torch.autocast('cuda',dtype=torch.bfloat16): pred=model.get_action(inp)['action_pred']
|
| 61 |
+
assert pred.shape==(1,40,132) and torch.isfinite(pred).all()
|
| 62 |
+
# Native decoder preserves the learned physical group ordering.
|
| 63 |
+
physical=proc.decode_action(pred.float().cpu().numpy(),EmbodimentTag.NEW_EMBODIMENT,
|
| 64 |
+
state={'body':view.state[view.segments[i]['start']:view.segments[i]['start']+1][None]})
|
| 65 |
+
action=physical['body']
|
| 66 |
+
assert action.shape==(1,40,23) and np.isfinite(action).all()
|
| 67 |
+
else:
|
| 68 |
+
from scripts.adapters.language import install
|
| 69 |
+
install(args.policy)
|
| 70 |
+
from lerobot.policies.factory import get_policy_class,make_pre_post_processors
|
| 71 |
+
from lerobot.configs import PreTrainedConfig
|
| 72 |
+
cfg=PreTrainedConfig.from_pretrained(path)
|
| 73 |
+
model=get_policy_class(cfg.type).from_pretrained(path).to('cuda').eval()
|
| 74 |
+
pre,post=make_pre_post_processors(cfg,pretrained_path=str(path))
|
| 75 |
+
dataset=lerobot_dataset(view,args.policy)
|
| 76 |
+
sample=dataset[int(view.anchors(args.policy)[0])]
|
| 77 |
+
if args.policy=='multi_task_dit':
|
| 78 |
+
for k in list(sample):
|
| 79 |
+
if k.startswith('observation.'):sample[k]=sample[k][-1]
|
| 80 |
+
raw={k:v.unsqueeze(0) if torch.is_tensor(v) else [v] for k,v in sample.items()}
|
| 81 |
+
for k in view.cameras:raw[k]=raw[k].float()/255
|
| 82 |
+
batch=pre(raw);model.reset()
|
| 83 |
+
with torch.inference_mode():
|
| 84 |
+
if args.policy=='multi_task_dit':
|
| 85 |
+
first=model.select_action(batch);pred=torch.stack([first,*list(model._queues['action'])],dim=1)
|
| 86 |
+
else:pred=model.predict_action_chunk(batch)
|
| 87 |
+
assert torch.isfinite(pred).all() and pred.shape[-1]==23
|
| 88 |
+
action=post(pred[:,0]);assert action.shape==(1,23) and torch.isfinite(action).all()
|
| 89 |
+
report={'status':'passed','policy':args.policy,'checkpoint':str(path),'separate_process_reload':True,
|
| 90 |
+
'prediction_shape':list(pred.shape),'physical_action_shape':list(action.shape)}
|
| 91 |
+
if args.policy=='gr00t_n17':report.update(real_missing_target_elements_checked=masks_checked,native_normalization_matches_source=True)
|
| 92 |
+
Path(args.output).write_text(json.dumps(report,indent=2)+'\n');print(json.dumps(report))
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
if __name__=='__main__':main()
|