> ## Documentation Index
> Fetch the complete documentation index at: https://wb-21fd5541-update-reference-docs-59.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Découvrez comment W&B Sweeps gère les signaux UNIX, les codes de sortie et la préemption dans les runs de balayage.

# Gestion des signaux et des runs de balayage

Cette page explique en détail comment W\&B Sweeps gère les signaux système et les codes de sortie des processus. Utilisez ces informations pour exécuter des balayages de manière fiable dans des environnements préemptibles tels que SLURM, EC2 Spot ou les VM préemptibles de Google Cloud. Les sections suivantes décrivent comment interrompre proprement les runs au clavier et fournissent des informations pour vous aider à comprendre et à anticiper le comportement de remise en file d’attente des runs. Cette page s’adresse aux utilisateurs qui exécutent des balayages sur une infrastructure préemptible ou qui ont besoin d’un contrôle fin sur le cycle de vie et le nettoyage des runs. Pour en savoir plus sur la façon dont W\&B remet les runs en file d’attente lorsqu’ils sont préemptés, voir [Reprendre les runs de balayages préemptibles](/fr/models/runs/resuming#resume-preemptible-sweeps-runs).

<div id="exit-status-and-signals">
  ## Code de sortie et signaux
</div>

W\&B utilise le statut de sortie du processus d’entraînement pour déterminer si un run est remis en file d’attente et comment son état est enregistré.

**Contrat des codes de sortie :**

* **Code de sortie 0** : W\&B considère que le run s’est terminé avec succès et ne le remet pas en file d’attente.
* **Code de sortie non nul** : W\&B considère le run comme ayant échoué ou comme ayant été préempté. Lorsque vous utilisez [`mark_preempting()`](/fr/models/ref/python/experiments/run#mark_preempting), W\&B remet le run en file d’attente afin qu’un autre agent (ou le même agent après redémarrage) puisse le reprendre.

Cela s’applique que le processus se termine dans un gestionnaire de signal, à la suite d’une exception ou via un appel explicite à `sys.exit()`. Il est important de comprendre ce contrat et de s’y fier dans des environnements préemptibles ou sur cluster.

Lorsque le processus se termine à cause d’un [signal interceptable](#catchable-signals-and-preemption), votre gestionnaire peut s’exécuter, appeler [`wandb.run.mark_preempting()`](/fr/models/ref/python/experiments/run#mark_preempting) si vous souhaitez que le run soit remis en file d’attente, effectuer un nettoyage (par exemple, enregistrer un point de contrôle), puis quitter avec un code non nul. Une convention courante est `sys.exit(128 + signum)` pour une terminaison due à un signal. W\&B enregistre ce code de sortie et les mêmes [règles de remise en file d’attente](/fr/models/runs/resuming#resume-preemptible-sweeps-runs) s’appliquent. Lorsque le noyau du système d’exploitation tue le processus avec [`SIGKILL`](#sigkill-uncatchable), le processus ne peut pas exécuter de hooks de sortie ; W\&B n’écrit donc pas de synthèse finale, et le run peut apparaître comme planté ou tué. L’agent démarre néanmoins le run suivant.

<div id="stale-runs-and-server-side-timeouts">
  ## Runs inactifs et délais d’expiration côté serveur
</div>

W\&B détermine l’état d’une exécution à la fois selon son code de sortie et son activité. Si une exécution ne se termine pas et ne publie aucune nouvelle métrique pendant environ 5 minutes, W\&B la marque comme plantée. Cela peut se produire lorsque le processus d’entraînement cesse de répondre, n’effectue plus de journalisation ou se termine sans sortie propre (par exemple après réception de `SIGKILL`). Consignez des métriques à intervalles réguliers ou quittez avec un code de sortie explicite afin que l’état de l’exécution reflète bien ce qui s’est passé.

<div id="catchable-signals-and-preemption">
  ## Signaux interceptables et préemption
</div>

La plupart des signaux dans les environnements préemptibles sont interceptables, ce qui signifie que votre script d’entraînement peut les intercepter et s’arrêter proprement. Vous pouvez enregistrer des gestionnaires de signaux personnalisés dans votre script d’entraînement. Lorsqu’un signal interceptable est reçu, votre gestionnaire s’exécute ; les métriques déjà envoyées à W\&B sont conservées, et l’agent détecte l’arrêt du processus et démarre le run suivant.

**Bonnes pratiques :**

* Enregistrez les gestionnaires le plus tôt possible (par exemple, avant d’entrer dans la boucle principale d’entraînement).
* Dans le gestionnaire, appelez [`wandb.run.mark_preempting()`](/fr/models/ref/python/experiments/run#mark_preempting) si vous souhaitez que le run soit remis en file d’attente après une préemption, effectuez les opérations de nettoyage (par exemple, enregistrez un checkpoint), puis quittez avec un code non nul.

L’exemple suivant enregistre des gestionnaires pour `SIGUSR1` (un signal de préemption de cluster courant) et `SIGTERM`. Il laisse `SIGINT` libre pour une utilisation interactive (par exemple, une annulation manuelle depuis le terminal). Le gestionnaire appelle `wandb.run.mark_preempting()` et quitte avec `128 + signum` :

```python theme={null}
import signal
import sys
import wandb


def signal_handler(signum, frame):
    if wandb.run is not None:
        # Facultatif : enregistrez un point de contrôle du modèle, videz les tampons, etc.
        print(f"Preempted with signal: {signal.Signals(signum).name}.")
        wandb.run.mark_preempting()
    sys.exit(128 + signum)


def train():
    signal.signal(signal.SIGUSR1, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    with wandb.init() as run:
        config = wandb.config
        for epoch in range(100):
            # Étape d'entraînement ; wandb.log(...) selon les besoins
            pass


if __name__ == "__main__":
    train()
```

<div id="sigkill-uncatchable">
  ## `SIGKILL` (impossible à intercepter)
</div>

`SIGKILL` provient du noyau du système d’exploitation et ne peut être ni intercepté ni ignoré. Le processus se termine immédiatement, sans possibilité d’exécuter des gestionnaires de signal ou des rappels `atexit`. W\&B ne peut pas écrire de résumé final pour le run. L’agent récupère malgré tout et poursuit le balayage, mais les données de ce run sont incomplètes. Utilisez `SIGKILL` uniquement en dernier recours. Privilégiez `SIGTERM` ou `SIGINT` lorsque vous avez besoin d’un arrêt propre.

<div id="signal-forwarding-from-agent-to-child">
  ## Transmission des signaux de l'agent au processus enfant
</div>

Lorsque vous utilisez la CLI [`wandb agent`](/fr/models/ref/cli/wandb-agent), l'agent exécute votre script d'entraînement en tant que processus enfant. Lorsque vous interrompez l'agent (par exemple avec Ctrl+C, ou lorsqu'un ordonnanceur envoie `SIGTERM` au job), le processus enfant (processus d'entraînement) ne reçoit pas le signal par défaut. Le script d'entraînement ne peut donc pas exécuter son gestionnaire ni appeler `mark_preempting()`. Pour plus d'informations, voir [wandb GitHub issue #3667](https://github.com/wandb/wandb/issues/3667).

Pour permettre au processus enfant de s'arrêter proprement et d'appeler `wandb.run.mark_preempting()` dans un gestionnaire, exécutez l'agent CLI avec `--forward-signals` :

```bash theme={null}
wandb agent --forward-signals entity/project/sweep_ID
```

W\&B ne prend pas en charge la transmission des signaux pour [`wandb.agent()`](/fr/models/ref/python/functions/agent) dans l'API Python. Ce mode d'exécution lance votre fonction d'entraînement dans un thread, et non dans un processus enfant distinct, donc le même comportement de transmission ne s'applique pas.

Lorsque l'agent CLI reçoit `SIGINT` ou `SIGTERM` avec la transmission activée, il relaie le signal au processus enfant. Le gestionnaire de votre script d'entraînement peut alors s'exécuter, appeler `wandb.run.mark_preempting()` et [`wandb.finish()`](/fr/models/ref/python/experiments/run#finish) avec un code de sortie non nul si nécessaire, puis se terminer avec un code non nul. Si vous appuyez deux fois sur Ctrl+C sur le processus de l'agent, l'agent reçoit `SIGTERM` par défaut. Avec `--forward-signals`, l'agent peut transmettre `SIGINT` au processus enfant pour que votre gestionnaire s'exécute.

Pour plus d'informations, voir la référence CLI [`wandb agent`](/fr/models/ref/cli/wandb-agent).

<div id="preemptible-clusters-like-slurm">
  ## Clusters préemptibles comme SLURM
</div>

Cette section explique comment configurer les balayages pour que les runs puissent reprendre après une préemption sur des clusters tels que SLURM, EC2 Spot ou les machines virtuelles préemptibles de Google Cloud. En cas de préemption, le processus d’entraînement doit recevoir le signal, marquer le run comme étant en cours de préemption, puis quitter avec un code non nul afin que W\&B remette le run en file d’attente. Un nouvel agent (ou le même agent une fois le job remis en file d’attente) peut alors reprendre le run.

**Assurez-vous que le processus d’entraînement reçoit le signal :**

* **Lorsque l’ordonnanceur envoie le signal à l’agent** : exécutez l’agent avec `wandb agent --forward-signals` afin que, lorsque l’ordonnanceur (ou l’utilisateur) envoie un signal à l’agent, l’agent le transmette au processus enfant. Le gestionnaire du processus enfant peut alors appeler `wandb.run.mark_preempting()`, [`wandb.finish(exit_code=...)`](/fr/models/ref/python/experiments/run#finish) avec un code non nul, puis `sys.exit(128 + signum)` (ou un autre code de sortie non nul).
* **Lorsque l’ordonnanceur envoie le signal au script de lancement (et non directement à l’agent)** : faites en sorte que le script de lancement envoie le signal de préemption directement au processus d’entraînement. Par exemple, le script d’entraînement écrit son ID de processus dans un fichier. Le script de lancement intercepte le signal du cluster (par exemple `SIGUSR1`) et exécute `kill -SIGUSR1 $(cat $PID_FILE)` afin que le gestionnaire du processus d’entraînement s’exécute.

**Dans le script d’entraînement :** enregistrez un gestionnaire pour le signal utilisé par votre cluster (par exemple `SIGTERM` ou `SIGUSR1`). Dans le gestionnaire, appelez `wandb.run.mark_preempting()` si un run est actif, puis terminez le run avec un code de sortie non nul et `sys.exit(128 + signum)` (ou un autre code non nul) afin que W\&B remette le run en file d’attente. Pour plus d’informations sur le moment où W\&B remet les runs en file d’attente et sur l’interaction avec `mark_preempting()`, voir [Resume preemptible balayage runs](/fr/models/runs/resuming#resume-preemptible-sweeps-runs).

**État du balayage :** exécutez `wandb sweep entity/project/sweep_ID --resume` avant de démarrer l’agent afin que le balayage soit en mode reprise et attribue les runs remis en file d’attente.

**Coordination multi-agent :** lorsque plusieurs agents s’exécutent en même temps (comme dans des jobs de tableau SLURM), ils peuvent entrer en concurrence pour récupérer le même run préempté. Il s’agit d’une limitation connue. Pour contourner ce problème, décalez le démarrage des agents ou utilisez des mécanismes de coordination externes, comme des verrous.

Pour les jobs SLURM multi-GPU où un seul processus doit appeler `wandb.agent()`, voir [How should I run sweeps on SLURM?](/fr/support/models/articles/how-should-i-run-sweeps-on-slurm).

<div id="wandb-sweep-cancel">
  ## `wandb sweep --cancel`
</div>

Cette section décrit comment la commande `--cancel` interagit avec les signaux et les processus enfants, car l’annulation se comporte différemment de l’envoi direct d’un signal du système d’exploitation. Vous annulez un balayage à l’aide de l’API W\&B, et non avec un signal du système d’exploitation. Exécutez une commande comme `wandb sweep --cancel entity/project/sweep_ID`. Le serveur demande à l’agent de quitter, puis l’agent termine les processus enfants en cours d’exécution et s’arrête. Il peut s’écouler un court délai (de l’ordre de l’intervalle d’interrogation de l’API par l’agent) avant que l’annulation ne prenne effet.

L’annulation envoie `SIGKILL` aux runs. Les processus enfants n’ont aucune possibilité d’exécuter des gestionnaires de signaux définis par l’utilisateur. Il en va de même lorsque vous utilisez le contrôle **Cancel** dans l’UI de balayage. Utilisez `--cancel` lorsque vous voulez arrêter l’ensemble du balayage et le marquer comme annulé. Pour arrêter proprement le run en cours, envoyez un signal interceptable au run (ou utilisez `--forward-signals` avec l’agent CLI et envoyez le signal à l’agent). Pour terminer proprement un balayage, utilisez plutôt [`wandb sweep --stop`](/fr/models/sweeps/pause-resume-and-cancel-sweeps#stop-a-sweep) que `--cancel`.

Pour plus d’informations sur les options de mise en pause, de reprise, d’arrêt et d’annulation, voir [Gérer les balayages](/fr/models/sweeps/pause-resume-and-cancel-sweeps).

<div id="signals-to-the-agent-versus-signals-to-the-run">
  ## Signaux envoyés à l’agent ou au run
</div>

Comprendre la différence entre un signal envoyé à l’agent et un signal envoyé au run d’entraînement vous aide à éviter les processus orphelins et les comportements inattendus. Si vous envoyez un signal au processus de l’agent (et non au processus enfant d’entraînement), l’agent peut s’arrêter tandis que le processus enfant continue de s’exécuter comme processus orphelin. Ce processus orphelin peut continuer à afficher du texte dans votre terminal, et le shell peut ne pas afficher de nouveau prompt tant que vous n’avez pas appuyé sur Entrée.

Sauf si vous utilisez `--forward-signals` avec l’agent CLI, l’arrêt de l’agent ne garantit pas l’arrêt du processus enfant d’entraînement.

Pour vérifier que l’agent s’est arrêté, utilisez une commande du système d’exploitation comme `ps -p [AGENT-PID]` ou `pgrep -f "wandb agent"` au lieu de vous fier à l’apparition du prompt.

<div id="reference-mark_preempting-and-final-run-state">
  ## Référence : `mark_preempting()` et l’état final du run
</div>

Le tableau suivant résume comment l’état du run dépend du moment où vous appelez `mark_preempting()` et de la façon dont le processus se termine. Il suppose que vous utilisez la CLI [`wandb agent`](/fr/models/ref/cli/wandb-agent) avec votre programme d’entraînement comme sous-processus.

| Scénario                                                            | Sans `mark_preempting()`        | Le gestionnaire de signal appelle `mark_preempting()` et quitte avec un code non nul | `mark_preempting()` toujours appelé juste après `init()` |
| ------------------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------- |
| Le run se termine normalement avec le code de sortie 0              | FINISHED                        | FINISHED                                                                             | FINISHED                                                 |
| Le run échoue avec un code de sortie non nul                        | FAILED                          | FAILED                                                                               | PREEMPTED                                                |
| Le run reçoit `SIGKILL`                                             | CRASHED après environ 5 minutes | CRASHED après environ 5 minutes (signal non interceptable)                           | PREEMPTED après environ 5 minutes                        |
| Le run reçoit `SIGINT`                                              | KILLED                          | PREEMPTED (avec un gestionnaire `SIGINT`)                                            | PREEMPTED                                                |
| Le run reçoit un autre signal (par exemple, `SIGTERM` ou `SIGUSR1`) | CRASHED après environ 5 minutes | PREEMPTED (avec un gestionnaire approprié)                                           | PREEMPTED après environ 5 minutes                        |

Si vous appelez `mark_preempting()` uniquement dans un gestionnaire de signal, vous ne couvrez pas les cas où ce gestionnaire n’est jamais exécuté, comme avec `SIGKILL`.

Si vous appelez toujours `mark_preempting()` immédiatement après `wandb.init()`, W\&B peut traiter tout échec comme une préemption et pourrait remettre le run en file d’attente de manière répétée, y compris en cas de bug ou de mauvaise configuration.

Pour les environnements avec un signal de préemption bien défini, l’approche habituelle consiste à utiliser un **gestionnaire de signal** qui appelle `mark_preempting()` et quitte avec un code non nul, plutôt qu’un appel inconditionnel après `init()`.
