mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-24 02:06:15 +00:00
feat(eval): include eval config (policy, env, eval settings) in Hub push
Saves eval_config.json locally and uploads it alongside results. The model card now includes a collapsible "Eval configuration" section showing the full config JSON used for the evaluation run. Made-with: Cursor
This commit is contained in:
@@ -503,19 +503,34 @@ def _compile_episode_data(
|
|||||||
return data_dict
|
return data_dict
|
||||||
|
|
||||||
|
|
||||||
|
def _serializable_config(obj: Any) -> Any:
|
||||||
|
"""Recursively convert a config dict so it is JSON-serializable."""
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
return {k: _serializable_config(v) for k, v in obj.items()}
|
||||||
|
if isinstance(obj, (list, tuple)):
|
||||||
|
return [_serializable_config(v) for v in obj]
|
||||||
|
if isinstance(obj, Path):
|
||||||
|
return str(obj)
|
||||||
|
if isinstance(obj, (int, float, str, bool, type(None))):
|
||||||
|
return obj
|
||||||
|
return str(obj)
|
||||||
|
|
||||||
|
|
||||||
def push_eval_to_hub(
|
def push_eval_to_hub(
|
||||||
repo_id: str,
|
repo_id: str,
|
||||||
output_dir: Path,
|
output_dir: Path,
|
||||||
info: dict,
|
info: dict,
|
||||||
env_type: str,
|
env_type: str,
|
||||||
|
eval_config: dict | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Upload eval results, videos, and an updated model card to the Hub.
|
"""Upload eval results, videos, config, and an updated model card to the Hub.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
repo_id: HF model repo (e.g. "user/my_policy").
|
repo_id: HF model repo (e.g. "user/my_policy").
|
||||||
output_dir: Local directory containing eval_info.json and videos/.
|
output_dir: Local directory containing eval_info.json and videos/.
|
||||||
info: The eval results dict (as returned by eval_policy_all).
|
info: The eval results dict (as returned by eval_policy_all).
|
||||||
env_type: Environment type string (e.g. "libero_plus", "pusht").
|
env_type: Environment type string (e.g. "libero_plus", "pusht").
|
||||||
|
eval_config: Serialized EvalPipelineConfig dict (policy + env + eval settings).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
URL of the last Hub commit.
|
URL of the last Hub commit.
|
||||||
@@ -525,9 +540,10 @@ def push_eval_to_hub(
|
|||||||
api = HfApi()
|
api = HfApi()
|
||||||
api.create_repo(repo_id=repo_id, exist_ok=True)
|
api.create_repo(repo_id=repo_id, exist_ok=True)
|
||||||
|
|
||||||
|
commit_url = ""
|
||||||
|
|
||||||
# 1. Upload eval_info.json
|
# 1. Upload eval_info.json
|
||||||
eval_json_path = output_dir / "eval_info.json"
|
eval_json_path = output_dir / "eval_info.json"
|
||||||
commit_url = ""
|
|
||||||
if eval_json_path.exists():
|
if eval_json_path.exists():
|
||||||
commit_url = api.upload_file(
|
commit_url = api.upload_file(
|
||||||
path_or_fileobj=str(eval_json_path),
|
path_or_fileobj=str(eval_json_path),
|
||||||
@@ -536,7 +552,17 @@ def push_eval_to_hub(
|
|||||||
commit_message=f"Upload eval results for {env_type}",
|
commit_message=f"Upload eval results for {env_type}",
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Upload rollout videos
|
# 2. Upload eval_config.json (policy, env, and eval settings used)
|
||||||
|
eval_config_path = output_dir / "eval_config.json"
|
||||||
|
if eval_config_path.exists():
|
||||||
|
api.upload_file(
|
||||||
|
path_or_fileobj=str(eval_config_path),
|
||||||
|
path_in_repo=f"eval/{env_type}/eval_config.json",
|
||||||
|
repo_id=repo_id,
|
||||||
|
commit_message=f"Upload eval config for {env_type}",
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. Upload rollout videos
|
||||||
videos_dir = output_dir / "videos"
|
videos_dir = output_dir / "videos"
|
||||||
if videos_dir.is_dir():
|
if videos_dir.is_dir():
|
||||||
api.upload_folder(
|
api.upload_folder(
|
||||||
@@ -546,15 +572,15 @@ def push_eval_to_hub(
|
|||||||
commit_message=f"Upload eval rollout videos for {env_type}",
|
commit_message=f"Upload eval rollout videos for {env_type}",
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. Update the model card with an eval results table
|
# 4. Update the model card with an eval results table and config summary
|
||||||
_update_model_card_with_eval(api, repo_id, info, env_type)
|
_update_model_card_with_eval(api, repo_id, info, env_type, eval_config=eval_config)
|
||||||
|
|
||||||
logging.info(f"Eval results pushed to https://huggingface.co/{repo_id}")
|
logging.info(f"Eval results pushed to https://huggingface.co/{repo_id}")
|
||||||
return commit_url
|
return commit_url
|
||||||
|
|
||||||
|
|
||||||
def _format_eval_table(info: dict, env_type: str) -> str:
|
def _format_eval_table(info: dict, env_type: str, eval_config: dict | None = None) -> str:
|
||||||
"""Build a markdown table from eval results."""
|
"""Build a markdown table from eval results, optionally including config."""
|
||||||
lines = [
|
lines = [
|
||||||
f"### Evaluation: `{env_type}`\n",
|
f"### Evaluation: `{env_type}`\n",
|
||||||
"| Suite | Success Rate (%) | Avg Sum Reward | Episodes |",
|
"| Suite | Success Rate (%) | Avg Sum Reward | Episodes |",
|
||||||
@@ -575,6 +601,14 @@ def _format_eval_table(info: dict, env_type: str) -> str:
|
|||||||
n_ep = overall.get("n_episodes", 0)
|
n_ep = overall.get("n_episodes", 0)
|
||||||
lines.append(f"| **Overall** | **{sr:.1f}** | **{reward:.2f}** | **{n_ep}** |")
|
lines.append(f"| **Overall** | **{sr:.1f}** | **{reward:.2f}** | **{n_ep}** |")
|
||||||
|
|
||||||
|
if eval_config:
|
||||||
|
lines.append("")
|
||||||
|
lines.append("<details><summary>Eval configuration</summary>\n")
|
||||||
|
lines.append("```json")
|
||||||
|
lines.append(json.dumps(eval_config, indent=2))
|
||||||
|
lines.append("```\n")
|
||||||
|
lines.append("</details>")
|
||||||
|
|
||||||
video_paths = overall.get("video_paths", [])
|
video_paths = overall.get("video_paths", [])
|
||||||
if video_paths:
|
if video_paths:
|
||||||
lines.append("")
|
lines.append("")
|
||||||
@@ -589,7 +623,9 @@ def _format_eval_table(info: dict, env_type: str) -> str:
|
|||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
def _update_model_card_with_eval(api: Any, repo_id: str, info: dict, env_type: str) -> None:
|
def _update_model_card_with_eval(
|
||||||
|
api: Any, repo_id: str, info: dict, env_type: str, eval_config: dict | None = None
|
||||||
|
) -> None:
|
||||||
"""Append or replace the eval section in the model card README."""
|
"""Append or replace the eval section in the model card README."""
|
||||||
from huggingface_hub import ModelCard
|
from huggingface_hub import ModelCard
|
||||||
|
|
||||||
@@ -600,7 +636,7 @@ def _update_model_card_with_eval(api: Any, repo_id: str, info: dict, env_type: s
|
|||||||
|
|
||||||
content = card.content or ""
|
content = card.content or ""
|
||||||
|
|
||||||
eval_table = _format_eval_table(info, env_type)
|
eval_table = _format_eval_table(info, env_type, eval_config=eval_config)
|
||||||
|
|
||||||
section_marker_start = f"<!-- eval-results-{env_type}-start -->"
|
section_marker_start = f"<!-- eval-results-{env_type}-start -->"
|
||||||
section_marker_end = f"<!-- eval-results-{env_type}-end -->"
|
section_marker_end = f"<!-- eval-results-{env_type}-end -->"
|
||||||
@@ -699,6 +735,10 @@ def eval_main(cfg: EvalPipelineConfig):
|
|||||||
with open(output_dir / "eval_info.json", "w") as f:
|
with open(output_dir / "eval_info.json", "w") as f:
|
||||||
json.dump(info, f, indent=2)
|
json.dump(info, f, indent=2)
|
||||||
|
|
||||||
|
eval_cfg_dict = _serializable_config(asdict(cfg))
|
||||||
|
with open(output_dir / "eval_config.json", "w") as f:
|
||||||
|
json.dump(eval_cfg_dict, f, indent=2)
|
||||||
|
|
||||||
if cfg.push_to_hub:
|
if cfg.push_to_hub:
|
||||||
repo_id = str(cfg.policy.pretrained_path)
|
repo_id = str(cfg.policy.pretrained_path)
|
||||||
push_eval_to_hub(
|
push_eval_to_hub(
|
||||||
@@ -706,6 +746,7 @@ def eval_main(cfg: EvalPipelineConfig):
|
|||||||
output_dir=output_dir,
|
output_dir=output_dir,
|
||||||
info=info,
|
info=info,
|
||||||
env_type=cfg.env.type,
|
env_type=cfg.env.type,
|
||||||
|
eval_config=eval_cfg_dict,
|
||||||
)
|
)
|
||||||
|
|
||||||
logging.info("End of eval")
|
logging.info("End of eval")
|
||||||
|
|||||||
Reference in New Issue
Block a user