dongqianyu99 commited on
Commit
dae7306
·
verified ·
1 Parent(s): 6835450

Publish completed ant QwenOFT H1 mean-profile 5000-update run

Browse files
Files changed (28) hide show
  1. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/README.md +8 -0
  2. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/checkpoints/model.pt +3 -0
  3. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/collection_quality_report.json +14 -0
  4. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/config.full.yaml +438 -0
  5. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/config.yaml +45 -0
  6. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/dataset_statistics.json +254 -0
  7. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/dataset_statistics_eval.json +254 -0
  8. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/files.sha256.json +110 -0
  9. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/latency_prompt_map.json +7 -0
  10. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/launch_audit.json +41 -0
  11. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/manifest.json +232 -0
  12. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/profile.source.json +66 -0
  13. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/provenance.json +147 -0
  14. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/startup_verified.json +67 -0
  15. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/summary.jsonl +10 -0
  16. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/task_contract.json +94 -0
  17. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/QWen3.py +444 -0
  18. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/QwenOFT.py +1244 -0
  19. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/datasets.py +0 -0
  20. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/doom-mean-reset.patch +192 -0
  21. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/gym-language-contract.patch +17 -0
  22. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/loader-spawn-cache.patch +18 -0
  23. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/loader-spawn-test.patch +31 -0
  24. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/mean-profile-preparation.patch +147 -0
  25. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/train_starvla.py +0 -0
  26. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/trainer_tools.py +736 -0
  27. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/worker_context.py +77 -0
  28. latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training_results.json +46 -0
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/README.md ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # ant QwenOFT H1 mean-profile VLA
2
+
3
+ Training complete:5000 optimizer updates, seed42, globalbatch128, microbatch64 perGPU, accumulation1, gradientcheckpointingTrue, BF16/ZeRO2. Hardware:2 RTX PRO6000 Blackwell GPUs. Fixed mean90.56460638563993ms 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 dimensions27/8.
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.018865827471017838; 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/6246ccc57caf32da0e02547c3e3e1080894d4035/ant/profile_latency/OpenVLA/mean_rtx3090_s2222_100ep_v1
8
+ Training:https://wandb.ai/dongqianyu99-zhejiang-university/latency-sensitive-bench/runs/mean3090-ant-vla-blackwell-20260930
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/checkpoints/model.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:afb954569065450b9b80a86ca96e3898e3fe8d2f8c38a06f63bf3b11c41163b3
3
+ size 9785070315
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/collection_quality_report.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "task": "ant",
3
+ "teacher_checkpoint": "/home/ubuntu/lzj/mean-profiling/ant/teacher-publication/checkpoint.pth",
4
+ "teacher_sha256": "a7899b32beb9b2cfec1bcff85b285dddc243a73f1c322fafefae599df0f67b3b",
5
+ "episodes": 100,
6
+ "train_episodes": 90,
7
+ "val_episodes": 10,
8
+ "train_rows": 90000,
9
+ "val_rows": 10000,
10
+ "fixed_latency_ms": 90.56460638563993,
11
+ "action_horizon": 1,
12
+ "unique_seeds": 100,
13
+ "state_dim": 27
14
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/config.full.yaml ADDED
@@ -0,0 +1,438 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ framework:
2
+ name: QwenOFT
3
+ qwenvl:
4
+ base_vlm: /home/ubuntu/lzj/models/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: 8
10
+ state_dim: 27
11
+ action_hidden_dim: 2560
12
+ future_action_window_size: 0
13
+ past_action_window_size: 0
14
+ loss_type: l1
15
+ state_encoding: discretized_text
16
+ task_objective: null
17
+ action_horizon: 1
18
+ action_env_dim: 8
19
+ kv_memory:
20
+ enabled: false
21
+ window: 4
22
+ rollout_len: 8
23
+ packed_train: false
24
+ rebased_sink: true
25
+ datasets:
26
+ vla_data:
27
+ dataset_py: lerobot_datasets
28
+ include_state: true
29
+ data_root_dir: /home/ubuntu/lzj/mean-profiling/ant/data
30
+ data_mix: lerobot
31
+ eval_data_mix: lerobot__val
32
+ custom_mixtures_path: /home/ubuntu/lzj/mean-profiling/ant/data/_generated_mixtures/lerobot.json
33
+ action_type: discrete
34
+ sequential_step_sampling: false
35
+ eval_sequential_step_sampling: null
36
+ num_workers: 2
37
+ eval_num_workers: 2
38
+ prefetch_factor: 4
39
+ persistent_workers: true
40
+ pin_memory: true
41
+ shuffle: true
42
+ action_balance:
43
+ enabled: false
44
+ strategy: balanced_epoch
45
+ action_key: action_id
46
+ target_flap_fraction: 0.3
47
+ noop_id: 0
48
+ flap_id: 1
49
+ latency_curriculum:
50
+ enabled: false
51
+ strategy: exclusive
52
+ latencies: null
53
+ phase_steps: null
54
+ phase_distributions: null
55
+ new_latency_passes: 1.0
56
+ replay_passes: 0.25
57
+ target_total_passes: 2.0
58
+ final_equalization: true
59
+ step_budget_mode: auto
60
+ eval_at_phase_end: false
61
+ save_at_phase_end: false
62
+ computed_plan: null
63
+ per_device_batch_size: 64
64
+ load_all_data_for_training: true
65
+ num_obs_frames: 1
66
+ image_mode: single
67
+ prompt_mode: raw
68
+ stitch_grid:
69
+ - 2
70
+ - 2
71
+ obs_image_size: null
72
+ video_backend: torchvision_av
73
+ gymnasium_task_contract:
74
+ action_space:
75
+ dtype: float32
76
+ high:
77
+ - 1.0
78
+ - 1.0
79
+ - 1.0
80
+ - 1.0
81
+ - 1.0
82
+ - 1.0
83
+ - 1.0
84
+ - 1.0
85
+ labels:
86
+ - back_right_hip_torque
87
+ - back_right_ankle_torque
88
+ - front_left_hip_torque
89
+ - front_left_ankle_torque
90
+ - front_right_hip_torque
91
+ - front_right_ankle_torque
92
+ - back_left_hip_torque
93
+ - back_left_ankle_torque
94
+ low:
95
+ - -1.0
96
+ - -1.0
97
+ - -1.0
98
+ - -1.0
99
+ - -1.0
100
+ - -1.0
101
+ - -1.0
102
+ - -1.0
103
+ type: box
104
+ base_prompt: Make the Ant move forward as fast as possible without falling.
105
+ Predict eight continuous torques in [-1, 1] ordered as back right hip, back
106
+ right ankle, front left hip, front left ankle, front right hip, front right
107
+ ankle, back left hip, and back left ankle.
108
+ env_fps: 10.0
109
+ env_id: LatencyBench/AntContinuous-v0
110
+ frame_stack: 1
111
+ make_kwargs:
112
+ base_env_id: Ant-v4
113
+ base_make_kwargs:
114
+ exclude_current_positions_from_observation: true
115
+ use_contact_forces: false
116
+ render_mode: rgb_array
117
+ noop_action:
118
+ - 0.0
119
+ - 0.0
120
+ - 0.0
121
+ - 0.0
122
+ - 0.0
123
+ - 0.0
124
+ - 0.0
125
+ - 0.0
126
+ obs_fps: 10.0
127
+ registration_imports:
128
+ - latency_bench.envs.gymnasium_ant
129
+ state_space:
130
+ labels:
131
+ - torso_z
132
+ - torso_quaternion_w
133
+ - torso_quaternion_x
134
+ - torso_quaternion_y
135
+ - torso_quaternion_z
136
+ - front_left_hip_angle
137
+ - front_left_ankle_angle
138
+ - front_right_hip_angle
139
+ - front_right_ankle_angle
140
+ - back_left_hip_angle
141
+ - back_left_ankle_angle
142
+ - back_right_hip_angle
143
+ - back_right_ankle_angle
144
+ - torso_x_velocity
145
+ - torso_y_velocity
146
+ - torso_z_velocity
147
+ - torso_angular_velocity_x
148
+ - torso_angular_velocity_y
149
+ - torso_angular_velocity_z
150
+ - front_left_hip_angular_velocity
151
+ - front_left_ankle_angular_velocity
152
+ - front_right_hip_angular_velocity
153
+ - front_right_ankle_angular_velocity
154
+ - back_left_hip_angular_velocity
155
+ - back_left_ankle_angular_velocity
156
+ - back_right_hip_angular_velocity
157
+ - back_right_ankle_angular_velocity
158
+ task_name: ant_rgb_state
159
+ active_action_dim: 8
160
+ dataset:
161
+ source_hf: ''
162
+ config_name: null
163
+ source_subdir: null
164
+ converted_name: ant_rgb_state_profile_20260911T112232Z
165
+ single_source_hf: ''
166
+ mixed_source_hf: ''
167
+ single_converted_name: ant_rgb_state_profile_20260911T112232Z
168
+ mixed_converted_name: ant_rgb_state_profile_20260911T112232Z
169
+ single_latency_filter: null
170
+ mixed_latency_filter: null
171
+ force_download: false
172
+ setup_force: false
173
+ skip_verification: false
174
+ target_latency_unit: observation_steps
175
+ verify_rows: 200
176
+ max_episodes: null
177
+ episodes_per_latency: null
178
+ latency_filter: null
179
+ debug_subset:
180
+ enabled: false
181
+ max_episodes: 5
182
+ suffix: debug
183
+ base_model:
184
+ repo_id: StarVLA/Qwen3-VL-4B-Instruct-Action
185
+ initialization:
186
+ checkpoint_local_dir: null
187
+ checkpoint_hf_repo_id: null
188
+ checkpoint_filename: null
189
+ trainer:
190
+ max_train_steps: 5000
191
+ num_warmup_steps: 100
192
+ save_interval: 500
193
+ eval_interval: 500
194
+ eval_num_batches: 200
195
+ per_latency_eval_num_batches: null
196
+ eval_action_classification: false
197
+ eval_action_classification_interval: null
198
+ cc_f1_tolerance: 1
199
+ learning_rate:
200
+ base: 2.0e-05
201
+ qwen_vl_interface: 1.0e-05
202
+ action_model: 0.0001
203
+ lr_scheduler_type: cosine_with_min_lr
204
+ scheduler_specific_kwargs:
205
+ min_lr: 1.0e-06
206
+ freeze_modules: ''
207
+ freeze_vit: false
208
+ freeze_tied_embedding: false
209
+ freeze_llm_layers: []
210
+ loss_scale:
211
+ vla: 1.0
212
+ vlm: 0.1
213
+ max_grad_norm: 1.0
214
+ weight_decay: 0.0
215
+ logging_frequency: 1
216
+ profile_timing:
217
+ enabled: false
218
+ log_interval: 10
219
+ gradient_clipping: 1.0
220
+ gradient_accumulation_steps: 1
221
+ distributed_backend: deepspeed
222
+ is_resume: false
223
+ pretrained_checkpoint: null
224
+ resume_step: 0
225
+ reload_modules: null
226
+ optimizer:
227
+ name: AdamW
228
+ betas:
229
+ - 0.9
230
+ - 0.95
231
+ eps: 1.0e-08
232
+ weight_decay: 1.0e-08
233
+ fused: true
234
+ save_format: pt
235
+ workspace_dir: WORKSPACE_DIR
236
+ run_root_dir: /home/ubuntu/lzj/mean-profiling/ant/vla
237
+ seed: 42
238
+ wandb_entity: dongqianyu99-zhejiang-university
239
+ wandb_project: latency-sensitive-bench
240
+ auth:
241
+ env_file: null
242
+ hf_token_env: HF_TOKEN
243
+ wandb_api_key_env: WANDB_API_KEY
244
+ paths:
245
+ run_root_dir: /mnt/results/latency-sensitive-bench/profile-latency-new/ant/20260911T112232Z-ant/vla
246
+ dataset_local_dir: /mnt/results/latency-sensitive-bench/profile-latency-new/ant/20260911T112232Z-ant/converted
247
+ dataset_cache_dir: null
248
+ base_model_dir: /home/ubuntu/lzj/models/Qwen3-VL-4B-Instruct
249
+ accelerate_config: starVLA/config/deepseeds/deepspeed_zero2_cpu_offload.yaml
250
+ launch:
251
+ use_accelerate: true
252
+ gpus: null
253
+ num_processes: 1
254
+ dry_run: false
255
+ conda:
256
+ enabled: true
257
+ env_name: null
258
+ rl_games:
259
+ model_alias: openvla
260
+ env_eval:
261
+ image_size: 224
262
+ frameskip: 1
263
+ image_transform: raw_rgb
264
+ prompt_mode: raw
265
+ ghost_trail:
266
+ history_frames: 5
267
+ gamma: 1.3
268
+ min_alpha: 35
269
+ scroll_px_per_step: 4.0
270
+ ground_fraction: 0.22
271
+ seed: 42
272
+ fixed_episode_seeds: true
273
+ latency_seed_stride: 0
274
+ task_seed_stride: 0
275
+ task_description: ''
276
+ eval_parallel_envs: 5
277
+ action_chunk_execution:
278
+ enabled: false
279
+ chunk_size: null
280
+ enabled: false
281
+ gymnasium:
282
+ make_kwargs: {}
283
+ mid_train:
284
+ enabled: false
285
+ interval_steps: 100
286
+ latencies:
287
+ - 0
288
+ num_episodes: 5
289
+ max_steps_per_episode: 3600
290
+ post_train:
291
+ enabled: false
292
+ latencies:
293
+ - 0
294
+ - 1
295
+ - 2
296
+ - 3
297
+ - 4
298
+ num_episodes: 5
299
+ max_steps_per_episode: 3600
300
+ eval_backend: latency_bench
301
+ distributed_mode: none
302
+ vectorized:
303
+ enabled: false
304
+ batch_size: 1
305
+ latency:
306
+ prompt_map_path: null
307
+ mode: single
308
+ values:
309
+ - 1
310
+ - 2
311
+ task: gymnasium
312
+ gymnasium:
313
+ task_contract:
314
+ action_space:
315
+ dtype: float32
316
+ high:
317
+ - 1.0
318
+ - 1.0
319
+ - 1.0
320
+ - 1.0
321
+ - 1.0
322
+ - 1.0
323
+ - 1.0
324
+ - 1.0
325
+ labels:
326
+ - back_right_hip_torque
327
+ - back_right_ankle_torque
328
+ - front_left_hip_torque
329
+ - front_left_ankle_torque
330
+ - front_right_hip_torque
331
+ - front_right_ankle_torque
332
+ - back_left_hip_torque
333
+ - back_left_ankle_torque
334
+ low:
335
+ - -1.0
336
+ - -1.0
337
+ - -1.0
338
+ - -1.0
339
+ - -1.0
340
+ - -1.0
341
+ - -1.0
342
+ - -1.0
343
+ type: box
344
+ base_prompt: Make the Ant move forward as fast as possible without falling.
345
+ Predict eight continuous torques in [-1, 1] ordered as back right hip, back
346
+ right ankle, front left hip, front left ankle, front right hip, front right
347
+ ankle, back left hip, and back left ankle.
348
+ env_fps: 10.0
349
+ env_id: LatencyBench/AntContinuous-v0
350
+ frame_stack: 1
351
+ make_kwargs:
352
+ base_env_id: Ant-v4
353
+ base_make_kwargs:
354
+ exclude_current_positions_from_observation: true
355
+ use_contact_forces: false
356
+ render_mode: rgb_array
357
+ noop_action:
358
+ - 0.0
359
+ - 0.0
360
+ - 0.0
361
+ - 0.0
362
+ - 0.0
363
+ - 0.0
364
+ - 0.0
365
+ - 0.0
366
+ obs_fps: 10.0
367
+ registration_imports:
368
+ - latency_bench.envs.gymnasium_ant
369
+ state_space:
370
+ labels:
371
+ - torso_z
372
+ - torso_quaternion_w
373
+ - torso_quaternion_x
374
+ - torso_quaternion_y
375
+ - torso_quaternion_z
376
+ - front_left_hip_angle
377
+ - front_left_ankle_angle
378
+ - front_right_hip_angle
379
+ - front_right_ankle_angle
380
+ - back_left_hip_angle
381
+ - back_left_ankle_angle
382
+ - back_right_hip_angle
383
+ - back_right_ankle_angle
384
+ - torso_x_velocity
385
+ - torso_y_velocity
386
+ - torso_z_velocity
387
+ - torso_angular_velocity_x
388
+ - torso_angular_velocity_y
389
+ - torso_angular_velocity_z
390
+ - front_left_hip_angular_velocity
391
+ - front_left_ankle_angular_velocity
392
+ - front_right_hip_angular_velocity
393
+ - front_right_ankle_angular_velocity
394
+ - back_left_hip_angular_velocity
395
+ - back_left_ankle_angular_velocity
396
+ - back_right_hip_angular_velocity
397
+ - back_right_ankle_angular_velocity
398
+ task_name: ant_rgb_state
399
+ task_name: ant_rgb_state
400
+ initialization_mode: scratch
401
+ action_carrier: native
402
+ model: openvla
403
+ env: gymnasium
404
+ init: scratch
405
+ mode: single
406
+ checkpoint:
407
+ load: none
408
+ hf_repo_id: null
409
+ save_best_model: false
410
+ save_final_model: true
411
+ save_pt_file: true
412
+ save_training_state: true
413
+ save_safetensors_file: false
414
+ local:
415
+ keep_last_n: 2
416
+ sync:
417
+ enabled: false
418
+ repo_id: null
419
+ keep_last_n: 0
420
+ sync_every_n_checkpoints: 1
421
+ resume_policy: local_latest
422
+ run_id: ant-qwenoft-mean-3090-s42
423
+ output_dir: /home/ubuntu/lzj/mean-profiling/ant/vla/ant-qwenoft-mean-3090-s42
424
+ config_yaml: /home/ubuntu/lzj/mean-profiling/ant/vla.yaml
425
+ is_debug: false
426
+ version_id: '0.21'
427
+ wandb_name: ant-qwenoft-mean-3090-s42
428
+ wandb_group: mean-profiling-3090-blackwell
429
+ training_latency_condition: mean-profile
430
+ wandb_tags:
431
+ - ant
432
+ - qwenoft
433
+ - mean-profile
434
+ - h1
435
+ - blackwell
436
+ - throughput-priority
437
+ - micro64-global128
438
+ - gradient-checkpointing
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/config.yaml ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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: 8
10
+ state_dim: 27
11
+ action_hidden_dim: 2560
12
+ future_action_window_size: 0
13
+ past_action_window_size: 0
14
+ loss_type: l1
15
+ state_encoding: discretized_text
16
+ task_objective: null
17
+ action_horizon: 1
18
+ action_env_dim: 8
19
+ kv_memory:
20
+ enabled: false
21
+ window: 4
22
+ rollout_len: 8
23
+ packed_train: false
24
+ rebased_sink: true
25
+ version_id: '0.21'
26
+ datasets:
27
+ vla_data:
28
+ include_state: true
29
+ obs_image_size: null
30
+ image_mode: single
31
+ stitch_grid:
32
+ - 2
33
+ - 2
34
+ num_obs_frames: 1
35
+ trainer:
36
+ pretrained_checkpoint: null
37
+ backbone:
38
+ repo_id: Qwen/Qwen3-VL-4B-Instruct
39
+ revision: ebb281ec70b05090aa6165b016eac8ec08e71b17
40
+ allow_patterns:
41
+ - '*.json'
42
+ - '*.safetensors'
43
+ - '*.txt'
44
+ - '*.jinja'
45
+ - '*.model'
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/dataset_statistics.json ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "new_embodiment": {
3
+ "action": {
4
+ "mean": [
5
+ -0.19492579996585846,
6
+ 0.04469946771860123,
7
+ 0.10307926684617996,
8
+ 0.06500687450170517,
9
+ -0.07598323374986649,
10
+ 0.10469535738229752,
11
+ -0.027709610760211945,
12
+ 0.1776909977197647
13
+ ],
14
+ "std": [
15
+ 0.2916632294654846,
16
+ 0.18000850081443787,
17
+ 0.3355802595615387,
18
+ 0.2449917495250702,
19
+ 0.3372354805469513,
20
+ 0.055361147969961166,
21
+ 0.29043376445770264,
22
+ 0.06610933691263199
23
+ ],
24
+ "max": [
25
+ 0.6329135894775391,
26
+ 0.6100203394889832,
27
+ 1.0,
28
+ 0.7914915680885315,
29
+ 0.7937509417533875,
30
+ 0.4119175970554352,
31
+ 0.8763647675514221,
32
+ 0.4558652639389038
33
+ ],
34
+ "min": [
35
+ -1.0,
36
+ -0.5158019661903381,
37
+ -0.7942351698875427,
38
+ -0.6549583673477173,
39
+ -0.7899840474128723,
40
+ -0.1914844959974289,
41
+ -0.8830769658088684,
42
+ -0.25145918130874634
43
+ ],
44
+ "q01": [
45
+ -0.7246493166685104,
46
+ -0.36842879801988604,
47
+ -0.4745602348446846,
48
+ -0.32548967510461807,
49
+ -0.6590064060688019,
50
+ -0.00425734419375658,
51
+ -0.667693349123001,
52
+ 0.03753467075526715
53
+ ],
54
+ "q99": [
55
+ 0.38912478089332564,
56
+ 0.4704011854529379,
57
+ 0.9895162123441696,
58
+ 0.6802999329566952,
59
+ 0.6482488948106765,
60
+ 0.26210771769285157,
61
+ 0.4697745823860167,
62
+ 0.32386209577322006
63
+ ],
64
+ "mask": [
65
+ true,
66
+ true,
67
+ true,
68
+ true,
69
+ true,
70
+ true,
71
+ true,
72
+ true
73
+ ]
74
+ },
75
+ "state": {
76
+ "mean": [
77
+ -0.03927938640117645,
78
+ 0.873625636100769,
79
+ -0.2643051743507385,
80
+ -0.5193795561790466,
81
+ -0.03129394352436066,
82
+ 0.37781062722206116,
83
+ 0.3676287829875946,
84
+ -0.12228213250637054,
85
+ 0.10376925021409988,
86
+ 0.0348481647670269,
87
+ 0.10585025697946548,
88
+ -0.3591102361679077,
89
+ 0.4178043007850647,
90
+ 0.3299199938774109,
91
+ -0.09238386154174805,
92
+ -0.07736567407846451,
93
+ 0.08624251186847687,
94
+ -0.0959310308098793,
95
+ 0.05461423099040985,
96
+ -0.14337068796157837,
97
+ -0.10180454701185226,
98
+ 0.04959503188729286,
99
+ 0.18128058314323425,
100
+ -0.015483468770980835,
101
+ 0.28967317938804626,
102
+ 0.2775602340698242,
103
+ -0.33862781524658203
104
+ ],
105
+ "std": [
106
+ 0.24292634427547455,
107
+ 0.06530473381280856,
108
+ 0.13237309455871582,
109
+ 0.13348814845085144,
110
+ 0.15209674835205078,
111
+ 0.4691879153251648,
112
+ 0.39366209506988525,
113
+ 0.6651913523674011,
114
+ 0.08553778380155563,
115
+ 0.6054338812828064,
116
+ 0.08797592669725418,
117
+ 0.48128432035446167,
118
+ 0.33567723631858826,
119
+ 0.2170608788728714,
120
+ 0.24111506342887878,
121
+ 0.35090306401252747,
122
+ 0.2857387363910675,
123
+ 0.20407065749168396,
124
+ 0.22814442217350006,
125
+ 0.3093811273574829,
126
+ 0.3431510031223297,
127
+ 0.41457003355026245,
128
+ 0.05459607020020485,
129
+ 0.33612897992134094,
130
+ 0.05801063030958176,
131
+ 0.34881407022476196,
132
+ 0.31408628821372986
133
+ ],
134
+ "max": [
135
+ 1.0,
136
+ 1.0,
137
+ 1.0,
138
+ 1.0,
139
+ 1.0,
140
+ 1.0,
141
+ 1.0,
142
+ 1.0,
143
+ 1.0,
144
+ 1.0,
145
+ 1.0,
146
+ 1.0,
147
+ 1.0,
148
+ 1.0,
149
+ 1.0,
150
+ 1.0,
151
+ 1.0,
152
+ 1.0,
153
+ 1.0,
154
+ 1.0,
155
+ 1.0,
156
+ 1.0,
157
+ 1.0,
158
+ 1.0,
159
+ 1.0,
160
+ 1.0,
161
+ 1.0
162
+ ],
163
+ "min": [
164
+ -1.0,
165
+ -1.0,
166
+ -1.0,
167
+ -1.0,
168
+ -1.0,
169
+ -1.0,
170
+ -1.0,
171
+ -1.0,
172
+ -1.0,
173
+ -1.0,
174
+ -1.0,
175
+ -1.0,
176
+ -1.0,
177
+ -1.0,
178
+ -1.0,
179
+ -1.0,
180
+ -1.0,
181
+ -1.0,
182
+ -1.0,
183
+ -1.0,
184
+ -1.0,
185
+ -1.0,
186
+ -1.0,
187
+ -1.0,
188
+ -1.0,
189
+ -1.0,
190
+ -1.0
191
+ ],
192
+ "q01": [
193
+ -0.518711000084877,
194
+ 0.7832470858097076,
195
+ -0.48747050642967227,
196
+ -0.8287142354249954,
197
+ -0.3970690089464188,
198
+ -0.7996528524160386,
199
+ -0.18211212992668152,
200
+ -0.9498927110433578,
201
+ 0.05799898505210877,
202
+ -0.9213203591108322,
203
+ 0.06377534270286564,
204
+ -0.9172824740409851,
205
+ -0.14945583879947663,
206
+ -0.47512653708457947,
207
+ -0.5667413014173508,
208
+ -0.720048052072525,
209
+ -0.6468206322193146,
210
+ -0.4755307936668396,
211
+ -0.4702010858058929,
212
+ -0.6532424187660217,
213
+ -0.637979581952095,
214
+ -0.8247658777236938,
215
+ 0.17336984157562255,
216
+ -0.7672037160396576,
217
+ 0.28981017947196963,
218
+ -0.6314861965179444,
219
+ -0.8276125568151474
220
+ ],
221
+ "q99": [
222
+ 0.5284208941459655,
223
+ 0.8995754766464233,
224
+ 0.07895282626152018,
225
+ -0.29791240513324774,
226
+ 0.3613759696483611,
227
+ 0.9232616770267487,
228
+ 0.9520751488208771,
229
+ 0.944451036453247,
230
+ 0.11151877999305723,
231
+ 0.8851249778270721,
232
+ 0.11331057786941529,
233
+ 0.7722350835800162,
234
+ 0.9076120913028717,
235
+ 0.7570547771453847,
236
+ 0.543445667028426,
237
+ 0.718429600000381,
238
+ 0.6114705646038047,
239
+ 0.3634060764312739,
240
+ 0.6103977251052853,
241
+ 0.6279066145420074,
242
+ 0.8004330182075499,
243
+ 0.8364576077461242,
244
+ 0.1844600486755371,
245
+ 0.5733257675170897,
246
+ 0.2910482907295227,
247
+ 0.8056755244731904,
248
+ 0.2977444970607755
249
+ ]
250
+ },
251
+ "num_transitions": 90000,
252
+ "num_trajectories": 90
253
+ }
254
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/dataset_statistics_eval.json ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "new_embodiment": {
3
+ "action": {
4
+ "mean": [
5
+ -0.194865420460701,
6
+ 0.045133840292692184,
7
+ 0.10328157991170883,
8
+ 0.06766745448112488,
9
+ -0.07633885741233826,
10
+ 0.10476952791213989,
11
+ -0.02807306870818138,
12
+ 0.17962458729743958
13
+ ],
14
+ "std": [
15
+ 0.2925890386104584,
16
+ 0.179392471909523,
17
+ 0.33783403038978577,
18
+ 0.24546940624713898,
19
+ 0.33822667598724365,
20
+ 0.052730318158864975,
21
+ 0.28934720158576965,
22
+ 0.06433656811714172
23
+ ],
24
+ "max": [
25
+ 0.5322136878967285,
26
+ 0.555697500705719,
27
+ 1.0,
28
+ 0.7869182229042053,
29
+ 0.8116569519042969,
30
+ 0.3534770607948303,
31
+ 0.7703150510787964,
32
+ 0.440703809261322
33
+ ],
34
+ "min": [
35
+ -1.0,
36
+ -0.47031596302986145,
37
+ -0.730529248714447,
38
+ -0.6560564637184143,
39
+ -0.8157420754432678,
40
+ -0.052262552082538605,
41
+ -0.8346825242042542,
42
+ -0.025107912719249725
43
+ ],
44
+ "q01": [
45
+ -0.7260830360651016,
46
+ -0.36782372266054153,
47
+ -0.47317668825387954,
48
+ -0.31557414084672925,
49
+ -0.66000783264637,
50
+ 0.0017754910979419962,
51
+ -0.6687433636188507,
52
+ 0.044861558452248575
53
+ ],
54
+ "q99": [
55
+ 0.38754730314016345,
56
+ 0.46464609265327456,
57
+ 1.0,
58
+ 0.6821840578317643,
59
+ 0.6534658980369571,
60
+ 0.2567682746052742,
61
+ 0.466521698832512,
62
+ 0.3254868069291115
63
+ ],
64
+ "mask": [
65
+ true,
66
+ true,
67
+ true,
68
+ true,
69
+ true,
70
+ true,
71
+ true,
72
+ true
73
+ ]
74
+ },
75
+ "state": {
76
+ "mean": [
77
+ -0.03446132689714432,
78
+ 0.8770178556442261,
79
+ -0.26813629269599915,
80
+ -0.5243244767189026,
81
+ -0.036261554807424545,
82
+ 0.38102391362190247,
83
+ 0.368524432182312,
84
+ -0.12297961115837097,
85
+ 0.10743273794651031,
86
+ 0.03137503191828728,
87
+ 0.10943524539470673,
88
+ -0.3544634282588959,
89
+ 0.4190467298030853,
90
+ 0.3441190719604492,
91
+ -0.09693243354558945,
92
+ -0.07695353031158447,
93
+ 0.08566370606422424,
94
+ -0.09651442617177963,
95
+ 0.054840974509716034,
96
+ -0.14340537786483765,
97
+ -0.1022859439253807,
98
+ 0.04956989362835884,
99
+ 0.18135188519954681,
100
+ -0.015385560691356659,
101
+ 0.2896140515804291,
102
+ 0.2777324914932251,
103
+ -0.3387359380722046
104
+ ],
105
+ "std": [
106
+ 0.23964886367321014,
107
+ 0.023951832205056853,
108
+ 0.11455036699771881,
109
+ 0.1093541756272316,
110
+ 0.14902757108211517,
111
+ 0.46400782465934753,
112
+ 0.39322152733802795,
113
+ 0.666404664516449,
114
+ 0.06292039155960083,
115
+ 0.6054039001464844,
116
+ 0.06391128897666931,
117
+ 0.48054811358451843,
118
+ 0.33541467785835266,
119
+ 0.20528468489646912,
120
+ 0.23731346428394318,
121
+ 0.3526037931442261,
122
+ 0.28841397166252136,
123
+ 0.2061758041381836,
124
+ 0.22504228353500366,
125
+ 0.3090031147003174,
126
+ 0.3419175148010254,
127
+ 0.4147181808948517,
128
+ 0.054758649319410324,
129
+ 0.3359338939189911,
130
+ 0.05688738450407982,
131
+ 0.35167375206947327,
132
+ 0.3148670196533203
133
+ ],
134
+ "max": [
135
+ 0.8509142398834229,
136
+ 0.946937084197998,
137
+ 0.28078794479370117,
138
+ 0.13082659244537354,
139
+ 0.7544276714324951,
140
+ 0.9886277914047241,
141
+ 0.9866607189178467,
142
+ 0.9863904714584351,
143
+ 0.9886200428009033,
144
+ 0.9180272817611694,
145
+ 0.958978533744812,
146
+ 0.976783037185669,
147
+ 0.994597315788269,
148
+ 0.9207459688186646,
149
+ 0.9506415128707886,
150
+ 0.9698106050491333,
151
+ 0.8268996477127075,
152
+ 0.5188288688659668,
153
+ 0.899605393409729,
154
+ 0.8091031312942505,
155
+ 0.9923845529556274,
156
+ 0.9708276987075806,
157
+ 0.9690662622451782,
158
+ 0.8003939390182495,
159
+ 1.0,
160
+ 0.9442052841186523,
161
+ 0.9767690896987915
162
+ ],
163
+ "min": [
164
+ -0.6200818419456482,
165
+ 0.5183085203170776,
166
+ -0.7695543766021729,
167
+ -0.9485529661178589,
168
+ -0.8429219722747803,
169
+ -0.9506317377090454,
170
+ -0.9622589349746704,
171
+ -1.0,
172
+ -0.9714068174362183,
173
+ -1.0,
174
+ -0.9771743416786194,
175
+ -0.9749248623847961,
176
+ -1.0,
177
+ -0.7751631736755371,
178
+ -0.8879467844963074,
179
+ -1.0,
180
+ -1.0,
181
+ -0.7477306723594666,
182
+ -0.7433239221572876,
183
+ -0.8804571628570557,
184
+ -0.9065881967544556,
185
+ -0.9710677862167358,
186
+ -0.9849591851234436,
187
+ -0.9246947765350342,
188
+ -0.9404064416885376,
189
+ -0.956570029258728,
190
+ -1.0
191
+ ],
192
+ "q01": [
193
+ -0.5038023978471756,
194
+ 0.7942106568813324,
195
+ -0.478440306186676,
196
+ -0.8267061966657638,
197
+ -0.39669507682323457,
198
+ -0.7862418442964554,
199
+ -0.1804919111728668,
200
+ -0.9492776465415954,
201
+ 0.10735819339752198,
202
+ -0.919443871974945,
203
+ 0.11226175785064697,
204
+ -0.9184101295471191,
205
+ -0.1500501948595047,
206
+ -0.34228765964508057,
207
+ -0.5650507682561874,
208
+ -0.7155984926223755,
209
+ -0.6520336997509003,
210
+ -0.4805710732936859,
211
+ -0.46580726146698,
212
+ -0.6449919211864471,
213
+ -0.6242685246467591,
214
+ -0.819073212146759,
215
+ 0.17577075600624084,
216
+ -0.7636688566207885,
217
+ 0.28973553419113157,
218
+ -0.6239250099658966,
219
+ -0.8301586848497391
220
+ ],
221
+ "q99": [
222
+ 0.531941732168198,
223
+ 0.8995999598503113,
224
+ 0.04719418048858646,
225
+ -0.3141447216272354,
226
+ 0.33111580729484597,
227
+ 0.9231463372707368,
228
+ 0.9524696123600006,
229
+ 0.9444611060619355,
230
+ 0.11135421514511108,
231
+ 0.8837961864471436,
232
+ 0.11332787156105041,
233
+ 0.7487831759452822,
234
+ 0.9076400446891785,
235
+ 0.7634504711627961,
236
+ 0.5362399804592136,
237
+ 0.7234879946708682,
238
+ 0.6047599220275879,
239
+ 0.36570024728775025,
240
+ 0.6087638485431678,
241
+ 0.6343572854995728,
242
+ 0.8117764890193939,
243
+ 0.836361961364746,
244
+ 0.18426278471946717,
245
+ 0.5724071526527406,
246
+ 0.2910631883144379,
247
+ 0.7999724817276002,
248
+ 0.29399525642395047
249
+ ]
250
+ },
251
+ "num_transitions": 10000,
252
+ "num_trajectories": 10
253
+ }
254
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/files.sha256.json ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "README.md": {
3
+ "bytes": 1131,
4
+ "sha256": "b8d91b05b9719d73549c3e71fb89b846804e31e9870616d346875796a961a975"
5
+ },
6
+ "checkpoints/model.pt": {
7
+ "bytes": 9785070315,
8
+ "sha256": "afb954569065450b9b80a86ca96e3898e3fe8d2f8c38a06f63bf3b11c41163b3"
9
+ },
10
+ "collection_quality_report.json": {
11
+ "bytes": 421,
12
+ "sha256": "3a21eb8ce207f265fee8197c317c6f93013e10dd96dc32f5a702b1b658773de5"
13
+ },
14
+ "config.full.yaml": {
15
+ "bytes": 11309,
16
+ "sha256": "936acd2c14d8aa4c4ab817eab4acf046827b879f12b6454e4a1ae7e1ef69136c"
17
+ },
18
+ "config.yaml": {
19
+ "bytes": 965,
20
+ "sha256": "dfda64a8e3d18e6ceb8c0fdc686915cd897f0df78ea24b100b58a468335ee7c6"
21
+ },
22
+ "dataset_statistics.json": {
23
+ "bytes": 5727,
24
+ "sha256": "8808223c11ba20721109750a91a23cc2c81b5b92d8e48320edfe5f22722b3389"
25
+ },
26
+ "dataset_statistics_eval.json": {
27
+ "bytes": 6413,
28
+ "sha256": "85ee7976398bfc0cf7f75e6f3c9528f9fa872a77dcab0bb849dc3920683e973a"
29
+ },
30
+ "latency_prompt_map.json": {
31
+ "bytes": 497,
32
+ "sha256": "8be52633a13e9032392595f2e6d637a98f539dacb6757e1dec59e65ffc2074e3"
33
+ },
34
+ "launch_audit.json": {
35
+ "bytes": 1786,
36
+ "sha256": "262ce08821f1c2261b8567eed44c53818fba6d75cfe967c35e61fc2ddee42070"
37
+ },
38
+ "manifest.json": {
39
+ "bytes": 6710,
40
+ "sha256": "ec82945da958d7e29d172928b136177d2b0e23612b62889f64b475446cd655c4"
41
+ },
42
+ "profile.source.json": {
43
+ "bytes": 2148,
44
+ "sha256": "0e49f72ec05ac600a54e07bbca5af3143708444d606167f33fa21788a9fefe50"
45
+ },
46
+ "provenance.json": {
47
+ "bytes": 6152,
48
+ "sha256": "ddb044cd3a9ea15f9c07c2370df31f15c4ffda0236fee45b0935a99aba79720d"
49
+ },
50
+ "startup_verified.json": {
51
+ "bytes": 1553,
52
+ "sha256": "f7ade30c273f14972b8deacf42b9f0ff2bad29bb0cefffc1b131977a9933d3b5"
53
+ },
54
+ "summary.jsonl": {
55
+ "bytes": 159,
56
+ "sha256": "2905acb79c5fffb33fd4689d87af3bd05d74f4184b9d36ff3eae0c64deecd930"
57
+ },
58
+ "task_contract.json": {
59
+ "bytes": 2298,
60
+ "sha256": "a874a91173a6579750761e4aa65d8daecc167dc9a2d0c58041813f83d589c959"
61
+ },
62
+ "training-code/QWen3.py": {
63
+ "bytes": 17586,
64
+ "sha256": "5fe76fc98eaa84e215c3c25c11326815499d2a4b0683c6ab95382b881f535370"
65
+ },
66
+ "training-code/QwenOFT.py": {
67
+ "bytes": 62854,
68
+ "sha256": "acad1b0dcde6e9fed6e2912b6534db0f8e33a74ec7f6630bbf1b0161fc298b0d"
69
+ },
70
+ "training-code/datasets.py": {
71
+ "bytes": 154347,
72
+ "sha256": "33e22556471128a2bad18a1ef8b84c399847b4d98b2979d785d139de0cdf36df"
73
+ },
74
+ "training-code/doom-mean-reset.patch": {
75
+ "bytes": 9569,
76
+ "sha256": "4113989e16c547bd1f02f5f10f285c4d511be3a3c18d10a0aa3316bd8ff3175d"
77
+ },
78
+ "training-code/gym-language-contract.patch": {
79
+ "bytes": 688,
80
+ "sha256": "9f5dee2c5b52d5507aff27373a8cf6def4959361d2c5264d5079b416f28a4c49"
81
+ },
82
+ "training-code/loader-spawn-cache.patch": {
83
+ "bytes": 711,
84
+ "sha256": "24532ce84e5aa9aef381493fe699b4bb4987afd954422191ae852b0f5eb5b2cd"
85
+ },
86
+ "training-code/loader-spawn-test.patch": {
87
+ "bytes": 1500,
88
+ "sha256": "a36db15b13d68defeecb75ddc9efa1ac202d9aa0a40f66ba5baaee2dcaef21f1"
89
+ },
90
+ "training-code/mean-profile-preparation.patch": {
91
+ "bytes": 7053,
92
+ "sha256": "de5fe558376c98515a1cfb186ffdc09b618acc207eb8a5f667b0cbb303767c09"
93
+ },
94
+ "training-code/train_starvla.py": {
95
+ "bytes": 125159,
96
+ "sha256": "f872648f941cec272af724772e9d6fc3721e9ed6eaf0c8683ab285eb9396212e"
97
+ },
98
+ "training-code/trainer_tools.py": {
99
+ "bytes": 29144,
100
+ "sha256": "9a48bdfb99b405b03fececfc1e051d3ebb179eb423c15141f02b9be62e2e1791"
101
+ },
102
+ "training-code/worker_context.py": {
103
+ "bytes": 2279,
104
+ "sha256": "8493631104269f0630655ee2faae592a84ebfb3119aca142c8ac2701f5214464"
105
+ },
106
+ "training_results.json": {
107
+ "bytes": 1762,
108
+ "sha256": "8c3b350880c1fd9996fa4a77a9c81c8e9a4c3d0fd0c96ef5272dc88669bbba63"
109
+ }
110
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/latency_prompt_map.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "1": {
3
+ "prompt": "Make the Ant move forward as fast as possible without falling. Predict eight continuous torques in [-1, 1] ordered as back right hip, back right ankle, front left hip, front left ankle, front right hip, front right ankle, back left hip, and back left ankle. Current action latency is 1 raw frames (100.00 ms). The environment runs at 10 FPS and observations are emitted at 10 FPS. Choose the best next action.",
4
+ "latency_raw_frames": 1,
5
+ "latency_ms": 100.0
6
+ }
7
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/launch_audit.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "reference": "/home/ubuntu/lzj/mean-profiling/reference/checkpoint-configs/latency-aware/ant/vla/starvla-qwenoft-h1/ant_rgb_state_profile_20260911T112232Z_openvla_native_sft_5k/config.full.yaml",
3
+ "reference_sha256": "1abb758e750efa660e3a4e949a974eedf19e6d8e49cc5ef98ec29c6da453c56e",
4
+ "scientific_trainer_fields_equal": [
5
+ "max_train_steps",
6
+ "num_warmup_steps",
7
+ "learning_rate",
8
+ "lr_scheduler_type",
9
+ "scheduler_specific_kwargs",
10
+ "optimizer",
11
+ "eval_interval",
12
+ "eval_num_batches",
13
+ "save_interval",
14
+ "loss_scale"
15
+ ],
16
+ "seed": 42,
17
+ "horizon": 1,
18
+ "state_dim": 27,
19
+ "action_dim": 8,
20
+ "global_batch": 128,
21
+ "micro_per_gpu": 64,
22
+ "accumulation": 1,
23
+ "gradient_checkpointing": true,
24
+ "formal_optimizer_updates": 5000,
25
+ "preflight_updates_discarded": 20,
26
+ "post_train_rollout_eval": false,
27
+ "data_revision": "6246ccc57caf32da0e02547c3e3e1080894d4035",
28
+ "runtime_save_training_state": true,
29
+ "runtime_keep_last_recovery_states": 2,
30
+ "profile_timing_same_as_reference": false,
31
+ "reference_global_batch": 16,
32
+ "profile_timing_enabled": false,
33
+ "user_authorized_scientific_difference": "Prioritize two GPU utilization; user explicitly requests Flappy micro64*2/accum1/global128, referenceglobal16 no longerbinding",
34
+ "failed_initial_preflight": "Missing requiredwandb_tags, noformaloptimizerupdates",
35
+ "metadata_repair": "Explicitwandb_tags added",
36
+ "optimizer_budget": 5000,
37
+ "previous_runtime_failure": "32/accum2 CUDA OOM atsecond optimizer update after1testupdate",
38
+ "runtime_adjustment": "Enableexistingnon-reentrantgradientcheckpointing;64*2*accum1/global128, othermath/scientificbudgets maintained",
39
+ "startup_verified": true,
40
+ "formal_startup_evidence": "/home/ubuntu/lzj/mean-profiling/ant/vla.startup-verified.json"
41
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/manifest.json ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "dataset_name": "lerobot",
3
+ "env_name": "ant_rgb_state",
4
+ "episodes": 90,
5
+ "frames": 90000,
6
+ "task_prompts": [
7
+ "Make the Ant move forward as fast as possible without falling. Predict eight continuous torques in [-1, 1] ordered as back right hip, back right ankle, front left hip, front left ankle, front right hip, front right ankle, back left hip, and back left ankle. Current action latency is 1 raw frames (100.00 ms). The environment runs at 10 FPS and observations are emitted at 10 FPS. Choose the best next action."
8
+ ],
9
+ "format": "lerobot_v2_image_parquet",
10
+ "source": "/home/ubuntu/lzj/mean-profiling/ant/data/raw",
11
+ "integration_name": "gymnasium",
12
+ "task_name": "ant_rgb_state",
13
+ "action_layout": "gymnasium_continuous_v1",
14
+ "action_labels": [
15
+ "back_right_hip_torque",
16
+ "back_right_ankle_torque",
17
+ "front_left_hip_torque",
18
+ "front_left_ankle_torque",
19
+ "front_right_hip_torque",
20
+ "front_right_ankle_torque",
21
+ "back_left_hip_torque",
22
+ "back_left_ankle_torque"
23
+ ],
24
+ "carrier_action_labels": [
25
+ "back_right_hip_torque",
26
+ "back_right_ankle_torque",
27
+ "front_left_hip_torque",
28
+ "front_left_ankle_torque",
29
+ "front_right_hip_torque",
30
+ "front_right_ankle_torque",
31
+ "back_left_hip_torque",
32
+ "back_left_ankle_torque"
33
+ ],
34
+ "action_dim": 8,
35
+ "active_action_dim": 8,
36
+ "action_carrier": "native",
37
+ "rows_unit": "decision_step",
38
+ "obs_fps": 10.0,
39
+ "obs_stride_raw_frames": 1,
40
+ "uses_state": true,
41
+ "state_dim": 27,
42
+ "state_labels": [
43
+ "torso_z",
44
+ "torso_quaternion_w",
45
+ "torso_quaternion_x",
46
+ "torso_quaternion_y",
47
+ "torso_quaternion_z",
48
+ "front_left_hip_angle",
49
+ "front_left_ankle_angle",
50
+ "front_right_hip_angle",
51
+ "front_right_ankle_angle",
52
+ "back_left_hip_angle",
53
+ "back_left_ankle_angle",
54
+ "back_right_hip_angle",
55
+ "back_right_ankle_angle",
56
+ "torso_x_velocity",
57
+ "torso_y_velocity",
58
+ "torso_z_velocity",
59
+ "torso_angular_velocity_x",
60
+ "torso_angular_velocity_y",
61
+ "torso_angular_velocity_z",
62
+ "front_left_hip_angular_velocity",
63
+ "front_left_ankle_angular_velocity",
64
+ "front_right_hip_angular_velocity",
65
+ "front_right_ankle_angular_velocity",
66
+ "back_left_hip_angular_velocity",
67
+ "back_left_ankle_angular_velocity",
68
+ "back_right_hip_angular_velocity",
69
+ "back_right_ankle_angular_velocity"
70
+ ],
71
+ "state_normalization": {
72
+ "type": "min_max",
73
+ "min": [
74
+ 0.24513374269008636,
75
+ -0.8805694580078125,
76
+ -0.33691075444221497,
77
+ -0.4456058740615845,
78
+ -0.9112266302108765,
79
+ -0.6140146255493164,
80
+ -0.09917011857032776,
81
+ -0.6453427076339722,
82
+ -1.3009648323059082,
83
+ -0.6563361883163452,
84
+ -1.2990498542785645,
85
+ -0.6459031105041504,
86
+ -0.09470116347074509,
87
+ -1.9981460571289062,
88
+ -2.8870370388031006,
89
+ -2.5440866947174072,
90
+ -5.5593695640563965,
91
+ -6.843714237213135,
92
+ -5.608675003051758,
93
+ -10.695633888244629,
94
+ -9.341012001037598,
95
+ -13.44798469543457,
96
+ -11.152595520019531,
97
+ -14.51861572265625,
98
+ -11.078145980834961,
99
+ -12.814929008483887,
100
+ -5.827189922332764
101
+ ],
102
+ "max": [
103
+ 0.9843788743019104,
104
+ 1.098469853401184,
105
+ 0.557304859161377,
106
+ 0.9673683047294617,
107
+ 0.7864838242530823,
108
+ 0.6336118578910828,
109
+ 1.329100489616394,
110
+ 0.624431848526001,
111
+ 0.09998779743909836,
112
+ 0.6632993221282959,
113
+ 0.09576962888240814,
114
+ 0.5784072875976562,
115
+ 1.320813536643982,
116
+ 5.847742557525635,
117
+ 3.869502544403076,
118
+ 2.964057445526123,
119
+ 4.749230861663818,
120
+ 8.3289155960083,
121
+ 4.963846206665039,
122
+ 14.281911849975586,
123
+ 11.46115779876709,
124
+ 12.22265911102295,
125
+ 7.717322826385498,
126
+ 15.017043113708496,
127
+ 6.09257173538208,
128
+ 7.276201248168945,
129
+ 11.832711219787598
130
+ ]
131
+ },
132
+ "robot_type": "rl_games_gymnasium",
133
+ "latency_prompt_map_path": "/home/ubuntu/lzj/mean-profiling/ant/data/lerobot/latency_prompt_map.json",
134
+ "custom_mixtures_path": "/home/ubuntu/lzj/mean-profiling/ant/data/_generated_mixtures/lerobot.json",
135
+ "gymnasium_task": {
136
+ "action_space": {
137
+ "dtype": "float32",
138
+ "high": [
139
+ 1.0,
140
+ 1.0,
141
+ 1.0,
142
+ 1.0,
143
+ 1.0,
144
+ 1.0,
145
+ 1.0,
146
+ 1.0
147
+ ],
148
+ "labels": [
149
+ "back_right_hip_torque",
150
+ "back_right_ankle_torque",
151
+ "front_left_hip_torque",
152
+ "front_left_ankle_torque",
153
+ "front_right_hip_torque",
154
+ "front_right_ankle_torque",
155
+ "back_left_hip_torque",
156
+ "back_left_ankle_torque"
157
+ ],
158
+ "low": [
159
+ -1.0,
160
+ -1.0,
161
+ -1.0,
162
+ -1.0,
163
+ -1.0,
164
+ -1.0,
165
+ -1.0,
166
+ -1.0
167
+ ],
168
+ "type": "box"
169
+ },
170
+ "base_prompt": "Make the Ant move forward as fast as possible without falling. Predict eight continuous torques in [-1, 1] ordered as back right hip, back right ankle, front left hip, front left ankle, front right hip, front right ankle, back left hip, and back left ankle.",
171
+ "env_fps": 10.0,
172
+ "env_id": "LatencyBench/AntContinuous-v0",
173
+ "frame_stack": 1,
174
+ "make_kwargs": {
175
+ "base_env_id": "Ant-v4",
176
+ "base_make_kwargs": {
177
+ "exclude_current_positions_from_observation": true,
178
+ "use_contact_forces": false
179
+ },
180
+ "render_mode": "rgb_array"
181
+ },
182
+ "noop_action": [
183
+ 0.0,
184
+ 0.0,
185
+ 0.0,
186
+ 0.0,
187
+ 0.0,
188
+ 0.0,
189
+ 0.0,
190
+ 0.0
191
+ ],
192
+ "obs_fps": 10.0,
193
+ "registration_imports": [
194
+ "latency_bench.envs.gymnasium_ant"
195
+ ],
196
+ "state_space": {
197
+ "labels": [
198
+ "torso_z",
199
+ "torso_quaternion_w",
200
+ "torso_quaternion_x",
201
+ "torso_quaternion_y",
202
+ "torso_quaternion_z",
203
+ "front_left_hip_angle",
204
+ "front_left_ankle_angle",
205
+ "front_right_hip_angle",
206
+ "front_right_ankle_angle",
207
+ "back_left_hip_angle",
208
+ "back_left_ankle_angle",
209
+ "back_right_hip_angle",
210
+ "back_right_ankle_angle",
211
+ "torso_x_velocity",
212
+ "torso_y_velocity",
213
+ "torso_z_velocity",
214
+ "torso_angular_velocity_x",
215
+ "torso_angular_velocity_y",
216
+ "torso_angular_velocity_z",
217
+ "front_left_hip_angular_velocity",
218
+ "front_left_ankle_angular_velocity",
219
+ "front_right_hip_angular_velocity",
220
+ "front_right_ankle_angular_velocity",
221
+ "back_left_hip_angular_velocity",
222
+ "back_left_ankle_angular_velocity",
223
+ "back_right_hip_angular_velocity",
224
+ "back_right_ankle_angular_velocity"
225
+ ]
226
+ },
227
+ "task_name": "ant_rgb_state"
228
+ },
229
+ "validation_dataset_name": "lerobot__val",
230
+ "validation_episodes": 10,
231
+ "validation_frames": 10000
232
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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": 10,
5
+ "frame_ms": 100.0,
6
+ "gpu_class": "1x-rtx3090",
7
+ "instance_id": "instance_859cf1e47bca6046",
8
+ "latency_kind": "observation_to_action_latency",
9
+ "latency_method": "temporal",
10
+ "model_id": "qwenoft",
11
+ "n_admitted_observations": 7976,
12
+ "n_capacity_drops": 294,
13
+ "n_observation_attempts": 8270,
14
+ "per_slot_summary": {
15
+ "0": {
16
+ "admitted_count": 7976,
17
+ "mean_observation_to_action_latency_ms": 90.56460638563993,
18
+ "mean_worker_service_time_ms": 90.22241150451042,
19
+ "p95_observation_to_action_latency_ms": 92.13036412000656,
20
+ "p95_worker_service_time_ms": 91.75596672296524,
21
+ "p99_worker_service_time_ms": 110.92562991380692
22
+ }
23
+ },
24
+ "provenance": {
25
+ "base_config": "/workspace/tasks/20260911T023128Z-p-only4/ant/profile.yaml",
26
+ "checkpoint_kind": "best",
27
+ "model_artifact": {
28
+ "checkpoint": "checkpoints/steps_5000_pytorch_model.pt",
29
+ "model_config": "config.full.yaml",
30
+ "path_in_repo": "OpenVLA/zero-latency/ant_rgb_state_l0_return_gt3000_100ep_openvla_native_sft_5k",
31
+ "repo_id": "latency-sensitive-bench/extra-envs-checkpoints",
32
+ "source": "local"
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": "20260911T033037730561Z",
44
+ "summary": {
45
+ "frame_ms": 100.0,
46
+ "max_ms": 130.29119229316711,
47
+ "mean_effective_frames": 0.9056460638563993,
48
+ "mean_ms": 90.56460638563993,
49
+ "min_ms": 87.98952293395996,
50
+ "n_samples": 7976,
51
+ "p50_frames": 0.8970676946640015,
52
+ "p50_ms": 89.70676946640015,
53
+ "p90_frames": 0.9074710392951966,
54
+ "p90_ms": 90.74710392951965,
55
+ "p95_frames": 0.9213036412000656,
56
+ "p95_ms": 92.13036412000656,
57
+ "p99_frames": 1.1217999053001404,
58
+ "p99_ms": 112.17999053001404,
59
+ "prob_latency_gt_1_frame": 0.03686058174523571,
60
+ "prob_latency_gt_2_frames": 0.0,
61
+ "prob_latency_gt_3_frames": 0.0,
62
+ "std_ms": 4.164609685850939
63
+ },
64
+ "visualization_path": "latency_profile.png",
65
+ "workload_id": "ant"
66
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/provenance.json ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "task": "ant",
3
+ "model": "QwenOFT",
4
+ "run_id": "ant-qwenoft-mean-3090-s42",
5
+ "training_status": "complete",
6
+ "training_hardware": {
7
+ "gpu": "RTX PRO6000 Blackwell Server Edition",
8
+ "count": 2,
9
+ "physical_indices": [
10
+ 0,
11
+ 1
12
+ ]
13
+ },
14
+ "profile": {
15
+ "mean_ms": 90.56460638563993,
16
+ "profile": "/home/ubuntu/lzj/mean-profiling/reference/checkpoint-configs/latency-aware/ant/small-policy/sample-factory-qwenoft-rtx3090-v1/profile/profile.json",
17
+ "sha256": "0e49f72ec05ac600a54e07bbca5af3143708444d606167f33fa21788a9fefe50"
18
+ },
19
+ "profile_hardware": "RTX3090",
20
+ "condition": "fixed_mean_profile",
21
+ "horizon": 1,
22
+ "seed": 42,
23
+ "state_dim": 27,
24
+ "action_dim": 8,
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": "1c2f38f16553622eef2c62ddde86036be989ea31",
34
+ "path": "latency-aware/ant/small-policy/sample-factory-qwenoft-rtx3090-mean-v1",
35
+ "local_checkpoint": "/home/ubuntu/lzj/mean-profiling/ant/teacher-publication/checkpoint.pth",
36
+ "status": "complete_verified",
37
+ "completed_env_steps": 10006528,
38
+ "selected_env_steps": 9969664,
39
+ "selected_source_checkpoint": "/home/ubuntu/lzj/mean-profiling/ant/teacher/ant_mean_3090_s2222/checkpoint_p0/best_000019472_9969664_reward_3371.900.pth",
40
+ "tensor_count": 15,
41
+ "weights_only_export": true,
42
+ "tensor_equality_verified": true,
43
+ "hash_readback_verified": true,
44
+ "url": "https://huggingface.co/latency-sensitive-bench/benchmark-models/tree/1c2f38f16553622eef2c62ddde86036be989ea31/latency-aware/ant/small-policy/sample-factory-qwenoft-rtx3090-mean-v1",
45
+ "completed_utc": "2026-09-30T13:54:03.497277+00:00"
46
+ },
47
+ "dataset": {
48
+ "repo_id": "latency-sensitive-bench/Standard-Pipeline",
49
+ "repo_type": "dataset",
50
+ "path": "ant/profile_latency/OpenVLA/mean_rtx3090_s2222_100ep_v1",
51
+ "revision": "6246ccc57caf32da0e02547c3e3e1080894d4035",
52
+ "data_uploaded_revision": "4faa069b31f461177e1adbb0c039da5db0adacb8",
53
+ "url": "https://huggingface.co/datasets/latency-sensitive-bench/Standard-Pipeline/tree/6246ccc57caf32da0e02547c3e3e1080894d4035/ant/profile_latency/OpenVLA/mean_rtx3090_s2222_100ep_v1",
54
+ "status": "complete_verified",
55
+ "episodes": 100,
56
+ "rows": 100000,
57
+ "strict_return_gt2500": true,
58
+ "payload_files_verified": 26,
59
+ "checksum_manifest_verified": true,
60
+ "dataset_config": "ant_profile_latency_mean_qwenoft",
61
+ "completed_utc": "2026-09-30T14:50:21.716749+00:00"
62
+ },
63
+ "collection": {
64
+ "task": "ant",
65
+ "teacher_checkpoint": "/home/ubuntu/lzj/mean-profiling/ant/teacher-publication/checkpoint.pth",
66
+ "teacher_sha256": "a7899b32beb9b2cfec1bcff85b285dddc243a73f1c322fafefae599df0f67b3b",
67
+ "episodes": 100,
68
+ "train_episodes": 90,
69
+ "val_episodes": 10,
70
+ "train_rows": 90000,
71
+ "val_rows": 10000,
72
+ "fixed_latency_ms": 90.56460638563993,
73
+ "action_horizon": 1,
74
+ "unique_seeds": 100,
75
+ "state_dim": 27
76
+ },
77
+ "runtime": {
78
+ "repo": "c3c6a39365a151e9b7a5e215452fd64e957c2b29",
79
+ "starvla": "ccca13c5177fe3d3c884b6e2de4965d916016649",
80
+ "runtime_fixes": [
81
+ "mean-profile-preparation.patch",
82
+ "gym-language-contract.patch",
83
+ "loader-spawn-cache.patch",
84
+ "loader-spawn-test.patch",
85
+ "doom-mean-reset.patch"
86
+ ],
87
+ "pytorch3d": {
88
+ "revision": "33824be3cbc87a7dd1db0f6a9a9de9ac81b2d0ba",
89
+ "build": "transforms-only, no native render extension; QwenOFT uses transforms only"
90
+ },
91
+ "decord": {
92
+ "version": "0.6.0",
93
+ "build": "official source CPU decoder CP310",
94
+ "wheel_sha256": "e193b356b1e984b4eff08d23b62e482c2c9e5037a6efdc0b1af47079ae2e4c47"
95
+ }
96
+ },
97
+ "source_reference_revision": "180da0effdaaaeacef218cf5fbaa4db41af67495",
98
+ "performance_configuration_policy": "User authorized throughput priority; reference scientific parameters and actual runtime differences are documented by full config/launch audit",
99
+ "training_results": {
100
+ "training_complete": true,
101
+ "optimizer_updates": 5000,
102
+ "selection": "final_5000",
103
+ "train_loss": 0.011952828615903854,
104
+ "validation_action_loss": 0.018865827471017838,
105
+ "validation_samples": 10112,
106
+ "final_learning_rates": {
107
+ "learning_rate/action_model": 4.9999999999999996e-06,
108
+ "learning_rate/qwen_vl_interface": 5e-07
109
+ },
110
+ "closed_loop_evaluation": "pending until all four VLA trainings complete",
111
+ "tensor_keys": 730,
112
+ "serialized_tensor_elements_including_tied_aliases": 4892400136,
113
+ "weights_only_load": "passed",
114
+ "all_tensors_finite": true,
115
+ "completion_proof": {
116
+ "task": "ant",
117
+ "optimizer_updates": 5000,
118
+ "deepspeed_global_steps": 5000,
119
+ "deepspeed_global_samples": 640000,
120
+ "skipped_steps": 0,
121
+ "scheduler_last_epoch": 5000,
122
+ "scheduler_step_count": 5001,
123
+ "final_learning_rates": [
124
+ 5e-07,
125
+ 4.9999999999999996e-06
126
+ ],
127
+ "summary_final_steps": 5000,
128
+ "final_inference_weight_bytes": 9785070315,
129
+ "wandb_global_step": 5000,
130
+ "wrapper_exit": 2,
131
+ "trainer_exit_separately_captured": false,
132
+ "training_complete_verified": true,
133
+ "failure_scope": "launcher_tail_parse_after_trainer_cleanup",
134
+ "cause": "Sharedrun_vla.shwasmodifiedforGPUmask/portwhileAntbashwasblockedinsidePython; remainingfileoffsetlaterparsedmodifiedtailandEOF",
135
+ "trainer_cleanup_log": "bothranksDestroyDistributedProcessGroup at2026-10-01 00:03:21UTC; no model exception",
136
+ "evidence_files": [
137
+ "checkpoints/steps_5000_state/pytorch_model/mp_rank_00_model_states.pt",
138
+ "checkpoints/steps_5000_state/custom_checkpoint_0.pkl",
139
+ "summary.jsonl",
140
+ "config.full.yaml"
141
+ ],
142
+ "note": "No syntheticvla.exit0; wrapperexit2preserved; upload uses explicitcompletionproof"
143
+ }
144
+ },
145
+ "optimizer_recovery_states": "retained on host; inference weights only in this bundle",
146
+ "wandb_url": "https://wandb.ai/dongqianyu99-zhejiang-university/latency-sensitive-bench/runs/mean3090-ant-vla-blackwell-20260930"
147
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/startup_verified.json ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "snapshot_utc": "2026-09-30T17:38:42Z",
3
+ "unit": "mean-profile-ant-vla-20261001-b64gc.service",
4
+ "wrapper_pid": 675875,
5
+ "rank_pids": [
6
+ 676262,
7
+ 676263
8
+ ],
9
+ "physical_gpus": [
10
+ 0,
11
+ 1
12
+ ],
13
+ "formal_training_started": true,
14
+ "formal_optimizer_updates_completed": 36,
15
+ "formal_optimizer_budget": 5000,
16
+ "preflight_optimizer_updates_discarded": 20,
17
+ "consecutive_logged_steps": [
18
+ 33,
19
+ 34,
20
+ 35,
21
+ 36
22
+ ],
23
+ "loss_at_steps": {
24
+ "33": 0.3258245885372162,
25
+ "34": 0.3107373118400574,
26
+ "35": 0.49765458703041077,
27
+ "36": 0.4269329905509949
28
+ },
29
+ "finite_loss_verified": true,
30
+ "gpu_utilization_percent": [
31
+ 100,
32
+ 100
33
+ ],
34
+ "gpu_used_mib": [
35
+ 63815,
36
+ 65995
37
+ ],
38
+ "micro_batch_per_gpu": 64,
39
+ "gradient_accumulation": 1,
40
+ "global_batch": 128,
41
+ "gradient_checkpointing": true,
42
+ "seconds_per_optimizer_update_recent": 3.7,
43
+ "data_wait_seconds_recent": 0.0006755599970347248,
44
+ "host_memory_peak_bytes": 38237016064,
45
+ "host_memory_current_bytes": 17592913920,
46
+ "cgroup_memory_events": {
47
+ "low": 0,
48
+ "high": 0,
49
+ "max": 0,
50
+ "oom": 0,
51
+ "oom_kill": 0,
52
+ "oom_group_kill": 0
53
+ },
54
+ "remaining_compute_hours": 5.1018888888888885,
55
+ "estimated_remaining_hours_including_offline_validation_and_saves": [
56
+ 5.5,
57
+ 6.0
58
+ ],
59
+ "estimated_completion_utc_range": [
60
+ "2026-09-30T23:10:00Z",
61
+ "2026-09-30T23:40:00Z"
62
+ ],
63
+ "next_completion_check_utc": "2026-10-01T00:00:00Z",
64
+ "training_complete": false,
65
+ "closed_loop_evaluation_complete": false,
66
+ "publication_complete": false
67
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/task_contract.json ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "action_space": {
3
+ "dtype": "float32",
4
+ "high": [
5
+ 1.0,
6
+ 1.0,
7
+ 1.0,
8
+ 1.0,
9
+ 1.0,
10
+ 1.0,
11
+ 1.0,
12
+ 1.0
13
+ ],
14
+ "labels": [
15
+ "back_right_hip_torque",
16
+ "back_right_ankle_torque",
17
+ "front_left_hip_torque",
18
+ "front_left_ankle_torque",
19
+ "front_right_hip_torque",
20
+ "front_right_ankle_torque",
21
+ "back_left_hip_torque",
22
+ "back_left_ankle_torque"
23
+ ],
24
+ "low": [
25
+ -1.0,
26
+ -1.0,
27
+ -1.0,
28
+ -1.0,
29
+ -1.0,
30
+ -1.0,
31
+ -1.0,
32
+ -1.0
33
+ ],
34
+ "type": "box"
35
+ },
36
+ "base_prompt": "Make the Ant move forward as fast as possible without falling. Predict eight continuous torques in [-1, 1] ordered as back right hip, back right ankle, front left hip, front left ankle, front right hip, front right ankle, back left hip, and back left ankle.",
37
+ "env_fps": 10.0,
38
+ "env_id": "LatencyBench/AntContinuous-v0",
39
+ "frame_stack": 1,
40
+ "make_kwargs": {
41
+ "base_env_id": "Ant-v4",
42
+ "base_make_kwargs": {
43
+ "exclude_current_positions_from_observation": true,
44
+ "use_contact_forces": false
45
+ },
46
+ "render_mode": "rgb_array"
47
+ },
48
+ "noop_action": [
49
+ 0.0,
50
+ 0.0,
51
+ 0.0,
52
+ 0.0,
53
+ 0.0,
54
+ 0.0,
55
+ 0.0,
56
+ 0.0
57
+ ],
58
+ "obs_fps": 10.0,
59
+ "registration_imports": [
60
+ "latency_bench.envs.gymnasium_ant"
61
+ ],
62
+ "state_space": {
63
+ "labels": [
64
+ "torso_z",
65
+ "torso_quaternion_w",
66
+ "torso_quaternion_x",
67
+ "torso_quaternion_y",
68
+ "torso_quaternion_z",
69
+ "front_left_hip_angle",
70
+ "front_left_ankle_angle",
71
+ "front_right_hip_angle",
72
+ "front_right_ankle_angle",
73
+ "back_left_hip_angle",
74
+ "back_left_ankle_angle",
75
+ "back_right_hip_angle",
76
+ "back_right_ankle_angle",
77
+ "torso_x_velocity",
78
+ "torso_y_velocity",
79
+ "torso_z_velocity",
80
+ "torso_angular_velocity_x",
81
+ "torso_angular_velocity_y",
82
+ "torso_angular_velocity_z",
83
+ "front_left_hip_angular_velocity",
84
+ "front_left_ankle_angular_velocity",
85
+ "front_right_hip_angular_velocity",
86
+ "front_right_ankle_angular_velocity",
87
+ "back_left_hip_angular_velocity",
88
+ "back_left_ankle_angular_velocity",
89
+ "back_right_hip_angular_velocity",
90
+ "back_right_ankle_angular_velocity"
91
+ ]
92
+ },
93
+ "task_name": "ant_rgb_state"
94
+ }
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/datasets.py ADDED
The diff for this file is too large to render. See raw diff
 
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training-code/train_starvla.py ADDED
The diff for this file is too large to render. See raw diff
 
