7.15. Escrevendo o seu próprio¶
Quando o catálogo não cobre um modelo – uma rede de pesquisa cujo layout de saída é sob medida, um ajuste em uma arquitetura existente, um tensor cuja interpretação semântica é específica da aplicação – a aplicação fornece seu próprio pós-processador. O protocolo é simples: um callable que recebe (model, inputs, outputs) e retorna o que quer que a aplicação espere de predict().
Uma classe com __call__ é a forma convencional:
class MyPostprocessor:
def __init__(self, threshold=0.5):
self.threshold = threshold
def __call__(self, model, inputs, outputs):
...
return result
Uma função simples também funciona – o motor apenas verifica se o objeto é callable.
7.15.1. Conectando-o¶
Dois pontos de conexão. O argumento nomeado postprocess= no construtor vincula o callable para cada chamada de predict() no modelo:
model = ml.Model("/rom/my_model.tflite",
postprocess=MyPostprocessor())
Para sobrescrever o vínculo para uma única chamada – trocar de decodificadores sem recarregar o modelo – passe callback= diretamente para predict:
result = model.predict([img], callback=MyOtherPostprocessor())
A assinatura do callable é a mesma em qualquer caso.
7.15.2. O que o callable recebe¶
model– a instância deModel, útil para os parâmetros de quantização (output_scale,output_zero_point,output_dtype) e as dimensões de entrada (input_shape).inputs– a lista de entradas que a aplicação passou parapredict(). O primeiro elemento geralmente é a instância deNormalizationvinculada; seu atributoroié o queNMSespera para remapear as caixas de volta para a imagem original.outputs– os tensores de saída brutos como uma lista de objetosndarray, em seu dtype nativo. Saídas em ponto flutuante chegam como estão; saídas inteiras chegam quantizadas.
7.15.3. Aritmética quantizada¶
Os decodificadores fornecidos recorrem todos aos mesmos auxiliares em ml.utils, e um personalizado geralmente quer o mesmo padrão: quantize() eleva um limiar de ponto flutuante para o espaço quantizado do modelo, threshold() filtra sem desquantizar o tensor inteiro, e dequantize() é executado uma vez sobre os sobreviventes. sigmoid() e logit() estão disponíveis para redes cujos canais de saída são logits pré-sigmoide (os detectores MediaPipe são o caso canônico).
Para modelos com saídas em ponto flutuante – cabeças de regressão, modelos com uma camada final de desquantização embutida – os auxiliares de quantização passam adiante inalterados, de modo que o mesmo código de pós-processador funciona contra qualquer dtype sem tratamento especial.
7.15.4. Valor de retorno¶
O que quer que o callable retorne é o que predict() retorna. Para decodificadores que emitem caixas, a convenção é empurrar os candidatos através de um NMS e retornar suas listas por classe – o formato de chamada que non-max suppression documenta e que o passo a passo do YOLOv8 constrói em contexto. Para qualquer outra coisa, retorne o que for conveniente para a aplicação: um único ndarray, uma string de rótulo, uma tupla de (class, score, embedding), um dicionário.