모델
Model은 컴파일된 모델 아카이브를 로드하고 Neat이 실행할 수 있는 경로를 노출합니다.
각 전처리, 추론 및 후처리 단계를 수동으로 연결하지 않고 모델을 인식하는 방식으로 실행하려는 경우 Model을 사용합니다. 먼저 계약을 검사한 다음 실행합니다. 알 수 없는 텐서는 없습니다.
Model이 제공하는 기능
input_specs()및output_specs()는 모델이 예상하고 생성하는 텐서 계약을 보여줍니다.metadata(),info()및 Pythonsummary()는 로드된 모델을 검사하는 데 도움이 됩니다.run(...)은 단일 추론을 실행합니다.build(...)는 푸시/풀 실행을 위한 재사용 가능한 모델 러너를 만듭니다.graph()는 모델 경로를 재사용 가능한Graph조각으로 반환합니다.preprocess(),inference()및postprocess()는 고급 구성을 위한 경로 단계를 노출합니다.
참조:
모델 아카이브 로드
이 페이지의 예제에서는 model_path가 Model Compiler에서 컴파일된 모델 아카이브를 가리키고, 일반적으로 Neat을 실행하는 시스템에 복사된 .tar.gz 파일을 사용한다고 가정합니다. 먼저 기본 옵션을 사용하고, 계약 또는 입력 소스에서 필요한 경우에만 ModelOptions를 추가합니다.
const std::string model_path = "resnet_50_model.tar.gz";
simaai::neat::Model model(model_path);
const auto info = model.info();
const auto inputs = model.input_specs();
const auto outputs = model.output_specs();
이 검사 단계가 예상치 못한 결과를 보인다면, 여기서 멈추세요. 더 큰 애플리케이션을 작성하기 전에 모델 경로, 아티팩트 또는 계약을 수정하세요.
모델 실행 경로 선택
작업에 맞는 경로를 선택하세요. 간단한 테스트는 작게 유지하고, 그래프 수준의 문제를 해결하기 위해 그래프 수준의 기능을 사용하세요.
| 필요 | 사용 | 반환 값 |
|---|---|---|
| 모델 입력을 한 번 실행 | model.run(...) | 텐서 입력의 경우 TensorList, 샘플 입력의 경우 Sample |
| 여러 입력에 걸쳐 모델 경로 재사용 | model.build(...) 및 모델 러너 | 모델 경로 주변의 푸시/풀 제어 |
| 모델 실 행만 측정 | model.benchmark(...) 또는 측정된 모델 러너 | BenchmarkReport 또는 MeasureReport |
| 모델을 애플리케이션 흐름에 포함 | graph.add(model) | Graph 단계 |
| 구성에 대한 경로 경계 노출 | model.graph(route_options) | 재사용 가능한 Graph 조각 |
| 하나의 모델 단계를 디버깅 | model.fragment(ModelStage::...) | 단계별 Graph 조각 |
직접적인 모델 호출을 사용하여 확실하게 하세요. 모델이 애플리케이션의 일부가 될 때 Graph를 사용하세요. (예: 명명된 입력, 소스 노드, 분기, 조인, 렌더링, 비디오 출력, 메타데이터 출력 또는 여러 모델)
모델 옵션 선택
기본 ModelOptions로 시작하세요. 모델 계약 또는 입력 소스에서 필요한 경우에만 옵션을 추가하세요.
| 목표 | 사용할 옵션 | 참고 사항 |
|---|---|---|
| 디코딩된 이미지 입력을 전송 | preprocess.kind, preprocess.preset 및 크기 조정, 색상, 레이아웃, 정규화, 양자화 또는 테셀레이션과 같은 특정 전처리 필드 | 애플리케이션에서 픽셀을 전송하고 Neat가 이를 모델 계약에 맞게 조정해야 할 때 사용하세요. |
| 모델의 공개 입력에 맞는 텐서 제공 | 전처리 옵션을 설정하지 않습니다. | 크기 조정과 정규화를 마친 FP32 입력에 도 필요한 Cast/Quant/Tess 어댑터를 유지합니다. |
| 감지 출력을 디코딩 | decode_type, decode_type_option, score_threshold, nms_iou_threshold, top_k, num_classes | 감지 모델은 명시적인 디코딩 의도가 필요합니다. |
| 추출된 모델 파일을 검사를 위해 보관 | cleanup_extracted_model_data = false | 디버깅 중에 유용합니다. 일반 실행에서는 기본값을 유지하세요. |
| 하나의 프로세스에서 여러 모델 경로 실행 | name_suffix 및 그래프 요소 이름 접두사/접미사 | 생성된 이름과 진단 정보를 읽기 쉽게 유지합니다. |
| 모델 경로 조기 중단 | inference_terminal | 고급 경로 디버깅 경로입니다. 처음 실행하는 코드에는 사용하지 마세요. |
| 실행 배치 또는 내부 큐 조정 | processcvu, processmla, advanced_execution | 고급 기능입니다. 조정하기 전에 기본 경로를 먼저 측정하세요. |
감지 디코딩을 사용한 이미지 입력
모델이 이미지 전처리를 예상하고 YOLO 스타일의 감지 결과를 출력할 때 이 패턴을 사용하세요.
simaai::neat::Model::Options options;
options.preprocess.kind = simaai::neat::InputKind::Image;
options.preprocess.preset = simaai::neat::NormalizePreset::COCO_YOLO;
options.decode_type = simaai::neat::BoxDecodeType::YoloV8;
options.score_threshold = 0.25f;
options.nms_iou_threshold = 0.45f;
options.top_k = 100;
simaai::neat::Model model(model_path, options);
설정을 일관성 있게 유지하세요. 필요한 계약 내용을 변경하지 않는 한 기본값이나 더 이상 사용되지 않는 필드를 설정하지 마세요. 텐서 또는 이미지 메타데이터에 이미 소스 형식이 포함되어 있는 경우, 페이지에서 그 이유를 설명하지 않는 한 반복하지 않도록 하세요.
객체 감지 디코딩 필드 가이드
경로가 원시 추론 텐서 대신 디코딩된 박스, 포즈 결과 또는 분할 결과를 반환해야 할 때 객체 감지 디코딩 옵션을 설정하세요. 원시 모델 출력을 원하는 경우 필드를 설정하지 않은 상태로 두세요.
| 필드 | 사용 시점 | 기본 의미 |
|---|---|---|
decode_type | 객체 감지 모델에 BoxDecode 단계를 연결하기 위해 Neat이 필요합니다. | Unspecified; 객체 감지 디코딩 의도가 없습니다. |
decode_type_option | 객체 감지 헤드 텐서 순서에 특정 변형이 필요합니다. | Auto; 모델 계약 및 경로 계획자가 결정하도록 합니다. |
score_threshold | 디코딩 중에 낮은 신뢰도의 후보를 제거하려는 경우. | 0; 이후 필터링 전에 후보를 유지합니다. |
nms_iou_threshold | 디코딩 경로에 비최대값 억제(NMS)를 적용해야 합니다. | 0; 모델에 패키징된 값을 유지합니다. 패키징된 값이 없으면 YOLO26은 NMS를 건너뛰고, 다른 디코더는 겹치는 모든 박스를 억제합니다. |
top_k | 출력당 감지 횟수에 제한을 두려는 경우. | 0; 상위 K 제한이 없습니다. |
num_classes | 클래스 헤드 깊이를 안정적으로 추론할 수 없는 경우(예: 단일 클래스 YOLO 분할 헤드). | 0; MPK 메타데이터 또는 이전 추론을 사용합니다. |
대부분의 YOLO 스타일 모델의 경우 decode_type, score_threshold, nms_iou_threshold 및 top_k로 시작하세요. 모델 계약에 도움이 필요한 경우에만 num_classes를 추가하세요. 텐서 순서에 특정 선택자가 필요한 것으로 알려지지 않은 한 decode_type_option을 Auto로 유지하세요.
simaai::neat::Model::Options options;
options.decode_type = simaai::neat::BoxDecodeType::YoloV8;
options.score_threshold = 0.25f;
options.nms_iou_threshold = 0.45f;
options.top_k = 100;
options.num_classes = 1; // Set only when the model contract needs the class count.
simaai::neat::Model model(model_path, options);
새 예제에서는 boxdecode_original_width 또는 boxdecode_original_height를 사용하지 마십시오. 좌표 반전은 전처리 메타데이터를 사용합니다. 이미지 크기를 하드 코딩하는 대신 해당 메타데이터를 유지하십시오.
원시 텐서 입력
model.input_specs()에 맞는 텐서를 제공할 때는 기본값을 사용하세요. Neat은 MPK에 필요한 텐서 어댑터를 유지합니다.
simaai::neat::Model model(model_path);
기존 preprocess.enable = Off 설정은 MLA 전용 실행을 선택하거나 MPK에 필요한 Cast/Quant/Tess 어댑터를 제거하지 않습니다. 비어 있지 않은 preprocess.transforms 목록은 Off보다 우선합니다.
전처리 필드 가이드
ModelOptions.preprocess는 어떤 종류의 입력을 제공할지, 그리고 Neat이 추론 전에 어떤 방식으로 조정할지를 나타냅니다.
| 필요 | 필드 |
|---|---|
| 이미지 또는 텐서 입력 선택 | preprocess.kind |
| 기존 전처리 제어(필수 MPK 어댑터는 유지됨) | preprocess.enable |
| 동적 이미지 입력 제한 | preprocess.input_max_width, preprocess.input_max_height, preprocess.input_max_depth |
| 크기 조정, 자르기 또는 레터박스 적용 | preprocess.resize |
| RGB, BGR, NV12, I420 또는 흑백으로 변환 | preprocess.color_convert |
| HWC, CHW 또는 다른 축 순서로 변환 | preprocess.layout_convert |
| 평균/표준 편차 정규화 적용 | preprocess.normalize 또는 preprocess.preset |
| 추론 전에 양자화 | preprocess.quantize |
| 추론 전에 테셀레이션 | preprocess.tessellate |
| 순 서가 지정된 변환 목록으로 재정의 | preprocess.transforms |
실제로 전송하는 입력에 대한 가장 작은 옵션 세트를 설정하세요. 사양은 계약이고, 결정된 전처리 계획은 영수증입니다.
사양 검토 및 직접 실행
Graph를 구성하기 전에 모델 계약을 검토하세요. 사양은 무엇을 할당하고, 전송하고, 디코딩해야 하는지 알려줍니다.
simaai::neat::Model model(model_path, options);
const auto inputs = model.input_specs();
const auto outputs = model.output_specs();
const auto metadata = model.metadata();
const auto info = model.info();
for (const auto& input : inputs) {
const auto& shape = input.shape;
}
for (const auto& output : outputs) {
const auto& shape = output.shape;
}
simaai::neat::Tensor input = simaai::neat::Tensor::from_cv_mat(
frame,
simaai::neat::ImageSpec::PixelFormat::BGR,
simaai::neat::TensorMemory::CPU);
simaai::neat::TensorList result = model.run(
simaai::neat::TensorList{input},
/*timeout_ms=*/2000);
Python에서는 입력 목록 또는 튜플을 전달합니다. model.run([tensor])는 “모델 입력 하나”를 의미합니다. 배치 차원을 추가하지 않습니다.
model.run(...)은 호출할 때마다 다시 빌드하는 방식이 아니라 한 번에 실행되는 방식입니다. 첫 번째 호출 시 내부 실행기를 지연 방식으로 빌드하고 캐시하며, 이후 호출에서는 해당 실행기를 재사용하고 이를 통해 데이터를 전달합니다. 라우팅 옵션, 런타임 옵션, 측정, 종료/비우기 제어 또는 프로듀서/컨슈머 루프가 필요한 경우 명시적인 model.build(...)를 사용하십시오.
OpenCV 지원으로 빌드된 C++는 model.run(std::vector<cv::Mat>{frame}, timeout_ms)를 노출합니다. 예제에서 픽셀 형식 또는 메모리 소유권을 명시해야 하는 경우 명시적인 Tensor::from_cv_mat(...) 경로를 사용하십시오.
여러 입력 및 배치와 함께 작업
모델은 여러 입력 포트, 컴파일된 배치 크기 또는 둘 다를 가질 수 있습니다. 이러한 개념을 혼합하지 마십시오.
- 여러 입력은 모델 입력당 텐서 하나를 의미합니다. 예상되는 순서와 계약을 확인하려면
model.input_specs()를 검사하십시오. - 배치는 모델이 추론당 여러 논리적 샘플을 처리하도록 컴파일되었음을 의미합니다.
model.compiled_batch_size()를 검사하십시오. - Python 입력 목록은 배치 차원이 아닌 입력 목록을 선택합니다.
model.run([tensor])는 입력 하나입니다.tensor를 배치 크기 1로 변환하지 않습니다.
const int batch = model.compiled_batch_size();
const auto specs = model.input_specs();
// Two-input model: pass one tensor per ingress.
simaai::neat::TensorList inputs{left_tensor, right_tensor};
simaai::neat::TensorList outputs = model.run(inputs, /*timeout_ms=*/2000);
일괄 처리 모델이 [N, ...] 형태를 예상하는 경우, 입력 사양에 따라 텐서 형태에 N을 넣습니다. 모델에 N 입력 포트가 없는 한, N 별도의 Python 목록 항목을 전달하지 마십시오.
라우트 계획 검사
전처리, 출력 토폴로지 또는 라우트 선택이 예상과 다를 경우, 추측하기보다는 라우트를 검사하십시오.
| 질문 | 검사 |
|---|---|
| 모델이 어떤 텐서 형태와 데이터 유형을 노출합니까? | input_specs() 및 output_specs() |
| 컴파일된 아티팩트는 무엇을 선언했습니까? | info() 및 metadata() |
| 전처리는 어떤 입력을 예상합니까? | preprocess_requirements() |
| Neat은 어떤 전처리 경로를 컴파일했습니까? | resolved_preprocess_plan() / Python preprocess_plan() |
| 어떤 배치 크기가 컴파일되었습니까? | compiled_batch_size() |
| 어떤 출력 형식을 디코딩해야 합니까? | 출 력 사양 + info().output_topology |
const auto requirements = model.preprocess_requirements();
const auto plan = model.resolved_preprocess_plan();
std::cout << "preprocess: " << plan.to_debug_string() << "\n";
해결된 계획은 감사 추적 기록입니다. 여기에는 요청된 옵션, 적용된 옵션, 그래프 계열, 입력 계약, MLA 계약 및 경고가 포함됩니다.
모델 경로 스냅샷을 읽습니다.
모델 아카이브를 직접 읽지 않고 간결한 경로 스냅샷이 필요한 경우 info()를 사용합니다. Python은 또한 빠른 텍스트 보기를 위해 summary()를 제공합니다. C++는 구조화된 info() 결과를 사용합니다.
| 질문 | 검사할 info() 필드 |
|---|---|
| 어떤 아티팩트를 로드했습니까? | model_name, mpk_json_path |
| 어떤 어댑터 단계가 필요합니까? | needs |
| 어떤 어댑터 단계를 사용할 수 있습니까? | capabilities |
| 경로에 전처리 또는 후처리가 포함되었습니까? | selection.include_preprocess_stage, selection.include_postprocess_stage |
| 이것은 추론 전용 경로입니까? | selection.infer_only |
| 어떤 전처리 그래프 또는 후처리 유형이 선택되었습니까? | selection.preprocess_graph, selection.selected_post_kind |
| 물리적 출력과 논리적 출력의 수는 얼마입니까? | output_topology.physical_outputs, logical_outputs, packed_outputs |
| 경로 플래너에서 어떤 경고가 발생했습니까? | warnings |
const auto info = model.info();
std::cout << "model=" << info.model_name << "\n";
std::cout << "physical_outputs=" << info.output_topology.physical_outputs
<< " logical_outputs=" << info.output_topology.logical_outputs << "\n";
for (const auto& warning : info.warnings) {
std::cerr << warning << "\n";
}
경로 경고를 가장 중요한 증거로 취급합니다. 경로 계획 도구가 이상한 정보를 제공하는 경우, 무시하지 말고 문제를 해결하십시오. 먼저 계약 또는 옵션을 수정하십시오.
모델이 실행하는 내용 확인
execution_plan()을 사용하면 모델이 실행하는 노드, 각 노드가 실행되는 위치, 노드 사이를 흐르는 텐서를 확인할 수 있습니다. Model::execution_plan(...)은 build(...)와 같은 인수를 받아 프레임을 실행하지 않고 모델을 빌드한 뒤, 계획을 읽고 닫습니다. 따라서 디바이스가 필요합니다.
const simaai::neat::ExecutionPlan plan = model.execution_plan();
std::cout << plan.to_json() << "\n";
JSON은 모델의 그래프 노드를 파이프라인 순서로 나열하고, 연속한 두 노드 사이마다 하나의 에지를 둡니다.
| 필드 | 의미 |
|---|---|
model | 모델 이름, batch(MLA 1회 실행당 프레임 수), memory(처리 중인 프레임당 디바이스 메모리) |
nodes[].kind | 노드 종류(예: Input, Preproc, Quant, ModelFragment, Dequant, SimaBoxDecode, Output) |
nodes[].runs_on | 노드가 실행되는 위치: A65(Arm 코어), EV74(비전 프로세서), 또는 MLA |
nodes[].kernel | EV74 또는 A65 노드가 실행하는 커널 |
nodes[].settings | 확정된 Preproc 및 SimaBoxDecode 설정이며, 이름은 PreprocOptions 및 BoxDecodeOptions와 같습니다 |
edges[].tensors | 한 노드에서 다음 노드로 가는 텐서: 배치 우선 shape, dtype, 양자화된 텐서의 quant(scale, zero_point) |
SimaBoxDecode 설정은 모델이나 옵션에서 지정한 경우에만 임계값과 top_k를 표시합니다. 지정하지 않으면 디코더 기본값이 적용됩니다. 스키마는 schemas/execution_plan_v1.schema.json입니다.
그래프에서 모델 사용
일반적인 경로의 경우, 공개 입력과 출력 사이에 모델을 직접 추가합니다.
simaai::neat::Graph graph("classifier");
graph.add(simaai::neat::nodes::Input("image"));
graph.add(model);
graph.add(simaai::neat::nodes::Output("classes"));
기본 공개 Input이 한 모델의 이미지 입력에 직접 연결되면 해당 입력의 이미지 해석과 형식을 상속합니다. 소스 형식을 명시적으로 지정할 수도 있으며 그래프는 모델 계약과 일치하는지 검증합니다. 소스 메모리, 크기, 타임스탬프, 큐 옵션은 변경되지 않습니다. 소비자가 여러 개이거나 연결이 모호하면 입력 옵션을 명시해야 합니다.
이 해석은 Graph 입력 경계에 적용됩니다. 모델을 직접 호출할 때는 여전히 모델이 선언한 이미지 또는 텐서 계약을 만족하는 입력이 필요합니다.
재사용 가능한 코드 조각이나 명확한 경로 경계가 필요할 때 model.graph(...)를 사용하세요.
경로 옵션은 해당 코드 조각이 계약을 어떻게 노출할지 제어합니다.
| 필요 사항 | 경로 옵션 |
|---|---|
| 반환된 그래프에 공개 입력 노드를 추가 | include_input |
| 반환된 그래프에 공개 출력 노드를 추가 | include_output |
| 별도의 물리적 출력을 노출 | expose_all_outputs |
| 생성된 요소 이름의 중복을 방지 | name_suffix |
| 특정 상위 요소 이름에서 연결 | upstream_name |
| 이 경로에 대한 고급 실행을 재정의 | advanced_execution |
simaai::neat::Model::RouteOptions route;
route.include_input = true;
route.include_output = true;
route.name_suffix = "_detector";
simaai::neat::Graph model_fragment = model.graph(route);
경로 경계는 일종의 계약입니다. 경로의 일부를 공개 입력 또는 출력으로 노출하려는 경우 경로 경계를 추가합니다. 모델이 더 큰 그래프 내의 단일 단계일 뿐인 경우에는 경로 경계를 추가하지 않습니다.
고급 구성을 위해 단계 조각 사용
대부분의 앱은 전체 모델 경로를 추가해야 합니다. 경로를 의도적으로 구성하거나 디버깅할 때만 단계 조각을 사용합니다.
simaai::neat::Graph preprocess =
model.fragment(simaai::neat::Model::Stage::Preprocess);
simaai::neat::Graph inference =
model.fragment(simaai::neat::Model::Stage::Inference);
simaai::neat::Graph postprocess =
model.fragment(simaai::neat::Model::Stage::Postprocess);
고급 헬퍼는 진단 및 의도적인 구성에 사용됩니다. 초기 실행 코드에서는 사용하지 마십시오.
| 헬퍼 | 언어 | 사용 시점 |
|---|---|---|
backend_fragment(stage) | C++ 및 Python | 디버깅 중에 특정 모델 단계를 검사해야 할 때 백엔드 단편을 검사합니다. |
input_appsrc_options(tensor_mode) | C++ 및 Python | 단일 입력 모델에는 입력 경계에 대한 InputOptions Neat 파생이 필요합니다. |
input_appsrc_options_list(tensor_mode) | C++ | 다중 입력 모델에는 각 입력에 대한 InputOptions가 필요합니다. |
find_config_path_by_plugin(plugin_id) | C++ 및 Python | 증거를 수집하는 동안 특정 플러그인에 대한 추출된 구성 파일이 필요합니 다. |
find_config_path_by_processor(processor) | C++ 및 Python | MLA, CVU 또는 APU와 같은 프로세서에 대한 구성 파일이 필요합니다. |
infer_output_name() | C++ 및 Python | 진단을 위해 추론 단계의 표준 출력 요소 이름이 필요합니다. |
헬퍼가 백엔드 이름 또는 추출된 파일을 노출하는 경우 결과를 애플리케이션 제어 흐름이 아닌 증거로 처리합니다. 공개 Model, Graph, Run, Input 및 Output API는 일반적인 애플리케이션 경로를 따라야 합니다.
기본 설정을 기준으로 모델 실행을 조정하십시오.
대부분의 모델은 먼저 기본 실행 설정으로 실행해야 합니다. 올바른 기본 설정이 설정되면 고급 실행 필드를 사용하여 한 번에 하나의 변경 사항을 테스트합니다. 하나의 설정을 변경하고, 측정하고, 기본 경로를 제어 경로로 유지합니다.
advanced_execution은 각 필드가 의도를 직접적으로 나타내기 때문에 새로운 문서 및 예제에 대한 선호되는 옵션 표면입니다. 설정되지 않은 필드는 아무런 영향이 없습니다. 경로 수준 옵션은 해당 경로에 대한 모델 수준 옵션을 재정의합니다.
| 필요 | 필드 | 참고 사항 |
|---|---|---|
| 모델에서 관리하는 전처리 실행 위치 선택 | advanced_execution.preprocess_target | "AUTO", "A65" 또는 "EV74"와 같은 대상 토큰은 위치를 증명할 때만 사용하십시오. |
| 모델에서 관리하는 후처리 실행 위치 선택 | advanced_execution.postprocess_target | 하나 이상의 대상에서 실행할 수 있는 후처리 단계에 사용합니다. |
| 전처리를 비동기적으로 실행하도록 허용 | advanced_execution.preprocess_async | 활성화하기 전후에 지연 시간과 처리량을 측정합니다. |
| MLA 추론을 비동기적으로 실행하도록 허용 | advanced_execution.inference_async | 작업을 계속 진행하는 스트리밍 워크로드에 유용합니다. |
| 추론 출력 버퍼링 조정 | advanced_execution.inference_output_buffers | 측정 결과 출력 버퍼링이 병목 현상인 경우에만 늘립니다. |
| 출력 캐시 동기화를 연기 | advanced_execution.defer_output_cache_sync | 고급 기능입니다. 소비자 및 메모리 수명이 이해될 때만 사용하십시오. |
| 내부 동시 실행 조정 | advanced_execution.internal_queue_depth | 내부에 삽입되는 큐와 수치 연산 Runner 실행 용량을 제한하며, MLA 출력 버퍼링을 재정의하지 않습니다. |
제한된 범위의 작업 중첩, 입력 수명 및 프레임당 CPU 측정은
큐를 무한히 늘리지 않고 프레임 처리 중첩하기를 참조하세요.
유지된 prepared_runner 필드는 소스 호환성을 위한 것이며, 선택 가능한
실행 백엔드가 아닙니다. 기본값을 유지하세요. 폐기된 모드는 그래프 구성 시 실패합니다.
실험과 일치하는 가장 좁은 범위에서 고급 실행 필드를 설정하십시오.
simaai::neat::Model::RouteOptions route;
route.name_suffix = "_lane0";
route.advanced_execution.inference_async = true;
auto runner = model.build(route);
기본 경로를 제어 기준으로 유지합니다. 하나의 필드를 변경하고, 측정하고, 증거를 내보낸 다음, 해당 변경 사항이 앱에 포함될 가치가 있는지 결정합니다.
C++는 또한 Model::Options, Model::RouteOptions 및 GraphOptions에서 더 낮은 수준의 processcvu, processmla, prepared_runner 및 async_queue_depth 필드를 노출합니다. 기존 C++ 코드를 유지하거나 특정 하위 수준 필드를 디버깅하는 경우가 아니라면 고객 예제에 서는 advanced_execution을 사용하는 것이 좋습니다.
모델 러너 재사용
각 입력에 대해 경로를 다시 빌드하는 것을 피하려는 경우 build(...)를 사용합니다.
auto runner = model.build();
// Convenience path: push one input and wait for its output.
simaai::neat::TensorList result = runner.run(
simaai::neat::TensorList{input},
/*timeout_ms=*/2000);
// Async path: push now, pull later.
runner.push(simaai::neat::TensorList{input});
simaai::neat::Sample sample = runner.pull(/*timeout_ms=*/2000);
runner.close_input();
runner.close();
각 호출마다 하나의 입력을 전달하고 해당 입력에 대한 출력을 기다려야 할 때는 runner.run(...)을 사용합니다. 애플리케이션에서 작업을 계속 진행하려는 경우 push(...) / pull(...)을 사용합니다. 더 이상 입력이 들어오지 않고 남은 작업을 완료하려는 경우 close_input()을 사용합니다. 러너가 작업을 완료했을 때는 close()를 사용합니다.
push는 입력 큐에 공간이 생길 때까지 기다립니다. 기다리지 않고 제출하려면 C++에서는 Sample 또는 TensorList를 받는 runner.try_push, Python에서는 runner.try_push_samples / runner.try_push_tensors를 사용합니다. 러너를 빌드하기 전에 RunOptions.queue_depth와 overflow_policy를 설정합니다. 제한된 큐가 가득 차면 KeepLatest는 가장 오래된 대기 샘플을 교체하고 true를 반환하며, Block과 DropIncoming은 false를 반환합니다. 입력이 닫혔을 때도 false를 반환합니다. 한 프레임에 대응하는 텐서들은 하나의 TensorSet 샘플에 함께 넣습니다. 교체는 실행 중인 추론을 취소하지 않으며 전달 메서드는 페이로드를 복사하지 않습니다.
특정 경로에 대한 러너 구축
재사용 가능한 러너가 기본 경로와 다른 경로 수준의 실행 설정 또는 경로 이름을 사용해야 할 때는 특정 경로에 대한 러너를 사용합니다. Model::build(...)는 러너의 입력 및 출력 경계를 자동으로 설정합니다.
simaai::neat::Model::RouteOptions route;
route.name_suffix = "_detector";
auto runner = model.build(route);
실제 입력이 첫 번째 프로덕션 배포 전에 형태, 형식 또는 페이로드 호환성을 입증해야 할 때 빌드에 시드 값을 설정합니다.
auto runner = model.build(simaai::neat::TensorList{input}, route);
증거 확보 및 조기 오류 감지를 위해 시드 빌드를 사용하되, 습관처럼 사용하지 마십시오. 기본 빌드가 이미 계약 조건을 충족하는지 확인되면 예제 크기를 줄이십시오.
이미지 입력에서 시드는 현재 형태와 픽셀 형식을 제공하며 preprocess.input_max_width, input_max_height, input_max_depth를 대체하지 않습니다. 예를 들어 용량을 1920×1920으로 선언하고 640×640 시드를 사용하면 같은 러너에서 이후의 800×800 및 1920×1080 프레임을 수용합니다. 용량을 초과하는 프레임은 거부됩니다. 한도를 생략하면 시드 없는 빌드와 동일하게 결정된 용량이 유지됩니다.
모델 실행 측정
빠른 합성 모델 벤치마크를 위해 model.benchmark(...)를 사용하십시오. input_specs()에서 입력을 생성하고 주요 지연 시간, 처리량, 전력 및 보드 텔레메트리를 반환합니다. 이를 사용하여 모델/런타임 변경 사항을 비교하되, 프로덕션 애플리케이션의 처리량을 주장하는 데 사용하지 마십시오.
simaai::neat::BenchmarkReport report = model.benchmark();
std::cout << "latency_ms=" << report.latency_ms
<< " fps=" << report.fps << "\n";
BoxDecode 경로의 경우, 합성 입력 이미지의 원본 이미지 기하학적 정보를 설명하여 벤치마크가 모델 좌표에서 감지 결과를 역으로 매핑할 수 있도록 합니다.
simaai::neat::BenchmarkOptions options;
options.num_samples = 100;
options.original_width = 1920;
options.original_height = 1080;
options.resize_mode = simaai::neat::ResizeMode::Letterbox;
simaai::neat::BenchmarkReport report = model.benchmark(options);
original_width와 original_height를 함께 설정합니다. 벤치마크는 여전히 모델 형태의 합성 텐서를 생성합니다. 이 필드는 BoxDecode 메타데이터에 필요한 소스 지오메트리와 크기 조정 변환을 제공합니다. 이 필드를 생략하면 벤치마크는 해결된 전처리 및 모델 입력 계약에서 지오메트리를 추론합니다. 이러한 실행별 값은 ModelOptions의 더 이상 사용되지 않는 BoxDecode 지오메트리 힌트보다 우선합니다. 기존 benchmark(num_samples) 호출은 동일한 동작을 유 지합니다.
MLA 레이어별 또는 스테이지별 시간도 측정하려면 include_mla_layer_stats 또는 include_mla_stage_stats를 설정합니다. 결과를 보관하려면 report.save(path)로 보고서를 YAML로 저장하며, 경로가 .json으로 끝나면 JSON으로 저장합니다. 두 가지 예는 모델의 성능을 평가해 보세요.에서 확인하세요.
직접 준비한 입력으로 측정
감지 또는 세그멘테이션 디코더처럼 비용이 데이터에 따라 달라지는 모델은 합성 입력으로 정확히 측정되지 않을 수 있습니다. 실제 데이터로 측정하려면 run()에 전달할 텐서를 모델 입력마다 하나씩 inputs에 설정하십시오. 벤치마크는 이 텐서를 지연 시간과 처리량 측정 모두에 사용합니다.
cv::Mat image;
cv::cvtColor(cv::imread("bus.jpg"), image, cv::COLOR_BGR2RGB); // the format input_specs() reports
simaai::neat::BenchmarkOptions options;
options.inputs = simaai::neat::TensorList{
simaai::neat::Tensor::from_cv_mat(image, simaai::neat::ImageSpec::PixelFormat::RGB,
simaai::neat::TensorMemory::EV74)};
simaai::neat::BenchmarkReport report = model.benchmark(options);
std::cout << "input=" << report.input << " fps=" << report.fps << "\n"; // input=provided
입력의 개수, 형태 또는 데이터 형식이 모델과 맞지 않으면 benchmark()는 시작하기 전에 오류 코드 misconfig.input_shape와 함께 NeatError를 던집니다. 경로가 변환할 수 없는 색상 형식은 run()과 마찬가지로 파이프라인을 만들 때 실패합니다. 이미지를 전처리하는 경로에서는 이미지가 어떤 크기든 사용할 수 있습니다. 색상 형식은 input_specs()가 보고하는 형식을 사용하십시오.
실행 길이
벤치마크는 단일 실행(single-flight) 지연 시간을 측정한 다음, 별도의 처리량 패스를 실행합니다. 각 패스는 최소 num_samples개의 입력을 측정하고 min_duration_ms(기본값 10초)가 경과할 때까지 계속되므로, 두 패스는 같은 규칙으로 멈추고 처리량 패스의 전력 및 온도 측정값은 짧은 버스트가 아니라 여러 텔레메트리 샘플에 걸쳐 수집됩니다. latency_ms는 지연 시간 패스의 평균 지연 시간이며, latency_p50_ms, latency_p95_ms, latency_p99_ms는 그 백분위수입니다. throughput_samples와 throughput_duration_ms는 처리량 패스에서 측정한 내용을 보고합니다. 빠른 스모크 테스트를 하려면 min_duration_ms를 0으로 설정하여 각 패스가 num_samples개의 입력 후에 중지하도록 하십시오.
simaai::neat::BenchmarkOptions options;
options.min_duration_ms = 0;
simaai::neat::BenchmarkReport report = model.benchmark(options);
전력 및 보드 텔레메트리
전력 및 보드 텔레메트리는 Neat 설치 프로그램이 보드에 설정하는 DevKit 텔레메트리 데몬인 Sentinel에서 제공됩니다.
벤치마크는 처리량 패스 전후로 TelemetryMonitor를 사용하여 이를 기록합니다.
모든 필수 출력이 완료된 후 측정을 종료하며, 측정 시작 이후부터 종료 이전까지의 샘플을 선택합니다.
준비, 모니터 작업자 조인 및 마지막 Sentinel 읽기는 측정되는 작업 시간을 늘리지 않습니다.
샘플 타임스탬프가 잘못되었거나 시계의 불연속이 감지되면 무관한 구간을 선택하지 않고
벤치마크 텔레메트리를 사용할 수 없는 것으로 처리합니다. report.telemetry는 결과를 요약합니다:
| 필드 | 의미 |
|---|---|
avg_power_watts, peak_power_watts | 처리량 패스 동안의 SoM 전력 추정 평균값과 추정 최고값입니다. |
energy_joules | num_samples개의 입력을 처리하는 데 필요한 에너지입니다. 평균 전력에, 측정된 처리량으로 num_samples개의 입력을 처리하는 데 걸리는 시간을 곱한 값입니다. 패스가 길어져도 이 값은 증가하지 않습니다. |
telemetry.power_mla_watts, telemetry.power_rails_watts | MLA 레일 전력 및 보드가 보고하는 모든 레일의 전력입니다. MLA 레일이 없는 보드는 이를 samples == 0으로 보고합니다. |
telemetry.die_temperatures_c, telemetry.die_temperature_max_c | MLA, APU, CVU, TOP 위치의 온다이 센서와, 각 샘플에서 그중 가장 높은 온도입니다. |
telemetry.board_temperatures_c | SoM 상단 및 하단 센서입니다. |
telemetry.cpu_usage_pct | CPU 사용률입니다. 각 샘플은 직전 구간의 평균이므로 maximum은 가장 바쁜 구간입니다. |
telemetry.cpu_cores_usage_pct | 코어 순서대로 나열한 각 CPU 코어의 사용률입니다. 전체 코어 평균인 cpu_usage_pct가 낮게 유지되어도 바쁜 코어 하나가 여기에 표시됩니다. |
telemetry.mla_memory_mib | MLA가 보유한 메모리(MiB)입니다. MLA 메모리 풀의 버퍼와 MLA만 사용하는 공유 버퍼를 포함합니다. 모델은 로드될 때 이 메모리를 할당하므로, 다른 작업이 실행 중이지 않다면 이 값이 모델의 MLA 메모리입니다. |
telemetry.ev74_memory_mib | EV74가 보유한 메모리(MiB)입니다. 예를 들어 EV74가 MLA를 위해 전처리하는 입력 프레임이 여기에 해당합니다. EV74와 MLA가 함께 사용하는 공유 버퍼는 mla_memory_mib가 아니라 여기에 집계되므로, 두 값을 더하면 모델의 가속기 메모리가 됩니다. 비디오 디코더나 카메라 ISP가 만든 프레임은 어느 쪽에도 포함되지 않습니다. |
telemetry.linux_memory_used_mib | 보드 전체가 사용하는 메모리(Linux MemTotal - MemAvailable, MiB)입니다. 모든 프로세스와 EV74의 버퍼를 포함하므로 위의 두 값에 더하지 마십시오. |
각 지표는 samples, mean, minimum, maximum, last를 보고하며, samples == 0은 보드가 해당 지표를 보고하지 않았음을 의미합니다. 보고된 0도 샘플로 계산됩니다. 모델이 로드되지 않은 경우 mla_memory_mib와 ev74_memory_mib는 samples > 0인 상태로 0 MiB를 나타냅니다. Sentinel은 2초마다 샘플링하므로 10초 패스에서는 약 5개의 샘플이 수집됩니다. 모든 값은 보드 전체 기준이며 실행 중인 다른 프로세스도 포함합니다.
Sentinel이 실행 중이 아니거나 패스가 너무 짧아 샘플을 수집하지 못한 경우에도 벤치마크는 지연 시간과 FPS를 반환합니다. 전력 필드는 0으로 유지되고, telemetry.status에 그 이유가 표시되며(정상인 경우 "ok"), 출력되는 요약에는 Power: unavailable (<reason>)이 표시됩니다.
자체 루프 측정
입력 루프를 직접 제어하고 해당 루프를 측정하려는 경우 러너 측정을 사용합니다.
auto runner = model.build();
auto scope = runner.start_measurement();
for (const auto& input : inputs) {
(void)runner.run(simaai::neat::TensorList{input}, /*timeout_ms=*/2000);
}
simaai::neat::MeasureReport report = scope.stop();
runner.close();
단발성 연기 테스트와 재사용 가능한 러너를 비교하지 마십시오. 두 가지는 서로 다른 질문에 대한 답을 제공합니다.
보드 텔레메트리 기록
자체 추론 루프처럼 원하는 구간 동안 전력, 온도, CPU 사용률, 메모리를 기록하려면 TelemetryMonitor를 사용하십시오. 이 값들은 Sentinel에서 읽으며, report.telemetry와 같은 Telemetry 요약을 반환합니다.
simaai::neat::TelemetryMonitor monitor;
monitor.start();
for (const auto& input : inputs) {
(void)runner.run(simaai::neat::TensorList{input}, /*timeout_ms=*/2000);
}
simaai::neat::Telemetry telemetry = monitor.stop();
std::cout << telemetry; // YAML
telemetry.save("telemetry.json"); // .json writes JSON; other names write YAML
모니터는 기본적으로 /run/simaai-sentinel/api.sock을 읽습니다. 다른 경로에 마운트된 Sentinel Unix 소켓을 사용하려면 C++에서는 TelemetryMonitor(socket_path), Python에서는 pyneat.TelemetryMonitor(socket_path="...")에 해당 경로를 전달하십시오. 이 설정은 해당 모니터의 엔드포인트만 선택하며 Model::benchmark()를 변경하거나 Sentinel을 시작하지 않습니다. 생성 시에는 소켓에 연결하지 않습니다.
start()는 새 구간을 시작합니다. 그 이후에 Sentinel이 수집한 샘플만 집계되며, Sentinel은 2초마다 샘플링하므로 이보다 짧은 구간에는 샘플이 없을 수 있습니다. 이 경우status가 이를 알려 줍니다.read()는 중지하지 않고 지금까지의 요약을 반환합니다. 각 측정값의last는 가장 최근 값이며,mean,minimum,maximum은 지금까지의 구간을 대상으로 합니다.stop()은 최종 요약을 반환합니다. 다시 호출해도 같은 결과를 반환합니다.- 요약은 측정값마다 항목 하나만 유지하므로, 구간이 아무리 길어도 메모리 사용량은 일정합니다.
- 한 프로세스 안에서든 여러 프로세스에서든 여러 모니터를 서로 동시에, 그리고
Model::benchmark()와 동시에 실행할 수 있습니다. - 모든 값은 Sentinel, 모니터, 다른 프로세스를 포함한 보드 전체 기준입니다. 각 전력 값은 샘플마다 한 번 수행하는 250ms 측정에서 나오므로, 측정 사이의 짧은 급증은 나타나지 않습니다.
- Sentinel이 실행 중이 아니면 모니터는 경고를 한 번 출력하고,
status는 Sentinel을 설치하거나 시작하는 방법을 설명하며, 모니터는 Sentinel이 응답하는 시점부터 샘플을 집계합니다. Sentinel 문제로 예외를 던지지 않습니다. - Python에서
start(),read(),stop()은 대기하는 동안 GIL을 해제합니다.
출력 디코딩
먼저 출력 사양을 확인하십시오. 모델 출력 계약에서 페이로드가 감지, 자세 또는 분할 형식인지 확인한 경우에만 디코딩하십시오.
| 출력 계약 | 사용법 |
|---|---|
| 원시 텐서 | 반환된 텐서를 직접 사용하거나 Python에서 to_numpy(...) / to_torch(...)를 사용하여 변환합니다. |
| 텐서로 패킹된 박스 | C++ simaai::neat::decode_bbox(...) / Python pyneat.decode_bbox(...) |
| 유형이 지정된 레코드로 패킹된 박스 | C++ simaai::neat::decode_bbox_tensor(...) / Python pyneat.detections.decode_bbox_tensor(...) |
| 패킹된 자세 | C++ simaai::neat::decode_pose(...) / Python pyneat.decode_pose(...) |
| 패킹된 분할 | C++ simaai::neat::decode_segmentation(...) / Python pyneat.decode_segmentation(...) |
다음 단계에서 텐서가 필요한 경우 텐서 디코딩을 사용하십시오. 애플리케이션 코드가 x1, y1, x2, y2, score 및 class_id를 포함하는 레코드를 원하는 경우 유형 지정 디코딩을 사용하십시오.
const auto decoded = simaai::neat::decode_bbox_tensor(
outputs[0],
image_width,
image_height,
/*expected_topk=*/100,
/*strict=*/true);
for (const auto& box : decoded.boxes) {
handle_box(box.x1, box.y1, box.x2, box.y2, box.score, box.class_id);
}
해당 형식에서 예상하는 출력 형식에만 디코딩 도우미를 사용하세요.
오류 처리
모델 기반 그래프는 원시 Graph와 동일한 진단 계약을 사용합니다.
- 치명적인 오류의 경우
NeatError.report().error_code를 사용합니다. - 문제 해결에 도움이 되는 정보와 힌트를 제공하기 위해
NeatError.report().repro_note를 사용합니다. - 플러그인 또는 런타임 관련 세부 정보를 제공하기 위해
NeatError.report().bus를 사용합니다.
try {
auto result = model.run(simaai::neat::TensorList{input}, /*timeout_ms=*/2000);
} catch (const simaai::neat::NeatError& error) {
const auto& report = error.report();
std::cerr << report.error_code << "\n";
std::cerr << report.repro_note << "\n";
}
misconfig.*, build.*, runtime.* 또는 io.*와 같은 error_code 값으로 시작합니다. 구조화된 오류 메시지가 가리키는 곳을 확인한 후에만 버스 로그를 읽으세요.