7.8. Tensor-I/O

Motorn accepterar en enda tensor på indatasidan och producerar en eller flera på utdatasidan. Tensorerna är ndarray-objekt med den form, dtype och deskriptorvokabulär som numpy-kapitlet introducerade. Deras former och dtyper kommer från modellfilen och rapporteras via input_shape / output_shape och input_dtype / output_dtype.

7.8.1. Kvantisering

De flesta nätverk som kameran kör arbetar med kvantiserade heltalstensorer – int8 eller uint8 – för att rymmas inom kamerans RAM- och beräkningsbudget. En kvantiserad tensor bär heltalsvärden som representerar reellvärda tal via en skala och en nollpunkt per tensor:

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

Skalan och nollpunkten kommer från modellens kalibrering vid träningstillfället och lagras i modellfilen. De exponeras som input_scale, input_zero_point, output_scale och output_zero_point – var och en en lista med en post per indata- eller utdatatensor.

ml.utils.quantize() och ml.utils.dequantize() tillämpar formlerna mot ett angivet utdataindex:

import ml.utils

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

Båda funktionerna returnerar värdet oförändrat när utdata-dtypen vid det givna indexet redan är flyttal, så anropet är säkert oavsett modellens kvantiseringsstatus.

7.8.2. Vad skriptet ser på utdatasidan

Vad predict() returnerar beror på om en efterbehandlare är registrerad.

Utan efterbehandlare blir motorns råa heltalsutdata automatiskt dekvantiserade till flyttal och returneras som en lista med flyttals-ndarray-objekt. Skriptet tar emot reellvärda tal redo att läsas. Detta är rätt väg för klassificeringsnätverk, vars enda utdatatensor redan är en lista med konfidenspoäng per klass som applikationen itererar över – inget avkodningssteg behövs. Det är också den enkla vägen för att snabbt få en okänd modell att köra eller för ad hoc-granskning från REPL.

Med en efterbehandlare registrerad (via postprocess= på konstruktorn eller callback= på predict-anropet) överlämnas de råa kvantiserade tensorerna direkt till efterbehandlarens anropbara objekt. Efterbehandlaren tar emot de råa kvantiserade tensorerna och ansvarar för vilken dekvantisering den än behöver.

Uppdelningen är ett prestandaval. Automatisk dekvantisering allokerar en ny flyttalstensor för varje utdata och går igenom varje element. En efterbehandlare som bara behöver några få värden ur varje tensor – tröskla konfidenspoängen, avkoda sedan rutor för de överlevande – hoppar över kostnaden för att dekvantisera resten. Rutavkodarna som levereras under ml.postprocessing tar alla denna väg, och ml.utils.threshold() är byggd för precis detta fall: den tar en kvantiserad poängtensor och returnerar de index vars dekvantiserade värden passerar ett reellvärt tröskelvärde, utan att dekvantisera hela tensorn.