モデルの出力の読み込みと解釈
| 項目 | 値 |
|---|---|
| カテゴリ | モデルと推論 |
| 難易度 | 中級 |
| 推定所要時間 | 10-15 minutes |
| ラベル | output, patterns, sink |
スループットを最適化したり、複雑なグラフロジックを追加したりする前に、実行によって返されるものを読み取るための、安定した防御的な方法が必要です。出力は常にSampleですが、その形 状は異なります。単一のテンソルである場合もあれば、名前付きフィールドのバンドルである場合もあります(第009章参照)。バンドルから.tensorにアクセスしたり、存在しない形状を想定したりすることは、この章で回避することを学ぶバグです。
以前と同じ最小限の同期グラフを構築し、1フレームを実行し、次に結果を体系的に検査します。そのkind、テンソルが存在するかどうか、フィールドの数、およびテンソルのランクを確認します。最終的には、ランタイムが提供するあらゆるモデルに対して機能する、再利用可能な出力読み取りパターンが得られます。
ウォークスルー
入力の構成
入力コントラクト(ピクセルのformat、width、height、depth)を宣言し、プッシュするフレームと一致させます。これは、これらの章全体で使用されるのと同じ境界コントラクトです。
simaai::neat::InputOptions in;
in.format = "RGB";
in.width = rgb.cols;
in.height = rgb.rows;
in.depth = rgb.channels();
グラフの作成と構築
入力ノードを出力ノードに接続し、build()して同期Runに変換し、フレームを渡して、build()が具体的な形状をネゴシエートできるようにします。間にモデルがないため、出力は入力とミラーリングされます。これは、出力構造を研究するのに最適な場所である理由です。
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()を使用して検査します。
// 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が空の場合に例外をスローし、次にrankをshape.size()から印刷します。(fields=0行はプレースホルダーです。TensorListは、PythonのSampleが持つバンドルフィールド構造を運びません。)
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を検査します。
完全なソース
完全なソースプログラムを表示
// 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;
}
}