Instructions to use albus2024/dita_droid_pretrain with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LeRobot
How to use albus2024/dita_droid_pretrain with LeRobot:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Download scripts/convert_dita_checkpoints.py from albus2024/dita_droid_pretrain: direct link, hf CLI and curl.
- Browser
- Download file 22.8 kB
-
https://huggingface.co/albus2024/dita_droid_pretrain/resolve/main/scripts/convert_dita_checkpoints.py
- Command line
-
hf download hf://albus2024/dita_droid_pretrain/scripts/convert_dita_checkpoints.py
-
curl -L -o convert_dita_checkpoints.py https://huggingface.co/albus2024/dita_droid_pretrain/resolve/main/scripts/convert_dita_checkpoints.py
22.8 kB
| #!/usr/bin/env python3 | |
| """Convert Dita checkpoints into LeRobot policies or DROID fine-tuning initializations.""" | |
| from __future__ import annotations | |
| import argparse | |
| import gc | |
| import hashlib | |
| import json | |
| import shutil | |
| import subprocess | |
| import sys | |
| from dataclasses import dataclass, replace | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| from typing import Any | |
| REPO_ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(REPO_ROOT / "src")) | |
| import torch | |
| from huggingface_hub import HfApi | |
| from huggingface_hub.errors import HfHubHTTPError | |
| from lerobot.configs.types import FeatureType, NormalizationMode, PolicyFeature | |
| from lerobot.utils.constants import ACTION | |
| from lerobot_policy_dita import DitaConfig, DitaPolicy, make_dita_pre_post_processors | |
| from lerobot_policy_dita.policy.processor_dita import validate_dita_action_stats | |
| DEFAULT_STATS_ROOT = Path( | |
| "/home/shared/lyanam/huggingface/hub/" | |
| "datasets--openvla--modified_libero_rlds/snapshots/" | |
| "6ce6aaaaabdbe590b1eef5cd29c0d33f14a08551" | |
| ) | |
| class ConversionSpec: | |
| checkpoint: str | |
| output_name: str | |
| repo_id: str | |
| suite: str | |
| stats_glob: str | None | |
| SPECS: dict[str, ConversionSpec] = { | |
| "spatial": ConversionSpec( | |
| "model_ckpt/libero_spatial.pth", | |
| "dita_spatial", | |
| "albus2024/dita_spatial", | |
| "libero_spatial_no_noops", | |
| "libero_spatial_no_noops/1.0.0/dataset_statistics_*.json", | |
| ), | |
| "object": ConversionSpec( | |
| "model_ckpt/libero_object.pth", | |
| "dita_object", | |
| "albus2024/dita_object", | |
| "libero_object_no_noops", | |
| "libero_object_no_noops/1.0.0/dataset_statistics_*.json", | |
| ), | |
| "goal": ConversionSpec( | |
| "model_ckpt/libero_goal.pth", | |
| "dita_goal", | |
| "albus2024/dita_goal", | |
| "libero_goal_no_noops", | |
| "libero_goal_no_noops/1.0.0/dataset_statistics_*.json", | |
| ), | |
| "long": ConversionSpec( | |
| "model_ckpt/libero_long.pth", | |
| "dita_long", | |
| "albus2024/dita_long", | |
| "libero_10_no_noops", | |
| "libero_10_no_noops/1.0.0/dataset_statistics_*.json", | |
| ), | |
| "droid_pretrain": ConversionSpec( | |
| "model_ckpt/dita_droid_pretrain.pth", | |
| "dita_droid_pretrain", | |
| "albus2024/dita_droid_pretrain", | |
| "droid_pretrain", | |
| None, | |
| ), | |
| } | |
| def sha256_file(path: Path) -> str: | |
| digest = hashlib.sha256() | |
| with path.open("rb") as stream: | |
| for chunk in iter(lambda: stream.read(16 * 1024 * 1024), b""): | |
| digest.update(chunk) | |
| return digest.hexdigest() | |
| def git_revision() -> str: | |
| return subprocess.check_output( | |
| ["git", "rev-parse", "HEAD"], cwd=REPO_ROOT, text=True | |
| ).strip() | |
| def find_stats_file(stats_root: Path, spec: ConversionSpec) -> Path: | |
| if spec.stats_glob is None: | |
| raise RuntimeError( | |
| "DROID has no bundled action statistics. Convert it as a fine-tuning " | |
| "initialization and bind target-dataset statistics during training." | |
| ) | |
| matches = sorted(stats_root.glob(spec.stats_glob)) | |
| if len(matches) != 1: | |
| raise FileNotFoundError( | |
| f"Expected exactly one statistics file matching {stats_root / spec.stats_glob}, " | |
| f"found {len(matches)}." | |
| ) | |
| return matches[0] | |
| def load_dataset_stats(path: Path) -> tuple[dict[str, dict[str, torch.Tensor]], dict[str, Any]]: | |
| with path.open() as stream: | |
| raw = json.load(stream) | |
| action_stats = raw.get(ACTION) | |
| if not isinstance(action_stats, dict): | |
| raise ValueError(f"Statistics file {path} has no {ACTION!r} mapping.") | |
| for required in ("q01", "q99"): | |
| values = action_stats.get(required) | |
| if not isinstance(values, list) or len(values) != 7: | |
| raise ValueError(f"{path}: action.{required} must contain exactly seven values.") | |
| validate_dita_action_stats({ACTION: action_stats}) | |
| tensor_stats = { | |
| ACTION: { | |
| name: torch.as_tensor(values, dtype=torch.float32) | |
| for name, values in action_stats.items() | |
| if isinstance(values, list) and len(values) == 7 | |
| } | |
| } | |
| return tensor_stats, raw | |
| def make_dita_config(spec: ConversionSpec, device: str) -> DitaConfig: | |
| return DitaConfig( | |
| dataset_suite=None if spec.suite == "droid_pretrain" else spec.suite, | |
| input_features={ | |
| "observation.images.image": PolicyFeature( | |
| type=FeatureType.VISUAL, | |
| shape=(3, 256, 256), | |
| ) | |
| }, | |
| output_features={ACTION: PolicyFeature(type=FeatureType.ACTION, shape=(7,))}, | |
| device=device, | |
| num_layers=12, | |
| traj_length=11, | |
| dropout_rate=0.0, | |
| freeze_backbone=False, | |
| optimizer_lr=5e-4 if spec.suite == "libero_10_no_noops" else 1e-4, | |
| use_qformer=True, | |
| prediction_type="epsilon", | |
| attn_implementation="eager", | |
| num_inference_steps=10, | |
| dtype="float16", | |
| tokenizer_max_length=77, | |
| image_resolution=(224, 224), | |
| chunk_size=10, | |
| n_action_steps=1, | |
| history_frames=2, | |
| gripper_threshold=0.5, | |
| gripper_mode="continuous" if spec.suite == "droid_pretrain" else "binary", | |
| normalization_mapping={ | |
| "VISUAL": NormalizationMode.IDENTITY, | |
| "ACTION": NormalizationMode.QUANTILES, | |
| "LANGUAGE": NormalizationMode.IDENTITY, | |
| }, | |
| push_to_hub=False, | |
| ) | |
| def extract_parameter_state(checkpoint_path: Path) -> dict[str, torch.Tensor]: | |
| checkpoint = torch.load(checkpoint_path, map_location="cpu", weights_only=False) | |
| if not isinstance(checkpoint, dict): | |
| raise TypeError(f"{checkpoint_path}: expected a checkpoint dictionary.") | |
| state = checkpoint.get("parameter", checkpoint) | |
| if not isinstance(state, dict) or not state or not all( | |
| isinstance(k, str) and isinstance(v, torch.Tensor) for k, v in state.items() | |
| ): | |
| raise TypeError( | |
| f"{checkpoint_path}: expected a tensor state dict or checkpoint['parameter']." | |
| ) | |
| return state | |
| def align_and_validate_state( | |
| state: dict[str, torch.Tensor], expected: dict[str, torch.Tensor] | |
| ) -> tuple[dict[str, torch.Tensor], str | None]: | |
| prefix: str | None = None | |
| if set(state) == set(expected): | |
| aligned = state | |
| elif state and all(key.startswith("module.") for key in state): | |
| stripped = {key.removeprefix("module."): value for key, value in state.items()} | |
| aligned = stripped | |
| if set(stripped) == set(expected): | |
| prefix = "module." | |
| else: | |
| aligned = state | |
| missing = sorted(set(expected) - set(aligned)) | |
| unexpected = sorted(set(aligned) - set(expected)) | |
| shape_mismatches = sorted( | |
| (key, tuple(aligned[key].shape), tuple(expected[key].shape)) | |
| for key in set(aligned) & set(expected) | |
| if aligned[key].shape != expected[key].shape | |
| ) | |
| if missing or unexpected or shape_mismatches: | |
| raise RuntimeError( | |
| "Checkpoint incompatibility:\n" | |
| f"missing keys ({len(missing)}): {missing}\n" | |
| f"unexpected keys ({len(unexpected)}): {unexpected}\n" | |
| f"shape mismatches ({len(shape_mismatches)}): {shape_mismatches}" | |
| ) | |
| return aligned, prefix | |
| def model_card( | |
| spec: ConversionSpec, | |
| checkpoint_path: Path, | |
| checkpoint_sha: str, | |
| stats_path: Path, | |
| stats_sha: str, | |
| stats_raw: dict[str, Any], | |
| code_sha: str, | |
| runtime_wheel_name: str | None = None, | |
| ) -> str: | |
| q01 = stats_raw[ACTION]["q01"] | |
| q99 = stats_raw[ACTION]["q99"] | |
| install = ( | |
| f'pip install "lerobot_policy_dita[libero] @ https://huggingface.co/{spec.repo_id}/resolve/main/{runtime_wheel_name}"' | |
| if runtime_wheel_name | |
| else f'pip install "git+https://github.com/A-Dumbledore/lerobot_policy_dita.git@{code_sha}"' | |
| ) | |
| return f"""--- | |
| library_name: lerobot | |
| pipeline_tag: robotics | |
| tags: | |
| - lerobot | |
| - dita | |
| - libero | |
| --- | |
| # Dita — {spec.suite} | |
| This repository is a strict LeRobot conversion of `{checkpoint_path.name}` from the original | |
| [Dita](https://github.com/RoboDita/Dita) implementation. It is configured for the | |
| `{spec.suite}` action distribution and is not interchangeable with another LIBERO suite. | |
| ## Provenance and compatibility contract | |
| - Source checkpoint: `{checkpoint_path.as_posix()}` | |
| - Source checkpoint SHA256: `{checkpoint_sha}` | |
| - Conversion code commit: `{code_sha}` | |
| - Dataset statistics: `{stats_path.as_posix()}` | |
| - Dataset statistics SHA256: `{stats_sha}` | |
| - Image processing: one RGB primary/agentview camera, resized to 224×224, followed once by | |
| ImageNet mean `(0.485, 0.456, 0.406)` and std `(0.229, 0.224, 0.225)`. | |
| - Action processing: dimensions 0–5 use this suite's `q01`/`q99`; dimension 6 is not normalized. | |
| - Action mask: `[true, true, true, true, true, true, false]`. | |
| - q01: `{json.dumps(q01)}` | |
| - q99: `{json.dumps(q99)}` | |
| - Gripper: normalized output is thresholded with `> 0.5`, then the Cartesian dimensions are | |
| unnormalized, then the original Dita gripper sign flip is applied. | |
| - Rollout: `traj_length=11`, two-frame history, `chunk_size=10`, `n_action_steps=1`, | |
| `num_inference_steps=10`, DDIM `prediction_type=epsilon`, and CUDA AMP by default. | |
| - `policy.dtype` controls compute precision (`float32`, `float16`, `bfloat16`); | |
| parameters remain FP32. It is the sole precision setting; `use_amp` is derived internally. | |
| - LIBERO's LeRobot environment processor performs the 180° image rotation. The policy does not | |
| rotate the image a second time. | |
| ## Install | |
| ```bash | |
| {install} | |
| ``` | |
| ## Evaluate | |
| ```bash | |
| lerobot-eval \\ | |
| --policy.path={spec.repo_id} \\ | |
| --policy.gripper_mode=binary \\ | |
| --policy.device=cuda \\ | |
| --env.type=libero \\ | |
| --env.task={spec.suite.removesuffix('_no_noops')} \\ | |
| --env.max_parallel_tasks=1 \\ | |
| --eval.batch_size=1 \\ | |
| --eval.n_episodes=10 \\ | |
| --output_dir=./outputs/{spec.output_name} | |
| ``` | |
| The processor state in this repository contains the required statistics; no source `.pth` file or | |
| local dataset metadata is needed at evaluation time. | |
| ## Validation | |
| The converter checks all parameter names and shapes, loads with `strict=True`, and checks the | |
| saved artifact files. Run the project's parity and converted-checkpoint verification scripts | |
| separately before using the policy; conversion alone does not establish rollout parity. | |
| """ | |
| def pretraining_model_card( | |
| spec: ConversionSpec, checkpoint_sha: str, code_sha: str, runtime_wheel_name: str | None | |
| ) -> str: | |
| install = ( | |
| f'pip install "lerobot_policy_dita[training] @ https://huggingface.co/{spec.repo_id}/resolve/main/{runtime_wheel_name}"' | |
| if runtime_wheel_name | |
| else 'pip install -e ".[training]" # From the updated lerobot_policy_dita checkout' | |
| ) | |
| return f"""--- | |
| library_name: lerobot | |
| pipeline_tag: robotics | |
| tags: | |
| - lerobot | |
| - dita | |
| - droid | |
| - pretraining | |
| --- | |
| # Dita DROID pretraining initialization | |
| This repository converts the original Dita DROID weights for subsequent fine-tuning. | |
| It contains model weights, a LeRobot Dita configuration, and processor templates without | |
| dataset action statistics. It is not a ready-to-deploy DROID or LIBERO policy. | |
| ## Provenance | |
| - Source: `{spec.checkpoint}` from the original [Dita](https://github.com/RoboDita/Dita) release. | |
| - Source checkpoint SHA256: `{checkpoint_sha}`. | |
| - Conversion base Git commit: `{code_sha}`. See `conversion_metadata.json` for the | |
| working-tree status, conversion-script hash, and packaged runtime hash when present. | |
| - All model parameter keys and shapes are checked, followed by `strict=True` loading. | |
| - Optimizer/scheduler state is not exported. No source DROID action statistics are inferred. | |
| ## Install | |
| Use Python 3.12. The updated Dita runtime translates LeRobot's standard fine-tuning | |
| normalizer overrides to Dita's custom processors. | |
| ```bash | |
| {install} | |
| ``` | |
| ## Fine-tune | |
| Use a LeRobot dataset with seven-dimensional actions, a compatible primary RGB camera, | |
| and task language. Its action metadata must contain seven-element `q01` and `q99` arrays | |
| computed in the target training action convention. The default `gripper_mode=continuous` | |
| uses quantiles for the first six dimensions and dataset `action.min/max` for the gripper. | |
| Continuous outputs are restored to the recorded position units without thresholding or | |
| sign inversion. Use `--policy.gripper_mode=binary` for LIBERO signed training targets | |
| (-1=open, +1=closed), encoded internally as 1=open, 0=closed. | |
| ImageNet image normalization and CLIP text encoding remain fixed. | |
| ```bash | |
| lerobot-train \\ | |
| --policy.path={spec.repo_id} \\ | |
| --policy.gripper_mode=continuous \\ | |
| --policy.device=cuda \\ | |
| --policy.dtype=bfloat16 \\ | |
| --policy.input_features=null \\ | |
| --policy.push_to_hub=false \\ | |
| --dataset.repo_id=YOUR_USERNAME/YOUR_DATASET \\ | |
| --output_dir=outputs/train/dita_droid_finetune | |
| ``` | |
| `policy.dtype` accepts `float32`, `float16` and `bfloat16` and controls autocast; | |
| weights and optimizer state remain FP32. Use BF16 on a supported GPU, or select | |
| FP32/FP16 explicitly. The default CUDA precision remains FP16 for compatibility. | |
| At the start of a new fine-tuning run, LeRobot binds the target dataset's statistics to | |
| both processor templates. They remain fixed during that run and are saved with the | |
| fine-tuned checkpoint. LeRobot 0.6.1 restores saved statistics on resume. The same | |
| pretraining repository can initialize multiple target datasets; no per-dataset pretraining | |
| upload is needed. Missing quantiles cause an explicit error when processing actions. | |
| The processor templates require cached or downloadable DINOv2/CLIP assets. Load the | |
| fine-tuned checkpoint together with its saved processors for evaluation and deployment. | |
| Any `validation_report.json` documents the checks actually run for this upload; no | |
| task success rate or DROID deployment equivalence is claimed. | |
| """ | |
| def previous_revision(api: HfApi, repo_id: str) -> str | None: | |
| try: | |
| return api.model_info(repo_id).sha | |
| except HfHubHTTPError as error: | |
| if error.response is not None and error.response.status_code == 404: | |
| return None | |
| raise | |
| def convert_one( | |
| spec: ConversionSpec, | |
| *, | |
| output_root: Path, | |
| stats_root: Path, | |
| device: str, | |
| push_to_hub: bool, | |
| repo_id_override: str | None, | |
| runtime_wheel: Path | None = None, | |
| ) -> dict[str, Any]: | |
| if repo_id_override: | |
| spec = replace(spec, repo_id=repo_id_override) | |
| pretraining_only = spec.suite == "droid_pretrain" | |
| if runtime_wheel is not None and (not runtime_wheel.is_file() or runtime_wheel.suffix != ".whl"): | |
| raise ValueError(f"Expected an existing runtime wheel: {runtime_wheel}") | |
| checkpoint_path = REPO_ROOT / spec.checkpoint | |
| if not checkpoint_path.is_file(): | |
| raise FileNotFoundError(checkpoint_path) | |
| stats_path = None if pretraining_only else find_stats_file(stats_root, spec) | |
| output_dir = output_root / spec.output_name | |
| if output_dir.exists() and any(output_dir.iterdir()): | |
| raise FileExistsError( | |
| f"Refusing to mix artifacts in non-empty {output_dir}; use a fresh output root." | |
| ) | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| checkpoint_sha = sha256_file(checkpoint_path) | |
| stats_sha = sha256_file(stats_path) if stats_path else None | |
| dataset_stats, stats_raw = load_dataset_stats(stats_path) if stats_path else (None, None) | |
| code_sha = git_revision() | |
| config = make_dita_config(spec, device) | |
| policy = DitaPolicy(config) | |
| expected = policy.model.state_dict() | |
| state = extract_parameter_state(checkpoint_path) | |
| aligned, stripped_prefix = align_and_validate_state(state, expected) | |
| load_result = policy.model.load_state_dict(aligned, strict=True) | |
| if load_result.missing_keys or load_result.unexpected_keys: | |
| raise RuntimeError(f"Strict load unexpectedly returned {load_result}.") | |
| policy.save_pretrained(output_dir) | |
| preprocessor, postprocessor = make_dita_pre_post_processors(config, dataset_stats) | |
| preprocessor.save_pretrained(output_dir) | |
| postprocessor.save_pretrained(output_dir) | |
| metadata: dict[str, Any] = { | |
| "converted_at_utc": datetime.now(timezone.utc).isoformat(), | |
| "source_model": "Dita/scripts/llama_dp.py", | |
| "source_checkpoint": spec.checkpoint, | |
| "source_checkpoint_sha256": checkpoint_sha, | |
| "checkpoint_parameter_tensors": len(aligned), | |
| "stripped_prefix": stripped_prefix, | |
| "strict_load": { | |
| "missing_keys": [], | |
| "unexpected_keys": [], | |
| "shape_mismatches": [], | |
| }, | |
| "dataset_suite": spec.suite, | |
| "artifact_purpose": "finetuning_initialization" if pretraining_only else "deployment_policy", | |
| "statistics_binding": "target_dataset_at_finetuning" if pretraining_only else "bundled", | |
| "dataset_statistics_file": str(stats_path) if stats_path else None, | |
| "dataset_statistics_sha256": stats_sha, | |
| "action_q01": stats_raw[ACTION]["q01"] if stats_raw else None, | |
| "action_q99": stats_raw[ACTION]["q99"] if stats_raw else None, | |
| "gripper_mode": config.gripper_mode, | |
| "action_normalization_mask": list(config.action_normalization_mask), | |
| "conversion_code_git_sha": code_sha, | |
| "conversion_code_git_dirty": bool(subprocess.check_output( | |
| ["git", "status", "--porcelain"], cwd=REPO_ROOT, text=True | |
| ).strip()), | |
| "conversion_script_sha256": sha256_file(Path(__file__)), | |
| "target_repo_id": repo_id_override or spec.repo_id, | |
| } | |
| (output_dir / "conversion_metadata.json").write_text( | |
| json.dumps(metadata, indent=2) + "\n" | |
| ) | |
| (output_dir / "README.md").write_text( | |
| pretraining_model_card(spec, checkpoint_sha, code_sha, runtime_wheel.name if runtime_wheel else None) | |
| if pretraining_only else model_card( | |
| spec, | |
| checkpoint_path, | |
| checkpoint_sha, | |
| stats_path, | |
| stats_sha, | |
| stats_raw, | |
| code_sha, | |
| runtime_wheel.name if runtime_wheel else None, | |
| ) | |
| ) | |
| required = { | |
| "config.json", | |
| "model.safetensors", | |
| "policy_preprocessor.json", | |
| "policy_postprocessor.json", | |
| "README.md", | |
| "conversion_metadata.json", | |
| } | |
| missing_artifacts = sorted(required - {path.name for path in output_dir.iterdir()}) | |
| if missing_artifacts: | |
| raise RuntimeError(f"Conversion did not produce required files: {missing_artifacts}") | |
| state_files = sorted(path.name for path in output_dir.glob("*.safetensors") if path.name != "model.safetensors") | |
| if not state_files and not pretraining_only: | |
| raise RuntimeError("Processor state safetensors were not saved.") | |
| metadata["processor_state_files"] = state_files | |
| if runtime_wheel is not None: | |
| shutil.copy2(runtime_wheel, output_dir / runtime_wheel.name) | |
| metadata["runtime_wheel"] = runtime_wheel.name | |
| metadata["runtime_wheel_sha256"] = sha256_file(runtime_wheel) | |
| (output_dir / "conversion_metadata.json").write_text( | |
| json.dumps(metadata, indent=2) + "\n" | |
| ) | |
| if push_to_hub: | |
| api = HfApi() | |
| account = api.whoami()["name"] | |
| target_repo = repo_id_override or spec.repo_id | |
| if target_repo.split("/", 1)[0] != account: | |
| raise PermissionError(f"Authenticated as {account}, cannot publish {target_repo}.") | |
| old_sha = previous_revision(api, target_repo) | |
| metadata["previous_hub_revision"] = old_sha | |
| (output_dir / "conversion_metadata.json").write_text( | |
| json.dumps(metadata, indent=2) + "\n" | |
| ) | |
| api.create_repo(target_repo, repo_type="model", exist_ok=True) | |
| commit = api.upload_folder( | |
| repo_id=target_repo, | |
| repo_type="model", | |
| folder_path=output_dir, | |
| commit_message=f"Synchronize Dita {spec.suite} LeRobot checkpoint", | |
| ) | |
| metadata["hub_commit_sha"] = commit.oid | |
| print(f"Uploaded {target_repo} at {commit.oid} (previous: {old_sha})") | |
| print( | |
| f"Converted {checkpoint_path.name}: {len(aligned)} tensors, strict=True, " | |
| f"SHA256={checkpoint_sha}, output={output_dir}" | |
| ) | |
| del preprocessor, postprocessor, policy, aligned, state, expected | |
| gc.collect() | |
| if torch.cuda.is_available(): | |
| torch.cuda.empty_cache() | |
| return metadata | |
| def parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| selection = parser.add_mutually_exclusive_group(required=True) | |
| selection.add_argument( | |
| "--checkpoint", | |
| help="spatial/object/goal/long/droid_pretrain, or a known checkpoint filename/path.", | |
| ) | |
| selection.add_argument("--all", action="store_true", help="Convert all four deployable LIBERO checkpoints.") | |
| parser.add_argument("--output-dir", type=Path, default=REPO_ROOT / "converted_checkpoints") | |
| parser.add_argument("--stats-root", type=Path, default=DEFAULT_STATS_ROOT) | |
| parser.add_argument("--device", default="cuda") | |
| parser.add_argument("--push-to-hub", action="store_true") | |
| parser.add_argument("--repo-id", help="Override the target repo for a single checkpoint only.") | |
| parser.add_argument("--runtime-wheel", type=Path, help="Bundle an updated Dita runtime wheel with the model.") | |
| return parser.parse_args() | |
| def main() -> None: | |
| args = parse_args() | |
| if args.repo_id and args.all: | |
| raise ValueError("--repo-id can only be used with --checkpoint.") | |
| selected = [SPECS[name] for name in ("spatial", "object", "goal", "long")] | |
| if not args.all: | |
| if args.checkpoint in SPECS: | |
| selected = [SPECS[args.checkpoint]] | |
| else: | |
| checkpoint_path = Path(args.checkpoint).expanduser().resolve() | |
| matches = [ | |
| spec | |
| for spec in SPECS.values() | |
| if Path(spec.checkpoint).name == checkpoint_path.name | |
| or (spec.suite == "droid_pretrain" and checkpoint_path.name == "droid_pretrain.pth") | |
| ] | |
| if len(matches) != 1: | |
| raise ValueError( | |
| f"Unknown checkpoint {args.checkpoint!r}; expected one of " | |
| f"{sorted(SPECS)} or their checkpoint paths." | |
| ) | |
| selected = [replace(matches[0], checkpoint=str(checkpoint_path))] | |
| for spec in selected: | |
| convert_one( | |
| spec, | |
| output_root=args.output_dir, | |
| stats_root=args.stats_root, | |
| device=args.device, | |
| push_to_hub=args.push_to_hub, | |
| repo_id_override=args.repo_id, | |
| runtime_wheel=args.runtime_wheel, | |
| ) | |
| if __name__ == "__main__": | |
| main() | |