跳至主要内容

模型

Model 會載入已編譯的模型封存檔,並公開 Neat 可以執行的路徑。

當您想要進行模型感知型執行,而無需手動設定每個預處理、推論和後處理階段時,請使用 Model。首先檢查合約。然後執行它。沒有神秘的張量。

Model 提供的功能​

  • input_specs() 和 output_specs() 會顯示模型預期和產生的張量合約。
  • metadata()、info() 和 Python summary() 可幫助您檢查已載入的模型。
  • run(...) 會執行單次直接推論。
  • build(...) 會建立一個可重複使用的模型執行器,用於推/拉執行。
  • graph() 會將模型路徑作為一個可重複使用的 Graph 片段傳回。
  • preprocess()、inference() 和 postprocess() 會公開路徑階段,以進行更進階的組合。

參考資料:

載入模型封存檔​

本頁面上的範例假設 model_path 指向來自 Model Compiler 的已編譯模型封存檔,通常是一個 .tar.gz 檔案,已複製到執行 Neat 的機器上。首先使用預設選項;只有在合約或輸入來源需要時,才新增 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 應將其調整為模型合約時,請使用此選項。
提供符合模型公開輸入的張量不設定前處理選項保留所需的 Cast/Quant/Tess 轉接器,包括已調整大小及正規化的 FP32 輸入。
解碼偵測輸出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您需要 Neat 將 BoxDecode 階段附加到物體檢測模型。Unspecified;沒有物體檢測解碼意圖。
decode_type_option物體檢測頭張量排序需要特定的變體。Auto;讓模型合約和路由規劃器決定。
score_threshold您希望在解碼期間捨棄低信度候選者。0;在後續篩選之前保留候選者。
nms_iou_threshold解碼路徑應該應用非最大值抑制。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 執行的影格數)和 memory(每個處理中影格的裝置記憶體)
nodes[].kind節點種類,例如 Input、Preproc、Quant、ModelFragment、Dequant、SimaBoxDecode 或 Output
nodes[].runs_on節點的執行位置:A65(Arm 核心)、EV74(視覺處理器)或 MLA
nodes[].kernelEV74 或 A65 節點執行的核心程式(kernel)
nodes[].settings已解析的 Preproc 與 SimaBoxDecode 設定,名稱與 PreprocOptions 和 BoxDecodeOptions 相同
edges[].tensors從一個節點流向下一個節點的張量:批次在前的 shape、dtype,以及量化張量的 quant(scale、zero_point)

只有在模型或您的選項設定了閾值與 top_k 時,SimaBoxDecode 設定才會顯示它們;否則會套用解碼器預設值。結構描述為 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++ 也會公開更底層的 processcvu、processmla、prepared_runner 和 async_queue_depth 欄位,這些欄位位於 Model::Options、Model::RouteOptions 和 GraphOptions 中。除非您要保留現有的 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);

請將「seeded build」用於驗證和及早發現問題,而不是將其視為一種習慣。如果預設建置已經證明符合規範,請保持範例的規模較小。

對於影像輸入,種子提供目前的形狀與像素格式,不會取代 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。兩者的範例請參閱為您的模型進行基準測試。

使用自己的輸入進行測量​

對於成本取決於資料的模型(例如偵測或分割解碼器),合成輸入可能無法反映實際情況。若要以真實資料測量,請將 inputs 設為您會傳給 run() 的張量,每個模型輸入各一個。基準測試會將這些張量同時用於延遲與吞吐量的測量。

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);

功耗與板子遙測​

功耗與板卡遙測來自 Sentinel,也就是 Neat 安裝程式在 DevKit 上 設定的遙測常駐程式。基準測試透過涵蓋吞吐量測試的 TelemetryMonitor 記錄這些資料。它選取量測開始之後、 所有必要輸出完成且量測停止之前的樣本。準備、等待監視執行緒結束,以及最後一次 Sentinel 讀取都不會延長計時工作負載。無效樣本時間戳記或偵測到時鐘不連續時, 基準測試遙測會變成無法取得,而不會選取不相關的時間視窗。 report.telemetry 會彙整如下:

欄位意義
avg_power_watts, peak_power_watts吞吐量階段期間 SoM 功耗的估計平均值與估計最高值。
energy_joules處理 num_samples 個輸入所需的能量:平均功耗乘以依測得的吞吐量處理 num_samples 個輸入所需的時間。較長的階段不會使其增加。
telemetry.power_mla_watts, telemetry.power_rails_wattsMLA 電源軌功耗,以及板子回報的每一個電源軌。沒有 MLA 電源軌的板子會以 samples == 0 回報該項。
telemetry.die_temperatures_c, telemetry.die_temperature_max_cMLA、APU、CVU 與 TOP 位置的晶粒內感測器,以及每個樣本中其中最高的溫度。
telemetry.board_temperatures_cSoM 頂面與底面感測器。
telemetry.cpu_usage_pctCPU 使用率。每個樣本為前一個區間的平均值,因此 maximum 代表最忙碌的區間。
telemetry.cpu_cores_usage_pct依核心順序列出的各 CPU 核心使用率。即使所有核心平均值 cpu_usage_pct 維持偏低,單一忙碌的核心也會顯示在這裡。
telemetry.mla_memory_mibMLA 持有的記憶體(MiB),包括 MLA 記憶體池中的緩衝區,以及只有 MLA 使用的共用緩衝區。模型在載入時分配這些記憶體,因此若沒有其他程式在執行,這就是模型的 MLA 記憶體。
telemetry.ev74_memory_mibEV74 持有的記憶體(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。功耗欄位會保持為零,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();

不要將一次性的煙霧測試與經過預熱的可重複使用的執行器進行比較。這兩者回答的是不同的問題。

記錄板子遙測​

使用 TelemetryMonitor 在您選擇的任何視窗(例如您自己的推論迴圈)記錄功耗、溫度、CPU 使用率與記憶體。它會從 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、監控器本身與其他程序。每個功耗數值來自每個樣本的一次 250 毫秒讀數,因此讀數之間的短暫尖峰不會顯示出來。
  • 當 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";
}

從 error_code 等值開始,例如 misconfig.*、build.*、runtime.* 或 io.*。僅在結構化錯誤訊息指示後,才閱讀匯流排日誌。

參閱​

教程​