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

グラフ内にモデルを埋め込む

項目
カテゴリグラフとパイプライン
難易度上級
推定所要時間20-25 minutes
ラベルgraph, hybrid, model, mpk

第3章では、単純な入力/出力ノードからグラフを構築しました。第1章では、モデルをスタンドアロンオブジェクトとして実行しました。この章では、これら2つを組み合わせます。Model自体がグラフ互換のノードであるため、他のステージと同様に、それを公開Graphに組み込むことができます。これは、グラフレベルの制御(複数の入力、名前付き出力、カスタムルーティングなど)が必要でありながら、モデルの実行を再利用可能な単一のフラグメントとして扱う場合に、ブリッジパターンを使用するプロダクションシステムが採用する手法です。

重要な考え方は、低レベルのランタイムグラフ、StageModelExecutorOptions、または内部ノードIDに決して触れないことです。モデルをgraph.add(...)に渡し、NEATがそのフラグメント(必要に応じて、前処理/推論/後処理)をビルド時に適切な内部実行プランに変換します。最終的に、モデルを公開グラフに組み込み、組み込まれたトポロジを出力し、モデルの出力カーディナリティを読み取ることができます。

ウォークスルー

モデルのロード

構築では、コンパイルされたアーカイブをロードし、第1章と同様に、実行に備えて準備します。ここでは、オプションオブジェクトは渡さず、パスのみを渡します。なぜなら、この章は前処理ではなく、構成について扱うからです。結果として得られるModelは、グラフレイヤーが理解できるオブジェクトになります。

tutorials/014_embed_model_inside_graph/embed_model_inside_graph.cpp
simaai::neat::Model model(model_path);

モデルをグラフに組み込む

これがこの章の主な目的です。新しいGraphに、名前付き入力境界、モデル自体、名前付き出力境界の順に3つのノードが追加されます。Modelはグラフ互換であるため、add(model)は、モデル全体のルートを単一のフラグメントとして追加します。特別なAPIはなく、ランタイムに直接アクセスすることはありません。graph.describe()を出力すると、組み込まれたトポロジが表示され、モデルが名前付き境界の間に正しく組み込まれていることを確認できます。

境界は、simaai::neat::nodes::Input("image")nodes::Output("result")から取得され、モデルは直接graph.add(model)に渡されます。

tutorials/014_embed_model_inside_graph/embed_model_inside_graph.cpp
simaai::neat::Graph graph;
graph.add(simaai::neat::nodes::Input("image"));
graph.add(model);
graph.add(simaai::neat::nodes::Output("result"));

std::cout << graph.describe() << "\n";

モデルの確認

最後に、モデルフラグメントが実際にどのようなものを提供するかを読み取ります。これにより、モデルが正しくロードされたことを確認し、グラフが下流で生成する出力トポロジを確認できます。

model.info()は、情報構造体を返します。model_nameoutput_topology.physical_outputsおよびlogical_outputsを出力することで、モデルの出力の配線が明確になります。

tutorials/014_embed_model_inside_graph/embed_model_inside_graph.cpp
const auto info = model.info();
std::cout << "model=" << (info.model_name.empty() ? "<unnamed>" : info.model_name)
<< " physical_outputs=" << info.output_topology.physical_outputs
<< " logical_outputs=" << info.output_topology.logical_outputs << "\n";

実行

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

C++ (prebuilt):

./lib/sima-neat/tutorials/tutorial_014_embed_model_inside_graph \
--model /tmp/yolo_v8s.tar.gz

C++ (build from source):

./build.sh --target tutorial_014_embed_model_inside_graph
./build/tutorials-standalone/tutorial_014_embed_model_inside_graph \
--model /tmp/yolo_v8s.tar.gz

期待される出力(C++ビルドでは、まず合成されたグラフの説明が出力されます):

model=yolo_v8s physical_outputs=1 logical_outputs=1
[OK] 014_embed_model_inside_graph

(Pythonビルドでは、グラフの説明の後にmodel fragment added to public Graphが出力されます。)

この章のC++ソースを、カスタムのCMakeLists.txtを使用して独自のプロジェクトに統合する方法(追加のフォルダーは不要)については、ランディングページにあるチュートリアルの実行方法を参照してください。

完全なソース

完全なソースプログラムを表示
tutorials/014_embed_model_inside_graph/embed_model_inside_graph.cpp
// Hybrid graph composition: a Model is added directly to a public Graph.
//
// Usage:
// tutorial_014_embed_model_inside_graph --model /path/to/model.tar.gz

#include "neat.h"

#include <iostream>
#include <string>

namespace {

bool get_arg(int argc, char** argv, const std::string& key, std::string& out) {
for (int i = 1; i + 1 < argc; ++i) {
if (key == argv[i]) {
out = argv[i + 1];
return true;
}
}
return false;
}

} // namespace

int main(int argc, char** argv) {
try {
std::string model_path;
if (!get_arg(argc, argv, "--model", model_path)) {
std::cerr << "Usage: tutorial_014_embed_model_inside_graph --model <path>\n";
return 1;
}

simaai::neat::Model model(model_path);

// CORE LOGIC
// Model is now a Graph-compatible object. `graph.add(model)` appends the
// model route fragment (preprocess/inference/postprocess as needed) without
// exposing the internal low-level runtime graph.
simaai::neat::Graph graph;
graph.add(simaai::neat::nodes::Input("image"));
graph.add(model);
graph.add(simaai::neat::nodes::Output("result"));

std::cout << graph.describe() << "\n";

const auto info = model.info();
std::cout << "model=" << (info.model_name.empty() ? "<unnamed>" : info.model_name)
<< " physical_outputs=" << info.output_topology.physical_outputs
<< " logical_outputs=" << info.output_topology.logical_outputs << "\n";
std::cout << "[OK] 014_embed_model_inside_graph\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}

ソース