7.12. Post-processory

Sieć wykrywająca obiekty nie emituje ramek. Emituje jeden lub więcej tensorów, których układ zależy od architektury, na której model był trenowany – dwuwymiarowy tensor kandydujących predykcji dla detektora z rodziny YOLO, parę tensorów (boxes, scores) dla detektora MediaPipe, płaską listę współrzędnych punktów kluczowych dla sieci wykrywającej pozę. Aplikacja nie może odczytać żadnego z nich bezpośrednio; to, czego chce – listy ramek, listy punktów kluczowych, podziału na poszczególne klasy – musi zostać zdekodowane z surowego tensora.

Tym dekoderem jest post-processor. Moduł ml.postprocessing grupuje je według ekosystemu źródłowego.

7.12.1. Darknet

ml.postprocessing.darknet dekoduje modele z oryginalnej ery YOLO. YOLO v2 wprowadziło idee siatki i kotwicy, które większość późniejszych detektorów odziedziczyła w tej czy innej formie, więc układ v2 jest najczystszym punktem wyjścia.

YOLO v2 zaczyna od podziału obrazu wejściowego na grubą siatkę – układ 13 na 13 dla kanonicznego wejścia 416 pikseli, mniejszy dla mniejszych modeli – i trenuje sieć tak, aby każda komórka siatki była odpowiedzialna za wykrywanie dowolnego obiektu, którego środek znajduje się w jej obrębie. Przestrzenny układ tensora wyjściowego odzwierciedla układ wejścia: jedna pozycja w wyjściu na każdą komórkę obrazu.

W każdej komórce siatki sieć nie przewiduje ramki z niczego. Wybiera spośród kilku wcześniej dobranych kształtów referencyjnych zwanych kotwicami – stałych par (width, height) wyznaczonych offline przez klastrowanie rozmiarów ramek w zbiorze treningowym, tak aby pokrywały typowe obiekty, które model ma napotkać. Zadaniem sieci w każdej komórce jest przewidzenie, dla każdej kotwicy, niewielkiego przesunięcia środka ramki w obrębie komórki, skali szerokości i wysokości kotwicy, wyniku objectness (prawdopodobieństwa, że cokolwiek tam jest) oraz wektora prawdopodobieństw dla poszczególnych klas. Siatka 13 na 13 z domyślnymi 5 kotwicami i 20 klasami emituje zatem 13 * 13 * 5 * (4 + 1 + 20) = 21,125 liczb na wnioskowanie.

YoloV2 dekoduje ten układ: przechodzi przez komórki, stosuje przesunięcia i skale każdej kotwicy w celu odzyskania bezwzględnych współrzędnych ramki, łączy objectness z prawdopodobieństwem klasy w celu uzyskania wyniku dla poszczególnych klas, stosuje próg i przekazuje te, które przetrwały, do NMS. Klasa przyjmuje argument konstruktora anchors=, gdy model był trenowany na niestandardowej tabeli kotwic, a w przeciwnym razie korzysta z wbudowanej wartości domyślnej. Warianty dostrojone do konkretnych zestawów klas są dostarczane w tym samym submodule.

7.12.2. Ultralytics

ml.postprocessing.ultralytics dekoduje nowsze generacje YOLO. YoloV8 odczytuje wyjście w układzie kolumnowym, gdzie każda kolumna to jedna predykcja kotwicy zawierająca współrzędne ramki i wektor wyników dla poszczególnych klas – kanał objectness, który niosły wcześniejsze wyjścia YOLO, został w v8 usunięty, a wyniki klas występują samodzielnie. Przewodnik po YOLOv8 krok po kroku przechodzi przez dekodowanie tensor po tensorze. Starsze wersje z ery Ultralytics są dostarczane w tym samym submodule dla modeli trenowanych na ich układach.

7.12.3. MediaPipe

ml.postprocessing.mediapipe dekoduje lekką rodzinę modeli Google działających na urządzeniu. BlazeFace to detektor twarzy omówiony w hello-blazeface: szybki detektor oparty na kotwicach, który emituje ramki i sześć współrzędnych punktów charakterystycznych na twarz, zwracane jako krotki (box, score, keypoints) z punktami charakterystycznymi dołączonymi do każdej ramki, a nie jako osobna lista wyjściowa. Modele wykrywania dłoni, punktów charakterystycznych i pozy z tej samej rodziny są dostarczane wraz z nim i stosują ten sam kształt zwracany z dołączonymi punktami kluczowymi.

7.12.4. Wybór jednego z nich

Właściwy post-processor jest określony przez architekturę, na której model był trenowany, a nie przez to, czego chce aplikacja. Plik .tflite YOLOv8 dekoduje się poprawnie tylko przez YoloV8; plik .tflite BlazeFace tylko przez BlazeFace. Wybór post-processora jest częścią wyboru modelu. Gdy architektura modelu nie jest reprezentowana przez dostarczany post-processor, napisanie własnego jest proste.

Sieci klasyfikujące stanowią wyjątek. Ich pojedynczy tensor wyjściowy jest już tym, czego chce aplikacja – listą wyników dla poszczególnych klas – i żaden post-processor nie jest potrzebny. Wczytanie modelu bez postprocess= i odczytanie wyniku predict jako płaskiej ndarray to właściwa droga, jak omówiono w We/Wy tensorów.