Auto-sync: 2026-06-30 09:33:44
Browse files- dovla_cil/eval/maniskill_policy_rollout.py +56 -3
- dovla_cil/models/dovla.py +63 -0
- dovla_cil/training/losses.py +1 -0
- dovla_cil/training/trainer.py +75 -0
- logs/auto_sync_hf.log +1 -0
dovla_cil/eval/maniskill_policy_rollout.py
CHANGED
|
@@ -144,6 +144,12 @@ def evaluate_maniskill_policy_rollout(
|
|
| 144 |
field, and executes the best chunk. This keeps the proposal geometry counterfactual while
|
| 145 |
avoiding same-state validation candidates.
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
When ``selection_mode == 'field_optim'`` the evaluator starts from the policy mean plus
|
| 148 |
optional Gaussian multi-start proposals, performs projected gradient ascent on the learned
|
| 149 |
field potential in action space, and executes the best optimized chunk. This is still
|
|
@@ -173,13 +179,15 @@ def evaluate_maniskill_policy_rollout(
|
|
| 173 |
"policy",
|
| 174 |
"field",
|
| 175 |
"field_optim",
|
|
|
|
| 176 |
"lattice",
|
| 177 |
"retrieval_lattice",
|
| 178 |
"retrieval_residual",
|
| 179 |
}:
|
| 180 |
raise ValueError(
|
| 181 |
-
"selection_mode must be 'policy', 'field', 'field_optim',
|
| 182 |
-
"'
|
|
|
|
| 183 |
)
|
| 184 |
if num_candidates <= 0:
|
| 185 |
raise ValueError("num_candidates must be positive")
|
|
@@ -388,7 +396,12 @@ def evaluate_maniskill_policy_rollout(
|
|
| 388 |
task_summaries[task_id] = _summarize_rows(task_rows)
|
| 389 |
|
| 390 |
effective_num_candidates = num_candidates
|
| 391 |
-
if selection_mode in {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 392 |
effective_num_candidates = max(
|
| 393 |
[int(row.get("lattice_candidate_count", 0)) for row in rows],
|
| 394 |
default=0,
|
|
@@ -410,6 +423,9 @@ def evaluate_maniskill_policy_rollout(
|
|
| 410 |
if selection_mode in {"field", "field_optim", "retrieval_residual"}
|
| 411 |
and num_candidates > 1
|
| 412 |
else 0.0,
|
|
|
|
|
|
|
|
|
|
| 413 |
"selection_margin": selection_margin,
|
| 414 |
"prepend_policy_candidate": bool(prepend_policy_candidate),
|
| 415 |
"field_optim_steps": field_optim_steps
|
|
@@ -1413,6 +1429,7 @@ def _evaluate_task_cases(
|
|
| 1413 |
retrieval_residual_scales
|
| 1414 |
),
|
| 1415 |
retrieval_residual_reduce=retrieval_residual_reduce,
|
|
|
|
| 1416 |
),
|
| 1417 |
"selected_residual_scale": _selected_residual_scale(
|
| 1418 |
case,
|
|
@@ -1433,6 +1450,7 @@ def _evaluate_task_cases(
|
|
| 1433 |
retrieval_residual_scales
|
| 1434 |
),
|
| 1435 |
retrieval_residual_reduce=retrieval_residual_reduce,
|
|
|
|
| 1436 |
),
|
| 1437 |
"candidate_source_group_id": case.candidate_source_group_id,
|
| 1438 |
}
|
|
@@ -1848,6 +1866,29 @@ def _select_action_chunk(
|
|
| 1848 |
|
| 1849 |
policy_mean = model.forward_policy(observations, instructions)
|
| 1850 |
batch_size = policy_mean.shape[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1851 |
if selection_mode == "retrieval_residual":
|
| 1852 |
if action_candidates is None:
|
| 1853 |
raise ValueError("retrieval_residual selection requires action_candidates")
|
|
@@ -2639,7 +2680,10 @@ def _effective_lattice_candidate_count(
|
|
| 2639 |
prepended_policy_candidate: bool = False,
|
| 2640 |
residual_scale_count: int = 1,
|
| 2641 |
retrieval_residual_reduce: str = "none",
|
|
|
|
| 2642 |
) -> int:
|
|
|
|
|
|
|
| 2643 |
count = len(case.candidate_action_values)
|
| 2644 |
if prepended_policy_candidate and selection_mode in {"lattice", "retrieval_lattice"}:
|
| 2645 |
count += 1
|
|
@@ -2804,6 +2848,7 @@ def _selected_candidate_type(
|
|
| 2804 |
prepended_policy_candidate: bool = False,
|
| 2805 |
residual_scale_count: int = 1,
|
| 2806 |
retrieval_residual_reduce: str = "none",
|
|
|
|
| 2807 |
) -> str:
|
| 2808 |
if selection_mode == "policy":
|
| 2809 |
return "policy_continuous"
|
|
@@ -2811,6 +2856,14 @@ def _selected_candidate_type(
|
|
| 2811 |
return "field_selected"
|
| 2812 |
if selection_mode == "field_optim":
|
| 2813 |
return "field_optim_selected"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2814 |
if selection_mode == "retrieval_residual":
|
| 2815 |
if retrieval_residual_reduce in _FIELD_CONDITIONED_RESIDUAL_REDUCERS:
|
| 2816 |
scale_count = max(1, int(residual_scale_count))
|
|
|
|
| 144 |
field, and executes the best chunk. This keeps the proposal geometry counterfactual while
|
| 145 |
avoiding same-state validation candidates.
|
| 146 |
|
| 147 |
+
When ``selection_mode == 'proposal_lattice'`` the model generates a fixed typed
|
| 148 |
+
counterfactual proposal set directly from the current state and instruction. The learned
|
| 149 |
+
field scores those model-generated proposals and executes one selected chunk, preserving
|
| 150 |
+
clean deployment while testing whether proposal support, not field scoring, is the
|
| 151 |
+
bottleneck.
|
| 152 |
+
|
| 153 |
When ``selection_mode == 'field_optim'`` the evaluator starts from the policy mean plus
|
| 154 |
optional Gaussian multi-start proposals, performs projected gradient ascent on the learned
|
| 155 |
field potential in action space, and executes the best optimized chunk. This is still
|
|
|
|
| 179 |
"policy",
|
| 180 |
"field",
|
| 181 |
"field_optim",
|
| 182 |
+
"proposal_lattice",
|
| 183 |
"lattice",
|
| 184 |
"retrieval_lattice",
|
| 185 |
"retrieval_residual",
|
| 186 |
}:
|
| 187 |
raise ValueError(
|
| 188 |
+
"selection_mode must be 'policy', 'field', 'field_optim', "
|
| 189 |
+
"'proposal_lattice', 'lattice', 'retrieval_lattice', or "
|
| 190 |
+
"'retrieval_residual'"
|
| 191 |
)
|
| 192 |
if num_candidates <= 0:
|
| 193 |
raise ValueError("num_candidates must be positive")
|
|
|
|
| 396 |
task_summaries[task_id] = _summarize_rows(task_rows)
|
| 397 |
|
| 398 |
effective_num_candidates = num_candidates
|
| 399 |
+
if selection_mode in {
|
| 400 |
+
"proposal_lattice",
|
| 401 |
+
"lattice",
|
| 402 |
+
"retrieval_lattice",
|
| 403 |
+
"retrieval_residual",
|
| 404 |
+
}:
|
| 405 |
effective_num_candidates = max(
|
| 406 |
[int(row.get("lattice_candidate_count", 0)) for row in rows],
|
| 407 |
default=0,
|
|
|
|
| 423 |
if selection_mode in {"field", "field_optim", "retrieval_residual"}
|
| 424 |
and num_candidates > 1
|
| 425 |
else 0.0,
|
| 426 |
+
"proposal_types": list(model_config.proposal_types)
|
| 427 |
+
if selection_mode == "proposal_lattice"
|
| 428 |
+
else [],
|
| 429 |
"selection_margin": selection_margin,
|
| 430 |
"prepend_policy_candidate": bool(prepend_policy_candidate),
|
| 431 |
"field_optim_steps": field_optim_steps
|
|
|
|
| 1429 |
retrieval_residual_scales
|
| 1430 |
),
|
| 1431 |
retrieval_residual_reduce=retrieval_residual_reduce,
|
| 1432 |
+
proposal_types=model_config.proposal_types,
|
| 1433 |
),
|
| 1434 |
"selected_residual_scale": _selected_residual_scale(
|
| 1435 |
case,
|
|
|
|
| 1450 |
retrieval_residual_scales
|
| 1451 |
),
|
| 1452 |
retrieval_residual_reduce=retrieval_residual_reduce,
|
| 1453 |
+
proposal_type_count=len(model_config.proposal_types),
|
| 1454 |
),
|
| 1455 |
"candidate_source_group_id": case.candidate_source_group_id,
|
| 1456 |
}
|
|
|
|
| 1866 |
|
| 1867 |
policy_mean = model.forward_policy(observations, instructions)
|
| 1868 |
batch_size = policy_mean.shape[0]
|
| 1869 |
+
if selection_mode == "proposal_lattice":
|
| 1870 |
+
proposals = model.forward_proposals(observations, instructions)
|
| 1871 |
+
return _select_lattice_action_chunk(
|
| 1872 |
+
model,
|
| 1873 |
+
observations,
|
| 1874 |
+
instructions,
|
| 1875 |
+
proposals,
|
| 1876 |
+
torch=torch,
|
| 1877 |
+
action_low=(
|
| 1878 |
+
action_low.unsqueeze(1)
|
| 1879 |
+
if action_low is not None and action_low.ndim == 3
|
| 1880 |
+
else action_low
|
| 1881 |
+
),
|
| 1882 |
+
action_high=(
|
| 1883 |
+
action_high.unsqueeze(1)
|
| 1884 |
+
if action_high is not None and action_high.ndim == 3
|
| 1885 |
+
else action_high
|
| 1886 |
+
),
|
| 1887 |
+
candidate_mask=None,
|
| 1888 |
+
candidate_type_bonus=None,
|
| 1889 |
+
selection_margin=selection_margin,
|
| 1890 |
+
baseline_action=policy_mean if prepend_policy_candidate else None,
|
| 1891 |
+
)
|
| 1892 |
if selection_mode == "retrieval_residual":
|
| 1893 |
if action_candidates is None:
|
| 1894 |
raise ValueError("retrieval_residual selection requires action_candidates")
|
|
|
|
| 2680 |
prepended_policy_candidate: bool = False,
|
| 2681 |
residual_scale_count: int = 1,
|
| 2682 |
retrieval_residual_reduce: str = "none",
|
| 2683 |
+
proposal_type_count: int = 0,
|
| 2684 |
) -> int:
|
| 2685 |
+
if selection_mode == "proposal_lattice":
|
| 2686 |
+
return int(proposal_type_count) + (1 if prepended_policy_candidate else 0)
|
| 2687 |
count = len(case.candidate_action_values)
|
| 2688 |
if prepended_policy_candidate and selection_mode in {"lattice", "retrieval_lattice"}:
|
| 2689 |
count += 1
|
|
|
|
| 2848 |
prepended_policy_candidate: bool = False,
|
| 2849 |
residual_scale_count: int = 1,
|
| 2850 |
retrieval_residual_reduce: str = "none",
|
| 2851 |
+
proposal_types: tuple[str, ...] = (),
|
| 2852 |
) -> str:
|
| 2853 |
if selection_mode == "policy":
|
| 2854 |
return "policy_continuous"
|
|
|
|
| 2856 |
return "field_selected"
|
| 2857 |
if selection_mode == "field_optim":
|
| 2858 |
return "field_optim_selected"
|
| 2859 |
+
if selection_mode == "proposal_lattice":
|
| 2860 |
+
if prepended_policy_candidate:
|
| 2861 |
+
if selected_index == 0:
|
| 2862 |
+
return "policy_continuous"
|
| 2863 |
+
selected_index -= 1
|
| 2864 |
+
if 0 <= selected_index < len(proposal_types):
|
| 2865 |
+
return f"proposal_{proposal_types[selected_index]}"
|
| 2866 |
+
return "proposal_unknown"
|
| 2867 |
if selection_mode == "retrieval_residual":
|
| 2868 |
if retrieval_residual_reduce in _FIELD_CONDITIONED_RESIDUAL_REDUCERS:
|
| 2869 |
scale_count = max(1, int(residual_scale_count))
|
dovla_cil/models/dovla.py
CHANGED
|
@@ -42,6 +42,7 @@ class DoVLAConfig:
|
|
| 42 |
backbone_model: str | None = None
|
| 43 |
backbone_freeze: bool = True
|
| 44 |
backbone_local_files_only: bool = True
|
|
|
|
| 45 |
# Backward-compatible aliases from the first scaffold.
|
| 46 |
observation_dim: int | None = None
|
| 47 |
language_dim: int | None = None
|
|
@@ -51,6 +52,14 @@ class DoVLAConfig:
|
|
| 51 |
self.obs_dim = int(self.observation_dim)
|
| 52 |
if self.language_dim is not None:
|
| 53 |
self.lang_dim = int(self.language_dim)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
for name in (
|
| 55 |
"obs_dim",
|
| 56 |
"lang_dim",
|
|
@@ -247,6 +256,25 @@ if nn is not None:
|
|
| 247 |
self.config.action_dim,
|
| 248 |
action_horizon=self.config.action_horizon,
|
| 249 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
self.effect_head = EffectHead(self.config.intervention_dim, self.config.effect_dim)
|
| 251 |
self.reward_head = RewardHead(self.config.intervention_dim)
|
| 252 |
self.regret_head = RegretHead(self.config.intervention_dim)
|
|
@@ -280,6 +308,41 @@ if nn is not None:
|
|
| 280 |
context = self.encode_context(observation, instruction)
|
| 281 |
return self.policy_head(context)
|
| 282 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
def forward_effect(self, observation, instruction, action, skill_type=None):
|
| 284 |
z = self.encode_intervention(observation, instruction, action, skill_type=skill_type)
|
| 285 |
return self.interventional_field(z)
|
|
|
|
| 42 |
backbone_model: str | None = None
|
| 43 |
backbone_freeze: bool = True
|
| 44 |
backbone_local_files_only: bool = True
|
| 45 |
+
proposal_types: tuple[str, ...] = ()
|
| 46 |
# Backward-compatible aliases from the first scaffold.
|
| 47 |
observation_dim: int | None = None
|
| 48 |
language_dim: int | None = None
|
|
|
|
| 52 |
self.obs_dim = int(self.observation_dim)
|
| 53 |
if self.language_dim is not None:
|
| 54 |
self.lang_dim = int(self.language_dim)
|
| 55 |
+
if isinstance(self.proposal_types, str):
|
| 56 |
+
self.proposal_types = tuple(
|
| 57 |
+
item.strip()
|
| 58 |
+
for item in self.proposal_types.split(",")
|
| 59 |
+
if item.strip()
|
| 60 |
+
)
|
| 61 |
+
else:
|
| 62 |
+
self.proposal_types = tuple(str(item) for item in self.proposal_types)
|
| 63 |
for name in (
|
| 64 |
"obs_dim",
|
| 65 |
"lang_dim",
|
|
|
|
| 256 |
self.config.action_dim,
|
| 257 |
action_horizon=self.config.action_horizon,
|
| 258 |
)
|
| 259 |
+
self.proposal_types = tuple(self.config.proposal_types)
|
| 260 |
+
if self.proposal_types:
|
| 261 |
+
self.proposal_type_to_index = {
|
| 262 |
+
proposal_type: index
|
| 263 |
+
for index, proposal_type in enumerate(self.proposal_types)
|
| 264 |
+
}
|
| 265 |
+
self.proposal_type_embedding = nn.Embedding(
|
| 266 |
+
len(self.proposal_types),
|
| 267 |
+
self.config.hidden_dim,
|
| 268 |
+
)
|
| 269 |
+
self.proposal_head = PolicyHead(
|
| 270 |
+
self.config.hidden_dim,
|
| 271 |
+
self.config.action_dim,
|
| 272 |
+
action_horizon=self.config.action_horizon,
|
| 273 |
+
)
|
| 274 |
+
else:
|
| 275 |
+
self.proposal_type_to_index = {}
|
| 276 |
+
self.proposal_type_embedding = None
|
| 277 |
+
self.proposal_head = None
|
| 278 |
self.effect_head = EffectHead(self.config.intervention_dim, self.config.effect_dim)
|
| 279 |
self.reward_head = RewardHead(self.config.intervention_dim)
|
| 280 |
self.regret_head = RegretHead(self.config.intervention_dim)
|
|
|
|
| 308 |
context = self.encode_context(observation, instruction)
|
| 309 |
return self.policy_head(context)
|
| 310 |
|
| 311 |
+
def forward_proposals(self, observation, instruction, proposal_types=None):
|
| 312 |
+
"""Generate a typed counterfactual proposal lattice for each state.
|
| 313 |
+
|
| 314 |
+
The standard policy head stays unimodal. This optional head predicts one action
|
| 315 |
+
chunk per configured intervention family, giving the learned field a clean
|
| 316 |
+
deployment-time proposal set without reading same-state dataset candidates.
|
| 317 |
+
"""
|
| 318 |
+
|
| 319 |
+
if not self.proposal_types or self.proposal_type_embedding is None:
|
| 320 |
+
raise ValueError("DoVLAConfig.proposal_types is empty")
|
| 321 |
+
requested = tuple(proposal_types) if proposal_types is not None else self.proposal_types
|
| 322 |
+
missing = [
|
| 323 |
+
proposal_type
|
| 324 |
+
for proposal_type in requested
|
| 325 |
+
if proposal_type not in self.proposal_type_to_index
|
| 326 |
+
]
|
| 327 |
+
if missing:
|
| 328 |
+
raise ValueError(f"unknown proposal_types: {missing}")
|
| 329 |
+
context = self.encode_context(observation, instruction)
|
| 330 |
+
indices = torch.tensor(
|
| 331 |
+
[self.proposal_type_to_index[proposal_type] for proposal_type in requested],
|
| 332 |
+
dtype=torch.long,
|
| 333 |
+
device=context.device,
|
| 334 |
+
)
|
| 335 |
+
type_features = self.proposal_type_embedding(indices)
|
| 336 |
+
fused = context.unsqueeze(1) + type_features.unsqueeze(0)
|
| 337 |
+
flat = fused.reshape(context.shape[0] * len(requested), context.shape[-1])
|
| 338 |
+
proposals = self.proposal_head(flat)
|
| 339 |
+
return proposals.reshape(
|
| 340 |
+
context.shape[0],
|
| 341 |
+
len(requested),
|
| 342 |
+
self.config.action_horizon,
|
| 343 |
+
self.config.action_dim,
|
| 344 |
+
)
|
| 345 |
+
|
| 346 |
def forward_effect(self, observation, instruction, action, skill_type=None):
|
| 347 |
z = self.encode_intervention(observation, instruction, action, skill_type=skill_type)
|
| 348 |
return self.interventional_field(z)
|
dovla_cil/training/losses.py
CHANGED
|
@@ -373,6 +373,7 @@ class InterventionalLossWeights:
|
|
| 373 |
field_preference: float = 1.0
|
| 374 |
field_effect: float = 1.0
|
| 375 |
field_anchor: float = 0.25
|
|
|
|
| 376 |
# Backward-compatible aliases from the first config schema.
|
| 377 |
bc_best_action: float = 1.0
|
| 378 |
forward_effect_prediction: float = 1.0
|
|
|
|
| 373 |
field_preference: float = 1.0
|
| 374 |
field_effect: float = 1.0
|
| 375 |
field_anchor: float = 0.25
|
| 376 |
+
proposal: float = 0.0
|
| 377 |
# Backward-compatible aliases from the first config schema.
|
| 378 |
bc_best_action: float = 1.0
|
| 379 |
forward_effect_prediction: float = 1.0
|
dovla_cil/training/trainer.py
CHANGED
|
@@ -67,6 +67,7 @@ class TrainerConfig:
|
|
| 67 |
backbone_feature_batch_size: int = 64
|
| 68 |
policy_target_types: tuple[str, ...] = ()
|
| 69 |
policy_target_map: str | Path | None = None
|
|
|
|
| 70 |
|
| 71 |
def __post_init__(self) -> None:
|
| 72 |
if self.lr is not None:
|
|
@@ -106,6 +107,14 @@ class TrainerConfig:
|
|
| 106 |
for item in self.policy_target_types.split(",")
|
| 107 |
if item.strip()
|
| 108 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
if self.policy_target_map is not None:
|
| 110 |
self.policy_target_map = Path(self.policy_target_map)
|
| 111 |
|
|
@@ -141,6 +150,7 @@ class DoVLATrainer:
|
|
| 141 |
backbone_model=config.backbone_model,
|
| 142 |
backbone_freeze=config.backbone_freeze,
|
| 143 |
backbone_local_files_only=config.backbone_local_files_only,
|
|
|
|
| 144 |
)
|
| 145 |
self.model = None
|
| 146 |
self.optimizer = None
|
|
@@ -301,6 +311,7 @@ class DoVLATrainer:
|
|
| 301 |
pred_best_actions = self.model.forward_policy(best_obs, best_instructions)
|
| 302 |
|
| 303 |
bc = behavior_cloning_loss(pred_best_actions, best_actions)
|
|
|
|
| 304 |
effect = effect_prediction_loss(effect_outputs["effect_vector"], effect_target)
|
| 305 |
success = success_loss(effect_outputs["success_logit"], target_success)
|
| 306 |
progress = progress_loss(effect_outputs["progress"], target_progress)
|
|
@@ -317,10 +328,13 @@ class DoVLATrainer:
|
|
| 317 |
action_features=action,
|
| 318 |
neighbors_per_node=self.config.lattice_neighbors,
|
| 319 |
)
|
|
|
|
|
|
|
| 320 |
if self.config.objective == "lattice_field":
|
| 321 |
anchor = float(weights.field_anchor)
|
| 322 |
total = (
|
| 323 |
weights.weight("bc") * bc
|
|
|
|
| 324 |
+ float(weights.field_potential) * field["potential"]
|
| 325 |
+ float(weights.field_preference) * field["preference"]
|
| 326 |
+ float(weights.field_effect) * field["effect"]
|
|
@@ -334,6 +348,7 @@ class DoVLATrainer:
|
|
| 334 |
else:
|
| 335 |
total = (
|
| 336 |
weights.weight("bc") * bc
|
|
|
|
| 337 |
+ weights.weight("effect") * effect
|
| 338 |
+ weights.weight("success") * success
|
| 339 |
+ weights.weight("progress") * progress
|
|
@@ -343,6 +358,7 @@ class DoVLATrainer:
|
|
| 343 |
metrics = self._batch_metrics(
|
| 344 |
total=total,
|
| 345 |
bc=bc,
|
|
|
|
| 346 |
rank=rank,
|
| 347 |
pred_reward=pred_reward,
|
| 348 |
pred_progress=effect_outputs["progress"],
|
|
@@ -360,6 +376,62 @@ class DoVLATrainer:
|
|
| 360 |
)
|
| 361 |
return total, metrics
|
| 362 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
def _policy_bc_targets(self, records: list[CILRecord]) -> tuple[list[CILRecord], Any]:
|
| 364 |
assert torch is not None
|
| 365 |
target_records: list[CILRecord] = []
|
|
@@ -421,6 +493,7 @@ class DoVLATrainer:
|
|
| 421 |
*,
|
| 422 |
total,
|
| 423 |
bc,
|
|
|
|
| 424 |
rank,
|
| 425 |
pred_reward,
|
| 426 |
pred_progress,
|
|
@@ -446,6 +519,7 @@ class DoVLATrainer:
|
|
| 446 |
return {
|
| 447 |
"total_loss": float(total.detach().cpu()),
|
| 448 |
"bc_loss": float(bc.detach().cpu()),
|
|
|
|
| 449 |
"rank_loss": float(rank.detach().cpu()),
|
| 450 |
"rank_acc": float(rank_acc),
|
| 451 |
"progress_mae": float(progress_mae),
|
|
@@ -686,6 +760,7 @@ class _MetricAccumulator:
|
|
| 686 |
return {
|
| 687 |
"total_loss": 0.0,
|
| 688 |
"bc_loss": 0.0,
|
|
|
|
| 689 |
"rank_loss": 0.0,
|
| 690 |
"rank_acc": 0.0,
|
| 691 |
"progress_mae": 0.0,
|
|
|
|
| 67 |
backbone_feature_batch_size: int = 64
|
| 68 |
policy_target_types: tuple[str, ...] = ()
|
| 69 |
policy_target_map: str | Path | None = None
|
| 70 |
+
proposal_types: tuple[str, ...] = ()
|
| 71 |
|
| 72 |
def __post_init__(self) -> None:
|
| 73 |
if self.lr is not None:
|
|
|
|
| 107 |
for item in self.policy_target_types.split(",")
|
| 108 |
if item.strip()
|
| 109 |
)
|
| 110 |
+
if isinstance(self.proposal_types, str):
|
| 111 |
+
self.proposal_types = tuple(
|
| 112 |
+
item.strip()
|
| 113 |
+
for item in self.proposal_types.split(",")
|
| 114 |
+
if item.strip()
|
| 115 |
+
)
|
| 116 |
+
else:
|
| 117 |
+
self.proposal_types = tuple(str(item) for item in self.proposal_types)
|
| 118 |
if self.policy_target_map is not None:
|
| 119 |
self.policy_target_map = Path(self.policy_target_map)
|
| 120 |
|
|
|
|
| 150 |
backbone_model=config.backbone_model,
|
| 151 |
backbone_freeze=config.backbone_freeze,
|
| 152 |
backbone_local_files_only=config.backbone_local_files_only,
|
| 153 |
+
proposal_types=config.proposal_types,
|
| 154 |
)
|
| 155 |
self.model = None
|
| 156 |
self.optimizer = None
|
|
|
|
| 311 |
pred_best_actions = self.model.forward_policy(best_obs, best_instructions)
|
| 312 |
|
| 313 |
bc = behavior_cloning_loss(pred_best_actions, best_actions)
|
| 314 |
+
proposal = bc * 0.0
|
| 315 |
effect = effect_prediction_loss(effect_outputs["effect_vector"], effect_target)
|
| 316 |
success = success_loss(effect_outputs["success_logit"], target_success)
|
| 317 |
progress = progress_loss(effect_outputs["progress"], target_progress)
|
|
|
|
| 328 |
action_features=action,
|
| 329 |
neighbors_per_node=self.config.lattice_neighbors,
|
| 330 |
)
|
| 331 |
+
if self.config.proposal_types and float(weights.proposal) > 0.0:
|
| 332 |
+
proposal = self._proposal_bc_loss(records)
|
| 333 |
if self.config.objective == "lattice_field":
|
| 334 |
anchor = float(weights.field_anchor)
|
| 335 |
total = (
|
| 336 |
weights.weight("bc") * bc
|
| 337 |
+
+ float(weights.proposal) * proposal
|
| 338 |
+ float(weights.field_potential) * field["potential"]
|
| 339 |
+ float(weights.field_preference) * field["preference"]
|
| 340 |
+ float(weights.field_effect) * field["effect"]
|
|
|
|
| 348 |
else:
|
| 349 |
total = (
|
| 350 |
weights.weight("bc") * bc
|
| 351 |
+
+ float(weights.proposal) * proposal
|
| 352 |
+ weights.weight("effect") * effect
|
| 353 |
+ weights.weight("success") * success
|
| 354 |
+ weights.weight("progress") * progress
|
|
|
|
| 358 |
metrics = self._batch_metrics(
|
| 359 |
total=total,
|
| 360 |
bc=bc,
|
| 361 |
+
proposal=proposal,
|
| 362 |
rank=rank,
|
| 363 |
pred_reward=pred_reward,
|
| 364 |
pred_progress=effect_outputs["progress"],
|
|
|
|
| 376 |
)
|
| 377 |
return total, metrics
|
| 378 |
|
| 379 |
+
def _proposal_bc_loss(self, records: list[CILRecord]) -> Any:
|
| 380 |
+
assert torch is not None
|
| 381 |
+
assert self.model is not None
|
| 382 |
+
grouped: dict[str, CILRecord] = {}
|
| 383 |
+
for record in records:
|
| 384 |
+
grouped.setdefault(record.group_id, record)
|
| 385 |
+
input_records: list[CILRecord] = []
|
| 386 |
+
target_values: list[list[list[list[float]]]] = []
|
| 387 |
+
target_mask: list[list[float]] = []
|
| 388 |
+
zero_action = [
|
| 389 |
+
[0.0 for _ in range(self.config.action_dim)]
|
| 390 |
+
for _ in range(self.config.action_horizon)
|
| 391 |
+
]
|
| 392 |
+
for group_id, fallback_record in grouped.items():
|
| 393 |
+
try:
|
| 394 |
+
group_records = self.dataset.get_group(group_id)
|
| 395 |
+
except KeyError:
|
| 396 |
+
group_records = [record for record in records if record.group_id == group_id]
|
| 397 |
+
input_records.append(group_records[0] if group_records else fallback_record)
|
| 398 |
+
group_targets: list[list[list[float]]] = []
|
| 399 |
+
group_mask: list[float] = []
|
| 400 |
+
for proposal_type in self.config.proposal_types:
|
| 401 |
+
candidates = [
|
| 402 |
+
record
|
| 403 |
+
for record in group_records
|
| 404 |
+
if record.candidate_type == proposal_type
|
| 405 |
+
]
|
| 406 |
+
if candidates:
|
| 407 |
+
target = max(candidates, key=_record_score)
|
| 408 |
+
group_targets.append(
|
| 409 |
+
vectorize_toy_action(
|
| 410 |
+
target.action_chunk,
|
| 411 |
+
action_dim=self.config.action_dim,
|
| 412 |
+
action_horizon=self.config.action_horizon,
|
| 413 |
+
)
|
| 414 |
+
)
|
| 415 |
+
group_mask.append(1.0)
|
| 416 |
+
else:
|
| 417 |
+
group_targets.append(zero_action)
|
| 418 |
+
group_mask.append(0.0)
|
| 419 |
+
target_values.append(group_targets)
|
| 420 |
+
target_mask.append(group_mask)
|
| 421 |
+
|
| 422 |
+
if not input_records or not any(any(row) for row in target_mask):
|
| 423 |
+
return self.model.forward_policy(
|
| 424 |
+
self._obs_tensor(records[:1]),
|
| 425 |
+
[records[0].instruction] if records else [""],
|
| 426 |
+
).sum() * 0.0
|
| 427 |
+
predictions = self.model.forward_proposals(
|
| 428 |
+
self._obs_tensor(input_records),
|
| 429 |
+
[record.instruction for record in input_records],
|
| 430 |
+
)
|
| 431 |
+
targets = torch.tensor(target_values, dtype=torch.float32, device=self.device)
|
| 432 |
+
mask = torch.tensor(target_mask, dtype=torch.float32, device=self.device)
|
| 433 |
+
return behavior_cloning_loss(predictions, targets, mask=mask)
|
| 434 |
+
|
| 435 |
def _policy_bc_targets(self, records: list[CILRecord]) -> tuple[list[CILRecord], Any]:
|
| 436 |
assert torch is not None
|
| 437 |
target_records: list[CILRecord] = []
|
|
|
|
| 493 |
*,
|
| 494 |
total,
|
| 495 |
bc,
|
| 496 |
+
proposal,
|
| 497 |
rank,
|
| 498 |
pred_reward,
|
| 499 |
pred_progress,
|
|
|
|
| 519 |
return {
|
| 520 |
"total_loss": float(total.detach().cpu()),
|
| 521 |
"bc_loss": float(bc.detach().cpu()),
|
| 522 |
+
"proposal_loss": float(proposal.detach().cpu()),
|
| 523 |
"rank_loss": float(rank.detach().cpu()),
|
| 524 |
"rank_acc": float(rank_acc),
|
| 525 |
"progress_mae": float(progress_mae),
|
|
|
|
| 760 |
return {
|
| 761 |
"total_loss": 0.0,
|
| 762 |
"bc_loss": 0.0,
|
| 763 |
+
"proposal_loss": 0.0,
|
| 764 |
"rank_loss": 0.0,
|
| 765 |
"rank_acc": 0.0,
|
| 766 |
"progress_mae": 0.0,
|
logs/auto_sync_hf.log
CHANGED
|
@@ -260,3 +260,4 @@ No files have been modified since last commit. Skipping to prevent empty commit.
|
|
| 260 |
No files have been modified since last commit. Skipping to prevent empty commit.
|
| 261 |
No files have been modified since last commit. Skipping to prevent empty commit.
|
| 262 |
No files have been modified since last commit. Skipping to prevent empty commit.
|
|
|
|
|
|
| 260 |
No files have been modified since last commit. Skipping to prevent empty commit.
|
| 261 |
No files have been modified since last commit. Skipping to prevent empty commit.
|
| 262 |
No files have been modified since last commit. Skipping to prevent empty commit.
|
| 263 |
+
No files have been modified since last commit. Skipping to prevent empty commit.
|