
A five-gate review workflow for LeRobot demonstration data: what to check, in what order, and what to automate. Cheap metadata checks first, human review last, with runnable commands.
What you need to know
- •Order the checks by cost. The metadata pass downloads 84 KB and runs in about a second on a 50 episode dataset once that is cached; watching video costs minutes per episode. Run the cheap gate first and let it decide what a human looks at.
- •In a LeRobot v3.0 dataset, per-episode min, max, mean, std and length already live in meta/episodes/*.parquet. You do not need to touch a single MP4 to rank episodes by how unlike the rest they are.
- •Use a robust z score built on the median absolute deviation, not a standard deviation. Two bad episodes inflate the standard deviation enough to hide themselves.
- •Flags that cluster across consecutive episodes mean the environment changed. Flags that appear on isolated episodes mean the operator slipped. The two need different fixes.
- •Published curation work reports real gains from throwing data away: CUPID reached state of the art on RoboMimic with under 33 percent of the data, and DemInf reports 5 to 10 percent improvements from mutual-information filtering.
- •No automated gate tells you whether the demonstration shows the task you actually meant. That one still needs a person, on a sample.
The problem is triage, not detection
Everybody who records LeRobot datasets eventually writes a script that finds broken episodes. The script is the easy part. The hard part is that a 300 episode dataset contains roughly 300 things you could look at, each one takes about a minute to watch properly, and you have a GPU rental waiting. So the script gets written, gets run once, produces a list of 40 suspicious episodes, and then nobody reviews the list because reviewing the list is the expensive bit.
A review workflow fixes that by ordering the checks so that every stage only receives what the stage before it could not settle. Cheap and automatic first, expensive and human last. The point is not to catch every defect. The point is to spend your review minutes on the episodes most likely to be worth reviewing, and to know what you skipped.
| Gate | Question it answers | Cost on a 50 episode set | Automate it? |
|---|---|---|---|
| 0. Schema | Will the trainer load this at all | seconds, metadata only | Fully |
| 1. Statistics | Which episodes are unlike the rest | 84 KB, under 3 seconds | Fully |
| 2. Targeted replay | Is the flagged episode actually broken | minutes, flagged episodes only | Partly |
| 3. Sampled review | Does the demonstration show the task you meant | about a minute per episode | Never fully |
| 4. Edit and re-check | Did the prune leave a coherent dataset | minutes, metadata only | Fully |
The rest of this article walks each gate with the commands that run it. The worked example is a real public dataset, lerobot/svla_so101_pickplace: 50 episodes, 11,939 frames at 30 fps, two cameras, six joints on an SO-100 class follower arm. You can point every command below at your own dataset instead, or at anything on the public dataset directory.

Gate 0: does the trainer accept the format at all
This gate takes ten seconds and saves the most wasted GPU hours, because the failure it catches happens after the pod is already billing. Read meta/info.json and check four things: the codebase version, the frame rate, the feature schema, and the episode count.
python - <<'EOF'
import json
from huggingface_hub import hf_hub_download
p = hf_hub_download("lerobot/svla_so101_pickplace", "meta/info.json", repo_type="dataset")
info = json.load(open(p))
print(info["codebase_version"], info["robot_type"], info["fps"], "fps")
print(info["total_episodes"], "episodes,", info["total_frames"], "frames")
for k, v in info["features"].items():
print(" ", k, v["dtype"], v["shape"])
EOFv3.0 so100_follower 30 fps
50 episodes, 11939 frames
action float32 [6]
observation.state float32 [6]
observation.images.up video [480, 640, 3]
observation.images.side video [480, 640, 3]
timestamp float32 [1]
frame_index int64 [1]
episode_index int64 [1]
index int64 [1]
task_index int64 [1]Three checks fail here often enough to be worth naming. First, the codebase version against the policy you plan to train: GR00T N1.7 and GR00T N1.5 want LeRobot v2.0 or v2.1, and a v3.0 dataset crashes the GR00T loader, so it has to be converted down first (see dataset rejected as v3). ACT, SmolVLA and Pi0.5 take v3.0. Second, the episode count against the minimum the policy needs: 30 for SmolVLA, 50 for ACT, Pi0.5, GR00T N1.5 and GR00T N1.7. Third, the camera keys, because a policy config that names observation.images.front will not find a dataset that recorded observation.images.up.
A v3.0 to v2.1 conversion changes the file layout but not the content, so a dataset that passes gate 0 in one format still has the same defects in the other. Convert first, audit second. Auditing the v3.0 copy and then converting means your episode indices may no longer line up with what you reviewed. Both formats are covered in the dataset documentation.
Gate 1: the statistics pass that costs 84 KB
This is the gate most people skip, and it is the one with the best ratio of information to effort. A LeRobot v3.0 dataset stores per-episode summary statistics in the metadata itself, in meta/episodes/chunk-000/file-000.parquet. Reading that file gives you, for every episode and every feature, the min, max, mean, std and count, plus the episode length in frames, the task string, and the pointers into the shared shards: a chunk and file index, the row range into the parquet (dataset_from_index and dataset_to_index), and the start and end timestamp inside each MP4.
The size difference is the whole argument for running this first. For the example dataset the full repository is 86.1 MB, of which 85.7 MB is the two MP4 shards. The entire meta/ directory is 84,121 bytes, about one tenth of one percent of the repository. You can rank every episode in the dataset before downloading any video.
#!/usr/bin/env python3
"""Gate 1: rank LeRobot v3.0 episodes from metadata alone. No video is decoded."""
import json, sys
import numpy as np
import pyarrow.parquet as pq
from huggingface_hub import snapshot_download
repo = sys.argv[1] if len(sys.argv) > 1 else "lerobot/svla_so101_pickplace"
root = snapshot_download(repo, repo_type="dataset", allow_patterns=["meta/*"])
info = json.load(open(f"{root}/meta/info.json"))
t = pq.read_table(f"{root}/meta/episodes").to_pandas()
def flat(v): # image stats arrive as nested per-channel arrays
a = np.asarray(v, dtype=object).ravel()
return np.array([flat(x)[0] if isinstance(x, np.ndarray) else float(x) for x in a])
def col(name):
return np.stack([flat(v) for v in t[name]])
def rz(x): # robust z score: median absolute deviation, not std
med = np.median(x)
mad = np.median(np.abs(x - med)) or 1e-9
return (x - med) / (1.4826 * mad)
joints = info["features"]["action"]["names"]
cams = [k.split("observation.images.")[1] for k in info["features"] if ".images." in k]
length = t["length"].to_numpy().astype(float)
span = col("stats/action/max") - col("stats/action/min")
flags = {"length": np.abs(rz(length)) > 3}
for j, name in enumerate(joints):
flags["span:" + name.split(".")[0]] = np.abs(rz(span[:, j])) > 3
for cam in cams:
lum = col(f"stats/observation.images.{cam}/mean").mean(1)
flags["light:" + cam] = np.abs(rz(lum)) > 3
for i in range(len(t)):
hit = [k for k, v in flags.items() if v[i]]
if hit:
print(f"episode {i:>4} {int(length[i]):>4} frames {', '.join(hit)}")
n = sum(any(v[i] for v in flags.values()) for i in range(len(t)))
print(f"\n{n}/{len(t)} episodes flagged for review, {len(t) - n} clean")$ python audit.py lerobot/svla_so101_pickplace
episode 13 231 frames span:gripper
episode 27 203 frames light:up
episode 28 224 frames light:up, light:side
episode 30 288 frames span:gripper, light:side
episode 37 227 frames span:gripper
5/50 episodes flagged for review, 45 cleanFive episodes out of fifty go to a human. Forty-five do not. That is the entire job of gate 1, and the output is already more useful than a flat list, because of how the flags group.
- Episodes 27, 28 and 30 are flagged on brightness, and they are consecutive. Consecutive flags on an environment feature are not three bad operators, they are one lighting change: a lamp switched on, a blind moved, the sun came round. Peak to peak brightness across the whole run is 9.2 percent on the up camera and 12.3 percent on the side camera.
- Episodes 13, 30 and 37 are flagged on gripper travel, and they are scattered. Scattered flags on an action feature are operator variance: the gripper opened less far on those attempts. Median gripper span across the set is 23.22 units, the smallest is 17.47.
- Episode 30 carries two independent flags. Episodes that trip more than one rule are where a reviewer should start.
A standard deviation is computed from the same numbers you are trying to screen. Two frozen episodes with an extreme mean pull the standard deviation up far enough that their own z score drops below the threshold, and they pass. The median absolute deviation has a breakdown point of 50 percent: up to half the dataset can be garbage before the estimate moves. The 1.4826 factor rescales it so that a threshold of 3 means roughly what it means for a normal distribution.
One honest limit of this gate, visible in the metadata itself. The count field for the image statistics is 100 on every episode, while the count for observation.state equals the episode length (303 for episode 0). Image statistics are computed from a sample of 100 frames per episode. That is plenty to detect a lighting shift across a whole episode. It is useless for finding a single frozen frame or a two second camera dropout, which is a different check that does require decoding video.
The other structural limit: summary statistics describe the envelope, not the path. Two episodes with identical per-joint min and max can trace completely different trajectories, one smooth and one full of the jerk that comes from a stuttering leader-follower link. Gate 1 will not separate them. That is what gate 2 is for.
Gate 2: replay only what gate 1 flagged
Now you decode video, but only for five episodes instead of fifty. LeRobot ships a viewer that renders camera streams, joint states and actions on a shared timeline.
- 1Open the worst offender first
Episode 30 tripped two rules, so it goes first. The viewer opens a rerun.io window with every camera and every joint on one timeline. It sits behind an optional extra: install lerobot[viz] first, or the command exits telling you rerun-sdk is missing. The same extra ships the Foxglove backend used in step three.
bashlerobot-dataset-viz \ --repo-id lerobot/svla_so101_pickplace \ --episode-index 30 - 2Point it at a local recording instead
If the dataset came off your own machine rather than the Hub, add --root and switch the mode. This is the normal case right after a recording session with the desktop client.
bashlerobot-dataset-viz \ --repo-id my-user/pick-place-run3 \ --root ./my_local_data_dir \ --mode local \ --episode-index 30 - 3Scrub instead of watching, if you prefer a timeline
The Foxglove display mode starts a WebSocket server on port 8765 and serves the episode as a seekable timeline, which is faster than watching linearly when you already know roughly where the problem is.
bashlerobot-dataset-viz \ --repo-id lerobot/svla_so101_pickplace \ --episode-index 30 \ --display-mode foxglove # then connect Foxglove to ws://127.0.0.1:8765 - 4Write the verdict down as you go
Keep a plain text file with one line per reviewed episode: index, verdict, reason. You will need the index list intact for gate 4, and you will want the reasons when you decide whether to re-record.
bashcat >> review.tsv <<'EOF' 30 drop light changed mid-episode, gripper never fully opened 13 keep short grip travel but the cube was already close 27 keep brightness only, task completed cleanly EOF
One more viewer is worth knowing, and it is worth being clear that it is one tool and not two. The hosted LeRobot Dataset Visualizer Space is the deployed instance of the open source huggingface/lerobot-dataset-visualizer repo (Apache-2.0, same README, run it yourself with bun install and bun dev). Either way you get the same app: synchronized video and time series charts, and a filtering panel that flags low movement, jerky motion and outlier episode length, then exports the flagged episode ids as a ready to run LeRobot CLI command. Its heuristics overlap with gate 1, but it is built for looking at one episode closely rather than ranking fifty at once. Use it when you want to look, not when you want to rank. The upstream question of how to record demonstrations that need less of this in the first place is covered in collecting high quality VLA training data.
Gate 3: the sampled human review
Everything so far finds episodes that are statistically odd. None of it finds an episode where the arm moved smoothly, the lighting was fine, the gripper closed properly, and the operator picked up the wrong object. That failure looks completely normal in every summary statistic, and it is the one that quietly teaches your policy the wrong thing.
| What the reviewer sees | Verdict | Action | Why |
|---|---|---|---|
| Task completed, clean motion | Keep | nothing | This is the reference class |
| Task completed, recovered from a fumble | Keep | nothing | Recovery behaviour is worth learning, not a defect |
| Task completed, wrong object or wrong place | Relabel or drop | modify_tasks, or delete | The language annotation no longer matches the trajectory |
| Task not completed, operator gave up | Drop | delete_episodes | Teaches the policy to stop halfway |
| Arm moved but nothing was in frame | Drop | delete_episodes | Camera framing or object placement error |
| Whole run drifted (lighting, camera nudged) | Re-record | start a new session | Deleting the drifted half throws away most of your data |
Sample size, not full coverage. At 30 fps, the 11,939 frames of the example dataset are 6.6 minutes of footage per camera, 13 minutes for both. That is watchable. Scale to 500 episodes and it is more than an hour per camera, which nobody does twice. A workable rule: review every episode gate 1 flagged, plus a fixed random 10 percent of the unflagged ones. The random sample is not there to catch defects. It is there to measure your false negative rate, so you find out whether gate 1 is calibrated for your task before you trust it on the next dataset.
If somebody else recorded the data, this gate is also where operator variance shows up. DROID was collected by 50 people across three continents over 12 months, 76,000 trajectories in 564 scenes; the RoboTurk work collected 137.5 hours from remote workers. At that scale the reviewer is not checking whether a single episode is broken, they are checking whether one operator's habit has become a systematic bias in the data. The same applies at small scale as soon as two people take turns on the teleoperation station.

Gate 4: act on the verdict, then re-check
LeRobot has a first-party editing CLI. It handles the bookkeeping that makes manual deletion dangerous: renumbering the remaining episodes, rewriting the parquet and MP4 offsets, and pruning tasks that no episode references any more.
# delete every episode you marked "drop", in ONE call, into a NEW repo
lerobot-edit-dataset \
--repo_id my-user/pick-place-run3 \
--new_repo_id my-user/pick-place-run3-clean \
--operation.type delete_episodes \
--operation.episode_indices "[30, 41, 44]"
# fix a language annotation instead of deleting the episode
lerobot-edit-dataset \
--repo_id my-user/pick-place-run3-clean \
--operation.type modify_tasks \
--operation.episode_tasks '{"12": "Pick up the blue cube and place it in the bin"}'
# confirm what survived
lerobot-edit-dataset \
--repo_id my-user/pick-place-run3-clean \
--operation.type info \
--operation.show_features trueTwo traps here, both silent. First: omit --new_repo_id and delete_episodes modifies the original dataset in place. Second, and worse: after a delete, the remaining episodes are renumbered 0, 1, 2 and so on in their original order. Your review list of indices is now wrong. If you delete episode 13 and then run a second call to delete episode 30, you have just deleted what used to be episode 31. Collect every index first, delete them in a single call, and re-run the audit against the new repo rather than trusting the old list. Note also that modify_tasks is always in place and ignores --new_repo_id entirely.
Then run gate 1 again against the cleaned repo. This is not ceremony. Pruning changes the reference distribution, so episodes that sat just inside the threshold before may sit outside it now, and, more usefully, you find out whether you removed a defect or removed the evidence of one. If your gripper-span flags disappear entirely after deleting three episodes, the problem was three episodes. If new ones appear, the problem is your calibration and no amount of deleting will fix it. That distinction is the difference between a dataset you can train on and a recording session you need to repeat.
What the published curation work actually found
It is easy to treat pruning as damage control. The research points the other way: filtering is a lever, and a fairly strong one. Three results are worth carrying into your own workflow.
| Work | Method | Reported result |
|---|---|---|
| Belkhale, Cui and Sadigh, 2023 | Formalises data quality as action divergence and transition diversity | State diversity is not always beneficial; more varied data can make a policy worse |
| CUPID, 2025 | Influence functions ranked against evaluation rollouts | State of the art diffusion policies on RoboMimic from under 33 percent of the data |
| DemInf, 2025 | k-nearest-neighbour mutual information on VAE embeddings of states and actions | 5 to 10 percent improvement on RoboMimic, gains on real ALOHA and Franka setups |
| Northcutt et al., 2021 | Confident learning on ten standard test sets | At least 3.3 percent label errors on average, at least 6 percent of the ImageNet validation set |
The Belkhale result is the one that changes how you read your own flags. If diversity were unambiguously good, every unusual episode would be worth keeping and gate 1 would be pointing you at your best data. It is not that simple: the paper separates variation in the action you took at a given state, which hurts, from variation in how the world responds, which is just noise you have to model. An episode where the operator took a different but equally valid route to the goal is diversity worth keeping. An episode where the operator took a worse route to the same goal is action divergence, and it makes the policy hesitate at exactly that state. Both look identical in a min-max table. Only a person watching can tell them apart, which is why gate 3 does not go away.
The CUPID and DemInf numbers come with a caveat worth stating plainly, because it constrains what you can do before your first training run. CUPID ranks demonstrations by their influence on a policy's expected return, which requires evaluation rollouts, which requires a trained policy. You cannot run it on day one. DemInf is unsupervised and needs no policy, which makes it the closer analogue to what gate 1 does, just with a learned embedding instead of hand-written rules. Neither replaces looking at the video.
- Directly reported gains: under 33 percent of curated data matched state of the art in the CUPID experiments
- Shorter training runs, because fewer frames per epoch on a fixed step budget means more passes over the good data
- A smaller, cleaner set is far easier to reason about when a policy misbehaves later
- Removing systematically bad episodes removes a systematic bias, not just noise
- Below the policy minimum you cannot train at all: 30 episodes for SmolVLA, 50 for ACT, Pi0.5 and both GR00T variants
- Pruning to a narrow distribution produces a policy that only works in the exact setup you kept, a failure documented on the policy only works in one setup page
- Recovery and correction behaviour looks like a defect to every automated rule, and deleting it removes the policy's ability to recover
- Every rule you tune against one dataset is a rule you have overfitted to that dataset's lighting and task
Two ways to run this
Everything above runs on a laptop against a Hub repo or a local folder. The full loop, start to finish:
pip install "lerobot[viz]" # the viz extra is what ships lerobot-dataset-viz
# gate 0 + 1: metadata only, no video
python audit.py my-user/pick-place-run3
# gate 2: replay only the flagged indices
lerobot-dataset-viz --repo-id my-user/pick-place-run3 --episode-index 30
# optional: automated scoring across nine dimensions
git clone https://github.com/RoboticsData/score_lerobot_episodes.git
cd score_lerobot_episodes && pip install -e .
python score_dataset.py --repo_id my-user/pick-place-run3 --threshold 0.5
# gate 4: prune into a new repo
lerobot-edit-dataset \
--repo_id my-user/pick-place-run3 \
--new_repo_id my-user/pick-place-run3-clean \
--operation.type delete_episodes \
--operation.episode_indices "[30, 41, 44]"The score_lerobot_episodes toolkit (Apache-2.0) is the closest thing to an off-the-shelf gate 1 and gate 2 combined. It scores visual clarity, smoothness from the second derivative of joint angles, path efficiency, collision spikes, joint stability, gripper consistency, actuator saturation, runtime against a nominal duration, and, with --vision_type vlm_gemini, task success graded by a vision-language model. Default retention threshold is 0.5. It writes per-episode scores to a JSON file and can emit a filtered copy of the dataset. It downloads and decodes video, so it is minutes not seconds, which is exactly why it belongs after the metadata pass rather than instead of it.
Every number in this workflow (robust z of 3, a 10 percent random sample, a 0.5 score threshold) is a starting point, not a constant. Run the audit against a dataset you already trained on successfully and see which of its episodes get flagged. If your known-good set trips five rules, your thresholds are too tight for your task.
The platform removes the recording and training friction around this workflow. It does not grade your episodes for you, and it is worth being clear about which is which.
- The desktop client records LeRobot-format datasets (episodes, camera streams, joint states) straight out of a teleop session, so the metadata this workflow reads is written correctly from the start. Get it from the download page.
- Datasets can come from a Hugging Face repo id, from the public directory, or from your own machine, which means the same audit script points at all three without changes.
- Training takes model plus dataset plus hyperparameters in a form, rents a GPU on a spot market by required VRAM, and writes checkpoints to object storage. A run on the 24 GB tier (SmolVLA, ACT) costs about 1 to 3 USD; the A100 or H100 tier (GR00T N1.7, GR00T N1.5, Pi0.5) about 4 to 12 USD. See pricing.
- The CLI and the MCP server expose the same operations to a terminal and to an AI agent, which is how you get the audit into a pre-training hook rather than a thing you remember to do.
- The failure mode index maps symptoms back to causes, including several that originate in the dataset rather than the policy.
What it does not do: there is no button that scores your episodes and deletes the bad ones. The audit script above is still yours to run. What the platform changes is the cost of the loop around it, because re-recording ten replacement episodes after a review is a teleop session rather than a hardware expedition, and a second training run to confirm the prune helped is single-digit dollars rather than a decision.
Inference has to sit next to the servos for fast tasks. The control loop is 20 ms per action step for ACT and up to 485 ms for Pi0.5, and adding public-internet round trips to that turns a working policy into a hesitant one. Cloud inference is fine for slow pick and place, not for fast reactive motion. No amount of dataset quality control changes that, and a dataset audit is not the fix for a policy that hesitates over the network.
What to automate, and what never to automate
The split is not about difficulty. It is about whether the check has a ground truth that exists independently of your intent.
| Check | Ground truth | Automate |
|---|---|---|
| Schema, version, fps, feature names | Defined by the format | Always |
| Episode length outliers | The rest of the dataset | Always |
| Per-joint travel outliers | The rest of the dataset | Always |
| Brightness and contrast drift | The rest of the dataset | Always |
| Frozen frames, camera dropouts | Frame-to-frame difference | Always, but it costs video decode |
| Motion smoothness, jerk | Second derivative of joint angles | Always, with a task-tuned threshold |
| Did the gripper actually grasp | Contact, which you did not record | Proxy only, verify by eye |
| Did the episode complete the task | Your definition of the task | Proxy only, or a VLM grader you also have to check |
| Is this the task you meant to teach | Only in your head | Never |
Every gate here is downstream repair. The checks that pay best are the ones you run before the session: fixed camera mounts that cannot be nudged, a lighting setup that does not depend on the time of day, a calibration pass at the start of every session, and a written task description you read out loud before recording so the language annotation and the trajectory cannot drift apart. The recording walkthrough and the SO-100 data collection guide cover the setup side.
What this workflow cannot tell you
Being honest about the boundary matters more than the checks themselves, because a green audit report creates confidence that the audit did not earn.
- It cannot tell you the task is learnable. A dataset can pass every gate and still be impossible to learn from, because the information the policy needs (an occluded object, a force, a state the cameras never see) was never recorded. Nothing in the metadata reveals a missing observation.
- It cannot predict closed-loop success. Every metric here is computed on the demonstration in isolation. The published methods that do relate data to closed-loop return, such as CUPID, need evaluation rollouts from a trained policy first.
- It cannot separate a hard episode from a bad one. An episode that took 40 percent longer because the object started in an awkward pose is exactly the data you want; the same statistics describe an episode where the operator was distracted.
- It cannot fix a policy that fails at inference but trained fine. That is usually an environment gap or a latency problem rather than a data defect, and it lives on the loss falls but the policy does nothing and policy freezes mid-motion pages.
- It does not document the dataset. The Datasheets for Datasets proposal is worth following here: record who collected it, on what hardware, under what lighting, with what task wording, and what you pruned and why. Six months later that file is worth more than the audit output.

One last framing. The reason to run this before renting a GPU is not that the GPU is expensive. On the 24 GB tier a SmolVLA run is a couple of dollars, and even a GR00T N1.7 run on an A100 lands between 4 and 12 USD. The reason is attribution. If you train on unaudited data and the policy is bad, you cannot tell whether the data was bad, the hyperparameters were wrong, or the task is hard, so the next thing you do is guess. An audited dataset does not guarantee a good policy. It guarantees that when the policy is bad, you are debugging something else. That is worth three seconds and 84 KB.
The policy is bad and you do not know why
The failure mode index maps what you are seeing on the arm back to the cause, from datasets rejected as v3 to a gripper that never closes to a policy that only works in one setup.
Open the failure modesHow many episodes should I review by hand?▾
Review every episode the automated pass flagged, plus a fixed random 10 percent of the ones it did not. The flagged set is the work; the random sample exists to measure your false negative rate. If the random sample keeps turning up defects the script missed, your thresholds are too loose and you fix the script rather than reviewing more.
Should I delete a flagged episode or keep it?▾
Delete only if a person watched it and the task was not completed, or was completed on the wrong object. Statistical oddness on its own is not a reason. An episode where the operator fumbled and recovered is valuable, because recovery behaviour is exactly what a policy needs and cannot invent. Deleting it makes the policy brittle rather than clean.
How few episodes can I get away with after pruning?▾
The trainable policies have hard minimums: 30 episodes for SmolVLA, 50 for ACT, Pi0.5, GR00T N1.5 and GR00T N1.7. Those are floors for the run to be worth starting, not targets. If pruning takes you near the floor, re-record rather than train on the remainder, because a set that is both small and narrow produces a policy that only works in the one setup you kept.
Can I run the metadata audit on a v2.1 dataset?▾
The layout differs. LeRobot v3.0 keeps per-episode statistics in chunked parquet under meta/episodes/, while v2.1 keeps one file per episode and stores episode statistics separately. The rules are identical, only the reader changes. If you are training GR00T you will be on v2.1 anyway, since a v3.0 dataset crashes the GR00T loader and has to be converted down first.
Is an automated quality score enough on its own?▾
No, and the tools that offer one do not claim it is. score_lerobot_episodes gives a 0 to 1 aggregate across nine dimensions with a default cut at 0.5, which is a good ranking and a bad verdict. Nothing in it knows what task you intended, and an episode that scores well while demonstrating the wrong behaviour is the single most damaging thing in a training set.
Does the audit have to run before every training run?▾
Before the first run on a dataset, yes. After that, run it again whenever the dataset changes: after a prune, after merging in a second recording session, after a format conversion. Merges are the common trap, because two individually clean sessions recorded weeks apart can have completely different lighting distributions and the merged set is bimodal in a way neither half was.
Sources
- LeRobotDataset v3.0 format documentation
- LeRobot: Using Dataset Tools (lerobot-edit-dataset, lerobot-dataset-viz)
- huggingface/lerobot repository
- score_lerobot_episodes: quantitative scoring toolkit for LeRobot episodes
- One-click Robot Data Curation for Higher Quality Datasets
- huggingface/lerobot-dataset-visualizer
- LeRobot Dataset Visualizer (hosted Space)
- Data Quality in Imitation Learning (Belkhale, Cui, Sadigh, NeurIPS 2023)
- CUPID: Curating Data your Robot Loves with Influence Functions
- Robot Data Curation with Mutual Information Estimators (DemInf)
- What Matters in Learning from Offline Human Demonstrations for Robot Manipulation
- Pervasive Label Errors in Test Sets Destabilize Machine Learning Benchmarks
- DROID: A Large-Scale In-The-Wild Robot Manipulation Dataset
- RoboTurk: A Crowdsourcing Platform for Robotic Skill Learning through Imitation
- Datasheets for Datasets (Gebru et al.)
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started