Publish completed intercept QwenOFT H1 mean-profile 5000-update run
Browse files- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/README.md +8 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/checkpoints/model.pt +3 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/collection_quality_report.json +306 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/config.full.yaml +102 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/config.yaml +41 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/dataset_statistics.json +127 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/dataset_statistics_eval.json +127 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/files.sha256.json +106 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/launch_audit.json +29 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/manifest.json +10 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/profile.source.json +66 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/provenance.json +415 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/startup_verified.json +56 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/summary.jsonl +10 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/task_contract.json +131 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/QWen3.py +444 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/QwenOFT.py +1244 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/datasets.py +0 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/doom-mean-reset.patch +192 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/gym-language-contract.patch +17 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/loader-spawn-cache.patch +18 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/loader-spawn-test.patch +31 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/mean-profile-preparation.patch +147 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/train_starvla.py +0 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/trainer_tools.py +736 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/worker_context.py +77 -0
- latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training_results.json +20 -0
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# intercept QwenOFT H1 mean-profile VLA
|
| 2 |
+
|
| 3 |
+
Training complete:5000 optimizer updates, seed0, globalbatch128, microbatch64 perGPU, accumulation1, gradientcheckpointingTrue, BF16/ZeRO2. Hardware:2 RTX PRO6000 Blackwell GPUs. Fixed mean99.05021289731565ms uses originalRTX3090 profile; all actual science/runtime settings and user-authorized throughput changes are recorded in config.full.yaml/launch_audit.json. Horizon1, native state/action dimensions7/7.
|
| 4 |
+
|
| 5 |
+
Puretensor weights checkCPUweights_only/allfinite passed. Portable inferenceconfig, normalization/statistics, taskcontract, sourcecode/profile/data provenance included. Full optimizer recovery remains local. Finalofflinevalidation loss0.03070492297410965; this is not closed-loop task-performance acceptance. Latency-bench evaluation pending until all four models finish.
|
| 6 |
+
|
| 7 |
+
Dataset:https://huggingface.co/datasets/latency-sensitive-bench/Standard-Pipeline/tree/bba7d56c0ad5bb2b6d6ed07adb70b4f1afd5d25a/mikasa_intercept_grab_fast/profile_latency/QwenOFT/mean_rtx3090_h1_s123_275ep_v1
|
| 8 |
+
Training:https://wandb.ai/dongqianyu99-zhejiang-university/starvla_tasks/runs/mean3090-intercept-vla-blackwell-20260930
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/checkpoints/model.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7bea6746bf7973ff7d4e4e8a457a3bfbc0ce981c80fe5f7afadca651ec5ca85e
|
| 3 |
+
size 9785101729
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/collection_quality_report.json
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"episodes": 275,
|
| 3 |
+
"unique_seeds": 275,
|
| 4 |
+
"rows": 12211,
|
| 5 |
+
"length_min": 37,
|
| 6 |
+
"length_max": 55,
|
| 7 |
+
"all_success_once": true,
|
| 8 |
+
"state_dim": 7,
|
| 9 |
+
"action_dim": 7,
|
| 10 |
+
"rgb_shape": [
|
| 11 |
+
128,
|
| 12 |
+
128,
|
| 13 |
+
6
|
| 14 |
+
],
|
| 15 |
+
"finite_numeric": true,
|
| 16 |
+
"latency_raw_frames": 2,
|
| 17 |
+
"applied_command_matches_source_issued_action": true,
|
| 18 |
+
"action_horizon": 1,
|
| 19 |
+
"dagger": false,
|
| 20 |
+
"raw_bytes": 1204453838,
|
| 21 |
+
"episode_seeds": [
|
| 22 |
+
0,
|
| 23 |
+
1,
|
| 24 |
+
10,
|
| 25 |
+
180,
|
| 26 |
+
181,
|
| 27 |
+
182,
|
| 28 |
+
183,
|
| 29 |
+
184,
|
| 30 |
+
185,
|
| 31 |
+
186,
|
| 32 |
+
187,
|
| 33 |
+
188,
|
| 34 |
+
189,
|
| 35 |
+
11,
|
| 36 |
+
190,
|
| 37 |
+
191,
|
| 38 |
+
192,
|
| 39 |
+
193,
|
| 40 |
+
194,
|
| 41 |
+
195,
|
| 42 |
+
196,
|
| 43 |
+
197,
|
| 44 |
+
198,
|
| 45 |
+
199,
|
| 46 |
+
12,
|
| 47 |
+
20,
|
| 48 |
+
21,
|
| 49 |
+
22,
|
| 50 |
+
23,
|
| 51 |
+
24,
|
| 52 |
+
25,
|
| 53 |
+
26,
|
| 54 |
+
27,
|
| 55 |
+
28,
|
| 56 |
+
29,
|
| 57 |
+
13,
|
| 58 |
+
200,
|
| 59 |
+
201,
|
| 60 |
+
202,
|
| 61 |
+
203,
|
| 62 |
+
204,
|
| 63 |
+
205,
|
| 64 |
+
206,
|
| 65 |
+
207,
|
| 66 |
+
208,
|
| 67 |
+
209,
|
| 68 |
+
14,
|
| 69 |
+
210,
|
| 70 |
+
211,
|
| 71 |
+
212,
|
| 72 |
+
213,
|
| 73 |
+
214,
|
| 74 |
+
215,
|
| 75 |
+
216,
|
| 76 |
+
217,
|
| 77 |
+
218,
|
| 78 |
+
219,
|
| 79 |
+
15,
|
| 80 |
+
220,
|
| 81 |
+
221,
|
| 82 |
+
222,
|
| 83 |
+
223,
|
| 84 |
+
224,
|
| 85 |
+
225,
|
| 86 |
+
226,
|
| 87 |
+
227,
|
| 88 |
+
228,
|
| 89 |
+
229,
|
| 90 |
+
16,
|
| 91 |
+
230,
|
| 92 |
+
231,
|
| 93 |
+
232,
|
| 94 |
+
233,
|
| 95 |
+
234,
|
| 96 |
+
235,
|
| 97 |
+
236,
|
| 98 |
+
237,
|
| 99 |
+
238,
|
| 100 |
+
239,
|
| 101 |
+
17,
|
| 102 |
+
240,
|
| 103 |
+
241,
|
| 104 |
+
242,
|
| 105 |
+
243,
|
| 106 |
+
244,
|
| 107 |
+
245,
|
| 108 |
+
246,
|
| 109 |
+
247,
|
| 110 |
+
248,
|
| 111 |
+
249,
|
| 112 |
+
18,
|
| 113 |
+
250,
|
| 114 |
+
251,
|
| 115 |
+
252,
|
| 116 |
+
253,
|
| 117 |
+
254,
|
| 118 |
+
255,
|
| 119 |
+
256,
|
| 120 |
+
257,
|
| 121 |
+
258,
|
| 122 |
+
259,
|
| 123 |
+
19,
|
| 124 |
+
260,
|
| 125 |
+
261,
|
| 126 |
+
262,
|
| 127 |
+
263,
|
| 128 |
+
264,
|
| 129 |
+
265,
|
| 130 |
+
266,
|
| 131 |
+
267,
|
| 132 |
+
268,
|
| 133 |
+
269,
|
| 134 |
+
2,
|
| 135 |
+
100,
|
| 136 |
+
270,
|
| 137 |
+
271,
|
| 138 |
+
272,
|
| 139 |
+
273,
|
| 140 |
+
274,
|
| 141 |
+
30,
|
| 142 |
+
31,
|
| 143 |
+
32,
|
| 144 |
+
33,
|
| 145 |
+
34,
|
| 146 |
+
101,
|
| 147 |
+
35,
|
| 148 |
+
36,
|
| 149 |
+
37,
|
| 150 |
+
38,
|
| 151 |
+
39,
|
| 152 |
+
40,
|
| 153 |
+
41,
|
| 154 |
+
42,
|
| 155 |
+
43,
|
| 156 |
+
44,
|
| 157 |
+
102,
|
| 158 |
+
45,
|
| 159 |
+
46,
|
| 160 |
+
47,
|
| 161 |
+
48,
|
| 162 |
+
49,
|
| 163 |
+
50,
|
| 164 |
+
51,
|
| 165 |
+
52,
|
| 166 |
+
53,
|
| 167 |
+
54,
|
| 168 |
+
103,
|
| 169 |
+
55,
|
| 170 |
+
56,
|
| 171 |
+
57,
|
| 172 |
+
58,
|
| 173 |
+
59,
|
| 174 |
+
60,
|
| 175 |
+
61,
|
| 176 |
+
62,
|
| 177 |
+
63,
|
| 178 |
+
64,
|
| 179 |
+
104,
|
| 180 |
+
65,
|
| 181 |
+
66,
|
| 182 |
+
67,
|
| 183 |
+
68,
|
| 184 |
+
69,
|
| 185 |
+
70,
|
| 186 |
+
71,
|
| 187 |
+
72,
|
| 188 |
+
73,
|
| 189 |
+
74,
|
| 190 |
+
105,
|
| 191 |
+
75,
|
| 192 |
+
76,
|
| 193 |
+
77,
|
| 194 |
+
78,
|
| 195 |
+
79,
|
| 196 |
+
80,
|
| 197 |
+
81,
|
| 198 |
+
82,
|
| 199 |
+
83,
|
| 200 |
+
84,
|
| 201 |
+
106,
|
| 202 |
+
85,
|
| 203 |
+
86,
|
| 204 |
+
87,
|
| 205 |
+
88,
|
| 206 |
+
89,
|
| 207 |
+
90,
|
| 208 |
+
91,
|
| 209 |
+
92,
|
| 210 |
+
93,
|
| 211 |
+
94,
|
| 212 |
+
107,
|
| 213 |
+
95,
|
| 214 |
+
96,
|
| 215 |
+
97,
|
| 216 |
+
98,
|
| 217 |
+
99,
|
| 218 |
+
108,
|
| 219 |
+
109,
|
| 220 |
+
3,
|
| 221 |
+
110,
|
| 222 |
+
111,
|
| 223 |
+
112,
|
| 224 |
+
113,
|
| 225 |
+
114,
|
| 226 |
+
115,
|
| 227 |
+
116,
|
| 228 |
+
117,
|
| 229 |
+
118,
|
| 230 |
+
119,
|
| 231 |
+
4,
|
| 232 |
+
120,
|
| 233 |
+
121,
|
| 234 |
+
122,
|
| 235 |
+
123,
|
| 236 |
+
124,
|
| 237 |
+
125,
|
| 238 |
+
126,
|
| 239 |
+
127,
|
| 240 |
+
128,
|
| 241 |
+
129,
|
| 242 |
+
5,
|
| 243 |
+
130,
|
| 244 |
+
131,
|
| 245 |
+
132,
|
| 246 |
+
133,
|
| 247 |
+
134,
|
| 248 |
+
135,
|
| 249 |
+
136,
|
| 250 |
+
137,
|
| 251 |
+
138,
|
| 252 |
+
139,
|
| 253 |
+
6,
|
| 254 |
+
140,
|
| 255 |
+
141,
|
| 256 |
+
142,
|
| 257 |
+
143,
|
| 258 |
+
144,
|
| 259 |
+
145,
|
| 260 |
+
146,
|
| 261 |
+
147,
|
| 262 |
+
148,
|
| 263 |
+
149,
|
| 264 |
+
7,
|
| 265 |
+
150,
|
| 266 |
+
151,
|
| 267 |
+
152,
|
| 268 |
+
153,
|
| 269 |
+
154,
|
| 270 |
+
155,
|
| 271 |
+
156,
|
| 272 |
+
157,
|
| 273 |
+
158,
|
| 274 |
+
159,
|
| 275 |
+
8,
|
| 276 |
+
160,
|
| 277 |
+
161,
|
| 278 |
+
162,
|
| 279 |
+
163,
|
| 280 |
+
164,
|
| 281 |
+
165,
|
| 282 |
+
166,
|
| 283 |
+
167,
|
| 284 |
+
168,
|
| 285 |
+
169,
|
| 286 |
+
9,
|
| 287 |
+
170,
|
| 288 |
+
171,
|
| 289 |
+
172,
|
| 290 |
+
173,
|
| 291 |
+
174,
|
| 292 |
+
175,
|
| 293 |
+
176,
|
| 294 |
+
177,
|
| 295 |
+
178,
|
| 296 |
+
179
|
| 297 |
+
],
|
| 298 |
+
"train_episodes": 250,
|
| 299 |
+
"val_episodes": 25,
|
| 300 |
+
"train_rows": 11119,
|
| 301 |
+
"val_rows": 1092,
|
| 302 |
+
"teacher_checkpoint": "/home/ubuntu/lzj/mean-profiling/intercept/teacher-publication/checkpoint.pt",
|
| 303 |
+
"teacher_sha256": "e47526f03fad3ed251dd78f3cacfba8aa8b60bd1578f40bd7a1393e17298e973",
|
| 304 |
+
"fixed_latency_ms": 99.05021289731565,
|
| 305 |
+
"conversion_complete": true
|
| 306 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/config.full.yaml
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
run_id: intercept-qwenoft-mean-3090-h1-s0
|
| 2 |
+
run_root_dir: /home/ubuntu/lzj/mean-profiling/intercept/vla
|
| 3 |
+
seed: 0
|
| 4 |
+
wandb_name: intercept-qwenoft-mean-3090-h1-s0
|
| 5 |
+
wandb_project: starvla_tasks
|
| 6 |
+
wandb_entity: dongqianyu99-zhejiang-university
|
| 7 |
+
wandb_group: mean-profiling-3090-blackwell
|
| 8 |
+
wandb_tags:
|
| 9 |
+
- intercept
|
| 10 |
+
- qwenoft
|
| 11 |
+
- mean-profile
|
| 12 |
+
- h1
|
| 13 |
+
training_latency_condition: mean-profile
|
| 14 |
+
is_debug: false
|
| 15 |
+
version_id: '0.21'
|
| 16 |
+
framework:
|
| 17 |
+
name: QwenOFT
|
| 18 |
+
qwenvl:
|
| 19 |
+
base_vlm: /home/ubuntu/lzj/models/Qwen3-VL-4B-Instruct
|
| 20 |
+
attn_implementation: flash_attention_2
|
| 21 |
+
enable_gradient_checkpointing: true
|
| 22 |
+
action_model:
|
| 23 |
+
action_model_type: MLP
|
| 24 |
+
action_dim: 7
|
| 25 |
+
state_dim: 7
|
| 26 |
+
action_hidden_dim: 2560
|
| 27 |
+
future_action_window_size: 0
|
| 28 |
+
past_action_window_size: 0
|
| 29 |
+
loss_type: l1
|
| 30 |
+
state_encoding: continuous_projector
|
| 31 |
+
task_objective: null
|
| 32 |
+
action_env_dim: 7
|
| 33 |
+
action_horizon: 1
|
| 34 |
+
datasets:
|
| 35 |
+
vla_data:
|
| 36 |
+
dataset_py: latency_bench_tasks
|
| 37 |
+
lerobot_version: v3.0
|
| 38 |
+
include_state: true
|
| 39 |
+
task_contract_path: /home/ubuntu/lzj/mean-profiling/intercept/data/lerobot/task_contract.json
|
| 40 |
+
data_root_dir: /
|
| 41 |
+
data_mix: train
|
| 42 |
+
eval_data_mix: validation
|
| 43 |
+
custom_mixtures_path: /home/ubuntu/lzj/mean-profiling/intercept/data/mixture.json
|
| 44 |
+
sequential_step_sampling: true
|
| 45 |
+
per_device_batch_size: 64
|
| 46 |
+
pin_memory: true
|
| 47 |
+
persistent_workers: true
|
| 48 |
+
num_workers: 2
|
| 49 |
+
prefetch_factor: 4
|
| 50 |
+
obs_image_size:
|
| 51 |
+
- 224
|
| 52 |
+
- 224
|
| 53 |
+
video_backend: pyav
|
| 54 |
+
image_mode: single
|
| 55 |
+
stitch_grid:
|
| 56 |
+
- 1
|
| 57 |
+
- 2
|
| 58 |
+
num_obs_frames: 1
|
| 59 |
+
trainer:
|
| 60 |
+
max_train_steps: 5000
|
| 61 |
+
num_warmup_steps: 100
|
| 62 |
+
save_interval: 500
|
| 63 |
+
eval_interval: 5000
|
| 64 |
+
eval_num_batches: 1
|
| 65 |
+
logging_frequency: 5
|
| 66 |
+
learning_rate:
|
| 67 |
+
base: 3.0e-05
|
| 68 |
+
action_model: 0.0001
|
| 69 |
+
lr_scheduler_type: cosine_with_min_lr
|
| 70 |
+
scheduler_specific_kwargs:
|
| 71 |
+
min_lr: 1.0e-06
|
| 72 |
+
freeze_modules: ''
|
| 73 |
+
freeze_vit: false
|
| 74 |
+
freeze_tied_embedding: false
|
| 75 |
+
freeze_llm_layers: []
|
| 76 |
+
loss_scale:
|
| 77 |
+
vla: 1.0
|
| 78 |
+
gradient_clipping: 1.0
|
| 79 |
+
gradient_accumulation_steps: 1
|
| 80 |
+
pretrained_checkpoint: null
|
| 81 |
+
is_resume: false
|
| 82 |
+
distributed_backend: deepspeed
|
| 83 |
+
optimizer:
|
| 84 |
+
name: AdamW
|
| 85 |
+
betas:
|
| 86 |
+
- 0.9
|
| 87 |
+
- 0.95
|
| 88 |
+
eps: 1.0e-08
|
| 89 |
+
weight_decay: 1.0e-08
|
| 90 |
+
fused: true
|
| 91 |
+
profile_timing:
|
| 92 |
+
enabled: false
|
| 93 |
+
log_interval: 10
|
| 94 |
+
checkpoint:
|
| 95 |
+
save_best_model: false
|
| 96 |
+
save_final_model: true
|
| 97 |
+
save_pt_file: true
|
| 98 |
+
save_training_state: true
|
| 99 |
+
local:
|
| 100 |
+
keep_last_n: 1
|
| 101 |
+
config_yaml: /home/ubuntu/lzj/mean-profiling/intercept/vla.yaml
|
| 102 |
+
output_dir: /home/ubuntu/lzj/mean-profiling/intercept/vla/intercept-qwenoft-mean-3090-h1-s0
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/config.yaml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
framework:
|
| 2 |
+
name: QwenOFT
|
| 3 |
+
qwenvl:
|
| 4 |
+
base_vlm: Qwen/Qwen3-VL-4B-Instruct
|
| 5 |
+
attn_implementation: flash_attention_2
|
| 6 |
+
enable_gradient_checkpointing: true
|
| 7 |
+
action_model:
|
| 8 |
+
action_model_type: MLP
|
| 9 |
+
action_dim: 7
|
| 10 |
+
state_dim: 7
|
| 11 |
+
action_hidden_dim: 2560
|
| 12 |
+
future_action_window_size: 0
|
| 13 |
+
past_action_window_size: 0
|
| 14 |
+
loss_type: l1
|
| 15 |
+
state_encoding: continuous_projector
|
| 16 |
+
task_objective: null
|
| 17 |
+
action_env_dim: 7
|
| 18 |
+
action_horizon: 1
|
| 19 |
+
version_id: '0.21'
|
| 20 |
+
datasets:
|
| 21 |
+
vla_data:
|
| 22 |
+
include_state: true
|
| 23 |
+
obs_image_size:
|
| 24 |
+
- 224
|
| 25 |
+
- 224
|
| 26 |
+
image_mode: single
|
| 27 |
+
stitch_grid:
|
| 28 |
+
- 1
|
| 29 |
+
- 2
|
| 30 |
+
num_obs_frames: 1
|
| 31 |
+
trainer:
|
| 32 |
+
pretrained_checkpoint: null
|
| 33 |
+
backbone:
|
| 34 |
+
repo_id: Qwen/Qwen3-VL-4B-Instruct
|
| 35 |
+
revision: ebb281ec70b05090aa6165b016eac8ec08e71b17
|
| 36 |
+
allow_patterns:
|
| 37 |
+
- '*.json'
|
| 38 |
+
- '*.safetensors'
|
| 39 |
+
- '*.txt'
|
| 40 |
+
- '*.jinja'
|
| 41 |
+
- '*.model'
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/dataset_statistics.json
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"new_embodiment": {
|
| 3 |
+
"action": {
|
| 4 |
+
"mean": [
|
| 5 |
+
-0.048697348684072495,
|
| 6 |
+
0.24886493384838104,
|
| 7 |
+
-0.16479986906051636,
|
| 8 |
+
-0.032609131187200546,
|
| 9 |
+
-0.501691997051239,
|
| 10 |
+
0.44703611731529236,
|
| 11 |
+
0.6793776154518127
|
| 12 |
+
],
|
| 13 |
+
"std": [
|
| 14 |
+
0.1079770028591156,
|
| 15 |
+
0.47203660011291504,
|
| 16 |
+
0.14475314319133759,
|
| 17 |
+
0.2849433720111847,
|
| 18 |
+
0.318120539188385,
|
| 19 |
+
0.35324087738990784,
|
| 20 |
+
0.6349603533744812
|
| 21 |
+
],
|
| 22 |
+
"max": [
|
| 23 |
+
0.30179348587989807,
|
| 24 |
+
0.7795099020004272,
|
| 25 |
+
0.2688712179660797,
|
| 26 |
+
0.8059309720993042,
|
| 27 |
+
0.5333064198493958,
|
| 28 |
+
1.0,
|
| 29 |
+
1.0
|
| 30 |
+
],
|
| 31 |
+
"min": [
|
| 32 |
+
-0.6572320461273193,
|
| 33 |
+
-1.0,
|
| 34 |
+
-0.8218780755996704,
|
| 35 |
+
-0.7686842083930969,
|
| 36 |
+
-0.9920897483825684,
|
| 37 |
+
-0.30364304780960083,
|
| 38 |
+
-1.0
|
| 39 |
+
],
|
| 40 |
+
"q01": [
|
| 41 |
+
-0.4552440994977951,
|
| 42 |
+
-1.0,
|
| 43 |
+
-0.7419710528850555,
|
| 44 |
+
-0.475678391456604,
|
| 45 |
+
-0.9305388247966766,
|
| 46 |
+
-0.20442348539829253,
|
| 47 |
+
-0.8981475746631622
|
| 48 |
+
],
|
| 49 |
+
"q99": [
|
| 50 |
+
0.1125871029496192,
|
| 51 |
+
0.7402709639072418,
|
| 52 |
+
0.22590837419033025,
|
| 53 |
+
0.7005072689056396,
|
| 54 |
+
0.16810789704322815,
|
| 55 |
+
1.0,
|
| 56 |
+
1.0
|
| 57 |
+
],
|
| 58 |
+
"mask": [
|
| 59 |
+
true,
|
| 60 |
+
true,
|
| 61 |
+
true,
|
| 62 |
+
true,
|
| 63 |
+
true,
|
| 64 |
+
true,
|
| 65 |
+
true
|
| 66 |
+
]
|
| 67 |
+
},
|
| 68 |
+
"state": {
|
| 69 |
+
"mean": [
|
| 70 |
+
-0.14554812014102936,
|
| 71 |
+
0.07433967292308807,
|
| 72 |
+
0.1436367779970169,
|
| 73 |
+
2.700871706008911,
|
| 74 |
+
0.45681115984916687,
|
| 75 |
+
-0.48229172825813293,
|
| 76 |
+
0.06972828507423401
|
| 77 |
+
],
|
| 78 |
+
"std": [
|
| 79 |
+
0.07678605616092682,
|
| 80 |
+
0.21988309919834137,
|
| 81 |
+
0.09388109296560287,
|
| 82 |
+
1.109084963798523,
|
| 83 |
+
0.3012843728065491,
|
| 84 |
+
0.3006882667541504,
|
| 85 |
+
0.0164483655244112
|
| 86 |
+
],
|
| 87 |
+
"max": [
|
| 88 |
+
0.014208495616912842,
|
| 89 |
+
0.5909395217895508,
|
| 90 |
+
0.3558541536331177,
|
| 91 |
+
3.1415467262268066,
|
| 92 |
+
0.9420600533485413,
|
| 93 |
+
0.09770228713750839,
|
| 94 |
+
0.07999999821186066
|
| 95 |
+
],
|
| 96 |
+
"min": [
|
| 97 |
+
-0.37446925044059753,
|
| 98 |
+
-0.29720592498779297,
|
| 99 |
+
0.016940969973802567,
|
| 100 |
+
-3.140481948852539,
|
| 101 |
+
-0.10707131028175354,
|
| 102 |
+
-1.2001688480377197,
|
| 103 |
+
0.03238735347986221
|
| 104 |
+
],
|
| 105 |
+
"q01": [
|
| 106 |
+
-0.3139808464050293,
|
| 107 |
+
-0.25229972660541533,
|
| 108 |
+
0.0188405978679657,
|
| 109 |
+
-3.134360270500183,
|
| 110 |
+
-0.04521620273590088,
|
| 111 |
+
-1.0301372480392457,
|
| 112 |
+
0.03585549935698509
|
| 113 |
+
],
|
| 114 |
+
"q99": [
|
| 115 |
+
-0.013631248474121185,
|
| 116 |
+
0.5275326526165007,
|
| 117 |
+
0.33348673582077026,
|
| 118 |
+
3.1367013454437256,
|
| 119 |
+
0.8922865676879882,
|
| 120 |
+
0.056985778063535636,
|
| 121 |
+
0.07999999821186066
|
| 122 |
+
]
|
| 123 |
+
},
|
| 124 |
+
"num_transitions": 11119,
|
| 125 |
+
"num_trajectories": 250
|
| 126 |
+
}
|
| 127 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/dataset_statistics_eval.json
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"new_embodiment": {
|
| 3 |
+
"action": {
|
| 4 |
+
"mean": [
|
| 5 |
+
-0.048697348684072495,
|
| 6 |
+
0.24886493384838104,
|
| 7 |
+
-0.16479986906051636,
|
| 8 |
+
-0.032609131187200546,
|
| 9 |
+
-0.501691997051239,
|
| 10 |
+
0.44703611731529236,
|
| 11 |
+
0.6793776154518127
|
| 12 |
+
],
|
| 13 |
+
"std": [
|
| 14 |
+
0.1079770028591156,
|
| 15 |
+
0.47203660011291504,
|
| 16 |
+
0.14475314319133759,
|
| 17 |
+
0.2849433720111847,
|
| 18 |
+
0.318120539188385,
|
| 19 |
+
0.35324087738990784,
|
| 20 |
+
0.6349603533744812
|
| 21 |
+
],
|
| 22 |
+
"max": [
|
| 23 |
+
0.30179348587989807,
|
| 24 |
+
0.7795099020004272,
|
| 25 |
+
0.2688712179660797,
|
| 26 |
+
0.8059309720993042,
|
| 27 |
+
0.5333064198493958,
|
| 28 |
+
1.0,
|
| 29 |
+
1.0
|
| 30 |
+
],
|
| 31 |
+
"min": [
|
| 32 |
+
-0.6572320461273193,
|
| 33 |
+
-1.0,
|
| 34 |
+
-0.8218780755996704,
|
| 35 |
+
-0.7686842083930969,
|
| 36 |
+
-0.9920897483825684,
|
| 37 |
+
-0.30364304780960083,
|
| 38 |
+
-1.0
|
| 39 |
+
],
|
| 40 |
+
"q01": [
|
| 41 |
+
-0.4552440994977951,
|
| 42 |
+
-1.0,
|
| 43 |
+
-0.7419710528850555,
|
| 44 |
+
-0.475678391456604,
|
| 45 |
+
-0.9305388247966766,
|
| 46 |
+
-0.20442348539829253,
|
| 47 |
+
-0.8981475746631622
|
| 48 |
+
],
|
| 49 |
+
"q99": [
|
| 50 |
+
0.1125871029496192,
|
| 51 |
+
0.7402709639072418,
|
| 52 |
+
0.22590837419033025,
|
| 53 |
+
0.7005072689056396,
|
| 54 |
+
0.16810789704322815,
|
| 55 |
+
1.0,
|
| 56 |
+
1.0
|
| 57 |
+
],
|
| 58 |
+
"mask": [
|
| 59 |
+
true,
|
| 60 |
+
true,
|
| 61 |
+
true,
|
| 62 |
+
true,
|
| 63 |
+
true,
|
| 64 |
+
true,
|
| 65 |
+
true
|
| 66 |
+
]
|
| 67 |
+
},
|
| 68 |
+
"state": {
|
| 69 |
+
"mean": [
|
| 70 |
+
-0.14554812014102936,
|
| 71 |
+
0.07433967292308807,
|
| 72 |
+
0.1436367779970169,
|
| 73 |
+
2.700871706008911,
|
| 74 |
+
0.45681115984916687,
|
| 75 |
+
-0.48229172825813293,
|
| 76 |
+
0.06972828507423401
|
| 77 |
+
],
|
| 78 |
+
"std": [
|
| 79 |
+
0.07678605616092682,
|
| 80 |
+
0.21988309919834137,
|
| 81 |
+
0.09388109296560287,
|
| 82 |
+
1.109084963798523,
|
| 83 |
+
0.3012843728065491,
|
| 84 |
+
0.3006882667541504,
|
| 85 |
+
0.0164483655244112
|
| 86 |
+
],
|
| 87 |
+
"max": [
|
| 88 |
+
0.014208495616912842,
|
| 89 |
+
0.5909395217895508,
|
| 90 |
+
0.3558541536331177,
|
| 91 |
+
3.1415467262268066,
|
| 92 |
+
0.9420600533485413,
|
| 93 |
+
0.09770228713750839,
|
| 94 |
+
0.07999999821186066
|
| 95 |
+
],
|
| 96 |
+
"min": [
|
| 97 |
+
-0.37446925044059753,
|
| 98 |
+
-0.29720592498779297,
|
| 99 |
+
0.016940969973802567,
|
| 100 |
+
-3.140481948852539,
|
| 101 |
+
-0.10707131028175354,
|
| 102 |
+
-1.2001688480377197,
|
| 103 |
+
0.03238735347986221
|
| 104 |
+
],
|
| 105 |
+
"q01": [
|
| 106 |
+
-0.3139808464050293,
|
| 107 |
+
-0.25229972660541533,
|
| 108 |
+
0.0188405978679657,
|
| 109 |
+
-3.134360270500183,
|
| 110 |
+
-0.04521620273590088,
|
| 111 |
+
-1.0301372480392457,
|
| 112 |
+
0.03585549935698509
|
| 113 |
+
],
|
| 114 |
+
"q99": [
|
| 115 |
+
-0.013631248474121185,
|
| 116 |
+
0.5275326526165007,
|
| 117 |
+
0.33348673582077026,
|
| 118 |
+
3.1367013454437256,
|
| 119 |
+
0.8922865676879882,
|
| 120 |
+
0.056985778063535636,
|
| 121 |
+
0.07999999821186066
|
| 122 |
+
]
|
| 123 |
+
},
|
| 124 |
+
"num_transitions": 1092,
|
| 125 |
+
"num_trajectories": 25
|
| 126 |
+
}
|
| 127 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/files.sha256.json
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"README.md": {
|
| 3 |
+
"bytes": 1155,
|
| 4 |
+
"sha256": "c9ccbe7aa494be2270decacd8f7f3f56afbd0e5c57c58529b6e9c08d72fe5402"
|
| 5 |
+
},
|
| 6 |
+
"checkpoints/model.pt": {
|
| 7 |
+
"bytes": 9785101729,
|
| 8 |
+
"sha256": "7bea6746bf7973ff7d4e4e8a457a3bfbc0ce981c80fe5f7afadca651ec5ca85e"
|
| 9 |
+
},
|
| 10 |
+
"collection_quality_report.json": {
|
| 11 |
+
"bytes": 3134,
|
| 12 |
+
"sha256": "ce3effc9d3267edf410642d0ecd3b5b7790c072e1ff5d0f79eeab4ae883fe1a1"
|
| 13 |
+
},
|
| 14 |
+
"config.full.yaml": {
|
| 15 |
+
"bytes": 2574,
|
| 16 |
+
"sha256": "e9e1231302aa3ae47efd1673f356c0004daf815f47eb9c02ba62714d89b53f0b"
|
| 17 |
+
},
|
| 18 |
+
"config.yaml": {
|
| 19 |
+
"bytes": 871,
|
| 20 |
+
"sha256": "d0612a41944af616e4750bb1217353a90a562ac978571d9ebd663ec7caae61ed"
|
| 21 |
+
},
|
| 22 |
+
"dataset_statistics.json": {
|
| 23 |
+
"bytes": 2843,
|
| 24 |
+
"sha256": "7a5fde37c6a0b220321d0acdb73b8b48b7ffe9f6295b7a0bb0e0abcf4849b653"
|
| 25 |
+
},
|
| 26 |
+
"dataset_statistics_eval.json": {
|
| 27 |
+
"bytes": 2841,
|
| 28 |
+
"sha256": "fcbc27a8dc2d8f6f8b5069300bae4fbf71ddacea91c8b23551fa652b88d0801e"
|
| 29 |
+
},
|
| 30 |
+
"launch_audit.json": {
|
| 31 |
+
"bytes": 1062,
|
| 32 |
+
"sha256": "afcbecdd81e19e8bf4d3a6fa946e7b891f126cb60d0ea8e24f5badeab961f07c"
|
| 33 |
+
},
|
| 34 |
+
"manifest.json": {
|
| 35 |
+
"bytes": 221,
|
| 36 |
+
"sha256": "97b5bea337c766d9db49c8e3a01ad93de4721dbdf965acf31e7eba4948f24421"
|
| 37 |
+
},
|
| 38 |
+
"profile.source.json": {
|
| 39 |
+
"bytes": 2190,
|
| 40 |
+
"sha256": "31a9b15d6b04182439934f0b377cb3d09be16ce21f3cf95c1049918f8a5d4984"
|
| 41 |
+
},
|
| 42 |
+
"provenance.json": {
|
| 43 |
+
"bytes": 8324,
|
| 44 |
+
"sha256": "67f1a7c42dbd772f40995fc73454cf954dda78c2408741bc9274f5259766f7ce"
|
| 45 |
+
},
|
| 46 |
+
"startup_verified.json": {
|
| 47 |
+
"bytes": 1210,
|
| 48 |
+
"sha256": "6bd0871b87954dbf4dfba1778e84ba91361741c4e7d106e97299965d2c0ba1c0"
|
| 49 |
+
},
|
| 50 |
+
"summary.jsonl": {
|
| 51 |
+
"bytes": 159,
|
| 52 |
+
"sha256": "2905acb79c5fffb33fd4689d87af3bd05d74f4184b9d36ff3eae0c64deecd930"
|
| 53 |
+
},
|
| 54 |
+
"task_contract.json": {
|
| 55 |
+
"bytes": 3063,
|
| 56 |
+
"sha256": "b40d58b27dfa9431f86f83ea5d4f36bcd652b3e3517c044638282df0d1e86c6b"
|
| 57 |
+
},
|
| 58 |
+
"training-code/QWen3.py": {
|
| 59 |
+
"bytes": 17586,
|
| 60 |
+
"sha256": "5fe76fc98eaa84e215c3c25c11326815499d2a4b0683c6ab95382b881f535370"
|
| 61 |
+
},
|
| 62 |
+
"training-code/QwenOFT.py": {
|
| 63 |
+
"bytes": 62854,
|
| 64 |
+
"sha256": "acad1b0dcde6e9fed6e2912b6534db0f8e33a74ec7f6630bbf1b0161fc298b0d"
|
| 65 |
+
},
|
| 66 |
+
"training-code/datasets.py": {
|
| 67 |
+
"bytes": 154347,
|
| 68 |
+
"sha256": "33e22556471128a2bad18a1ef8b84c399847b4d98b2979d785d139de0cdf36df"
|
| 69 |
+
},
|
| 70 |
+
"training-code/doom-mean-reset.patch": {
|
| 71 |
+
"bytes": 9569,
|
| 72 |
+
"sha256": "4113989e16c547bd1f02f5f10f285c4d511be3a3c18d10a0aa3316bd8ff3175d"
|
| 73 |
+
},
|
| 74 |
+
"training-code/gym-language-contract.patch": {
|
| 75 |
+
"bytes": 688,
|
| 76 |
+
"sha256": "9f5dee2c5b52d5507aff27373a8cf6def4959361d2c5264d5079b416f28a4c49"
|
| 77 |
+
},
|
| 78 |
+
"training-code/loader-spawn-cache.patch": {
|
| 79 |
+
"bytes": 711,
|
| 80 |
+
"sha256": "24532ce84e5aa9aef381493fe699b4bb4987afd954422191ae852b0f5eb5b2cd"
|
| 81 |
+
},
|
| 82 |
+
"training-code/loader-spawn-test.patch": {
|
| 83 |
+
"bytes": 1500,
|
| 84 |
+
"sha256": "a36db15b13d68defeecb75ddc9efa1ac202d9aa0a40f66ba5baaee2dcaef21f1"
|
| 85 |
+
},
|
| 86 |
+
"training-code/mean-profile-preparation.patch": {
|
| 87 |
+
"bytes": 7053,
|
| 88 |
+
"sha256": "de5fe558376c98515a1cfb186ffdc09b618acc207eb8a5f667b0cbb303767c09"
|
| 89 |
+
},
|
| 90 |
+
"training-code/train_starvla.py": {
|
| 91 |
+
"bytes": 125159,
|
| 92 |
+
"sha256": "f872648f941cec272af724772e9d6fc3721e9ed6eaf0c8683ab285eb9396212e"
|
| 93 |
+
},
|
| 94 |
+
"training-code/trainer_tools.py": {
|
| 95 |
+
"bytes": 29144,
|
| 96 |
+
"sha256": "9a48bdfb99b405b03fececfc1e051d3ebb179eb423c15141f02b9be62e2e1791"
|
| 97 |
+
},
|
| 98 |
+
"training-code/worker_context.py": {
|
| 99 |
+
"bytes": 2279,
|
| 100 |
+
"sha256": "8493631104269f0630655ee2faae592a84ebfb3119aca142c8ac2701f5214464"
|
| 101 |
+
},
|
| 102 |
+
"training_results.json": {
|
| 103 |
+
"bytes": 610,
|
| 104 |
+
"sha256": "7165309b2a84a8fa8250cff1dc2ca1717837f114b5aac269ee9a7e3188182e17"
|
| 105 |
+
}
|
| 106 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/launch_audit.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"physical_gpus": [
|
| 3 |
+
2,
|
| 4 |
+
3
|
| 5 |
+
],
|
| 6 |
+
"seed": 0,
|
| 7 |
+
"horizon": 1,
|
| 8 |
+
"dagger_updates": 0,
|
| 9 |
+
"state_dim": 7,
|
| 10 |
+
"action_dim": 7,
|
| 11 |
+
"micro_per_gpu": 64,
|
| 12 |
+
"gradient_accumulation": 1,
|
| 13 |
+
"global_batch": 128,
|
| 14 |
+
"gradient_checkpointing": true,
|
| 15 |
+
"formal_budget": 5000,
|
| 16 |
+
"post_train_rollout_eval": false,
|
| 17 |
+
"init": "same pinnedQwen3-VL-4B backbone; scratchactionhead",
|
| 18 |
+
"user_authorization": "AfterDeadlycompletes startInterceptGPU23; no newtaskGPU01afterAnt",
|
| 19 |
+
"runtime_capacity_choice": "GCtrue64 methodpassedAnt/Deadly; Interceptmuststillcomplete20boundedthenverifyformalstartup",
|
| 20 |
+
"dataset_revision": "bba7d56c0ad5bb2b6d6ed07adb70b4f1afd5d25a",
|
| 21 |
+
"startup_verified": true,
|
| 22 |
+
"recovery_keep_last_n": 1,
|
| 23 |
+
"storage_capacity": {
|
| 24 |
+
"free_before_bytes": 266315395072,
|
| 25 |
+
"estimated_two_intercept_saves_plus_one_ant_transient_bytes": 218520000000,
|
| 26 |
+
"policy": "preserveonecompleteoptimizerrecovery;newsavebeforeoldprune;originaldata/artifactsnotdeleted"
|
| 27 |
+
},
|
| 28 |
+
"formal_startup_evidence": "/home/ubuntu/lzj/mean-profiling/intercept/vla.startup-verified.json"
|
| 29 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/manifest.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"format": "latency_bench_starvla_h1_v1",
|
| 3 |
+
"task": "mikasa_intercept_grab_fast",
|
| 4 |
+
"robot_type": "latency_mikasa_h1",
|
| 5 |
+
"state_dim": 7,
|
| 6 |
+
"action_dim": 7,
|
| 7 |
+
"action_horizon": 1,
|
| 8 |
+
"vla_fps": 20.0,
|
| 9 |
+
"dagger": false
|
| 10 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/profile.source.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"burst_model_path": "latency_burst_model.json",
|
| 3 |
+
"distribution_path": "latency_distribution.json",
|
| 4 |
+
"env_fps": 20,
|
| 5 |
+
"frame_ms": 50.0,
|
| 6 |
+
"gpu_class": "1x-rtx3090",
|
| 7 |
+
"instance_id": "instance_3a0d42681a03715c",
|
| 8 |
+
"latency_kind": "observation_to_action_latency",
|
| 9 |
+
"latency_method": "temporal",
|
| 10 |
+
"model_id": "qwenoft",
|
| 11 |
+
"n_admitted_observations": 7505,
|
| 12 |
+
"n_capacity_drops": 7495,
|
| 13 |
+
"n_observation_attempts": 15000,
|
| 14 |
+
"per_slot_summary": {
|
| 15 |
+
"0": {
|
| 16 |
+
"admitted_count": 7505,
|
| 17 |
+
"mean_observation_to_action_latency_ms": 99.05021289731565,
|
| 18 |
+
"mean_worker_service_time_ms": 98.55270653801072,
|
| 19 |
+
"p95_observation_to_action_latency_ms": 100.32732703685761,
|
| 20 |
+
"p95_worker_service_time_ms": 99.84762226343155,
|
| 21 |
+
"p99_worker_service_time_ms": 100.37557450771331
|
| 22 |
+
}
|
| 23 |
+
},
|
| 24 |
+
"provenance": {
|
| 25 |
+
"base_config": "configs/examples/mikasa/official_starvla_profile.yaml",
|
| 26 |
+
"checkpoint_kind": "best",
|
| 27 |
+
"model_artifact": {
|
| 28 |
+
"checkpoint": "checkpoints/model.pt",
|
| 29 |
+
"model_config": "config.yaml",
|
| 30 |
+
"path_in_repo": "zero-latency/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/mikasa_intercept_grab_fast_qwenoft_formal_latest_r8gpu8_20260907T082154Z",
|
| 31 |
+
"repo_id": "latency-sensitive-bench/benchmark-models",
|
| 32 |
+
"source": "hf"
|
| 33 |
+
},
|
| 34 |
+
"session_ids": [
|
| 35 |
+
0,
|
| 36 |
+
1,
|
| 37 |
+
2,
|
| 38 |
+
3,
|
| 39 |
+
4
|
| 40 |
+
]
|
| 41 |
+
},
|
| 42 |
+
"sample_model_type": "hidden_regime",
|
| 43 |
+
"source_run_id": "20260909T044501695676Z",
|
| 44 |
+
"summary": {
|
| 45 |
+
"frame_ms": 50.0,
|
| 46 |
+
"max_ms": 101.19136601686478,
|
| 47 |
+
"mean_effective_frames": 1.981004257946313,
|
| 48 |
+
"mean_ms": 99.05021289731565,
|
| 49 |
+
"min_ms": 57.612886011600494,
|
| 50 |
+
"n_samples": 7505,
|
| 51 |
+
"p50_frames": 1.9978350806236267,
|
| 52 |
+
"p50_ms": 99.89175403118134,
|
| 53 |
+
"p90_frames": 2.0040290887355803,
|
| 54 |
+
"p90_ms": 100.20145443677902,
|
| 55 |
+
"p95_frames": 2.006546540737152,
|
| 56 |
+
"p95_ms": 100.32732703685761,
|
| 57 |
+
"p99_frames": 2.0128874400138854,
|
| 58 |
+
"p99_ms": 100.64437200069428,
|
| 59 |
+
"prob_latency_gt_1_frame": 1.0,
|
| 60 |
+
"prob_latency_gt_2_frames": 0.2139906728847435,
|
| 61 |
+
"prob_latency_gt_3_frames": 0.0,
|
| 62 |
+
"std_ms": 4.583189247461202
|
| 63 |
+
},
|
| 64 |
+
"visualization_path": "latency_profile.png",
|
| 65 |
+
"workload_id": "mikasa_intercept_grab_fast"
|
| 66 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/provenance.json
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"task": "intercept",
|
| 3 |
+
"model": "QwenOFT",
|
| 4 |
+
"run_id": "intercept-qwenoft-mean-3090-h1-s0",
|
| 5 |
+
"training_status": "complete",
|
| 6 |
+
"training_hardware": {
|
| 7 |
+
"gpu": "RTX PRO6000 Blackwell Server Edition",
|
| 8 |
+
"count": 2,
|
| 9 |
+
"physical_indices": [
|
| 10 |
+
2,
|
| 11 |
+
3
|
| 12 |
+
]
|
| 13 |
+
},
|
| 14 |
+
"profile": {
|
| 15 |
+
"mean_ms": 99.05021289731565,
|
| 16 |
+
"profile": "/home/ubuntu/lzj/profiles/intercept-published/profiles/qwenoft/1x-rtx3090/mikasa_intercept_grab_fast/instance_3a0d42681a03715c/profile.json",
|
| 17 |
+
"sha256": "31a9b15d6b04182439934f0b377cb3d09be16ce21f3cf95c1049918f8a5d4984"
|
| 18 |
+
},
|
| 19 |
+
"profile_hardware": "RTX3090",
|
| 20 |
+
"condition": "fixed_mean_profile",
|
| 21 |
+
"horizon": 1,
|
| 22 |
+
"seed": 0,
|
| 23 |
+
"state_dim": 7,
|
| 24 |
+
"action_dim": 7,
|
| 25 |
+
"micro_per_gpu": 64,
|
| 26 |
+
"gradient_accumulation": 1,
|
| 27 |
+
"global_batch": 128,
|
| 28 |
+
"gradient_checkpointing": true,
|
| 29 |
+
"precision": "BF16",
|
| 30 |
+
"backend": "ZeRO2",
|
| 31 |
+
"teacher": {
|
| 32 |
+
"repo_id": "latency-sensitive-bench/benchmark-models",
|
| 33 |
+
"revision": "ba7e4f3b6047b82711de01432a7d5bf3895df8b4",
|
| 34 |
+
"path": "latency-aware/mikasa-intercept-grab-fast/small-policy/ppo-qwenoft-rtx3090-mean-h1-s123-v1",
|
| 35 |
+
"status": "complete_verified",
|
| 36 |
+
"local_checkpoint": "/home/ubuntu/lzj/mean-profiling/intercept/teacher-publication/checkpoint.pt",
|
| 37 |
+
"completed_env_steps": 149975040,
|
| 38 |
+
"completed_iterations": 2441,
|
| 39 |
+
"selected_env_steps": 32256000,
|
| 40 |
+
"selected_iteration": 525,
|
| 41 |
+
"tensor_count": 17,
|
| 42 |
+
"weights_only_export": true,
|
| 43 |
+
"tensor_equality_verified": true,
|
| 44 |
+
"hash_readback_verified": true,
|
| 45 |
+
"url": "https://huggingface.co/latency-sensitive-bench/benchmark-models/tree/ba7e4f3b6047b82711de01432a7d5bf3895df8b4/latency-aware/mikasa-intercept-grab-fast/small-policy/ppo-qwenoft-rtx3090-mean-h1-s123-v1",
|
| 46 |
+
"completed_utc": "2026-09-30T16:34:58.230455+00:00"
|
| 47 |
+
},
|
| 48 |
+
"dataset": {
|
| 49 |
+
"repo_id": "latency-sensitive-bench/Standard-Pipeline",
|
| 50 |
+
"repo_type": "dataset",
|
| 51 |
+
"path": "mikasa_intercept_grab_fast/profile_latency/QwenOFT/mean_rtx3090_h1_s123_275ep_v1",
|
| 52 |
+
"revision": "bba7d56c0ad5bb2b6d6ed07adb70b4f1afd5d25a",
|
| 53 |
+
"data_uploaded_revision": "d746ca2b6461ea211589cdf4d796512335aa34ae",
|
| 54 |
+
"status": "complete_verified",
|
| 55 |
+
"episodes": 275,
|
| 56 |
+
"rows": 12211,
|
| 57 |
+
"raw_npz_episodes": 275,
|
| 58 |
+
"dataset_config": "mikasa_intercept_grab_fast_mean_qwenoft_h1_episode_manifest",
|
| 59 |
+
"viewer_rows": "per_episode metadata manifest",
|
| 60 |
+
"payload_files_verified": 308,
|
| 61 |
+
"checksum_manifest_verified": true,
|
| 62 |
+
"url": "https://huggingface.co/datasets/latency-sensitive-bench/Standard-Pipeline/tree/bba7d56c0ad5bb2b6d6ed07adb70b4f1afd5d25a/mikasa_intercept_grab_fast/profile_latency/QwenOFT/mean_rtx3090_h1_s123_275ep_v1",
|
| 63 |
+
"completed_utc": "2026-09-30T17:34:21.416539+00:00"
|
| 64 |
+
},
|
| 65 |
+
"collection": {
|
| 66 |
+
"episodes": 275,
|
| 67 |
+
"unique_seeds": 275,
|
| 68 |
+
"rows": 12211,
|
| 69 |
+
"length_min": 37,
|
| 70 |
+
"length_max": 55,
|
| 71 |
+
"all_success_once": true,
|
| 72 |
+
"state_dim": 7,
|
| 73 |
+
"action_dim": 7,
|
| 74 |
+
"rgb_shape": [
|
| 75 |
+
128,
|
| 76 |
+
128,
|
| 77 |
+
6
|
| 78 |
+
],
|
| 79 |
+
"finite_numeric": true,
|
| 80 |
+
"latency_raw_frames": 2,
|
| 81 |
+
"applied_command_matches_source_issued_action": true,
|
| 82 |
+
"action_horizon": 1,
|
| 83 |
+
"dagger": false,
|
| 84 |
+
"raw_bytes": 1204453838,
|
| 85 |
+
"episode_seeds": [
|
| 86 |
+
0,
|
| 87 |
+
1,
|
| 88 |
+
10,
|
| 89 |
+
180,
|
| 90 |
+
181,
|
| 91 |
+
182,
|
| 92 |
+
183,
|
| 93 |
+
184,
|
| 94 |
+
185,
|
| 95 |
+
186,
|
| 96 |
+
187,
|
| 97 |
+
188,
|
| 98 |
+
189,
|
| 99 |
+
11,
|
| 100 |
+
190,
|
| 101 |
+
191,
|
| 102 |
+
192,
|
| 103 |
+
193,
|
| 104 |
+
194,
|
| 105 |
+
195,
|
| 106 |
+
196,
|
| 107 |
+
197,
|
| 108 |
+
198,
|
| 109 |
+
199,
|
| 110 |
+
12,
|
| 111 |
+
20,
|
| 112 |
+
21,
|
| 113 |
+
22,
|
| 114 |
+
23,
|
| 115 |
+
24,
|
| 116 |
+
25,
|
| 117 |
+
26,
|
| 118 |
+
27,
|
| 119 |
+
28,
|
| 120 |
+
29,
|
| 121 |
+
13,
|
| 122 |
+
200,
|
| 123 |
+
201,
|
| 124 |
+
202,
|
| 125 |
+
203,
|
| 126 |
+
204,
|
| 127 |
+
205,
|
| 128 |
+
206,
|
| 129 |
+
207,
|
| 130 |
+
208,
|
| 131 |
+
209,
|
| 132 |
+
14,
|
| 133 |
+
210,
|
| 134 |
+
211,
|
| 135 |
+
212,
|
| 136 |
+
213,
|
| 137 |
+
214,
|
| 138 |
+
215,
|
| 139 |
+
216,
|
| 140 |
+
217,
|
| 141 |
+
218,
|
| 142 |
+
219,
|
| 143 |
+
15,
|
| 144 |
+
220,
|
| 145 |
+
221,
|
| 146 |
+
222,
|
| 147 |
+
223,
|
| 148 |
+
224,
|
| 149 |
+
225,
|
| 150 |
+
226,
|
| 151 |
+
227,
|
| 152 |
+
228,
|
| 153 |
+
229,
|
| 154 |
+
16,
|
| 155 |
+
230,
|
| 156 |
+
231,
|
| 157 |
+
232,
|
| 158 |
+
233,
|
| 159 |
+
234,
|
| 160 |
+
235,
|
| 161 |
+
236,
|
| 162 |
+
237,
|
| 163 |
+
238,
|
| 164 |
+
239,
|
| 165 |
+
17,
|
| 166 |
+
240,
|
| 167 |
+
241,
|
| 168 |
+
242,
|
| 169 |
+
243,
|
| 170 |
+
244,
|
| 171 |
+
245,
|
| 172 |
+
246,
|
| 173 |
+
247,
|
| 174 |
+
248,
|
| 175 |
+
249,
|
| 176 |
+
18,
|
| 177 |
+
250,
|
| 178 |
+
251,
|
| 179 |
+
252,
|
| 180 |
+
253,
|
| 181 |
+
254,
|
| 182 |
+
255,
|
| 183 |
+
256,
|
| 184 |
+
257,
|
| 185 |
+
258,
|
| 186 |
+
259,
|
| 187 |
+
19,
|
| 188 |
+
260,
|
| 189 |
+
261,
|
| 190 |
+
262,
|
| 191 |
+
263,
|
| 192 |
+
264,
|
| 193 |
+
265,
|
| 194 |
+
266,
|
| 195 |
+
267,
|
| 196 |
+
268,
|
| 197 |
+
269,
|
| 198 |
+
2,
|
| 199 |
+
100,
|
| 200 |
+
270,
|
| 201 |
+
271,
|
| 202 |
+
272,
|
| 203 |
+
273,
|
| 204 |
+
274,
|
| 205 |
+
30,
|
| 206 |
+
31,
|
| 207 |
+
32,
|
| 208 |
+
33,
|
| 209 |
+
34,
|
| 210 |
+
101,
|
| 211 |
+
35,
|
| 212 |
+
36,
|
| 213 |
+
37,
|
| 214 |
+
38,
|
| 215 |
+
39,
|
| 216 |
+
40,
|
| 217 |
+
41,
|
| 218 |
+
42,
|
| 219 |
+
43,
|
| 220 |
+
44,
|
| 221 |
+
102,
|
| 222 |
+
45,
|
| 223 |
+
46,
|
| 224 |
+
47,
|
| 225 |
+
48,
|
| 226 |
+
49,
|
| 227 |
+
50,
|
| 228 |
+
51,
|
| 229 |
+
52,
|
| 230 |
+
53,
|
| 231 |
+
54,
|
| 232 |
+
103,
|
| 233 |
+
55,
|
| 234 |
+
56,
|
| 235 |
+
57,
|
| 236 |
+
58,
|
| 237 |
+
59,
|
| 238 |
+
60,
|
| 239 |
+
61,
|
| 240 |
+
62,
|
| 241 |
+
63,
|
| 242 |
+
64,
|
| 243 |
+
104,
|
| 244 |
+
65,
|
| 245 |
+
66,
|
| 246 |
+
67,
|
| 247 |
+
68,
|
| 248 |
+
69,
|
| 249 |
+
70,
|
| 250 |
+
71,
|
| 251 |
+
72,
|
| 252 |
+
73,
|
| 253 |
+
74,
|
| 254 |
+
105,
|
| 255 |
+
75,
|
| 256 |
+
76,
|
| 257 |
+
77,
|
| 258 |
+
78,
|
| 259 |
+
79,
|
| 260 |
+
80,
|
| 261 |
+
81,
|
| 262 |
+
82,
|
| 263 |
+
83,
|
| 264 |
+
84,
|
| 265 |
+
106,
|
| 266 |
+
85,
|
| 267 |
+
86,
|
| 268 |
+
87,
|
| 269 |
+
88,
|
| 270 |
+
89,
|
| 271 |
+
90,
|
| 272 |
+
91,
|
| 273 |
+
92,
|
| 274 |
+
93,
|
| 275 |
+
94,
|
| 276 |
+
107,
|
| 277 |
+
95,
|
| 278 |
+
96,
|
| 279 |
+
97,
|
| 280 |
+
98,
|
| 281 |
+
99,
|
| 282 |
+
108,
|
| 283 |
+
109,
|
| 284 |
+
3,
|
| 285 |
+
110,
|
| 286 |
+
111,
|
| 287 |
+
112,
|
| 288 |
+
113,
|
| 289 |
+
114,
|
| 290 |
+
115,
|
| 291 |
+
116,
|
| 292 |
+
117,
|
| 293 |
+
118,
|
| 294 |
+
119,
|
| 295 |
+
4,
|
| 296 |
+
120,
|
| 297 |
+
121,
|
| 298 |
+
122,
|
| 299 |
+
123,
|
| 300 |
+
124,
|
| 301 |
+
125,
|
| 302 |
+
126,
|
| 303 |
+
127,
|
| 304 |
+
128,
|
| 305 |
+
129,
|
| 306 |
+
5,
|
| 307 |
+
130,
|
| 308 |
+
131,
|
| 309 |
+
132,
|
| 310 |
+
133,
|
| 311 |
+
134,
|
| 312 |
+
135,
|
| 313 |
+
136,
|
| 314 |
+
137,
|
| 315 |
+
138,
|
| 316 |
+
139,
|
| 317 |
+
6,
|
| 318 |
+
140,
|
| 319 |
+
141,
|
| 320 |
+
142,
|
| 321 |
+
143,
|
| 322 |
+
144,
|
| 323 |
+
145,
|
| 324 |
+
146,
|
| 325 |
+
147,
|
| 326 |
+
148,
|
| 327 |
+
149,
|
| 328 |
+
7,
|
| 329 |
+
150,
|
| 330 |
+
151,
|
| 331 |
+
152,
|
| 332 |
+
153,
|
| 333 |
+
154,
|
| 334 |
+
155,
|
| 335 |
+
156,
|
| 336 |
+
157,
|
| 337 |
+
158,
|
| 338 |
+
159,
|
| 339 |
+
8,
|
| 340 |
+
160,
|
| 341 |
+
161,
|
| 342 |
+
162,
|
| 343 |
+
163,
|
| 344 |
+
164,
|
| 345 |
+
165,
|
| 346 |
+
166,
|
| 347 |
+
167,
|
| 348 |
+
168,
|
| 349 |
+
169,
|
| 350 |
+
9,
|
| 351 |
+
170,
|
| 352 |
+
171,
|
| 353 |
+
172,
|
| 354 |
+
173,
|
| 355 |
+
174,
|
| 356 |
+
175,
|
| 357 |
+
176,
|
| 358 |
+
177,
|
| 359 |
+
178,
|
| 360 |
+
179
|
| 361 |
+
],
|
| 362 |
+
"train_episodes": 250,
|
| 363 |
+
"val_episodes": 25,
|
| 364 |
+
"train_rows": 11119,
|
| 365 |
+
"val_rows": 1092,
|
| 366 |
+
"teacher_checkpoint": "/home/ubuntu/lzj/mean-profiling/intercept/teacher-publication/checkpoint.pt",
|
| 367 |
+
"teacher_sha256": "e47526f03fad3ed251dd78f3cacfba8aa8b60bd1578f40bd7a1393e17298e973",
|
| 368 |
+
"fixed_latency_ms": 99.05021289731565,
|
| 369 |
+
"conversion_complete": true
|
| 370 |
+
},
|
| 371 |
+
"runtime": {
|
| 372 |
+
"repo": "c3c6a39365a151e9b7a5e215452fd64e957c2b29",
|
| 373 |
+
"starvla": "ccca13c5177fe3d3c884b6e2de4965d916016649",
|
| 374 |
+
"runtime_fixes": [
|
| 375 |
+
"mean-profile-preparation.patch",
|
| 376 |
+
"gym-language-contract.patch",
|
| 377 |
+
"loader-spawn-cache.patch",
|
| 378 |
+
"loader-spawn-test.patch",
|
| 379 |
+
"doom-mean-reset.patch"
|
| 380 |
+
],
|
| 381 |
+
"pytorch3d": {
|
| 382 |
+
"revision": "33824be3cbc87a7dd1db0f6a9a9de9ac81b2d0ba",
|
| 383 |
+
"build": "transforms-only, no native render extension; QwenOFT uses transforms only"
|
| 384 |
+
},
|
| 385 |
+
"decord": {
|
| 386 |
+
"version": "0.6.0",
|
| 387 |
+
"build": "official source CPU decoder CP310",
|
| 388 |
+
"wheel_sha256": "e193b356b1e984b4eff08d23b62e482c2c9e5037a6efdc0b1af47079ae2e4c47"
|
| 389 |
+
}
|
| 390 |
+
},
|
| 391 |
+
"source_reference_revision": "180da0effdaaaeacef218cf5fbaa4db41af67495",
|
| 392 |
+
"performance_configuration_policy": "User authorized throughput priority; reference scientific parameters and actual runtime differences are documented by full config/launch audit",
|
| 393 |
+
"training_results": {
|
| 394 |
+
"training_complete": true,
|
| 395 |
+
"optimizer_updates": 5000,
|
| 396 |
+
"selection": "final_5000",
|
| 397 |
+
"train_loss": 0.0017812054138630629,
|
| 398 |
+
"validation_action_loss": 0.03070492297410965,
|
| 399 |
+
"validation_samples": 128,
|
| 400 |
+
"final_learning_rates": {
|
| 401 |
+
"learning_rate/base": 1e-06,
|
| 402 |
+
"learning_rate/action_model": 3.3333333333333333e-06
|
| 403 |
+
},
|
| 404 |
+
"closed_loop_evaluation": "pending until all four VLA trainings complete",
|
| 405 |
+
"tensor_keys": 732,
|
| 406 |
+
"serialized_tensor_elements_including_tied_aliases": 4892415495,
|
| 407 |
+
"weights_only_load": "passed",
|
| 408 |
+
"all_tensors_finite": true,
|
| 409 |
+
"completion_proof": {
|
| 410 |
+
"wrapper_exit": 0
|
| 411 |
+
}
|
| 412 |
+
},
|
| 413 |
+
"optimizer_recovery_states": "retained on host; inference weights only in this bundle",
|
| 414 |
+
"wandb_url": "https://wandb.ai/dongqianyu99-zhejiang-university/starvla_tasks/runs/mean3090-intercept-vla-blackwell-20260930"
|
| 415 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/startup_verified.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"snapshot_utc": "2026-10-01T00:14:27Z",
|
| 3 |
+
"unit": "mean-profile-intercept-vla-20261001-g23-gc.service",
|
| 4 |
+
"wrapper_pid": 3705659,
|
| 5 |
+
"rank_pids": [
|
| 6 |
+
3706042,
|
| 7 |
+
3706043
|
| 8 |
+
],
|
| 9 |
+
"physical_gpus": [
|
| 10 |
+
2,
|
| 11 |
+
3
|
| 12 |
+
],
|
| 13 |
+
"formal_updates_completed": 55,
|
| 14 |
+
"formal_budget": 5000,
|
| 15 |
+
"preflight_updates_discarded": 20,
|
| 16 |
+
"consecutive_observed_updates": [
|
| 17 |
+
50,
|
| 18 |
+
51,
|
| 19 |
+
52,
|
| 20 |
+
53,
|
| 21 |
+
54,
|
| 22 |
+
55
|
| 23 |
+
],
|
| 24 |
+
"loss_at_logged_steps": {
|
| 25 |
+
"50": 0.33528196811676025,
|
| 26 |
+
"55": 0.2620769143104553
|
| 27 |
+
},
|
| 28 |
+
"finite_loss_verified": true,
|
| 29 |
+
"micro_per_gpu": 64,
|
| 30 |
+
"global_batch": 128,
|
| 31 |
+
"gradient_accumulation": 1,
|
| 32 |
+
"gradient_checkpointing": true,
|
| 33 |
+
"seed": 0,
|
| 34 |
+
"horizon": 1,
|
| 35 |
+
"dagger_updates": 0,
|
| 36 |
+
"gpu_used_mib": [
|
| 37 |
+
55707,
|
| 38 |
+
65727
|
| 39 |
+
],
|
| 40 |
+
"gpu_utilization_percent": [
|
| 41 |
+
100,
|
| 42 |
+
100
|
| 43 |
+
],
|
| 44 |
+
"host_memory_current_bytes": 13883498496,
|
| 45 |
+
"host_memory_peak_bytes": 38294556672,
|
| 46 |
+
"seconds_per_update_recent": 2.74,
|
| 47 |
+
"data_wait_seconds_recent": 0.00069506,
|
| 48 |
+
"estimated_completion_utc_range": [
|
| 49 |
+
"2026-10-01T04:45:00Z",
|
| 50 |
+
"2026-10-01T05:15:00Z"
|
| 51 |
+
],
|
| 52 |
+
"completion_check_utc": "2026-10-01T05:30:00Z",
|
| 53 |
+
"training_complete": false,
|
| 54 |
+
"publication_complete": false,
|
| 55 |
+
"closed_loop_evaluation_complete": false
|
| 56 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/summary.jsonl
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"steps": 500}
|
| 2 |
+
{"steps": 1000}
|
| 3 |
+
{"steps": 1500}
|
| 4 |
+
{"steps": 2000}
|
| 5 |
+
{"steps": 2500}
|
| 6 |
+
{"steps": 3000}
|
| 7 |
+
{"steps": 3500}
|
| 8 |
+
{"steps": 4000}
|
| 9 |
+
{"steps": 4500}
|
| 10 |
+
{"steps": 5000}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/task_contract.json
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"format": "latency_bench_starvla_h1_v1",
|
| 3 |
+
"task": "mikasa_intercept_grab_fast",
|
| 4 |
+
"robot_type": "latency_mikasa_h1",
|
| 5 |
+
"state_dim": 7,
|
| 6 |
+
"action_dim": 7,
|
| 7 |
+
"action_horizon": 1,
|
| 8 |
+
"fps": 20.0,
|
| 9 |
+
"state_key": "observation.state",
|
| 10 |
+
"action_key": "action",
|
| 11 |
+
"video_keys": [
|
| 12 |
+
"video.top",
|
| 13 |
+
"video.wrist"
|
| 14 |
+
],
|
| 15 |
+
"auxiliary": {},
|
| 16 |
+
"normalization": {
|
| 17 |
+
"state": {
|
| 18 |
+
"mean": [
|
| 19 |
+
-0.14554812014102936,
|
| 20 |
+
0.07433967292308807,
|
| 21 |
+
0.1436367779970169,
|
| 22 |
+
2.700871706008911,
|
| 23 |
+
0.45681115984916687,
|
| 24 |
+
-0.48229172825813293,
|
| 25 |
+
0.06972828507423401
|
| 26 |
+
],
|
| 27 |
+
"std": [
|
| 28 |
+
0.07678605616092682,
|
| 29 |
+
0.21988309919834137,
|
| 30 |
+
0.09388109296560287,
|
| 31 |
+
1.109084963798523,
|
| 32 |
+
0.3012843728065491,
|
| 33 |
+
0.3006882667541504,
|
| 34 |
+
0.0164483655244112
|
| 35 |
+
],
|
| 36 |
+
"min": [
|
| 37 |
+
-0.37446925044059753,
|
| 38 |
+
-0.29720592498779297,
|
| 39 |
+
0.016940969973802567,
|
| 40 |
+
-3.140481948852539,
|
| 41 |
+
-0.10707131028175354,
|
| 42 |
+
-1.2001688480377197,
|
| 43 |
+
0.03238735347986221
|
| 44 |
+
],
|
| 45 |
+
"max": [
|
| 46 |
+
0.014208495616912842,
|
| 47 |
+
0.5909395217895508,
|
| 48 |
+
0.3558541536331177,
|
| 49 |
+
3.1415467262268066,
|
| 50 |
+
0.9420600533485413,
|
| 51 |
+
0.09770228713750839,
|
| 52 |
+
0.07999999821186066
|
| 53 |
+
],
|
| 54 |
+
"q01": [
|
| 55 |
+
-0.3139808464050293,
|
| 56 |
+
-0.25229972660541533,
|
| 57 |
+
0.0188405978679657,
|
| 58 |
+
-3.134360270500183,
|
| 59 |
+
-0.04521620273590088,
|
| 60 |
+
-1.0301372480392457,
|
| 61 |
+
0.03585549935698509
|
| 62 |
+
],
|
| 63 |
+
"q99": [
|
| 64 |
+
-0.013631248474121185,
|
| 65 |
+
0.5275326526165007,
|
| 66 |
+
0.33348673582077026,
|
| 67 |
+
3.1367013454437256,
|
| 68 |
+
0.8922865676879882,
|
| 69 |
+
0.056985778063535636,
|
| 70 |
+
0.07999999821186066
|
| 71 |
+
]
|
| 72 |
+
},
|
| 73 |
+
"action": {
|
| 74 |
+
"mean": [
|
| 75 |
+
-0.048697348684072495,
|
| 76 |
+
0.24886493384838104,
|
| 77 |
+
-0.16479986906051636,
|
| 78 |
+
-0.032609131187200546,
|
| 79 |
+
-0.501691997051239,
|
| 80 |
+
0.44703611731529236,
|
| 81 |
+
0.6793776154518127
|
| 82 |
+
],
|
| 83 |
+
"std": [
|
| 84 |
+
0.1079770028591156,
|
| 85 |
+
0.47203660011291504,
|
| 86 |
+
0.14475314319133759,
|
| 87 |
+
0.2849433720111847,
|
| 88 |
+
0.318120539188385,
|
| 89 |
+
0.35324087738990784,
|
| 90 |
+
0.6349603533744812
|
| 91 |
+
],
|
| 92 |
+
"min": [
|
| 93 |
+
-0.6572320461273193,
|
| 94 |
+
-1.0,
|
| 95 |
+
-0.8218780755996704,
|
| 96 |
+
-0.7686842083930969,
|
| 97 |
+
-0.9920897483825684,
|
| 98 |
+
-0.30364304780960083,
|
| 99 |
+
-1.0
|
| 100 |
+
],
|
| 101 |
+
"max": [
|
| 102 |
+
0.30179348587989807,
|
| 103 |
+
0.7795099020004272,
|
| 104 |
+
0.2688712179660797,
|
| 105 |
+
0.8059309720993042,
|
| 106 |
+
0.5333064198493958,
|
| 107 |
+
1.0,
|
| 108 |
+
1.0
|
| 109 |
+
],
|
| 110 |
+
"q01": [
|
| 111 |
+
-0.4552440994977951,
|
| 112 |
+
-1.0,
|
| 113 |
+
-0.7419710528850555,
|
| 114 |
+
-0.475678391456604,
|
| 115 |
+
-0.9305388247966766,
|
| 116 |
+
-0.20442348539829253,
|
| 117 |
+
-0.8981475746631622
|
| 118 |
+
],
|
| 119 |
+
"q99": [
|
| 120 |
+
0.1125871029496192,
|
| 121 |
+
0.7402709639072418,
|
| 122 |
+
0.22590837419033025,
|
| 123 |
+
0.7005072689056396,
|
| 124 |
+
0.16810789704322815,
|
| 125 |
+
1.0,
|
| 126 |
+
1.0
|
| 127 |
+
]
|
| 128 |
+
}
|
| 129 |
+
},
|
| 130 |
+
"prompt": "Intercept the rolling ball and grasp it to stop it."
|
| 131 |
+
}
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/QWen3.py
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 starVLA community. All rights reserved.
|
| 2 |
+
# Licensed under the MIT License, Version 1.0 (the "License");
|
| 3 |
+
# Implemented by [Jinhui YE / HKUST University] in [2025].
|
| 4 |
+
|
| 5 |
+
import time
|
| 6 |
+
from collections import OrderedDict
|
| 7 |
+
from functools import wraps
|
| 8 |
+
from inspect import signature
|
| 9 |
+
from typing import Optional
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
from starVLA.model.profiling import stage_timer as _stage
|
| 13 |
+
from starVLA.training.trainer_utils import initialize_overwatch
|
| 14 |
+
from transformers import AutoProcessor, Qwen3VLForConditionalGeneration
|
| 15 |
+
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 16 |
+
|
| 17 |
+
logger = initialize_overwatch(__name__)
|
| 18 |
+
|
| 19 |
+
IGNORE_INDEX = -100
|
| 20 |
+
IMAGE_TOKEN_INDEX = 151655
|
| 21 |
+
VIDEO_TOKEN_INDEX = 151656
|
| 22 |
+
DEFAULT_IMAGE_TOKEN = "<image>"
|
| 23 |
+
DEFAULT_VIDEO_TOKEN = "<video>"
|
| 24 |
+
|
| 25 |
+
_ACTION_TOKEN_MIN = 151669 # how can we know this range? check how you add fast tokens into VLM
|
| 26 |
+
_ACTION_TOKEN_MAX = (
|
| 27 |
+
153716 # here only for fast_tokenizer, see starVLA/model/modules/vlm/tools/add_qwen_special_tokens/README.md
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
import torch.nn as nn
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _patch_qwen3vl_flash_attention_position_ids() -> None:
|
| 35 |
+
from transformers.models.qwen3_vl import modeling_qwen3_vl as qwen3_vl
|
| 36 |
+
|
| 37 |
+
text_attention_cls = qwen3_vl.Qwen3VLTextAttention
|
| 38 |
+
if hasattr(text_attention_cls, "_starvla_flash_attention_position_ids_patched"):
|
| 39 |
+
return
|
| 40 |
+
|
| 41 |
+
original_forward = text_attention_cls.forward
|
| 42 |
+
|
| 43 |
+
def forward_without_flash_attention_position_ids(self, *args, **kwargs):
|
| 44 |
+
# Qwen3-VL already applies M-RoPE through position_embeddings before
|
| 45 |
+
# attention. Passing the repeated temporal M-RoPE ids into HF's FA2
|
| 46 |
+
# wrapper makes Transformers 4.57 mis-detect packed sequences and build
|
| 47 |
+
# empty cu_seqlens on KV-memory cache steps.
|
| 48 |
+
if self.config._attn_implementation == "flash_attention_2" and "position_ids" in kwargs:
|
| 49 |
+
del kwargs["position_ids"]
|
| 50 |
+
return original_forward(self, *args, **kwargs)
|
| 51 |
+
|
| 52 |
+
text_attention_cls.forward = forward_without_flash_attention_position_ids
|
| 53 |
+
text_attention_cls._starvla_flash_attention_position_ids_patched = True
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
# The fixed Triton stage counts keep the PyTorch 2.11 CUDA 12.8 kernel
|
| 57 |
+
# within the shared-memory budget on sm_120.
|
| 58 |
+
_FLEX_KERNEL_OPTIONS = {
|
| 59 |
+
"BACKEND": "TRITON",
|
| 60 |
+
"fwd_num_stages": 2,
|
| 61 |
+
"bwd_num_stages": 1,
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def _patch_qwen3vl_flex_attention_support() -> None:
|
| 66 |
+
"""Allow loading Qwen3-VL with ``attn_implementation='flex_attention'``.
|
| 67 |
+
|
| 68 |
+
Qwen3-VL's attention already dispatches through the generic
|
| 69 |
+
``ALL_ATTENTION_FUNCTIONS`` interface (which includes the ``flex_attention``
|
| 70 |
+
integration), but the model classes conservatively declare
|
| 71 |
+
``_supports_flex_attn = False``, so Transformers 4.57 refuses to load with
|
| 72 |
+
flex at __init__. Flipping the flag lets the official dispatch path run; the
|
| 73 |
+
packed KV-memory training (`QwenOFT._forward_memory_packed`) passes a
|
| 74 |
+
BlockMask that FlexAttention consumes natively.
|
| 75 |
+
|
| 76 |
+
Also wraps the registered ``flex_attention`` interface to inject the fixed
|
| 77 |
+
Triton kernel options. Wrapping the dispatch entry — rather than threading
|
| 78 |
+
``kernel_options`` through the top-level forward kwargs — guarantees both the
|
| 79 |
+
forward and the gradient-checkpointing backward recompile pick it up.
|
| 80 |
+
"""
|
| 81 |
+
from transformers.models.qwen3_vl import modeling_qwen3_vl as qwen3_vl
|
| 82 |
+
|
| 83 |
+
for cls_name in (
|
| 84 |
+
"Qwen3VLForConditionalGeneration",
|
| 85 |
+
"Qwen3VLModel",
|
| 86 |
+
"Qwen3VLTextModel",
|
| 87 |
+
"Qwen3VLPreTrainedModel",
|
| 88 |
+
):
|
| 89 |
+
cls = getattr(qwen3_vl, cls_name, None)
|
| 90 |
+
if cls is not None:
|
| 91 |
+
cls._supports_flex_attn = True
|
| 92 |
+
|
| 93 |
+
from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
|
| 94 |
+
|
| 95 |
+
base_flex = ALL_ATTENTION_FUNCTIONS["flex_attention"]
|
| 96 |
+
if getattr(base_flex, "_starvla_kernel_options_patched", False):
|
| 97 |
+
return
|
| 98 |
+
|
| 99 |
+
def flex_attention_with_kernel_options(*args, **kwargs):
|
| 100 |
+
kwargs["kernel_options"] = _FLEX_KERNEL_OPTIONS
|
| 101 |
+
return base_flex(*args, **kwargs)
|
| 102 |
+
|
| 103 |
+
flex_attention_with_kernel_options._starvla_kernel_options_patched = True
|
| 104 |
+
ALL_ATTENTION_FUNCTIONS["flex_attention"] = flex_attention_with_kernel_options
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def _position_tensor_key(tensor):
|
| 108 |
+
if tensor is None:
|
| 109 |
+
return None
|
| 110 |
+
return (tensor.device, tensor.dtype, tuple(tensor.shape),
|
| 111 |
+
tuple(tensor.detach().cpu().reshape(-1).tolist()))
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def _cache_position_method(module, name, weight):
|
| 115 |
+
"""Cache layout-only outputs on one native model instance, never image features."""
|
| 116 |
+
original = getattr(module, name)
|
| 117 |
+
parameters = signature(original)
|
| 118 |
+
cache = OrderedDict()
|
| 119 |
+
|
| 120 |
+
@wraps(original)
|
| 121 |
+
def cached(*args, **kwargs):
|
| 122 |
+
if module.training or torch.is_grad_enabled():
|
| 123 |
+
cache.clear()
|
| 124 |
+
return original(*args, **kwargs)
|
| 125 |
+
arguments = parameters.bind(*args, **kwargs)
|
| 126 |
+
arguments.apply_defaults()
|
| 127 |
+
parameter = weight()
|
| 128 |
+
key = (
|
| 129 |
+
parameter.device, parameter.dtype, parameter._version,
|
| 130 |
+
tuple(_position_tensor_key(value) for value in arguments.arguments.values()),
|
| 131 |
+
)
|
| 132 |
+
if key not in cache:
|
| 133 |
+
cache[key] = original(*args, **kwargs)
|
| 134 |
+
if len(cache) > 8:
|
| 135 |
+
cache.popitem(last=False)
|
| 136 |
+
cache.move_to_end(key)
|
| 137 |
+
return cache[key]
|
| 138 |
+
|
| 139 |
+
setattr(module, name, cached)
|
| 140 |
+
return cache
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
class _QWen3_VL_Interface(nn.Module):
|
| 144 |
+
"""
|
| 145 |
+
This exists because of the diversity of VLMs, so we encapsulate the changes here.
|
| 146 |
+
Lightweight wrapper around Qwen3-VL (Qwen3VLForConditionalGeneration).
|
| 147 |
+
|
| 148 |
+
Purpose:
|
| 149 |
+
- Unify interface with other VLM backends (CausalLM-like usage).
|
| 150 |
+
- Centralize preprocessing (tokenization + multimodal packing).
|
| 151 |
+
- Provide consistent forward / generate signatures.
|
| 152 |
+
|
| 153 |
+
"""
|
| 154 |
+
|
| 155 |
+
def __init__(self, config: Optional[dict] = None, **kwargs):
|
| 156 |
+
"""
|
| 157 |
+
Initialize the Qwen3-VL wrapper.
|
| 158 |
+
Following https://huggingface.co/Qwen/Qwen3-VL-4B-Instruct
|
| 159 |
+
|
| 160 |
+
"""
|
| 161 |
+
super().__init__()
|
| 162 |
+
|
| 163 |
+
qwenvl_config = config.framework.get("qwenvl", {})
|
| 164 |
+
model_id = qwenvl_config.get("base_vlm", "Qwen/Qwen3-VL-4B-Instruct")
|
| 165 |
+
attn_implementation = qwenvl_config.get("attn_implementation", "sdpa")
|
| 166 |
+
enable_grad_ckpt = qwenvl_config.get("enable_gradient_checkpointing", False)
|
| 167 |
+
print(
|
| 168 |
+
f"[QWen3] loading {model_id} with gradient_checkpointing={enable_grad_ckpt}",
|
| 169 |
+
flush=True,
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
if attn_implementation == "flash_attention_2":
|
| 173 |
+
import flash_attn_2_cuda # noqa: F401
|
| 174 |
+
|
| 175 |
+
_patch_qwen3vl_flash_attention_position_ids()
|
| 176 |
+
|
| 177 |
+
if attn_implementation == "flex_attention":
|
| 178 |
+
_patch_qwen3vl_flex_attention_support()
|
| 179 |
+
|
| 180 |
+
model = Qwen3VLForConditionalGeneration.from_pretrained(
|
| 181 |
+
model_id,
|
| 182 |
+
attn_implementation=attn_implementation,
|
| 183 |
+
dtype=torch.bfloat16,
|
| 184 |
+
ignore_mismatched_sizes=True, # resize image no longer needed? @TODO check bug
|
| 185 |
+
)
|
| 186 |
+
processor = AutoProcessor.from_pretrained(model_id)
|
| 187 |
+
processor.tokenizer.padding_side = "left"
|
| 188 |
+
|
| 189 |
+
if enable_grad_ckpt:
|
| 190 |
+
model.config.text_config.use_cache = False
|
| 191 |
+
model.gradient_checkpointing_enable(
|
| 192 |
+
gradient_checkpointing_kwargs={"use_reentrant": False}
|
| 193 |
+
)
|
| 194 |
+
model.enable_input_require_grads()
|
| 195 |
+
print(
|
| 196 |
+
"[QWen3] gradient_checkpointing ENABLED "
|
| 197 |
+
f"(use_reentrant=False, active={model.is_gradient_checkpointing}, "
|
| 198 |
+
f"text_use_cache={model.config.text_config.use_cache})",
|
| 199 |
+
flush=True,
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
self.model = model
|
| 203 |
+
self.processor = processor
|
| 204 |
+
self.config = config
|
| 205 |
+
# align qwen3 with qwen2.5
|
| 206 |
+
self.model.config.hidden_size = self.model.config.text_config.hidden_size
|
| 207 |
+
self._last_build_timing = {}
|
| 208 |
+
visual = model.model.visual
|
| 209 |
+
self._position_caches = [
|
| 210 |
+
_cache_position_method(model.model, "get_rope_index", lambda: visual.rotary_pos_emb.inv_freq),
|
| 211 |
+
_cache_position_method(visual, "rot_pos_emb", lambda: visual.rotary_pos_emb.inv_freq),
|
| 212 |
+
_cache_position_method(visual, "fast_pos_embed_interpolate", lambda: visual.pos_embed.weight),
|
| 213 |
+
]
|
| 214 |
+
|
| 215 |
+
# only for fast base model
|
| 216 |
+
if "-Action" in model_id:
|
| 217 |
+
self._ACTION_TOKEN_MIN = _ACTION_TOKEN_MIN
|
| 218 |
+
self._ACTION_TOKEN_MAX = _ACTION_TOKEN_MAX
|
| 219 |
+
|
| 220 |
+
def _clear_position_caches(self):
|
| 221 |
+
for cache in self._position_caches:
|
| 222 |
+
cache.clear()
|
| 223 |
+
|
| 224 |
+
def train(self, mode=True):
|
| 225 |
+
self._clear_position_caches()
|
| 226 |
+
return super().train(mode)
|
| 227 |
+
|
| 228 |
+
def _apply(self, fn, recurse=True):
|
| 229 |
+
self._clear_position_caches()
|
| 230 |
+
return super()._apply(fn, recurse=recurse)
|
| 231 |
+
|
| 232 |
+
def _load_from_state_dict(self, *args, **kwargs):
|
| 233 |
+
self._clear_position_caches()
|
| 234 |
+
return super()._load_from_state_dict(*args, **kwargs)
|
| 235 |
+
|
| 236 |
+
def _profile_timing_enabled(self) -> bool:
|
| 237 |
+
return self.config.trainer.profile_timing.enabled
|
| 238 |
+
|
| 239 |
+
def _profile_sync(self) -> None:
|
| 240 |
+
if self._profile_timing_enabled() and torch.cuda.is_available():
|
| 241 |
+
torch.cuda.synchronize()
|
| 242 |
+
|
| 243 |
+
def forward(
|
| 244 |
+
self,
|
| 245 |
+
**kwargs,
|
| 246 |
+
) -> CausalLMOutputWithPast:
|
| 247 |
+
"""
|
| 248 |
+
Forward pass delegating to underlying Qwen2.5-VL backbone.
|
| 249 |
+
"""
|
| 250 |
+
|
| 251 |
+
with torch.autocast("cuda", dtype=torch.bfloat16):
|
| 252 |
+
outputs = self.model(
|
| 253 |
+
**kwargs,
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
return outputs
|
| 257 |
+
|
| 258 |
+
def forward_last_hidden(self, **kwargs) -> torch.Tensor:
|
| 259 |
+
"""
|
| 260 |
+
Run the Qwen3-VL backbone and return only the final hidden state.
|
| 261 |
+
|
| 262 |
+
OFT-style action heads only consume the last hidden layer. Calling the
|
| 263 |
+
full conditional-generation wrapper with ``output_hidden_states=True``
|
| 264 |
+
returns every layer's activations, which can largely erase the memory
|
| 265 |
+
benefit of gradient checkpointing.
|
| 266 |
+
"""
|
| 267 |
+
|
| 268 |
+
kwargs = dict(kwargs)
|
| 269 |
+
kwargs.pop("output_hidden_states", None)
|
| 270 |
+
kwargs.pop("output_attentions", None)
|
| 271 |
+
kwargs.setdefault("return_dict", True)
|
| 272 |
+
kwargs.setdefault("use_cache", False)
|
| 273 |
+
|
| 274 |
+
def _wrapper_forward_last_hidden() -> torch.Tensor:
|
| 275 |
+
lm_head = getattr(self.model, "lm_head", None)
|
| 276 |
+
captured = {}
|
| 277 |
+
handle = None
|
| 278 |
+
if lm_head is not None:
|
| 279 |
+
def _capture_lm_head_input(_module, inputs):
|
| 280 |
+
captured["last_hidden"] = inputs[0]
|
| 281 |
+
|
| 282 |
+
handle = lm_head.register_forward_pre_hook(_capture_lm_head_input)
|
| 283 |
+
|
| 284 |
+
with torch.autocast("cuda", dtype=torch.bfloat16):
|
| 285 |
+
try:
|
| 286 |
+
outputs = self.model(
|
| 287 |
+
**kwargs,
|
| 288 |
+
output_attentions=False,
|
| 289 |
+
output_hidden_states=False,
|
| 290 |
+
return_dict=True,
|
| 291 |
+
)
|
| 292 |
+
finally:
|
| 293 |
+
if handle is not None:
|
| 294 |
+
handle.remove()
|
| 295 |
+
|
| 296 |
+
if "last_hidden" in captured:
|
| 297 |
+
return captured["last_hidden"]
|
| 298 |
+
|
| 299 |
+
# Last-resort compatibility path for unusual model wrappers that do
|
| 300 |
+
# not expose lm_head. This preserves correctness, but returns every
|
| 301 |
+
# layer and therefore saves less memory.
|
| 302 |
+
with torch.autocast("cuda", dtype=torch.bfloat16):
|
| 303 |
+
outputs = self.model(
|
| 304 |
+
**kwargs,
|
| 305 |
+
output_attentions=False,
|
| 306 |
+
output_hidden_states=True,
|
| 307 |
+
return_dict=True,
|
| 308 |
+
)
|
| 309 |
+
return outputs.hidden_states[-1]
|
| 310 |
+
|
| 311 |
+
backbone = getattr(self.model, "model", None)
|
| 312 |
+
if backbone is None:
|
| 313 |
+
return _wrapper_forward_last_hidden()
|
| 314 |
+
|
| 315 |
+
try:
|
| 316 |
+
with torch.autocast("cuda", dtype=torch.bfloat16):
|
| 317 |
+
outputs = backbone(**kwargs)
|
| 318 |
+
except TypeError:
|
| 319 |
+
# Some Transformers Qwen-VL wrappers inject visual embeddings before
|
| 320 |
+
# calling the text backbone, so the backbone itself may not accept
|
| 321 |
+
# pixel_values/image_grid_thw. Keep those versions working.
|
| 322 |
+
return _wrapper_forward_last_hidden()
|
| 323 |
+
|
| 324 |
+
if hasattr(outputs, "last_hidden_state"):
|
| 325 |
+
return outputs.last_hidden_state
|
| 326 |
+
return outputs[0]
|
| 327 |
+
|
| 328 |
+
def generate(
|
| 329 |
+
self,
|
| 330 |
+
**kwargs,
|
| 331 |
+
):
|
| 332 |
+
"""
|
| 333 |
+
High-level generation interface (auto-regressive decoding), optionally vision-conditioned.
|
| 334 |
+
|
| 335 |
+
Args:
|
| 336 |
+
**kwargs: fully follow raw model.generate() signature.
|
| 337 |
+
Returns:
|
| 338 |
+
GenerateOutput | Model-dependent generation return.
|
| 339 |
+
"""
|
| 340 |
+
with torch.autocast("cuda", dtype=torch.float16):
|
| 341 |
+
generation_output = self.model.generate(
|
| 342 |
+
**kwargs,
|
| 343 |
+
)
|
| 344 |
+
return generation_output
|
| 345 |
+
|
| 346 |
+
def build_qwenvl_inputs(self, images, instructions, solutions=None, profiler=None, **kwargs):
|
| 347 |
+
"""
|
| 348 |
+
Build model inputs from raw data (images + instructions + optional solutions).
|
| 349 |
+
Follow Oficial Qwen3-VL Instruct format: https://huggingface.co/Qwen/Qwen3-VL-4B-Instruct
|
| 350 |
+
"""
|
| 351 |
+
|
| 352 |
+
# Create messages: one message per sample
|
| 353 |
+
with _stage(profiler, "starvla_qwen_message_build_ms"):
|
| 354 |
+
messages = []
|
| 355 |
+
assert len(images) == len(instructions), "Images and instructions must have the same length"
|
| 356 |
+
for imgs, instruction in zip(images, instructions):
|
| 357 |
+
content = [{"type": "image", "image": img} for img in imgs]
|
| 358 |
+
|
| 359 |
+
if "CoT_prompt" in self.config.datasets.vla_data: # If using a grounding prompt to task
|
| 360 |
+
CoT_prompt = self.config.datasets.vla_data.get("CoT_prompt", "")
|
| 361 |
+
prompt = CoT_prompt.replace("{instruction}", instruction)
|
| 362 |
+
else:
|
| 363 |
+
prompt = instruction
|
| 364 |
+
|
| 365 |
+
content.append({"type": "text", "text": prompt})
|
| 366 |
+
msg = [{"role": "user", "content": content}]
|
| 367 |
+
|
| 368 |
+
if solutions is not None:
|
| 369 |
+
solution = solutions[len(messages)]
|
| 370 |
+
msg.append({"role": "assistant", "content": [{"type": "text", "text": solution}]})
|
| 371 |
+
messages.append(msg)
|
| 372 |
+
|
| 373 |
+
profile_timing = self._profile_timing_enabled()
|
| 374 |
+
build_timing = {}
|
| 375 |
+
|
| 376 |
+
t_processor = time.perf_counter() if profile_timing else None
|
| 377 |
+
with _stage(profiler, "starvla_qwen_processor_ms"):
|
| 378 |
+
batch_inputs = self.processor.apply_chat_template(
|
| 379 |
+
messages, tokenize=True, padding=True, add_generation_prompt=True, return_dict=True, return_tensors="pt"
|
| 380 |
+
)
|
| 381 |
+
if profile_timing:
|
| 382 |
+
build_timing["timing/qwen_processor"] = time.perf_counter() - t_processor
|
| 383 |
+
|
| 384 |
+
# if solutions, mask out the solution tokens in labels
|
| 385 |
+
if solutions is not None: # here only for fast_tokenizer now.
|
| 386 |
+
action_token_min = _ACTION_TOKEN_MIN # how can we know this range? --> we has other way for this, but is slower see qwenhelix branch
|
| 387 |
+
action_token_max = _ACTION_TOKEN_MAX # here only for fast_tokenizer, see starVLA/model/modules/vlm/tools/add_qwen_special_tokens/README.md
|
| 388 |
+
labels = batch_inputs["input_ids"].clone()
|
| 389 |
+
# For each sequence in the batch, find the first occurrence of an action token.
|
| 390 |
+
for i in range(labels.size(0)):
|
| 391 |
+
seq = labels[i]
|
| 392 |
+
# Create a mask for tokens within the action token range.
|
| 393 |
+
mask_seq = (seq >= action_token_min) & (seq <= action_token_max)
|
| 394 |
+
nonzero_indices = torch.nonzero(mask_seq, as_tuple=False)
|
| 395 |
+
if nonzero_indices.numel() > 0:
|
| 396 |
+
first_action_index = nonzero_indices[0].item()
|
| 397 |
+
# Mask out all tokens before the first action token.
|
| 398 |
+
seq[:first_action_index] = IGNORE_INDEX
|
| 399 |
+
else:
|
| 400 |
+
# If no action token is found, mask the entire sequence.
|
| 401 |
+
seq[:] = IGNORE_INDEX
|
| 402 |
+
RuntimeWarning(
|
| 403 |
+
"action token are on in yout tokenizer, plz see starVLA/model/modules/vlm/tools/add_qwen_special_tokens/README.md."
|
| 404 |
+
)
|
| 405 |
+
|
| 406 |
+
labels[labels == self.processor.tokenizer.pad_token_id] = -100 ## mask out pad tokens as well
|
| 407 |
+
batch_inputs["labels"] = labels
|
| 408 |
+
|
| 409 |
+
t_h2d = time.perf_counter() if profile_timing else None
|
| 410 |
+
with _stage(profiler, "starvla_qwen_h2d_ms"):
|
| 411 |
+
batch_inputs = batch_inputs.to(self.model.device)
|
| 412 |
+
if profile_timing:
|
| 413 |
+
self._profile_sync()
|
| 414 |
+
build_timing["timing/qwen_h2d"] = time.perf_counter() - t_h2d
|
| 415 |
+
self._last_build_timing = build_timing
|
| 416 |
+
return batch_inputs
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
if __name__ == "__main__":
|
| 420 |
+
import argparse
|
| 421 |
+
import os
|
| 422 |
+
|
| 423 |
+
from omegaconf import OmegaConf
|
| 424 |
+
|
| 425 |
+
parser = argparse.ArgumentParser()
|
| 426 |
+
parser.add_argument(
|
| 427 |
+
"--config_yaml",
|
| 428 |
+
type=str,
|
| 429 |
+
default="examples/SimplerEnv/train_files/starvla_cotrain_oxe.yaml",
|
| 430 |
+
help="Path to YAML config",
|
| 431 |
+
)
|
| 432 |
+
args, clipargs = parser.parse_known_args()
|
| 433 |
+
|
| 434 |
+
if os.getenv("DEBUGPY_ENABLE", "0") == "1":
|
| 435 |
+
import debugpy
|
| 436 |
+
debugpy.listen(("0.0.0.0", 10092))
|
| 437 |
+
print("Rank 0 waiting for debugger attach on port 10092...")
|
| 438 |
+
debugpy.wait_for_client()
|
| 439 |
+
|
| 440 |
+
cfg = OmegaConf.load(args.config_yaml)
|
| 441 |
+
|
| 442 |
+
cfg.framework.qwenvl.base_vlm = "./playground/Pretrained_models/Qwen3-VL-4B-Instruct"
|
| 443 |
+
qwen_vl = _QWen3_VL_Interface(cfg)
|
| 444 |
+
pass
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/QwenOFT.py
ADDED
|
@@ -0,0 +1,1244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 starVLA community. All rights reserved.
|
| 2 |
+
# Licensed under the MIT License, Version 1.0 (the "License");
|
| 3 |
+
# Implemented by [Jinhui YE / HKUST University] in [2025].
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
Qwen-OFT Framework
|
| 7 |
+
|
| 8 |
+
A lightweight implementation that uses an action special token to parallelly predict continuous actions
|
| 9 |
+
conditioned on multi-view images plus a language instruction (shares parameters with the VLM).
|
| 10 |
+
Inspired by OpenVLA-OFT
|
| 11 |
+
Key Points:
|
| 12 |
+
- Qwen2.5 vision-language backbone
|
| 13 |
+
- Injects an action special token into the VLM
|
| 14 |
+
- Continuous action prediction via L1 regression over the action special token hidden states
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
Note: How to add special tokens to Qwen2.5:
|
| 18 |
+
download our model checkpoint with special tokens added: https://huggingface.co/StarVLA/Qwen2.5-VL-3B-Instruct-Action
|
| 19 |
+
or /starVLA/model/modules/vlm/tools/add_qwen_special_tokens/README.md (adpat a little code)
|
| 20 |
+
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
from dataclasses import dataclass, field
|
| 24 |
+
import time
|
| 25 |
+
from typing import Any, List, Optional, Tuple
|
| 26 |
+
|
| 27 |
+
import numpy as np
|
| 28 |
+
import torch
|
| 29 |
+
import torch.nn as nn
|
| 30 |
+
import torch.nn.functional as F
|
| 31 |
+
from PIL import Image
|
| 32 |
+
|
| 33 |
+
from deployment.model_server.tools.image_tools import to_pil_preserve
|
| 34 |
+
from starVLA.model.tools import FRAMEWORK_REGISTRY
|
| 35 |
+
from starVLA.training.trainer_utils import initialize_overwatch
|
| 36 |
+
|
| 37 |
+
logger = initialize_overwatch(__name__)
|
| 38 |
+
|
| 39 |
+
# HuggingFace Default / LLaMa-2 IGNORE_INDEX (for labels)
|
| 40 |
+
IGNORE_INDEX = -100
|
| 41 |
+
|
| 42 |
+
from starVLA.model.framework.base_framework import baseframework
|
| 43 |
+
from starVLA.model.framework.share_tools import add_discretized_state_to_instruction, merge_framework_config
|
| 44 |
+
from starVLA.model.modules.action_model.MLP_ActionHeader import get_action_model
|
| 45 |
+
from starVLA.model.modules.vlm import get_vlm_model
|
| 46 |
+
from starVLA.training.trainer_utils.trainer_tools import resize_images
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
# ──────────────────────────────────────────────────────────────────────
|
| 50 |
+
# Default Config for QwenOFT
|
| 51 |
+
# - Documents every framework-level parameter with type + description
|
| 52 |
+
# - YAML values override these defaults; extra YAML keys are preserved
|
| 53 |
+
# ──────────────────────────────────────────────────────────────────────
|
| 54 |
+
@dataclass
|
| 55 |
+
class QwenOFTDefaultConfig:
|
| 56 |
+
"""QwenOFT framework default parameters.
|
| 57 |
+
|
| 58 |
+
All fields can be overridden by the corresponding key in the YAML
|
| 59 |
+
``framework:`` section. Extra YAML keys not listed here are kept
|
| 60 |
+
as-is (Config-as-API flexibility).
|
| 61 |
+
"""
|
| 62 |
+
|
| 63 |
+
# --- Registry identifier (must match @FRAMEWORK_REGISTRY.register) ---
|
| 64 |
+
name: str = "QwenOFT"
|
| 65 |
+
|
| 66 |
+
# === VLM backbone (Qwen2.5-VL / Qwen3-VL) ===
|
| 67 |
+
qwenvl: dict = field(
|
| 68 |
+
default_factory=lambda: {
|
| 69 |
+
# Path to base VLM checkpoint (local or HF hub id)
|
| 70 |
+
"base_vlm": "./playground/Pretrained_models/Qwen3-VL-4B-Instruct-Action",
|
| 71 |
+
# Attention implementation: "flash_attention_2" | "eager" | "sdpa"
|
| 72 |
+
"attn_implementation": "flash_attention_2",
|
| 73 |
+
}
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
# === Action head (MLP regression over action special tokens) ===
|
| 77 |
+
action_model: dict = field(
|
| 78 |
+
default_factory=lambda: {
|
| 79 |
+
# Action head architecture type
|
| 80 |
+
"action_model_type": "MLP",
|
| 81 |
+
# Dimensionality of each action vector (e.g., 7 for 6-DoF + gripper)
|
| 82 |
+
"action_dim": 7,
|
| 83 |
+
# Proprioceptive state dimension for continuous_projector mode
|
| 84 |
+
"state_dim": 7,
|
| 85 |
+
# Hidden dim for the action MLP (auto-set from VLM hidden_size at runtime)
|
| 86 |
+
"action_hidden_dim": 2560,
|
| 87 |
+
# How many future steps to predict
|
| 88 |
+
"future_action_window_size": 8,
|
| 89 |
+
# How many past steps included in action chunk (usually 0)
|
| 90 |
+
"past_action_window_size": 0,
|
| 91 |
+
# Loss over predicted actions. Supported: "l1", "discrete_ce",
|
| 92 |
+
# "multibinary_bce", "asterix_factorized_ce".
|
| 93 |
+
# "multibinary_ce" is accepted as a CLI alias for "multibinary_bce".
|
| 94 |
+
"loss_type": "l1",
|
| 95 |
+
# State conditioning: preserve the existing text-bin path by default.
|
| 96 |
+
"state_encoding": "discretized_text",
|
| 97 |
+
}
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
@FRAMEWORK_REGISTRY.register("QwenOFT")
|
| 102 |
+
class Qwenvl_OFT(baseframework):
|
| 103 |
+
"""
|
| 104 |
+
Multimodal vision-language-action model (OFT variant).
|
| 105 |
+
|
| 106 |
+
Components:
|
| 107 |
+
- Qwen2.5-VL / Qwen3-VL backbone for fused language/vision token embeddings
|
| 108 |
+
- Action special token injected into the VLM sequence
|
| 109 |
+
- MLP regression head over action token hidden states (L1 loss)
|
| 110 |
+
|
| 111 |
+
Focus: Predict future continuous actions conditioned on images + instruction.
|
| 112 |
+
"""
|
| 113 |
+
|
| 114 |
+
def __init__(
|
| 115 |
+
self,
|
| 116 |
+
config: Optional[dict] = None,
|
| 117 |
+
**kwargs,
|
| 118 |
+
) -> None:
|
| 119 |
+
"""
|
| 120 |
+
Construct all submodules and cache key configuration values.
|
| 121 |
+
|
| 122 |
+
Args:
|
| 123 |
+
config: Hierarchical configuration (OmegaConf/dict) containing framework + trainer sections.
|
| 124 |
+
**kwargs: Reserved for future overrides (unused).
|
| 125 |
+
"""
|
| 126 |
+
super().__init__()
|
| 127 |
+
# Merge framework defaults with YAML config (YAML wins on conflicts)
|
| 128 |
+
self.config = merge_framework_config(QwenOFTDefaultConfig, config)
|
| 129 |
+
self.qwen_vl_interface = get_vlm_model(config=self.config)
|
| 130 |
+
# align action_hidden_dim to VLM hidden_size at runtime
|
| 131 |
+
self.config.framework.action_model.action_hidden_dim = self.qwen_vl_interface.model.config.hidden_size
|
| 132 |
+
self.action_model = get_action_model(config=self.config)
|
| 133 |
+
|
| 134 |
+
# `action_horizon` is the single source of truth for chunk length.
|
| 135 |
+
# Legacy aliases (`future_action_window_size`, `past_action_window_size`)
|
| 136 |
+
# are normalised upstream by `share_tools.apply_config_compat`, so we
|
| 137 |
+
# only ever read `action_horizon` here.
|
| 138 |
+
self.action_horizon = int(self.config.framework.action_model.action_horizon)
|
| 139 |
+
self.chunk_len = self.action_horizon
|
| 140 |
+
self.action_dim = int(self.config.framework.action_model.action_dim)
|
| 141 |
+
self.action_env_dim = int(getattr(self.config.framework.action_model, "action_env_dim", self.action_dim))
|
| 142 |
+
self.action_loss_type = str(getattr(self.config.framework.action_model, "loss_type", "l1")).lower()
|
| 143 |
+
self.state_encoding = str(self.config.framework.action_model.state_encoding)
|
| 144 |
+
if self.state_encoding not in {"discretized_text", "continuous_projector"}:
|
| 145 |
+
raise ValueError(f"Unsupported state_encoding: {self.state_encoding}")
|
| 146 |
+
if self.state_encoding == "continuous_projector":
|
| 147 |
+
self.action_model.state_projector = nn.Linear(
|
| 148 |
+
int(self.config.framework.action_model.state_dim),
|
| 149 |
+
int(self.config.framework.action_model.action_hidden_dim),
|
| 150 |
+
)
|
| 151 |
+
cross_task_cfg = getattr(getattr(self.config, "rl_games", None), "cross_task", None)
|
| 152 |
+
self.loss_by_task = self._to_plain_dict(getattr(cross_task_cfg, "loss_by_task", None))
|
| 153 |
+
self.loss_weight_by_task = self._to_plain_dict(getattr(cross_task_cfg, "loss_weight_by_task", None))
|
| 154 |
+
# self.hidden_dim = config.framework.action_model.action_hidden_dim
|
| 155 |
+
|
| 156 |
+
self.action_token = "🔍" # TODO also can add spacail token to Qwen, but too complex
|
| 157 |
+
self.action_token_id = self.qwen_vl_interface.processor.tokenizer("🔍", add_special_tokens=False)["input_ids"][0]
|
| 158 |
+
|
| 159 |
+
# --- Fixed-size KV memory; default OFF so existing paths are untouched. ---
|
| 160 |
+
kv_cfg = getattr(self.config.framework, "kv_memory", None)
|
| 161 |
+
self.kv_memory_enabled = bool(getattr(kv_cfg, "enabled", False)) if kv_cfg is not None else False
|
| 162 |
+
self.kv_window = int(getattr(kv_cfg, "window", 4)) if kv_cfg is not None else 4
|
| 163 |
+
self.kv_rollout_len = int(getattr(kv_cfg, "rollout_len", max(self.kv_window + 1, 2))) if kv_cfg is not None else self.kv_window + 1
|
| 164 |
+
# Packed single-forward training path (StreamingLLM-style sliding-window+sink
|
| 165 |
+
# mask via FlexAttention) — replaces the per-frame Python loop. Eval/live still
|
| 166 |
+
# uses the streaming KV memory (`_predict_action_memory`). OFF by default so the
|
| 167 |
+
# legacy loop stays the reference; enable per experiment via
|
| 168 |
+
# `framework.kv_memory.packed_train: true`.
|
| 169 |
+
self.kv_train_packed = bool(getattr(kv_cfg, "packed_train", False)) if kv_cfg is not None else False
|
| 170 |
+
# Per-step re-based sink for the packed path: replicate the (short) text
|
| 171 |
+
# prefix once per step and re-base its M-RoPE positions so the sink→frame
|
| 172 |
+
# distance matches the eval rolling-KV layout exactly (StreamingLLM
|
| 173 |
+
# "positions within the cache"), instead of one global sink whose distance
|
| 174 |
+
# grows with the step index. ON by default — the growing-distance sink was
|
| 175 |
+
# the train/eval forward mismatch behind the packed run's eval collapse.
|
| 176 |
+
# Set false to fall back to the single global sink for A/B.
|
| 177 |
+
self.kv_rebased_sink = bool(getattr(kv_cfg, "rebased_sink", True)) if kv_cfg is not None else True
|
| 178 |
+
if self.state_encoding == "continuous_projector" and self.kv_memory_enabled:
|
| 179 |
+
raise ValueError("continuous_projector requires framework.kv_memory.enabled=false")
|
| 180 |
+
# Per-stream (eval slot) memories; rebuilt per sample during training.
|
| 181 |
+
self._kv_memories: dict = {}
|
| 182 |
+
|
| 183 |
+
# L1 loss
|
| 184 |
+
self.l1_loss = nn.L1Loss()
|
| 185 |
+
|
| 186 |
+
def _profile_timing_enabled(self) -> bool:
|
| 187 |
+
return self.config.trainer.profile_timing.enabled
|
| 188 |
+
|
| 189 |
+
def _profile_sync(self) -> None:
|
| 190 |
+
if self._profile_timing_enabled() and torch.cuda.is_available():
|
| 191 |
+
torch.cuda.synchronize()
|
| 192 |
+
|
| 193 |
+
def _profile_start(self) -> float:
|
| 194 |
+
self._profile_sync()
|
| 195 |
+
return time.perf_counter()
|
| 196 |
+
|
| 197 |
+
def _profile_elapsed(self, start: float) -> float:
|
| 198 |
+
self._profile_sync()
|
| 199 |
+
return time.perf_counter() - start
|
| 200 |
+
|
| 201 |
+
@staticmethod
|
| 202 |
+
def _to_plain_dict(value: Any) -> dict[str, Any]:
|
| 203 |
+
if value is None or value == "":
|
| 204 |
+
return {}
|
| 205 |
+
if isinstance(value, dict):
|
| 206 |
+
return {str(key): item for key, item in value.items()}
|
| 207 |
+
if hasattr(value, "items"):
|
| 208 |
+
return {str(key): item for key, item in value.items()}
|
| 209 |
+
return {}
|
| 210 |
+
|
| 211 |
+
def _prepare_action_array(self, action: Any) -> np.ndarray:
|
| 212 |
+
values = np.asarray(action)
|
| 213 |
+
if values.ndim == 1:
|
| 214 |
+
values = values[None, :]
|
| 215 |
+
if values.shape[-1] == self.action_dim:
|
| 216 |
+
return values
|
| 217 |
+
if values.shape[-1] > self.action_dim:
|
| 218 |
+
return values[..., : self.action_dim]
|
| 219 |
+
pad_width = [(0, 0)] * values.ndim
|
| 220 |
+
pad_width[-1] = (0, self.action_dim - values.shape[-1])
|
| 221 |
+
return np.pad(values, pad_width, mode="constant")
|
| 222 |
+
|
| 223 |
+
def _compute_action_loss_for_type(
|
| 224 |
+
self,
|
| 225 |
+
pred_actions: torch.Tensor,
|
| 226 |
+
actions_target: torch.Tensor,
|
| 227 |
+
*,
|
| 228 |
+
loss_type: str,
|
| 229 |
+
action_env_dims: Optional[List[int]] = None,
|
| 230 |
+
) -> torch.Tensor:
|
| 231 |
+
loss_type = str(loss_type).lower()
|
| 232 |
+
effective_dim = min(self.action_env_dim, pred_actions.shape[-1], actions_target.shape[-1])
|
| 233 |
+
if effective_dim <= 0:
|
| 234 |
+
raise ValueError(
|
| 235 |
+
f"Invalid action_env_dim={self.action_env_dim} for predicted shape={pred_actions.shape} "
|
| 236 |
+
f"and target shape={actions_target.shape}"
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
if loss_type in {"discrete_ce", "ce", "cross_entropy"}:
|
| 240 |
+
if action_env_dims is not None:
|
| 241 |
+
dims = torch.as_tensor(action_env_dims, device=pred_actions.device, dtype=torch.long)
|
| 242 |
+
if dims.numel() != pred_actions.shape[0]:
|
| 243 |
+
raise ValueError(f"Expected {pred_actions.shape[0]} action_env_dims, got {dims.numel()}")
|
| 244 |
+
losses = []
|
| 245 |
+
weights = []
|
| 246 |
+
for dim_value in torch.unique(dims).tolist():
|
| 247 |
+
dim = int(min(dim_value, pred_actions.shape[-1], actions_target.shape[-1]))
|
| 248 |
+
if dim < 2:
|
| 249 |
+
raise ValueError(f"discrete_ce requires at least 2 classes, got action_env_dim={dim}")
|
| 250 |
+
mask = dims == int(dim_value)
|
| 251 |
+
logits = pred_actions[mask, ..., :dim]
|
| 252 |
+
target_class = actions_target[mask, ..., :dim].argmax(dim=-1).long()
|
| 253 |
+
losses.append(F.cross_entropy(logits.reshape(-1, dim), target_class.reshape(-1), reduction="sum"))
|
| 254 |
+
weights.append(target_class.numel())
|
| 255 |
+
total_weight = max(1, int(sum(weights)))
|
| 256 |
+
return sum(losses) / total_weight
|
| 257 |
+
|
| 258 |
+
if effective_dim < 2:
|
| 259 |
+
raise ValueError(
|
| 260 |
+
f"action_model.loss_type={loss_type!r} requires at least 2 action classes, "
|
| 261 |
+
f"got effective_dim={effective_dim}"
|
| 262 |
+
)
|
| 263 |
+
logits = pred_actions[..., :effective_dim]
|
| 264 |
+
target_class = actions_target[..., :effective_dim].argmax(dim=-1).long()
|
| 265 |
+
return F.cross_entropy(
|
| 266 |
+
logits.reshape(-1, effective_dim),
|
| 267 |
+
target_class.reshape(-1),
|
| 268 |
+
)
|
| 269 |
+
|
| 270 |
+
if loss_type in {"asterix_factorized_ce", "factorized_6_ce", "factorized_ce"}:
|
| 271 |
+
if effective_dim < 6:
|
| 272 |
+
raise ValueError(
|
| 273 |
+
f"action_model.loss_type={loss_type!r} requires at least 6 action logits, "
|
| 274 |
+
f"got effective_dim={effective_dim}"
|
| 275 |
+
)
|
| 276 |
+
vertical_logits = pred_actions[..., 0:3]
|
| 277 |
+
vertical_target = actions_target[..., 0:3].argmax(dim=-1).long()
|
| 278 |
+
horizontal_logits = pred_actions[..., 3:6]
|
| 279 |
+
horizontal_target = actions_target[..., 3:6].argmax(dim=-1).long()
|
| 280 |
+
vertical_loss = F.cross_entropy(
|
| 281 |
+
vertical_logits.reshape(-1, 3),
|
| 282 |
+
vertical_target.reshape(-1),
|
| 283 |
+
)
|
| 284 |
+
horizontal_loss = F.cross_entropy(
|
| 285 |
+
horizontal_logits.reshape(-1, 3),
|
| 286 |
+
horizontal_target.reshape(-1),
|
| 287 |
+
)
|
| 288 |
+
return 0.5 * (vertical_loss + horizontal_loss)
|
| 289 |
+
|
| 290 |
+
if loss_type in {"multibinary_bce", "multibinary_ce", "bce", "binary_cross_entropy"}:
|
| 291 |
+
logits = pred_actions[..., :effective_dim]
|
| 292 |
+
targets = (actions_target[..., :effective_dim] > 0).to(dtype=logits.dtype)
|
| 293 |
+
return F.binary_cross_entropy_with_logits(logits, targets)
|
| 294 |
+
|
| 295 |
+
if loss_type in {"mse", "l2"}:
|
| 296 |
+
return F.mse_loss(
|
| 297 |
+
pred_actions[..., :effective_dim],
|
| 298 |
+
actions_target[..., :effective_dim],
|
| 299 |
+
)
|
| 300 |
+
|
| 301 |
+
if loss_type not in {"l1", "mae"}:
|
| 302 |
+
raise ValueError(
|
| 303 |
+
f"Unsupported action_model.loss_type={loss_type!r}; "
|
| 304 |
+
"expected one of: l1, discrete_ce, multibinary_bce, asterix_factorized_ce"
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
return self.l1_loss(
|
| 308 |
+
pred_actions[..., :effective_dim],
|
| 309 |
+
actions_target[..., :effective_dim],
|
| 310 |
+
)
|
| 311 |
+
|
| 312 |
+
def _compute_action_loss(
|
| 313 |
+
self,
|
| 314 |
+
pred_actions: torch.Tensor,
|
| 315 |
+
actions_target: torch.Tensor,
|
| 316 |
+
action_env_dims: Optional[List[int]] = None,
|
| 317 |
+
rl_games_tasks: Optional[List[str]] = None,
|
| 318 |
+
action_loss_mask: Optional[torch.Tensor] = None,
|
| 319 |
+
) -> torch.Tensor:
|
| 320 |
+
if action_loss_mask is not None:
|
| 321 |
+
error = (pred_actions[..., :self.action_env_dim] - actions_target[..., :self.action_env_dim]).abs().mean(dim=-1)
|
| 322 |
+
error = error.masked_fill(~action_loss_mask, 0)
|
| 323 |
+
return (error.sum(dim=-1) / action_loss_mask.sum(dim=-1)).mean()
|
| 324 |
+
if self.loss_by_task and rl_games_tasks is not None:
|
| 325 |
+
if len(rl_games_tasks) != pred_actions.shape[0]:
|
| 326 |
+
raise ValueError(f"Expected {pred_actions.shape[0]} task labels, got {len(rl_games_tasks)}")
|
| 327 |
+
losses = []
|
| 328 |
+
weights = []
|
| 329 |
+
for task_name in sorted(set(str(task) for task in rl_games_tasks)):
|
| 330 |
+
mask_values = [str(task) == task_name for task in rl_games_tasks]
|
| 331 |
+
mask = torch.as_tensor(mask_values, device=pred_actions.device, dtype=torch.bool)
|
| 332 |
+
if not bool(mask.any()):
|
| 333 |
+
continue
|
| 334 |
+
task_loss_type = str(self.loss_by_task.get(task_name, self.action_loss_type)).lower()
|
| 335 |
+
task_dims = (
|
| 336 |
+
[int(dim) for dim, keep in zip(action_env_dims, mask_values) if keep]
|
| 337 |
+
if action_env_dims is not None
|
| 338 |
+
else None
|
| 339 |
+
)
|
| 340 |
+
task_loss = self._compute_action_loss_for_type(
|
| 341 |
+
pred_actions[mask],
|
| 342 |
+
actions_target[mask],
|
| 343 |
+
loss_type=task_loss_type,
|
| 344 |
+
action_env_dims=task_dims,
|
| 345 |
+
)
|
| 346 |
+
task_weight = float(self.loss_weight_by_task.get(task_name, 1.0))
|
| 347 |
+
sample_count = int(mask.sum().item())
|
| 348 |
+
losses.append(task_loss * task_weight * sample_count)
|
| 349 |
+
weights.append(task_weight * sample_count)
|
| 350 |
+
if losses:
|
| 351 |
+
return sum(losses) / max(1.0, float(sum(weights)))
|
| 352 |
+
|
| 353 |
+
return self._compute_action_loss_for_type(
|
| 354 |
+
pred_actions,
|
| 355 |
+
actions_target,
|
| 356 |
+
loss_type=self.action_loss_type,
|
| 357 |
+
action_env_dims=action_env_dims,
|
| 358 |
+
)
|
| 359 |
+
|
| 360 |
+
def _forward_qwen_last_hidden(self, qwen_inputs: dict) -> torch.Tensor:
|
| 361 |
+
"""
|
| 362 |
+
Return only the final VLM hidden state when the interface supports it.
|
| 363 |
+
|
| 364 |
+
Requesting ``output_hidden_states=True`` from Hugging Face materializes
|
| 365 |
+
every layer's activations. For OFT we only need the last layer, so the
|
| 366 |
+
Qwen3 interface exposes a leaner path that preserves the practical memory
|
| 367 |
+
savings from gradient checkpointing.
|
| 368 |
+
"""
|
| 369 |
+
|
| 370 |
+
if hasattr(self.qwen_vl_interface, "forward_last_hidden"):
|
| 371 |
+
return self.qwen_vl_interface.forward_last_hidden(**qwen_inputs)
|
| 372 |
+
|
| 373 |
+
qwenvl_outputs = self.qwen_vl_interface(
|
| 374 |
+
**qwen_inputs,
|
| 375 |
+
output_attentions=False,
|
| 376 |
+
output_hidden_states=True,
|
| 377 |
+
return_dict=True,
|
| 378 |
+
use_cache=False,
|
| 379 |
+
)
|
| 380 |
+
return qwenvl_outputs.hidden_states[-1]
|
| 381 |
+
|
| 382 |
+
def _state_conditioned_instructions(self, instructions, state):
|
| 383 |
+
if state is not None and self.state_encoding == "discretized_text":
|
| 384 |
+
return self.add_discretized_state_to_instruction(instructions, state)
|
| 385 |
+
return instructions
|
| 386 |
+
|
| 387 |
+
def _condition_action_queries(self, action_queries, state):
|
| 388 |
+
if self.state_encoding == "discretized_text":
|
| 389 |
+
return action_queries
|
| 390 |
+
state_tensor = torch.as_tensor(
|
| 391 |
+
np.stack(state), device=action_queries.device, dtype=action_queries.dtype
|
| 392 |
+
).squeeze(1)
|
| 393 |
+
return action_queries + self.action_model.state_projector(state_tensor).unsqueeze(1)
|
| 394 |
+
|
| 395 |
+
def forward(
|
| 396 |
+
self,
|
| 397 |
+
examples: List[dict] = None,
|
| 398 |
+
**kwargs,
|
| 399 |
+
) -> Tuple:
|
| 400 |
+
"""
|
| 401 |
+
Training forward: directly regress future actions (no diffusion).
|
| 402 |
+
|
| 403 |
+
Flow:
|
| 404 |
+
1. Build QwenVL inputs (images + instruction tokens)
|
| 405 |
+
2. Extract hidden states from configured layer range
|
| 406 |
+
7. Predict action and compute L1 loss
|
| 407 |
+
|
| 408 |
+
Args:
|
| 409 |
+
examples: List[dict], each dict requires:
|
| 410 |
+
- image: List[PIL.Image] (multi-view)
|
| 411 |
+
- lang: str instruction
|
| 412 |
+
- action: np.ndarray or list shaped [T, action_dim]
|
| 413 |
+
**kwargs: Reserved.
|
| 414 |
+
|
| 415 |
+
Returns:
|
| 416 |
+
dict:
|
| 417 |
+
action_loss (torch.Tensor): Scalar diffusion noise prediction loss.
|
| 418 |
+
"""
|
| 419 |
+
if self.kv_memory_enabled:
|
| 420 |
+
if self.kv_train_packed:
|
| 421 |
+
return self._forward_memory_packed(examples)
|
| 422 |
+
return self._forward_memory(examples)
|
| 423 |
+
|
| 424 |
+
batch_images = [example["image"] for example in examples] # [B,[PLT]]
|
| 425 |
+
instructions = [example["lang"] for example in examples] # [B, str]
|
| 426 |
+
actions = [self._prepare_action_array(example["action"]) for example in examples] # label [B, len, action_dim]
|
| 427 |
+
action_env_dims = [int(example.get("action_env_dim", self.action_env_dim)) for example in examples]
|
| 428 |
+
rl_games_tasks = [str(example.get("rl_games_task", "")) for example in examples]
|
| 429 |
+
if not any(rl_games_tasks):
|
| 430 |
+
rl_games_tasks = None
|
| 431 |
+
state = (
|
| 432 |
+
[example["state"] for example in examples] if "state" in examples[0] else None
|
| 433 |
+
) # List[ndarray (1, state_dim)] or None
|
| 434 |
+
|
| 435 |
+
instructions = self._state_conditioned_instructions(instructions, state)
|
| 436 |
+
|
| 437 |
+
# step 0: add special action token to instruction
|
| 438 |
+
action_tokens = (
|
| 439 |
+
self.action_token * self.chunk_len
|
| 440 |
+
) # can't add " " between two tokens, otherwise will be tokenized to multiple tokens
|
| 441 |
+
prompt_suffix = f" Please predict the next {self.chunk_len} robot actions: <action>{action_tokens}<action>."
|
| 442 |
+
instructions = [instruction + prompt_suffix for instruction in instructions]
|
| 443 |
+
|
| 444 |
+
profile_timing = self._profile_timing_enabled()
|
| 445 |
+
timing_metrics = {}
|
| 446 |
+
batch_stats = {}
|
| 447 |
+
|
| 448 |
+
# Step 1: QWenVL input format
|
| 449 |
+
t_inputs = self._profile_start() if profile_timing else None
|
| 450 |
+
qwen_inputs = self.qwen_vl_interface.build_qwenvl_inputs(images=batch_images, instructions=instructions)
|
| 451 |
+
if profile_timing:
|
| 452 |
+
timing_metrics["timing/qwen_input_build_total"] = self._profile_elapsed(t_inputs)
|
| 453 |
+
timing_metrics.update(self.qwen_vl_interface._last_build_timing)
|
| 454 |
+
input_ids = qwen_inputs["input_ids"]
|
| 455 |
+
attention_mask = qwen_inputs["attention_mask"]
|
| 456 |
+
effective_tokens = attention_mask.sum()
|
| 457 |
+
effective_token_count = effective_tokens.detach().float().item()
|
| 458 |
+
batch_stats["batch/size"] = float(len(examples))
|
| 459 |
+
batch_stats["batch/input_len_max"] = float(input_ids.shape[1])
|
| 460 |
+
batch_stats["batch/input_len_mean"] = float(effective_token_count / input_ids.shape[0])
|
| 461 |
+
batch_stats["batch/padding_ratio"] = float(1.0 - effective_token_count / float(attention_mask.numel()))
|
| 462 |
+
batch_stats["batch/effective_tokens"] = float(effective_token_count)
|
| 463 |
+
batch_stats["batch/image_count"] = float(sum(len(images) for images in batch_images))
|
| 464 |
+
if "pixel_values" in qwen_inputs:
|
| 465 |
+
batch_stats["batch/pixel_values_rows"] = float(qwen_inputs["pixel_values"].shape[0])
|
| 466 |
+
|
| 467 |
+
t_vlm = self._profile_start() if profile_timing else None
|
| 468 |
+
with torch.autocast("cuda", dtype=torch.bfloat16):
|
| 469 |
+
last_hidden = self._forward_qwen_last_hidden(qwen_inputs) # [B, L, H]
|
| 470 |
+
if profile_timing:
|
| 471 |
+
timing_metrics["timing/vlm_forward"] = self._profile_elapsed(t_vlm)
|
| 472 |
+
|
| 473 |
+
# Step 4: Action Expert Forward and Loss
|
| 474 |
+
t_action = self._profile_start() if profile_timing else None
|
| 475 |
+
with torch.autocast("cuda", dtype=torch.float32):
|
| 476 |
+
# Extract action token embeddings as action prediction queries
|
| 477 |
+
input_ids = qwen_inputs["input_ids"]
|
| 478 |
+
action_queries = self._gather_action_token_embeddings(
|
| 479 |
+
last_hidden, input_ids, action_token_id=self.action_token_id
|
| 480 |
+
) # [B, chunk_len, H]
|
| 481 |
+
action_queries = self._condition_action_queries(action_queries, state)
|
| 482 |
+
pred_actions = self.action_model.predict_action(action_queries) # (B, chunk_len, action_dim)
|
| 483 |
+
|
| 484 |
+
# Label alignment: take the last chunk_len segment
|
| 485 |
+
actions = torch.tensor(
|
| 486 |
+
np.array(actions), device=pred_actions.device, dtype=pred_actions.dtype
|
| 487 |
+
) # [B, T_full, action_dim]
|
| 488 |
+
actions_target = actions[:, -self.action_horizon :, :] # (B, action_horizon, action_dim)
|
| 489 |
+
|
| 490 |
+
action_loss_mask = None
|
| 491 |
+
if "action_loss_mask" in examples[0]:
|
| 492 |
+
action_loss_mask = torch.as_tensor(
|
| 493 |
+
np.asarray([example["action_loss_mask"] for example in examples]),
|
| 494 |
+
device=pred_actions.device,
|
| 495 |
+
dtype=torch.bool,
|
| 496 |
+
)
|
| 497 |
+
|
| 498 |
+
action_loss = self._compute_action_loss(
|
| 499 |
+
pred_actions,
|
| 500 |
+
actions_target,
|
| 501 |
+
action_env_dims=action_env_dims,
|
| 502 |
+
rl_games_tasks=rl_games_tasks,
|
| 503 |
+
action_loss_mask=action_loss_mask,
|
| 504 |
+
)
|
| 505 |
+
if profile_timing:
|
| 506 |
+
timing_metrics["timing/action_head_loss"] = self._profile_elapsed(t_action)
|
| 507 |
+
|
| 508 |
+
return {"action_loss": action_loss, "loss_weight": float(len(examples)), "timing": timing_metrics, "batch_stats": batch_stats}
|
| 509 |
+
|
| 510 |
+
@torch.inference_mode()
|
| 511 |
+
def predict_action(
|
| 512 |
+
self,
|
| 513 |
+
examples: List[dict] = None,
|
| 514 |
+
**kwargs: str,
|
| 515 |
+
) -> np.ndarray:
|
| 516 |
+
"""
|
| 517 |
+
|
| 518 |
+
Steps:
|
| 519 |
+
1. Resize images to training resolution (if specified)
|
| 520 |
+
2. Encode with QwenVL (hidden states retained)
|
| 521 |
+
6. Return normalized action trajectory
|
| 522 |
+
|
| 523 |
+
Returns:
|
| 524 |
+
dict:
|
| 525 |
+
normalized_actions (np.ndarray): Shape [B, T, action_dim], diffusion-sampled normalized actions.
|
| 526 |
+
"""
|
| 527 |
+
if type(examples) is not list:
|
| 528 |
+
examples = [examples]
|
| 529 |
+
if self.kv_memory_enabled:
|
| 530 |
+
return self._predict_action_memory(examples)
|
| 531 |
+
batch_images = [to_pil_preserve(example["image"]) for example in examples] # [B,[PLT]]
|
| 532 |
+
instructions = [example["lang"] for example in examples] # [B, str]
|
| 533 |
+
state = (
|
| 534 |
+
[example["state"] for example in examples] if "state" in examples[0] else None
|
| 535 |
+
) # List[ndarray (1, state_dim)] or None
|
| 536 |
+
|
| 537 |
+
instructions = self._state_conditioned_instructions(instructions, state)
|
| 538 |
+
|
| 539 |
+
train_obs_image_size = getattr(self.config.datasets.vla_data, "obs_image_size", None)
|
| 540 |
+
if train_obs_image_size:
|
| 541 |
+
batch_images = resize_images(batch_images, target_size=train_obs_image_size)
|
| 542 |
+
|
| 543 |
+
# step 0: add special action token to instruction
|
| 544 |
+
action_tokens = (
|
| 545 |
+
self.action_token * self.chunk_len
|
| 546 |
+
) # can't add " " between two tokens, otherwise will be tokenized to multiple tokens
|
| 547 |
+
prompt_suffix = f" Please predict the next {self.chunk_len} robot actions: <action>{action_tokens}<action>."
|
| 548 |
+
instructions = [instruction + prompt_suffix for instruction in instructions]
|
| 549 |
+
|
| 550 |
+
# Step 1: QWenVL input format
|
| 551 |
+
qwen_inputs = self.qwen_vl_interface.build_qwenvl_inputs(images=batch_images, instructions=instructions)
|
| 552 |
+
with torch.autocast("cuda", dtype=torch.bfloat16):
|
| 553 |
+
last_hidden = self._forward_qwen_last_hidden(qwen_inputs) # [B, L, H]
|
| 554 |
+
|
| 555 |
+
# Step 4: Action Expert Forward and Loss
|
| 556 |
+
with torch.autocast("cuda", dtype=torch.float32):
|
| 557 |
+
# Extract action token embeddings as action prediction queries
|
| 558 |
+
input_ids = qwen_inputs["input_ids"]
|
| 559 |
+
action_queries = self._gather_action_token_embeddings(
|
| 560 |
+
last_hidden, input_ids, action_token_id=self.action_token_id
|
| 561 |
+
) # [B, chunk_len, H]
|
| 562 |
+
action_queries = self._condition_action_queries(action_queries, state)
|
| 563 |
+
pred_actions = self.action_model.predict_action(action_queries) # (B, chunk_len, action_dim)
|
| 564 |
+
|
| 565 |
+
normalized_actions = pred_actions.detach().cpu().numpy()
|
| 566 |
+
return {"normalized_actions": normalized_actions}
|
| 567 |
+
|
| 568 |
+
# ──────────────────────────────────────────────────────────────────
|
| 569 |
+
# Fixed-size KV memory. Default OFF. The cache/rotation core is
|
| 570 |
+
# unit-proven (tests/test_kv_memory_equivalence.py); the processor-driven
|
| 571 |
+
# segment construction below still needs end-to-end validation against the
|
| 572 |
+
# real Qwen3-VL-4B checkpoint on GPU.
|
| 573 |
+
# ──────────────────────────────────────────────────────────────────
|
| 574 |
+
def reset_memory(self, slot_id=None) -> None:
|
| 575 |
+
"""Clear the KV memory for one eval slot (episode boundary), or all."""
|
| 576 |
+
if slot_id is None:
|
| 577 |
+
self._kv_memories.clear()
|
| 578 |
+
else:
|
| 579 |
+
self._kv_memories.pop(slot_id, None)
|
| 580 |
+
|
| 581 |
+
def _kv_components(self):
|
| 582 |
+
hf = self.qwen_vl_interface.model # Qwen3VLForConditionalGeneration
|
| 583 |
+
backbone = hf.model # Qwen3VLModel
|
| 584 |
+
text_model = backbone.language_model # Qwen3VLTextModel
|
| 585 |
+
return backbone, text_model.rotary_emb, len(text_model.layers), text_model.config
|
| 586 |
+
|
| 587 |
+
def _new_kv_memory(self):
|
| 588 |
+
from starVLA.model.modules.vlm.kv_memory import FrameKVMemory
|
| 589 |
+
|
| 590 |
+
_, rotary_emb, num_layers, text_config = self._kv_components()
|
| 591 |
+
return FrameKVMemory(rotary_emb, self.kv_window, num_layers, text_config)
|
| 592 |
+
|
| 593 |
+
def _build_text_first_inputs(self, frames, instruction):
|
| 594 |
+
"""Tokenize a text-first window: [instruction][frame imgs...][action suffix].
|
| 595 |
+
|
| 596 |
+
Returns the processor batch (input_ids, pixel_values, image_grid_thw, ...).
|
| 597 |
+
Static text comes first so its KV stays positionally stable across steps.
|
| 598 |
+
"""
|
| 599 |
+
action_tokens = self.action_token * self.chunk_len
|
| 600 |
+
action_suffix = f" Please predict the next {self.chunk_len} robot actions: <action>{action_tokens}<action>."
|
| 601 |
+
content = [{"type": "text", "text": instruction}]
|
| 602 |
+
content += [{"type": "image", "image": img} for img in frames]
|
| 603 |
+
content += [{"type": "text", "text": action_suffix}]
|
| 604 |
+
messages = [[{"role": "user", "content": content}]]
|
| 605 |
+
batch = self.qwen_vl_interface.processor.apply_chat_template(
|
| 606 |
+
messages, tokenize=True, padding=True, add_generation_prompt=True, return_dict=True, return_tensors="pt"
|
| 607 |
+
)
|
| 608 |
+
return batch.to(self.qwen_vl_interface.model.device)
|
| 609 |
+
|
| 610 |
+
def _kv_segments(self, input_ids_1d):
|
| 611 |
+
"""Partition a text-first window's ids into (prefix_len, [frame_lens], action_len).
|
| 612 |
+
|
| 613 |
+
A frame block spans its vision_start .. vision_end (inclusive); the prefix is
|
| 614 |
+
everything before the first vision_start; the action suffix is everything
|
| 615 |
+
after the last vision_end. Using the real special-token ids keeps eviction
|
| 616 |
+
leak-free (each evicted frame removes exactly its own markers).
|
| 617 |
+
"""
|
| 618 |
+
cfg = self.qwen_vl_interface.model.config
|
| 619 |
+
vstart = int(getattr(cfg, "vision_start_token_id", 151652))
|
| 620 |
+
vend = int(getattr(cfg, "vision_end_token_id", 151653))
|
| 621 |
+
ids = input_ids_1d.tolist()
|
| 622 |
+
starts = [i for i, t in enumerate(ids) if t == vstart]
|
| 623 |
+
ends = [i for i, t in enumerate(ids) if t == vend]
|
| 624 |
+
if not starts or not ends:
|
| 625 |
+
raise RuntimeError("KV memory: window inputs have no vision_start/end tokens")
|
| 626 |
+
action_start = ends[-1] + 1
|
| 627 |
+
bounds = starts + [action_start]
|
| 628 |
+
frame_lens = [bounds[i + 1] - bounds[i] for i in range(len(starts))]
|
| 629 |
+
return starts[0], frame_lens, len(ids) - action_start
|
| 630 |
+
|
| 631 |
+
def _kv_forward_step_batched(self, memories, frames, instruction):
|
| 632 |
+
"""Run ONE streaming KV-memory step for a group of same-layout streams and
|
| 633 |
+
return ``(last_hidden_new [B, new_len, H], new_input_ids [B, new_len])``.
|
| 634 |
+
|
| 635 |
+
``memories`` and ``frames`` are aligned lists (one entry per stream / eval
|
| 636 |
+
slot); all entries share ``instruction`` and history layout (grouped by the
|
| 637 |
+
caller). Two efficiencies vs a naive per-frame, per-step encode:
|
| 638 |
+
|
| 639 |
+
- Single-frame preprocessing: the image processor runs once per stream on its
|
| 640 |
+
OWN current frame. The window's multi-frame token layout (needed only for
|
| 641 |
+
M-RoPE positions via ``get_rope_index``) is reconstructed by replicating the
|
| 642 |
+
frame's token block, so duplicate frames are never re-preprocessed.
|
| 643 |
+
- Cross-stream batching: the streams' canonical KV blocks are stacked along the
|
| 644 |
+
batch dim, so the whole group is one forward; results are sliced back to each
|
| 645 |
+
stream's memory afterwards.
|
| 646 |
+
|
| 647 |
+
Past frames come from each stream's memory; only the current frame's pixels are
|
| 648 |
+
encoded fresh. ``B == 1`` (training rollout) reuses the same path with no
|
| 649 |
+
stacking, so gradients flow through the in-place memory updates.
|
| 650 |
+
"""
|
| 651 |
+
from starVLA.model.modules.vlm.kv_memory import FrameKVMemory, memory_step
|
| 652 |
+
|
| 653 |
+
backbone = self.qwen_vl_interface.model.model # Qwen3VLModel
|
| 654 |
+
B = len(memories)
|
| 655 |
+
n_past = memories[0].num_past_frames()
|
| 656 |
+
n_visible = n_past + 1
|
| 657 |
+
|
| 658 |
+
# One processor call per stream's current frame (no duplicate-frame work).
|
| 659 |
+
per_stream = [self._build_text_first_inputs([frame], instruction) for frame in frames]
|
| 660 |
+
ids1 = per_stream[0]["input_ids"][0]
|
| 661 |
+
cur_grid = per_stream[0]["image_grid_thw"][:1]
|
| 662 |
+
prefix_len, frame_lens, action_len = self._kv_segments(ids1)
|
| 663 |
+
frame_len = frame_lens[0]
|
| 664 |
+
|
| 665 |
+
# Reconstruct the n_visible-frame window ids by replicating the frame block,
|
| 666 |
+
# then let get_rope_index assign the (content-independent) M-RoPE positions.
|
| 667 |
+
prefix, action = ids1[:prefix_len], ids1[prefix_len + frame_len :]
|
| 668 |
+
block = ids1[prefix_len : prefix_len + frame_len]
|
| 669 |
+
window_ids = torch.cat([prefix] + [block] * n_visible + [action]).unsqueeze(0)
|
| 670 |
+
full_pos1, _ = backbone.get_rope_index(window_ids, image_grid_thw=cur_grid.repeat(n_visible, 1))
|
| 671 |
+
full_pos = full_pos1.expand(3, B, full_pos1.shape[-1]).contiguous()
|
| 672 |
+
|
| 673 |
+
batched = FrameKVMemory.stack(memories) if B > 1 else memories[0]
|
| 674 |
+
past_len = batched.past_length()
|
| 675 |
+
is_prefill = not batched.has_text()
|
| 676 |
+
expected_past = 0 if is_prefill else prefix_len + n_past * frame_len
|
| 677 |
+
if past_len != expected_past:
|
| 678 |
+
raise RuntimeError(
|
| 679 |
+
f"KV memory slice misaligned: memory past_length={past_len} but window "
|
| 680 |
+
f"expects {expected_past}. Token boundaries drifted across steps."
|
| 681 |
+
)
|
| 682 |
+
if is_prefill:
|
| 683 |
+
new_start, frame_start_in_new, prefill_text_len = 0, prefix_len, prefix_len
|
| 684 |
+
else:
|
| 685 |
+
new_start, frame_start_in_new, prefill_text_len = past_len, 0, 0
|
| 686 |
+
|
| 687 |
+
new_ids = window_ids[:, new_start:].expand(B, -1).contiguous()
|
| 688 |
+
pixels = torch.cat([b["pixel_values"] for b in per_stream], dim=0)
|
| 689 |
+
grid = cur_grid.repeat(B, 1)
|
| 690 |
+
|
| 691 |
+
last_hidden = memory_step(
|
| 692 |
+
backbone,
|
| 693 |
+
batched,
|
| 694 |
+
new_input_ids=new_ids,
|
| 695 |
+
pixel_values=pixels,
|
| 696 |
+
image_grid_thw=grid,
|
| 697 |
+
full_position_ids=full_pos,
|
| 698 |
+
frame_start_in_new=frame_start_in_new,
|
| 699 |
+
frame_len=frame_len,
|
| 700 |
+
prefill_text_len=prefill_text_len,
|
| 701 |
+
)
|
| 702 |
+
|
| 703 |
+
if B > 1:
|
| 704 |
+
for i, memory in enumerate(memories):
|
| 705 |
+
sliced = batched.slice(i)
|
| 706 |
+
memory.text, memory.frames = sliced.text, sliced.frames
|
| 707 |
+
return last_hidden, new_ids
|
| 708 |
+
|
| 709 |
+
# ------------------------------------------------------------------ #
|
| 710 |
+
# Packed single-forward training path (StreamingLLM-style mask).
|
| 711 |
+
#
|
| 712 |
+
# Instead of the per-frame Python loop (`_forward_memory`), pack the whole
|
| 713 |
+
# rollout into ONE sequence
|
| 714 |
+
# [text/sink] [frame_1][action_1] ... [frame_R][action_R]
|
| 715 |
+
# and run a SINGLE Qwen3-VL forward under a FlexAttention BlockMask that
|
| 716 |
+
# reproduces the streaming visibility:
|
| 717 |
+
# * the text prefix is an always-visible attention sink (StreamingLLM);
|
| 718 |
+
# * each frame attends only the last `kv_window` frames (sliding window);
|
| 719 |
+
# * each action read-out block is isolated (seen only by its own step), so
|
| 720 |
+
# the frame KV stream is bit-identical to the streaming loop / eval.
|
| 721 |
+
# Custom M-RoPE `position_ids` keep frame->frame and action->frame relative
|
| 722 |
+
# distances exactly matching eval; only the text-sink->frame absolute distance
|
| 723 |
+
# grows with step (accepted approximation — the sink is distance-robust).
|
| 724 |
+
# Eval/live (`_predict_action_memory`) is untouched and keeps the rolling KV.
|
| 725 |
+
# ------------------------------------------------------------------ #
|
| 726 |
+
def _packed_window_layout(self, frames, instruction):
|
| 727 |
+
"""Build the packed [text][f1][a1]...[fR][aR] window for ONE stream.
|
| 728 |
+
|
| 729 |
+
Token structure is content-independent (image placeholders are identical
|
| 730 |
+
per frame), so the prefix / frame / action blocks are reconstructed by
|
| 731 |
+
token-replication (matching `_kv_forward_step_batched`'s `window_ids`),
|
| 732 |
+
which guarantees each action block equals the per-step layout exactly.
|
| 733 |
+
|
| 734 |
+
Returns dict with: input_ids [1,L], pixel_values, image_grid_thw [R,3],
|
| 735 |
+
position_ids [3,1,L] (custom M-RoPE), kind [L] (0=text,1=frame,2=action),
|
| 736 |
+
step [L] (frame/step index for frame&action tokens, -1 for text),
|
| 737 |
+
action_idx [R, chunk_len] (packed indices of each step's action tokens).
|
| 738 |
+
"""
|
| 739 |
+
R = len(frames)
|
| 740 |
+
backbone = self.qwen_vl_interface.model.model # Qwen3VLModel
|
| 741 |
+
device = self.qwen_vl_interface.model.device
|
| 742 |
+
|
| 743 |
+
# One processor call per frame: identical token block, distinct pixels.
|
| 744 |
+
per_frame = [self._build_text_first_inputs([f], instruction) for f in frames]
|
| 745 |
+
ids1 = per_frame[0]["input_ids"][0]
|
| 746 |
+
grid1 = per_frame[0]["image_grid_thw"][:1] # [1,3]
|
| 747 |
+
prefix_len, frame_lens, action_len = self._kv_segments(ids1)
|
| 748 |
+
frame_len = frame_lens[0]
|
| 749 |
+
for pf in per_frame:
|
| 750 |
+
pl, fl, al = self._kv_segments(pf["input_ids"][0])
|
| 751 |
+
if (pl, fl[0], al) != (prefix_len, frame_len, action_len):
|
| 752 |
+
raise RuntimeError(
|
| 753 |
+
"Packed KV memory: frames have inconsistent token layout "
|
| 754 |
+
f"({(pl, fl[0], al)} vs {(prefix_len, frame_len, action_len)}); "
|
| 755 |
+
"all frames in a rollout must share resolution/grid."
|
| 756 |
+
)
|
| 757 |
+
|
| 758 |
+
prefix = ids1[:prefix_len]
|
| 759 |
+
frame_block = ids1[prefix_len : prefix_len + frame_len]
|
| 760 |
+
action_block = ids1[prefix_len + frame_len :] # incl. assistant header
|
| 761 |
+
block_len = frame_len + action_len
|
| 762 |
+
|
| 763 |
+
pixel_values = torch.cat([pf["pixel_values"] for pf in per_frame], dim=0)
|
| 764 |
+
grid = grid1.repeat(R, 1) # [R, 3]
|
| 765 |
+
|
| 766 |
+
# Frames-only M-RoPE positions: frames laid out consecutively (no action
|
| 767 |
+
# in between), so frame→frame / action→frame relative distances match eval
|
| 768 |
+
# ("positions within the cache"). These are the canonical frame positions
|
| 769 |
+
# reused regardless of where the frame sits in the packed token sequence.
|
| 770 |
+
frames_only_ids = torch.cat([prefix] + [frame_block] * R).unsqueeze(0)
|
| 771 |
+
full_pos_f, _ = backbone.get_rope_index(frames_only_ids, image_grid_thw=grid) # [3,1,Lf]
|
| 772 |
+
text_pos = full_pos_f[:, :, :prefix_len]
|
| 773 |
+
|
| 774 |
+
action_offsets = (action_block == self.action_token_id).nonzero(as_tuple=False).flatten()
|
| 775 |
+
if action_offsets.numel() < self.chunk_len:
|
| 776 |
+
raise RuntimeError(
|
| 777 |
+
f"Packed KV memory: action block has {action_offsets.numel()} action "
|
| 778 |
+
f"tokens (< chunk_len={self.chunk_len})."
|
| 779 |
+
)
|
| 780 |
+
action_offsets = action_offsets[-self.chunk_len :] # last chunk_len, matches gather
|
| 781 |
+
|
| 782 |
+
def _frame_pos(k):
|
| 783 |
+
s = prefix_len + k * frame_len
|
| 784 |
+
return full_pos_f[:, :, s : s + frame_len]
|
| 785 |
+
|
| 786 |
+
def _action_pos(frame_pos_k):
|
| 787 |
+
fk_max = int(frame_pos_k.max().item())
|
| 788 |
+
ap = torch.arange(1, action_len + 1, device=device) + fk_max
|
| 789 |
+
return ap.view(1, 1, -1).expand(3, 1, -1)
|
| 790 |
+
|
| 791 |
+
W = self.kv_window
|
| 792 |
+
parts, pos_parts, kind_parts, step_parts, action_idx_rows = [], [], [], [], []
|
| 793 |
+
|
| 794 |
+
if self.kv_rebased_sink:
|
| 795 |
+
# Per-step re-based sink, with the redundant pre-eviction sinks deduped.
|
| 796 |
+
# Steps k < W all have j0=0 → o_k=0 → an identical sink at [0,P), so they
|
| 797 |
+
# share ONE front sink (step=-2, visible to queries with step < W). This
|
| 798 |
+
# is exactly eval before any eviction (sink pinned at the front, frames
|
| 799 |
+
# growing from P). Steps k >= W each carry their own re-based sink
|
| 800 |
+
# ([sink_k][frame_k][action_k]) at o_k so the sink→frame distance equals
|
| 801 |
+
# eval's rolling-KV layout (bit-exact). Net: 1 + max(0,R-W) sinks instead
|
| 802 |
+
# of R, saving (W-1) prefix copies while staying numerically identical.
|
| 803 |
+
base0 = int(full_pos_f[0, 0, prefix_len].item()) # frame 0's temporal start
|
| 804 |
+
|
| 805 |
+
# Shared front sink for the pre-eviction steps (step=-2).
|
| 806 |
+
parts.append(prefix)
|
| 807 |
+
pos_parts.append(text_pos)
|
| 808 |
+
kind_parts.append(torch.zeros(prefix_len, dtype=torch.long, device=device))
|
| 809 |
+
step_parts.append(torch.full((prefix_len,), -2, dtype=torch.long, device=device))
|
| 810 |
+
cursor = prefix_len
|
| 811 |
+
|
| 812 |
+
for k in range(R):
|
| 813 |
+
if k >= W:
|
| 814 |
+
# Re-based sink_k sits right before step k's oldest visible frame.
|
| 815 |
+
j0 = k - W + 1
|
| 816 |
+
o_k = int(full_pos_f[0, 0, prefix_len + j0 * frame_len].item()) - base0
|
| 817 |
+
parts.append(prefix)
|
| 818 |
+
pos_parts.append(text_pos + o_k) # shift all 3 M-RoPE dims by o_k
|
| 819 |
+
kind_parts.append(torch.zeros(prefix_len, dtype=torch.long, device=device))
|
| 820 |
+
step_parts.append(torch.full((prefix_len,), k, dtype=torch.long, device=device))
|
| 821 |
+
cursor += prefix_len
|
| 822 |
+
frame_pos_k = _frame_pos(k)
|
| 823 |
+
parts += [frame_block, action_block]
|
| 824 |
+
pos_parts += [frame_pos_k, _action_pos(frame_pos_k)]
|
| 825 |
+
kind_parts.append(
|
| 826 |
+
torch.cat([
|
| 827 |
+
torch.ones(frame_len, dtype=torch.long, device=device),
|
| 828 |
+
torch.full((action_len,), 2, dtype=torch.long, device=device),
|
| 829 |
+
])
|
| 830 |
+
)
|
| 831 |
+
step_parts.append(torch.full((block_len,), k, dtype=torch.long, device=device))
|
| 832 |
+
action_idx_rows.append(cursor + frame_len + action_offsets)
|
| 833 |
+
cursor += block_len
|
| 834 |
+
else:
|
| 835 |
+
# Legacy single global sink: [sink][f0 a0]...[f_{R-1} a_{R-1}]. Sink at
|
| 836 |
+
# the front (step=-1, always visible); sink→frame distance grows with k.
|
| 837 |
+
parts.append(prefix)
|
| 838 |
+
pos_parts.append(text_pos)
|
| 839 |
+
kind_parts.append(torch.zeros(prefix_len, dtype=torch.long, device=device))
|
| 840 |
+
step_parts.append(torch.full((prefix_len,), -1, dtype=torch.long, device=device))
|
| 841 |
+
cursor = prefix_len
|
| 842 |
+
for k in range(R):
|
| 843 |
+
frame_pos_k = _frame_pos(k)
|
| 844 |
+
parts += [frame_block, action_block]
|
| 845 |
+
pos_parts += [frame_pos_k, _action_pos(frame_pos_k)]
|
| 846 |
+
kind_parts.append(
|
| 847 |
+
torch.cat([
|
| 848 |
+
torch.ones(frame_len, dtype=torch.long, device=device),
|
| 849 |
+
torch.full((action_len,), 2, dtype=torch.long, device=device),
|
| 850 |
+
])
|
| 851 |
+
)
|
| 852 |
+
step_parts.append(torch.full((block_len,), k, dtype=torch.long, device=device))
|
| 853 |
+
action_idx_rows.append(cursor + frame_len + action_offsets)
|
| 854 |
+
cursor += block_len
|
| 855 |
+
|
| 856 |
+
input_ids = torch.cat(parts).unsqueeze(0) # [1, L]
|
| 857 |
+
position_ids = torch.cat(pos_parts, dim=2) # [3,1,L]
|
| 858 |
+
kind = torch.cat(kind_parts)
|
| 859 |
+
step = torch.cat(step_parts)
|
| 860 |
+
action_idx = torch.stack(action_idx_rows, dim=0) # [R, chunk_len]
|
| 861 |
+
|
| 862 |
+
return {
|
| 863 |
+
"input_ids": input_ids,
|
| 864 |
+
"pixel_values": pixel_values,
|
| 865 |
+
"image_grid_thw": grid,
|
| 866 |
+
"position_ids": position_ids,
|
| 867 |
+
"kind": kind,
|
| 868 |
+
"step": step,
|
| 869 |
+
"action_idx": action_idx,
|
| 870 |
+
}
|
| 871 |
+
|
| 872 |
+
def _packed_block_mask(self, kind, step, L, batch):
|
| 873 |
+
"""Build a FlexAttention BlockMask for the packed sliding-window+sink layout."""
|
| 874 |
+
from torch.nn.attention.flex_attention import create_block_mask
|
| 875 |
+
from starVLA.model.modules.vlm.kv_memory import make_packed_sliding_mask_mod
|
| 876 |
+
|
| 877 |
+
mask_mod = make_packed_sliding_mask_mod(kind, step, self.kv_window)
|
| 878 |
+
# B=H=None → the mask is shared across batch and heads (it depends only on
|
| 879 |
+
# token kind/step, not content), so one BlockMask covers the whole batch.
|
| 880 |
+
return create_block_mask(mask_mod, B=None, H=None, Q_LEN=L, KV_LEN=L, device=str(kind.device))
|
| 881 |
+
|
| 882 |
+
def _assert_flex_attention(self):
|
| 883 |
+
"""Packed training needs the decoder dispatched through FlexAttention.
|
| 884 |
+
|
| 885 |
+
We pass a FlexAttention BlockMask straight to the model, so the decoder
|
| 886 |
+
(forward AND gradient-checkpointing recompute during backward) must use
|
| 887 |
+
the flex backend the whole time — a transient toggle around the forward
|
| 888 |
+
would leave the recompute on sdpa and choke on the BlockMask. The eval
|
| 889 |
+
streaming path also runs fine under flex, so the whole model is simply
|
| 890 |
+
loaded with `attn_implementation: flex_attention`.
|
| 891 |
+
"""
|
| 892 |
+
_, _, _, text_config = self._kv_components()
|
| 893 |
+
impl = getattr(text_config, "_attn_implementation", None)
|
| 894 |
+
if impl != "flex_attention":
|
| 895 |
+
raise RuntimeError(
|
| 896 |
+
"Packed KV-memory training requires the VLM loaded with "
|
| 897 |
+
f"attn_implementation='flex_attention' (got {impl!r}). Set "
|
| 898 |
+
"framework.qwenvl.attn_implementation: flex_attention."
|
| 899 |
+
)
|
| 900 |
+
|
| 901 |
+
def _forward_memory_packed(self, examples: List[dict]) -> dict:
|
| 902 |
+
"""Training forward via a single packed sliding-window+sink masked pass.
|
| 903 |
+
|
| 904 |
+
Per-frame supervision identical to `_forward_memory` (every real frame is
|
| 905 |
+
supervised at the window it naturally has), but the expensive VLM forward
|
| 906 |
+
runs ONCE per rollout instead of R times. Full BPTT across the rollout
|
| 907 |
+
(no per-step detach) — pair with gradient checkpointing for memory.
|
| 908 |
+
"""
|
| 909 |
+
self._assert_flex_attention()
|
| 910 |
+
profile = self._profile_timing_enabled()
|
| 911 |
+
timing = {"timing/mem_nograd_fwd": 0.0, "timing/mem_grad_fwd": 0.0, "timing/mem_action_head": 0.0}
|
| 912 |
+
|
| 913 |
+
instructions = [
|
| 914 |
+
self._instruction_with_state(ex["lang"], ex.get("state") if "state" in ex else None)
|
| 915 |
+
for ex in examples
|
| 916 |
+
]
|
| 917 |
+
groups: dict = {}
|
| 918 |
+
for idx, instruction in enumerate(instructions):
|
| 919 |
+
valid = np.asarray(examples[idx]["valid"], dtype=bool)
|
| 920 |
+
groups.setdefault((instruction, tuple(valid.tolist())), []).append(idx)
|
| 921 |
+
|
| 922 |
+
total_weighted_loss = None
|
| 923 |
+
total_weight = 0.0
|
| 924 |
+
for (instruction, valid_key), indices in groups.items():
|
| 925 |
+
valid = np.asarray(valid_key, dtype=bool)
|
| 926 |
+
real_positions = [k for k in range(len(valid)) if valid[k]]
|
| 927 |
+
R = len(real_positions)
|
| 928 |
+
if R == 0:
|
| 929 |
+
continue
|
| 930 |
+
action_env_dims = [int(examples[i].get("action_env_dim", self.action_env_dim)) for i in indices]
|
| 931 |
+
rl_games_tasks = [str(examples[i].get("rl_games_task", "")) for i in indices]
|
| 932 |
+
if not any(rl_games_tasks):
|
| 933 |
+
rl_games_tasks = None
|
| 934 |
+
|
| 935 |
+
# Build per-sample packed layouts (identical token structure, distinct pixels).
|
| 936 |
+
layouts = [
|
| 937 |
+
self._packed_window_layout([examples[i]["image"][k] for k in real_positions], instruction)
|
| 938 |
+
for i in indices
|
| 939 |
+
]
|
| 940 |
+
base = layouts[0]
|
| 941 |
+
L = base["input_ids"].shape[1]
|
| 942 |
+
B = len(indices)
|
| 943 |
+
input_ids = base["input_ids"].expand(B, -1).contiguous()
|
| 944 |
+
position_ids = base["position_ids"].expand(3, B, -1).contiguous()
|
| 945 |
+
pixel_values = torch.cat([lay["pixel_values"] for lay in layouts], dim=0)
|
| 946 |
+
grid = torch.cat([lay["image_grid_thw"] for lay in layouts], dim=0) # [B*R, 3]
|
| 947 |
+
action_idx = base["action_idx"] # [R, chunk_len]
|
| 948 |
+
block_mask = self._packed_block_mask(base["kind"], base["step"], L, B)
|
| 949 |
+
|
| 950 |
+
t_fwd = self._profile_start() if profile else None
|
| 951 |
+
last_hidden = self._forward_qwen_last_hidden(
|
| 952 |
+
{
|
| 953 |
+
"input_ids": input_ids,
|
| 954 |
+
"position_ids": position_ids,
|
| 955 |
+
"attention_mask": block_mask,
|
| 956 |
+
"pixel_values": pixel_values,
|
| 957 |
+
"image_grid_thw": grid,
|
| 958 |
+
"use_cache": False,
|
| 959 |
+
}
|
| 960 |
+
) # [B, L, H]
|
| 961 |
+
if profile:
|
| 962 |
+
timing["timing/mem_grad_fwd"] += self._profile_elapsed(t_fwd)
|
| 963 |
+
|
| 964 |
+
t_head = self._profile_start() if profile else None
|
| 965 |
+
with torch.autocast("cuda", dtype=torch.float32):
|
| 966 |
+
for j, k in enumerate(real_positions):
|
| 967 |
+
action_queries = last_hidden[:, action_idx[j], :] # [B, chunk_len, H]
|
| 968 |
+
group_preds = self.action_model.predict_action(action_queries) # [B, chunk, dim]
|
| 969 |
+
targets = [self._prepare_action_array(examples[i]["actions_per_frame"][k]) for i in indices]
|
| 970 |
+
targets = torch.tensor(np.array(targets), device=group_preds.device, dtype=group_preds.dtype)
|
| 971 |
+
targets = targets[:, -self.action_horizon :, :]
|
| 972 |
+
step_loss = self._compute_action_loss(
|
| 973 |
+
group_preds, targets, action_env_dims=action_env_dims, rl_games_tasks=rl_games_tasks
|
| 974 |
+
)
|
| 975 |
+
weight = float(sum(float(np.asarray(examples[i]["density_weight"])[k]) for i in indices))
|
| 976 |
+
contribution = step_loss * weight
|
| 977 |
+
total_weighted_loss = contribution if total_weighted_loss is None else total_weighted_loss + contribution
|
| 978 |
+
total_weight += weight
|
| 979 |
+
if profile:
|
| 980 |
+
timing["timing/mem_action_head"] += self._profile_elapsed(t_head)
|
| 981 |
+
|
| 982 |
+
action_loss = total_weighted_loss / max(total_weight, 1e-6)
|
| 983 |
+
return {"action_loss": action_loss, "loss_weight": total_weight, "timing": timing, "batch_stats": {}}
|
| 984 |
+
|
| 985 |
+
def _instruction_with_state(self, instruction, state):
|
| 986 |
+
if state is None:
|
| 987 |
+
return instruction
|
| 988 |
+
return self.add_discretized_state_to_instruction([instruction], [state])[0]
|
| 989 |
+
|
| 990 |
+
def _forward_memory(self, examples: List[dict]) -> dict:
|
| 991 |
+
"""Training forward with the streaming KV memory: per-frame supervision.
|
| 992 |
+
|
| 993 |
+
Each sample is an R-frame rollout `[t-R+1 .. t]`; the dataloader marks clamp
|
| 994 |
+
padding via ``valid`` and supplies one action chunk per frame in
|
| 995 |
+
``actions_per_frame`` ([R, H, dim]). We skip padding frames (never feed memory,
|
| 996 |
+
never supervise) and supervise EVERY real frame at the window it naturally has
|
| 997 |
+
(1 -> window, then evict), which matches eval (`_predict_action_memory`) frame
|
| 998 |
+
for frame, including the unfilled window at an episode's start. History KV is
|
| 999 |
+
detached between steps (truncated BPTT depth 1), so each step's loss flows only
|
| 1000 |
+
through that step's forward; the per-step weighted losses are summed and the
|
| 1001 |
+
trainer backpropagates once.
|
| 1002 |
+
|
| 1003 |
+
Same-instruction, same-`valid` rollouts share a token layout and window
|
| 1004 |
+
evolution, so they run each step in one batched forward via
|
| 1005 |
+
`_kv_forward_step_batched` (degrades to per-sample otherwise), mirroring
|
| 1006 |
+
`_predict_action_memory`.
|
| 1007 |
+
"""
|
| 1008 |
+
profile = self._profile_timing_enabled()
|
| 1009 |
+
timing = {"timing/mem_nograd_fwd": 0.0, "timing/mem_grad_fwd": 0.0, "timing/mem_action_head": 0.0}
|
| 1010 |
+
|
| 1011 |
+
instructions = [
|
| 1012 |
+
self._instruction_with_state(ex["lang"], ex.get("state") if "state" in ex else None)
|
| 1013 |
+
for ex in examples
|
| 1014 |
+
]
|
| 1015 |
+
|
| 1016 |
+
# Group rollouts that share the prompt AND the same real/padding pattern: those
|
| 1017 |
+
# march through identical window states each step, so they batch cleanly.
|
| 1018 |
+
groups: dict = {}
|
| 1019 |
+
for idx, instruction in enumerate(instructions):
|
| 1020 |
+
valid = np.asarray(examples[idx]["valid"], dtype=bool)
|
| 1021 |
+
groups.setdefault((instruction, tuple(valid.tolist())), []).append(idx)
|
| 1022 |
+
|
| 1023 |
+
total_weighted_loss = None
|
| 1024 |
+
total_weight = 0.0
|
| 1025 |
+
for (instruction, valid_key), indices in groups.items():
|
| 1026 |
+
valid = np.asarray(valid_key, dtype=bool)
|
| 1027 |
+
real_positions = [k for k in range(len(valid)) if valid[k]]
|
| 1028 |
+
memories = [self._new_kv_memory() for _ in indices]
|
| 1029 |
+
rollout = [examples[i]["image"] for i in indices]
|
| 1030 |
+
action_env_dims = [int(examples[i].get("action_env_dim", self.action_env_dim)) for i in indices]
|
| 1031 |
+
rl_games_tasks = [str(examples[i].get("rl_games_task", "")) for i in indices]
|
| 1032 |
+
if not any(rl_games_tasks):
|
| 1033 |
+
rl_games_tasks = None
|
| 1034 |
+
|
| 1035 |
+
for step_pos, k in enumerate(real_positions):
|
| 1036 |
+
if step_pos > 0:
|
| 1037 |
+
for memory in memories:
|
| 1038 |
+
memory.detach_() # truncated BPTT: history is a fixed memory
|
| 1039 |
+
step_frames = [frames[k] for frames in rollout]
|
| 1040 |
+
|
| 1041 |
+
t_fwd = self._profile_start() if profile else None
|
| 1042 |
+
last_hidden, new_ids = self._kv_forward_step_batched(memories, step_frames, instruction)
|
| 1043 |
+
if profile:
|
| 1044 |
+
timing["timing/mem_grad_fwd"] += self._profile_elapsed(t_fwd)
|
| 1045 |
+
|
| 1046 |
+
t_head = self._profile_start() if profile else None
|
| 1047 |
+
with torch.autocast("cuda", dtype=torch.float32):
|
| 1048 |
+
action_queries = self._gather_action_token_embeddings(
|
| 1049 |
+
last_hidden, new_ids, action_token_id=self.action_token_id
|
| 1050 |
+
)
|
| 1051 |
+
group_preds = self.action_model.predict_action(action_queries) # [Bg, chunk, dim]
|
| 1052 |
+
targets = [
|
| 1053 |
+
self._prepare_action_array(examples[i]["actions_per_frame"][k]) for i in indices
|
| 1054 |
+
]
|
| 1055 |
+
targets = torch.tensor(
|
| 1056 |
+
np.array(targets), device=group_preds.device, dtype=group_preds.dtype
|
| 1057 |
+
) # [Bg, H, dim]
|
| 1058 |
+
targets = targets[:, -self.action_horizon :, :]
|
| 1059 |
+
step_loss = self._compute_action_loss(
|
| 1060 |
+
group_preds,
|
| 1061 |
+
targets,
|
| 1062 |
+
action_env_dims=action_env_dims,
|
| 1063 |
+
rl_games_tasks=rl_games_tasks,
|
| 1064 |
+
)
|
| 1065 |
+
if profile:
|
| 1066 |
+
timing["timing/mem_action_head"] += self._profile_elapsed(t_head)
|
| 1067 |
+
|
| 1068 |
+
# Per-supervised-step density weight: sum of the group's per-frame
|
| 1069 |
+
# weights at frame k. Uniform weights reduce this to a sample-count
|
| 1070 |
+
# weighting, i.e. the exact mean over all supervised (sample, step).
|
| 1071 |
+
weight = float(
|
| 1072 |
+
sum(float(np.asarray(examples[i]["density_weight"])[k]) for i in indices)
|
| 1073 |
+
)
|
| 1074 |
+
contribution = step_loss * weight
|
| 1075 |
+
total_weighted_loss = contribution if total_weighted_loss is None else total_weighted_loss + contribution
|
| 1076 |
+
total_weight += weight
|
| 1077 |
+
|
| 1078 |
+
action_loss = total_weighted_loss / max(total_weight, 1e-6)
|
| 1079 |
+
return {"action_loss": action_loss, "loss_weight": total_weight, "timing": timing, "batch_stats": {}}
|
| 1080 |
+
|
| 1081 |
+
@torch.inference_mode()
|
| 1082 |
+
def _predict_action_memory(self, examples: List[dict]) -> dict:
|
| 1083 |
+
"""Eval-time streaming prediction with per-slot KV memory (req3, milestone 4).
|
| 1084 |
+
|
| 1085 |
+
The newest frame (``example['image'][-1]``) is encoded fresh; past frames are
|
| 1086 |
+
served from the slot's memory. ``reset_memory(slot_id)`` (called from the
|
| 1087 |
+
policy at episode boundaries) clears a slot. Streams that currently share the
|
| 1088 |
+
same instruction and history layout are processed in one batched forward; the
|
| 1089 |
+
rest fall back to their own group (no left padding needed).
|
| 1090 |
+
"""
|
| 1091 |
+
train_obs_image_size = getattr(self.config.datasets.vla_data, "obs_image_size", None)
|
| 1092 |
+
|
| 1093 |
+
# Resolve each example to its (slot memory, current frame, instruction).
|
| 1094 |
+
entries = []
|
| 1095 |
+
for idx, example in enumerate(examples):
|
| 1096 |
+
slot_id = example.get("slot_id", idx)
|
| 1097 |
+
frames = [to_pil_preserve(f) for f in example["image"]]
|
| 1098 |
+
if train_obs_image_size:
|
| 1099 |
+
frames = resize_images(frames, target_size=train_obs_image_size)
|
| 1100 |
+
state = example.get("state") if "state" in example else None
|
| 1101 |
+
instruction = self._instruction_with_state(example["lang"], state)
|
| 1102 |
+
memory = self._kv_memories.get(slot_id)
|
| 1103 |
+
if memory is None:
|
| 1104 |
+
memory = self._new_kv_memory()
|
| 1105 |
+
self._kv_memories[slot_id] = memory
|
| 1106 |
+
entries.append({"idx": idx, "frame": frames[-1], "instruction": instruction, "memory": memory})
|
| 1107 |
+
|
| 1108 |
+
# Group same-layout streams (same prompt + same cached layout) into one forward.
|
| 1109 |
+
groups: dict = {}
|
| 1110 |
+
for entry in entries:
|
| 1111 |
+
key = (entry["instruction"], entry["memory"].layout_key())
|
| 1112 |
+
groups.setdefault(key, []).append(entry)
|
| 1113 |
+
|
| 1114 |
+
preds: List[Optional[torch.Tensor]] = [None] * len(examples)
|
| 1115 |
+
for group in groups.values():
|
| 1116 |
+
instruction = group[0]["instruction"]
|
| 1117 |
+
memories = [entry["memory"] for entry in group]
|
| 1118 |
+
frames = [entry["frame"] for entry in group]
|
| 1119 |
+
last_hidden, new_ids = self._kv_forward_step_batched(memories, frames, instruction)
|
| 1120 |
+
with torch.autocast("cuda", dtype=torch.float32):
|
| 1121 |
+
action_queries = self._gather_action_token_embeddings(
|
| 1122 |
+
last_hidden, new_ids, action_token_id=self.action_token_id
|
| 1123 |
+
)
|
| 1124 |
+
pred_actions = self.action_model.predict_action(action_queries)
|
| 1125 |
+
for i, entry in enumerate(group):
|
| 1126 |
+
preds[entry["idx"]] = pred_actions[i : i + 1]
|
| 1127 |
+
|
| 1128 |
+
normalized = torch.cat(preds, dim=0).detach().cpu().numpy()
|
| 1129 |
+
return {"normalized_actions": normalized}
|
| 1130 |
+
|
| 1131 |
+
def _gather_action_token_embeddings(
|
| 1132 |
+
self,
|
| 1133 |
+
last_hidden: torch.Tensor, # [B, L, H]
|
| 1134 |
+
input_ids: torch.Tensor, # [B, L]
|
| 1135 |
+
action_token_id=None, # Can be int or List[int]
|
| 1136 |
+
) -> torch.Tensor:
|
| 1137 |
+
"""
|
| 1138 |
+
Vectorized batch extraction of action token embeddings:
|
| 1139 |
+
- No per-sample for loop
|
| 1140 |
+
- Select the last chunk_len action placeholder tokens from each sample
|
| 1141 |
+
Args:
|
| 1142 |
+
last_hidden: [B, L, H]
|
| 1143 |
+
input_ids: [B, L]
|
| 1144 |
+
action_token_id: int or List[int]
|
| 1145 |
+
Returns:
|
| 1146 |
+
action_queries: [B, chunk_len, H]
|
| 1147 |
+
"""
|
| 1148 |
+
if action_token_id is None:
|
| 1149 |
+
raise ValueError("action_token_id must not be None")
|
| 1150 |
+
|
| 1151 |
+
device = input_ids.device
|
| 1152 |
+
B, L, H = last_hidden.shape
|
| 1153 |
+
|
| 1154 |
+
# Support multiple ids (e.g., multiple variants)
|
| 1155 |
+
if isinstance(action_token_id, (list, tuple, set)):
|
| 1156 |
+
id_list = torch.tensor(list(action_token_id), device=device, dtype=input_ids.dtype)
|
| 1157 |
+
# torch.isin requires PyTorch >=1.10
|
| 1158 |
+
mask = torch.isin(input_ids, id_list)
|
| 1159 |
+
else:
|
| 1160 |
+
mask = input_ids == action_token_id # [B, L]
|
| 1161 |
+
|
| 1162 |
+
counts = mask.sum(dim=1) # [B]
|
| 1163 |
+
if (counts < self.chunk_len).any():
|
| 1164 |
+
insufficient = (counts < self.chunk_len).nonzero(as_tuple=False).flatten().tolist()
|
| 1165 |
+
raise RuntimeError(
|
| 1166 |
+
f"The following samples have insufficient action tokens (< {self.chunk_len}): {insufficient} |"
|
| 1167 |
+
f" counts={counts.tolist()}"
|
| 1168 |
+
)
|
| 1169 |
+
|
| 1170 |
+
# Position indices
|
| 1171 |
+
idx = torch.arange(L, device=device).unsqueeze(0).expand(B, L) # [B, L]
|
| 1172 |
+
masked_pos = torch.where(mask, idx, torch.full_like(idx, -1)) # Set non-action positions to -1
|
| 1173 |
+
|
| 1174 |
+
# Take the last chunk_len positions (higher indices = later in sequence)
|
| 1175 |
+
# Note: count sufficiency already verified, so -1 won't be incorrectly selected
|
| 1176 |
+
topk_pos = masked_pos.topk(k=self.chunk_len, dim=-1).values # [B, chunk_len] unsorted
|
| 1177 |
+
# Sort in temporal order
|
| 1178 |
+
selected_pos = topk_pos.sort(dim=-1).values # [B, chunk_len]
|
| 1179 |
+
|
| 1180 |
+
# Gather
|
| 1181 |
+
expanded_index = selected_pos.unsqueeze(-1).expand(-1, -1, H) # [B, chunk_len, H]
|
| 1182 |
+
action_queries = last_hidden.gather(dim=1, index=expanded_index) # [B, chunk_len, H]
|
| 1183 |
+
return action_queries
|
| 1184 |
+
|
| 1185 |
+
# Discretised state → instruction prefix (π₀.5 style); shared with QwenPI_v3.
|
| 1186 |
+
add_discretized_state_to_instruction = staticmethod(add_discretized_state_to_instruction)
|
| 1187 |
+
|
| 1188 |
+
|
| 1189 |
+
if __name__ == "__main__":
|
| 1190 |
+
import argparse
|
| 1191 |
+
import os
|
| 1192 |
+
|
| 1193 |
+
from omegaconf import OmegaConf
|
| 1194 |
+
|
| 1195 |
+
parser = argparse.ArgumentParser()
|
| 1196 |
+
parser.add_argument(
|
| 1197 |
+
"--config_yaml",
|
| 1198 |
+
type=str,
|
| 1199 |
+
default="examples/LIBERO/train_files/starvla_cotrain_libero.yaml",
|
| 1200 |
+
help="Path to YAML config",
|
| 1201 |
+
)
|
| 1202 |
+
args, clipargs = parser.parse_known_args()
|
| 1203 |
+
|
| 1204 |
+
if os.getenv("DEBUGPY_ENABLE", "0") == "1":
|
| 1205 |
+
import debugpy
|
| 1206 |
+
|
| 1207 |
+
debugpy.listen(("0.0.0.0", 10092))
|
| 1208 |
+
print("Rank 0 waiting for debugger attach on port 10092...")
|
| 1209 |
+
debugpy.wait_for_client()
|
| 1210 |
+
|
| 1211 |
+
cfg = OmegaConf.load(args.config_yaml)
|
| 1212 |
+
|
| 1213 |
+
model = Qwenvl_OFT(cfg)
|
| 1214 |
+
print(model)
|
| 1215 |
+
|
| 1216 |
+
image = Image.fromarray(np.random.randint(0, 255, (224, 224, 3), dtype=np.uint8))
|
| 1217 |
+
sample = {
|
| 1218 |
+
"action": np.random.uniform(-1, 1, size=(16, 7)).astype(np.float16),
|
| 1219 |
+
"image": [image],
|
| 1220 |
+
"lang": "This is a fake instruction for testing.",
|
| 1221 |
+
"state": np.random.uniform(-1, 1, size=(1, 7)).astype(np.float16), # chunk, state_dim
|
| 1222 |
+
}
|
| 1223 |
+
sample2 = sample.copy()
|
| 1224 |
+
sample2["lang"] = "Another fake instruction for testing."
|
| 1225 |
+
|
| 1226 |
+
batch = [sample, sample2]
|
| 1227 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 1228 |
+
model = model.to(device)
|
| 1229 |
+
forward_output = model(batch)
|
| 1230 |
+
action_loss = forward_output["action_loss"]
|
| 1231 |
+
print(f"[train] Action Loss (with state): {action_loss.item()}")
|
| 1232 |
+
|
| 1233 |
+
predict_output = model.predict_action(examples=[batch[0]])
|
| 1234 |
+
normalized_actions = predict_output["normalized_actions"]
|
| 1235 |
+
print(f"[infer] Predicted Action shape: {normalized_actions.shape}")
|
| 1236 |
+
|
| 1237 |
+
# Backward-compat: examples without `state` should still work.
|
| 1238 |
+
sample_no_state = {k: v for k, v in sample.items() if k != "state"}
|
| 1239 |
+
forward_no_state = model([sample_no_state, sample_no_state])
|
| 1240 |
+
print(f"[train] Action Loss (no state): {forward_no_state['action_loss'].item()}")
|
| 1241 |
+
predict_no_state = model.predict_action(examples=[sample_no_state])
|
| 1242 |
+
print(f"[infer] Predicted Action shape (no state): {predict_no_state['normalized_actions'].shape}")
|
| 1243 |
+
|
| 1244 |
+
print("Finished")
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/datasets.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/doom-mean-reset.patch
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
diff --git a/latency_bench/data/sf_teacher_rollout.py b/latency_bench/data/sf_teacher_rollout.py
|
| 2 |
+
index ef45d95b..e32b942e 100644
|
| 3 |
+
--- a/latency_bench/data/sf_teacher_rollout.py
|
| 4 |
+
+++ b/latency_bench/data/sf_teacher_rollout.py
|
| 5 |
+
@@ -964,6 +964,11 @@ def _replay_accepted_specs(
|
| 6 |
+
row_buffer_size=int(row_buffer_size),
|
| 7 |
+
export_image_size=export_image_size,
|
| 8 |
+
)
|
| 9 |
+
+ stream_current_images = (
|
| 10 |
+
+ image_transform_config.normalized_transform() == "raw_rgb"
|
| 11 |
+
+ and int(context_window) <= 1
|
| 12 |
+
+ and filter_preset == "none"
|
| 13 |
+
+ )
|
| 14 |
+
image_resolution: list[int] | None = None
|
| 15 |
+
|
| 16 |
+
def assign_specs_to_slots(slots: list[int]) -> dict[int, int]:
|
| 17 |
+
@@ -1051,7 +1056,10 @@ def _replay_accepted_specs(
|
| 18 |
+
next_raw_frame=next_decision_step * obs_stride_raw_frames,
|
| 19 |
+
max_steps_per_episode=int(max_steps_per_episode),
|
| 20 |
+
)
|
| 21 |
+
- active_episode_frames[slot].append(np.array(image_frame, copy=True))
|
| 22 |
+
+ if stream_current_images:
|
| 23 |
+
+ image_writer.write(image_frame, image_path)
|
| 24 |
+
+ else:
|
| 25 |
+
+ active_episode_frames[slot].append(np.array(image_frame, copy=True))
|
| 26 |
+
row = teacher_dataset_row(
|
| 27 |
+
env_name=env_name,
|
| 28 |
+
spec=spec,
|
| 29 |
+
@@ -1104,7 +1112,9 @@ def _replay_accepted_specs(
|
| 30 |
+
)
|
| 31 |
+
else:
|
| 32 |
+
shard_rows = [dict(row) for row in final_rows]
|
| 33 |
+
- if _is_demon_ghost_trail:
|
| 34 |
+
+ if stream_current_images:
|
| 35 |
+
+ image_writer.flush()
|
| 36 |
+
+ elif _is_demon_ghost_trail:
|
| 37 |
+
# Frames are pre-built ghost trail images; map directly.
|
| 38 |
+
ep_steps = [int(row["decision_step"]) for row in final_rows]
|
| 39 |
+
frames_by_decision_step = {
|
| 40 |
+
@@ -1118,12 +1128,13 @@ def _replay_accepted_specs(
|
| 41 |
+
frames=active_episode_frames[slot],
|
| 42 |
+
config=image_transform_config,
|
| 43 |
+
)
|
| 44 |
+
- shard_rows = _write_filtered_episode_images(
|
| 45 |
+
- image_writer,
|
| 46 |
+
- shard_rows,
|
| 47 |
+
- frames_by_decision_step,
|
| 48 |
+
- context_window=int(context_window),
|
| 49 |
+
- )
|
| 50 |
+
+ if not stream_current_images:
|
| 51 |
+
+ shard_rows = _write_filtered_episode_images(
|
| 52 |
+
+ image_writer,
|
| 53 |
+
+ shard_rows,
|
| 54 |
+
+ frames_by_decision_step,
|
| 55 |
+
+ context_window=int(context_window),
|
| 56 |
+
+ )
|
| 57 |
+
write_episode_row_shard(output_dir, shard_rows)
|
| 58 |
+
progress.update(1)
|
| 59 |
+
replayed += 1
|
| 60 |
+
@@ -1272,9 +1283,8 @@ def _build_rollout_driver_runtime(
|
| 61 |
+
driver = BatchedTeacherRolloutDriver(runtime["envs"][0], int(num_envs))
|
| 62 |
+
else:
|
| 63 |
+
recreate_env = None
|
| 64 |
+
- if integration_name == "deadly_corridor" and (
|
| 65 |
+
- "export_zero_action_history" in config or getattr(cfg, "rollout_profile_seed_by_reset", False)
|
| 66 |
+
- ):
|
| 67 |
+
+ # ViZDoom applies reset seeds only while creating its native game.
|
| 68 |
+
+ if integration_name == "deadly_corridor":
|
| 69 |
+
from sample_factory.algo.utils.make_env import make_env_func_batched
|
| 70 |
+
from sample_factory.utils.attr_dict import AttrDict
|
| 71 |
+
|
| 72 |
+
diff --git a/tests/data/test_sf_teacher_dataset_export.py b/tests/data/test_sf_teacher_dataset_export.py
|
| 73 |
+
index e6b25233..61a58118 100644
|
| 74 |
+
--- a/tests/data/test_sf_teacher_dataset_export.py
|
| 75 |
+
+++ b/tests/data/test_sf_teacher_dataset_export.py
|
| 76 |
+
@@ -498,8 +498,11 @@ def test_serial_rollout_driver_continues_life_loss_with_seedless_reset(monkeypat
|
| 77 |
+
assert info == {"reset_seed": None}
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
-def test_serial_teacher_replay_starts_each_seed_in_a_fresh_environment(monkeypatch):
|
| 81 |
+
- sf_teacher_drivers = _load_sf_teacher_drivers_for_direct_test(monkeypatch)
|
| 82 |
+
+@pytest.mark.parametrize("latency_type", ["fixed", "profile_sample"])
|
| 83 |
+
+def test_serial_teacher_replay_starts_each_seed_in_a_fresh_environment(monkeypatch, tmp_path, latency_type):
|
| 84 |
+
+ from contextlib import nullcontext
|
| 85 |
+
+ from latency_bench.data import sf_teacher_rollout
|
| 86 |
+
+ from sample_factory.algo.utils import make_env
|
| 87 |
+
|
| 88 |
+
class StatefulEnv:
|
| 89 |
+
action_space = object()
|
| 90 |
+
@@ -514,13 +517,17 @@ def test_serial_teacher_replay_starts_each_seed_in_a_fresh_environment(monkeypat
|
| 91 |
+
def close(self):
|
| 92 |
+
return None
|
| 93 |
+
|
| 94 |
+
- driver = sf_teacher_drivers.SerialTeacherRolloutDriver(
|
| 95 |
+
- [StatefulEnv()], recreate_env=lambda _slot: StatefulEnv()
|
| 96 |
+
+ cfg = SimpleNamespace(frame_stack=4, action_history_decisions=8, latency_type=latency_type)
|
| 97 |
+
+ monkeypatch.setattr(sf_teacher_rollout, "sample_factory_init_log", lambda _: nullcontext())
|
| 98 |
+
+ monkeypatch.setattr(sf_teacher_rollout, "build_sample_factory_rollout_cfg", lambda _: (object(), cfg))
|
| 99 |
+
+ monkeypatch.setattr(sf_teacher_rollout, "build_env_and_policy", lambda *args: {"envs": [StatefulEnv()]})
|
| 100 |
+
+ monkeypatch.setattr(make_env, "make_env_func_batched", lambda *args, **kwargs: StatefulEnv())
|
| 101 |
+
+ _, _, driver, _ = sf_teacher_rollout._build_rollout_driver_runtime(
|
| 102 |
+
+ {"env_name": "deadly_corridor", "integration_name": "deadly_corridor"},
|
| 103 |
+
+ output_dir=tmp_path, num_envs=1, export_env_raw_rgb_frames=False,
|
| 104 |
+
)
|
| 105 |
+
-
|
| 106 |
+
driver.reset_slots({0: 42})
|
| 107 |
+
observation, _ = driver.reset_slots({0: 43})[0]
|
| 108 |
+
-
|
| 109 |
+
assert observation["obs"].tolist() == [43, 1]
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
@@ -670,19 +677,22 @@ def test_collect_teacher_dataset_probe_retries_until_episode_is_accepted(monkeyp
|
| 113 |
+
def action_distribution(self):
|
| 114 |
+
return object()
|
| 115 |
+
|
| 116 |
+
+ reset_seeds = []
|
| 117 |
+
+
|
| 118 |
+
class FakeEnv:
|
| 119 |
+
def __init__(self):
|
| 120 |
+
self.action_space = object()
|
| 121 |
+
- self.reset_seeds: list[int] = []
|
| 122 |
+
+ self.reset_seeds = reset_seeds
|
| 123 |
+
|
| 124 |
+
def reset(self, seed=None):
|
| 125 |
+
self.reset_seeds.append(seed)
|
| 126 |
+
+ self.current_seed = seed
|
| 127 |
+
return np.zeros((3, 2, 2), dtype=np.uint8), [
|
| 128 |
+
{"env_raw_rgb_frame_stack": _env_raw_rgb_frame_stack(5)}
|
| 129 |
+
]
|
| 130 |
+
|
| 131 |
+
def step(self, action):
|
| 132 |
+
- reward = 3000.0 if len(self.reset_seeds) == 1 else 3000.001
|
| 133 |
+
+ reward = 3000.0 if self.current_seed == 11 else 3000.001
|
| 134 |
+
return (
|
| 135 |
+
np.zeros((3, 2, 2), dtype=np.uint8),
|
| 136 |
+
np.float32(reward),
|
| 137 |
+
@@ -702,6 +712,8 @@ def test_collect_teacher_dataset_probe_retries_until_episode_is_accepted(monkeyp
|
| 138 |
+
|
| 139 |
+
fake_env = FakeEnv()
|
| 140 |
+
fake_actor = FakeActorCritic()
|
| 141 |
+
+ from sample_factory.algo.utils import make_env
|
| 142 |
+
+ monkeypatch.setattr(make_env, "make_env_func_batched", lambda *args, **kwargs: FakeEnv())
|
| 143 |
+
fake_cfg = SimpleNamespace(frame_stack=1, env_fps=35.0, obs_fps=17.5, seed=11)
|
| 144 |
+
|
| 145 |
+
monkeypatch.setattr(exporter, "build_sample_factory_rollout_cfg", lambda config: (object(), fake_cfg))
|
| 146 |
+
@@ -1273,7 +1285,8 @@ def test_collect_teacher_dataset_saves_demon_attack_latest_raw_rgb_image(monkeyp
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
@pytest.mark.parametrize("export_history", [False, True])
|
| 150 |
+
-def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch, tmp_path, export_history):
|
| 151 |
+
+@pytest.mark.parametrize("filter_preset", ["clean_v1", "none"])
|
| 152 |
+
+def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch, tmp_path, export_history, filter_preset):
|
| 153 |
+
import torch
|
| 154 |
+
from gymnasium.spaces import Discrete
|
| 155 |
+
from PIL import Image
|
| 156 |
+
@@ -1307,6 +1320,9 @@ def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch,
|
| 157 |
+
class FakeEnv:
|
| 158 |
+
action_space = Discrete(2)
|
| 159 |
+
|
| 160 |
+
+ def __init__(self, exporting):
|
| 161 |
+
+ self.exporting = exporting
|
| 162 |
+
+
|
| 163 |
+
def reset(self, seed=None):
|
| 164 |
+
self.steps = 0
|
| 165 |
+
obs = {"obs": np.zeros((1, 12, 2, 2), dtype=np.uint8)}
|
| 166 |
+
@@ -1323,6 +1339,9 @@ def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch,
|
| 167 |
+
|
| 168 |
+
def step(self, action):
|
| 169 |
+
self.steps += 1
|
| 170 |
+
+ if self.exporting and self.steps == 2 and filter_preset == "none":
|
| 171 |
+
+ image_path = output_dir / "images/train/episode_000000/decision_step_000000.png"
|
| 172 |
+
+ assert image_path.is_file(), "First frame must be on disk before the episode ends"
|
| 173 |
+
obs = {"obs": np.ones((1, 12, 2, 2), dtype=np.uint8)}
|
| 174 |
+
next_stack = np.stack([np.full((6, 5, 3), 50 + idx, dtype=np.uint8) for idx in range(4)], axis=0)
|
| 175 |
+
return (
|
| 176 |
+
@@ -1354,7 +1373,7 @@ def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch,
|
| 177 |
+
exporter,
|
| 178 |
+
"build_env_and_policy",
|
| 179 |
+
lambda module, cfg, config, num_envs: {
|
| 180 |
+
- "envs": [FakeEnv()],
|
| 181 |
+
+ "envs": [FakeEnv(cfg.export_env_raw_rgb_frames)],
|
| 182 |
+
"env_info": object(),
|
| 183 |
+
"actor_critic": FakeActorCritic(),
|
| 184 |
+
"device": torch.device("cpu"),
|
| 185 |
+
@@ -1382,6 +1401,7 @@ def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch,
|
| 186 |
+
export_image_size=None,
|
| 187 |
+
image_writer_workers=0,
|
| 188 |
+
row_buffer_size=1,
|
| 189 |
+
+ filter_preset=filter_preset,
|
| 190 |
+
)
|
| 191 |
+
train_rows = _read_shard_rows(output_dir, "train")
|
| 192 |
+
val_rows = _read_shard_rows(output_dir, "val")
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/gym-language-contract.patch
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
diff --git a/examples/rl_games/train_files/data_registry/data_config.py b/examples/rl_games/train_files/data_registry/data_config.py
|
| 2 |
+
index 2ef8b39..6d76aeb 100644
|
| 3 |
+
--- a/examples/rl_games/train_files/data_registry/data_config.py
|
| 4 |
+
+++ b/examples/rl_games/train_files/data_registry/data_config.py
|
| 5 |
+
@@ -53,10 +53,10 @@ class AtlantisDataConfig(FlappyDataConfig):
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class GymnasiumDataConfig(FlappyDataConfig):
|
| 9 |
+
- pass
|
| 10 |
+
+ language_keys = ["annotation.human.task_description"]
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
-class GymnasiumNativeDataConfig(FlappyDataConfig):
|
| 14 |
+
+class GymnasiumNativeDataConfig(GymnasiumDataConfig):
|
| 15 |
+
"""Configure native Gymnasium state and action vectors."""
|
| 16 |
+
|
| 17 |
+
state_keys = ["state.native"]
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/loader-spawn-cache.patch
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
diff --git a/starVLA/dataloader/gr00t_lerobot/datasets.py b/starVLA/dataloader/gr00t_lerobot/datasets.py
|
| 2 |
+
index 17ce5f1..2346230 100644
|
| 3 |
+
--- a/starVLA/dataloader/gr00t_lerobot/datasets.py
|
| 4 |
+
+++ b/starVLA/dataloader/gr00t_lerobot/datasets.py
|
| 5 |
+
@@ -667,6 +667,13 @@ class LeRobotSingleDataset(Dataset):
|
| 6 |
+
# Check if the dataset is valid
|
| 7 |
+
self._check_integrity()
|
| 8 |
+
|
| 9 |
+
+ def __getstate__(self):
|
| 10 |
+
+ state = self.__dict__.copy()
|
| 11 |
+
+ state["_parquet_cache"] = {}
|
| 12 |
+
+ state["curr_traj_data"] = None
|
| 13 |
+
+ state["curr_traj_id"] = None
|
| 14 |
+
+ return state
|
| 15 |
+
+
|
| 16 |
+
@property
|
| 17 |
+
def dataset_path(self) -> Path:
|
| 18 |
+
"""The path to the dataset that contains the METADATA_FILENAME file."""
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/loader-spawn-test.patch
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
diff --git a/tests/data/test_starvla_tasks.py b/tests/data/test_starvla_tasks.py
|
| 2 |
+
index 1403fa13..62468aa6 100644
|
| 3 |
+
--- a/tests/data/test_starvla_tasks.py
|
| 4 |
+
+++ b/tests/data/test_starvla_tasks.py
|
| 5 |
+
@@ -395,3 +395,26 @@ def test_balance_masked_l1_uses_equal_sample_weights_and_no_padding_gradient(mon
|
| 6 |
+
all_valid = torch.ones((2, 8), dtype=torch.bool)
|
| 7 |
+
torch.testing.assert_close(model._compute_action_loss(pred, target, action_loss_mask=all_valid),
|
| 8 |
+
(pred - target).abs().mean())
|
| 9 |
+
+
|
| 10 |
+
+
|
| 11 |
+
+def test_lerobot_spawn_reload_preserves_data_without_copying_parent_cache(tmp_path, monkeypatch):
|
| 12 |
+
+ import pickle
|
| 13 |
+
+ pytest.importorskip("accelerate")
|
| 14 |
+
+ monkeypatch.syspath_prepend(str(Path(__file__).resolve().parents[2] / "third_party/starVLA"))
|
| 15 |
+
+ from starVLA.dataloader.gr00t_lerobot.datasets import LeRobotSingleDataset
|
| 16 |
+
+
|
| 17 |
+
+ path = tmp_path / "episode.parquet"
|
| 18 |
+
+ table = pd.DataFrame({"image": [b"rgb" * (1024 * 1024)], "action": [1]})
|
| 19 |
+
+ table.to_parquet(path)
|
| 20 |
+
+ parent = object.__new__(LeRobotSingleDataset)
|
| 21 |
+
+ parent._parquet_cache = {path: table}
|
| 22 |
+
+ parent.curr_traj_data = table
|
| 23 |
+
+ parent.curr_traj_id = 0
|
| 24 |
+
+ parent._all_steps = [(0, 0)]
|
| 25 |
+
+
|
| 26 |
+
+ payload = pickle.dumps(parent)
|
| 27 |
+
+ assert len(payload) < 64 * 1024
|
| 28 |
+
+ worker = pickle.loads(payload)
|
| 29 |
+
+ assert worker._all_steps == parent._all_steps
|
| 30 |
+
+ pd.testing.assert_frame_equal(worker._read_trajectory_parquet(path), table)
|
| 31 |
+
+ pd.testing.assert_frame_equal(parent.curr_traj_data, table)
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/mean-profile-preparation.patch
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
diff --git a/latency_bench/data/sf_teacher_rollout.py b/latency_bench/data/sf_teacher_rollout.py
|
| 2 |
+
index ef45d95b..af0a9fa9 100644
|
| 3 |
+
--- a/latency_bench/data/sf_teacher_rollout.py
|
| 4 |
+
+++ b/latency_bench/data/sf_teacher_rollout.py
|
| 5 |
+
@@ -964,6 +964,11 @@ def _replay_accepted_specs(
|
| 6 |
+
row_buffer_size=int(row_buffer_size),
|
| 7 |
+
export_image_size=export_image_size,
|
| 8 |
+
)
|
| 9 |
+
+ stream_current_images = (
|
| 10 |
+
+ image_transform_config.normalized_transform() == "raw_rgb"
|
| 11 |
+
+ and int(context_window) <= 1
|
| 12 |
+
+ and filter_preset == "none"
|
| 13 |
+
+ )
|
| 14 |
+
image_resolution: list[int] | None = None
|
| 15 |
+
|
| 16 |
+
def assign_specs_to_slots(slots: list[int]) -> dict[int, int]:
|
| 17 |
+
@@ -1051,7 +1056,10 @@ def _replay_accepted_specs(
|
| 18 |
+
next_raw_frame=next_decision_step * obs_stride_raw_frames,
|
| 19 |
+
max_steps_per_episode=int(max_steps_per_episode),
|
| 20 |
+
)
|
| 21 |
+
- active_episode_frames[slot].append(np.array(image_frame, copy=True))
|
| 22 |
+
+ if stream_current_images:
|
| 23 |
+
+ image_writer.write(image_frame, image_path)
|
| 24 |
+
+ else:
|
| 25 |
+
+ active_episode_frames[slot].append(np.array(image_frame, copy=True))
|
| 26 |
+
row = teacher_dataset_row(
|
| 27 |
+
env_name=env_name,
|
| 28 |
+
spec=spec,
|
| 29 |
+
@@ -1104,7 +1112,9 @@ def _replay_accepted_specs(
|
| 30 |
+
)
|
| 31 |
+
else:
|
| 32 |
+
shard_rows = [dict(row) for row in final_rows]
|
| 33 |
+
- if _is_demon_ghost_trail:
|
| 34 |
+
+ if stream_current_images:
|
| 35 |
+
+ image_writer.flush()
|
| 36 |
+
+ elif _is_demon_ghost_trail:
|
| 37 |
+
# Frames are pre-built ghost trail images; map directly.
|
| 38 |
+
ep_steps = [int(row["decision_step"]) for row in final_rows]
|
| 39 |
+
frames_by_decision_step = {
|
| 40 |
+
@@ -1118,12 +1128,13 @@ def _replay_accepted_specs(
|
| 41 |
+
frames=active_episode_frames[slot],
|
| 42 |
+
config=image_transform_config,
|
| 43 |
+
)
|
| 44 |
+
- shard_rows = _write_filtered_episode_images(
|
| 45 |
+
- image_writer,
|
| 46 |
+
- shard_rows,
|
| 47 |
+
- frames_by_decision_step,
|
| 48 |
+
- context_window=int(context_window),
|
| 49 |
+
- )
|
| 50 |
+
+ if not stream_current_images:
|
| 51 |
+
+ shard_rows = _write_filtered_episode_images(
|
| 52 |
+
+ image_writer,
|
| 53 |
+
+ shard_rows,
|
| 54 |
+
+ frames_by_decision_step,
|
| 55 |
+
+ context_window=int(context_window),
|
| 56 |
+
+ )
|
| 57 |
+
write_episode_row_shard(output_dir, shard_rows)
|
| 58 |
+
progress.update(1)
|
| 59 |
+
replayed += 1
|
| 60 |
+
diff --git a/tests/data/test_sf_teacher_dataset_export.py b/tests/data/test_sf_teacher_dataset_export.py
|
| 61 |
+
index e6b25233..7d5537ac 100644
|
| 62 |
+
--- a/tests/data/test_sf_teacher_dataset_export.py
|
| 63 |
+
+++ b/tests/data/test_sf_teacher_dataset_export.py
|
| 64 |
+
@@ -1273,7 +1273,8 @@ def test_collect_teacher_dataset_saves_demon_attack_latest_raw_rgb_image(monkeyp
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
@pytest.mark.parametrize("export_history", [False, True])
|
| 68 |
+
-def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch, tmp_path, export_history):
|
| 69 |
+
+@pytest.mark.parametrize("filter_preset", ["clean_v1", "none"])
|
| 70 |
+
+def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch, tmp_path, export_history, filter_preset):
|
| 71 |
+
import torch
|
| 72 |
+
from gymnasium.spaces import Discrete
|
| 73 |
+
from PIL import Image
|
| 74 |
+
@@ -1307,6 +1308,9 @@ def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch,
|
| 75 |
+
class FakeEnv:
|
| 76 |
+
action_space = Discrete(2)
|
| 77 |
+
|
| 78 |
+
+ def __init__(self, exporting):
|
| 79 |
+
+ self.exporting = exporting
|
| 80 |
+
+
|
| 81 |
+
def reset(self, seed=None):
|
| 82 |
+
self.steps = 0
|
| 83 |
+
obs = {"obs": np.zeros((1, 12, 2, 2), dtype=np.uint8)}
|
| 84 |
+
@@ -1323,6 +1327,9 @@ def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch,
|
| 85 |
+
|
| 86 |
+
def step(self, action):
|
| 87 |
+
self.steps += 1
|
| 88 |
+
+ if self.exporting and self.steps == 2 and filter_preset == "none":
|
| 89 |
+
+ image_path = output_dir / "images/train/episode_000000/decision_step_000000.png"
|
| 90 |
+
+ assert image_path.is_file(), "First frame must be on disk before the episode ends"
|
| 91 |
+
obs = {"obs": np.ones((1, 12, 2, 2), dtype=np.uint8)}
|
| 92 |
+
next_stack = np.stack([np.full((6, 5, 3), 50 + idx, dtype=np.uint8) for idx in range(4)], axis=0)
|
| 93 |
+
return (
|
| 94 |
+
@@ -1354,7 +1361,7 @@ def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch,
|
| 95 |
+
exporter,
|
| 96 |
+
"build_env_and_policy",
|
| 97 |
+
lambda module, cfg, config, num_envs: {
|
| 98 |
+
- "envs": [FakeEnv()],
|
| 99 |
+
+ "envs": [FakeEnv(cfg.export_env_raw_rgb_frames)],
|
| 100 |
+
"env_info": object(),
|
| 101 |
+
"actor_critic": FakeActorCritic(),
|
| 102 |
+
"device": torch.device("cpu"),
|
| 103 |
+
@@ -1382,6 +1389,7 @@ def test_collect_teacher_dataset_saves_flappy_latest_raw_rgb_image(monkeypatch,
|
| 104 |
+
export_image_size=None,
|
| 105 |
+
image_writer_workers=0,
|
| 106 |
+
row_buffer_size=1,
|
| 107 |
+
+ filter_preset=filter_preset,
|
| 108 |
+
)
|
| 109 |
+
train_rows = _read_shard_rows(output_dir, "train")
|
| 110 |
+
val_rows = _read_shard_rows(output_dir, "val")
|
| 111 |
+
diff --git a/third_party/MIKASA-Robo b/third_party/MIKASA-Robo
|
| 112 |
+
--- a/third_party/MIKASA-Robo
|
| 113 |
+
+++ b/third_party/MIKASA-Robo
|
| 114 |
+
@@ -1 +1 @@
|
| 115 |
+
-Subproject commit 017490bbb787c6efa8c025f9558e1a691965c0e0
|
| 116 |
+
+Subproject commit 017490bbb787c6efa8c025f9558e1a691965c0e0-dirty
|
| 117 |
+
diff --git a/third_party/starVLA b/third_party/starVLA
|
| 118 |
+
--- a/third_party/starVLA
|
| 119 |
+
+++ b/third_party/starVLA
|
| 120 |
+
@@ -1 +1 @@
|
| 121 |
+
-Subproject commit ccca13c5177fe3d3c884b6e2de4965d916016649
|
| 122 |
+
+Subproject commit ccca13c5177fe3d3c884b6e2de4965d916016649-dirty
|
| 123 |
+
diff --git a/training/gymnasium_sf/integration.py b/training/gymnasium_sf/integration.py
|
| 124 |
+
index 61c65ae9..874f655f 100644
|
| 125 |
+
--- a/training/gymnasium_sf/integration.py
|
| 126 |
+
+++ b/training/gymnasium_sf/integration.py
|
| 127 |
+
@@ -64,7 +64,6 @@ from training.common.sf_raw_reward_stats_compat import (
|
| 128 |
+
)
|
| 129 |
+
from training.common.utils import env_instance_seed, seed_everything, str2bool
|
| 130 |
+
from training.common.wandb_compat import patch_sample_factory_wandb
|
| 131 |
+
-from sample_factory.algo.mc_ppo.env import EpisodeEnv
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
SF_ENV_NAME = "latency_gymnasium"
|
| 135 |
+
@@ -385,7 +384,11 @@ def make_gymnasium_sf_env(
|
| 136 |
+
env_id=int(env_config.env_id),
|
| 137 |
+
issued_action_labels=action_labels,
|
| 138 |
+
)
|
| 139 |
+
- return EpisodeEnv(env, cfg.ppo) if cfg.algo == "PPO" else env
|
| 140 |
+
+ if cfg.algo == "PPO":
|
| 141 |
+
+ from sample_factory.algo.mc_ppo.env import EpisodeEnv
|
| 142 |
+
+
|
| 143 |
+
+ return EpisodeEnv(env, cfg.ppo)
|
| 144 |
+
+ return env
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def register_gymnasium_envs() -> None:
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/train_starvla.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/trainer_tools.py
ADDED
|
@@ -0,0 +1,736 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
metrics.py
|
| 3 |
+
|
| 4 |
+
Utility classes defining a Metrics container and multiple Trackers to enable model/stage-specific logging to various
|
| 5 |
+
endpoints (e.g., JSONL local logs, Weights & Biases).
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from typing import Tuple
|
| 9 |
+
import re
|
| 10 |
+
import json
|
| 11 |
+
import numpy as np
|
| 12 |
+
import torch
|
| 13 |
+
import torch.distributed as dist
|
| 14 |
+
from transformers import get_scheduler
|
| 15 |
+
|
| 16 |
+
from accelerate import Accelerator, DeepSpeedPlugin
|
| 17 |
+
from accelerate.logging import get_logger
|
| 18 |
+
from starVLA.training.trainer_utils.config_tracker import reload_module_paths
|
| 19 |
+
|
| 20 |
+
logger = get_logger(__name__)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def build_accelerator(cfg, *, use_deepspeed: bool) -> Accelerator:
|
| 24 |
+
accelerator_kwargs = {
|
| 25 |
+
"gradient_accumulation_steps": cfg.trainer.gradient_accumulation_steps,
|
| 26 |
+
}
|
| 27 |
+
if use_deepspeed:
|
| 28 |
+
accelerator_kwargs["deepspeed_plugin"] = DeepSpeedPlugin()
|
| 29 |
+
|
| 30 |
+
accelerator = Accelerator(**accelerator_kwargs)
|
| 31 |
+
accelerator.print(accelerator.state)
|
| 32 |
+
return accelerator
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
_VOCAB_SIZED_WEIGHT_SUFFIXES = (
|
| 36 |
+
"embed_tokens.weight",
|
| 37 |
+
"lm_head.weight",
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _adapt_vocab_sized_weights(module, state_dict):
|
| 42 |
+
"""Copy overlapping vocab rows when tokenizer-sized Qwen weights changed."""
|
| 43 |
+
target_state_dict = module.state_dict()
|
| 44 |
+
adapted = dict(state_dict)
|
| 45 |
+
adapted_keys = []
|
| 46 |
+
for key, source_tensor in state_dict.items():
|
| 47 |
+
target_tensor = target_state_dict.get(key)
|
| 48 |
+
if target_tensor is None or not hasattr(source_tensor, "shape") or not hasattr(target_tensor, "shape"):
|
| 49 |
+
continue
|
| 50 |
+
if tuple(source_tensor.shape) == tuple(target_tensor.shape):
|
| 51 |
+
continue
|
| 52 |
+
if not key.endswith(_VOCAB_SIZED_WEIGHT_SUFFIXES):
|
| 53 |
+
continue
|
| 54 |
+
if source_tensor.ndim != 2 or target_tensor.ndim != 2:
|
| 55 |
+
continue
|
| 56 |
+
if source_tensor.shape[1] != target_tensor.shape[1]:
|
| 57 |
+
continue
|
| 58 |
+
|
| 59 |
+
patched_tensor = target_tensor.detach().clone()
|
| 60 |
+
rows = min(source_tensor.shape[0], target_tensor.shape[0])
|
| 61 |
+
patched_tensor[:rows].copy_(source_tensor[:rows].to(dtype=patched_tensor.dtype, device=patched_tensor.device))
|
| 62 |
+
adapted[key] = patched_tensor
|
| 63 |
+
adapted_keys.append((key, tuple(source_tensor.shape), tuple(target_tensor.shape), rows))
|
| 64 |
+
return adapted, adapted_keys
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
# === Define Tracker Interface ===
|
| 68 |
+
#
|
| 69 |
+
|
| 70 |
+
# utils/cli_parser.py
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def normalize_dotlist_args(args):
|
| 74 |
+
"""
|
| 75 |
+
Convert ['--x.y', 'val'] and ['--flag'] → ['x.y=val', 'flag=true']
|
| 76 |
+
"""
|
| 77 |
+
normalized = []
|
| 78 |
+
skip = False
|
| 79 |
+
for i in range(len(args)):
|
| 80 |
+
if skip:
|
| 81 |
+
skip = False
|
| 82 |
+
continue
|
| 83 |
+
|
| 84 |
+
arg = args[i]
|
| 85 |
+
if arg.startswith("--"):
|
| 86 |
+
key = arg.lstrip("-")
|
| 87 |
+
if "=" in key:
|
| 88 |
+
normalized.append(key)
|
| 89 |
+
elif i + 1 < len(args) and not args[i + 1].startswith("--"):
|
| 90 |
+
normalized.append(f"{key}={args[i + 1]}")
|
| 91 |
+
skip = True
|
| 92 |
+
else:
|
| 93 |
+
normalized.append(f"{key}=true")
|
| 94 |
+
else:
|
| 95 |
+
pass # skip orphaned values
|
| 96 |
+
return normalized
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def setup_optimizer_and_scheduler(model, cfg) -> Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler._LRScheduler]:
|
| 100 |
+
"""Build AdamW optimizer + LR scheduler from cfg.trainer.
|
| 101 |
+
|
| 102 |
+
Supports:
|
| 103 |
+
- Per-module learning rates via cfg.trainer.learning_rate
|
| 104 |
+
- cosine_with_min_lr (or any transformers scheduler) via cfg.trainer.lr_scheduler_type
|
| 105 |
+
"""
|
| 106 |
+
param_groups = build_param_lr_groups(model=model, cfg=cfg)
|
| 107 |
+
fused_available = torch.cuda.is_available()
|
| 108 |
+
optimizer = torch.optim.AdamW(
|
| 109 |
+
param_groups,
|
| 110 |
+
lr=cfg.trainer.learning_rate.base,
|
| 111 |
+
betas=tuple(cfg.trainer.optimizer.betas),
|
| 112 |
+
weight_decay=cfg.trainer.optimizer.weight_decay,
|
| 113 |
+
eps=cfg.trainer.optimizer.eps,
|
| 114 |
+
fused=fused_available,
|
| 115 |
+
)
|
| 116 |
+
|
| 117 |
+
if dist.is_initialized() and dist.get_rank() == 0:
|
| 118 |
+
for group in optimizer.param_groups:
|
| 119 |
+
logger.info(f"LR Group {group['name']}: lr={group['lr']}, num_params={len(group['params'])}")
|
| 120 |
+
|
| 121 |
+
# Strip keys unknown to transformers' get_scheduler before passing kwargs.
|
| 122 |
+
sched_kwargs = cfg.trainer.scheduler_specific_kwargs
|
| 123 |
+
lr_scheduler = get_scheduler(
|
| 124 |
+
name=cfg.trainer.lr_scheduler_type,
|
| 125 |
+
optimizer=optimizer,
|
| 126 |
+
num_warmup_steps=cfg.trainer.num_warmup_steps,
|
| 127 |
+
num_training_steps=cfg.trainer.max_train_steps,
|
| 128 |
+
scheduler_specific_kwargs=sched_kwargs,
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
return optimizer, lr_scheduler
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def _qwen_vl_interface(model):
|
| 135 |
+
return model.qwen_vl_interface
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def _llm_language_model(model):
|
| 139 |
+
return _qwen_vl_interface(model).model.model.language_model
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def _vit_module(model):
|
| 143 |
+
# Qwen-VL keeps the visual tower and its vision-to-language connector/merger
|
| 144 |
+
# under this module.
|
| 145 |
+
return _qwen_vl_interface(model).model.model.visual
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def _llm_decoder_layers(model):
|
| 149 |
+
return _llm_language_model(model).layers
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def _tied_embedding_modules(model):
|
| 153 |
+
language_model = _llm_language_model(model)
|
| 154 |
+
hf_model = _qwen_vl_interface(model).model
|
| 155 |
+
return language_model.embed_tokens, hf_model.lm_head
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def resolve_freeze_llm_layer_indices(model, cfg):
|
| 159 |
+
"""Translate ``trainer.freeze_llm_layers=[start,end]`` to an inclusive layer list."""
|
| 160 |
+
freeze_llm_layers = cfg.trainer.freeze_llm_layers
|
| 161 |
+
if not freeze_llm_layers:
|
| 162 |
+
return [], len(_llm_decoder_layers(model))
|
| 163 |
+
start, end = [int(value) for value in freeze_llm_layers]
|
| 164 |
+
return list(range(start, end + 1)), len(_llm_decoder_layers(model))
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def _module_param_ids(module):
|
| 168 |
+
return {id(param) for param in module.parameters()}
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def _has_vit_or_llm_freeze_request(cfg) -> bool:
|
| 172 |
+
return bool(cfg.trainer.freeze_vit or cfg.trainer.freeze_tied_embedding or cfg.trainer.freeze_llm_layers)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def vit_llm_frozen_param_ids(model, cfg):
|
| 176 |
+
"""Parameter ids excluded from optimizer by trainer freeze controls."""
|
| 177 |
+
if not _has_vit_or_llm_freeze_request(cfg):
|
| 178 |
+
return set()
|
| 179 |
+
|
| 180 |
+
frozen_params = set()
|
| 181 |
+
freeze_vit = cfg.trainer.freeze_vit
|
| 182 |
+
if freeze_vit:
|
| 183 |
+
frozen_params.update(_module_param_ids(_vit_module(model)))
|
| 184 |
+
|
| 185 |
+
if cfg.trainer.freeze_tied_embedding:
|
| 186 |
+
for module in _tied_embedding_modules(model):
|
| 187 |
+
frozen_params.update(_module_param_ids(module))
|
| 188 |
+
|
| 189 |
+
frozen_layer_indices, _ = resolve_freeze_llm_layer_indices(model, cfg)
|
| 190 |
+
layers = list(_llm_decoder_layers(model))
|
| 191 |
+
for layer_index in frozen_layer_indices:
|
| 192 |
+
frozen_params.update(_module_param_ids(layers[layer_index]))
|
| 193 |
+
|
| 194 |
+
return frozen_params
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def build_param_lr_groups(model, cfg):
|
| 198 |
+
"""
|
| 199 |
+
build multiple param groups based on cfg.trainer.learning_rate.
|
| 200 |
+
support specifying different learning rates for different modules, the rest use base.
|
| 201 |
+
|
| 202 |
+
Args:
|
| 203 |
+
vla: nn.Module model object
|
| 204 |
+
cfg: config object, requires cfg.trainer.learning_rate dictionary
|
| 205 |
+
|
| 206 |
+
Returns:
|
| 207 |
+
List[Dict]: param_groups that can be used to build optimizer with torch.optim
|
| 208 |
+
"""
|
| 209 |
+
|
| 210 |
+
lr_cfg = cfg.trainer.learning_rate
|
| 211 |
+
base_lr = lr_cfg.get("base", 1e-4) # default base learning rate
|
| 212 |
+
|
| 213 |
+
freeze_modules = cfg.trainer.get("freeze_modules", "")
|
| 214 |
+
if not isinstance(freeze_modules, str):
|
| 215 |
+
freeze_modules = ""
|
| 216 |
+
freeze_patterns = [p.strip() for p in freeze_modules.split(",") if p.strip()]
|
| 217 |
+
|
| 218 |
+
used_params = set()
|
| 219 |
+
frozen_params = set()
|
| 220 |
+
param_groups = []
|
| 221 |
+
|
| 222 |
+
for freeze_path in freeze_patterns:
|
| 223 |
+
module = model
|
| 224 |
+
try:
|
| 225 |
+
for attr in freeze_path.split("."):
|
| 226 |
+
module = getattr(module, attr)
|
| 227 |
+
frozen_params.update(id(p) for p in module.parameters())
|
| 228 |
+
except AttributeError:
|
| 229 |
+
print(f"⚠️ freeze module path does not exist: {freeze_path}")
|
| 230 |
+
continue
|
| 231 |
+
|
| 232 |
+
# Exclude trainer freeze controls so frozen parameters never enter an
|
| 233 |
+
# optimizer param group.
|
| 234 |
+
frozen_params.update(vit_llm_frozen_param_ids(model, cfg))
|
| 235 |
+
|
| 236 |
+
for module_name, lr in lr_cfg.items():
|
| 237 |
+
if module_name == "base":
|
| 238 |
+
continue
|
| 239 |
+
# try to find the module under vla by module_name (support nested paths)
|
| 240 |
+
module = model
|
| 241 |
+
try:
|
| 242 |
+
for attr in module_name.split("."):
|
| 243 |
+
module = getattr(module, attr)
|
| 244 |
+
# filter out frozen parameters
|
| 245 |
+
params = [p for p in module.parameters() if id(p) not in frozen_params]
|
| 246 |
+
if params: # only add param group if there are trainable parameters
|
| 247 |
+
param_groups.append({"params": params, "lr": lr, "name": module_name})
|
| 248 |
+
used_params.update(id(p) for p in params)
|
| 249 |
+
except AttributeError:
|
| 250 |
+
ReferenceError(f"⚠️ module path `{module_name}` not found in vla")
|
| 251 |
+
|
| 252 |
+
# assign base learning rate to the remaining unused parameters (exclude frozen ones)
|
| 253 |
+
other_params = [p for p in model.parameters() if id(p) not in used_params and id(p) not in frozen_params]
|
| 254 |
+
if other_params:
|
| 255 |
+
param_groups.append({"params": other_params, "lr": base_lr, "name": "base"})
|
| 256 |
+
|
| 257 |
+
return param_groups
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
import torch.distributed as dist
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def only_main_process(func):
|
| 264 |
+
"""
|
| 265 |
+
decorator: only run in main process (rank=0)
|
| 266 |
+
"""
|
| 267 |
+
|
| 268 |
+
def wrapper(*args, **kwargs):
|
| 269 |
+
if dist.is_initialized() and dist.get_rank() != 0:
|
| 270 |
+
return None # non-main process does not execute
|
| 271 |
+
return func(*args, **kwargs)
|
| 272 |
+
|
| 273 |
+
return wrapper
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
from torchvision.ops import box_iou
|
| 277 |
+
from PIL import Image
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
def resize_images(images, target_size=(224, 224)):
|
| 281 |
+
"""
|
| 282 |
+
recursively resize all images in the nested list.
|
| 283 |
+
|
| 284 |
+
:param images: nested list of images or single image.
|
| 285 |
+
:param target_size: target size (width, height) after resizing.
|
| 286 |
+
:return: resized images list, keeping the original nested structure.
|
| 287 |
+
"""
|
| 288 |
+
if isinstance(images, Image.Image): # if it is a single PIL image
|
| 289 |
+
return images.resize(target_size)
|
| 290 |
+
elif isinstance(images, list): # if it is a list, recursively process each element
|
| 291 |
+
return [resize_images(img, target_size) for img in images]
|
| 292 |
+
else:
|
| 293 |
+
raise ValueError("Unsupported image type or structure.")
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
def stitch_frames(frames, grid=(2, 2), size=(224, 224)):
|
| 297 |
+
"""Tile temporal frames into one image, keeping the token count of a single image.
|
| 298 |
+
|
| 299 |
+
Used by the ``stitch`` multi-image mode: instead of feeding N frames as N
|
| 300 |
+
tokenized images, they are laid out on a fixed grid and the whole canvas is
|
| 301 |
+
returned at ``size``. Both training (``_pack_sample``) and eval
|
| 302 |
+
(``policy/starvla.py``) call this so the layouts match.
|
| 303 |
+
|
| 304 |
+
Args:
|
| 305 |
+
frames: list of ``PIL.Image`` or ``np.ndarray`` (H, W, C), oldest first.
|
| 306 |
+
grid: (rows, cols). Cells beyond ``len(frames)`` repeat the last frame so
|
| 307 |
+
the layout is fixed regardless of how many frames are available.
|
| 308 |
+
size: (width, height) of the returned canvas.
|
| 309 |
+
"""
|
| 310 |
+
rows, cols = grid
|
| 311 |
+
canvas_w, canvas_h = size
|
| 312 |
+
cell_w, cell_h = canvas_w // cols, canvas_h // rows
|
| 313 |
+
pil = [f if isinstance(f, Image.Image) else Image.fromarray(f) for f in frames]
|
| 314 |
+
capacity = rows * cols
|
| 315 |
+
pil = (pil + [pil[-1]] * (capacity - len(pil))) if len(pil) < capacity else pil[:capacity]
|
| 316 |
+
canvas = Image.new("RGB", (canvas_w, canvas_h))
|
| 317 |
+
for idx, frame in enumerate(pil):
|
| 318 |
+
r, c = divmod(idx, cols)
|
| 319 |
+
canvas.paste(frame.convert("RGB").resize((cell_w, cell_h)), (c * cell_w, r * cell_h))
|
| 320 |
+
return canvas
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
import torch.distributed as dist
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
class TrainerUtils:
|
| 327 |
+
@staticmethod
|
| 328 |
+
def freeze_backbones(model, freeze_modules=""):
|
| 329 |
+
"""
|
| 330 |
+
directly freeze the specified submodules based on the relative module path list (patterns), no longer recursively find all submodule names:
|
| 331 |
+
- patterns: read from config.trainer.freeze_modules, separated by commas to get the "relative path" list
|
| 332 |
+
for example "qwen_vl_interface, action_model.net",
|
| 333 |
+
it means to freeze model.qwen_vl_interface and model.action_model.net.
|
| 334 |
+
|
| 335 |
+
Args:
|
| 336 |
+
model: nn.Module model object
|
| 337 |
+
freeze_modules: relative module path list (patterns)
|
| 338 |
+
|
| 339 |
+
Returns:
|
| 340 |
+
model: nn.Module model object
|
| 341 |
+
return:
|
| 342 |
+
- model:
|
| 343 |
+
"""
|
| 344 |
+
frozen = []
|
| 345 |
+
print("#"*30)
|
| 346 |
+
print(freeze_modules)
|
| 347 |
+
if freeze_modules and type(freeze_modules) == str:
|
| 348 |
+
# split and remove whitespace
|
| 349 |
+
patterns = [p.strip() for p in freeze_modules.split(",") if p.strip()] if freeze_modules else []
|
| 350 |
+
|
| 351 |
+
for path in patterns:
|
| 352 |
+
# split the "relative path" by dots, for example "action_model.net" → ["action_model", "net"]
|
| 353 |
+
attrs = path.split(".")
|
| 354 |
+
module = model
|
| 355 |
+
try:
|
| 356 |
+
for attr in attrs:
|
| 357 |
+
module = getattr(module, attr)
|
| 358 |
+
# if the module is successfully get, freeze it and its all submodule parameters
|
| 359 |
+
for param in module.parameters():
|
| 360 |
+
param.requires_grad = False
|
| 361 |
+
frozen.append(path)
|
| 362 |
+
except AttributeError:
|
| 363 |
+
# if the attribute does not exist, skip and print warning
|
| 364 |
+
print(f"⚠️ module path does not exist, cannot freeze: {path}")
|
| 365 |
+
continue
|
| 366 |
+
|
| 367 |
+
# accelerator.wait_for_everyone() # synchronize when distributed training
|
| 368 |
+
if not dist.is_initialized() or dist.get_rank() == 0:
|
| 369 |
+
print(f"🔒 Frozen modules with re pattern: {frozen}")
|
| 370 |
+
return model
|
| 371 |
+
|
| 372 |
+
@staticmethod
|
| 373 |
+
def freeze_vit_and_llm_layers(model, cfg):
|
| 374 |
+
"""Apply trainer.freeze_vit/freeze_tied_embedding/freeze_llm_layers.
|
| 375 |
+
|
| 376 |
+
trainer.freeze_vit freezes Qwen-VL's visual module, including the
|
| 377 |
+
vision-to-language connector/merger it contains.
|
| 378 |
+
"""
|
| 379 |
+
if not _has_vit_or_llm_freeze_request(cfg):
|
| 380 |
+
return model
|
| 381 |
+
|
| 382 |
+
freeze_vit = cfg.trainer.freeze_vit
|
| 383 |
+
if freeze_vit:
|
| 384 |
+
for param in _vit_module(model).parameters():
|
| 385 |
+
param.requires_grad = False
|
| 386 |
+
|
| 387 |
+
freeze_tied_embedding = cfg.trainer.freeze_tied_embedding
|
| 388 |
+
if freeze_tied_embedding:
|
| 389 |
+
for module in _tied_embedding_modules(model):
|
| 390 |
+
for param in module.parameters():
|
| 391 |
+
param.requires_grad = False
|
| 392 |
+
|
| 393 |
+
frozen_layer_indices, total = resolve_freeze_llm_layer_indices(model, cfg)
|
| 394 |
+
layers = list(_llm_decoder_layers(model))
|
| 395 |
+
for layer_index in frozen_layer_indices:
|
| 396 |
+
for param in layers[layer_index].parameters():
|
| 397 |
+
param.requires_grad = False
|
| 398 |
+
|
| 399 |
+
if not dist.is_initialized() or dist.get_rank() == 0:
|
| 400 |
+
print(
|
| 401 |
+
"🔒 freeze_vit="
|
| 402 |
+
f"{freeze_vit}, freeze_tied_embedding={freeze_tied_embedding}, "
|
| 403 |
+
f"freeze_llm_layers={list(frozen_layer_indices)}"
|
| 404 |
+
f" / total={total}"
|
| 405 |
+
)
|
| 406 |
+
return model
|
| 407 |
+
|
| 408 |
+
@staticmethod
|
| 409 |
+
def print_trainable_parameters(model):
|
| 410 |
+
"""
|
| 411 |
+
print the total number of parameters and trainable parameters of the model
|
| 412 |
+
:param model: PyTorch model instance
|
| 413 |
+
"""
|
| 414 |
+
if dist.is_initialized() and dist.get_rank() != 0:
|
| 415 |
+
return
|
| 416 |
+
print("📊 model parameter statistics:")
|
| 417 |
+
num_params = sum(p.numel() for p in model.parameters())
|
| 418 |
+
num_trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
|
| 419 |
+
print(
|
| 420 |
+
f"# Parameters (in millions): {num_params / 10**6:.3f} Total, {num_trainable_params / 10**6:.3f} Trainable"
|
| 421 |
+
)
|
| 422 |
+
return num_params, num_trainable_params
|
| 423 |
+
|
| 424 |
+
@staticmethod
|
| 425 |
+
def load_pretrained_backbones(model, checkpoint_path=None, reload_modules=None):
|
| 426 |
+
"""
|
| 427 |
+
load checkpoint:
|
| 428 |
+
- if reload_modules is set, load by path part
|
| 429 |
+
- otherwise → load the entire model parameters (overwrite model)
|
| 430 |
+
|
| 431 |
+
return:
|
| 432 |
+
replace, loaded_modules: list of module paths that successfully loaded parameters; if global load, then ["<full_model>"]
|
| 433 |
+
"""
|
| 434 |
+
if not checkpoint_path:
|
| 435 |
+
return []
|
| 436 |
+
if not dist.is_initialized() or dist.get_rank() == 0:
|
| 437 |
+
print(f"📦 loading checkpoint: {checkpoint_path}")
|
| 438 |
+
try:
|
| 439 |
+
if _is_safetensors_path(checkpoint_path):
|
| 440 |
+
from safetensors.torch import load_file
|
| 441 |
+
|
| 442 |
+
checkpoint = load_file(checkpoint_path)
|
| 443 |
+
else:
|
| 444 |
+
checkpoint = torch.load(checkpoint_path, map_location="cpu")
|
| 445 |
+
except Exception as e:
|
| 446 |
+
raise RuntimeError(f"❌ loading checkpoint failed: {e}")
|
| 447 |
+
|
| 448 |
+
loaded_modules = []
|
| 449 |
+
|
| 450 |
+
module_paths = reload_module_paths(reload_modules)
|
| 451 |
+
if module_paths: # partial load
|
| 452 |
+
for path in module_paths:
|
| 453 |
+
reload_path = path.split(".")
|
| 454 |
+
module = model
|
| 455 |
+
try:
|
| 456 |
+
for module_name in reload_path: # find the module to modify level by level
|
| 457 |
+
module = getattr(module, module_name)
|
| 458 |
+
prefix = path + "."
|
| 459 |
+
sub_state_dict = {k[len(prefix) :]: v for k, v in checkpoint.items() if k.startswith(prefix)}
|
| 460 |
+
if sub_state_dict:
|
| 461 |
+
sub_state_dict, adapted_keys = _adapt_vocab_sized_weights(module, sub_state_dict)
|
| 462 |
+
for key, source_shape, target_shape, rows in adapted_keys:
|
| 463 |
+
if not dist.is_initialized() or dist.get_rank() == 0:
|
| 464 |
+
print(
|
| 465 |
+
f"⚠️ adapted vocab-sized weight '{path}.{key}' from checkpoint shape "
|
| 466 |
+
f"{source_shape} to current shape {target_shape}; copied {rows} rows"
|
| 467 |
+
)
|
| 468 |
+
module.load_state_dict(sub_state_dict, strict=True)
|
| 469 |
+
if not dist.is_initialized() or dist.get_rank() == 0:
|
| 470 |
+
print(f"✅ parameters loaded to module '{path}'")
|
| 471 |
+
loaded_modules.append(path)
|
| 472 |
+
else:
|
| 473 |
+
print(f"⚠️ parameters not found in checkpoint '{path}'")
|
| 474 |
+
except AttributeError:
|
| 475 |
+
print(f"❌ cannot find module path: {path}")
|
| 476 |
+
else: # full load
|
| 477 |
+
try:
|
| 478 |
+
checkpoint, adapted_keys = _adapt_vocab_sized_weights(model, checkpoint)
|
| 479 |
+
for key, source_shape, target_shape, rows in adapted_keys:
|
| 480 |
+
if not dist.is_initialized() or dist.get_rank() == 0:
|
| 481 |
+
print(
|
| 482 |
+
f"⚠️ adapted vocab-sized weight '{key}' from checkpoint shape "
|
| 483 |
+
f"{source_shape} to current shape {target_shape}; copied {rows} rows"
|
| 484 |
+
)
|
| 485 |
+
model.load_state_dict(checkpoint, strict=False)
|
| 486 |
+
if not dist.is_initialized() or dist.get_rank() == 0:
|
| 487 |
+
print("✅ loaded <full_model> model parameters")
|
| 488 |
+
loaded_modules = ["<full_model>"]
|
| 489 |
+
except Exception as e:
|
| 490 |
+
raise RuntimeError(f"❌ loading full model failed: {e}")
|
| 491 |
+
return model
|
| 492 |
+
|
| 493 |
+
@staticmethod
|
| 494 |
+
def print_freeze_status(model):
|
| 495 |
+
"""
|
| 496 |
+
print the freezing status of each parameter in the model
|
| 497 |
+
:param model: PyTorch model instance
|
| 498 |
+
"""
|
| 499 |
+
for name, param in model.named_parameters():
|
| 500 |
+
status = "Frozen" if not param.requires_grad else "Trainable"
|
| 501 |
+
print(f"{name:60s} | {status}")
|
| 502 |
+
|
| 503 |
+
@staticmethod
|
| 504 |
+
def setup_distributed_training(accelerator, *components):
|
| 505 |
+
"""
|
| 506 |
+
use Accelerator to prepare distributed training components
|
| 507 |
+
:param accelerator: Accelerate instance
|
| 508 |
+
:param components: any number of components (such as model, optimizer, dataloader, etc.)
|
| 509 |
+
:return: prepared distributed components (in the same order as input)
|
| 510 |
+
"""
|
| 511 |
+
|
| 512 |
+
# use accelerator.prepare method to wrap components
|
| 513 |
+
prepared_components = accelerator.prepare(*components)
|
| 514 |
+
return prepared_components
|
| 515 |
+
|
| 516 |
+
@staticmethod
|
| 517 |
+
def euclidean_distance(predicted: np.ndarray, ground_truth: np.ndarray) -> float:
|
| 518 |
+
return np.linalg.norm(predicted - ground_truth)
|
| 519 |
+
|
| 520 |
+
@staticmethod
|
| 521 |
+
def _reset_dataloader(dataloader, epoch_counter):
|
| 522 |
+
"""safe reset dataloader iterator"""
|
| 523 |
+
# 1. update epoch counter
|
| 524 |
+
epoch_counter += 1
|
| 525 |
+
|
| 526 |
+
dataset = getattr(dataloader, "dataset", None)
|
| 527 |
+
if dataset is not None and callable(getattr(dataset, "set_epoch", None)):
|
| 528 |
+
dataset.set_epoch(epoch_counter)
|
| 529 |
+
|
| 530 |
+
# 2. set new epoch (distributed core)
|
| 531 |
+
if hasattr(dataloader, "sampler") and callable(getattr(dataloader.sampler, "set_epoch", None)):
|
| 532 |
+
dataloader.sampler.set_epoch(epoch_counter)
|
| 533 |
+
|
| 534 |
+
# 3. create new iterator
|
| 535 |
+
return iter(dataloader), epoch_counter
|
| 536 |
+
|
| 537 |
+
@staticmethod
|
| 538 |
+
def compute_grad_angle_with_stats(grads_a: list[torch.Tensor], grads_v: list[torch.Tensor]) -> Tuple[float, float]:
|
| 539 |
+
"""
|
| 540 |
+
compute the cosine angle between two groups of gradient vectors (degrees), and calculate the average angle and variance.
|
| 541 |
+
grads_a, grads_v: gradient Tensor list corresponding to the same parameter list interface_params
|
| 542 |
+
return:
|
| 543 |
+
mean_angle_deg: average angle (degrees)
|
| 544 |
+
angle_variance: angle variance
|
| 545 |
+
"""
|
| 546 |
+
angle_degs = []
|
| 547 |
+
|
| 548 |
+
# compute the cosine angle between each gradient block grads_a[0].shape = 1280, 3, 14, 14
|
| 549 |
+
# grads_1 = grads_a[0][0] # [3, 14, 14]
|
| 550 |
+
# grads_2 = grads_v[0][0]
|
| 551 |
+
# grads_a = grads_1.view(-1, 3) # reshape to [196, 3]
|
| 552 |
+
# grads_v = grads_2.view(-1, 3)
|
| 553 |
+
|
| 554 |
+
# lang linear
|
| 555 |
+
# reshape to 14*14, 3
|
| 556 |
+
# layer
|
| 557 |
+
grads_action = grads_a[0] # [2048, 11008]
|
| 558 |
+
grads_action = grads_action[
|
| 559 |
+
:32, :7
|
| 560 |
+
] # only take the first 7 elements, avoid cosim failure in high-dimensional space
|
| 561 |
+
grads_vl = grads_v[0] # [2048, 11008]
|
| 562 |
+
grads_vl = grads_vl[
|
| 563 |
+
:32, :7
|
| 564 |
+
] # only take the first 32 elements, 7 dimensions, avoid cosim failure in high-dimensional space
|
| 565 |
+
for g_a, g_v in zip(grads_action, grads_vl):
|
| 566 |
+
dot = torch.sum(g_a * g_v)
|
| 567 |
+
norm_a_sq = torch.sum(g_a * g_a)
|
| 568 |
+
norm_v_sq = torch.sum(g_v * g_v)
|
| 569 |
+
|
| 570 |
+
# avoid division by zero
|
| 571 |
+
norm_a = torch.sqrt(norm_a_sq + 1e-16)
|
| 572 |
+
norm_v = torch.sqrt(norm_v_sq + 1e-16)
|
| 573 |
+
|
| 574 |
+
cos_sim = (dot / (norm_a * norm_v)).clamp(-1.0, 1.0)
|
| 575 |
+
angle_rad = torch.acos(cos_sim)
|
| 576 |
+
angle_deg = angle_rad * (180.0 / torch.pi)
|
| 577 |
+
|
| 578 |
+
angle_degs.append(angle_deg.item())
|
| 579 |
+
|
| 580 |
+
# compute the average angle and variance
|
| 581 |
+
angle_degs_tensor = torch.tensor(angle_degs)
|
| 582 |
+
mean_angle_deg = torch.mean(angle_degs_tensor).item()
|
| 583 |
+
angle_variance = torch.sqrt(torch.var(angle_degs_tensor)).item()
|
| 584 |
+
# accelerator.wait_for_everyone()
|
| 585 |
+
return mean_angle_deg, angle_variance
|
| 586 |
+
|
| 587 |
+
@staticmethod
|
| 588 |
+
def pcgrad_project(grads_a: list[torch.Tensor], grads_v: list[torch.Tensor]) -> list[torch.Tensor]:
|
| 589 |
+
"""
|
| 590 |
+
apply PCGrad projection to the second group of gradients grads_v, suppress negative transfer between grads_a and grads_v
|
| 591 |
+
if the dot product of two groups of gradients < 0, then:
|
| 592 |
+
grads_v <- grads_v - (dot / ||grads_a||^2) * grads_a
|
| 593 |
+
return the new grads_v list
|
| 594 |
+
"""
|
| 595 |
+
# first compute dot and ||grads_a||^2
|
| 596 |
+
dot, norm_a_sq = 0.0, 0.0
|
| 597 |
+
for g_a, g_v in zip(grads_a, grads_v):
|
| 598 |
+
dot += torch.sum(g_a * g_v)
|
| 599 |
+
norm_a_sq += torch.sum(g_a * g_a)
|
| 600 |
+
|
| 601 |
+
if dot < 0:
|
| 602 |
+
coeff = dot / (norm_a_sq + 1e-6)
|
| 603 |
+
# projection
|
| 604 |
+
grads_v = [g_v - coeff * g_a for g_a, g_v in zip(grads_a, grads_v)]
|
| 605 |
+
|
| 606 |
+
return grads_v
|
| 607 |
+
|
| 608 |
+
@staticmethod
|
| 609 |
+
def eval_qwenpi(qwenpi, dataloader, num_batches=20):
|
| 610 |
+
"""
|
| 611 |
+
evaluate QwenQFormerDiT model, compute IoU and action distance.
|
| 612 |
+
|
| 613 |
+
Args:
|
| 614 |
+
qwenpi: QwenQFormerDiT model instance.
|
| 615 |
+
dataloader: data loader.
|
| 616 |
+
num_batches: number of batches to evaluate.
|
| 617 |
+
|
| 618 |
+
Returns:
|
| 619 |
+
dict: contains IoU and action distance evaluation results.
|
| 620 |
+
"""
|
| 621 |
+
iou_scores = []
|
| 622 |
+
action_distances = []
|
| 623 |
+
count = 0
|
| 624 |
+
|
| 625 |
+
dataset_iter = iter(dataloader)
|
| 626 |
+
while count < num_batches:
|
| 627 |
+
try:
|
| 628 |
+
batch_samples = next(dataset_iter)
|
| 629 |
+
count += 1
|
| 630 |
+
except StopIteration:
|
| 631 |
+
break
|
| 632 |
+
|
| 633 |
+
# extract data
|
| 634 |
+
images = [example["image"] for example in batch_samples]
|
| 635 |
+
instructions = [example["lang"] for example in batch_samples]
|
| 636 |
+
actions = [example["action"] for example in batch_samples]
|
| 637 |
+
solutions = [example["solution"] for example in batch_samples]
|
| 638 |
+
|
| 639 |
+
# model prediction
|
| 640 |
+
predicted_solutions, normalized_actions = qwenpi.predict_action_withCoT(
|
| 641 |
+
images=images, instructions=instructions, use_ddim=False, num_ddim_steps=20
|
| 642 |
+
)
|
| 643 |
+
|
| 644 |
+
# extract and convert predicted results
|
| 645 |
+
parsed_solutions = []
|
| 646 |
+
for solution in predicted_solutions:
|
| 647 |
+
parsed_solution = TrainerUtils.extract_json_from_string(solution)
|
| 648 |
+
parsed_solutions.append(parsed_solution)
|
| 649 |
+
|
| 650 |
+
# compute IoU
|
| 651 |
+
for pred_dict, gt_dict in zip(parsed_solutions, solutions):
|
| 652 |
+
pred_pick_bbox = torch.tensor(pred_dict["pick"]["bbox_2d"], dtype=torch.float32).unsqueeze(0)
|
| 653 |
+
gt_pick_bbox = torch.tensor(gt_dict["pick"]["bbox_2d"], dtype=torch.float32).unsqueeze(0)
|
| 654 |
+
pred_place_bbox = torch.tensor(pred_dict["place"]["bbox_2d"], dtype=torch.float32).unsqueeze(0)
|
| 655 |
+
gt_place_bbox = torch.tensor(gt_dict["place"]["bbox_2d"], dtype=torch.float32).unsqueeze(0)
|
| 656 |
+
|
| 657 |
+
pick_iou = box_iou(pred_pick_bbox, gt_pick_bbox).item()
|
| 658 |
+
place_iou = box_iou(pred_place_bbox, gt_place_bbox).item()
|
| 659 |
+
|
| 660 |
+
iou_scores.append({"pick_iou": pick_iou, "place_iou": place_iou})
|
| 661 |
+
|
| 662 |
+
# compute action distance
|
| 663 |
+
actions = np.array(actions) # convert to numpy array
|
| 664 |
+
num_pots = np.prod(actions.shape) # B*len*dim
|
| 665 |
+
action_distance = TrainerUtils.euclidean_distance(normalized_actions, actions)
|
| 666 |
+
average_action_distance = action_distance / num_pots
|
| 667 |
+
action_distances.append(average_action_distance)
|
| 668 |
+
|
| 669 |
+
# summarize results
|
| 670 |
+
avg_action_distance = np.mean(action_distances)
|
| 671 |
+
return {"iou_scores": iou_scores, "average_action_distance": avg_action_distance}
|
| 672 |
+
|
| 673 |
+
@staticmethod
|
| 674 |
+
def extract_json_from_string(input_string):
|
| 675 |
+
"""
|
| 676 |
+
extract valid JSON part from string and convert to dictionary.
|
| 677 |
+
|
| 678 |
+
Args:
|
| 679 |
+
input_string (str): string containing extra characters.
|
| 680 |
+
|
| 681 |
+
Returns:
|
| 682 |
+
dict: dictionary extracted and parsed.
|
| 683 |
+
"""
|
| 684 |
+
json_match = re.search(r"{.*}", input_string, re.DOTALL)
|
| 685 |
+
if json_match:
|
| 686 |
+
json_str = json_match.group(0)
|
| 687 |
+
try:
|
| 688 |
+
return json.loads(json_str)
|
| 689 |
+
except json.JSONDecodeError as e:
|
| 690 |
+
print(f"JSON decode failed: {e}")
|
| 691 |
+
return None
|
| 692 |
+
else:
|
| 693 |
+
print("No valid JSON part found")
|
| 694 |
+
return None
|
| 695 |
+
|
| 696 |
+
def _get_latest_checkpoint(self, checkpoint_dir):
|
| 697 |
+
"""Find the latest checkpoint in the directory based on step number."""
|
| 698 |
+
if not os.path.exists(checkpoint_dir):
|
| 699 |
+
self.accelerator.print(f"No checkpoint directory found at {checkpoint_dir}")
|
| 700 |
+
return None, 0
|
| 701 |
+
|
| 702 |
+
checkpoints = []
|
| 703 |
+
for name in os.listdir(checkpoint_dir):
|
| 704 |
+
path = os.path.join(checkpoint_dir, name)
|
| 705 |
+
state_match = re.match(r"steps_(\d+)_state$", name)
|
| 706 |
+
if state_match and os.path.isdir(path):
|
| 707 |
+
checkpoints.append((name, int(state_match.group(1)), 1))
|
| 708 |
+
continue
|
| 709 |
+
file_match = re.match(r"steps_(\d+)_(?:pytorch_model\.pt|model\.safetensors)$", name)
|
| 710 |
+
if file_match and os.path.isfile(path):
|
| 711 |
+
checkpoints.append((name, int(file_match.group(1)), 0))
|
| 712 |
+
|
| 713 |
+
if not checkpoints:
|
| 714 |
+
self.accelerator.print(f"No checkpoints found in {checkpoint_dir}")
|
| 715 |
+
return None, 0
|
| 716 |
+
|
| 717 |
+
# Sort by step number, preferring full training-state directories over
|
| 718 |
+
# model-only files when both exist for the same step.
|
| 719 |
+
checkpoints.sort(key=lambda x: (x[1], x[2]))
|
| 720 |
+
latest_checkpoint, completed_steps, _ = checkpoints[-1]
|
| 721 |
+
|
| 722 |
+
latest_checkpoint_path = os.path.join(checkpoint_dir, latest_checkpoint)
|
| 723 |
+
self.accelerator.print(f"Latest checkpoint found: {latest_checkpoint_path}")
|
| 724 |
+
return latest_checkpoint_path, completed_steps
|
| 725 |
+
|
| 726 |
+
import os
|
| 727 |
+
|
| 728 |
+
|
| 729 |
+
def is_main_process():
|
| 730 |
+
rank = int(os.environ.get("RANK", 0)) # if RANK is not set, default to 0
|
| 731 |
+
return rank == 0
|
| 732 |
+
|
| 733 |
+
|
| 734 |
+
def _is_safetensors_path(path):
|
| 735 |
+
"""Check if a path refers to a safetensors file."""
|
| 736 |
+
return str(path).endswith(".safetensors")
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training-code/worker_context.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import multiprocessing.context
|
| 2 |
+
import os
|
| 3 |
+
from typing import Any
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
_DISTRIBUTED_ENV_KEYS = (
|
| 7 |
+
"LOCAL_RANK",
|
| 8 |
+
"RANK",
|
| 9 |
+
"WORLD_SIZE",
|
| 10 |
+
"MASTER_ADDR",
|
| 11 |
+
"MASTER_PORT",
|
| 12 |
+
"GROUP_RANK",
|
| 13 |
+
"LOCAL_WORLD_SIZE",
|
| 14 |
+
"ROLE_RANK",
|
| 15 |
+
"ROLE_WORLD_SIZE",
|
| 16 |
+
"TORCHELASTIC_RUN_ID",
|
| 17 |
+
"TORCHELASTIC_RESTART_COUNT",
|
| 18 |
+
"TORCHELASTIC_MAX_RESTARTS",
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class _CpuOnlyWorkerSpawnProcess(multiprocessing.context.SpawnProcess):
|
| 23 |
+
def start(self):
|
| 24 |
+
had_cuda_visible_devices = "CUDA_VISIBLE_DEVICES" in os.environ
|
| 25 |
+
if had_cuda_visible_devices:
|
| 26 |
+
cuda_visible_devices = os.environ["CUDA_VISIBLE_DEVICES"]
|
| 27 |
+
distributed_env = {key: os.environ[key] for key in _DISTRIBUTED_ENV_KEYS if key in os.environ}
|
| 28 |
+
|
| 29 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = ""
|
| 30 |
+
for key in distributed_env:
|
| 31 |
+
del os.environ[key]
|
| 32 |
+
try:
|
| 33 |
+
return super().start()
|
| 34 |
+
finally:
|
| 35 |
+
if had_cuda_visible_devices:
|
| 36 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = cuda_visible_devices
|
| 37 |
+
else:
|
| 38 |
+
del os.environ["CUDA_VISIBLE_DEVICES"]
|
| 39 |
+
for key, value in distributed_env.items():
|
| 40 |
+
os.environ[key] = value
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class CpuOnlyWorkerSpawnContext(multiprocessing.context.SpawnContext):
|
| 44 |
+
Process = _CpuOnlyWorkerSpawnProcess
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
CPU_ONLY_WORKER_CONTEXT = CpuOnlyWorkerSpawnContext()
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _as_bool(value: Any, default: bool = False) -> bool:
|
| 51 |
+
if value is None:
|
| 52 |
+
return default
|
| 53 |
+
if isinstance(value, bool):
|
| 54 |
+
return value
|
| 55 |
+
if isinstance(value, str):
|
| 56 |
+
return value.strip().lower() not in {"false", "0", "no", "off"}
|
| 57 |
+
return bool(value)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def build_cpu_only_dataloader_kwargs(
|
| 61 |
+
num_workers: int,
|
| 62 |
+
*,
|
| 63 |
+
pin_memory: Any = False,
|
| 64 |
+
persistent_workers: Any = False,
|
| 65 |
+
prefetch_factor: Any = None,
|
| 66 |
+
) -> dict[str, Any]:
|
| 67 |
+
dataloader_kwargs: dict[str, Any] = {
|
| 68 |
+
"pin_memory": _as_bool(pin_memory),
|
| 69 |
+
}
|
| 70 |
+
if int(num_workers) <= 0:
|
| 71 |
+
return dataloader_kwargs
|
| 72 |
+
|
| 73 |
+
dataloader_kwargs["multiprocessing_context"] = CPU_ONLY_WORKER_CONTEXT
|
| 74 |
+
dataloader_kwargs["persistent_workers"] = _as_bool(persistent_workers)
|
| 75 |
+
if prefetch_factor is not None:
|
| 76 |
+
dataloader_kwargs["prefetch_factor"] = int(prefetch_factor)
|
| 77 |
+
return dataloader_kwargs
|
latency-aware/mikasa-intercept-grab-fast/vla/starvla-qwenoft-h1/intercept-qwenoft-mean-3090-h1-s0/training_results.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"training_complete": true,
|
| 3 |
+
"optimizer_updates": 5000,
|
| 4 |
+
"selection": "final_5000",
|
| 5 |
+
"train_loss": 0.0017812054138630629,
|
| 6 |
+
"validation_action_loss": 0.03070492297410965,
|
| 7 |
+
"validation_samples": 128,
|
| 8 |
+
"final_learning_rates": {
|
| 9 |
+
"learning_rate/base": 1e-06,
|
| 10 |
+
"learning_rate/action_model": 3.3333333333333333e-06
|
| 11 |
+
},
|
| 12 |
+
"closed_loop_evaluation": "pending until all four VLA trainings complete",
|
| 13 |
+
"tensor_keys": 732,
|
| 14 |
+
"serialized_tensor_elements_including_tied_aliases": 4892415495,
|
| 15 |
+
"weights_only_load": "passed",
|
| 16 |
+
"all_tensors_finite": true,
|
| 17 |
+
"completion_proof": {
|
| 18 |
+
"wrapper_exit": 0
|
| 19 |
+
}
|
| 20 |
+
}
|