7.12. Pós-processadores

Uma rede de detecção não emite caixas. Ela emite um ou mais tensores cujo layout depende da arquitetura contra a qual o modelo foi treinado – um tensor 2-D de predições candidatas para um detector da família YOLO, um par de tensores (boxes, scores) para um detector MediaPipe, uma lista plana de coordenadas de pontos-chave para uma rede de pose. A aplicação não consegue ler nenhum desses diretamente; o que ela quer – uma lista de caixas, uma lista de pontos-chave, um detalhamento por classe – precisa ser decodificado a partir do tensor bruto.

Esse decodificador é um pós-processador. O módulo ml.postprocessing os agrupa por ecossistema de origem.

7.12.1. Darknet

O ml.postprocessing.darknet decodifica modelos da era original do YOLO. O YOLO v2 introduziu as ideias de grade e âncora que a maioria dos detectores posteriores herdou de alguma forma, então o layout do v2 é o ponto de partida mais limpo.

O YOLO v2 começa dividindo a imagem de entrada em uma grade grosseira – um layout de 13 por 13 para a entrada canônica de 416 pixels, menor para modelos menores – e treina a rede de modo que cada célula da grade seja responsável por detectar qualquer objeto cujo centro caia dentro dela. O layout espacial do tensor de saída espelha o layout da entrada: uma posição na saída para cada célula na imagem.

Em cada célula da grade, a rede não prediz uma caixa do nada. Ela escolhe entre várias formas de referência pré-selecionadas chamadas âncoras – pares (width, height) fixos derivados offline pelo agrupamento dos tamanhos de caixa no conjunto de treinamento para que cubram os objetos típicos que o modelo deve ver. A tarefa da rede em cada célula é predizer, para cada âncora, um pequeno deslocamento do centro da caixa dentro da célula, uma escala sobre a largura e a altura da âncora, uma pontuação de objectness (a probabilidade de que haja algo ali) e um vetor de probabilidade por classe. Uma grade de 13 por 13 com as 5 âncoras padrão e 20 classes, portanto, emite 13 * 13 * 5 * (4 + 1 + 20) = 21,125 números por inferência.

A YoloV2 decodifica esse layout: ela percorre as células, aplica os deslocamentos e escalas de cada âncora para recuperar as coordenadas absolutas da caixa, combina o objectness com a probabilidade de classe para uma pontuação por classe, aplica o limiar e empurra os sobreviventes para o NMS. A classe recebe um argumento de construtor anchors= quando o modelo foi treinado contra uma tabela de âncoras personalizada e recorre a um padrão embutido caso contrário. Variantes ajustadas para conjuntos de classes específicos são fornecidas no mesmo submódulo.

7.12.2. Ultralytics

O ml.postprocessing.ultralytics decodifica as gerações mais novas do YOLO. A YoloV8 lê uma saída em ordem de coluna (column-major) onde cada coluna é uma predição de âncora contendo as coordenadas da caixa e um vetor de pontuação por classe – o canal de objectness que as saídas YOLO anteriores carregavam foi removido no v8, e as pontuações de classe ficam isoladas. O passo a passo do YOLOv8 percorre a decodificação tensor por tensor. Versões mais antigas da era Ultralytics são fornecidas no mesmo submódulo para modelos treinados contra seus layouts.

7.12.3. MediaPipe

O ml.postprocessing.mediapipe decodifica a família leve on-device do Google. A BlazeFace é o detector de rostos abordado em hello-blazeface: um detector rápido baseado em âncoras que emite caixas e seis coordenadas de pontos de referência por rosto, retornadas como tuplas (box, score, keypoints) com os pontos de referência anexados a cada caixa, em vez de como uma lista de saída separada. Modelos de detecção de mãos, de pontos de referência e de pose da mesma família são fornecidos junto a ela e seguem o mesmo formato de retorno com pontos-chave anexados.

7.12.4. Escolhendo um

O pós-processador correto é determinado pela arquitetura contra a qual o modelo foi treinado, não pelo que a aplicação quer. Um .tflite YOLOv8 só decodifica corretamente através da YoloV8; um .tflite BlazeFace só através da BlazeFace. Escolher o pós-processador faz parte de escolher o modelo. Quando a arquitetura de um modelo não é representada por um pós-processador fornecido, escrever o seu próprio é simples.

As redes de classificação são a exceção. Seu único tensor de saída já é o que a aplicação quer – uma lista de pontuações por classe – e nenhum pós-processador é necessário. Carregar o modelo sem postprocess= e ler o resultado de predict como um ndarray plano é o caminho correto, como E/S de tensores abordou.