7.12. Post-processori¶
Una rete di rilevamento non emette box. Emette uno o più tensori la cui disposizione dipende dall’architettura su cui il modello è stato addestrato – un tensore 2-D di predizioni candidate per un rilevatore della famiglia YOLO, una coppia di tensori (boxes, scores) per un rilevatore MediaPipe, una lista piatta di coordinate di keypoint per una rete di posa. L’applicazione non può leggere direttamente nessuno di questi; ciò che vuole – una lista di box, una lista di keypoint, una suddivisione per classe – deve essere decodificato dal tensore grezzo.
Quel decodificatore è un post-processore. Il modulo ml.postprocessing li raggruppa per ecosistema di origine.
7.12.1. Darknet¶
ml.postprocessing.darknet decodifica i modelli dell’era originale di YOLO. YOLO v2 ha introdotto le idee di grid e anchor che la maggior parte dei rilevatori successivi ha ereditato in qualche forma, quindi la disposizione v2 è il punto di partenza più pulito.
YOLO v2 inizia dividendo l’immagine di input in una griglia grossolana – una disposizione 13 per 13 per l’input canonico da 416 pixel, più piccola per i modelli più piccoli – e addestra la rete in modo che ogni cella della griglia sia responsabile del rilevamento di qualsiasi oggetto il cui centro ricada al suo interno. La disposizione spaziale del tensore di output rispecchia la disposizione dell’input: una posizione nell’output per ogni cella nell’immagine.
In ogni cella della griglia, la rete non predice un box dal nulla. Sceglie tra diverse forme di riferimento pre-selezionate chiamate anchor – coppie fisse (width, height) derivate offline raggruppando le dimensioni dei box nel set di addestramento in modo che coprano gli oggetti tipici che il modello dovrebbe vedere. Il compito della rete in ogni cella è predire, per ciascun anchor, un piccolo offset rispetto al centro del box all’interno della cella, un fattore di scala sulla larghezza e altezza dell’anchor, un punteggio di objectness (la probabilità che ci sia qualcosa) e un vettore di probabilità per classe. Una griglia 13 per 13 con i 5 anchor predefiniti e 20 classi emette quindi 13 * 13 * 5 * (4 + 1 + 20) = 21,125 numeri per inferenza.
YoloV2 decodifica quella disposizione: percorre le celle, applica gli offset e i fattori di scala di ciascun anchor per recuperare le coordinate assolute del box, combina l’objectness con la probabilità di classe per ottenere un punteggio per classe, applica la soglia e invia i sopravvissuti alla NMS. La classe accetta un argomento del costruttore anchors= quando il modello è stato addestrato su una tabella di anchor personalizzata e altrimenti ricade su un valore predefinito integrato. Varianti ottimizzate per set di classi specifici sono fornite nello stesso sottomodulo.
7.12.2. Ultralytics¶
ml.postprocessing.ultralytics decodifica le generazioni più recenti di YOLO. YoloV8 legge un output in ordine per colonne dove ogni colonna è una predizione di anchor contenente le coordinate del box e un vettore di punteggi per classe – il canale di objectness che gli output YOLO precedenti trasportavano è stato rimosso in v8 e i punteggi di classe sono autonomi. La spiegazione dettagliata di YOLOv8 ripercorre la decodifica tensore per tensore. Le versioni più vecchie dell’era Ultralytics sono fornite nello stesso sottomodulo per i modelli addestrati sulle loro disposizioni.
7.12.3. MediaPipe¶
ml.postprocessing.mediapipe decodifica la famiglia leggera on-device di Google. BlazeFace è il rilevatore di volti trattato in hello-blazeface: un veloce rilevatore basato su anchor che emette box e sei coordinate di punti di riferimento per volto, restituite come tuple (box, score, keypoints) con i punti di riferimento collegati a ciascun box anziché come una lista di output separata. Modelli di rilevamento delle mani, di punti di riferimento e di posa della stessa famiglia sono forniti insieme a esso e seguono la stessa forma di ritorno con keypoint collegati.
7.12.4. Sceglierne uno¶
Il post-processore corretto è determinato dall’architettura su cui il modello è stato addestrato, non da ciò che l’applicazione desidera. Un .tflite YOLOv8 si decodifica correttamente solo tramite YoloV8; un .tflite BlazeFace solo tramite BlazeFace. Scegliere il post-processore fa parte della scelta del modello. Quando l’architettura di un modello non è rappresentata da un post-processore fornito, scriverne uno proprio è semplice.
Le reti di classificazione sono l’eccezione. Il loro singolo tensore di output è già ciò che l’applicazione desidera – una lista di punteggi per classe – e non è necessario alcun post-processore. Caricare il modello senza postprocess= e leggere il risultato della predizione come un ndarray piatto è la strada giusta, come trattato in I/O dei tensori.