7.8. Intrare/ieșire de tensori

Motorul acceptă un singur tensor pe partea de intrare și produce unul sau mai mulți pe partea de ieșire. Tensorii sunt obiecte ndarray cu forma, dtype-ul și vocabularul de descriptori introduse în capitolul despre numpy. Formele și dtype-urile lor provin din fișierul de model și sunt raportate prin input_shape / output_shape și input_dtype / output_dtype.

7.8.1. Cuantizare

Majoritatea rețelelor pe care le rulează camera operează pe tensori întregi cuantizați – int8 sau uint8 – pentru a se încadra în bugetul de RAM și de calcul al camerei. Un tensor cuantizat poartă valori întregi care reprezintă numere cu valori reale printr-o scalare per tensor și un punct zero:

\[\text{real} = \text{scale} \times (q - \text{zero_point})\]
\[q = \mathrm{round}(\text{real} / \text{scale}) + \text{zero_point}\]

Scalarea și punctul zero provin din calibrarea din timpul antrenării modelului și sunt stocate în fișierul de model. Acestea sunt expuse ca input_scale, input_zero_point, output_scale și output_zero_point – fiecare o listă cu câte o intrare pentru fiecare tensor de intrare sau de ieșire.

ml.utils.quantize() și ml.utils.dequantize() aplică formulele asupra unui index de ieșire specificat:

import ml.utils

real_tensor = ml.utils.dequantize(model, q_tensor, index=0)
q_tensor    = ml.utils.quantize(model, real_tensor, index=0)

Ambele funcții returnează valoarea neschimbată atunci când dtype-ul de ieșire la indexul dat este deja float, așa că apelul este sigur indiferent de starea de cuantizare a modelului.

7.8.2. Ce vede scriptul pe partea de ieșire

Ce returnează predict() depinde de faptul dacă este înregistrat un post-procesor.

Fără post-procesor, ieșirile întregi brute ale motorului sunt dequantizate automat în float și returnate ca o listă de obiecte ndarray de tip float. Scriptul primește numere cu valori reale, gata de citit. Aceasta este calea corectă pentru rețelele de clasificare, al căror unic tensor de ieșire este deja o listă de scoruri de încredere per clasă pe care aplicația le parcurge – fără pas de decodare. Este de asemenea calea simplă pentru a pune rapid în funcțiune un model necunoscut sau pentru inspecție ad-hoc din REPL.

Cu un post-procesor înregistrat (prin postprocess= din constructor sau callback= din apelul predict), tensorii cuantizați bruți sunt predați direct obiectului apelabil al post-procesorului. Post-procesorul primește tensorii cuantizați bruți și este responsabil pentru orice dequantizare de care are nevoie.

Împărțirea este o alegere de performanță. Dequantizarea automată alocă un nou tensor float pentru fiecare ieșire și parcurge fiecare element. Un post-procesor care are nevoie doar de câteva valori din fiecare tensor – aplică un prag scorurilor de încredere, apoi decodează casetele pentru cele care trec – evită costul dequantizării restului. Decodoarele de casete livrate sub ml.postprocessing urmează toate această cale, iar ml.utils.threshold() este construită exact pentru acest caz: primește un tensor de scoruri cuantizat și returnează indicii ale căror valori dequantizate trec un prag cu valoare reală, fără a dequantiza întregul tensor.