Addestrare modelli su volumi, nuvole di punti e video
Addestra una 3D U-Net su maschere CT dipinte, esegui il tuo detector OpenPCDet su cuboidi lidar e addestra un tracker video, ciascuno valutato su item esclusi dall'addestramento.
Novità nella v2.14.0
Questo tutorial addestra tre modelli su dati 3D e in movimento a partire dalle annotazioni di uno studio Potato. volume-unet addestra una 3D U-Net che segmenta volumi CT e MR, external-3d esegue un detector lidar che costruisci e fai girare tu, e video-track addestra un tracker di oggetti nei video. La pagina è pensata per i team che annotano già volumi, nuvole di punti o video in Potato e vogliono un modello che pre-etichetti il resto. Per ogni trainer la pagina descrive lo schema che produce i dati di addestramento, una configurazione che supera potato validate, che cosa fa un'esecuzione, il punteggio held-out e come le predizioni arrivano agli annotatori. Il tutorial sul primo modello illustra la pagina Training con un classificatore di testo che non richiede GPU, e questo tutorial presuppone quel percorso.
Riepilogo dei trainer
Tutti e tre i trainer addestrano uno schema per esecuzione, valutano il modello sugli item val e test esclusi dall'addestramento e scrivono le predizioni come pre-etichette che gli annotatori modificano. volume-unet e i detector dietro external-3d richiedono in pratica una GPU, quindi le configurazioni di quei due trainer inviano le esecuzioni a un server GPU via ssh, come configura il tutorial sull'addestramento remoto.
| Trainer | Schema | Item | Installazione sulla macchina di addestramento | Punteggio principale |
|---|---|---|---|---|
volume-unet | volume_annotation (maschere dipinte) | volumi NIfTI, serie DICOM | pip install "potato-annotation[train-vision,volumes]"; in pratica una GPU | mean_dice |
external-3d | spatial_annotation (cuboidi) | nuvole di punti | il tuo detector (OpenPCDet, MMDetection3D) | map_3d_50 |
video-track | video_annotation in modalità tracking o combined | video | pip install "potato-annotation[train-vision]" opencv-python | idf1 |
Ogni trainer legge i media da file su disco. Il percorso di un media deve puntare dentro la cartella del task o media_directory, e gli item il cui media è un URL http(s) restano fuori dall'addestramento e dalla predizione.
Segmentare volumi CT e MR con volume-unet
volume-unet impara le maschere che gli annotatori dipingono in uno schema volume_annotation e dipinge maschere sui volumi che nessuno ha ancora segmentato. Lo schema mostra un file NIfTI o una serie DICOM in tre viste collegate (assiale, coronale e sagittale), e gli annotatori dipingono ogni etichetta con il pennello o chiudendo un poligono. Il campo dell'item indicato da source_field contiene un file .nii o .nii.gz, oppure una cartella con una serie DICOM, relativo a media_directory. Un riquadro disegnato su una slice non ha maschera, quindi una risposta con soli riquadri non è un bersaglio di addestramento. Potato è un software di ricerca privo di autorizzazioni cliniche o regolatorie, e un modello addestrato qui propone maschere che gli annotatori correggono.
La configurazione seguente segmenta la milza e il fegato, addestra su un server GPU e pre-etichetta ogni volume fuori dalla partizione train:
annotation_task_name: "Spleen segmentation"
task_dir: .
media_directory: media
data_files:
- data/scans.jsonl
item_properties:
id_key: id
text_key: volume
output_annotation_dir: annotation_output
user_config:
allow_all_users: true
annotation_schemes:
- annotation_type: volume_annotation
name: organs
description: "Paint the spleen and the liver on every slice where each organ appears."
source_field: volume
labels:
- {name: Spleen, color: "#e6553a", key_value: "1"}
- {name: Liver, color: "#3a8ee6", key_value: "2"}
tools: [brush, eraser, polygon]
default_window: Soft tissue
model_training:
enabled: true
backend: gpu-server
backends:
gpu-server:
type: ssh
host: gpu.example.edu
python: /opt/conda/bin/python3
gpus: auto
min_free_gpu_mb: 8000
trainer_params:
volume-unet:
patch: 96
writeback:
schemas: [organs]
min_confidence: 0.6
include_unlabeled: trueOgni riga dei dati indica un volume:
{"id": "case_001", "volume": "case_001.nii.gz"}
{"id": "case_002", "volume": "case_002_ct"}Il valore patch imposta il lato della patch cubica su cui la rete si addestra e, con i tre livelli predefiniti, deve essere un multiplo di 4 (2 elevato a levels meno 1). Una patch di 96 voxel dà alla rete più contesto attorno a ciascun voxel rispetto ai 64 predefiniti e richiede più memoria GPU. Con min_free_gpu_mb: 8000, Potato avvia un'esecuzione solo su una GPU con almeno 8.000 MB liberi. Potato calcola la probabilità media del modello dentro la maschera di ciascuna etichetta dipinta e riporta la più bassa di queste medie come confidenza del volume. Con min_confidence: 0.6, un volume in cui una qualsiasi struttura è stata dipinta con una probabilità media inferiore a 0.6 non riceve alcuna pre-etichetta.
Fasi di addestramento in un'esecuzione di volume-unet
Un'esecuzione legge ogni volume annotato nell'ordine dei voxel in cui dipinge il widget e trasforma le maschere in un'etichetta per voxel. Dove due etichette si sovrappongono, prevale l'ultima nell'ordine delle etichette del bundle, come nell'esportazione NIfTI. Le intensità vengono troncate ai percentili 0.5 e 99.5 di ciascun volume e standardizzate. La rete è una 3D U-Net in puro torch (tre livelli, instance norm), addestrata con entropia incrociata più Dice morbido su 100 patch casuali per epoca. Metà delle patch è centrata su un voxel dipinto, così le strutture piccole compaiono nell'addestramento. Dopo ogni epoca l'esecuzione calcola il Dice medio sui volumi val e conserva l'epoca migliore, e si ferma dopo 10 epoche senza un punteggio migliore. La predizione fa scorrere la patch sull'intero volume con sovrapposizione di mezza patch e media le probabilità.
La partizione train richiede almeno tre volumi annotati (min_instances). Un volume la cui forma dipinta differisce dalla forma del file resta fuori con un avviso nel log dell'esecuzione, e lo stesso vale per un volume il cui file manca. Con più lettori sullo stesso volume, l'esecuzione si addestra sulle maschere del lettore a cui aggregation risolve, perché gli aggregatori confrontano risposte intere. Segment CT Volumes With Several Readers and STAPLE tratta l'accordo tra lettori e una maschera di consenso costruita fuori da Potato.
Punteggio held-out per i volumi
Potato calcola il coefficiente di Dice per ogni etichetta su ogni volume escluso dall'addestramento (dice_<label>) e riporta la media su tutti i volumi e le etichette come mean_dice. Un'etichetta che il modello ha omesso in un volume che la contiene ottiene 0. Il trainer sceglie l'epoca migliore su val, quindi la tabella Runs prende il punteggio principale da test (test_mean_dice). L'elenco completo dei punteggi per etichetta si trova nella pagina dell'esecuzione.
Pre-etichette a maschera sulle viste delle slice
Le maschere predette tornano nella forma propria del widget, per etichetta e per slice assiale, contrassegnate con prefilled_from: model, e compaiono sulle viste delle slice quando un annotatore apre il volume. L'annotatore corregge le maschere con il pennello e la gomma, e le maschere corrette vengono salvate come risposta di quell'annotatore. Gli item nella partizione train dell'esecuzione non ricevono mai una pre-etichetta. Senza include_unlabeled, un'esecuzione predice solo sui volumi val e test dell'esecuzione.
Rilevare cuboidi 3D con external-3d
external-3d passa le scansioni annotate a un detector 3D che installi sull'host di addestramento, esegue i tuoi comandi di addestramento e di predizione e rilegge i box predetti come cuboidi. Potato non include un detector per nuvole di punti. I due detector per cui il trainer è costruito, OpenPCDet e MMDetection3D, richiedono operatori CUDA compilati sull'host GPU. Dal lato di Potato, il trainer richiede soltanto numpy.
I dati di addestramento provengono da uno schema spatial_annotation con lo strumento cuboid_3d. Gli annotatori posizionano box orientati su scansioni PCD, PLY, LAS o KITTI .bin, memorizzati in metri nel sistema di riferimento della scansione (vedi Nuvole di punti 3D e cuboidi). I cuboidi sono gli unici bersagli, quindi punti, polilinee e segmenti di punti nello stesso schema vengono ignorati. Ogni item è una scansione, e una sequenza di frame con id di traccia non è un bersaglio di addestramento.
Cartella del dataset scritta per il detector
Prima che il comando di addestramento venga eseguito, Potato scrive le scansioni in {dataset}, una cartella chiamata dataset dentro la cartella dell'esecuzione, nel layout di dataset personalizzato di OpenPCDet:
points/<id>.npycontiene una scansione come righe float32x y z intensity, con intensità 0 quando il file non ne ha.labels/<id>.txtcontiene una riga per cuboide per le scansionitraineval, comex y z dx dy dz heading label. L'heading è la rotazione attorno a +z in radianti, e beccheggio e rollio non vengono conservati. Uno spazio nel nome di un'etichetta diventa un trattino basso.ImageSets/train.txt,val.txtepredict.txtelencano gli id delle scansioni.predict.txtelenca le scansionivaletest, più quelle non etichettate quandowriteback.include_unlabeledè attivo.
Ogni <id> corrisponde ai primi 16 caratteri esadecimali dello SHA-1 dell'id dell'item, perché gli id degli item possono contenere caratteri che un nome di file non può avere. Le etichette delle scansioni test non vengono mai scritte nella cartella. Potato scrive una scansione georeferenziata relativamente all'origine della scansione, perché le coordinate georeferenziate sono troppo grandi per un float32, e sposta i box predetti dello stesso scostamento.
File di predizione riletti dal detector
Il comando di predizione scrive un file per scansione in {predictions}/<id>.txt, un box per riga, come x y z dx dy dz heading label score. Il punteggio è facoltativo. Potato esclude una riga che non si interpreta, contiene nan o inf, indica un'etichetta che lo schema non ha oppure descrive un box senza dimensioni, e il log dell'esecuzione conta le righe escluse. I box restanti tornano come oggetti cuboid_3d contrassegnati con source: model, e il punteggio di ciascun box diventa la confidenza del box. Potato imposta la confidenza di una scansione al punteggio più basso tra i suoi box, e min_confidence si applica a quel valore.
Comandi di addestramento e di predizione
Entrambi i comandi sono impostati come parametri del trainer. Potato divide ogni comando in parole come farebbe una shell, sostituisce {dataset}, {workdir} (la cartella dell'esecuzione) e {predictions} con i percorsi corrispondenti ed esegue il comando nella cartella dell'esecuzione senza una shell. Le altre parentesi graffe in un comando restano come scritte. Pipe, && e $VARIABLES arrivano al programma come parole letterali, quindi il lavoro in più passaggi va in uno script. Aggiungi questo blocco alla configurazione dello studio sulle nuvole di punti:
model_training:
enabled: true
backend: gpu-server
backends:
gpu-server:
type: ssh
host: gpu.example.edu
python: /opt/conda/envs/pcdet/bin/python
gpus: auto
trainer_params:
external-3d:
train_command: "/opt/pcdet-jobs/train.sh {dataset} {workdir}"
predict_command: "/opt/conda/envs/pcdet/bin/python /opt/pcdet-jobs/predict.py {dataset} {workdir} {predictions}"
timeout_s: 43200
writeback:
schemas: [objects]
min_confidence: 0.3
include_unlabeled: trueI percorsi si trovano sull'host di addestramento, dove risiede anche la cartella dell'esecuzione. timeout_s limita ogni comando, e qui 12 ore sostituiscono le 24 predefinite. Un comando di addestramento che termina con codice diverso da zero fa fallire l'esecuzione, e l'errore dell'esecuzione mostra le ultime righe dell'output del comando.
train.sh è il tuo script. Per OpenPCDet, lo script punta la configurazione del dataset personalizzato di OpenPCDet a {dataset}, costruisce i file di info con python -m pcdet.datasets.custom.custom_dataset create_custom_infos <dataset config>, esegue lo script di addestramento di OpenPCDet e salva il checkpoint sotto {workdir}. I nomi delle classi nella configurazione del detector devono coincidere con i nomi delle etichette così come sono scritti in labels/. La guida al dataset personalizzato di MMDetection3D legge le nuvole di punti solo come file .bin, quindi per MMDetection3D lo script scrive prima ogni points/<id>.npy come file .bin. Lascia vuoto train_command per saltare l'addestramento, e predict_command esegue allora un modello che hai addestrato altrove.
predict.py legge gli id in predict.txt, esegue il detector su ogni scansione e scrive i box. Le funzioni load_model e detect sono il tuo codice attorno al detector che hai addestrato:
"""predict.py DATASET WORKDIR PREDICTIONS: one line per box, one file per scan."""
import os
import sys
import numpy as np
from my_detector import load_model, detect # your code around OpenPCDet or MMDetection3D
dataset, workdir, out = sys.argv[1:4]
model = load_model(os.path.join(workdir, "output")) # where train.sh saved the checkpoint
os.makedirs(out, exist_ok=True)
with open(os.path.join(dataset, "ImageSets", "predict.txt")) as fh:
scan_ids = fh.read().split()
for scan_id in scan_ids:
points = np.load(os.path.join(dataset, "points", scan_id + ".npy")) # float32, (N, 4)
with open(os.path.join(out, scan_id + ".txt"), "w") as fh:
for x, y, z, dx, dy, dz, heading, label, score in detect(model, points):
fh.write(f"{x} {y} {z} {dx} {dy} {dz} {heading} {label} {score}\n")Senza un train_command, il comando di predizione è l'intera esecuzione, e un comando di predizione che termina con codice diverso da zero fa fallire l'esecuzione. Conta come fallito anche un comando di predizione che termina con codice 0 ma non scrive alcun <id>.txt per nessuna scansione, o scrive solo righe che Potato scarta, perché un'esecuzione valutata su quell'output sembrerebbe un modello che non ha trovato nulla. Un file vuoto è una scansione senza oggetti. Dopo un comando di addestramento, un comando di predizione che fallisce non fa fallire l'esecuzione. L'esecuzione termina con un avviso, "Prediction pass failed after 0 prediction(s)", e le ultime righe del comando, e non ha né predizioni né punteggio held-out. Cerca quell'avviso nel log dell'esecuzione quando un'esecuzione terminata mostra "not scored".
Punteggio held-out per i cuboidi
Potato calcola l'average precision 3D per etichetta con un IoU volumetrico di 0.5 (ap_3d_50_<label>). L'IoU è esatto per cuboidi orientati a qualsiasi rotazione. Potato riporta anche la media sulle etichette a IoU 0.25 e 0.5, e precisione, richiamo e F1 a 0.5. Il punteggio principale è map_3d_50. Con un train_command, la tabella Runs prende il punteggio principale prima da test, perché al comando di addestramento vengono consegnate le etichette val in {dataset} ed esso può scegliere un checkpoint su quelle etichette. Senza un train_command, il modello non ha mai visto gli item val dello studio, e il punteggio principale proviene da val.
Pre-etichette a cuboide nel visualizzatore di nuvole di punti
I cuboidi predetti compaiono nel visualizzatore di nuvole di punti come box modificabili, e un box modificato viene salvato come risposta dell'annotatore. Potato controlla ogni oggetto predetto prima che il visualizzatore lo disegni, ed esclude un oggetto la cui geometria non si interpreta, un cuboide senza dimensioni e un'etichetta che lo schema non ha. How to Annotate 3D Point Clouds tratta il lato dell'annotazione e l'accordo sui box 3D.
Tracciare oggetti nei video con video-track
video-track impara dalle tracce che gli annotatori disegnano in uno schema video_annotation in modalità tracking o combined, e predice tracce sui video che nessuno ha tracciato. Una traccia è un'etichetta e un box a ogni keyframe, e il widget interpola il box tra i keyframe. Uno schema video_annotation in un'altra modalità memorizza segmenti anziché tracce, e video-track non viene proposto per quello schema.
annotation_task_name: "Pedestrian tracking"
task_dir: .
media_directory: media
data_files:
- data/clips.jsonl
item_properties:
id_key: id
text_key: video
output_annotation_dir: annotation_output
user_config:
allow_all_users: true
annotation_schemes:
- annotation_type: video_annotation
name: tracks
description: "Track every pedestrian and cyclist with a box at each keyframe."
source_field: video
mode: tracking
video_fps: 25
labels:
- {name: pedestrian, color: "#FF6B6B"}
- {name: cyclist, color: "#45B7D1"}
tracking_options:
interpolation: linear
auto_advance_frames: 5
frame_stepping: true
model_training:
enabled: true
trainer_params:
video-track:
frame_fps: 2
predict_fps: 5
max_age: 4
writeback:
schemas: [tracks]
include_unlabeled: trueOgni riga dei dati indica una clip sotto media_directory:
{"id": "clip_001", "video": "/media/clip_001.mp4"}
{"id": "clip_002", "video": "/media/clip_002.mp4"}Imposta video_fps al frame rate reale delle clip. I keyframe sono numerati per frame, e il trainer numera i keyframe predetti al video_fps dello schema (30 se non impostato). Il trainer legge il box su un frame campionato secondo l'impostazione interpolation di ciascuna traccia (lineare, Catmull-Rom cubica o costante). I box da cui il modello impara seguono quindi il percorso che gli annotatori hanno visto. Come nel widget, una traccia con un solo keyframe viene mantenuta a ogni frame, e una traccia a cui il widget ha dato uno startFrame e un endFrame scorre tra quei due frame. Un keyframe fuori da quei due frame viene comunque mostrato sul proprio frame. predict_fps: 5 esegue il rilevamento su cinque frame al secondo invece di due. Cinque frame al secondo danno a un oggetto veloce più keyframe e costano più passaggi del detector. Una traccia termina dopo più di max_age frame campionati senza un rilevamento corrispondente, quindi max_age: 4 a 5 frame al secondo permette a una traccia di sopravvivere a 0,8 secondi di rilevamenti mancati. Un pedone che passa dietro un palo mantiene una sola traccia quando il detector lo ritrova entro 0,8 secondi. La configurazione addestra sulla macchina che esegue Potato (il backend local), e il backend gpu-server della configurazione per i volumi funziona anche per video-track.
Addestramento e collegamento delle tracce in un'esecuzione di video-track
Un'esecuzione campiona ogni video annotato a frame_fps frame al secondo, dall'inizio all'ultimo keyframe, e legge ogni frame campionato con OpenCV. I pixel della canvas dipendono dallo schermo dell'annotatore, quindi il trainer legge il box di ciascuna traccia su quel frame come frazione dell'immagine. Un frame campionato senza alcuna traccia resta nell'addestramento come esempio di scena vuota. L'esecuzione esegue il fine-tuning di un Faster R-CNN di torchvision su quei frame, con i parametri e i valori predefiniti di torchvision-detect. Il detector parte dai pesi COCO di torchvision. Dopo ogni epoca l'esecuzione calcola la mAP dei box a IoU 0.5 sui frame val (frame_map_50) e conserva l'epoca migliore.
La predizione campiona ogni video a predict_fps, esegue il rilevamento su ogni frame campionato e collega i rilevamenti in tracce. I rilevamenti di ciascun frame vengono associati uno a uno alle tracce attive con la stessa etichetta, in base alla sovrapposizione con l'ultimo box di ciascuna traccia, e un'associazione richiede un IoU di almeno match_iou (0.3). Un rilevamento non associato con un punteggio di almeno new_track_score (0.5) avvia una nuova traccia. Il trainer scarta, prima della scrittura e del calcolo del punteggio, una traccia prevista con meno di min_track_keyframes rilevamenti (2 per impostazione predefinita). Altrimenti il widget manterrebbe a ogni frame una traccia di un solo rilevamento. Ogni rilevamento diventa un keyframe, quindi il widget interpola tra i keyframe del modello come fa tra quelli di un annotatore.
La partizione train richiede almeno due video annotati (min_instances). Un video che OpenCV non riesce a decodificare resta fuori con un avviso che nomina l'item, e l'esecuzione continua con gli altri.
Punteggio held-out per le tracce
Potato confronta le tracce predette con quelle annotate su frame campionati a 2 al secondo lungo ogni video escluso dall'addestramento, più ogni keyframe annotato. I punteggi sono MOTA, IDF1, scambi di identità, mancati e falsi positivi, con un box considerato trovato a IoU 0.5. MOTA addebita mancati, falsi positivi e scambi di identità rispetto al numero di box annotati. IDF1 è il punteggio F1 dei box associati all'identità corretta lungo l'intero video, ed è il punteggio principale (idf1). Il trainer sceglie l'epoca migliore su val, quindi la tabella Runs prende il punteggio principale da test.
Pre-etichette di traccia nel widget video
Le tracce predette riempiono il widget video come tracce con keyframe contrassegnati come output dell'IA e con il punteggio del detector. Il widget converte i box nei pixel della propria canvas quando disegna le tracce, e l'annotatore corregge le tracce come qualsiasi altra traccia. La confidenza di un video è il punteggio medio dei keyframe del modello. In modalità combined una pre-etichetta contiene solo tracce, e l'annotatore aggiunge i segmenti. How to Annotate Video Object Tracking tratta il lato dell'annotazione, l'occlusione e l'accordo temporale.
Avviare un'esecuzione e controllare il risultato
Riavvia il server dopo aver aggiunto model_training, apri /admin/training, scegli lo schema e il trainer e fai clic su Train now. La pagina propone solo i trainer adatti a ciascuno schema, ed elenca un trainer non installato come "not installed", con il comando di installazione. Per controllare un server GPU prima della prima esecuzione, esegui:
potato training check gpu-server config.yamlIl controllo riporta la versione di Python remota, ogni GPU con la sua memoria libera, la GPU che un'esecuzione otterrebbe e i pacchetti di addestramento che mancano all'interprete remoto. Un'esecuzione può partire anche dall'API, con parametri per esecuzione che sostituiscono trainer_params:
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs \
-d '{"trainer": "video-track", "schemas": ["tracks"], "params": {"predict_fps": 10}}'Quando l'esecuzione termina, la tabella Runs elenca il punteggio held-out, con la partizione e la metrica in un tooltip. Le pre-etichette ancorano gli annotatori, quindi attiva include_unlabeled in un nuovo studio solo quando il punteggio held-out giustifica la visualizzazione delle risposte del modello. Il tutorial sul modello nel ciclo tratta come servire per prime le pre-etichette con la confidenza più bassa e come riaddestrare sulle correzioni, e Configurazione e route dell'addestramento di modelli elenca ogni chiave, i backend e i controlli del write-back.
Ulteriori letture
- Addestramento di modelli sulle tue annotazioni
- I trainer di Potato, raggruppati per compito
- Il tutorial sui modelli di visione, per rilevamento e segmentazione 2D
- Tracciamento video e propagazione delle maschere
- Annotazione video
- Annotazione di immagini mediche
- Consenso delle maschere con STAPLE
- Riferimento upstream per
volume-unet,external-3devideo-track
Riferimenti
- Bernardin, K., and Stiefelhagen, R. (2008). Evaluating Multiple Object Tracking Performance: The CLEAR MOT Metrics. EURASIP Journal on Image and Video Processing. https://doi.org/10.1155/2008/246309
- Çiçek, Ö., Abdulkadir, A., Lienkamp, S. S., Brox, T., and Ronneberger, O. (2016). 3D U-Net: Learning Dense Volumetric Segmentation from Sparse Annotation. MICCAI 2016. https://arxiv.org/abs/1606.06650
- Ristani, E., Solera, F., Zou, R. S., Cucchiara, R., and Tomasi, C. (2016). Performance Measures and a Data Set for Multi-Target, Multi-Camera Tracking. ECCV 2016 Workshop on Benchmarking Multi-Target Tracking. https://arxiv.org/abs/1609.01775