Logger degli esperimenti

Ogni famiglia addestrabile emette quattro eventi di addestramento. I logger integrati sono oggetti callback in ascolto su quegli stessi eventi, quindi l'integrazione con un backend e un hook scritto da te usano una sola interfaccia.

Attivare un logger

loggers= accetta un nome registrato, un'istanza configurata o un iterabile che mescola le due cose.

Per nome
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt")model.train(data="coco8.yaml", epochs=10, loggers="tensorboard")
Istanza configurata
from libreyolo import LibreYOLOfrom libreyolo.training import MLflowLogger model = LibreYOLO("LibreYOLO9s.pt")model.train(    data="coco8.yaml",    epochs=10,    loggers=[MLflowLogger(tracking_uri="sqlite:///mlflow.db"), "tensorboard"],)

I nomi non distinguono maiuscole e minuscole. L'insieme registrato è tensorboard, mlflow, wandb, comet, clearml, neptune, dvclive e dvc, dove l'ultimo è un alias di dvclive. Qualsiasi altro valore solleva subito un errore ed elenca i nomi validi. Non esiste un valore che li abiliti tutti, e non c'è nessun flag da CLI: loggers= è un argomento Python.

Cosa registra ogni backend

Scrivono tutti gli stessi nomi di metrica, quindi la dashboard ha lo stesso aspetto qualunque backend tu scelga:

ChiaveValore
train/lossla loss media di addestramento dell'epoca
train/loss/<component>ogni componente della loss che la famiglia riporta
lr/<group>il learning rate di ogni gruppo di parametri dell'ottimizzatore
val/<metric>ogni metrica di validazione, con il prefisso metrics/ rimosso
time/epoch_secondsil tempo reale trascorso durante l'epoca

Lo step è l'epoca contata a partire da 1. La configurazione di addestramento completamente risolta viene registrata come parametri all'avvio dell'addestramento, e il nome del run è per impostazione predefinita <family><size>-<task>, per esempio yolo9s-detect.

Alla fine dell'addestramento i backend che supportano gli artefatti caricano results.csv, train_config.yaml e summary.json quando esistono, più weights/best.pt con log_checkpoints=True. TensorBoard non carica niente, perché non ha un concetto di artefatto. Nessun logger carica le immagini dei grafici di validazione.

Comportamento in caso di errore

Un pacchetto del backend mancante solleva un errore alla costruzione, indicando il comando di installazione, perché chiedere un logger e non ottenere niente in silenzio nasconde un bug.

Un errore del backend durante il run fa l'opposto. La prima eccezione sollevata da un handler disabilita quel logger per il resto del run, la registra nel log, chiude il run del backend segnandolo come fallito, e l'addestramento continua. Un tracking server che va giù non ti costa l'addestramento.

I backend

Ognuno richiede il proprio extra.

NomeExtraCostruttore
tensorboardlibreyolo[tensorboard]TensorBoardLogger(log_dir=None)
mlflowlibreyolo[mlflow]MLflowLogger(tracking_uri, experiment_name, run_name, log_artifacts=True, log_checkpoints=False)
wandblibreyolo[wandb]WandbLogger(project, name, entity, log_checkpoints=False)
cometlibreyolo[comet]CometLogger(project_name, workspace, name, api_key, online, log_artifacts=True, log_checkpoints=False)
clearmllibreyolo[clearml]ClearMLLogger(project_name="LibreYOLO", task_name, tags, output_uri, log_artifacts=True, log_checkpoints=False)
neptunelibreyolo[neptune]NeptuneLogger(project, api_token, name, run_id, tags, mode, capture_console=False, log_artifacts=True, log_checkpoints=False)
dvclive, dvclibreyolo[dvclive]DVCLiveLogger(log_dir, resume, report, save_dvc_exp=False, dvcyaml=None, monitor_system=False, log_checkpoints=False)

Importa le classi da libreyolo.training.

Note specifiche dei singoli backend che vale la pena conoscere prima del primo run:

I file di evento di TensorBoard finiscono per impostazione predefinita in <save_dir>/tensorboard. Li visualizzi con tensorboard --logdir runs/train.

MLflow 3.x ha deprecato lo store su file locale ./mlruns e solleva un errore se non è impostato MLFLOW_ALLOW_FILE_STORE=true. Per un tracking locale senza server, passa invece un URI di database, come nello snippet qui sopra, e leggilo con mlflow ui --backend-store-uri sqlite:///mlflow.db.

Weights & Biases ripiega sulla variabile d'ambiente WANDB_PROJECT e poi su libreyolo. Comet ripiega su COMET_PROJECT_NAME e poi su libreyolo, e prende le credenziali dalla propria configurazione; online=False dà un esperimento offline. ClearML crea un task nuovo, riporta la configurazione sotto TrainConfig e disabilita la cattura automatica del framework, così le metriche non vengono riportate due volte. Neptune usa il client attuale neptune-scale invece del pacchetto legacy, e mode="offline" registra in locale.

DVCLive scrive in <save_dir>/dvclive. Costruisce il suo albero di riepilogo a partire da /, e non può tenere un float in un percorso che è anche un genitore, quindi train/loss/box viene scritto come train/loss.box mentre train/loss mantiene il suo nome. LibreYOLO disattiva anche i soliti default di DVCLive, che salvano un esperimento DVC e scrivono un dvc.yaml nella radice, così un logger attivato esplicitamente non crea nessuno stato di controllo di versione fuori dalla directory del run; passa save_dvc_exp=True o un dvcyaml= esplicito per riaverli.

