7.15. Escribir el tuyo propio#
Cuando el catálogo no cubre un modelo – una red de investigación cuya disposición de salida es a medida, un ajuste a una arquitectura existente, un tensor cuya interpretación semántica es específica de la aplicación – la aplicación proporciona su propio postprocesador. El protocolo es simple: un invocable que toma (model, inputs, outputs) y devuelve lo que la aplicación espera de predict().
Una clase con __call__ es la forma convencional:
class MyPostprocessor:
def __init__(self, threshold=0.5):
self.threshold = threshold
def __call__(self, model, inputs, outputs):
...
return result
Una función simple también funciona – el motor solo comprueba que el objeto sea invocable.
7.15.1. Conectarlo#
Dos puntos de conexión. El kwarg postprocess= en el constructor vincula el invocable para cada llamada a predict() en el modelo:
model = ml.Model("/rom/my_model.tflite",
postprocess=MyPostprocessor())
Para anular el vínculo en una sola llamada – intercambiar decodificadores sin recargar el modelo – pasa callback= a predict directamente:
result = model.predict([img], callback=MyOtherPostprocessor())
La firma del invocable es la misma en cualquiera de los casos.
7.15.2. Lo que recibe el invocable#
model– la instancia deModel, útil para los parámetros de cuantización (output_scale,output_zero_point,output_dtype) y las dimensiones de entrada (input_shape).inputs– la lista de entradas que la aplicación pasó apredict(). El primer elemento suele ser la instancia vinculada deNormalization; su atributoroies lo queNMSespera para reasignar los cuadros de vuelta a la imagen original.outputs– los tensores de salida en bruto como una lista de objetosndarray, en su dtype nativo. Las salidas de tipo flotante llegan tal cual; las salidas de tipo entero llegan cuantizadas.
7.15.3. Aritmética cuantizada#
Los decodificadores incluidos recurren todos a los mismos ayudantes en ml.utils, y uno personalizado normalmente quiere el mismo patrón: quantize() lleva un umbral flotante al espacio cuantizado del modelo, threshold() filtra sin descuantizar todo el tensor, y dequantize() se ejecuta una vez sobre los supervivientes. sigmoid() y logit() están disponibles para redes cuyos canales de salida son logits pre-sigmoide (los detectores MediaPipe son el caso canónico).
Para modelos con salidas de tipo flotante – cabezales de regresión, modelos con una capa final de descuantización incorporada – los ayudantes de cuantización pasan sin cambios, de modo que el mismo código de postprocesador funciona contra cualquiera de los dtypes sin casos especiales.
7.15.4. Valor de retorno#
Lo que sea que el invocable devuelva es lo que devuelve predict(). Para los decodificadores que emiten cuadros, la convención es enviar los candidatos a través de un NMS y devolver sus listas por clase – la forma de llamada que documenta la supresión de no máximos y que el recorrido de YOLOv8 construye en su contexto. Para cualquier otra cosa, devuelve lo que la aplicación encuentre conveniente: un único ndarray, una cadena de etiqueta, una tupla (class, score, embedding), un diccionario.