7.6. Anatomia de predict

Model.predict(inputs, *, callback=None) é onde o objeto de modelo carregado realmente faz o trabalho. Entre as entradas que entram e o resultado que sai, três estágios são executados em sequência: pré-processamento, despacho do motor, pós-processamento. Dois dos três recebem parâmetros que o script controla diretamente; o motor no meio é decidido pela câmera.

Um fluxo horizontal de cinco caixas conectadas da esquerda para a direita. A da extrema esquerda é "Entrada de imagem"; uma seta leva a "Pré-processamento" com o subtítulo "Normalização"; uma seta leva a "Motor" com o subtítulo "TFLM / STAI"; uma seta leva a "Pós-processamento" com o subtítulo "postprocess="; e uma seta final leva a "Resultado". Os três estágios do meio carregam uma etiqueta embaixo -- "controlável pelo usuário" abaixo de Pré-processamento, "automático" abaixo de Motor, e "controlável pelo usuário" abaixo de Pós-processamento.

Os três estágios de predict(). O pré-processamento e o pós-processamento recebem parâmetros que o script controla; o motor no meio é fixado pela câmera.

7.6.1. Pré-processamento

O estágio de pré-processamento transforma cada entrada no tensor denso que a rede espera. A entrada mais comum é uma image.Image, capturada em RGB565. O estágio a recorta e redimensiona para o input_shape da rede, converte de RGB565 para o formato de canais no qual a rede foi treinada (RGB888 para a maioria das redes de visão), aplica escala e deslocamento por canal e – quando a rede espera entrada inteira – quantiza para o input_dtype do modelo na mesma passagem. Redes treinadas para entrada em float pulam a etapa de quantização e recebem diretamente o resultado de escala e deslocamento.

O ml.preprocessing.Normalization padrão lê o dtype de entrada do modelo e executa a transformação correta automaticamente. Um Normalization ajustado manualmente sobrescreve os valores de escala, média e desvio padrão para modelos treinados contra estatísticas de canal personalizadas (as médias e os desvios padrão derivados do ImageNet são um caso comum). Um chamável simples sobrescreve o estágio inteiramente – útil quando a entrada não é uma imagem ou quando a aplicação já produziu ela própria o tensor denso.

7.6.2. Despacho do motor

O estágio do motor executa a rede. Para qual motor ele despacha é fixado pela câmera: o H7 e o RT1062 executam o TFLM (o interpretador TensorFlow Lite for Microcontrollers, despachando kernels CMSIS-NN otimizados para ARM onde eles existem); o AE3 executa o mesmo interpretador TFLM com seu fallback Cortex-M55 e a NPU Ethos-U tratando qualquer operador que o compilador offline Vela tenha marcado para o acelerador; o N6 executa o STAI, o runtime da ST para a NPU construída sob medida do N6.

O script não escolhe o motor. O motor que acompanha a câmera executa todos os modelos que a câmera carrega.

7.6.3. Pós-processamento

O estágio de pós-processamento transforma os tensores de saída brutos da rede de volta em um resultado utilizável. O comportamento padrão é dequantizar cada tensor de saída para ponto flutuante (ou repassá-lo inalterado para redes com saídas em float) e retorná-los como uma lista de objetos ndarray. A maioria das aplicações registra um pós-processador – um chamável que conhece o layout de saída da rede – para decodificar os tensores na forma de resultado sobre a qual a aplicação age: uma lista de caixas delimitadoras, uma lista de pontos-chave, uma lista de classes.

O script controla este estágio de duas maneiras. A palavra-chave postprocess= no construtor registra um pós-processador que é executado em cada chamada. A palavra-chave callback= em predict() sobrescreve o pós-processador registrado apenas para uma chamada – útil para alternar entre vários decodificadores sem recarregar o modelo. Qualquer uma das formas recebe (model, inputs, outputs) e retorna aquilo que a aplicação espera.

7.6.4. O que o script controla

O pré-processamento e o pós-processamento são os dois pontos de controle do script. O pré-processador padrão trata a maioria dos modelos de visão; o pós-processador certo para uma dada família de redes é escolhido no catálogo sob ml.postprocessing. O motor no meio é decidido pelo build e é executado da mesma forma independentemente do que o script solicite.