latency-aware/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/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/ant/vla/starvla-qwenoft-h1/ant-qwenoft-mean-3090-s42/training_results.json ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "training_complete": true,
3
+ "optimizer_updates": 5000,
4
+ "selection": "final_5000",
5
+ "train_loss": 0.011952828615903854,
6
+ "validation_action_loss": 0.018865827471017838,
7
+ "validation_samples": 10112,
8
+ "final_learning_rates": {
9
+ "learning_rate/action_model": 4.9999999999999996e-06,
10
+ "learning_rate/qwen_vl_interface": 5e-07
11
+ },
12
+ "closed_loop_evaluation": "pending until all four VLA trainings complete",
13
+ "tensor_keys": 730,
14
+ "serialized_tensor_elements_including_tied_aliases": 4892400136,
15
+ "weights_only_load": "passed",
16
+ "all_tensors_finite": true,
17
+ "completion_proof": {
18
+ "task": "ant",
19
+ "optimizer_updates": 5000,
20
+ "deepspeed_global_steps": 5000,
21
+ "deepspeed_global_samples": 640000,
22
+ "skipped_steps": 0,
23
+ "scheduler_last_epoch": 5000,
24
+ "scheduler_step_count": 5001,
25
+ "final_learning_rates": [
26
+ 5e-07,
27
+ 4.9999999999999996e-06
28
+ ],
29
+ "summary_final_steps": 5000,
30
+ "final_inference_weight_bytes": 9785070315,
31
+ "wandb_global_step": 5000,
32
+ "wrapper_exit": 2,
33
+ "trainer_exit_separately_captured": false,
34
+ "training_complete_verified": true,
35
+ "failure_scope": "launcher_tail_parse_after_trainer_cleanup",
36
+ "cause": "Sharedrun_vla.shwasmodifiedforGPUmask/portwhileAntbashwasblockedinsidePython; remainingfileoffsetlaterparsedmodifiedtailandEOF",
37
+ "trainer_cleanup_log": "bothranksDestroyDistributedProcessGroup at2026-10-01 00:03:21UTC; no model exception",
38
+ "evidence_files": [
39
+ "checkpoints/steps_5000_state/pytorch_model/mp_rank_00_model_states.pt",
40
+ "checkpoints/steps_5000_state/custom_checkpoint_0.pkl",
41
+ "summary.jsonl",
42
+ "config.full.yaml"
43
+ ],
44
+ "note": "No syntheticvla.exit0; wrapperexit2preserved; upload uses explicitcompletionproof"
45
+ }
46
+ }