7.15. Zelf een schrijven

Wanneer de catalogus een model niet dekt – een onderzoeksnetwerk waarvan de uitvoerindeling op maat is, een aanpassing aan een bestaande architectuur, een tensor waarvan de semantische interpretatie applicatiespecifiek is – levert de applicatie haar eigen post-processor. Het protocol is eenvoudig: een aanroepbaar object dat (model, inputs, outputs) neemt en teruggeeft wat de applicatie verwacht van predict().

Een klasse met __call__ is de conventionele vorm:

class MyPostprocessor:
    def __init__(self, threshold=0.5):
        self.threshold = threshold

    def __call__(self, model, inputs, outputs):
        ...
        return result

Een gewone functie werkt ook – de engine controleert alleen of het object aanroepbaar is.

7.15.1. Het inhaken

Twee aanhechtingspunten. Het postprocess= keyword-argument op de constructor bindt het aanroepbare object voor elke predict() aanroep op het model:

model = ml.Model("/rom/my_model.tflite",
                 postprocess=MyPostprocessor())

Om de binding voor een enkele aanroep te overschrijven – decoders wisselen zonder het model opnieuw te laden – geef je callback= rechtstreeks aan predict door:

result = model.predict([img], callback=MyOtherPostprocessor())

De handtekening van het aanroepbare object is in beide gevallen hetzelfde.

7.15.2. Wat het aanroepbare object ontvangt

  • model – de Model instantie, nuttig voor de kwantisatieparameters (output_scale, output_zero_point, output_dtype) en de invoerafmetingen (input_shape).

  • inputs – de lijst van invoeren die de applicatie aan predict() doorgaf. Het eerste element is meestal de gebonden Normalization instantie; haar roi attribuut is wat NMS verwacht om vakken terug te zetten naar de oorspronkelijke afbeelding.

  • outputs – de ruwe uitvoertensoren als een lijst van ndarray objecten, in hun oorspronkelijke dtype. Float-uitvoeren komen ongewijzigd binnen; integer-uitvoeren komen gekwantiseerd binnen.

7.15.3. Gekwantiseerde rekenkunde

De meegeleverde decoders grijpen allemaal naar dezelfde helpers in ml.utils, en een aangepaste wil meestal hetzelfde patroon: quantize() tilt een float-drempelwaarde naar de gekwantiseerde ruimte van het model, threshold() filtert zonder de hele tensor te dekwantiseren, en dequantize() draait eenmaal op de overgeblevenen. sigmoid() en logit() zijn beschikbaar voor netwerken waarvan de uitvoerkanalen pre-sigmoid logits zijn (de MediaPipe-detectoren zijn het canonieke geval).

Voor modellen met float-uitvoeren – regressiekoppen, modellen met een ingebakken finale dekwantisatielaag – laten de kwantisatiehelpers de waarden ongewijzigd door, zodat dezelfde post-processor-code tegen beide dtypes werkt zonder speciale behandeling.

7.15.4. Retourwaarde

Wat het aanroepbare object teruggeeft, is wat predict() teruggeeft. Voor vak-uitgevende decoders is de conventie om kandidaten door een NMS te duwen en zijn lijsten per klasse terug te geven – de aanroepvorm die non-max suppression documenteert en die de YOLOv8-doorloop in context opbouwt. Voor al het andere geef je terug wat de applicatie handig vindt: een enkele ndarray, een labelstring, een tuple van (class, score, embedding), een dictionary.