From fcfa3966ceab882ae08c9036f8cb3a06e92e5524 Mon Sep 17 00:00:00 2001 From: "Alejandro Estana (CALMIP)" Date: Wed, 3 Dec 2025 09:45:20 +0100 Subject: [PATCH] Optuna IA Turpan and HPO folder --- .../turpan/ressources_ia/HPO/optuna.md | 331 ++++++++++++++++++ .../turpan/ressources_ia/HPO/ray_tune.md | 290 +++++++++++++++ docs/arch_exp/turpan/ressources_ia/index.md | 8 +- .../arch_exp/turpan/ressources_ia/ray_tune.md | 306 ---------------- 4 files changed, 628 insertions(+), 307 deletions(-) create mode 100644 docs/arch_exp/turpan/ressources_ia/HPO/optuna.md create mode 100644 docs/arch_exp/turpan/ressources_ia/HPO/ray_tune.md delete mode 100644 docs/arch_exp/turpan/ressources_ia/ray_tune.md diff --git a/docs/arch_exp/turpan/ressources_ia/HPO/optuna.md b/docs/arch_exp/turpan/ressources_ia/HPO/optuna.md new file mode 100644 index 00000000..0a30a3e5 --- /dev/null +++ b/docs/arch_exp/turpan/ressources_ia/HPO/optuna.md @@ -0,0 +1,331 @@ +# Optuna + +Optuna est une bibliothèque d’optimisation d’hyperparamètres moderne, efficace et très flexible. Elle permet d’explorer automatiquement un espace d’hyperparamètres, d’utiliser des algorithmes avancés comme TPE, CMA-ES ou la recherche par grille, et d’exécuter de nombreux essais (« trials ») en parallèle. +Contrairement à Ray Tune, qui fonctionne bien sur un seul nœud, **Optuna supporte naturellement les environnements multi‑nœuds sur Turpan**, via une base de données partagée SQLite que chaque worker met à jour. Cela permet de lancer des centaines de trials en parallèle sur plusieurs GPU et plusieurs nœuds. + +Ce document explique comment utiliser Optuna sur Turpan : +- [copier les fichiers d’exemple](#copier-lexemple-optuna) +- [Structure du répertoire](#structure-du-répertoire) +- [installer Optuna dans le conteneur](#installer-optuna-dans-le-conteneur-turpan) +- [fonctionnement d’Optuna sur Turpan (multi‑nœuds)](#fonctionnement-doptuna-sur-turpan-multinœuds) +- [écrire votre fonction `objective(trial)` dynamique](#la-fonction-objectivetrial--cœur-doptuna) +- [Script SBATCH (multi‑nœuds, multi‑GPU)](#script-sbatch-multinœuds-multigpu) +- [récupérer le meilleur modèle](#récupérer-le-meilleur-modèle) +- [Algorithmes d'optimisation avancés dans Optuna](#algorithmes-doptimisation-avancés-dans-optuna) +- [Visualisations Optuna](#visualisations-optuna) +- [Adapter l’exemple à des modèles plus grands (LLM)](#adapter-lexemple-à-des-modèles-plus-grands-llm) +- [Lancer le job](#lancer-le-job) +- [Scalabilité attendue sur Turpan](#scalabilité-attendue-sur-turpan) + +Toute la documentation est adaptée exactement à l’exemple que nous avons mis en place pour Turpan. + +--- + +## Copier l'exemple Optuna + +Les scripts d’exemple Optuna se trouvent dans : + +``` +/work/shares/IA-Tests/optuna.tar.gz +``` + +Extraire dans votre espace : + +```bash +tar xvf /work/shares/IA-Tests/optuna.tar.gz +``` + +Le répertoire suivant sera créé : + +``` +optuna/ +├── code_and_slurm-scripts +│ ├── optuna_mnist.py +│ ├── check_best_model.py +│ ├── run-optuna-multinode_Turpan.sh +│ ├── study.db (fichier SQLite partagé) +├── data/ +│ └── MNIST/ (dataset MNIST) +└── README.txt +``` + +--- + +## Structure du répertoire + +| Fichier | Rôle | +|--------|------| +| `optuna_mnist.py` | Script principal Optuna (fonction objective + entraînement MNIST) | +| `check_best_model.py` | Lit le meilleur modèle depuis la base de données | +| `run-optuna-multinode_Turpan.sh` | Script SLURM multi‑nœuds utilisant apptainer | +| `study.db` | Base SQLite contenant les résultats (modifiable) | +| `data/MNIST` | Dataset téléchargé automatiquement | +| `README.txt` | Instructions | + +Optuna utilise le fichier `study.db` comme base de données distribuée. +Tous les workers (tâches SLURM) écrivent dans cette base. + +--- + +## Installer Optuna dans le conteneur Turpan + +Ouvrir un shell dans le conteneur : + +```bash +apptainer shell --bind /tmpdir,/work --nv /work/conteneurs/sessions-interactives/pytorch-24.02-py3-calmip-si.sif +``` + +Installer Optuna : + +```bash +Apptainer> pip install optuna +``` + +Quitter le conteneur : + +``` +Ctrl + d +``` + +Vous êtes maintenant prêt à lancer l’optimisation. + +--- + +## Fonctionnement d’Optuna sur Turpan (multi‑nœuds) + +Optuna fonctionne ainsi : + +- chaque tâche SLURM (chaque GPU typiquement) démarre un worker, +- chaque worker charge la même base de données `study.db`, +- la méthode `study.optimize()` demande à Optuna de créer un *trial*, +- Optuna réserve ce trial dans la base de données, +- votre code entraîne un modèle, +- Optuna écrit le résultat dans la base, +- un autre worker récupère un nouveau trial dans la base, +- et le processus continue. + + **Aucun worker ne traite deux fois le même trial** le travail se synchronise automatiquement + +--- + +## La fonction `objective(trial)` — cœur d’Optuna + +Exemple utilisé dans l’entraînement MNIST : + +```python +def objective(trial): + + config = { + "lr": trial.suggest_float("lr", 1e-4, 5e-3, log=True), + "batch_size": trial.suggest_categorical("batch_size", [16, 32, 64, 128]), + "conv1_channels": trial.suggest_categorical("conv1_channels", [8, 16, 32, 64]), + "conv2_channels": trial.suggest_categorical("conv2_channels", [16, 32, 64, 128]), + "kernel1": trial.suggest_categorical("kernel1", [3, 5, 7]), + "kernel2": trial.suggest_categorical("kernel2", [3, 5, 7]), + "fc_dim": trial.suggest_categorical("fc_dim", [64, 128, 256, 512]), + "epochs": trial.suggest_categorical("epochs", [2, 3, 5]), + "num_classes": 10 + } + + loss = train_model(config, trial) + return loss +``` + +### Pourquoi passer `trial` à `train_model()` ? + +Car `trial.report()` et `trial.should_prune()` sont nécessaires : + +```python +trial.report(epoch_loss, epoch) + +if trial.should_prune(): + raise optuna.TrialPruned() +``` + +Cela active le *pruning* : +→ arrête tôt les mauvaises configurations → économise du temps GPU + +--- + +## Script SBATCH (multi‑nœuds, multi‑GPU) + +Voici la version adaptée Turpan : + +```bash +#!/bin/bash +#SBATCH --job-name=optuna-mnist +#SBATCH --output=logs/%A_%a.%t.out +#SBATCH --error=logs/%A_%a.%t.err +#SBATCH --nodes=2 # 2 nodes +#SBATCH --ntasks-per-node=2 # 2 tasks per node -> 2 GPUs per node +#SBATCH --gres=gpu:2 # 2 GPUs per node +#SBATCH --gpus-per-task=1 +#SBATCH --cpus-per-task=4 +#SBATCH --time=00:10:00 +#SBATCH --mem=16G + +set -euo pipefail + +# ---------- USER CONFIG ---------- +USER_WORKDIR=/tmpdir/bhupen/trash/optuna/code_and_slurm-scripts # must be shared across nodes +CONTAINER=/work/conteneurs/sessions-interactives/pytorch-24.02-py3-calmip-si.sif +PY_SCRIPT=$USER_WORKDIR/optuna_mnist.py +STUDY_DB=sqlite:///study.db +STUDY_NAME="mnist_hpo" +TOTAL_TRIALS=500 # total trials you want to run across all workers +# ----------------------------------- + +mkdir -p "$USER_WORKDIR" +mkdir -p logs + +# Ensure $HOME/.local/bin is in PATH for the container +export PATH="$HOME/.local/bin:$PATH" + +echo "SLURM NODELIST: $SLURM_NODELIST" +echo "SLURM_NNODES: $SLURM_NNODES" +echo "SLURM_NTASKS_PER_NODE: $SLURM_NTASKS_PER_NODE" +echo "Total tasks (ntasks): $SLURM_NTASKS" +echo "This job will launch $SLURM_NTASKS tasks (one per GPU)." + +# srun will launch the same command once per task (ntasks = nodes * ntasks-per-node) +# Each task runs a separate Apptainer instance, inside the container the script uses SLURM vars +srun --kill-on-bad-exit=1 --ntasks=$SLURM_NTASKS \ + apptainer exec --nv \ + --bind /tmpdir \ + --env PATH="$PATH" \ + --env STUDY_DB="/optuna_workdir/$(basename "$STUDY_DB")" \ + --env STUDY_NAME="$STUDY_NAME" \ + --env TOTAL_TRIALS="$TOTAL_TRIALS" \ + "$CONTAINER" \ + python "$PY_SCRIPT" +``` + +### Comment cela se répartit ? + +Avec deux nœuds × deux GPU → **4 workers Optuna en parallèle**. + +Chaque worker traite un trial indépendant. + +--- + +## Récupérer le meilleur modèle + +Vous pouvez lancer : + +```bash +apptainer exec --bind /tmpdir,/work --nv /work/conteneurs/sessions-interactives/pytorch-24.02-py3-calmip-si.sif python check_best_model.py +``` + +Le fichier : + +```python +import optuna +study = optuna.load_study("mnist_hpo", storage="sqlite:///study.db") +print(study.best_value) +print(study.best_params) +``` + +--- + +## Algorithmes d'optimisation avancés dans Optuna + +Optuna propose plusieurs moteurs de recherche d’hyperparamètres : + +| Algorithme | Description | Recommandé | +|-----------|-------------|------------| +| **TPE (par défaut)** | Très efficace, adaptatif | Recommandé | +| **RandomSearch** | Basique mais robuste | petites expériences | +| **GridSearch** | exhaustive | espaces petits | +| **CMA‑ES** | optimisation évolutive | grands espaces continus | +| **BruteForceSampler** | debug | jamais en production | +| **NSGA‑II** | optimisation multi‑objectifs | problèmes scientifiques | + +### Exemple TPE (par défaut) +```python +study = optuna.create_study(direction="minimize", sampler=optuna.samplers.TPESampler()) +``` + +### Exemple CMA‑ES +```python +from optuna.samplers import CmaEsSampler +study = optuna.create_study(sampler=CmaEsSampler()) +``` + +--- + +## Visualisations Optuna + +Optuna permet de générer automatiquement des graphiques : + +```python +optuna.visualization.plot_optimization_history(study) +optuna.visualization.plot_param_importances(study) +optuna.visualization.plot_parallel_coordinate(study) +``` + +--- + +## Adapter l’exemple à des modèles plus grands (LLM) + +Il suffit de remplacer le modèle MNIST : + +```python +from transformers import T5ForConditionalGeneration, T5Config + +def build_model(config): + tconf = T5Config( + d_model=config["hidden_size"], + num_heads=config["num_heads"], + num_layers=config["num_layers"] + ) + return T5ForConditionalGeneration(tconf) +``` + +Et définir un espace : + +```python +"hidden_size": trial.suggest_categorical([512, 768, 1024]), +"num_layers": trial.suggest_int("num_layers", 4, 24), +"num_heads": trial.suggest_categorical([8, 12, 16]), +"lr": trial.suggest_float(1e-6, 1e-4, log=True), +``` + +--- + +## Lancer le job + +```bash +sbatch run-optuna-multinode_Turpan.sh +``` + +Suivre la file : + +```bash +squeue --me +``` + +Voir quels GPU sont utilisés : + +```bash +placement --checkme +``` + +--- + +## Scalabilité attendue sur Turpan + +Si vous lancez 500 trials avec 4 GPU : + +→ 4 trials simultanés +→ Optuna envoie un nouveau trial dès qu’un GPU se libère +→ Temps total ≈ (500 / 4) × durée d’un entraînement + +Avec 8 GPU → 8 trials simultanés +Avec 16 GPU → 16 trials simultanés + +Optuna scale **parfaitement** tant qu’il y a des trials à lancer. + +Ce document fournit un exemple complet utilisant MNIST, mais le schéma peut être étendu à des modèles plus complexes comme des CNN plus profonds ou des modèles transformers. + +--- + diff --git a/docs/arch_exp/turpan/ressources_ia/HPO/ray_tune.md b/docs/arch_exp/turpan/ressources_ia/HPO/ray_tune.md new file mode 100644 index 00000000..0fcf1871 --- /dev/null +++ b/docs/arch_exp/turpan/ressources_ia/HPO/ray_tune.md @@ -0,0 +1,290 @@ +# Ray Tune + +Ray Tune est un framework évolutif d'optimisation d'hyperparamètres qui facilite l'exécution de nombreux entraînements en parallèle. Chaque expérience, appelée *trial*, entraîne un modèle en utilisant une combinaison différente d'hyperparamètres — par exemple le taux d'apprentissage, la taille de lot, le nombre de couches ou les paramètres de l'optimiseur. Plutôt que de lancer des dizaines d'expériences à la main, Ray Tune automatise le processus et répartit le travail sur l'ensemble des ressources de calcul disponibles. + +L'un des points forts de Ray Tune est son support d'une large palette d'algorithmes d'optimisation modernes. Au-delà de la recherche aléatoire ou de la recherche sur grille, Ray Tune peut adapter automatiquement l'échantillonnage des hyperparamètres en utilisant des méthodes telles que BOHB, HEBO, Optuna, l'optimisation bayésienne, HyperBand ou ASHA. Ces algorithmes tirent parti des résultats des essais précédents et concentrent la recherche sur les régions les plus prometteuses, ce qui permet d'obtenir de meilleurs hyperparamètres avec moins d'expériences au total. + +Ce document explique comment lancer Ray Tune sur le cluster Turpan en utilisant des scripts SLURM pour deux et huit GPU. Il décrit aussi comment modifier la configuration des hyperparamètres, comment basculer entre différents algorithmes de recherche, et comment le script d'entraînement construit dynamiquement l'architecture du réseau de neurones à partir des hyperparamètres choisis pour chaque essai. Enfin, il indique comment adapter l'exemple à des modèles plus complexes, comme des LLM basés sur des transformeurs. + +--- +## Ressources sur cette page +- [Copier l'exemple de code et mettre en place l'environement](#copier-lexemple-de-code) +- [Structure du répertoire](#structure-du-répertoire) +- [Script SLURM (sbatch)](#script-slurm-sbatch) +- [Script principal Ray Tune](#script-principal-ray-tune) +- [Espace de recherche Ray Tune](#espace-de-recherche-ray-tune-modifiable) +- [Modèle MNIST dynamique](#modèle-mnist-dynamique) +- [Adapter pour des expériences LLM](#adapter-pour-des-expériences-llm) +- [Lancer le job](#lancer-le-job) +- [Évolutivité attendue](#évolutivité-attendue) +- [Algorithmes de recherche avancés](#utiliser-des-algorithmes-de-recherche-avancés-bohb-hebo-optuna-bayesopt) + +--- +## Copier l'exemple de code +Tous les scripts d'exemple Ray Tune et les lanceurs SLURM sont disponibles sur Turpan et peuvent être copiés directement depuis : +```bash +/work/shares/IA-Tests/ray_tune.tar.gz +``` +Pour l'extraire dans votre répertoire : +```bash +tar xvf /work/shares/IA-Tests/ray_tune.tar.gz +``` +Cela créera un répertoire nommé `ray_tune/`. + +### Installer ray dans l'environement +Le seul paquet nécessaire est ray. +Pour l’installer, il suffit d’ouvrir le conteneur en mode shell : +```bash +apptainer shell --bind /tmpdir,/work --nv /work/conteneurs/sessions-interactives/pytorch-24.02-py3-calmip-si.sif +``` +Ensuite, à l’intérieur du conteneur, on installe Ray avec pip : +```bash +Apptainer> pip install ray +``` +Pour sortir du conteneur : +```bash +Crlt + d // pour sortir du conteneur +``` +A ce moment là tout est pret pour [lancer le calcul](#lancer-le-job) + +--- + +## Structure du répertoire +Après extraction vous devriez obtenir : + +``` +ray_tune/ +├── code_and_slurm-scripts +│ ├── mnist_ddp.py +│ ├── ray_tune.py +│ ├── run-ray_tune_on_2gpus_Turpan.sh +│ ├── run-ray_tune_on_8gpus_Turpan.sh +├── data +│ └── MNIST/ (dataset MNIST téléchargé) +└── README.txt +``` + +### Description des fichiers + +| Fichier | Description | +|-----------------------------------|---------------------------------------------------------------| +| `mnist_ddp.py` | Script d'entraînement utilisant PyTorch | +| `ray_tune.py` | Script principal Ray Tune pour l'HPO | +| `run-ray_tune_on_2gpus_Turpan.sh` | Script SLURM pour lancer Ray Tune sur 2 GPU | +| `run-ray_tune_on_8gpus_Turpan.sh` | Script SLURM pour lancer Ray Tune sur 8 GPU | +| `data/MNIST` | Dataset MNIST (téléchargé automatiquement si manquant) | +| `README.txt` | Instructions et notes | + +#### Rôle des fichiers : comment Ray Tune interagit avec le code d’entraînement +Le fichier ray_tune.py agit comme une couche au-dessus du script d’entraînement : +- C’est lui qui définit l’espace d’hyperparamètres à explorer (learning rate, batch size, nombre de couches, etc.). +- Il choisit comment explorer cet espace : aléatoire, grid search, BOHB, Optuna, BayesOpt, etc. +- Il crée et lance chaque essai d’entraînement (trial) en appelant votre fonction d’entraînement dans mnist_ddp.py. +- Il attribue les ressources (nombre de GPU/CPU par essai). +- Il récupère les métriques via tune.report() et les utilise pour guider la recherche. + +## Script SLURM (sbatch) +Exemple de fichier sbatch utilisé pour lancer Ray Tune sur **4 nœuds**, chacun avec **2 GPU**, en utilisant `srun` + `apptainer` : + +```bash +#!/bin/bash +#SBATCH --job-name=benchmark +#SBATCH -N 4 +#SBATCH --ntasks=4 +#SBATCH --cpus-per-task=40 +#SBATCH --mem=100G +#SBATCH --time=40 +#SBATCH --gres=gpu:2 + +srun apptainer exec --bind /tmpdir,/work --nv /work/conteneurs/sessions-interactives/pytorch-24.02-py3-calmip-si.sif python3 ray_tune.py +``` + +### Concepts clés +Une tâche Ray correspond à un processus lancé par Ray. Avec `--ntasks=4` et `--gres=gpu:2`, vous avez 4 workers Ray et chaque worker voit 2 GPU. Ray détecte automatiquement les GPU réservés via `ray.cluster_resources()` et lance des *trials* en parallèle tant que des GPU libres existent. + +--- + +## Script principal Ray Tune +Le script `ray_tune.py` initialise Ray, définit l'espace de recherche, le scheduler et soumet les essais. + +### Initialiser Ray dans SLURM +Ray est lancé localement dans le job : + +```python +ray.init( + num_cpus=max(1, int(os.environ.get("SLURM_CPUS_PER_TASK", "40"))), + include_dashboard=False, + _system_config={"enable_metrics_collection": False} +) +``` + +### Attribution des ressources +Chaque essai utilise par exemple : + +```python +tune.with_resources(original_main, resources={"cpu": 4, "gpu": 1}) +``` + +Cela signifie que chaque *trial* consomme 1 GPU. Si chaque nœud offre 2 GPU → 2 trials simultanés par noeud. Avec 4 nœuds → 8 trials en parallèle. + +Si vous voulez qu'un *trial* utilise plusieurs GPU, changez `"gpu": 2` et adaptez le code d'entraînement pour utiliser `DistributedDataParallel` ou un schéma multi-GPU. + +--- + +## Espace de recherche Ray Tune (modifiable) +Exemple d'espace utilisé : + +```python +config = { + "lr": tune.loguniform(1e-5, 5e-3), + "batch_size": tune.choice([16, 32, 64, 128]), + "optimizer": tune.choice(["adam", "sgd", "adamw"]), + "weight_decay": tune.uniform(0.0, 0.1), + + "conv1_channels": tune.choice([8, 16, 32, 64]), + "conv2_channels": tune.choice([16, 32, 64, 128]), + "conv3_channels": tune.choice([0, 16, 32, 64, 128]), + + "kernel1": tune.choice([3, 5, 7]), + "kernel2": tune.choice([3, 5, 7]), + "kernel3": tune.choice([3, 5, 7]), + + "activation": tune.choice(["relu", "gelu", "silu"]), + "dropout": tune.uniform(0.0, 0.5), + + "fc_dim": tune.choice([64, 128, 256, 512]), + + "scheduler": tune.choice(["none", "cosine", "step", "onecycle"]), + "step_size": tune.choice([10, 20, 30]), + "gamma": tune.uniform(0.1, 0.9), + + "epochs": tune.choice([2, 3, 5]), + "num_classes": 10, +} +``` + +Adaptez ces paramètres pour élargir ou restreindre l'espace de recherche selon vos besoins. + +--- + +### Comment Ray collecte les résultats +À la fin d'une époque, le script d'entraînement appelle : + +```python +tune.report({"loss": loss_sum / len(train_loader)}) +``` + +Les logs Ray et le stdout s'écrivent dans le fichier SLURM (p.ex. `slurm-.out`), que vous pouvez consulter avec `cat`. + +--- + +## Modèle MNIST dynamique +Dans `mnist_ddp.py`, le modèle se construit dynamiquement à partir de la configuration Ray : + +```python +self.layer1 = nn.Sequential( + nn.Conv2d(1, config["conv1_channels"], config["kernel1"], padding=config["kernel1"]//2), + nn.BatchNorm2d(config["conv1_channels"]), + nn.ReLU(), + nn.MaxPool2d(2) +) +``` + +Cette approche permet : +- d'ajouter/supprimer des couches, +- de tester différentes largeurs de canaux, +- de changer les fonctions d'activation, +- d'activer le dropout, +- de varier la taille des noyaux. + +Tout cela est construit automatiquement pour chaque *trial*. + +--- + +## Adapter pour des expériences LLM +Pour passer de MNIST à un modèle T5 (ou autre LLM), remplacez la partie modèle par une construction T5 : + +```python +from transformers import T5Config, T5ForConditionalGeneration + +config_llm = T5Config( + d_model=config["hidden_size"], + num_heads=config["num_heads"], + num_layers=config["num_layers"] +) + +model = T5ForConditionalGeneration(config_llm) +``` + +Déclarez les hyperparamètres pertinents dans l'espace Ray, par exemple : + +```python +"hidden_size": tune.choice([512, 768, 1024]), +"num_heads": tune.choice([8, 12, 16]), +"num_layers": tune.choice([4, 8, 12]), +``` + +Supprimez les parties liées à MNIST (dataset, transformations) et adaptez le pipeline d'entraînement. + +--- + +## Lancer le job + +Dans `code_and_slurm-scripts` il y a deux exemples prêts à être lances `run-ray_tune_on_2gpus_Turpan.sh` et `run-ray_tune_on_8gpus_Turpan.sh` +Pour les lances il suffit de : + +```bash +sbatch run-ray_tune_on_2gpus_Turpan.sh +``` + +Pour surveillez la file d'attente : +```bash +squeue --me +``` + +Vérifiez l'utilisation des GPU une fois le job est en RUNNING (commande interne au cluster) : +```bash +placement --checkme +``` + +--- + +## Évolutivité attendue +Si chaque essai demande 1 GPU et que `num_samples=8` : +- 1 nœud = 2 GPUs → 2 essais simultanés +- 4 nœuds = 8 GPUs → 8 essais simultanés (tous les essais peuvent s'exécuter en parallèle) +- Le temps total ≈ temps d'entraînement d'un modèle (si le nombre d'essais ≤ nombre de GPUs) + +`num_samples` définit le nombre total d'essais Ray lancés (échantillonnés dans l'espace d'hyperparamètres). Ce n'est pas nécessairement égal au nombre de combinaisons possibles : Ray échantillonne `num_samples` points dans l'espace. + +--- + +## Utiliser des algorithmes de recherche avancés (BOHB, HEBO, Optuna, BayesOpt) +Ray Tune permet de remplacer la recherche aléatoire par des algorithmes adaptatifs. Ajoutez un *search_alg* au `Tuner` : + +```python +from ray import tune +from ray.tune.search.bohb import TuneBOHB +from ray.tune.search.optuna import OptunaSearch +from ray.tune.search.hebo import HEBOSearch +from ray.tune.search.bayesopt import BayesOptSearch + +search_alg = OptunaSearch() # <-- choisissez un algo + +tuner = tune.Tuner( + train_fn, + param_space=config, + tune_config=tune.TuneConfig( + metric="loss", + mode="min", + num_samples=50, # nombre total d'essais + search_alg=search_alg, # l'algorithme d'optimisation + max_concurrent_trials=8, + ), +) +tuner.fit() +``` + +Chaque algorithme a ses forces : Optuna est robuste et populaire, HEBO est efficace pour certains espaces complexes, BOHB combine bande passante et optimisation bayésienne pour être efficace en budget restreint. Le "meilleur" dépend du problème et du budget : essayez plusieurs algos. + +--- diff --git a/docs/arch_exp/turpan/ressources_ia/index.md b/docs/arch_exp/turpan/ressources_ia/index.md index ad836966..a110b20b 100644 --- a/docs/arch_exp/turpan/ressources_ia/index.md +++ b/docs/arch_exp/turpan/ressources_ia/index.md @@ -42,7 +42,13 @@ These adaptations allow users to launch distributed training jobs without needin Examples: [- Flow matching](./flow_matching.md) -[- Ray tuning](./ray_tune.md) + +## L’Hyperparameter Optimization (HPO) + +L’Hyperparameter Optimization (HPO) vise à trouver automatiquement les meilleurs hyperparamètres pour optimiser les performances d’un modèle. Dans le cadre du cluster Turpan, nous proposons deux approches complémentaires : Ray Tune, idéal pour des expérimentations sur un seul nœud, offrant une large variété d’algorithmes d’optimisation ; et Optima, mieux adapté aux charges multi-nœuds, permettant d’exploiter pleinement l’architecture distribuée de Turpan pour accélérer la recherche d’hyperparamètres à grande échelle. + +[- Ray tuning](./HPO/ray_tune.md) +[- Optuna](./HPO/optuna.md) --- diff --git a/docs/arch_exp/turpan/ressources_ia/ray_tune.md b/docs/arch_exp/turpan/ressources_ia/ray_tune.md deleted file mode 100644 index ba5d4a8e..00000000 --- a/docs/arch_exp/turpan/ressources_ia/ray_tune.md +++ /dev/null @@ -1,306 +0,0 @@ -# Ray Tune - -Ray Tune is a scalable framework for hyperparameter optimization that makes it easy to run many training experiments in parallel. Each experiment, called a trial, trains a model using a different combination of hyperparameters such as the learning rate, batch size, number of layers, or optimizer settings. Instead of manually launching dozens of experiments one by one, Ray Tune automates this process and distributes the work across all available computational resources. - -One of the strengths of Ray Tune is its support for a wide range of modern optimization algorithms. Beyond simple random search or grid search, it can automatically adapt the sampling of hyperparameters using methods such as BOHB, HEBO, Optuna, Bayesian Optimization, HyperBand, or ASHA. These algorithms learn from the results of previous trials and focus the search on the most promising regions, allowing the model to converge to better hyperparameters with fewer total experiments. - -This document explains how to run Ray Tune on the Turpan cluster using SLURM scripts for two and eight GPUs. It also describes how to modify the hyperparameter configuration, how to switch between search algorithms, and how the training script dynamically builds the neural-network architecture based on the hyperparameters selected for each trial. Finally, it explains how to adapt the example to a models, such as transformer-based LLMs. - ---- -## Resources on This Page -- [Copying the Example Code](#copying-the-example-code) -- [Directory Structure](#directory-structure) -- [SLURM Job Script](#slurm-job-script-sbatch) -- [Ray Tune Main Script](#ray-tune-main-script) -- [Ray Tune Search Space](#ray-tune-search-space-modifiable) -- [Dynamic MNIST Model](#dynamic-mnist-model) -- [Modifying for LLM Experiments](#how-to-modify-the-model-for-llm-experiments) -- [Running the Job](#running-the-job) -- [Expected Scaling](#expected-scaling) -- [Advanced Search Algorithms](#using-advanced-search-algorithms-bohb-hebo-optuna-bayesopt) - ---- -## Copying the Example Code -All the example Ray Tune scripts and SLURM launchers are available on Turpan and can be copied directly from: -```bash -/work/shares/IA-Tests/ray_tune.tar.gz -``` -To extract it in your directory: -```bash -tar xvf /work/shares/IA-Tests/ray_tune.tar.gz -``` -This will create a directory called `ray_tune/` - -## Directory Structure -After extracting, you will see: -```bash -ray_tune/ -├── code_and_slurm-scripts -│ ├── mnist_ddp.py -│ ├── ray_tune.py -│ ├── run-ray_tune_on_2gpus_Turpan.sh -│ ├── run-ray_tune_on_8gpus_Turpan.sh -├── data -│ └── MNIST/ (downloaded MNIST dataset) -└── README.txt -``` - -### File descriptions - -| File | Description | -|----------------------------------|---------------------------------------------------------------| -| `mnist_ddp.py` | Training script using PyTorch | -| `ray_tune.py` | Ray Tune hyperparameter tuning script | -| `run-ray_tune_on_2gpus_Turpan.sh` | SLURM script to run Ray Tune on 2 GPUs. | -| `run-ray_tune_on_8gpus_Turpan.sh` | SLURM script to run Ray Tune on 8 GPUs. | -| `data/MNIST` | MNIST dataset (downloaded automatically if missing). | -| `README.txt` | Instructions and notes. | - -The file to be changed to add your trainning is mnist_ddp.py - - -## SLURM Job Script (sbatch) -Below is the job file used to launch Ray Tune on **4 nodes**, each with **2 GPUs**, using `srun` + `apptainer`. - -```bash -#!/bin/bash -#SBATCH --job-name=benchmark -#SBATCH -N 4 -#SBATCH --ntasks=4 -#SBATCH --cpus-per-task=40 -#SBATCH --mem=100G -#SBATCH --time=40 -#SBATCH --gres=gpu:2 - -srun apptainer exec --bind /tmpdir,/work --nv /work/conteneurs/sessions-interactives/pytorch-24.02-py3-calmip-si.sif python3 ray_tune.py -``` - -### Key Concepts -- **1 Ray trial = 1 process launched by Ray** -- `--ntasks=4` = `-N 4` ⇒ 4 Ray workers -- Each worker sees **2 GPUs** because of `--gres=gpu:2` -- Ray automatically detects all the GPUs in the reservation via `ray.cluster_resources()` -- Trials run **in parallel** as long as there are free GPUs - ---- - -## Ray Tune Main Script -`ray_tune.py` launches Ray, defines the search space, scheduler, and submits trials. - -Key parts: - -### Initialize Ray in SLURM -Ray is launched locally inside the job: - -```python -ray.init( - num_cpus=max(1, int(os.environ.get("SLURM_CPUS_PER_TASK", "40"))), - include_dashboard=False, - _system_config={"enable_metrics_collection": False} -) -``` - -### Resource Assignment -Each trial uses: - -```python -tune.with_resources(original_main, resources={"cpu": 4, "gpu": 1}) -``` - -This means: -- Each trial uses **1 GPU** -- The cluster nodes have 2 GPUs → **2 parallel trials per node** -- With 4 nodes → **8 parallel trials total** - -GPUs per trial can be changed but at that time trainning the code needs to use `DistributedDataParallel` or other method to perform data parallelism: - -```python -resources={"gpu": 2} -``` - ---- - -## Ray Tune Search Space (modifiable) -Example used in your test: - -```python -config = { - "lr": tune.loguniform(1e-5, 5e-3), - "batch_size": tune.choice([16, 32, 64, 128]), - "optimizer": tune.choice(["adam", "sgd", "adamw"]), - "weight_decay": tune.uniform(0.0, 0.1), - - "conv1_channels": tune.choice([8, 16, 32, 64]), - "conv2_channels": tune.choice([16, 32, 64, 128]), - "conv3_channels": tune.choice([0, 16, 32, 64, 128]), - - "kernel1": tune.choice([3, 5, 7]), - "kernel2": tune.choice([3, 5, 7]), - "kernel3": tune.choice([3, 5, 7]), - - "activation": tune.choice(["relu", "gelu", "silu"]), - "dropout": tune.uniform(0.0, 0.5), - - "fc_dim": tune.choice([64, 128, 256, 512]), - - "scheduler": tune.choice(["none", "cosine", "step", "onecycle"]), - "step_size": tune.choice([10, 20, 30]), - "gamma": tune.uniform(0.1, 0.9), - - "epochs": tune.choice([2, 3, 5]), - "num_classes": 10, -} -``` - -💡 Modify these values to expand or reduce the search space. - ---- - -### How Ray collects results: -Each epoch: - -```python -tune.report({"loss": loss_sum / len(train_loader)}) -``` - -To check the output: -```python -cat slurm-.out -``` - ---- - -## Dynamic MNIST Model -Inside `mnist_ddp.py`, the model is built dynamically from the configuration. - -Example: - -```python -self.layer1 = nn.Sequential( - nn.Conv2d(1, config["conv1_channels"], config["kernel1"], padding=config["kernel1"]//2), - nn.BatchNorm2d(config["conv1_channels"]), - nn.ReLU(), - nn.MaxPool2d(2) -) -``` - -This allows: -- adding/removing layers -- testing different widths -- changing activations -- using dropout -- changing kernel sizes - -Everything is done **automatically per-trial**. - - - ---- - -## How to Modify the Model for LLM Experiments -If you switch from MNIST → T5 (5B model), just replace the model code: - -```python -from transformers import T5Config, T5ForConditionalGeneration - -config_llm = T5Config( - d_model=config["hidden_size"], - num_heads=config["num_heads"], - num_layers=config["num_layers"] -) - -model = T5ForConditionalGeneration(config_llm) -``` - -Define hyperparameters in the Ray config, example: - -```python -"hidden_size": tune.choice([512, 768, 1024]), -"num_heads": tune.choice([8, 12, 16]), -"num_layers": tune.choice([4, 8, 12]), -``` - -And remove MNIST parts. - ---- - -## Running the Job -Submit example for 2 GPUs: - -```bash -sbatch run-ray_tune_on_2gpus_Turpan.sh -``` - -Submit example for 8 GPUs: - -```bash -sbatch run-ray_tune_on_8gpus_Turpan.sh -``` - -To check that the code is running: -```bash -squeue --me -``` -To check that the GPUs are being used (it is a screen shot of the resources): -```bash -placement --checkme -``` - - ---- - -## Expected Scaling -If you keep: - -``` -resources={"gpu": 1} -num_samples=8 -``` - -Then: -- 1 node = 2 GPUs = 2 parallel trials -- 4 nodes = 8 GPUs = 8 parallel trials → all trials run simultaneously -- Runtime ≈ equal to training time of **one** model - -If you set: - -`num_samples` should be equal or greater than the number of GPUs. - ---- - -## Using Advanced Search Algorithms (BOHB, HEBO, Optuna, BayesOpt) - -Ray Tune allows you to replace the default random search with more advanced hyperparameter optimization (HPO) algorithms that adaptively select new trials based on previous results. - -These algorithms are enabled by adding a search algorithm to tune.Tuner() via the argument search_alg=. - -### Example: Adding a search algorithm - -``` -from ray import tune -from ray.tune.search.bohb import TuneBOHB -from ray.tune.search.optuna import OptunaSearch -from ray.tune.search.hebo import HEBOSearch -from ray.tune.search.bayesopt import BayesOptSearch - -search_alg = OptunaSearch() # <-- select one algo here - -tuner = tune.Tuner( - train_fn, - param_space=config, - tune_config=tune.TuneConfig( - metric="loss", - mode="min", - num_samples=50, # how many trials total - search_alg=search_alg, # <-- your optimizer - max_concurrent_trials=8, # how many trials run in parallel (e.g., number of GPUs) - ), -) -tuner.fit() -``` - - ---- - - -