Skip to content

Add rd inspect data-quality checks + per-frame provenance tags in convert/shardify - #6

Open
geyan21 wants to merge 3 commits into
TRI-ML:mainfrom
geyan21:feat/data-quality-pipeline
Open

geyan21 wants to merge 3 commits into
TRI-ML:mainfrom
geyan21:feat/data-quality-pipeline

Conversation

@geyan21

@geyan21 geyan21 commented Jul 20, 2026 •

Copy link
Copy Markdown

Summary

Two additions to the offline data pipeline, both born from silent data-quality failures we hit while collecting large teleop + HG-DAgger datasets on YAM rigs:

  1. rd inspect — a pre-flight quality checker for raw recordings. It scans every episode's SVO2 streams in parallel and flags frame-stutter, dropped frames, and black/failed camera streams before hours are spent converting (or worse, training) on bad data. Episodes get a verdict (clean / borderline / bad / broken / black / missing) with per-camera reasons and a summary table; results are cached in the episode dir (--force re-scans).
  2. Per-frame provenance tags in convert + shardify — rd convert now writes control_source (0 = policy, 1 = human), is_intervention, and sample_weight into every lowdim frame, and rd shardify can subset on them (--keep interventions | interventions_context | all). This is the data-side groundwork for HG-DAgger interactive-correction collection (the collection loop itself is a follow-up PR): record whole rollouts, tag who was in control, and let the trainer mix/upweight (ABC-style 80:10:10) without re-touching raw data.

