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.