File size: 7,699 Bytes
319eb16 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | # Custom Dataset Guide
Learn how to add your own dataset (like DROID, Bridge, etc.) to the Robometer training pipeline.
## Overview
Adding a custom dataset involves:
1. Creating a dataset loader module
2. Implementing the required data format
3. Integrating with the main converter
4. Testing and validation
## Required Data Format
Your dataset loader must produce trajectories in this format:
```python
{
'frames': List[Union[str, bytes, np.ndarray]], # Video file paths, MP4 bytes, or frame arrays
'actions': np.ndarray, # Robot actions (N, action_dim)
'is_robot': bool, # True for robot data, False for human
'task': str, # Human-readable task description
'optimal': str # "optimal", "suboptimal", or "failed"
}
```
## Step 0: Set Hugging Face repo ID
Before we start, you must have an HF account which will be pushed to.
You will set this by setting
```
export HF_USERNAME=<insert HF username here>
```
## Step 1: Create Dataset Loader
Create `data/{dataset_name}_loader.py`:
```python
#!/usr/bin/env python3
"""
{DatasetName} dataset loader for Robometer model training.
"""
import os
import numpy as np
from typing import List, Dict
from pathlib import Path
from tqdm import tqdm
def load_{dataset_name}_dataset(base_path: str) -> Dict[str, List[Dict]]:
"""Load {DatasetName} dataset and organize by task.
Args:
base_path: Path to the {DatasetName} dataset directory
Returns:
Dictionary mapping task names to lists of trajectory dictionaries
"""
print(f"Loading {DatasetName} dataset from: {base_path}")
print("=" * 100)
print(f"LOADING {DATASET_NAME} DATASET")
print("=" * 100)
task_data = {}
base_path = Path(base_path)
if not base_path.exists():
raise FileNotFoundError(f"{DatasetName} dataset path not found: {base_path}")
# TODO: Implement your dataset-specific logic here
# Example structure:
# Find your data files
data_files = list(base_path.glob("**/*.{your_format}")) # e.g., *.pkl, *.json, *.hdf5
for file_path in tqdm(data_files, desc=f"Processing {DatasetName} dataset"):
task_name = file_path.stem # or extract from your naming scheme
# Load your data file
trajectories = load_trajectories_from_file(file_path)
if trajectories:
task_data[task_name] = trajectories
print(f"Loaded {sum(len(trajectories) for trajectories in task_data.values())} trajectories from {len(task_data)} tasks")
return task_data
def load_trajectories_from_file(file_path: Path) -> List[Dict]:
"""Load trajectories from a single data file."""
trajectories = []
# TODO: Implement your file loading logic here
# This depends on your dataset format (HDF5, JSON, pickle, etc.)
# Example for different formats:
# For HDF5:
# import h5py
# with h5py.File(file_path, 'r') as f:
# # Navigate your HDF5 structure
# pass
# For JSON:
# import json
# with open(file_path, 'r') as f:
# data = json.load(f)
# For pickle:
# import pickle
# with open(file_path, 'rb') as f:
# data = pickle.load(f)
# Convert to required format:
# trajectory = {
# 'frames': your_frames, # List of file paths, bytes, or numpy arrays
# 'actions': your_actions, # numpy array of shape (sequence_length, action_dim)
# 'is_robot': True, # or False for human demonstrations
# 'task': your_task_description,
# 'optimal': 'optimal' # or 'suboptimal', 'failed'
# }
# trajectories.append(trajectory)
return trajectories
```
## Step 2: Add to Main Converter
Edit `data/generate_hf_dataset.py` to include your dataset:
```python
# In the main() function, add your dataset type:
elif "{dataset_name}" in cfg.dataset.dataset_name.lower():
from {dataset_name}_loader import load_{dataset_name}_dataset
# Load the trajectories using your loader
task_data = load_{dataset_name}_dataset(cfg.dataset.dataset_path)
trajectories = flatten_task_data(task_data)
```
## Step 3: Create Configuration File
Create `configs/data_gen_configs/{dataset_name}.yaml`:
```yaml
# {DatasetName} dataset configuration
dataset:
dataset_path: /path/to/your/{dataset_name}/dataset
dataset_name: {dataset_name}
output:
output_dir: {dataset_name}_dataset
max_trajectories: 1000 # Adjust as needed
max_frames: 32
use_video: true
fps: 10
hub:
push_to_hub: false
hub_repo_id: your-username/{dataset_name}_rbm
```
## Step 4: Test Your Implementation
Create a test script `test_{dataset_name}_loader.py`:
```python
from {dataset_name}_loader import load_{dataset_name}_dataset
from helpers import flatten_task_data
# Test your loader
task_data = load_{dataset_name}_dataset("/path/to/your/{dataset_name}/dataset")
trajectories = flatten_task_data(task_data)
print(f"Loaded {len(trajectories)} trajectories")
print(f"Sample trajectory keys: {list(trajectories[0].keys())}")
print(f"Sample task: {trajectories[0].get('task', 'No task found')}")
print(f"Sample frames type: {type(trajectories[0]['frames'])}")
print(f"Sample actions shape: {trajectories[0]['actions'].shape}")
```
## Step 5: Run Dataset Conversion
```bash
uv run python data/generate_hf_dataset.py \
--config_path=configs/data_gen_configs/{dataset_name}.yaml
```
## Frame Format Options
### Option 1: Video File Paths
```python
'frames': ['/path/to/video1.mp4', '/path/to/video2.mp4']
```
### Option 2: Raw Video Bytes (for streaming)
```python
'frames': video_bytes # bytes object containing MP4 data
```
### Option 3: Frame Arrays
```python
'frames': np.array([frame1, frame2, ...]) # shape: (seq_len, H, W, 3)
```
## Common Dataset Formats
### HDF5 Datasets (like LIBERO)
```python
import h5py
with h5py.File(file_path, 'r') as f:
frames = f['observations']['camera_data'][:]
actions = f['actions'][:]
```
### JSON + Video Files
```python
import json
with open(metadata_file, 'r') as f:
metadata = json.load(f)
video_path = base_path / metadata['video_file']
frames = [str(video_path)] # Let converter handle video loading
```
### Pickle Files
```python
import pickle
with open(file_path, 'rb') as f:
episode_data = pickle.load(f)
frames = episode_data['observations']['images']
actions = episode_data['actions']
```
## Error Handling
Add robust error handling to your loader:
```python
try:
# Your data loading code
pass
except Exception as e:
print(f"Error loading {file_path}: {e}")
continue # Skip problematic files
```
## Performance Tips
1. **Use tqdm** for progress bars on long operations
2. **Validate data shapes** before adding to trajectories
3. **Handle missing files gracefully**
4. **Use generators** for memory efficiency with large datasets
5. **Cache expensive operations** when possible
## Example Datasets to Reference
- **LIBERO**: `data/libero_loader.py` - HDF5 format
- **AgiBotWorld**: `data/agibotworld_loader.py` - Streaming format
## Integration Testing
After implementation, test with the training pipeline:
```bash
# Test dataset loading
uv run python data/generate_hf_dataset.py \
--config_path=configs/data_gen_configs/{dataset_name}.yaml \
--output.max_trajectories=10
# Test training integration
uv run accelerate launch --config_file configs/fsdp.yaml train.py \
--config_path=configs/config.yaml \
--dataset.dataset_path={dataset_name}_dataset/{dataset_name}
``` |