7.8. Тензорний введення/виведення#

Двигун приймає один тензор на вході та виробляє один або кілька на виході. Тензори є об’єктами ndarray із формою, типом даних і словником дескрипторів, що були введені у розділі numpy. Їхні форми та типи даних походять із файлу моделі та повідомляються через input_shape / output_shape та input_dtype / output_dtype.

7.8.1. Квантування#

Більшість мереж, що запускаються на камері, оперують квантованими цілочисельними тензорами – int8 або uint8 – щоб вміститись у рамки оперативної пам’яті та обчислювального бюджету камери. Квантований тензор містить цілочисельні значення, що представляють дійсно-значені числа через масштаб та нульову точку для кожного тензора:

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

Масштаб та нульова точка походять із калібрування моделі під час навчання та зберігаються у файлі моделі. Вони доступні як 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() створений саме для цього випадку: він приймає квантований тензор оцінок та повертає індекси, чиї деквантовані значення перевищують дійсно-значений поріг, без деквантування всього тензора.