Auto-sync: 2026-06-28 22:04:20
Browse files
dovla_cil/eval/maniskill_policy_rollout.py
CHANGED
|
@@ -27,6 +27,15 @@ _FIELD_CONDITIONED_RESIDUAL_REDUCERS = {"field_softmax"}
|
|
| 27 |
_RESIDUAL_REDUCERS = (
|
| 28 |
{"none"} | _NUMPY_RESIDUAL_REDUCERS | _FIELD_CONDITIONED_RESIDUAL_REDUCERS
|
| 29 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
|
| 32 |
@dataclass(frozen=True)
|
|
@@ -101,7 +110,8 @@ def evaluate_maniskill_policy_rollout(
|
|
| 101 |
training-split state with the same task rather than the evaluated state's own lattice. This
|
| 102 |
tests whether the field can use reusable intervention proposals without same-state proposal
|
| 103 |
leakage. ``retrieval_metric='zscore'`` standardizes state features by the train-bank
|
| 104 |
-
statistics for each task before nearest-neighbor lookup;
|
|
|
|
| 105 |
preserves earlier results exactly.
|
| 106 |
|
| 107 |
When ``selection_mode == 'retrieval_residual'`` the evaluator retrieves counterfactual
|
|
@@ -159,8 +169,8 @@ def evaluate_maniskill_policy_rollout(
|
|
| 159 |
raise ValueError("selection_margin must be non-negative")
|
| 160 |
if retrieval_neighbors <= 0:
|
| 161 |
raise ValueError("retrieval_neighbors must be positive")
|
| 162 |
-
if retrieval_metric not in
|
| 163 |
-
raise ValueError("retrieval_metric must be 'raw' or '
|
| 164 |
if retrieval_residual_anchor not in {"expert", "policy"}:
|
| 165 |
raise ValueError("retrieval_residual_anchor must be 'expert' or 'policy'")
|
| 166 |
if retrieval_residual_reduce not in _RESIDUAL_REDUCERS:
|
|
@@ -476,6 +486,7 @@ def _attach_retrieved_lattice_candidates(
|
|
| 476 |
query,
|
| 477 |
retrieval_neighbors=retrieval_neighbors,
|
| 478 |
retrieval_metric=retrieval_metric,
|
|
|
|
| 479 |
)
|
| 480 |
source_group_ids: list[str] = []
|
| 481 |
actions: list[list[list[float]]] = []
|
|
@@ -571,6 +582,7 @@ def _attach_retrieved_residual_candidates(
|
|
| 571 |
query,
|
| 572 |
retrieval_neighbors=retrieval_neighbors,
|
| 573 |
retrieval_metric=retrieval_metric,
|
|
|
|
| 574 |
)
|
| 575 |
source_group_ids: list[str] = []
|
| 576 |
residuals: list[list[list[float]]] = []
|
|
@@ -684,6 +696,7 @@ def _nearest_retrieval_entries(
|
|
| 684 |
*,
|
| 685 |
retrieval_neighbors: int,
|
| 686 |
retrieval_metric: str,
|
|
|
|
| 687 |
) -> list[tuple[Any, np.ndarray, Any, Any]]:
|
| 688 |
return [
|
| 689 |
entry
|
|
@@ -692,6 +705,7 @@ def _nearest_retrieval_entries(
|
|
| 692 |
query,
|
| 693 |
retrieval_neighbors=retrieval_neighbors,
|
| 694 |
retrieval_metric=retrieval_metric,
|
|
|
|
| 695 |
)
|
| 696 |
]
|
| 697 |
|
|
@@ -702,6 +716,7 @@ def _nearest_retrieval_entries_with_distances(
|
|
| 702 |
*,
|
| 703 |
retrieval_neighbors: int,
|
| 704 |
retrieval_metric: str,
|
|
|
|
| 705 |
) -> list[tuple[tuple[Any, np.ndarray, Any, Any], float]]:
|
| 706 |
if retrieval_metric == "raw":
|
| 707 |
scored = [
|
|
@@ -709,8 +724,26 @@ def _nearest_retrieval_entries_with_distances(
|
|
| 709 |
for item in candidates
|
| 710 |
]
|
| 711 |
return sorted(scored, key=lambda item: item[1])[:retrieval_neighbors]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 712 |
if retrieval_metric != "zscore":
|
| 713 |
-
raise ValueError("retrieval_metric must be 'raw' or '
|
| 714 |
features = np.stack([np.asarray(item[1], dtype=np.float32) for item in candidates], axis=0)
|
| 715 |
mean = features.mean(axis=0, dtype=np.float64).astype(np.float32)
|
| 716 |
std = features.std(axis=0, dtype=np.float64).astype(np.float32)
|
|
@@ -725,6 +758,38 @@ def _nearest_retrieval_entries_with_distances(
|
|
| 725 |
]
|
| 726 |
|
| 727 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 728 |
def _kernel_weights_from_distances(distances: list[float]) -> list[float]:
|
| 729 |
if not distances:
|
| 730 |
return []
|
|
|
|
| 27 |
_RESIDUAL_REDUCERS = (
|
| 28 |
{"none"} | _NUMPY_RESIDUAL_REDUCERS | _FIELD_CONDITIONED_RESIDUAL_REDUCERS
|
| 29 |
)
|
| 30 |
+
_RETRIEVAL_METRICS = {"raw", "zscore", "task_relative"}
|
| 31 |
+
_MANISKILL_ACTOR_STATE_DIM = 13
|
| 32 |
+
_TASKS_WITH_REFERENCE_ACTOR = {
|
| 33 |
+
"PickCube-v1",
|
| 34 |
+
"PushCube-v1",
|
| 35 |
+
"PullCube-v1",
|
| 36 |
+
"StackCube-v1",
|
| 37 |
+
"PegInsertionSide-v1",
|
| 38 |
+
}
|
| 39 |
|
| 40 |
|
| 41 |
@dataclass(frozen=True)
|
|
|
|
| 110 |
training-split state with the same task rather than the evaluated state's own lattice. This
|
| 111 |
tests whether the field can use reusable intervention proposals without same-state proposal
|
| 112 |
leakage. ``retrieval_metric='zscore'`` standardizes state features by the train-bank
|
| 113 |
+
statistics for each task before nearest-neighbor lookup; ``task_relative`` retrieves by
|
| 114 |
+
target/reference actor pose blocks rather than full robot state. The default ``raw`` metric
|
| 115 |
preserves earlier results exactly.
|
| 116 |
|
| 117 |
When ``selection_mode == 'retrieval_residual'`` the evaluator retrieves counterfactual
|
|
|
|
| 169 |
raise ValueError("selection_margin must be non-negative")
|
| 170 |
if retrieval_neighbors <= 0:
|
| 171 |
raise ValueError("retrieval_neighbors must be positive")
|
| 172 |
+
if retrieval_metric not in _RETRIEVAL_METRICS:
|
| 173 |
+
raise ValueError("retrieval_metric must be 'raw', 'zscore', or 'task_relative'")
|
| 174 |
if retrieval_residual_anchor not in {"expert", "policy"}:
|
| 175 |
raise ValueError("retrieval_residual_anchor must be 'expert' or 'policy'")
|
| 176 |
if retrieval_residual_reduce not in _RESIDUAL_REDUCERS:
|
|
|
|
| 486 |
query,
|
| 487 |
retrieval_neighbors=retrieval_neighbors,
|
| 488 |
retrieval_metric=retrieval_metric,
|
| 489 |
+
task_id=case.task_id,
|
| 490 |
)
|
| 491 |
source_group_ids: list[str] = []
|
| 492 |
actions: list[list[list[float]]] = []
|
|
|
|
| 582 |
query,
|
| 583 |
retrieval_neighbors=retrieval_neighbors,
|
| 584 |
retrieval_metric=retrieval_metric,
|
| 585 |
+
task_id=case.task_id,
|
| 586 |
)
|
| 587 |
source_group_ids: list[str] = []
|
| 588 |
residuals: list[list[list[float]]] = []
|
|
|
|
| 696 |
*,
|
| 697 |
retrieval_neighbors: int,
|
| 698 |
retrieval_metric: str,
|
| 699 |
+
task_id: str | None = None,
|
| 700 |
) -> list[tuple[Any, np.ndarray, Any, Any]]:
|
| 701 |
return [
|
| 702 |
entry
|
|
|
|
| 705 |
query,
|
| 706 |
retrieval_neighbors=retrieval_neighbors,
|
| 707 |
retrieval_metric=retrieval_metric,
|
| 708 |
+
task_id=task_id,
|
| 709 |
)
|
| 710 |
]
|
| 711 |
|
|
|
|
| 716 |
*,
|
| 717 |
retrieval_neighbors: int,
|
| 718 |
retrieval_metric: str,
|
| 719 |
+
task_id: str | None = None,
|
| 720 |
) -> list[tuple[tuple[Any, np.ndarray, Any, Any], float]]:
|
| 721 |
if retrieval_metric == "raw":
|
| 722 |
scored = [
|
|
|
|
| 724 |
for item in candidates
|
| 725 |
]
|
| 726 |
return sorted(scored, key=lambda item: item[1])[:retrieval_neighbors]
|
| 727 |
+
if retrieval_metric == "task_relative":
|
| 728 |
+
normalized_query = _task_relative_retrieval_feature(query, task_id=task_id)
|
| 729 |
+
scored = [
|
| 730 |
+
(
|
| 731 |
+
item,
|
| 732 |
+
float(
|
| 733 |
+
np.mean(
|
| 734 |
+
(
|
| 735 |
+
_task_relative_retrieval_feature(item[1], task_id=task_id)
|
| 736 |
+
- normalized_query
|
| 737 |
+
)
|
| 738 |
+
** 2
|
| 739 |
+
)
|
| 740 |
+
),
|
| 741 |
+
)
|
| 742 |
+
for item in candidates
|
| 743 |
+
]
|
| 744 |
+
return sorted(scored, key=lambda item: item[1])[:retrieval_neighbors]
|
| 745 |
if retrieval_metric != "zscore":
|
| 746 |
+
raise ValueError("retrieval_metric must be 'raw', 'zscore', or 'task_relative'")
|
| 747 |
features = np.stack([np.asarray(item[1], dtype=np.float32) for item in candidates], axis=0)
|
| 748 |
mean = features.mean(axis=0, dtype=np.float64).astype(np.float32)
|
| 749 |
std = features.std(axis=0, dtype=np.float64).astype(np.float32)
|
|
|
|
| 758 |
]
|
| 759 |
|
| 760 |
|
| 761 |
+
def _task_relative_retrieval_feature(
|
| 762 |
+
feature: np.ndarray,
|
| 763 |
+
*,
|
| 764 |
+
task_id: str | None,
|
| 765 |
+
) -> np.ndarray:
|
| 766 |
+
values = np.asarray(feature, dtype=np.float32).reshape(-1)
|
| 767 |
+
if values.size < _MANISKILL_ACTOR_STATE_DIM:
|
| 768 |
+
return values
|
| 769 |
+
target = values[:_MANISKILL_ACTOR_STATE_DIM]
|
| 770 |
+
parts = [
|
| 771 |
+
4.0 * target[:3],
|
| 772 |
+
target[3:7],
|
| 773 |
+
]
|
| 774 |
+
has_reference = (
|
| 775 |
+
task_id in _TASKS_WITH_REFERENCE_ACTOR
|
| 776 |
+
and values.size >= 2 * _MANISKILL_ACTOR_STATE_DIM
|
| 777 |
+
)
|
| 778 |
+
if has_reference:
|
| 779 |
+
start = _MANISKILL_ACTOR_STATE_DIM
|
| 780 |
+
reference = values[start : start + _MANISKILL_ACTOR_STATE_DIM]
|
| 781 |
+
parts.extend(
|
| 782 |
+
[
|
| 783 |
+
2.0 * reference[:3],
|
| 784 |
+
4.0 * (target[:3] - reference[:3]),
|
| 785 |
+
reference[3:7],
|
| 786 |
+
]
|
| 787 |
+
)
|
| 788 |
+
if task_id in {"LiftPegUpright-v1", "PegInsertionSide-v1"}:
|
| 789 |
+
parts.append(2.0 * target[3:7])
|
| 790 |
+
return np.concatenate(parts).astype(np.float32)
|
| 791 |
+
|
| 792 |
+
|
| 793 |
def _kernel_weights_from_distances(distances: list[float]) -> list[float]:
|
| 794 |
if not distances:
|
| 795 |
return []
|