Перейти до основного вмісту

Проведіть тестування вашої моделі

ПолеЗначення
КатегоріяМоделі та інференс
СкладністьПочатковий
Орієнтовний час читання5-10 minutes
Міткиbenchmark, synthetic, latency, throughput, power

У розділах 001 і 002 було показано, як запустити модель один раз, а потім як керувати нею асинхронно. У цьому розділі дається відповідь на наступне практичне питання: «Наскільки швидко працює ця модель на пристрої?» API для тестування навмисно невеликий. Ви завантажуєте модель, вибираєте, скільки зразків потрібно виміряти, викликаєте benchmark(...) і зчитуєте повернутий BenchmarkReport.

Для тестування використовуються input_specs() моделі для створення детермінованих синтетичних вхідних даних. Це робить його корисним для швидкого тестування моделі та порівняння скомпільованих варіантів моделі, але це не тестування камери. Воно не включає декодування камери, реальну варіативність попередньої обробки, динамічні розміри вхідних даних або поведінку постобробки, що залежить від даних.

Покроковий огляд

Завантажте модель

Почніть з того самого скомпільованого .tar.gz архіву, який використовувався в попередніх навчальних посібниках з моделі. Зображення не потрібне, оскільки для тестування створюються синтетичні тензори на основі оголошених вхідних специфікацій моделі.

Створіть simaai::neat::Model на основі шляху до архіву.

tutorials/003_benchmark_your_model/benchmark_your_model.cpp
simaai::neat::Model model(model_path);

Запустіть тестування

Викличте benchmark(samples). API запускає асинхронний запуск моделі, вимірює асинхронне вікно передачі/отримання, виводить зведену інформацію в stdout і повертає ті самі основні значення у BenchmarkReport.

Кількість зразків — це кількість виміряних синтетичних вхідних даних. Використовуйте більше число для більш стабільних показників пропускної здатності та споживання енергії; використовуйте менше число, якщо вам потрібна лише швидка перевірка.

Моделі виявлення, маршрут яких закінчується на BoxDecode, також можуть використовувати BenchmarkOptions. Встановіть original_width, original_height і resize_mode, щоб описати геометрію вихідного зображення, яку BoxDecode використовує під час відображення виявлень з координат моделі. Синтетичний тензор залишається розміром, як у моделі:

simaai::neat::BenchmarkOptions options;
options.num_samples = 100;
options.original_width = 1920;
options.original_height = 1080;
options.resize_mode = simaai::neat::ResizeMode::Letterbox;
auto report = model.benchmark(options);

Python надає доступ до тих самих полів через pyneat.BenchmarkOptions. Вкажіть обидва вихідні розміри або залиште обидва поля порожніми; якщо їх не вказано, еталонна програма визначає геометрію на основі обчисленого маршруту моделі. Геометрія, що використовується під час кожного запуску еталонної програми, має пріоритет над застарілою геометрією BoxDecode у ModelOptions.

tutorials/003_benchmark_your_model/benchmark_your_model.cpp
simaai::neat::BenchmarkReport report = model.benchmark(samples);
if (report.latency_ms <= 0.0 || report.fps <= 0.0)
throw std::runtime_error("benchmark produced no measured latency/fps");

Ознайомтеся зі звітом

У звіті, що повертається, зберігаються лише основні поля, які потрібні більшості користувачів: середня затримка від кінця до кінця в мілісекундах, пропускна здатність у кадрах за секунду, середня потужність плати у ватах (за наявності даних) та виміряна енергія в джоулях (за наявності даних).

Телеметрія живлення залежить від підтримки плати. Якщо середовище виконання не може збирати дані про напругу живлення на поточній цільовій платформі, тест все одно надає інформацію про затримку та пропускну здатність, а поля, що стосуються живлення, встановлюються на нуль.

tutorials/003_benchmark_your_model/benchmark_your_model.cpp
std::cout << "report_latency_ms=" << report.latency_ms << "\n";
std::cout << "report_fps=" << report.fps << "\n";
std::cout << "report_avg_power_watts=" << report.avg_power_watts << "\n";
std::cout << "report_energy_joules=" << report.energy_joules << "\n";

Запуск

