跳至主要内容

讀取並解讀模型輸出

欄位
類別模型與推論
難度中級
預估閱讀時間10-15 minutes
標籤output, patterns, sink

在您最佳化吞吐量或新增複雜圖邏輯之前,您需要一種穩定且可靠的方法來讀取執行階段傳回的任何內容。輸出始終是一個 Sample,但其形狀會有所不同:它可能是一個單一的張量,或者是一組帶有名稱的欄位(如第 009 章所述)。嘗試從一組欄位中獲取 .tensor,或者假設不存在的形狀,這就是本章要教您避免的錯誤。

我們建置與之前相同的最小同步圖,執行一個框架,然後有系統地檢查結果:其 kind,是否存在張量,有多少個欄位,以及張量的秩。到最後,您將擁有一個可重複使用的輸出讀取模式,適用於執行階段服務的任何模型。

操作指南

設定輸入

宣告輸入合約——像素 formatwidthheightdepth——與我們將推送的框架匹配。這是整個章節中使用的相同邊界合約。

tutorials/011_interpret_model_output/interpret_model_output.cpp
simaai::neat::InputOptions in;
in.format = "RGB";
in.width = rgb.cols;
in.height = rgb.rows;
in.depth = rgb.channels();

組合並建置圖

將輸入節點連接到輸出節點,並 build() 成一個同步 Run,傳遞框架,以便 build() 可以協商具體的形狀。由於中間沒有模型,因此輸出反映了輸入——這正是使其成為研究輸出結構的理想場所的原因。

tutorials/011_interpret_model_output/interpret_model_output.cpp
simaai::neat::Graph graph;
graph.add(simaai::neat::nodes::Input(in));
graph.add(simaai::neat::nodes::Output());
// Use Graph::run(...) for a one-shot synchronous frame.

執行一個框架

推送一個框架並同步提取一個結果。單個 run(...) 呼叫是單框架快捷方式;它傳回的內容是我們在此要剖析的對象。

C++: run.run(...) 傳回一個 TensorList——對於單張量輸出,這意味著一個條目,下一步將通過 out.size()out.front() 檢查該條目。

Python: run.run(...) 傳回一個 Sample,直接公開 .kind.tensor.tensors.fields

tutorials/011_interpret_model_output/interpret_model_output.cpp
// Graph::run is the one-frame synchronous shortcut.
simaai::neat::TensorList out = graph.run(std::vector<cv::Mat>{rgb});

檢查樣本

這就是本節的重點:在讀取有效負載之前,先讀取結構。首先檢查存在性和類型,然後從張量的 shape 中推導出秩。保護每個步驟(非空輸出、非空形狀)是使輸出讀取器能夠可靠地處理您無法控制其形狀的模型的原因。

C++: 報告 out.size() 和張量的存在性,如果為空或 out.front().shape 為空,則拋出異常,然後從 shape.size() 中列印 rank。(fields=0 行是一個佔位符——TensorList 不會攜帶 Python Sample 攜帶的欄位結構。)

Python: 輸出 sample.kindsample.tensor is not Nonelen(sample.fields),以及第一個張量的等級——將完整的總和類型表面整合在一個地方。對於張量類型的結果,存在 .tensor;對於張量集合的結果,讀取 .tensors;對於捆綁分支,讀取 .kind.fields

tutorials/011_interpret_model_output/interpret_model_output.cpp
std::cout << "outputs=" << out.size() << " has_tensor=" << (!out.empty() ? "yes" : "no")
<< " fields=" << 0 << "\n";
if (out.empty())
throw std::runtime_error("expected tensor output");
if (out.front().shape.empty())
throw std::runtime_error("output tensor shape is empty");
std::cout << "rank=" << out.front().shape.size() << "\n";

執行

Neat 安裝根目錄(包含 share/lib/ 的目錄)執行 PythonC++(預建版本) 命令;從 程式碼庫根目錄執行 從原始碼建置 命令。本章不需要模型封存檔。

C++ (prebuilt):

./lib/sima-neat/tutorials/tutorial_011_interpret_model_output

C++ (build from source):

./build.sh --target tutorial_011_interpret_model_output
./build/tutorials-standalone/tutorial_011_interpret_model_output

預期輸出(C++):

outputs=1 has_tensor=yes fields=0
rank=3
[OK] 011_interpret_model_output

Python 建置透過 Sample 表面輸出相同的資訊:

sample_kind=SampleKind.TensorSet
has_tensor=False
num_fields=0
output_rank=3

若要將本章的 C++ 原始碼整合到您自己的專案中,並使用自訂的 CMakeLists.txt(無需額外的資料夾),請參閱登陸頁面上的 如何執行教學

實務應用

用於讀取任何模型的輸出的防禦性檢查清單。

在讀取之前進行分類

  • 首先檢查 kind。單個張量結果為 SampleKind.Tensor;多欄位結果為 SampleKind.Bundle
  • 對於張量類型,存在 tensor,且 fields 為空。對於捆綁類型,讀取 fields,不要假設存在 tensor

驗證合約

  • 在取消引用張量之前,確認張量存在。
  • 在計算等級或索引維度之前,確認 shape 不為空。
  • 當消費者預期特定的元素類型時,檢查 tensor.dtype

完整原始碼

顯示完整原始碼程式
tutorials/011_interpret_model_output/interpret_model_output.cpp
// Inspect a Sample returned by a Graph: kind, tensor, fields, rank.
//
// Usage:
// tutorial_011_interpret_model_output

#include "neat.h"

#include <opencv2/core.hpp>

#include <iostream>
#include <stdexcept>

int main() {
try {
cv::Mat rgb(120, 160, CV_8UC3, cv::Scalar(110, 40, 30));
if (!rgb.isContinuous())
rgb = rgb.clone();

simaai::neat::InputOptions in;
in.format = "RGB";
in.width = rgb.cols;
in.height = rgb.rows;
in.depth = rgb.channels();

simaai::neat::Graph graph;
graph.add(simaai::neat::nodes::Input(in));
graph.add(simaai::neat::nodes::Output());
// Use Graph::run(...) for a one-shot synchronous frame.

// CORE LOGIC
// Graph::run is the one-frame synchronous shortcut.
simaai::neat::TensorList out = graph.run(std::vector<cv::Mat>{rgb});

std::cout << "outputs=" << out.size() << " has_tensor=" << (!out.empty() ? "yes" : "no")
<< " fields=" << 0 << "\n";
if (out.empty())
throw std::runtime_error("expected tensor output");
if (out.front().shape.empty())
throw std::runtime_error("output tensor shape is empty");
std::cout << "rank=" << out.front().shape.size() << "\n";
std::cout << "[OK] 011_interpret_model_output\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}

來源