Initial commit - AHAD QUANT v1
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
AHAD QUANT — Entraînement Unifié ML + RL
|
||||
=============================================
|
||||
UN seul point d'entrée pour entraîner tout le système. Plus aucun bouton
|
||||
séparé "Train ML" / "Fine-tune RL" côté interface : ce script orchestre les
|
||||
deux étapes comme UN SEUL système qui se met à jour ensemble, puis sauvegarde
|
||||
un bundle unique (ahad_quant_unified.zip) cohérent à la fin.
|
||||
|
||||
Étapes (toujours dans cet ordre — le RL a besoin de l'ensemble ML à jour
|
||||
pour calculer son observation/reward) :
|
||||
1. ML : train.py (ré-entraîne l'ensemble LightGBM+XGBoost+RF[+DL])
|
||||
2. RL : rl_train.py (fine-tune le PPO existant sur le nouvel ensemble,
|
||||
ou entraîne depuis zéro si aucun agent n'existe encore)
|
||||
3. Export : export_unified.py (combine PPO + ensemble + scaler dans
|
||||
ahad_quant_unified.zip — la SEULE sauvegarde qui compte du
|
||||
point de vue utilisateur)
|
||||
|
||||
Chaque étape garde son propre mécanisme accept/reject (le nouveau modèle ne
|
||||
remplace l'ancien que s'il est meilleur — déjà géré dans train.py/rl_train.py),
|
||||
donc cette orchestration ne change AUCUNE calibration existante : elle se
|
||||
contente de les enchaîner et de les rendre visibles comme un seul flux.
|
||||
|
||||
Usage :
|
||||
python train_unified.py # ML complet + RL fine-tune (200k steps)
|
||||
python train_unified.py --rl-steps 500000 # RL plus long
|
||||
python train_unified.py --rl-full # RL entraîné depuis zéro (pas fine-tune)
|
||||
python train_unified.py --ml-only # ML seul (cas avancé, déconseillé)
|
||||
python train_unified.py --rl-only # RL seul (cas avancé, déconseillé)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def _run_step(label: str, cmd: list[str]) -> bool:
|
||||
"""Lance une étape en sous-processus, stream le stdout en direct, et
|
||||
renvoie True si le code de sortie est 0 (succès)."""
|
||||
print("\n" + "═" * 65)
|
||||
print(f" {label}")
|
||||
print("═" * 65, flush=True)
|
||||
proc = subprocess.run(cmd, cwd=BASE_DIR)
|
||||
ok = proc.returncode == 0
|
||||
status = "✅ OK" if ok else f"❌ ÉCHEC (code {proc.returncode})"
|
||||
print(f"\n [{label}] {status}", flush=True)
|
||||
return ok
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="AHAD QUANT — Entraînement Unifié ML + RL (un seul système)"
|
||||
)
|
||||
parser.add_argument("--rl-steps", type=int, default=200_000,
|
||||
help="Steps RL pour le fine-tuning (défaut: 200 000)")
|
||||
parser.add_argument("--rl-full", action="store_true",
|
||||
help="Entraîner le RL depuis zéro (au lieu de fine-tuner l'existant)")
|
||||
parser.add_argument("--ml-only", action="store_true",
|
||||
help="[Avancé] N'entraîner QUE le ML — déconseillé, casse l'unification")
|
||||
parser.add_argument("--rl-only", action="store_true",
|
||||
help="[Avancé] N'entraîner QUE le RL — déconseillé, casse l'unification")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.ml_only and args.rl_only:
|
||||
print("❌ --ml-only et --rl-only sont mutuellement exclusifs.")
|
||||
sys.exit(1)
|
||||
|
||||
t0 = time.time()
|
||||
print("┌" + "─" * 63 + "┐")
|
||||
print("│ AHAD QUANT — ENTRAÎNEMENT UNIFIÉ (ML + RL = UN SEUL SYSTÈME) │"[:65].ljust(64) + "│")
|
||||
print("└" + "─" * 63 + "┘")
|
||||
|
||||
ml_ok = True
|
||||
rl_ok = True
|
||||
|
||||
# ── Étape 1 : ML ──────────────────────────────────────────────────────
|
||||
if not args.rl_only:
|
||||
ml_ok = _run_step(
|
||||
"ÉTAPE 1/3 — Entraînement ML (ensemble LightGBM+XGBoost+RF[+DL])",
|
||||
[sys.executable, "-u", os.path.join(BASE_DIR, "train.py")],
|
||||
)
|
||||
if not ml_ok:
|
||||
print("\n⚠️ ML en échec — le RL serait entraîné sur un ensemble obsolète.")
|
||||
print(" Arrêt ici pour ne pas désynchroniser ML et RL.")
|
||||
sys.exit(1)
|
||||
|
||||
# ── Étape 2 : RL ──────────────────────────────────────────────────────
|
||||
if not args.ml_only:
|
||||
rl_agent_exists = os.path.exists(os.path.join(BASE_DIR, "rl_agent.zip"))
|
||||
if args.rl_full or not rl_agent_exists:
|
||||
rl_cmd = [sys.executable, "-u", os.path.join(BASE_DIR, "rl_train.py"),
|
||||
"--steps", str(args.rl_steps)]
|
||||
rl_label = "ÉTAPE 2/3 — Entraînement RL PPO (depuis zéro, sur le nouvel ensemble ML)"
|
||||
else:
|
||||
rl_cmd = [sys.executable, "-u", os.path.join(BASE_DIR, "rl_train.py"),
|
||||
"--finetune", "--steps", str(args.rl_steps)]
|
||||
rl_label = "ÉTAPE 2/3 — Fine-tuning RL PPO (sur le nouvel ensemble ML)"
|
||||
rl_ok = _run_step(rl_label, rl_cmd)
|
||||
if not rl_ok:
|
||||
print("\n⚠️ RL en échec — le bundle unifié ne sera PAS régénéré "
|
||||
"(pour éviter d'exporter un PPO obsolète avec un ML à jour).")
|
||||
|
||||
# ── Étape 3 : Export unifié (sauvegarde UNIQUE) ──────────────────────
|
||||
export_ok = False
|
||||
if ml_ok and rl_ok:
|
||||
export_ok = _run_step(
|
||||
"ÉTAPE 3/3 — Sauvegarde unifiée (ahad_quant_unified.zip = ML + RL + scaler)",
|
||||
[sys.executable, "-u", os.path.join(BASE_DIR, "export_unified.py")],
|
||||
)
|
||||
else:
|
||||
print("\n[ÉTAPE 3/3] Sautée — une étape précédente a échoué, "
|
||||
"pas de sauvegarde unifiée pour éviter un bundle incohérent.")
|
||||
|
||||
elapsed = time.time() - t0
|
||||
print("\n" + "═" * 65)
|
||||
print(f" TERMINÉ en {elapsed/60:.1f} min")
|
||||
print(f" ML : {'✅' if ml_ok else '❌'} RL : {'✅' if rl_ok else '❌'} "
|
||||
f"Sauvegarde unifiée : {'✅' if export_ok else '❌'}")
|
||||
print("═" * 65)
|
||||
|
||||
sys.exit(0 if (ml_ok and rl_ok) else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user