Neptune è escluso di proposito da libreyolo[all]: il suo client stabile richiede protobuf sotto la 7 mentre l'extra TFLite richiede protobuf 7. Installa libreyolo[neptune] in un ambiente senza l'extra TFLite.

Scrivere un callback

Gli stessi quattro eventi guidano tutto.

Una semplice funzione
from libreyolo import LibreYOLOfrom libreyolo.training import TrainEpochEvent  def on_epoch(event: TrainEpochEvent) -> None:    print(f"epoch {event.epoch}/{event.total_epochs} loss={event.train_loss:.4f}")  model = LibreYOLO("LibreYOLO9s.pt")model.train(data="coco8.yaml", epochs=10, callbacks=on_epoch)
Un oggetto con più hook
from libreyolo import LibreYOLOfrom libreyolo.training import TrainEndEvent, TrainEpochEvent, TrainStartEvent  class RunLog:    def on_train_start(self, event: TrainStartEvent) -> None:        print(f"{event.model_family}{event.model_size} -> {event.save_dir}")     def on_train_epoch_end(self, event: TrainEpochEvent) -> None:        if event.is_best:            print(f"new best at epoch {event.epoch}: {event.best_metric}")     def on_train_end(self, event: TrainEndEvent) -> None:        print(f"done in {event.total_seconds:.0f}s")  model = LibreYOLO("LibreYOLO9s.pt")model.train(data="coco8.yaml", epochs=10, callbacks=RunLog())

EventoQuandoCosa porta
TrainStartEventdopo il setup, prima dell'epoca 1start_epoch, total_epochs, model_family, model_size, task, save_dir, config
TrainEpochEventdopo ogni epoca, addestramento e validazioneepoch, train_loss, train_loss_items, lr, val_metrics, validated, is_best, current_metric, best_metric, best_epoch, epoch_seconds
TrainEndEventdopo che l'addestramento è terminatocompleted_epochs, final_loss, best_metric, best_epoch, total_seconds, results
TrainExceptionEventse l'addestramento solleva un'eccezioneepoch, exception, exception_type, exception_message, elapsed_seconds

Un semplice callable riceve solo TrainEpochEvent. Un oggetto può implementare un sottoinsieme qualsiasi di on_train_start, on_train_epoch_end, on_train_end e on_train_exception; i metodi mancanti vengono saltati.

TrainStartEvent.config è la configurazione completamente risolta, i kwargs dell'utente uniti ai default della famiglia, come mapping di sola lettura. Gli eventi sono dataclass frozen e i loro mapping sono di sola lettura, quindi un callback non può cambiare il run scrivendoci dentro.

Un'eccezione sollevata da on_train_start, on_train_epoch_end o on_train_end si propaga e termina il run. Solo on_train_exception è protetto, così non può mascherare il fallimento originale.

Nell'addestramento multi-GPU i callback vengono invocati solo sul rank 0. Con lo spawn DDP automatico devono anche essere serializzabili con pickle, il che significa una classe o una funzione a livello di modulo invece di una closure o di una lambda. Vedi Addestramento multi-GPU.

Cosa scrive comunque ogni run

Tre file finiscono nella directory del run senza alcuna configurazione, con qualsiasi famiglia:

FileScrittoContenuto
status.jsonin modo atomico, a ogni epoca e all'avvio, alla fine e in caso di fallimentostate con valore running, completed o failed, current_epoch, total_epochs, progress, eta_seconds, le metrics più recenti, best_metric, best_epoch, e un oggetto error in caso di fallimento
metrics.jsonlin append una volta per epocauna riga JSON per epoca, con lo stesso schema di results.csv
train.login tempo realel'output su console del run

status.json è la lettura a basso costo per uno script o un agente che controlla periodicamente un run, e la scrittura atomica fa sì che un lettore non veda mai un file scritto a metà.

results.csv e summary.json sono separati e dipendono dalla famiglia. Vengono scritti per YOLOv9, YOLOv9-E2E, YOLOv9-P2, YOLOv7, YOLO-NAS, RF-DETR, EC e DINOv2, e non per le altre famiglie. results.csv riceve una riga per epoca con le componenti della loss, le metriche di validazione e i learning rate come colonne, e la sua intestazione si allarga quando compare una nuova colonna. Alla ripresa viene troncato alle righe precedenti all'epoca da cui si riparte, invece di duplicarle.

Accanto a questi, il trainer scrive sempre train_config.yaml al setup e i checkpoint sotto weights/.

Seguire un run in tempo reale

Seguire un run nel browser
libreyolo monitor                     # il run più recente sotto runs/libreyolo monitor runs/train/exp      # un run specifico

libreyolo monitor espone nel browser una dashboard costruita sui file qui sopra usando solo la libreria standard: grafici delle metriche, la coda del log e le eventuali immagini di validazione, che si aggiornano mentre il run è attivo. È di sola lettura e non tocca mai il processo di addestramento, quindi si aggancia a un run in corso, riapre uno finito o ispeziona uno andato in crash.

Correlati

Verificato con LibreYOLO v1.5.0.