sentinel / train.py
harry1911's picture
Sync from GitHub main
d0d9ca3 verified
Raw
History Blame Contribute Delete
6.02 kB
#!/usr/bin/env python
"""SENTINEL LLM training entry point."""
from __future__ import annotations
import argparse
import logging
import sys
import time
import warnings
from pathlib import Path
warnings.filterwarnings("ignore", category=RuntimeWarning)
warnings.filterwarnings("ignore", category=UserWarning)
import os
os.environ["TRANSFORMERS_VERBOSITY"] = "error"
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
datefmt="%H:%M:%S",
)
logger = logging.getLogger("sentinel.train")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Train or evaluate a SENTINEL LLM agent via GRPO",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"--agent", choices=["holmes", "forge", "argus", "hermes", "oracle"], default="holmes",
help="Which agent role to train.",
)
parser.add_argument(
"--model",
default="unsloth/Qwen2.5-7B-Instruct-bnb-4bit",
help="HuggingFace / Unsloth model name or local path.",
)
parser.add_argument(
"--load-in-4bit",
dest="load_in_4bit",
action="store_true",
default=True,
help="Load the base model in 4-bit quantized mode.",
)
parser.add_argument(
"--no-4bit",
dest="load_in_4bit",
action="store_false",
help="Disable 4-bit loading and use full-precision/bf16 weights instead.",
)
parser.add_argument("--episodes", type=int, default=200, help="Training episodes.")
parser.add_argument("--batch-size", type=int, default=4, help="Per-device GRPO batch size.")
parser.add_argument("--lora-r", type=int, default=16, help="LoRA rank.")
parser.add_argument("--lora-alpha", type=int, default=32, help="LoRA alpha.")
parser.add_argument("--checkpoint-dir", default="checkpoints", help="Checkpoint directory.")
parser.add_argument("--log-file", default="training_log.jsonl", help="Episode metrics JSONL.")
parser.add_argument("--env-spec", default="env_spec.yaml", help="Path to env_spec.yaml.")
parser.add_argument("--resume", action="store_true", help="Resume from latest checkpoint.")
parser.add_argument("--eval-only", action="store_true", help="Skip training and run evaluation.")
parser.add_argument("--eval-episodes", type=int, default=20, help="Episodes per tier in evaluation.")
parser.add_argument("--seed", type=int, default=42, help="Random seed.")
return parser.parse_args()
def main() -> int:
args = parse_args()
logger.info("Importing SENTINEL modules …")
from sentinel.env import Sentinel_Env
from sentinel.training.evaluate import print_eval_report, run_evaluation
from sentinel.training.pipeline import (
TrainingConfig,
build_grpo_trainer,
load_latest_checkpoint,
run_training_loop,
)
logger.info("Initialising environment from %s …", args.env_spec)
env = Sentinel_Env(config_path=args.env_spec)
reward_fn = env.reward_function
config = TrainingConfig(
agent=args.agent,
model_name=args.model,
load_in_4bit=args.load_in_4bit,
batch_size=args.batch_size,
max_steps=args.episodes,
lora_r=args.lora_r,
lora_alpha=args.lora_alpha,
checkpoint_dir=str(Path(args.checkpoint_dir) / args.agent),
log_file=args.log_file,
)
logger.info("=" * 54)
logger.info(" SENTINEL Mode : LLM (GRPO)")
logger.info(" Agent : %s", args.agent)
logger.info(" Model : %s", args.model)
logger.info(" 4-bit : %s", "on" if args.load_in_4bit else "off")
logger.info("=" * 54)
trainer, llm_agent = build_grpo_trainer(
agent=args.agent,
env=env,
config=config,
)
start_episode = 0
if args.resume:
ckpt = load_latest_checkpoint(config.checkpoint_dir)
if ckpt:
start_episode = ckpt.get("episode", 0) + 1
logger.info("Resuming from episode %d …", start_episode)
else:
logger.info("No checkpoint found in %s; starting from episode 0.", config.checkpoint_dir)
if args.eval_only:
logger.info("Eval-only mode: running %d episodes per tier …", args.eval_episodes)
results = run_evaluation(
env,
reward_fn,
llm_agent=llm_agent,
episodes_per_tier=args.eval_episodes,
seed=args.seed,
)
print_eval_report(results)
return 0
logger.info(
"Starting training | agent=%s | mode=LLM (GRPO) | episodes=%d | start=%d",
args.agent, args.episodes, start_episode,
)
t0 = time.perf_counter()
all_metrics = run_training_loop(
trainer=trainer,
env=env,
config=config,
reward_fn=reward_fn,
start_episode=start_episode,
llm_agent=llm_agent,
)
elapsed = time.perf_counter() - t0
logger.info(
"Training complete: %d episodes in %.1f s (%.2f s/ep)",
len(all_metrics), elapsed, elapsed / max(len(all_metrics), 1),
)
if all_metrics:
last_10 = all_metrics[-10:]
avg_reward = sum(m.total_reward for m in last_10) / len(last_10)
avg_mttr = sum(m.mttr for m in last_10) / len(last_10)
logger.info(
"Last 10 episodes | avg_reward=%.3f | avg_MTTR=%.1f",
avg_reward, avg_mttr,
)
logger.info("Running post-training evaluation (%d eps/tier) …", args.eval_episodes)
results = run_evaluation(
env,
reward_fn,
llm_agent=llm_agent,
episodes_per_tier=args.eval_episodes,
seed=args.seed,
)
print_eval_report(results)
return 0
if __name__ == "__main__":
sys.exit(main())