Njap-png
Add Agron neural-network zoo, optimisers, algorithms, schedulers and unified trainer
a311e94 Download agron_nets/training/cli.py from teddyoas/Agron: direct link, hf CLI and curl.
- Browser
- Download file 6.25 kB
-
https://huggingface.co/teddyoas/Agron/resolve/main/agron_nets/training/cli.py
- Command line
-
hf download hf://teddyoas/Agron/agron_nets/training/cli.py
-
curl -L -o cli.py https://huggingface.co/teddyoas/Agron/resolve/main/agron_nets/training/cli.py
6.25 kB
| """Shared command-line plumbing for the Agron training scripts. | |
| Both ``train_from_scratch.py`` and ``train_agron.py`` expose the same | |
| architecture / optimiser / algorithm / scheduler surface through | |
| :func:`add_training_args` and :func:`trainer_from_args`. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| from typing import Any, Dict, List, Optional, Tuple | |
| import torch | |
| from ..algorithms import algorithm_names | |
| from ..architectures import ARCHITECTURES, build_architecture | |
| from ..optimizers import optimizer_names | |
| from ..schedulers import scheduler_names | |
| from ..utils import count_parameters, format_parameters, read_json | |
| from .engine import AgronTrainer, TrainerConfig | |
| from .recipes import get_recipe, recipe_names | |
| def add_training_args(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: | |
| """Add the shared model/training flags to ``parser``.""" | |
| g = parser.add_argument_group("agron_nets") | |
| g.add_argument("--arch", choices=sorted(ARCHITECTURES), default=None, | |
| help="architecture from the Agron zoo (default: llama)") | |
| g.add_argument("--optimizer", choices=optimizer_names(), default=None, | |
| help="optimiser (default: from --recipe, else adamw)") | |
| g.add_argument("--scheduler", choices=scheduler_names() + ["none"], default=None, | |
| help="learning-rate schedule (default: from --recipe, else cosine)") | |
| g.add_argument("--algorithm", action="append", default=None, metavar="NAME", | |
| help=f"composable algorithm, repeatable (choices: {', '.join(algorithm_names())})") | |
| g.add_argument("--recipe", choices=recipe_names(), default=None, | |
| help="named optimiser+algorithm bundle (see agron_nets.training.RECIPES)") | |
| g.add_argument("--lr", type=float, default=None, help="peak learning rate") | |
| g.add_argument("--warmup", type=int, default=0, help="warmup steps") | |
| g.add_argument("--grad-accum", type=int, default=1, help="gradient accumulation micro-steps") | |
| g.add_argument("--grad-clip", type=float, default=1.0, help="global grad-norm clip (0 disables)") | |
| g.add_argument("--weight-decay", type=float, default=None) | |
| g.add_argument("--batch-size", type=int, default=8) | |
| g.add_argument("--log-every", type=int, default=25) | |
| g.add_argument("--save-every", type=int, default=0) | |
| g.add_argument("--device", default=None, help="cpu / cuda / cuda:1 / mps") | |
| g.add_argument("--seed", type=int, default=42) | |
| g.add_argument("--config-json", default=None, | |
| help="JSON string or path with architecture overrides") | |
| g.add_argument("--sophia-hessian-every", type=int, default=0, | |
| help="refresh Sophia's diagonal Hessian every N steps") | |
| return parser | |
| def arch_overrides(config_json: Optional[str]) -> Dict[str, Any]: | |
| if not config_json: | |
| return {} | |
| if os.path.exists(config_json): | |
| data = read_json(config_json, default={}) or {} | |
| else: | |
| data = json.loads(config_json) | |
| return {k: v for k, v in data.items() if k not in ("model_type", "architectures", "transformers_version")} | |
| def build_model_from_args( | |
| args: argparse.Namespace, | |
| preset: Optional[Dict[str, Any]] = None, | |
| model: Optional[torch.nn.Module] = None, | |
| ) -> Tuple[torch.nn.Module, Any]: | |
| """Build (or accept) the model described by ``args``.""" | |
| if model is not None: | |
| return model, model.config | |
| cfg = dict(preset or {}) | |
| cfg.update(arch_overrides(getattr(args, "config_json", None))) | |
| model, config = build_architecture(getattr(args, "arch", None) or "llama", **cfg) | |
| print(f"Architecture: {config.model_type} | {format_parameters(count_parameters(model))} params") | |
| return model, config | |
| def trainer_from_args( | |
| args: argparse.Namespace, | |
| model: torch.nn.Module, | |
| output_dir: Optional[str] = None, | |
| ) -> AgronTrainer: | |
| """Resolve recipe + explicit flags into a configured :class:`AgronTrainer`.""" | |
| recipe = get_recipe(args.recipe) if getattr(args, "recipe", None) else {} | |
| optimizer = args.optimizer or recipe.get("optimizer", "adamw") | |
| scheduler = args.scheduler or recipe.get("scheduler", "cosine") | |
| algorithms: List[Any] = list(recipe.get("algorithms", [])) | |
| extra = [] | |
| for item in getattr(args, "algorithm", None) or []: | |
| extra.extend(part.strip() for part in item.split(",") if part.strip()) | |
| for name in extra: | |
| if name not in algorithms: | |
| algorithms.append(name) | |
| if getattr(args, "grad_accum", 1) > 1 and "gradient_accumulation" not in algorithms: | |
| algorithms.append("gradient_accumulation") | |
| if getattr(args, "sophia_hessian_every", 0) > 0 and "sophia" != optimizer: | |
| print("warning: --sophia-hessian-every only applies to --optimizer sophia") | |
| config = TrainerConfig( | |
| max_steps=args.steps, | |
| epochs=getattr(args, "epochs", 1), | |
| warmup_steps=args.warmup, | |
| log_every=args.log_every, | |
| save_every=args.save_every, | |
| output_dir=output_dir or args.output, | |
| grad_clip=(args.grad_clip or None), | |
| seed=args.seed, | |
| weight_decay=args.weight_decay, | |
| sophia_hessian_every=args.sophia_hessian_every, | |
| ) | |
| optimizer_kwargs: Dict[str, Any] = {} | |
| if args.lr is not None: | |
| optimizer_kwargs["lr"] = args.lr | |
| trainer = AgronTrainer( | |
| model, | |
| optimizer=optimizer, | |
| scheduler=scheduler, | |
| algorithms=algorithms, | |
| config=config, | |
| device=args.device, | |
| optimizer_kwargs=optimizer_kwargs, | |
| ) | |
| # ``gradient_accumulation`` must know how many micro-steps to accumulate. | |
| if getattr(args, "grad_accum", 1) > 1: | |
| from ..algorithms.stability import GradientAccumulation | |
| for i, algo in enumerate(trainer.algorithms): | |
| if algo.name == "gradient_accumulation": | |
| trainer.algorithms[i] = GradientAccumulation(steps=args.grad_accum) | |
| print( | |
| f"Optimiser: {type(trainer.optimizer).__name__} | Scheduler: {scheduler} | " | |
| f"Algorithms: {', '.join(a.name for a in trainer.algorithms) or 'none'}" | |
| ) | |
| return trainer | |
| __all__ = [ | |
| "add_training_args", | |
| "arch_overrides", | |
| "build_model_from_args", | |
| "trainer_from_args", | |
| ] | |