7.15. Escrever o seu próprio¶
Quando o catálogo não cobre um modelo – uma rede de investigação cujo layout de saída é personalizado, uma modificação a uma arquitetura existente, um tensor cuja interpretação semântica é específica da aplicação – a aplicação fornece o seu próprio pós-processador. O protocolo é simples: um chamável que recebe (model, inputs, outputs) e retorna o que quer que a aplicação espera 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 que o objeto é chamável.
7.15.1. Ligá-lo¶
Dois pontos de ligação. O argumento de palavra-chave postprocess= no construtor vincula o chamável para cada chamada de predict() no modelo:
model = ml.Model("/rom/my_model.tflite",
postprocess=MyPostprocessor())
Para substituir a vinculação numa única chamada – trocar descodificadores sem recarregar o modelo – passar callback= ao predict diretamente:
result = model.predict([img], callback=MyOtherPostprocessor())
A assinatura do chamável é a mesma em qualquer dos casos.
7.15.2. O que o chamável 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 apredict(). O primeiro elemento é normalmente a instância deNormalizationvinculada; o seu atributoroié o queNMSespera para remapear caixas de volta para a imagem original.outputs– os tensores de saída brutos como uma lista de objetosndarray, no seu dtype nativo. As saídas float chegam como estão; as saídas inteiras chegam quantizadas.
7.15.3. Aritmética quantizada¶
Todos os descodificadores incluídos recorrem aos mesmos auxiliares em ml.utils, e um personalizado normalmente quer o mesmo padrão: quantize() eleva um limiar float 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é-sigmoid (os detetores MediaPipe são o caso canónico).
Para modelos com saídas float – cabeças de regressão, modelos com uma camada de desquantização final incorporada – os auxiliares de quantização passam sem alterações, pelo que o mesmo código do pós-processador funciona contra qualquer dtype sem tratamento especial.
7.15.4. Valor de retorno¶
O que quer que o chamável retorne é o que predict() retorna. Para descodificadores que emitem caixas, a convenção é enviar candidatos através de um NMS e retornar as suas listas por classe – a forma de chamada que supressão de não-máximos documenta e o tutorial YOLOv8 constrói em contexto. Para qualquer outra coisa, retornar o que for conveniente para a aplicação: um único ndarray, uma cadeia de etiqueta, um tuplo de (class, score, embedding), um dicionário.