Whalswp commited on
Commit
ad018f9
·
verified ·
1 Parent(s): bdf70f1

Kuavo original action: multi_task_dit checkpoint 10K

Browse files
Files changed (49) hide show
  1. v2/multi_task_dit/checkpoints/checkpoint-10K/README.md +7 -0
  2. v2/multi_task_dit/checkpoints/checkpoint-10K/checkpoint_manifest.json +302 -0
  3. v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/config.json +110 -0
  4. v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/model.safetensors +3 -0
  5. v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_postprocessor.json +32 -0
  6. v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_postprocessor_step_0_unnormalizer_processor.safetensors +3 -0
  7. v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_preprocessor.json +83 -0
  8. v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/policy_preprocessor_step_4_normalizer_processor.safetensors +3 -0
  9. v2/multi_task_dit/checkpoints/checkpoint-10K/pretrained_model/train_config.json +249 -0
  10. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/DATA_CHANGES.md +7 -0
  11. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/dataset.yaml +31 -0
  12. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/prepare.py +8 -0
  13. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/data/verify.py +8 -0
  14. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/CONTRACT.md +19 -0
  15. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/compat.py +4 -0
  16. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/gr00t_n15.yaml +55 -0
  17. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/gr00t_n17.yaml +57 -0
  18. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/hf_watcher.py +7 -0
  19. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/launch.sh +18 -0
  20. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/load_n17.py +4 -0
  21. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/loss_mask.py +4 -0
  22. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/multi_task_dit.yaml +33 -0
  23. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/native_assets.py +4 -0
  24. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/orchestrate.py +7 -0
  25. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/run.py +7 -0
  26. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/run_n17.py +7 -0
  27. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/total/gr00t_n15_total.yaml +104 -0
  28. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/configs/Bs128_30k/train/total/multi_task_dit_total.yaml +118 -0
  29. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/__init__.py +1 -0
  30. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/__init__.py +1 -0
  31. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/action_validity.py +98 -0
  32. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/groot_v3.py +86 -0
  33. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/adapters/language.py +56 -0
  34. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/__init__.py +1 -0
  35. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/prepare.py +18 -0
  36. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/validate.py +67 -0
  37. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/data/view.py +166 -0
  38. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/tests/test_contract.py +65 -0
  39. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/__init__.py +1 -0
  40. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/checkpoints.py +35 -0
  41. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/hf_watcher.py +363 -0
  42. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/language_guard.py +13 -0
  43. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/load_n17.py +20 -0
  44. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/native_assets.py +48 -0
  45. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/orchestrate.py +92 -0
  46. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/run_lerobot.py +312 -0
  47. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/run_n17.py +170 -0
  48. v2/multi_task_dit/checkpoints/checkpoint-10K/provenance/scripts/train/start_watchers.py +26 -0
  49. 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()