7.8. E/S de Tensores¶
O motor aceita um único tensor no lado de entrada e produz um ou mais no lado de saída. Os tensores são objetos ndarray com o vocabulário de forma, dtype e descritor que o capítulo numpy introduziu. As suas formas e dtypes provêm do ficheiro do modelo e são reportados através de input_shape / output_shape e input_dtype / output_dtype.
7.8.1. Quantização¶
A maioria das redes que a câmara executa operam em tensores inteiros quantizados – int8 ou uint8 – para caber na RAM e no orçamento de computação da câmara. Um tensor quantizado transporta valores inteiros que representam números de valor real através de uma escala e um ponto zero por tensor:
A escala e o ponto zero provêm da calibração em tempo de treino do modelo e são armazenados no ficheiro do modelo. São expostos como input_scale, input_zero_point, output_scale e output_zero_point – cada um uma lista com uma entrada por tensor de entrada ou saída.
ml.utils.quantize() e ml.utils.dequantize() aplicam as fórmulas a um índice de saída especificado:
import ml.utils
real_tensor = ml.utils.dequantize(model, q_tensor, index=0)
q_tensor = ml.utils.quantize(model, real_tensor, index=0)
Ambas as funções devolvem o valor sem alterações quando o dtype de saída no índice dado já é float, pelo que a chamada é segura independentemente do estado de quantização do modelo.
7.8.2. O que o script vê no lado de saída¶
O que predict() devolve depende de se existe um pós-processador registado.
Sem pós-processador, as saídas inteiras brutas do motor são desquantizadas automaticamente para ponto flutuante e devolvidas como uma lista de objetos ndarray em ponto flutuante. O script recebe números de valor real prontos a ler. Este é o caminho correto para redes de classificação, cujo único tensor de saída já é uma lista de pontuações de confiança por classe sobre a qual a aplicação itera – não é necessário nenhum passo de descodificação. É também o caminho fácil para pôr um modelo desconhecido a funcionar rapidamente ou para inspeção ad-hoc a partir do REPL.
Com um pós-processador registado (através de postprocess= no construtor ou callback= na chamada ao predict), os tensores quantizados brutos são entregues diretamente ao callable do pós-processador. O pós-processador recebe os tensores quantizados brutos e é responsável pela desquantização que necessitar.
A divisão é uma escolha de desempenho. A desquantização automática aloca um novo tensor float para cada saída e percorre todos os elementos. Um pós-processador que apenas precisa de alguns valores de cada tensor – aplica limiar às pontuações de confiança, depois descodifica caixas para os sobreviventes – evita o custo de desquantizar o resto. Os descodificadores de caixas incluídos em ml.postprocessing seguem todos este caminho, e ml.utils.threshold() foi construído exatamente para este caso: recebe um tensor de pontuações quantizadas e devolve os índices cujos valores desquantizados passam um limiar de valor real, sem desquantizar o tensor inteiro.