mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-15 14:02:14 +00:00
refactor(runtime): remove gibberish filtering
This commit is contained in:
@@ -1751,8 +1751,6 @@ class PI052Policy(PreTrainedPolicy):
|
||||
return out
|
||||
|
||||
def _generate_low_level_subtask(self, obs_i: dict[str, Tensor], task: str, i: int) -> str:
|
||||
from lerobot.runtime.adapter import looks_like_gibberish as _looks_like_gibberish # noqa: PLC0415
|
||||
|
||||
from .inference.pi052_adapter import _generate_with_policy # noqa: PLC0415
|
||||
|
||||
msg = ""
|
||||
@@ -1767,7 +1765,7 @@ class PI052Policy(PreTrainedPolicy):
|
||||
self.last_subtasks_raw[i] = msg or ""
|
||||
|
||||
# Feed the generated subtask verbatim, matching low-level training.
|
||||
if msg and not _looks_like_gibberish(msg):
|
||||
if msg:
|
||||
subtask = " ".join(msg.strip().split())
|
||||
self._last_good_subtasks[i] = subtask
|
||||
self.last_subtasks[i] = subtask
|
||||
@@ -1779,8 +1777,6 @@ class PI052Policy(PreTrainedPolicy):
|
||||
debug = getattr(self, "_last_select_message_debug", "") or ""
|
||||
if not task:
|
||||
reason = "No task string was available in the batch."
|
||||
elif msg:
|
||||
reason = f"Rejected generated subtask: {msg!r}"
|
||||
else:
|
||||
reason = f"Empty generated subtask. {debug}".strip()
|
||||
if self._last_good_subtasks[i]:
|
||||
|
||||
@@ -43,11 +43,10 @@ class GenerationConfig:
|
||||
|
||||
@dataclass
|
||||
class LanguageDiagnostics:
|
||||
"""Runtime-panel rejection and repeat counters keyed by text kind."""
|
||||
"""Runtime-panel generation counters keyed by text kind."""
|
||||
|
||||
last_raw: dict[str, str] = field(default_factory=dict)
|
||||
empty: dict[str, int] = field(default_factory=dict)
|
||||
gibberish: dict[str, int] = field(default_factory=dict)
|
||||
repeat: int = 0
|
||||
|
||||
def _bump(self, table: dict[str, int], kind: str) -> int:
|
||||
@@ -96,9 +95,9 @@ class BaseLanguageAdapter(ABC):
|
||||
state.set_context("plan", plan, label="plan")
|
||||
|
||||
def plan_from_text(self, text: str) -> str:
|
||||
"""Strip ``<say>`` speech markers and reject gibberish plans."""
|
||||
"""Strip ``<say>`` speech markers from a generated plan."""
|
||||
plan, _speech = split_plan_and_say(text)
|
||||
return "" if looks_like_gibberish(plan) else plan
|
||||
return plan
|
||||
|
||||
def _regenerate_context(self, observation: dict[str, Any] | None, state: RuntimeState) -> None:
|
||||
"""Default hierarchy: regenerate the subtask, then memory when it changes.
|
||||
@@ -127,7 +126,7 @@ class BaseLanguageAdapter(ABC):
|
||||
def _generate_filtered(
|
||||
self, kind: str, observation: dict[str, Any] | None, state: RuntimeState
|
||||
) -> str | None:
|
||||
"""Generate one ``kind``, record diagnostics, drop empty / gibberish output."""
|
||||
"""Generate one ``kind``, record diagnostics, and drop empty output."""
|
||||
text = self.generate_text(kind, observation, state)
|
||||
self.diag.last_raw[kind] = text or ""
|
||||
if not text:
|
||||
@@ -135,11 +134,6 @@ class BaseLanguageAdapter(ABC):
|
||||
if count == 1 or count % 5 == 0:
|
||||
state.log(f" [info] {kind} gen returned empty (x{count})")
|
||||
return None
|
||||
if looks_like_gibberish(text):
|
||||
count = self.diag._bump(self.diag.gibberish, kind)
|
||||
if count == 1 or count % 30 == 0:
|
||||
state.log(f" [info] {kind} gen rejected (gibberish x{count}): {text[:60]!r}")
|
||||
return None
|
||||
return text
|
||||
|
||||
|
||||
@@ -159,29 +153,6 @@ class DirectTaskPolicyAdapter(BaseLanguageAdapter):
|
||||
return ""
|
||||
|
||||
|
||||
def looks_like_gibberish(text: str) -> bool:
|
||||
"""Heuristic filter for malformed / collapsed LM-head output."""
|
||||
if not text or not text.strip():
|
||||
return True
|
||||
stripped = text.strip()
|
||||
alpha = sum(1 for c in stripped if c.isalpha())
|
||||
if alpha < max(3, len(stripped) // 8):
|
||||
return True
|
||||
if stripped.startswith('":') and stripped.count('"') > stripped.count(" "):
|
||||
return True
|
||||
if len(set(stripped)) <= 2 and len(stripped) > 4:
|
||||
return True
|
||||
cleaned = stripped.replace("\n", " ").replace(":", " ")
|
||||
for marker in ("Assistant", "User", "Ass "):
|
||||
if marker in cleaned and len(cleaned.split()) < 4:
|
||||
return True
|
||||
tokens = [t for t in cleaned.split() if any(c.isalpha() for c in t)]
|
||||
unique_alpha = {t.lower() for t in tokens}
|
||||
if len(unique_alpha) < 3 and len(stripped) < 80:
|
||||
return True
|
||||
return len(tokens) >= 8 and len(unique_alpha) <= max(3, len(tokens) // 10)
|
||||
|
||||
|
||||
def split_plan_and_say(text: str) -> tuple[str, str]:
|
||||
"""Split ``plan <say>speech</say>`` into ``(plan, speech)``."""
|
||||
if not text:
|
||||
|
||||
@@ -660,24 +660,19 @@ def _make_state_panel_renderer(
|
||||
dispatched = int(st.get("actions_dispatched") or 0)
|
||||
console.print(f" [dim]queued actions: {queue_len} dispatched: {dispatched}[/]")
|
||||
|
||||
# Surface repeated or rejected generations as overfitting diagnostics.
|
||||
# Surface repeated or empty generations as overfitting diagnostics.
|
||||
diag = getattr(runtime.policy_adapter, "diag", None)
|
||||
if diag is not None:
|
||||
raw_subtask = diag.last_raw.get("subtask")
|
||||
sub_rep = int(diag.repeat)
|
||||
sub_gib = int(diag.gibberish.get("subtask", 0))
|
||||
sub_empty = int(diag.empty.get("subtask", 0))
|
||||
if raw_subtask is not None or sub_rep or sub_gib or sub_empty:
|
||||
if raw_subtask is not None or sub_rep or sub_empty:
|
||||
raw_display = (raw_subtask or "(empty)")[:80]
|
||||
color = "yellow" if (sub_rep >= 3 or sub_gib >= 3 or sub_empty >= 3) else "dim"
|
||||
color = "yellow" if (sub_rep >= 3 or sub_empty >= 3) else "dim"
|
||||
console.print(
|
||||
f" [{color}]subtask diag repeat:{sub_rep} "
|
||||
f"gibberish:{sub_gib} empty:{sub_empty} "
|
||||
f" [{color}]subtask diag repeat:{sub_rep} empty:{sub_empty} "
|
||||
f"last_raw: {raw_display!r}[/]"
|
||||
)
|
||||
mem_gib = int(diag.gibberish.get("memory", 0))
|
||||
if mem_gib:
|
||||
console.print(f" [dim]gen rejects memory:{mem_gib}[/]")
|
||||
console.rule(style="cyan")
|
||||
# Show recent generation warnings and speech oldest-first.
|
||||
if scrollback:
|
||||
|
||||
@@ -17,7 +17,6 @@ from lerobot.runtime.adapter import (
|
||||
BaseLanguageAdapter,
|
||||
DirectTaskPolicyAdapter,
|
||||
GenerationConfig,
|
||||
looks_like_gibberish,
|
||||
)
|
||||
|
||||
|
||||
@@ -49,15 +48,15 @@ def test_cascade_sets_subtask_then_memory():
|
||||
assert adapter.calls == ["subtask", "memory"]
|
||||
|
||||
|
||||
def test_gibberish_subtask_is_rejected_and_counted():
|
||||
adapter = ScriptedAdapter({"subtask": [":::: ::"], "memory": ["should not run"]})
|
||||
def test_nonempty_generation_is_used_verbatim():
|
||||
adapter = ScriptedAdapter({"subtask": [":::: ::"], "memory": ["memory"]})
|
||||
state = RuntimeState(task="clean")
|
||||
|
||||
adapter.update_language_state(None, state)
|
||||
|
||||
assert "subtask" not in state.language_context
|
||||
assert adapter.diag.gibberish.get("subtask") == 1
|
||||
assert adapter.calls == ["subtask"] # memory never generated when subtask is rejected
|
||||
assert state.language_context["subtask"] == ":::: ::"
|
||||
assert state.language_context["memory"] == "memory"
|
||||
assert adapter.calls == ["subtask", "memory"]
|
||||
|
||||
|
||||
def test_throttle_regenerates_every_n_chunks():
|
||||
@@ -87,12 +86,6 @@ def test_handle_interjection_sets_plan_and_strips_say():
|
||||
assert state.language_context["plan"] == "turn to the left now"
|
||||
|
||||
|
||||
def test_looks_like_gibberish_basic():
|
||||
assert looks_like_gibberish("")
|
||||
assert looks_like_gibberish(":::: ::")
|
||||
assert not looks_like_gibberish("pick up the red cube")
|
||||
|
||||
|
||||
def test_direct_task_adapter_delegates_action_chunk():
|
||||
class Policy:
|
||||
def predict_action_chunk(self, observation):
|
||||
|
||||
Reference in New Issue
Block a user