LeRobot documentation
LeRobotDataset v3.0
LeRobotDataset v3.0
LeRobotDataset v3.0は、ロボット学習データのための標準化されたフォーマットです。マルチモーダルな時系列データ、感覚運動シグナル、マルチカメラ動画への統合アクセスを提供するとともに、Hugging Face Hub上でのインデックス作成、検索、可視化のための豊富なメタデータも備えています。
この形式のデータセットのレイアウトについて読む前に、その中身を確認するには、LeRobot Dataset Visualizerで1つを開いてください。ブラウザ上で、カメラの録画、同期された状態とアクションのチャート、すべてのエピソードを表示できます。ブラウザでデータセットを探索するを参照してください。
このドキュメントでは、以下の内容についてご案内します:
- v3.0の設計とディレクトリ構成を理解する
- データセットを記録して Hub にプッシュする
LeRobotDatasetで学習用のデータセットを読み込むStreamingLeRobotDatasetを使用して、ダウンロードせずにデータセットをストリーミングします- トレーニング中のデータ拡張のために画像変換を適用する
- 既存の
v2.1データセットをv3.0にマイグレーションする - Lance などの他の
LeRobotDataset形式や実装を試してください
v3の新機能
- ファイルベースのストレージ: 1つのParquet/MP4ファイルに複数のエピソードを格納(v2では1エピソードにつき1ファイルでした)。
- 関係メタデータ: エピソードの境界と参照は、ファイル名ではなくメタデータによって解決されます。
- Hubネイティブストリーミング:
StreamingLeRobotDatasetを使用して、Hubからデータセットを直接消費します。 - ファイルシステムへの負荷を軽減: ファイル数は少なく、サイズは大きくする ⇒ 初期化が高速化し、大規模環境での問題が減少します。
- 統合された組織:データと動画の両方で一貫したパステンプレートを持つ、整理されたディレクトリ構成。
- データセットのストレージバックエンド: データセットは、オブジェクトストレージから直接ランダムアクセスやインデックス作成を行うために、LanceDBテーブルとして保存することもできます(LanceDB データセットバックエンドを参照)。
インストール
LeRobotDataset v3.0 は lerobot >= 0.4.0 に含まれています。インストールガイドに従ってインストールしてください。
データセットを記録する
以下のコマンドを実行して、SO-101でデータセットを記録し、Hubにプッシュします:
lerobot-record \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem585A0076841 \
--robot.id=my_awesome_follower_arm \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 1920, height: 1080, fps: 30}}" \
--teleop.type=so101_leader \
--teleop.port=/dev/tty.usbmodem58760431551 \
--teleop.id=my_awesome_leader_arm \
--display_data=true \
--dataset.repo_id=${HF_USER}/record-test \
--dataset.num_episodes=5 \
--dataset.single_task="Grab the black cube" \
--dataset.streaming_encoding=true \
--dataset.encoder_threads=2
# Optional: --dataset.rgb_encoder.vcodec=auto詳細については、録画ガイドを参照してください。
フォーマット設計
v3のコア原則の一つは、ストレージとユーザーAPIの分離です。データは効率的に保存され(少数の大きなファイル)、一方、パブリックAPIは直感的なエピソードレベルのアクセスを提供します。
v3には3つの柱があります:
- 構造化データ: 低次元で高頻度のシグナル(状態、アクション、タイムスタンプ)を Apache Parquet に保存します。アクセスは、
datasetsスタックを介してメモリマップまたはストリーミングで行われます。 - 視覚データ: カメラフレームを連結してMP4にエンコードします。同じエピソードのフレームはグループ化され、実用的なサイズにするため、カメラごとにシャード化されます。
- メタデータ: スキーマ(特徴量名、dtype、形状)、フレームレート、正規化統計、およびエピソード分割(共有 Parquet/MP4 ファイル内の開始/終了オフセット)を記述する JSON/Parquet レコード。
数百万のエピソードにスケールさせるために、複数のエピソードからの表形式の行や動画フレームは、より大きなファイルに連結されます。エピソード固有のビューは、ファイルの境界ではなく、メタデータ経由で再構築されます。
ディレクトリ構成(簡略化)
meta/info.json: 正規化されたスキーマ(features、shapes/dtypes)、FPS、コードベースのバージョン、およびデータ/動画シャードの位置を特定するためのパステンプレート。meta/stats.json: 正規化に使用されるグローバルな特徴量統計(平均/標準偏差/最小値/最大値)。dataset.meta.statsとして公開されています。meta/tasks.parquet: タスク条件付きポリシー用の、自然言語のタスク説明を整数IDにマッピングしたデータ。meta/episodes/: 各エピソードの記録(長さ、タスク、オフセット)をスケーラビリティのためにチャンク化された Parquetとして保存します。data/: フレームごとのParquetシャード。各ファイルには通常、多数のエピソードが含まれます。videos/: カメラごとのMP4シャード。各ファイルには通常、多数のエピソードが含まれます。
学習用のデータセットを読み込む
LeRobotDataset は PyTorch テンソルの Python ディクショナリを返し、torch.utils.data.DataLoader と統合されています。以下はその使用例を示すコードです:
import torch
from lerobot.datasets import LeRobotDataset
repo_id = "yaak-ai/L2D-v3"
# 1) Load from the Hub (cached locally)
dataset = LeRobotDataset(repo_id)
# 2) Random access by index
sample = dataset[100]
print(sample)
# {
# 'observation.state': tensor([...]),
# 'action': tensor([...]),
# 'observation.images.front_left': tensor([C, H, W]),
# 'timestamp': tensor(1.234),
# ...
# }
# 3) Temporal windows via delta_timestamps (seconds relative to t)
delta_timestamps = {
"observation.images.front_left": [-0.2, -0.1, 0.0] # 0.2s and 0.1s before current frame
}
dataset = LeRobotDataset(repo_id, delta_timestamps=delta_timestamps)
# Accessing an index now returns a stack for the specified key(s)
sample = dataset[100]
print(sample["observation.images.front_left"].shape) # [T, C, H, W], where T=3
# 4) Wrap with a DataLoader for training
batch_size = 16
data_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size)
device = "cuda" if torch.cuda.is_available() else "cpu"
for batch in data_loader:
observations = batch["observation.state"].to(device)
actions = batch["action"].to(device)
images = batch["observation.images.front_left"].to(device)
# model.forward(batch)トレーニング中にデータセットをストリーミングする
--dataset.streaming=true を設定すると、データセットの Parquet ファイルと動画ファイルを事前にダウンロードせずに学習できます。エピソードはオンザフライで有界なインメモリプールに取得され、選択された各フレームはエポックごとに正確に1回だけ訪問されます。
lerobot-train \
--dataset.repo_id=yaak-ai/L2D-v3 \
--dataset.streaming=true \
--policy.type=act \
--output_dir=outputs/train/act_streaming同じリーダーはPythonでも利用可能であり、Hub上のデータセットやHF Storage Bucketに保存されたデータセットにも対応しています:
from lerobot.datasets import StreamingLeRobotDataset
dataset = StreamingLeRobotDataset("yaak-ai/L2D-v3")
bucket_dataset = StreamingLeRobotDataset("my-org/my-bucket", repo_type="bucket")CLI から、--dataset.repo_type=bucket または --dataset.root=hf://buckets/my-org/my-bucket を使用してバケットを選択します。
Training Dataset Streamingを参照してください。ワーカーとシャードがストリーミング並行性、MP4サイドカーインデックス、メモリ設定、レジューム、トラブルシューティングにどのように対応するかを説明しています。
画像変換
Image transformsは、モデルの頑健性と汎化性能を向上させるために、学習中にカメラフレームに適用されるデータ拡張です。LeRobotは、明るさ、コントラスト、彩度、色相、シャープネスの調整など、さまざまなtransformをサポートしています。
データセットの作成/記録時にトランスフォームを使用する
現在、transformはトレーニング時のみ適用され、レコーディング時には適用されません。データセットを作成またはレコーディングする際、transformなしの生画像が保存されます。これにより、後で異なる拡張を試しながら、データを再レコーディングせずに実験を行うことができます。
既存のデータセットにトランスフォームを追加する(API)
学習用のデータセットを読み込む際に、image_transforms パラメータを使用します:
from lerobot.datasets import LeRobotDataset
from lerobot.transforms import ImageTransforms, ImageTransformsConfig, ImageTransformConfig
# Option 1: Use default transform configuration (disabled by default)
transforms_config = ImageTransformsConfig(
enable=True, # Enable transforms
max_num_transforms=3, # Apply up to 3 transforms per frame
random_order=False, # Apply in standard order
)
transforms = ImageTransforms(transforms_config)
dataset = LeRobotDataset(
repo_id="your-username/your-dataset",
image_transforms=transforms
)
# Option 2: Create custom transform configuration
custom_transforms_config = ImageTransformsConfig(
enable=True,
max_num_transforms=2,
random_order=True,
tfs={
"brightness": ImageTransformConfig(
weight=1.0,
type="ColorJitter",
kwargs={"brightness": (0.7, 1.3)} # Adjust brightness range
),
"contrast": ImageTransformConfig(
weight=2.0, # Higher weight = more likely to be selected
type="ColorJitter",
kwargs={"contrast": (0.8, 1.2)}
),
"sharpness": ImageTransformConfig(
weight=0.5, # Lower weight = less likely to be selected
type="SharpnessJitter",
kwargs={"sharpness": (0.3, 2.0)}
),
}
)
dataset = LeRobotDataset(
repo_id="your-username/your-dataset",
image_transforms=ImageTransforms(custom_transforms_config)
)
# Option 3: Use pure torchvision transforms
from torchvision.transforms import v2
torchvision_transforms = v2.Compose([
v2.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
v2.GaussianBlur(kernel_size=3, sigma=(0.1, 2.0)),
])
dataset = LeRobotDataset(
repo_id="your-username/your-dataset",
image_transforms=torchvision_transforms
)利用可能な変換タイプ
type は、torchvision.transforms.v2 内の任意のクラスを指定できます。例:
ColorJitter: 明るさ、コントラスト、彩度、色相を調整しますSharpnessJitter: 画像のシャープネスをランダムに調整しますRandomAffine、RandomRotation: 幾何学的ジッターGaussianNoise、MotionBlur、JPEGCompression、GaussianPatchBrightness、RandomShadow、CoarseDropout、GammaCorrection、PlanckianJitter: 実際のロボットで観察されるカメラおよび照明効果Identity:変換なし(テストに有用)
その他の torchvision.transforms.v2 クラスも type として指定できます(RandomResizedCrop や GaussianBlur など)。これらはサンプルごとに1つずつ適用されます。また、torchvision のトランスフォームを image_transforms パラメータに直接渡すこともできます。
すべてのトランスフォームがバッチ化され、同じバッチ化されたトランスフォームが両方のバックエンドで実行されます。サンプル単位のクラス(SharpnessJitter、GaussianNoise、MotionBlur、GaussianPatchBrightness、RandomShadow、CoarseDropout、GammaCorrection、PlanckianJitter、RandomSubsetApply)およびmake_transform_from_configは、それらのBatched*代替品の非推奨なフロントエンドとしてインポート可能であり、使用時に警告が表示されます。これらは将来のリリースで削除されます。ImageTransformsは、(C, H, W)または(T, C, H, W)のテンソルを受け取る通常のtorch.nn.Moduleです。typeでトランスフォームを指定する設定には影響ありません。
既存のレシピにおける動作の変更。学習ではフレームを
uint8として読み込みます。以前、ColorJitter、RandomAffine、RandomRotation以外のLeRobotのトランスフォームは、浮動小数点以外の入力をそのまま返していました。そのため、GaussianNoise、MotionBlur、GaussianPatchBrightness、RandomShadow、CoarseDropout、GammaCorrection、またはPlanckianJitterを有効にしたレシピは、それらのトランスフォームを適用せずに学習されていました。現在ではこれらは両方のバックエンドで適用されるため、そのようなレシピは以前と異なる形で学習されます。つまり、要求した拡張が適用されるようになります。すでに適用されていたトランスフォームは、バッチ処理パスが浮動小数点で計算し、各トランスフォームの後に丸めるのではなく最後に一度だけ丸めるため、丸め処理の違いを除いて変更はありません。
設定オプション
enable: トランスフォームの有効化/無効化(デフォルト:False)max_num_transforms: フレームごとに適用される変換の最大数(デフォルト:3)random_order: 標準的な順序ではなく、ランダムな順序で変換を適用します(デフォルト:False)weight: 各transformのサンプリング確率(値が大きいほど選択されやすい。重みの合計が1でない場合は正規化されます)kwargs: 変換固有のパラメータ(例:明るさの範囲)backend: トレーニング中にトランスフォームが実行される場所。"dataloader"(デフォルト)または"gpu"。詳細は下記を参照gpu_compile:"gpu"バックエンドでは、バッチ化された変換をtorch.compileします(デフォルト:True)gpu_chunk_size:"gpu"バックエンドでは、一時的なメモリを制限するために、この数のサンプルずつ拡張を行います(デフォルト:32)
GPU上でトランスフォームを実行する
デフォルトでは、変換はDataLoaderのワーカー内でサンプルごとに実行されます。複数のカメラを使用する場合、ワーカーが実行する処理の中で最もコストがかかるのがこれであり、GPUがデータの到着を待たされることになります。backend=gpuを設定すると、ワーカーはデコードのみを行い、各バッチに対して同じ変換をポリシーデバイス上で適用します。
lerobot-train \
--dataset.repo_id=your-username/your-dataset \
--dataset.image_transforms.enable=true \
--dataset.image_transforms.backend=gpu \
--policy.type=act \
--policy.device=cudaランダム化は変更されていません。各サンプルは独自のトランスフォームとパラメータを抽出し、1つのカメラの履歴のフレームはそれらを共有します。拡張は、ポリシーのプリプロセッサの前に、トレーニングバッチにのみ適用されます。評価バッチ、ロールアウト、保存されたチェックポイントは変更されません。
知っておくべきこと:
- アクセラレータが必要です。
--policy.device=cpuは拒否されます。独自のimage_transformsコールバック関数を手動で構築したデータセットも、これらの処理がワーカー内で実行されるため、拒否されます。 - dataloader バックエンドがサポートするすべての transform 型に対応しています。バッチ処理の実装がない型(
JPEGCompression、およびColorJitter、RandomAffine、RandomRotation、Identity以外の torchvision クラス)は、バッチ内でサンプルごとに 1 つずつ適用されるため、高速化の恩恵を受けません。JPEGCompressionはさらに CPU 経由で往復します。フレームサイズを変更する transform は、サンプル数が 1 を超えるバッチでは拒否されます。 --seedによって再現可能であり、各プロセスには異なるシードが設定されます。データ拡張はポリシーの乱数ストリームを消費せず、gpu_compileおよびgpu_chunk_sizeは適用される内容を変更しません。- 両方のバックエンドは同じバッチ化された変換をfloatで実行し、最後に一度だけ丸めるため、同じパラメータが与えられた場合、片方のバックエンドで拡張されたサンプルは、もう片方のバックエンドで拡張された同じサンプルと等しくなります。
gpu_compileは、画像の形状ごとに 1 回だけ 15〜30 秒のコストがかかります。ピークメモリは、バッチ画像の float コピー 2 倍分と、1 つのチャンクのコピー数倍分程度です。メモリが逼迫している場合は、gpu_chunk_sizeを下げてください。random_order=trueを設定すると、変換時間がmax_num_transforms倍になります。
v2.1 → v3.0 への移行
コンバーターはエピソードごとのファイルをより大きなシャードに集約し、エピソードのオフセットやメタデータを書き出します。以下の手順に従って、データセットをコンバートしてください。
# Convert an existing v2.1 dataset hosted on the Hub:
python -m lerobot.scripts.convert_dataset_v21_to_v30 --repo-id=<HF_USER/DATASET_ID>機能
- parquetファイルを集約します:
episode_000000.parquet、episode_000001.parquet、… →file-000.parquet、… - mp4ファイルを集約します:
episode_000000.mp4、episode_000001.mp4、… →file-000.mp4、… meta/episodes/*(チャンク化された Parquet)を、エピソードごとの長さ、タスク、バイト/フレームオフセットで更新します。
よくある問題
プッシュする前に必ずfinalize()を呼び出してください
データセットの作成または記録を行う場合、parquetライターを正しく閉じるために、dataset.finalize()を必ず呼び出す必要があります。詳細はPR #1903を参照してください。
from lerobot.datasets import LeRobotDataset
# Create dataset and record episodes
dataset = LeRobotDataset.create(...)
for episode in range(num_episodes):
# Record frames
for frame in episode_data:
dataset.add_frame(frame)
dataset.save_episode()
# Call finalize() when done recording and before push_to_hub()
dataset.finalize() # Closes parquet writers, writes metadata footers
dataset.push_to_hub()なぜこれが必須なのか?
Dataset v3.0は、効率性のためにバッファリングされたメタデータを用いた増分parquet書き込みを使用します。finalize()メソッドは:
- バッファリングされたエピソードメタデータをディスクにフラッシュします
- フッターメタデータを書き込むために parquet のライターをクローズします。クローズしないと、parquet ファイルが破損します
- データセットが読み込み可能であることを確認します
finalize()を呼び出さないと、parquetファイルが不完全なままになり、データセットが正しく読み込めません。
その他の形式と実装
LanceDB データセットバックエンド
v3 データセットは、LanceDB テーブルとして保存することもできます。LanceDB は、AI のアクセスパターン向けに Lance フォーマットを基盤として構築されたマルチモーダルなデータレイクハウスです。任意の行とその動画バイトへの高速なランダムアクセス、および同じテーブル上でのスカラー、ベクトル、全文インデックスをサポートしており、EDA、マイニング、キュレーション、トレーニングをオブジェクトストレージから直接実行できます。レイアウトは meta/ を変更せず、タブラーデータ用の frames テーブルと、MP4 をブロブとして保持する videos テーブルを追加します。meta/info.json 内の "storage_format": "lance" がリーダーを選択します。LeRobotDataset API、アイテム、トレーニングパイプラインは変更ありません。
- GPUを供給しながら、HubやHF Storage Bucketsから直接トレーニングできます。 ランダムなフレームはインプレースで読み込まれ、各バッチに必要なバイトのみが取得されるため、すべてのバッチがグローバルシャッフルとなり、事前にダウンロードされるものは一切ありません。
- ファイルコピー程度のコストで変換します。動画は再エンコードされないため、アイテムはソースとビット単位で同一です。DROIDは34分で変換します。
- ゼロコピーの機能エンジニアリング。トレーナーが読み取るテーブルに、スコア、埋め込み、修正済みラベルなどの列を追加できます。動画を書き直したり、データセットをコピーしたりすることなく、インデックスを使ってそれらをフィルタリング・検索できます。リーダーは、認識していない列を無視します。
その仕組み、ベンチマーク、および例については、LanceDB LeRobot ガイドを参照してください。
カメラ機能は、動画ベースである必要があります。カメラフレームを画像として保存するデータセットはリーダーでサポートされておらず、変換前に動画を再エンコードする必要があります。
Lance データセットの読み取りには、lancedb 追加パッケージ(pip install "lerobot[lancedb]")が必要です。この形式は読み取り専用です。デフォルト形式でデータセットを記録・編集してから、lerobot-lancedb パッケージで変換してください。
pip install "lerobot-lancedb"
lerobot-lance-convert --repo-id lerobot/pusht --out ./pusht-lance # or --root <local dataset dir>読み込みには同じクラスを使用します:
from lerobot.datasets import LeRobotDataset
# Hub dataset repo
dataset = LeRobotDataset("lance-format/pusht-lance")
# HF Storage Bucket, read in place
dataset = LeRobotDataset("my-org/pusht-lance", repo_type="bucket")
# the same bucket as a root URI (other object store URIs work the same way)
dataset = LeRobotDataset("lerobot/pusht", root="hf://buckets/my-org/pusht-lance")HF Storage Bucket のオプションは lerobot-train で動作します:
lerobot-train \ --dataset.repo_id=my-org/pusht-lance \ --dataset.repo_type=bucket \ --policy.type=act
可視化は両方のビューアーで動作します。lerobot-dataset-viz --repo-id lance-format/pusht-lance --episode-index 0 は通常どおり Rerun でエピソードを開き、--display-mode foxglove を指定すると Foxglove に WebSocket 経由で配信します。いずれの場合も、画面に表示されているフレームのみが取得され、MCAP のエクスポートやダウンロードは行われません。