Details

  • raiden/inspector.py (new): SVO2 scan comparing frame count against wall-clock span (shrink factor), max inter-frame dt, and black-frame detection; camera-name agnostic; thresholds documented at the top of the module.
  • raiden/converter.py:
    • resamples the discrete control_source flag onto the camera-frame grid by nearest timestamp (never interpolated — it's a 0/1 flag), and only on the ns-timestamp alignment path;
    • --w-intervention N optionally upweights human-takeover frames (default 1.0 — ABC uses dataset mixing, not per-frame weights, so tags alone suffice);
    • --append converts new raw episodes into an existing converted dataset (numbering continues from the max existing episode, split metadata rebuilt over all episodes).
  • raiden/shardify.py: --keep subsetting with --keep-context N frames of lead-in before each intervention; filtered counts surfaced in processing_metadata.json; per-sample metadata carries sample_weight / is_intervention / episode_kind so merged shard sets stay splittable.
  • CLI wiring for rd inspect + the new convert/shardify flags; docs: user-guide page (docs/guide/inspection.md) and API reference, wired into the nav.

Backward compatibility

Ordinary teleop recordings convert exactly as before, except each lowdim frame gains the three new keys with neutral defaults (control_source=0, is_intervention=False, sample_weight=1.0) and episode metadata gains episode_kind="teleop". No existing key changes. --keep all is the shardify default, so existing pipelines are unaffected.

Testing

47 hardware-free unit tests in a new tests/ directory — no ZED SDK, SVO2 files, or robot required (pyzed is imported lazily inside _scan_svo2, so the modules import on a bare machine):

File Covers
tests/test_inspector.py _classify / _result_from_scan — the verdict ladder, exclusive threshold boundaries, worst-axis-wins, degenerate scans, the black-left-eye override; _is_episode_dir on a temp tree
tests/test_converter.py _resample_flag_nearest — nearest-not-interpolated, ties, out-of-range clamping, 100 Hz → 30 Hz downsampling, dtype/shape
tests/test_shardify.py _keep_anchor — all / interventions / interventions_context, look-ahead window, keep_context=0, end clamping, the empty-dataset footgun

Two tests are worth calling out as executable documentation of why the code is written the way it is: one asserts np.interp on the same inputs smears control_source into fractions while _resample_flag_nearest does not, and one pins that --keep interventions yields zero samples on a pure-teleop dataset.

This is the repo's first tests/ directory and there's no test job in CI, so they run locally with uv run pytest tests/ -v. Happy to add a CI job and a pytest dev-dependency if you want them gating.

ruff format --check + ruff check clean; mkdocs build --strict clean with the docs-CI dependency set.

🤖 Generated with Claude Code

geyan21 and others added 2 commits July 20, 2026 13:42
Data-quality inspection and the data-pipeline half of the HG-DAgger
recovery-data feature (the interactive `rd infer --intervene` collection
loop follows in a later commit).

- rd inspect: pre-flight quality check over raw SVO2 recordings — flags
  frame-stutter (clean/borderline/bad/broken via a shrink + max-gap
  classifier) and black left-eye frames, with a per-episode inspect.json
  cache (mtime-keyed) and a task-level inspect_report.json aggregate.
  Camera-name agnostic (any cameras/*.svo2 view is scanned).
- convert: per-frame control_source / is_intervention / sample_weight tags
  written into each lowdim pickle, resampled onto the camera grid by NEAREST
  timestamp (never np.interp, which would smear the 0/1 takeover flag);
  --w-intervention upweights human-takeover frames; episode_kind and
  intervention_ratio carried raw -> converted; --append numbers new episodes
  after the highest existing one and rebuilds split_all.json.
- shardify: --keep {all,interventions,interventions_context} + --keep-context
  subsetting on control_source, and episode_kind / is_intervention /
  sample_weight propagated into every sample's metadata so a merged shard set
  stays splittable for the ABC 80:10:10 base/intervention mix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the data-quality inspection user-guide page and inspector API
reference, and wire rd inspect into the README / guide index / nav.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new offline data-quality inspection step for raw recordings (rd inspect) and extends the convert→shardify pipeline to carry and optionally subset HG-DAgger-style per-frame provenance tags (control source / intervention / sample weights), with accompanying CLI and docs updates.

Changes:

  • Added rd inspect to scan raw cameras/*.svo2 for timing stutter, dropped/missing views, and left-eye black frames with per-episode caching and a task-level report.
  • Updated rd convert to write per-frame provenance tags (control_source, is_intervention, sample_weight) and added --append and --w-intervention.
  • Updated rd shardify to optionally subset anchors based on provenance tags (--keep ... / --keep-context) and to carry provenance into per-sample metadata; docs/navigation updated accordingly.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
README.md Adds rd inspect to the command list.
raiden/shardify.py Implements keep/subsetting logic and propagates provenance into shard sample metadata and processing stats.
raiden/inspector.py New raw SVO2 inspection implementation with caching, concurrency, and report generation.
raiden/converter.py Writes per-frame provenance tags into lowdim frames; adds --append and optional intervention upweighting.
raiden/cli.py Wires rd inspect, new convert flags, and shardify keep flags into the CLI.
mkdocs.yml Adds inspector docs to the nav and API reference.
docs/guide/inspection.md New user-guide page for rd inspect.
docs/guide/index.md Adds inspection into the guide flow and command table.
docs/api/inspector.md Adds mkdocstrings API stub for raiden.inspector.
docs/api/cli.md Adds InspectCommand to CLI API docs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread raiden/inspector.py
Comment on lines +431 to +433
raw = {ep_id: views for ep_id, views in raw.items() if views}
expected_cams = sorted({cam for views in raw.values() for cam in views})

Covers the three pure functions this PR introduces, with no ZED SDK, SVO2
files, or robot attached:

  - inspector._classify / _result_from_scan: the shrink / max-dt /
    frame-count verdict ladder, exclusive threshold boundaries, worst-axis
    -wins, degenerate scans, and the black-left-eye override (which beats
    an otherwise-clean timing verdict while preserving timing metrics for
    diagnosis); _is_episode_dir on a temp tree.
  - converter._resample_flag_nearest: control_source must be resampled by
    nearest timestamp and never interpolated — one test asserts np.interp
    on the same inputs smears the flag into fractions while this does not.
    Also ties, out-of-range clamping, 100 Hz -> 30 Hz downsampling, dtype.
  - shardify._keep_anchor: all / interventions / interventions_context,
    the forward look-ahead window, keep_context=0 still keeping one
    lead-in frame, end-of-episode clamping, and the documented footgun
    that --keep interventions empties a pure-teleop dataset.

This is the repo's first tests/ directory; there is no test job in CI, so
they run locally with `uv run pytest tests/ -v`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

2 participants