7.8. Ввод-вывод тензоров¶
Движок принимает один тензор на входной стороне и формирует один или несколько на выходной. Тензоры – это объекты ndarray с формой, dtype и набором дескрипторов, введёнными в главе про numpy. Их формы и dtype берутся из файла модели и сообщаются через input_shape / output_shape и input_dtype / output_dtype.
7.8.1. Квантование¶
Большинство сетей, запускаемых камерой, работают с квантованными целочисленными тензорами – int8 или uint8 – чтобы уложиться в бюджет ОЗУ и вычислений камеры. Квантованный тензор содержит целочисленные значения, которые представляют действительные числа через масштаб и нулевую точку для каждого тензора:
Масштаб и нулевая точка берутся из калибровки модели на этапе обучения и хранятся в файле модели. Они предоставляются как input_scale, input_zero_point, output_scale и output_zero_point – каждый список с одной записью на каждый входной или выходной тензор.
ml.utils.quantize() и ml.utils.dequantize() применяют формулы к указанному индексу выхода:
import ml.utils
real_tensor = ml.utils.dequantize(model, q_tensor, index=0)
q_tensor = ml.utils.quantize(model, real_tensor, index=0)
Обе функции возвращают значение без изменений, когда выходной dtype по заданному индексу уже является плавающей точкой, поэтому вызов безопасен независимо от состояния квантования модели.
7.8.2. Что скрипт видит на выходной стороне¶
Что возвращает predict(), зависит от того, зарегистрирован ли постобработчик.
Без постобработчика сырые целочисленные выходы движка автоматически деквантуются до плавающей точки и возвращаются как список объектов float ndarray. Скрипт получает действительные числа, готовые к чтению. Это правильный путь для сетей классификации, чей единственный выходной тензор уже является списком оценок уверенности по каждому классу, по которому приложение проходит – без шага декодирования. Это также простой путь для быстрого запуска неизвестной модели или для специального изучения из REPL.
С зарегистрированным постобработчиком (через postprocess= в конструкторе или callback= в вызове predict) сырые квантованные тензоры передаются вызываемому объекту постобработчика напрямую. Постобработчик получает сырые квантованные тензоры и отвечает за любую необходимую ему деквантизацию.
Это разделение – выбор в пользу производительности. Автоматическая деквантизация выделяет новый тензор float для каждого выхода и проходит по каждому элементу. Постобработчик, которому нужно лишь несколько значений из каждого тензора – пороговая фильтрация оценок уверенности, затем декодирование рамок для прошедших – избегает затрат на деквантизацию остального. Декодеры рамок, поставляемые в ml.postprocessing, все идут этим путём, а ml.utils.threshold() создан именно для этого случая: он принимает квантованный тензор оценок и возвращает индексы, чьи деквантованные значения проходят порог с действительным значением, без деквантизации всего тензора.