Skip to content

Commit 07521b5

Browse files
authored
Merge branch 'main' into main
2 parents c053731 + d6ddbb8 commit 07521b5

12 files changed

Lines changed: 702 additions & 59 deletions

File tree

examples/qwen_gr00t/conf/train/qwen_gr00t.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,24 @@ system:
1919
# Path to a checkpoint directory to resume training from (e.g. /path/to/checkpoints/005000)
2020
# resume_from:
2121

22+
# --- Activation Checkpointing ---
23+
# Modes: "none" (default), "full", "selective", "memory_budget"
24+
# "full": wraps every FSDP unit with gradient checkpointing
25+
# "selective": wraps every Nth layer (selective_ac_option="2" = every 2nd)
26+
# or uses per-op SAC (selective_ac_option="op")
27+
# "memory_budget": compiler-driven (requires torch.compile)
28+
activation_checkpoint:
29+
mode: none
30+
# For selective mode: "2" = every 2nd layer, "3" = every 3rd, "op" = per-op SAC
31+
# selective_ac_option: "2"
32+
preserve_rng_state: false
33+
# For memory_budget mode only:
34+
# memory_budget: 0.5
35+
# Only checkpoint the action model (DiT blocks). VLM layers use dynamic
36+
# sequence lengths (variable vision tokens) which breaks recomputation.
37+
# checkpoint_patterns:
38+
# - "action_model\\._head\\.model\\.transformer_blocks\\.\\d+$"
39+
2240
model:
2341
model_name: qwen_gr00t
2442
vlm:
@@ -56,6 +74,12 @@ model:
5674

5775
prompt_template: "Your task is {instruction}. To identify the key objects for your task. Locate their bounding boxes in [x1,y1,x2,y2] format."
5876

77+
# --- Chunked Cross Entropy ---
78+
# 0 = disabled
79+
# >0 = chunk size in tokens. Processes logits in fixed-size chunks to reduce
80+
# peak memory from the [batch*seq_len, vocab_size] float32 softmax tensor.
81+
chunked_ce_tokens: 0
82+
5983
normalization_mapping:
6084
VISUAL: IDENTITY
6185
STATE: MIN_MAX
@@ -123,6 +147,21 @@ data:
123147
# Path to VLM co-training data (WDS/Energon format). Leave unset to disable co-training.
124148
# vlm_data_path: /workspace/datasets/vlm_cotrain/
125149
tolerance_s: 0.0001
150+
151+
# --- Data Mixture ---
152+
# When data_mix is set, it takes precedence over data_path.
153+
# Each entry specifies a dataset path and sampling weight.
154+
# Weights are relative — they get normalized internally.
155+
# Optional: data_root_dir as a common prefix for relative paths.
156+
#
157+
# data_root_dir: /workspace/datasets/
158+
# data_mix:
159+
# - path: IPEC-COMMUNITY/libero_goal_no_noops_1.0.0_lerobot
160+
# weight: 1.0
161+
# - path: IPEC-COMMUNITY/libero_spatial_no_noops_1.0.0_lerobot
162+
# weight: 0.5
163+
# balance_dataset_weights: true # scale weights by dataset size (default: true)
164+
126165
preprocessor:
127166
name: policy_preprocessor
128167
steps:

flagscale/models/utils/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
OPTIMIZER_STATE = "optimizer_state.safetensors"
5050
OPTIMIZER_PARAM_GROUPS = "optimizer_param_groups.json"
5151
SCHEDULER_STATE = "scheduler_state.json"
52+
DATALOADER_STATE = "dataloader_state.json"
5253

5354
POLICY_PREPROCESSOR_DEFAULT_NAME = "policy_preprocessor"
5455
POLICY_POSTPROCESSOR_DEFAULT_NAME = "policy_postprocessor"

flagscale/models/vla/qwen_gr00t/configuration_qwen_gr00t.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class QwenGr00tConfig(PreTrainedConfig):
2323

2424
prompt_template: str | None = None
2525

