Skip to content

fix(export): extract rsl_rl actor from alg directly so export works again - #157

Merged
KraHsu merged 1 commit into
devfrom
fix/export-rsl-rl-actor
May 24, 2026
Merged

KraHsu merged 1 commit into
devfrom
fix/export-rsl-rl-actor

Conversation

@KraHsu

@KraHsu KraHsu commented May 24, 2026

Copy link
Copy Markdown
Owner

Problem

genelab export <rsl_rl-task> <ckpt> -f torchscript|onnx failed for every
rsl_rl checkpoint (e.g. GeneLab-Inverted-Pendulum-v0) with:

backend 'rsl_rl' did not expose an actor module; export is not supported for this checkpoint type

(This is the next failure after PR #156 fixed the separate "No display detected" crash.)

Root cause

_extract_rsl_rl_actor (src/genelab/rl/backends/rsl_rl.py) read
runner.alg.actor_critic and returned (None, num_obs) when missing. The
installed rsl_rl's PPO.__init__ no longer has actor_critic — it sets
self.actor, self._raw_actor, self.critic, self._raw_critic directly
(verified via inspect.getsource). So the attribute path was stale,
actor_module ended up None, and _export.py raised.

Fix

Source the actor from alg._raw_actor (the uncompiled module; falls back to
alg.actor, which may be a torch.compile wrapper). The actor is an
MLPModel whose own forward takes a TensorDict and self-normalizes, so we
use its as_jit() wrapper — _TorchMLPModel, which exposes the flat
forward(obs) -> deterministic action contract (learned obs normalizer baked
in) that GeneLab's exporter wraps with env-side scale/clip. Input dim comes
from actor.obs_dim. The legacy alg.actor_critic.actor / act_inference
path is kept as a fallback for older rsl_rl.

Verification (headless, real checkpoint)

QD_GRAPH=0 QT_QPA_PLATFORM=offscreen genelab export GeneLab-Inverted-Pendulum-v0 <ckpt> -f {torchscript,onnx} --out ...

  • both exit 0 and write .ts / .onnx + .metadata.json
  • TorchScript loads and runs: obs_dim 8 -> action_dim 1
  • ONNX passes onnx.checker; IO obs:[batch,8] -> actions:[batch,1] (dynamic batch)

Tests

tests/test_export.py:

  • test_extract_rsl_rl_actor_new_layout — builds a real rsl_rl MLPModel,
    asserts extraction returns a flat-input deterministic module with the right
    dim, and runs it through the full export_policy -> loadable .ts.
  • test_extract_rsl_rl_actor_legacy_layout — alg.actor_critic.actor path.
  • test_extract_rsl_rl_actor_missing_returns_none — (None, num_obs) so the
    caller raises a clear error.

tests/test_export.py — 10 passed; export+rsl suite 17 passed; ruff clean.

Docs

docs/concepts/eval-and-export.{en,zh}.md updated to describe the new
extraction path (convention: docs update for rl/backends/ changes).

Reference audit

find_referencing_symbols(_extract_rsl_rl_actor) → only caller is
RslRlBackend.make_inference_setup (rsl_rl.py:120); signature unchanged, no
other caller affected.

🤖 Generated with Claude Code

…gain

`genelab export <rsl_rl-task>` failed for every rsl_rl checkpoint with
"backend 'rsl_rl' did not expose an actor module": `_extract_rsl_rl_actor`
read `runner.alg.actor_critic`, but current rsl_rl's `PPO.__init__` stores
`self.actor` / `self._raw_actor` directly and no longer has `actor_critic`,
so extraction returned `(None, num_obs)` and `_export.py` raised.

Source the actor from `alg._raw_actor` (falling back to `alg.actor`) and use
the actor's `as_jit()` export wrapper, which exposes the flat
`forward(obs) -> deterministic action` contract (learned obs normalizer baked
in) that the exporter wraps with env-side scale/clip. The legacy
`alg.actor_critic.actor` / `act_inference` path is kept as a fallback for
older rsl_rl.

Verified headless (QD_GRAPH=0 QT_QPA_PLATFORM=offscreen) against a real
inverted-pendulum checkpoint: `-f torchscript` and `-f onnx` both exit 0 and
write loadable .ts / .onnx + .metadata.json (obs_dim 8 -> action_dim 1,
dynamic batch).

Tests build a real rsl_rl MLPModel and assert extraction + full export, plus
the legacy and missing-actor paths. Docs (eval-and-export EN/ZH) updated to
describe the new extraction path.
@KraHsu
KraHsu merged commit ce98f4b into dev May 24, 2026
3 checks passed
@KraHsu
KraHsu deleted the fix/export-rsl-rl-actor branch May 24, 2026 07:35
KraHsu added a commit that referenced this pull request Jun 5, 2026
fix(export): extract rsl_rl actor from alg directly so export works again
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant