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