メインコンテンツまでスキップ

モデルの出力の読み込みと解釈

項目
カテゴリモデルと推論
難易度中級
推定所要時間10-15 minutes
ラベルoutput, patterns, sink

スループットを最適化したり、複雑なグラフロジックを追加したりする前に、実行によって返されるものを読み取るための、安定した防御的な方法が必要です。出力は常にSampleですが、その形状は異なります。単一のテンソルである場合もあれば、名前付きフィールドのバンドルである場合もあります(第009章参照)。バンドルから.tensorにアクセスしたり、存在しない形状を想定したりすることは、この章で回避することを学ぶバグです。

以前と同じ最小限の同期グラフを構築し、1フレームを実行し、次に結果を体系的に検査します。その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.

1フレームの実行

1フレームをプッシュし、1つの結果を同期的に取得します。単一のrun(...)呼び出しは、1フレームのショートカットです。その戻り値は、ここで分析するオブジェクトです。

run.run(...)TensorListを返します。単一のテンソル出力の場合、これは1つのエントリを意味し、次のステップでout.size()out.front()を使用して検査します。

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からランクを導き出します。各ステップ(空でない出力、空でない形状)を保護することで、形状を制御できないモデルに対して堅牢な出力リーダーを作成できます。

out.size()とテンソルの存在を報告し、空の場合、またはout.front().shapeが空の場合に例外をスローし、次にrankshape.size()から印刷します。(fields=0行はプレースホルダーです。TensorListは、PythonのSampleが持つバンドルフィールド構造を運びません。)

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

実行

Python および C++(事前にビルドされたもの) コマンドを、Neat のインストールルートshare/lib/ を含むディレクトリ)から実行します。ソースコードからビルド コマンドは、リポジトリのルートから実行します。この章では、モデルアーカイブは必要ありません。

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

ソース