7.12. Pós-processadores#
Uma rede de deteção não emite caixas. Emite um ou mais tensores cujo layout depende da arquitetura contra a qual o modelo foi treinado – um tensor 2-D de previsões candidatas para um detetor da família YOLO, um par de tensores (boxes, scores) para um detetor MediaPipe, uma lista plana de coordenadas de pontos-chave para uma rede de poses. A aplicação não consegue ler nenhum destes diretamente; o que pretende – uma lista de caixas, uma lista de pontos-chave, uma desagregação por classe – tem de ser descodificado a partir do tensor bruto.
Esse descodificador é um pós-processador. O módulo ml.postprocessing agrupa-os por ecossistema de origem.
7.12.1. Darknet#
ml.postprocessing.darknet descodifica modelos da era YOLO original. O YOLO v2 introduziu as ideias de grelha e âncora que a maioria dos detetores posteriores herdou de alguma forma, pelo que o layout v2 é o ponto de partida mais claro.
O YOLO v2 começa por dividir a imagem de entrada numa grelha grosseira – um layout de 13x13 para a entrada canónica de 416 pixéis, menor para modelos mais pequenos – e treina a rede de modo a que cada célula da grelha seja responsável pela deteção de qualquer objeto cujo centro caia no seu interior. O layout espacial do tensor de saída espelha o layout da entrada: uma posição na saída por célula na imagem.
Em cada célula da grelha, a rede não prevê uma caixa do nada. Seleciona a partir de várias formas de referência pré-escolhidas chamadas âncoras – pares fixos de (width, height) derivados offline por agrupamento dos tamanhos de caixa no conjunto de treino, de modo a cobrirem os objetos típicos que o modelo deve detetar. A tarefa da rede em cada célula é prever, para cada âncora, um pequeno deslocamento para o centro da caixa dentro da célula, uma escala na largura e altura da âncora, uma pontuação de objetividade (a probabilidade de existir algo), e um vetor de probabilidade por classe. Uma grelha de 13x13 com as 5 âncoras predefinidas e 20 classes emite portanto 13 * 13 * 5 * (4 + 1 + 20) = 21,125 números por inferência.
YoloV2 descodifica esse layout: percorre as células, aplica os deslocamentos e escalas de cada âncora para recuperar coordenadas de caixa absolutas, combina objetividade com probabilidade de classe para uma pontuação por classe, aplica o limiar e envia os sobreviventes para o NMS. A classe recebe um argumento construtor anchors= quando o modelo foi treinado contra uma tabela de âncoras personalizada e usa um valor predefinido incorporado caso contrário. Variantes ajustadas para conjuntos de classes específicos estão incluídas no mesmo submódulo.
7.12.2. Ultralytics#
ml.postprocessing.ultralytics descodifica as gerações YOLO mais recentes. YoloV8 lê uma saída em formato coluna-principal onde cada coluna é uma previsão de âncora contendo coordenadas de caixa e um vetor de pontuação por classe – o canal de objetividade que as saídas YOLO anteriores transportavam foi eliminado na v8, e as pontuações de classe ficam sozinhas. O tutorial YOLOv8 percorre a descodificação tensor por tensor. Versões mais antigas da era Ultralytics estão incluídas no mesmo submódulo para modelos treinados contra os seus layouts.
7.12.3. MediaPipe#
ml.postprocessing.mediapipe descodifica a família leve de dispositivos da Google. BlazeFace é o detetor de rostos abordado em hello-blazeface: um detetor rápido baseado em âncoras que emite caixas e seis coordenadas de pontos de referência por rosto, retornados como tuplos (box, score, keypoints) com os pontos de referência anexados a cada caixa em vez de como uma lista de saída separada. Os modelos de deteção de mãos, pontos de referência e poses da mesma família estão incluídos e seguem a mesma forma de retorno com pontos-chave anexados.
7.12.4. Escolher um#
O pós-processador correto é determinado pela arquitetura contra a qual o modelo foi treinado, não pelo que a aplicação pretende. Um .tflite YOLOv8 só descodifica corretamente através de YoloV8; um .tflite BlazeFace apenas através de BlazeFace. Escolher o pós-processador faz parte da escolha do modelo. Quando a arquitetura de um modelo não está representada por um pós-processador incluído, escrever o seu próprio é simples.
As redes de classificação são a exceção. O seu único tensor de saída já é o que a aplicação pretende – uma lista de pontuações por classe – e não é necessário nenhum pós-processador. Carregar o modelo sem postprocess= e ler o resultado de predict como um ndarray plano é o caminho correto, como tensor I/O abordou.