7.8. Тензорний введення/виведення#
Двигун приймає один тензор на вході та виробляє один або кілька на виході. Тензори є об’єктами ndarray із формою, типом даних і словником дескрипторів, що були введені у розділі numpy. Їхні форми та типи даних походять із файлу моделі та повідомляються через 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)
Обидві функції повертають значення без змін, коли тип вихідних даних за вказаним індексом вже є float, тому виклик є безпечним незалежно від стану квантування моделі.
7.8.2. Що скрипт бачить на стороні виходу#
Те, що повертає predict(), залежить від того, чи зареєстровано постпроцесор.
Без постпроцесора необроблені цілочисельні виходи двигуна автоматично деквантуються до числа з плаваючою комою та повертаються у вигляді списку float-об’єктів ndarray. Скрипт отримує дійсно-значені числа, готові до читання. Це правильний шлях для класифікаційних мереж, єдиний вихідний тензор яких вже є списком оцінок впевненості по класах, який ітерує додаток – крок декодування не потрібен. Це також простий шлях для швидкого запуску невідомої моделі або для спеціального перевірки через REPL.
Якщо зареєстровано постпроцесор (через postprocess= у конструкторі або callback= під час виклику predict), необроблені квантовані тензори передаються безпосередньо у виклик постпроцесора. Постпроцесор отримує необроблені квантовані тензори та відповідає за будь-яке деквантування, яке йому потрібне.
Поділ – це вибір продуктивності. Автоматичне деквантування виділяє новий float-тензор для кожного виходу та обходить кожен елемент. Постпроцесор, якому потрібні лише кілька значень із кожного тензора – порогування оцінок впевненості, потім декодування рамок для тих, що пройшли – уникає витрат на деквантування решти. Декодери рамок, що постачаються під ml.postprocessing, всі використовують цей шлях, і ml.utils.threshold() створений саме для цього випадку: він приймає квантований тензор оцінок та повертає індекси, чиї деквантовані значення перевищують дійсно-значений поріг, без деквантування всього тензора.