Auto-sync: 2026-06-28 01:47:01
Browse files
dovla_cil/eval/maniskill_policy_rollout.py
CHANGED
|
@@ -60,6 +60,7 @@ def evaluate_maniskill_policy_rollout(
|
|
| 60 |
field_optim_trust_radius: float = 0.5,
|
| 61 |
field_optim_l2_penalty: float = 0.0,
|
| 62 |
retrieval_neighbors: int = 1,
|
|
|
|
| 63 |
retrieval_residual_scale: float = 1.0,
|
| 64 |
lattice_exclude_types: tuple[str, ...] = (),
|
| 65 |
) -> dict[str, Any]:
|
|
@@ -85,7 +86,9 @@ def evaluate_maniskill_policy_rollout(
|
|
| 85 |
When ``selection_mode == 'retrieval_lattice'`` action proposals come from the nearest
|
| 86 |
training-split state with the same task rather than the evaluated state's own lattice. This
|
| 87 |
tests whether the field can use reusable intervention proposals without same-state proposal
|
| 88 |
-
leakage.
|
|
|
|
|
|
|
| 89 |
|
| 90 |
When ``selection_mode == 'retrieval_residual'`` the evaluator retrieves counterfactual
|
| 91 |
action residuals (candidate minus expert action) from the nearest training-split state(s),
|
|
@@ -135,6 +138,8 @@ def evaluate_maniskill_policy_rollout(
|
|
| 135 |
raise ValueError("field_optim_l2_penalty must be non-negative")
|
| 136 |
if retrieval_neighbors <= 0:
|
| 137 |
raise ValueError("retrieval_neighbors must be positive")
|
|
|
|
|
|
|
| 138 |
if retrieval_residual_scale < 0:
|
| 139 |
raise ValueError("retrieval_residual_scale must be non-negative")
|
| 140 |
if selection_mode == "policy":
|
|
@@ -184,6 +189,7 @@ def evaluate_maniskill_policy_rollout(
|
|
| 184 |
obs_dim=model_config.obs_dim,
|
| 185 |
observation_mode=model_config.observation_mode,
|
| 186 |
retrieval_neighbors=retrieval_neighbors,
|
|
|
|
| 187 |
)
|
| 188 |
else:
|
| 189 |
cases = _attach_retrieved_residual_candidates(
|
|
@@ -193,6 +199,7 @@ def evaluate_maniskill_policy_rollout(
|
|
| 193 |
obs_dim=model_config.obs_dim,
|
| 194 |
observation_mode=model_config.observation_mode,
|
| 195 |
retrieval_neighbors=retrieval_neighbors,
|
|
|
|
| 196 |
)
|
| 197 |
by_task: dict[str, list[_RolloutCase]] = defaultdict(list)
|
| 198 |
for case in cases:
|
|
@@ -265,6 +272,9 @@ def evaluate_maniskill_policy_rollout(
|
|
| 265 |
"retrieval_neighbors": retrieval_neighbors
|
| 266 |
if selection_mode in {"retrieval_lattice", "retrieval_residual"}
|
| 267 |
else 0,
|
|
|
|
|
|
|
|
|
|
| 268 |
"retrieval_residual_scale": retrieval_residual_scale
|
| 269 |
if selection_mode == "retrieval_residual"
|
| 270 |
else 0.0,
|
|
@@ -355,6 +365,7 @@ def _attach_retrieved_lattice_candidates(
|
|
| 355 |
obs_dim: int,
|
| 356 |
observation_mode: str,
|
| 357 |
retrieval_neighbors: int,
|
|
|
|
| 358 |
) -> list[_RolloutCase]:
|
| 359 |
if observation_mode != "state":
|
| 360 |
raise ValueError("retrieval_lattice currently supports state observations only")
|
|
@@ -394,10 +405,12 @@ def _attach_retrieved_lattice_candidates(
|
|
| 394 |
vectorize_toy_observation(case.observation, obs_dim=obs_dim),
|
| 395 |
dtype=np.float32,
|
| 396 |
)
|
| 397 |
-
nearest =
|
| 398 |
candidates,
|
| 399 |
-
|
| 400 |
-
|
|
|
|
|
|
|
| 401 |
source_group_ids: list[str] = []
|
| 402 |
actions: list[list[list[float]]] = []
|
| 403 |
candidate_types: list[str] = []
|
|
@@ -424,6 +437,7 @@ def _attach_retrieved_residual_candidates(
|
|
| 424 |
obs_dim: int,
|
| 425 |
observation_mode: str,
|
| 426 |
retrieval_neighbors: int,
|
|
|
|
| 427 |
) -> list[_RolloutCase]:
|
| 428 |
if observation_mode != "state":
|
| 429 |
raise ValueError("retrieval_residual currently supports state observations only")
|
|
@@ -474,10 +488,12 @@ def _attach_retrieved_residual_candidates(
|
|
| 474 |
vectorize_toy_observation(case.observation, obs_dim=obs_dim),
|
| 475 |
dtype=np.float32,
|
| 476 |
)
|
| 477 |
-
nearest =
|
| 478 |
candidates,
|
| 479 |
-
|
| 480 |
-
|
|
|
|
|
|
|
| 481 |
source_group_ids: list[str] = []
|
| 482 |
residuals: list[list[list[float]]] = []
|
| 483 |
candidate_types: list[str] = []
|
|
@@ -496,6 +512,30 @@ def _attach_retrieved_residual_candidates(
|
|
| 496 |
return output
|
| 497 |
|
| 498 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 499 |
def _evaluate_task_cases(
|
| 500 |
task_id: str,
|
| 501 |
cases: list[_RolloutCase],
|
|
|
|
| 60 |
field_optim_trust_radius: float = 0.5,
|
| 61 |
field_optim_l2_penalty: float = 0.0,
|
| 62 |
retrieval_neighbors: int = 1,
|
| 63 |
+
retrieval_metric: str = "raw",
|
| 64 |
retrieval_residual_scale: float = 1.0,
|
| 65 |
lattice_exclude_types: tuple[str, ...] = (),
|
| 66 |
) -> dict[str, Any]:
|
|
|
|
| 86 |
When ``selection_mode == 'retrieval_lattice'`` action proposals come from the nearest
|
| 87 |
training-split state with the same task rather than the evaluated state's own lattice. This
|
| 88 |
tests whether the field can use reusable intervention proposals without same-state proposal
|
| 89 |
+
leakage. ``retrieval_metric='zscore'`` standardizes state features by the train-bank
|
| 90 |
+
statistics for each task before nearest-neighbor lookup; the default ``raw`` metric
|
| 91 |
+
preserves earlier results exactly.
|
| 92 |
|
| 93 |
When ``selection_mode == 'retrieval_residual'`` the evaluator retrieves counterfactual
|
| 94 |
action residuals (candidate minus expert action) from the nearest training-split state(s),
|
|
|
|
| 138 |
raise ValueError("field_optim_l2_penalty must be non-negative")
|
| 139 |
if retrieval_neighbors <= 0:
|
| 140 |
raise ValueError("retrieval_neighbors must be positive")
|
| 141 |
+
if retrieval_metric not in {"raw", "zscore"}:
|
| 142 |
+
raise ValueError("retrieval_metric must be 'raw' or 'zscore'")
|
| 143 |
if retrieval_residual_scale < 0:
|
| 144 |
raise ValueError("retrieval_residual_scale must be non-negative")
|
| 145 |
if selection_mode == "policy":
|
|
|
|
| 189 |
obs_dim=model_config.obs_dim,
|
| 190 |
observation_mode=model_config.observation_mode,
|
| 191 |
retrieval_neighbors=retrieval_neighbors,
|
| 192 |
+
retrieval_metric=retrieval_metric,
|
| 193 |
)
|
| 194 |
else:
|
| 195 |
cases = _attach_retrieved_residual_candidates(
|
|
|
|
| 199 |
obs_dim=model_config.obs_dim,
|
| 200 |
observation_mode=model_config.observation_mode,
|
| 201 |
retrieval_neighbors=retrieval_neighbors,
|
| 202 |
+
retrieval_metric=retrieval_metric,
|
| 203 |
)
|
| 204 |
by_task: dict[str, list[_RolloutCase]] = defaultdict(list)
|
| 205 |
for case in cases:
|
|
|
|
| 272 |
"retrieval_neighbors": retrieval_neighbors
|
| 273 |
if selection_mode in {"retrieval_lattice", "retrieval_residual"}
|
| 274 |
else 0,
|
| 275 |
+
"retrieval_metric": retrieval_metric
|
| 276 |
+
if selection_mode in {"retrieval_lattice", "retrieval_residual"}
|
| 277 |
+
else "none",
|
| 278 |
"retrieval_residual_scale": retrieval_residual_scale
|
| 279 |
if selection_mode == "retrieval_residual"
|
| 280 |
else 0.0,
|
|
|
|
| 365 |
obs_dim: int,
|
| 366 |
observation_mode: str,
|
| 367 |
retrieval_neighbors: int,
|
| 368 |
+
retrieval_metric: str = "raw",
|
| 369 |
) -> list[_RolloutCase]:
|
| 370 |
if observation_mode != "state":
|
| 371 |
raise ValueError("retrieval_lattice currently supports state observations only")
|
|
|
|
| 405 |
vectorize_toy_observation(case.observation, obs_dim=obs_dim),
|
| 406 |
dtype=np.float32,
|
| 407 |
)
|
| 408 |
+
nearest = _nearest_retrieval_entries(
|
| 409 |
candidates,
|
| 410 |
+
query,
|
| 411 |
+
retrieval_neighbors=retrieval_neighbors,
|
| 412 |
+
retrieval_metric=retrieval_metric,
|
| 413 |
+
)
|
| 414 |
source_group_ids: list[str] = []
|
| 415 |
actions: list[list[list[float]]] = []
|
| 416 |
candidate_types: list[str] = []
|
|
|
|
| 437 |
obs_dim: int,
|
| 438 |
observation_mode: str,
|
| 439 |
retrieval_neighbors: int,
|
| 440 |
+
retrieval_metric: str = "raw",
|
| 441 |
) -> list[_RolloutCase]:
|
| 442 |
if observation_mode != "state":
|
| 443 |
raise ValueError("retrieval_residual currently supports state observations only")
|
|
|
|
| 488 |
vectorize_toy_observation(case.observation, obs_dim=obs_dim),
|
| 489 |
dtype=np.float32,
|
| 490 |
)
|
| 491 |
+
nearest = _nearest_retrieval_entries(
|
| 492 |
candidates,
|
| 493 |
+
query,
|
| 494 |
+
retrieval_neighbors=retrieval_neighbors,
|
| 495 |
+
retrieval_metric=retrieval_metric,
|
| 496 |
+
)
|
| 497 |
source_group_ids: list[str] = []
|
| 498 |
residuals: list[list[list[float]]] = []
|
| 499 |
candidate_types: list[str] = []
|
|
|
|
| 512 |
return output
|
| 513 |
|
| 514 |
|
| 515 |
+
def _nearest_retrieval_entries(
|
| 516 |
+
candidates: list[tuple[Any, np.ndarray, Any, Any]],
|
| 517 |
+
query: np.ndarray,
|
| 518 |
+
*,
|
| 519 |
+
retrieval_neighbors: int,
|
| 520 |
+
retrieval_metric: str,
|
| 521 |
+
) -> list[tuple[Any, np.ndarray, Any, Any]]:
|
| 522 |
+
if retrieval_metric == "raw":
|
| 523 |
+
return sorted(
|
| 524 |
+
candidates,
|
| 525 |
+
key=lambda item: float(np.mean((item[1] - query) ** 2)),
|
| 526 |
+
)[:retrieval_neighbors]
|
| 527 |
+
if retrieval_metric != "zscore":
|
| 528 |
+
raise ValueError("retrieval_metric must be 'raw' or 'zscore'")
|
| 529 |
+
features = np.stack([np.asarray(item[1], dtype=np.float32) for item in candidates], axis=0)
|
| 530 |
+
mean = features.mean(axis=0, dtype=np.float64).astype(np.float32)
|
| 531 |
+
std = features.std(axis=0, dtype=np.float64).astype(np.float32)
|
| 532 |
+
scale = np.where(std > 1e-6, std, 1.0).astype(np.float32)
|
| 533 |
+
normalized_features = (features - mean) / scale
|
| 534 |
+
normalized_query = (np.asarray(query, dtype=np.float32) - mean) / scale
|
| 535 |
+
order = np.argsort(np.mean((normalized_features - normalized_query) ** 2, axis=1))
|
| 536 |
+
return [candidates[int(index)] for index in order[:retrieval_neighbors]]
|
| 537 |
+
|
| 538 |
+
|
| 539 |
def _evaluate_task_cases(
|
| 540 |
task_id: str,
|
| 541 |
cases: list[_RolloutCase],
|