* fix: sharing predicted chunk with user * [pre-commit.ci] pre-commit autoupdate (#1011) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Revert "[pre-commit.ci] pre-commit autoupdate" (#1025) * fix(ci): Pin draccus (<0.10.0) and torch (<2.7) to fix pipeline (#1022) Co-authored-by: imstevenpmwork <steven.palma@huggingface.co> Co-authored-by: Simon Alibert <75076266+aliberts@users.noreply.github.com> * fix(ci): Pin `torchcodec` (==0.2.1) to fix pipeline temporarly (#1030) * Update tutorial (#1021) Co-authored-by: Simon Alibert <75076266+aliberts@users.noreply.github.com> * Add description motor order SO-101 leader (#1051) * feat(encoding): switching to PyAV for ffmpeg related tasks (#983) * feat(docs): Add new docs build process (#1046) Co-authored-by: Mishig Davaadorj <dmishig@gmail.com> Co-authored-by: Steven Palma <steven.palma@huggingface.co> * Docs: adapt text + fix video code (#1064) * Fix typos (#1070) * docs: minor corrections and clean-up (#1089) * Update 10_use_so100.md; use diff syntax (#944) Co-authored-by: Pepijn <138571049+pkooij@users.noreply.github.com> * Update 12_use_so101.md (#1081) Co-authored-by: Pepijn <138571049+pkooij@users.noreply.github.com> * bug fix for #1071 When --display_data=true, Failed running control_robot. (#1073) * Add editable -e for feetech install command (#1133) * Fix: emptying action queue between resets (#1117) * fix: typos and grammar (#1148) * Update README.md (#1160) * Update README.md (#1163) * [Fix] Unpin torch beyond 2.6.0 & torchcodec beyond 0.2.1 (#1127) * (hotfix): nightly CI by clipping pymunk version below 7.0.0 (#1182) * [pre-commit.ci] pre-commit autoupdate (#1048) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Simon Alibert <simon.alibert@huggingface.co> * Add SmolVLA (#1175) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: fracapuano <francesco.capuano@huggingface.co> Co-authored-by: Steven Palma <imstevenpmwork@ieee.org> Co-authored-by: Dana Aubakirova <118912928+danaaubakirova@users.noreply.github.com> Co-authored-by: Remi <remi.cadene@huggingface.co> * Fix SmolVLA loss not sent to wandb (#1198) * Hardware API redesign (#777) Co-authored-by: Pepijn <138571049+pkooij@users.noreply.github.com> Co-authored-by: Steven Palma <imstevenpmwork@ieee.org> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Steven Palma <steven.palma@huggingface.co> Co-authored-by: Adil Zouitine <adilzouitinegm@gmail.com> Co-authored-by: Pepijn <pepijn@huggingface.co> * fix(smolvla): update record.py, fix populate_queues and remove unused dependencies (#1208) * replaced OBS_ROBOT with OBS_STATE constant (#1211) * Fix test_teleoperate (#1216) * Fix LeKiwi example (#1217) * Fix smolVLA dependencies (#1218) * fix(pyserial): adding pyserial dependency to global ones (#1219) * Update SmolVLA README.md (#1228) * Fix unable to set camera width/height to non-default (#1225) * Update tutorial link (#1250) * update KochFollower.get_observation() so it returns same observation structure as SO101 (#1248) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [pre-commit.ci] pre-commit autoupdate (#1185) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Simon Alibert <75076266+aliberts@users.noreply.github.com> * Proposal for fix for enter_pressed on Windows (#1230) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Simon Alibert <75076266+aliberts@users.noreply.github.com> * fix: update pi0 dependency version constraint (#1247) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Match motor names with ids lekiwi (#1261) * fix issues: checkpoints keys mismatch and 'task' tokenisation in smolvla (#1256) Co-authored-by: danaaubakirova <d.aubakirova@alumni.edu.kz> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Simon Alibert <75076266+aliberts@users.noreply.github.com> Co-authored-by: Simon Alibert <simon.alibert@huggingface.co> * fix(docs): update realsense documentation (#1268) * Use HF Papers (#1120) * Skip normalization parameters in load_smolvla (#1274) * fix(record): no teleop needed when running with policy (#1284) * Port HIL SERL (#644) Co-authored-by: Michel Aractingi <michel.aractingi@huggingface.co> Co-authored-by: Eugene Mironov <helper2424@gmail.com> Co-authored-by: s1lent4gnt <kmeftah.khalil@gmail.com> Co-authored-by: Ke Wang <superwk1017@gmail.com> Co-authored-by: Yoel Chornton <yoel.chornton@gmail.com> Co-authored-by: imstevenpmwork <steven.palma@huggingface.co> Co-authored-by: Simon Alibert <simon.alibert@huggingface.co> * fix(docs): SmolVLA fine-tuning getting started (#1201) Co-authored-by: Pepijn <138571049+pkooij@users.noreply.github.com> Co-authored-by: danaaubakirova <d.aubakirova@alumni.edu.kz> Co-authored-by: Simon Alibert <75076266+aliberts@users.noreply.github.com> Co-authored-by: Francesco Capuano <francesco_capuano@aol.com> Co-authored-by: Steven Palma <steven.palma@huggingface.co> * chore(teleop): print calibration path saved (#1286) * chore(dependencies): add gamepad support with pygame and hidapi (#1287) * Robot integration tutorial (#1285) * fix(docs): update send_feedback docstrings * Add sim tutorial, fix lekiwi motor config, add notebook links (#1275) Co-authored-by: AdilZouitine <adilzouitinegm@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Michel Aractingi <michel.aractingi@huggingface.co> Co-authored-by: s1lent4gnt <kmeftah.khalil@gmail.com> Co-authored-by: Michel Aractingi <michel.aractingi@gmail.com> Co-authored-by: Eugene Mironov <helper2424@gmail.com> Co-authored-by: imstevenpmwork <steven.palma@huggingface.co> Co-authored-by: Simon Alibert <75076266+aliberts@users.noreply.github.com> Co-authored-by: Steven Palma <imstevenpmwork@ieee.org> * Fixes on robot integration tutorial (#1290) * Add keyboard teleop device to control the end effector robot (#1289) * Improve type hints (#1293) * fix(record): no teleop arg in reset environment (#1294) * `learner.py` import so101_leader instead of so100 (#1295) Co-authored-by: Adil Zouitine <adilzouitinegm@gmail.com> * Fixing `PI0` Policy (#1297) * `gym_manipulator.py` Remove None value action_intervention of BaseLeaderTeleoperator (#1299) * (chore): incorrect resume parameter in recording documentation (#1301) * Update lekiwi.mdx (#1229) * bump `pi0` and `hil` transformers version (#1298) * docs: fix imitation learning robots docs command (#1308) * fix(benchmarks): remove .numpy() from frame in benchmark script (#1354) * add smolvla to the supported policies to run tests (: * add: chunk-level access for the policy * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add: smolvla in availables * remove: smolvla from library supported policies * fix: change env for training, xarm is broken as of now * add: predict_action_chunk to all supported policies * fix: add robot type constants * add: predict action chunk in base policy class * restore original Makefile * fix: minor * fix: dict keys come from lerobot/constants * fix: improve act encapsulation, properly supporting temporal ensembling * fix: smolvla action chunking * fix: very minor, but very annoying * fix: minor * fix minor naming Co-authored-by: Steven Palma <imstevenpmwork@ieee.org> Signed-off-by: Francesco Capuano <74058581+fracapuano@users.noreply.github.com> * fix: refactoring inference for single actions and chunks into different components * fix: minor * fix: temporal ensembling * fix: moving populate queues out of modular component for batch preparation * fix: minor for CI * fix: smovla debug * fix: reward classifier, maybe the last policy lacking? --------- Signed-off-by: Francesco Capuano <74058581+fracapuano@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Simon Alibert <75076266+aliberts@users.noreply.github.com> Co-authored-by: Adil Zouitine <adilzouitinegm@gmail.com> Co-authored-by: imstevenpmwork <steven.palma@huggingface.co> Co-authored-by: Pepijn <138571049+pkooij@users.noreply.github.com> Co-authored-by: Caroline Pascal <caroline8.pascal@gmail.com> Co-authored-by: Mishig Davaadorj <dmishig@gmail.com> Co-authored-by: omahs <73983677+omahs@users.noreply.github.com> Co-authored-by: CharlesCNorton <135471798+CharlesCNorton@users.noreply.github.com> Co-authored-by: masato-ka <jp6uzv@gmail.com> Co-authored-by: Ragnar <rodiondenmark@gmail.com> Co-authored-by: mshukor <mustafa.shukor97@gmail.com> Co-authored-by: Simon Alibert <simon.alibert@huggingface.co> Co-authored-by: Steven Palma <imstevenpmwork@ieee.org> Co-authored-by: Dana Aubakirova <118912928+danaaubakirova@users.noreply.github.com> Co-authored-by: Remi <remi.cadene@huggingface.co> Co-authored-by: Ben Zhang <5977478+ben-z@users.noreply.github.com> Co-authored-by: Pepijn <pepijn@huggingface.co> Co-authored-by: Dhruva <51377003+utterwqlnut@users.noreply.github.com> Co-authored-by: Daisuke Sato <tiryoh@gmail.com> Co-authored-by: Sarunas Kalade <sarunas.kalade@amd.com> Co-authored-by: koenvanwijk <koenvanwijk@users.noreply.github.com> Co-authored-by: Yushun Xiang <73413365+YushunXiang@users.noreply.github.com> Co-authored-by: danaaubakirova <d.aubakirova@alumni.edu.kz> Co-authored-by: Quentin Gallouédec <45557362+qgallouedec@users.noreply.github.com> Co-authored-by: Michel Aractingi <michel.aractingi@huggingface.co> Co-authored-by: Eugene Mironov <helper2424@gmail.com> Co-authored-by: s1lent4gnt <kmeftah.khalil@gmail.com> Co-authored-by: Ke Wang <superwk1017@gmail.com> Co-authored-by: Yoel Chornton <yoel.chornton@gmail.com> Co-authored-by: Michel Aractingi <michel.aractingi@gmail.com> Co-authored-by: tidely <43219534+tidely@users.noreply.github.com> Co-authored-by: David <17435126+DavidLMS@users.noreply.github.com>
244 lines
9.6 KiB
Python
244 lines
9.6 KiB
Python
# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
import abc
|
|
import logging
|
|
import os
|
|
from importlib.resources import files
|
|
from pathlib import Path
|
|
from tempfile import TemporaryDirectory
|
|
from typing import List, Type, TypeVar
|
|
|
|
import packaging
|
|
import safetensors
|
|
from huggingface_hub import HfApi, ModelCard, ModelCardData, hf_hub_download
|
|
from huggingface_hub.constants import SAFETENSORS_SINGLE_FILE
|
|
from huggingface_hub.errors import HfHubHTTPError
|
|
from safetensors.torch import load_model as load_model_as_safetensor
|
|
from safetensors.torch import save_model as save_model_as_safetensor
|
|
from torch import Tensor, nn
|
|
|
|
from lerobot.common.utils.hub import HubMixin
|
|
from lerobot.configs.policies import PreTrainedConfig
|
|
from lerobot.configs.train import TrainPipelineConfig
|
|
|
|
T = TypeVar("T", bound="PreTrainedPolicy")
|
|
|
|
|
|
class PreTrainedPolicy(nn.Module, HubMixin, abc.ABC):
|
|
"""
|
|
Base class for policy models.
|
|
"""
|
|
|
|
config_class: None
|
|
name: None
|
|
|
|
def __init__(self, config: PreTrainedConfig, *inputs, **kwargs):
|
|
super().__init__()
|
|
if not isinstance(config, PreTrainedConfig):
|
|
raise ValueError(
|
|
f"Parameter config in `{self.__class__.__name__}(config)` should be an instance of class "
|
|
"`PreTrainedConfig`. To create a model from a pretrained model use "
|
|
f"`model = {self.__class__.__name__}.from_pretrained(PRETRAINED_MODEL_NAME)`"
|
|
)
|
|
self.config = config
|
|
|
|
def __init_subclass__(cls, **kwargs):
|
|
super().__init_subclass__(**kwargs)
|
|
if not getattr(cls, "config_class", None):
|
|
raise TypeError(f"Class {cls.__name__} must define 'config_class'")
|
|
if not getattr(cls, "name", None):
|
|
raise TypeError(f"Class {cls.__name__} must define 'name'")
|
|
|
|
def _save_pretrained(self, save_directory: Path) -> None:
|
|
self.config._save_pretrained(save_directory)
|
|
model_to_save = self.module if hasattr(self, "module") else self
|
|
save_model_as_safetensor(model_to_save, str(save_directory / SAFETENSORS_SINGLE_FILE))
|
|
|
|
@classmethod
|
|
def from_pretrained(
|
|
cls: Type[T],
|
|
pretrained_name_or_path: str | Path,
|
|
*,
|
|
config: PreTrainedConfig | None = None,
|
|
force_download: bool = False,
|
|
resume_download: bool | None = None,
|
|
proxies: dict | None = None,
|
|
token: str | bool | None = None,
|
|
cache_dir: str | Path | None = None,
|
|
local_files_only: bool = False,
|
|
revision: str | None = None,
|
|
strict: bool = False,
|
|
**kwargs,
|
|
) -> T:
|
|
"""
|
|
The policy is set in evaluation mode by default using `policy.eval()` (dropout modules are
|
|
deactivated). To train it, you should first set it back in training mode with `policy.train()`.
|
|
"""
|
|
if config is None:
|
|
config = PreTrainedConfig.from_pretrained(
|
|
pretrained_name_or_path=pretrained_name_or_path,
|
|
force_download=force_download,
|
|
resume_download=resume_download,
|
|
proxies=proxies,
|
|
token=token,
|
|
cache_dir=cache_dir,
|
|
local_files_only=local_files_only,
|
|
revision=revision,
|
|
**kwargs,
|
|
)
|
|
model_id = str(pretrained_name_or_path)
|
|
instance = cls(config, **kwargs)
|
|
if os.path.isdir(model_id):
|
|
print("Loading weights from local directory")
|
|
model_file = os.path.join(model_id, SAFETENSORS_SINGLE_FILE)
|
|
policy = cls._load_as_safetensor(instance, model_file, config.device, strict)
|
|
else:
|
|
try:
|
|
model_file = hf_hub_download(
|
|
repo_id=model_id,
|
|
filename=SAFETENSORS_SINGLE_FILE,
|
|
revision=revision,
|
|
cache_dir=cache_dir,
|
|
force_download=force_download,
|
|
proxies=proxies,
|
|
resume_download=resume_download,
|
|
token=token,
|
|
local_files_only=local_files_only,
|
|
)
|
|
policy = cls._load_as_safetensor(instance, model_file, config.device, strict)
|
|
except HfHubHTTPError as e:
|
|
raise FileNotFoundError(
|
|
f"{SAFETENSORS_SINGLE_FILE} not found on the HuggingFace Hub in {model_id}"
|
|
) from e
|
|
|
|
policy.to(config.device)
|
|
policy.eval()
|
|
return policy
|
|
|
|
@classmethod
|
|
def _load_as_safetensor(cls, model: T, model_file: str, map_location: str, strict: bool) -> T:
|
|
if packaging.version.parse(safetensors.__version__) < packaging.version.parse("0.4.3"):
|
|
load_model_as_safetensor(model, model_file, strict=strict)
|
|
if map_location != "cpu":
|
|
logging.warning(
|
|
"Loading model weights on other devices than 'cpu' is not supported natively in your version of safetensors."
|
|
" This means that the model is loaded on 'cpu' first and then copied to the device."
|
|
" This leads to a slower loading time."
|
|
" Please update safetensors to version 0.4.3 or above for improved performance."
|
|
)
|
|
model.to(map_location)
|
|
else:
|
|
safetensors.torch.load_model(model, model_file, strict=strict, device=map_location)
|
|
return model
|
|
|
|
@abc.abstractmethod
|
|
def get_optim_params(self) -> dict:
|
|
"""
|
|
Returns the policy-specific parameters dict to be passed on to the optimizer.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@abc.abstractmethod
|
|
def reset(self):
|
|
"""To be called whenever the environment is reset.
|
|
|
|
Does things like clearing caches.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
# TODO(aliberts, rcadene): split into 'forward' and 'compute_loss'?
|
|
@abc.abstractmethod
|
|
def forward(self, batch: dict[str, Tensor]) -> tuple[Tensor, dict | None]:
|
|
"""_summary_
|
|
|
|
Args:
|
|
batch (dict[str, Tensor]): _description_
|
|
|
|
Returns:
|
|
tuple[Tensor, dict | None]: The loss and potentially other information. Apart from the loss which
|
|
is a Tensor, all other items should be logging-friendly, native Python types.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@abc.abstractmethod
|
|
def predict_action_chunk(self, batch: dict[str, Tensor]) -> Tensor:
|
|
"""Returns the action chunk (for action chunking policies) for a given observation, potentially in batch mode.
|
|
|
|
Child classes using action chunking should use this method within `select_action` to form the action chunk
|
|
cached for selection.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@abc.abstractmethod
|
|
def select_action(self, batch: dict[str, Tensor]) -> Tensor:
|
|
"""Return one action to run in the environment (potentially in batch mode).
|
|
|
|
When the model uses a history of observations, or outputs a sequence of actions, this method deals
|
|
with caching.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def push_model_to_hub(
|
|
self,
|
|
cfg: TrainPipelineConfig,
|
|
):
|
|
api = HfApi()
|
|
repo_id = api.create_repo(
|
|
repo_id=self.config.repo_id, private=self.config.private, exist_ok=True
|
|
).repo_id
|
|
|
|
# Push the files to the repo in a single commit
|
|
with TemporaryDirectory(ignore_cleanup_errors=True) as tmp:
|
|
saved_path = Path(tmp) / repo_id
|
|
|
|
self.save_pretrained(saved_path) # Calls _save_pretrained and stores model tensors
|
|
|
|
card = self.generate_model_card(
|
|
cfg.dataset.repo_id, self.config.type, self.config.license, self.config.tags
|
|
)
|
|
card.save(str(saved_path / "README.md"))
|
|
|
|
cfg.save_pretrained(saved_path) # Calls _save_pretrained and stores train config
|
|
|
|
commit_info = api.upload_folder(
|
|
repo_id=repo_id,
|
|
repo_type="model",
|
|
folder_path=saved_path,
|
|
commit_message="Upload policy weights, train config and readme",
|
|
allow_patterns=["*.safetensors", "*.json", "*.yaml", "*.md"],
|
|
ignore_patterns=["*.tmp", "*.log"],
|
|
)
|
|
|
|
logging.info(f"Model pushed to {commit_info.repo_url.url}")
|
|
|
|
def generate_model_card(
|
|
self, dataset_repo_id: str, model_type: str, license: str | None, tags: List[str] | None
|
|
) -> ModelCard:
|
|
base_model = "lerobot/smolvla_base" if model_type == "smolvla" else None # Set a base model
|
|
|
|
card_data = ModelCardData(
|
|
license=license or "apache-2.0",
|
|
library_name="lerobot",
|
|
pipeline_tag="robotics",
|
|
tags=list(set(tags or []).union({"robotics", "lerobot", model_type})),
|
|
model_name=model_type,
|
|
datasets=dataset_repo_id,
|
|
base_model=base_model,
|
|
)
|
|
|
|
template_card = files("lerobot.templates").joinpath("lerobot_modelcard_template.md").read_text()
|
|
card = ModelCard.from_template(card_data, template_str=template_card)
|
|
card.validate()
|
|
return card
|