7.12. Post-processors

Een detectienetwerk geeft geen vakken uit. Het geeft een of meer tensoren uit waarvan de indeling afhangt van de architectuur waartegen het model is getraind – een 2D-tensor van kandidaatvoorspellingen voor een detector uit de YOLO-familie, een paar (boxes, scores) tensoren voor een MediaPipe-detector, een platte lijst van sleutelpuntcoördinaten voor een houdingsnetwerk. De applicatie kan geen van deze rechtstreeks lezen; wat ze wil – een lijst van vakken, een lijst van sleutelpunten, een uitsplitsing per klasse – moet uit de ruwe tensor worden gedecodeerd.

Die decoder is een post-processor. De ml.postprocessing module groepeert ze per bronecosysteem.

7.12.1. Darknet

ml.postprocessing.darknet decodeert modellen uit het oorspronkelijke YOLO-tijdperk. YOLO v2 introduceerde de ideeën van het grid en het anker die de meeste latere detectoren in een of andere vorm hebben overgenomen, dus de v2-indeling is het schoonste startpunt.

YOLO v2 begint met het verdelen van de invoerafbeelding in een grof grid – een indeling van 13 bij 13 voor de canonieke invoer van 416 pixels, kleiner voor kleinere modellen – en traint het netwerk zo dat elke gridcel verantwoordelijk is voor het detecteren van elk object waarvan het middelpunt erbinnen valt. De ruimtelijke indeling van de uitvoertensor weerspiegelt de indeling van de invoer: één positie in de uitvoer per cel in de afbeelding.

Bij elke gridcel voorspelt het netwerk geen vak uit het niets. Het kiest uit verschillende vooraf gekozen referentievormen genaamd ankers – vaste (width, height) paren die offline worden afgeleid door de vakafmetingen in de trainingsset te clusteren zodat ze de typische objecten dekken die het model naar verwachting zal zien. De taak van het netwerk bij elke cel is om voor elk anker een kleine verschuiving van het vakmiddelpunt binnen de cel te voorspellen, een schaal op de breedte en hoogte van het anker, een objectness-score (de waarschijnlijkheid dat er iets is), en een waarschijnlijkheidsvector per klasse. Een grid van 13 bij 13 met de standaard 5 ankers en 20 klassen geeft daarom 13 * 13 * 5 * (4 + 1 + 20) = 21,125 getallen per inferentie uit.

YoloV2 decodeert die indeling: het doorloopt de cellen, past de verschuivingen en schalen van elk anker toe om absolute vakcoördinaten te herstellen, combineert objectness met klassewaarschijnlijkheid voor een score per klasse, past de drempelwaarde toe, en duwt de overgeblevenen naar NMS. De klasse neemt een anchors= constructorargument wanneer het model is getraind tegen een aangepaste ankertabel en valt anders terug op een ingebouwde standaard. Varianten afgestemd op specifieke klassesets worden in dezelfde submodule meegeleverd.

7.12.2. Ultralytics

ml.postprocessing.ultralytics decodeert de nieuwere YOLO-generaties. YoloV8 leest een kolomgewijze uitvoer waarbij elke kolom één ankervoorspelling is die vakcoördinaten en een scorevector per klasse bevat – het objectness-kanaal dat eerdere YOLO-uitvoeren droegen is in v8 weggelaten, en de klassescores staan op zichzelf. De YOLOv8-doorloop doorloopt de decodering tensor voor tensor. Oudere versies uit het Ultralytics-tijdperk worden in dezelfde submodule meegeleverd voor modellen die tegen hun indelingen zijn getraind.

7.12.3. MediaPipe

ml.postprocessing.mediapipe decodeert Googles lichtgewicht on-device familie. BlazeFace is de gezichtsdetector die wordt behandeld in hello-blazeface: een snelle op ankers gebaseerde detector die per gezicht vakken en zes oriëntatiepuntcoördinaten uitgeeft, geretourneerd als (box, score, keypoints) tuples met de oriëntatiepunten aan elk vak gekoppeld in plaats van als een aparte uitvoerlijst. Handdetectie-, oriëntatiepunt- en houdingsmodellen uit dezelfde familie worden ernaast meegeleverd en volgen dezelfde retourvorm met gekoppelde sleutelpunten.

7.12.4. Er een kiezen

De juiste post-processor wordt bepaald door de architectuur waartegen het model is getraind, niet door wat de applicatie wil. Een YOLOv8 .tflite decodeert alleen correct via YoloV8; een BlazeFace .tflite alleen via BlazeFace. Het kiezen van de post-processor is onderdeel van het kiezen van het model. Wanneer de architectuur van een model niet wordt vertegenwoordigd door een meegeleverde post-processor, is er zelf een schrijven eenvoudig.

Classificatienetwerken zijn de uitzondering. Hun enkele uitvoertensor is al wat de applicatie wil – een lijst van scores per klasse – en er is geen post-processor nodig. Het model laden zonder postprocess= en het predict-resultaat lezen als een platte ndarray is de juiste weg, zoals tensor-I/O behandelde.