26+
# Chunked cross-entropy for VLM co-training loss.
27+
# 0 = disabled (use HF model's built-in CE), >0 = chunk size in tokens.
28+
chunked_ce_tokens: int = 0
29+
2630
normalization_mapping: dict[str, NormalizationMode] = field(
2731
default_factory=lambda: {
2832
"VISUAL": NormalizationMode.IDENTITY,
@@ -61,8 +65,14 @@ def from_train_config(cls, train_config: TrainConfig) -> QwenGr00tConfig:
6165
action_model = GR00TActionHeadConfig.from_omegaconf(model_cfg.action_model)
6266

6367
prompt_template = getattr(model_cfg, "prompt_template", None)
68+
chunked_ce_tokens = getattr(model_cfg, "chunked_ce_tokens", 0)
6469

65-
kwargs = dict(vlm=vlm, action_model=action_model, prompt_template=prompt_template)
70+
kwargs = dict(
71+
vlm=vlm,
72+
action_model=action_model,
73+
prompt_template=prompt_template,
74+
chunked_ce_tokens=chunked_ce_tokens,
75+
)
6676

6777
raw_norm = getattr(model_cfg, "normalization_mapping", None)
6878
if raw_norm is not None:

flagscale/models/vla/qwen_gr00t/modeling_qwen_gr00t.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from flagscale.models.vla.registry import build_action_model, build_vlm
3333
from flagscale.models.vla.utils import get_vlm_config
3434
from flagscale.platforms.platform_manager import get_platform
35+
from flagscale.train.utils.chunked_cross_entropy import chunked_cross_entropy_loss
3536

3637

3738
class QwenGr00t(TrainablePolicy):
@@ -158,7 +159,16 @@ def forward(
158159

159160
if vlm_batch is not None:
160161
with torch.autocast(get_platform().amp_device_type(), dtype=torch.bfloat16):
161-
vlm_loss = self.vlm.model(**vlm_batch, return_dict=True).loss
162+
if self.config.chunked_ce_tokens > 0:
163+
labels = vlm_batch.pop("labels")
164+
output_vlm = self.vlm.model(**vlm_batch, return_dict=True)
165+
vlm_loss = chunked_cross_entropy_loss(
166+
output_vlm.logits,
167+
labels,
168+
chunk_tokens=self.config.chunked_ce_tokens,
169+
)
170+
else:
171+
vlm_loss = self.vlm.model(**vlm_batch, return_dict=True).loss
162172
result["vlm_loss"] = vlm_loss
163173

164174
return result

flagscale/models/vla/vlm/qwenvl_backbone.py

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
Qwen3VLForConditionalGeneration,
1919
)
2020

21-
from flagscale.logger import logger
2221
from flagscale.models.vla.registry import register_vlm
2322
from flagscale.platforms.platform_manager import get_platform
2423

@@ -91,14 +90,9 @@ def prepare_input(
9190
if isinstance(instructions, str):
9291
instructions = [instructions]
9392

94-
logger.info(f"[prepare_input] image_feature_keys={image_feature_keys}")
9593
batch_images: list[list[Image.Image]] | None = None
9694
for key in image_feature_keys:
9795
imgs = batch[key]
98-
if isinstance(imgs, torch.Tensor):
99-
logger.info(
100-
f"[prepare_input] key={key} tensor shape={imgs.shape} dtype={imgs.dtype}"
101-
)
10296
if isinstance(imgs, torch.Tensor) and imgs.ndim == 3:
10397
imgs = [imgs]
10498
key_images = [_to_pil(img) for img in imgs]
@@ -111,9 +105,6 @@ def prepare_input(
111105
for idx, sample_images in enumerate(batch_images):
112106
batch_images[idx] = [img for img in sample_images if img is not None]
113107

114-
logger.info(
115-
f"[prepare_input] batch_size={len(batch_images)} images_per_sample={[len(s) for s in batch_images]} pil_size={batch_images[0][0].size if batch_images else None}"
116-
)
117108
return batch_images, instructions
118109

119110
def build_qwenvl_inputs(
@@ -139,20 +130,13 @@ def _build_messages(
139130
return messages
140131

141132
def forward(self, batch: dict[str, torch.Tensor], **kwargs) -> dict[str, torch.Tensor]:
142-
logger.info(
143-
f"[VLM.forward] input keys={list(batch.keys())} "
144-
+ " ".join(f"{k}={v.shape}" for k, v in batch.items() if isinstance(v, torch.Tensor))
145-
)
146133
with torch.autocast(get_platform().amp_device_type(), dtype=torch.bfloat16):
147134
outputs = self.model(
148135
**batch,
149136
output_hidden_states=True,
150137
return_dict=True,
151138
**kwargs,
152139
)
153-
logger.info(
154-
f"[VLM.forward] hidden_states: {len(outputs.hidden_states)} layers, last={outputs.hidden_states[-1].shape}"
155-
)
156140
# TODO: (yupu) We should output the original outputs, not just the hidden states.
157141
return {"hidden_states": outputs.hidden_states}
158142

@@ -197,13 +181,6 @@ def build_qwenvl_inputs(
197181
text=texts, images=image_inputs, videos=video_inputs, padding=True, return_tensors="pt"
198182
)
199183

200-
logger.info(
201-
"[Qwen25.build_qwenvl_inputs] "
202-
+ " ".join(
203-
f"{k}={v.shape}" for k, v in batch_input.items() if isinstance(v, torch.Tensor)
204-
)
205-
)
206-
207184
# Use current CUDA device instead of self.model.device, which returns
208185
# a DTensor device under FSDP2 and causes mixed Tensor/DTensor errors.
209186
return batch_input.to(get_platform().device())
@@ -245,13 +222,6 @@ def build_qwenvl_inputs(
245222
return_tensors="pt",
246223
)
247224

248-
logger.info(
249-
"[Qwen3.build_qwenvl_inputs] "
250-
+ " ".join(
251-
f"{k}={v.shape}" for k, v in batch_inputs.items() if isinstance(v, torch.Tensor)
252-
)
253-
)
254-
255225
# Use current CUDA device instead of self.model.device, which returns
256226
# a DTensor device under FSDP2 and causes mixed Tensor/DTensor errors.
257227
return batch_inputs.to(get_platform().device())

0 commit comments

Comments
 (0)