Запустіть його, і ви повинні побачити зведену інформацію про результати тестування, яку буде надруковано. benchmark(), після чого будуть надруковані ті самі значення з отриманого звіту. Запустіть команди Python і C++ (попередньо скомпільовані) зNeat встановити в кореневу директорію **(директорію, яка містить) share/ і lib/); виконайте команди збірки з вихідного коду з кореневої директорії репозиторію.

C++ (prebuilt):

./lib/sima-neat/tutorials/tutorial_003_benchmark_your_model \
--model /tmp/resnet_50.tar.gz --samples 100

C++ (build from source):

./build.sh --target tutorial_003_benchmark_your_model
./build/tutorials-standalone/tutorial_003_benchmark_your_model \
--model /tmp/resnet_50.tar.gz --samples 100

Очікуваний результат (точні цифри залежать від моделі, плати та поточного навантаження; під час компіляції коду C++ також виводиться остання [OK] рядок):

NEAT Benchmark
Input: synthetic
Samples: 100
Latency: 12.4 ms
FPS: 80.6
Power avg: 2.3 W
Energy: 2.8 J
report_latency_ms=12.4
report_fps=80.6
report_avg_power_watts=2.3
report_energy_joules=2.8
[OK] 003_benchmark_your_model

Щоб інтегрувати вихідний код C++, наведений у цьому розділі, у ваш власний проєкт із використанням спеціального CMakeLists.txt (додаткова тека не потрібна), див. Як запускати навчальні матеріали на головній сторінці.

На практиці

Використовуйте цей тест для швидкої перевірки скомпільованого архіву моделі: чи працює він, яка виміряна асинхронна пропускна здатність і які основні показники енергоспоживання на цільовій платформі?

Для оцінки продуктивності застосунку також протестуйте реальний конвеєр. Синтетичний вхідний сигнал моделі навмисно стабільний, тому він не відображає тремтіння камери, обчислювальні витрати кодека, реальну попередню обробку, планування хоста під навантаженням або логіку застосунку на наступних етапах. Щоб налаштувати глибину черги та механізми регулювання потоку даних за допомогою власноруч створеного асинхронного запуску, див. Налаштування пропускної здатності та глибини черги.

Model::benchmark() вимагає конкретних розмірів input_specs(). Якщо форма вхідних даних є динамічною або не конкретною, тест чітко покаже помилку, замість того, щоб намагатися вгадати форму.

Повний початковий код

Показати повні програми
tutorials/003_benchmark_your_model/benchmark_your_model.cpp
// Benchmark a compiled model with deterministic synthetic inputs.
//
// Usage:
// tutorial_003_benchmark_your_model --model /path/to/model.tar.gz [--samples 100]

#include "neat.h"

#include <iostream>
#include <stdexcept>
#include <string>

namespace {

bool get_arg(int argc, char** argv, const std::string& key, std::string& out) {
for (int i = 1; i + 1 < argc; ++i) {
if (key == argv[i]) {
out = argv[i + 1];
return true;
}
}
return false;
}

int parse_int_arg(int argc, char** argv, const std::string& key, int def) {
std::string value;
if (!get_arg(argc, argv, key, value))
return def;
return std::stoi(value);
}

} // namespace

int main(int argc, char** argv) {
try {
std::string model_path;
if (!get_arg(argc, argv, "--model", model_path)) {
std::cerr << "Usage: tutorial_003_benchmark_your_model --model <path> [--samples <n>]\n";
return 1;
}
const int samples = parse_int_arg(argc, argv, "--samples", 100);

// CORE LOGIC
simaai::neat::Model model(model_path);

simaai::neat::BenchmarkReport report = model.benchmark(samples);
if (report.latency_ms <= 0.0 || report.fps <= 0.0)
throw std::runtime_error("benchmark produced no measured latency/fps");

std::cout << "report_latency_ms=" << report.latency_ms << "\n";
std::cout << "report_fps=" << report.fps << "\n";
std::cout << "report_avg_power_watts=" << report.avg_power_watts << "\n";
std::cout << "report_energy_joules=" << report.energy_joules << "\n";

std::cout << "[OK] 003_benchmark_your_model\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}

Джерело