feat: abstracted checkpoint eval configs
This commit is contained in:
parent
67c459ca94
commit
b91908bf7a
8 changed files with 55 additions and 30 deletions
8
configs/evaluation/default.yaml
Normal file
8
configs/evaluation/default.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default Evaluation Configuration
|
||||||
|
# Settings used for checkpoint evaluation during training.
|
||||||
|
|
||||||
|
evaluate_checkpoints: false
|
||||||
|
# Max number of control steps during evaluation rollout.
|
||||||
|
eval_max_steps: 5000
|
||||||
|
# Seed for deterministic evaluation reset.
|
||||||
|
eval_seed: 0
|
||||||
|
|
@ -11,6 +11,3 @@ checkpoint_frequency: 100
|
||||||
upload_final_model: false
|
upload_final_model: false
|
||||||
upload_checkpoints: false
|
upload_checkpoints: false
|
||||||
hf_entity: ""
|
hf_entity: ""
|
||||||
evaluate_checkpoints: false
|
|
||||||
eval_max_steps: 5000
|
|
||||||
eval_seed: 0
|
|
||||||
|
|
@ -6,6 +6,7 @@ defaults:
|
||||||
- brittle_star_config
|
- brittle_star_config
|
||||||
- experiment: base
|
- experiment: base
|
||||||
- logging: default
|
- logging: default
|
||||||
|
- evaluation: default
|
||||||
- ppo: default
|
- ppo: default
|
||||||
- architecture: centralized
|
- architecture: centralized
|
||||||
- morphology: 5_arms_full
|
- morphology: 5_arms_full
|
||||||
|
|
|
||||||
24
src/brittle_star_project/configs/config_evaluation.py
Normal file
24
src/brittle_star_project/configs/config_evaluation.py
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EvaluationConfig:
|
||||||
|
"""Evaluation settings.
|
||||||
|
|
||||||
|
Currently used for synchronous checkpoint evaluation during training.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# When enabled, each saved checkpoint is evaluated headlessly and the results
|
||||||
|
# are appended to a CSV in the run's metrics/ folder.
|
||||||
|
evaluate_checkpoints: bool = False
|
||||||
|
eval_max_steps: int = 5000
|
||||||
|
eval_seed: int = 0
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
if self.evaluate_checkpoints and self.eval_max_steps <= 0:
|
||||||
|
raise ValueError(
|
||||||
|
"Configuration Error: 'eval_max_steps' must be > 0 when "
|
||||||
|
"'evaluate_checkpoints' is enabled."
|
||||||
|
)
|
||||||
|
|
@ -2,6 +2,7 @@ from dataclasses import dataclass, field
|
||||||
|
|
||||||
from experiment_logger.config_logger import LoggingConfig
|
from experiment_logger.config_logger import LoggingConfig
|
||||||
from brittle_star_project.configs.config_experiment import ExperimentConfig
|
from brittle_star_project.configs.config_experiment import ExperimentConfig
|
||||||
|
from brittle_star_project.configs.config_evaluation import EvaluationConfig
|
||||||
from brittle_star_project.configs.config_ppo import PPOConfig
|
from brittle_star_project.configs.config_ppo import PPOConfig
|
||||||
from brittle_star_project.configs.config_architecture import ArchitectureConfig
|
from brittle_star_project.configs.config_architecture import ArchitectureConfig
|
||||||
from brittle_star_project.configs.config_simulation import SimulationSettings
|
from brittle_star_project.configs.config_simulation import SimulationSettings
|
||||||
|
|
@ -23,6 +24,7 @@ class BrittleStarConfig:
|
||||||
|
|
||||||
experiment: ExperimentConfig = field(default_factory=ExperimentConfig)
|
experiment: ExperimentConfig = field(default_factory=ExperimentConfig)
|
||||||
logging: LoggingConfig = field(default_factory=LoggingConfig)
|
logging: LoggingConfig = field(default_factory=LoggingConfig)
|
||||||
|
evaluation: EvaluationConfig = field(default_factory=EvaluationConfig)
|
||||||
ppo: PPOConfig = field(default_factory=PPOConfig)
|
ppo: PPOConfig = field(default_factory=PPOConfig)
|
||||||
# This field is polymorphic; defaults to the base class to allow subclasses
|
# This field is polymorphic; defaults to the base class to allow subclasses
|
||||||
# (CentralizedConfig, DecentralizedConfig) to be merged in via Hydra.
|
# (CentralizedConfig, DecentralizedConfig) to be merged in via Hydra.
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ from hydra.core.config_store import ConfigStore
|
||||||
|
|
||||||
from experiment_logger.config_logger import LoggingConfig
|
from experiment_logger.config_logger import LoggingConfig
|
||||||
from brittle_star_project.configs.config_experiment import ExperimentConfig
|
from brittle_star_project.configs.config_experiment import ExperimentConfig
|
||||||
|
from brittle_star_project.configs.config_evaluation import EvaluationConfig
|
||||||
from brittle_star_project.configs.config_ppo import PPOConfig
|
from brittle_star_project.configs.config_ppo import PPOConfig
|
||||||
from brittle_star_project.configs.config_architecture import (
|
from brittle_star_project.configs.config_architecture import (
|
||||||
CentralizedConfig,
|
CentralizedConfig,
|
||||||
|
|
@ -32,6 +33,7 @@ def register_configs() -> None:
|
||||||
# Sub-config groups — each group corresponds to a configs/ subdirectory.
|
# Sub-config groups — each group corresponds to a configs/ subdirectory.
|
||||||
cs.store(group="experiment", name="base_experiment", node=ExperimentConfig)
|
cs.store(group="experiment", name="base_experiment", node=ExperimentConfig)
|
||||||
cs.store(group="logging", name="base_logging", node=LoggingConfig)
|
cs.store(group="logging", name="base_logging", node=LoggingConfig)
|
||||||
|
cs.store(group="evaluation", name="base_evaluation", node=EvaluationConfig)
|
||||||
cs.store(group="ppo", name="base_ppo", node=PPOConfig)
|
cs.store(group="ppo", name="base_ppo", node=PPOConfig)
|
||||||
|
|
||||||
# Architecture variants — swap via CLI: architecture=decentralized
|
# Architecture variants — swap via CLI: architecture=decentralized
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,7 @@ class PPOTrainer:
|
||||||
self.ppo = cfg.ppo
|
self.ppo = cfg.ppo
|
||||||
self.experiment = cfg.experiment
|
self.experiment = cfg.experiment
|
||||||
self.logging_cfg = cfg.logging
|
self.logging_cfg = cfg.logging
|
||||||
|
self.evaluation_cfg = cfg.evaluation
|
||||||
self.env = env
|
self.env = env
|
||||||
self.run_dir = run_dir
|
self.run_dir = run_dir
|
||||||
self.run_name = run_name
|
self.run_name = run_name
|
||||||
|
|
@ -710,11 +711,22 @@ class PPOTrainer:
|
||||||
return csv_path
|
return csv_path
|
||||||
|
|
||||||
def _evaluate_checkpoint(self, iteration: int, *, trained_timesteps: int) -> None:
|
def _evaluate_checkpoint(self, iteration: int, *, trained_timesteps: int) -> None:
|
||||||
if not self.logging_cfg.evaluate_checkpoints:
|
if not self.evaluation_cfg.evaluate_checkpoints:
|
||||||
return
|
return
|
||||||
|
|
||||||
max_steps = int(self.logging_cfg.eval_max_steps)
|
max_steps = int(self.evaluation_cfg.eval_max_steps)
|
||||||
seed = int(self.logging_cfg.eval_seed)
|
seed = int(self.evaluation_cfg.eval_seed)
|
||||||
|
|
||||||
|
if max_steps <= 0:
|
||||||
|
self.logger.warning("[EVAL]: eval_max_steps must be > 0; skipping evaluation")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.logging_cfg.save_checkpoints or self.logging_cfg.checkpoint_frequency <= 0:
|
||||||
|
self.logger.warning(
|
||||||
|
"[EVAL]: evaluate_checkpoints is enabled but checkpoint saving is disabled; "
|
||||||
|
"skipping evaluation"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
# Run evaluation best-effort; never fail training because evaluation failed.
|
# Run evaluation best-effort; never fail training because evaluation failed.
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,6 @@ class LoggingConfig:
|
||||||
upload_final_model: bool = False
|
upload_final_model: bool = False
|
||||||
upload_checkpoints: bool = False
|
upload_checkpoints: bool = False
|
||||||
|
|
||||||
# Checkpoint evaluation (synchronous, in-process)
|
|
||||||
# When enabled, each saved checkpoint is evaluated headlessly and the results
|
|
||||||
# are appended to a CSV in the run's metrics/ folder.
|
|
||||||
evaluate_checkpoints: bool = False
|
|
||||||
eval_max_steps: int = 5000
|
|
||||||
eval_seed: int = 0
|
|
||||||
|
|
||||||
hf_entity: str = ""
|
hf_entity: str = ""
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
|
|
@ -39,19 +32,5 @@ class LoggingConfig:
|
||||||
"both 'track' and 'save_checkpoints' to also be True."
|
"both 'track' and 'save_checkpoints' to also be True."
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.evaluate_checkpoints:
|
# NOTE: Checkpoint evaluation settings live under the project's
|
||||||
if not self.save_checkpoints:
|
# `evaluation` config group (see brittle_star_project.configs).
|
||||||
raise ValueError(
|
|
||||||
"Configuration Error: 'evaluate_checkpoints' is True, but it requires "
|
|
||||||
"'save_checkpoints' to also be True."
|
|
||||||
)
|
|
||||||
if self.checkpoint_frequency <= 0:
|
|
||||||
raise ValueError(
|
|
||||||
"Configuration Error: 'evaluate_checkpoints' is True, but it requires "
|
|
||||||
"'checkpoint_frequency' to be > 0."
|
|
||||||
)
|
|
||||||
if self.eval_max_steps <= 0:
|
|
||||||
raise ValueError(
|
|
||||||
"Configuration Error: 'eval_max_steps' must be > 0 when "
|
|
||||||
"'evaluate_checkpoints' is enabled."
|
|
||||||
)
|
|
||||||
|
|
|
||||||
Reference in a new issue