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

最初のモデルを実行する

項目
カテゴリモデルと推論
難易度初級
推定所要時間<5 minutes
ラベルmodel, inference, foundations

これは最初の章です。目標は、可能な限り最小限の完全な推論です。コンパイルされたモデルを取得し、1つの画像を入力として与え、予測されたクラスのインデックスを出力します。グラフも、スレッドも、ストリーミングもありません。すべての Neat プログラムが構築されている3つの呼び出しだけです。

コンパイルされたモデル は、デプロイ可能な .tar.gz アーカイブであり、MPK推論コントラクトが含まれています。これには、モデルのアーティファクトと、ターゲットデバイスで実行するために Neat が必要とするランタイムメタデータが含まれます。アーカイブを解凍したり、ステージを自分で接続したりする必要はありません。Neat にアーカイブを指し示し、入力を与え、出力を読み取ります。最終的には、3行で推論を実行し、top1= クラスのインデックスを出力できます。

ウォークスルー

モデルをロードする

最初の行は、ディスク上のパスを、実行可能な Model に変換します。コンストラクションは、アーカイブをロードし、実行に備えて準備します。

build_options(size) を2番目の引数として渡して、このモデルが期待する入力コントラクト(RGBカラー、224×224、およびResNet-50がトレーニングされたImageNet正規化)を宣言します。ここで宣言することで、ランタイムは、生の画像をモデルが要求するテンソルに変換する方法を認識します。

tutorials/001_run_your_first_model/run_your_first_model.cpp
simaai::neat::Model model(model_path, build_options(size));

入力を準備する

次に、分類する正確に1つの画像を生成します。--image を渡すと、画像が読み込まれ、224×224 にリサイズされ、入力コントラクトに一致するようにRGBに変換されます。それ以外の場合は、単色のグレーフレームを合成して、完全なロード→実行→読み取りパスが、手元にアセットがなくても、最初から最後まで実行されるようにします。

フレームは cv::Mat であり、load_rgb(...) によって生成されるか、グレーのプレースホルダーとして生成されます。

tutorials/001_run_your_first_model/run_your_first_model.cpp
cv::Mat input = image.empty() ? cv::Mat(size, size, CV_8UC3, cv::Scalar(99, 99, 99))
: load_rgb(image, size);

推論を実行し、結果を読み取る

3番目の行は、実際の処理を実行します。run() は、入力と timeout_ms を受け取り、モデルを同期的に実行し、出力を返します。timeout_ms は、待機する最大壁時計時間です。2000 ミリ秒の場合、「デバイスが2秒以内に出力を生成しない場合は、エラーを発生させる」という意味であり、無限にハングするわけではありません。(-1 を渡すと、無期限にブロックされます。実際のコードでは、有限の値を優先してください。)次に、argmax を使用して出力を単一のクラスインデックスに減らし、top1= を出力します。

run()TensorList を返します。最初のテンソルのバイトを map_read() を使用して読み取ります。

tutorials/001_run_your_first_model/run_your_first_model.cpp
simaai::neat::TensorList outputs = model.run(std::vector<cv::Mat>{input}, /*timeout_ms=*/2000);

実行

実行すると、予測されたクラスのインデックスが標準出力に出力されるはずです。Neat のインストールルートshare/lib/ を含むディレクトリ)から Python および C++(事前に構築されたもの) コマンドを実行します。ソースからビルドする コマンドは、リポジトリのルートから実行します。

C++ (prebuilt):

./lib/sima-neat/tutorials/tutorial_001_run_your_first_model \
--model /tmp/resnet_50.tar.gz

C++ (build from source):

./build.sh --target tutorial_001_run_your_first_model
./build/tutorials-standalone/tutorial_001_run_your_first_model \
--model /tmp/resnet_50.tar.gz

予想される出力(正確なインデックスは画像によって異なります):

top1=285
[OK] 001_run_your_first_model

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

スループット、バッチ処理、またはライブストリームについては、第2章に進んでください。参照:モデル

実践

チュートリアルとテストがモデルアーカイブ(.tar.gz)とサンプルアセットを探す場所、およびそれらをローカルで提供する方法。これは、モデルをベースにしたすべてのチュートリアルの前提条件です。

sima-cli がPATHにあることを確認する

一部のテストは、非対話型のシェルから sima-cli を呼び出します。sima-cli をインストールした後、これを一度実行してください。

SIMA_CLI_BIN_DIR="<path-to-sima-cli-bin>"
grep -Fqx "export PATH=\"${SIMA_CLI_BIN_DIR}:\$PATH\"" ~/.bashrc || echo "export PATH=\"${SIMA_CLI_BIN_DIR}:\$PATH\"" >> ~/.bashrc
source ~/.bashrc

次に、確認します。

/bin/sh -c 'command -v sima-cli'

モデルアーカイブの場所と環境変数

抽出/ランタイム配置の制御ノブ:

  • SIMA_MPK_EXTRACT_ROOT=<dir> は、ベースの抽出ディレクトリを設定します。
  • SIMA_MPK_CLEANUP_EXTRACTED=0 は、プロセス終了後に抽出された proc_* モデルデータを保持します。
  • SIMA_MPK_EXTRACT_GC_STALE_PROC=0 は、起動時のデッド proc_* のクリーンアップを無効にします。

ResNet50

検索順序:

  1. SIMA_RESNET50_TAR(モデルごとのオーバーライド)
  2. SIMA_MODEL_TAR(モデルアーカイブテスト/例の共有フォールバック)
  3. tmp/resnet_50.tar.gz
  4. ローカルファイルが tmp/ に移動した場合:resnet_50.tar.gzresnet-50.tar.gz

ダウンロード(sima-cli が利用可能な場合):

sima-cli modelzoo get resnet_50

サンプル画像

チュートリアル/テストで使用されるデフォルトの画像候補:

  • tmp/coco_sample.jpg(見つからない場合はダウンロードされます)
  • test.jpg
  • tests/assets/preproc_dynamic/ilena_488.jpg

テストで使用されるCOCO画像のURLは、次のようにオーバーライドできます。

SIMA_COCO_URL=<custom_url>

テストがダウンロードする場所

テストと例では、通常、ダウンロードされたアセットをリポジトリのルートにある tmp/ に配置します。チュートリアルは、必要なアセットが不足している場合、正常にスキップされます。

アセットのトラブルシューティング

  • チュートリアルで SKIP: missing ... が出力された場合は、アセットを提供するか、フラグ(例:--model <path>--image <path>)を渡します。
  • sima-cli が利用できない場合は、環境変数を設定して、ローカルのモデルアーカイブを指し示します。

完全なソース

完全なソースプログラムを表示
tutorials/001_run_your_first_model/run_your_first_model.cpp
// Run a ResNet-50 model on an image in three lines of Neat.
//
// Usage:
// tutorial_001_run_your_first_model --model /path/to/resnet_50.tar.gz [--image /path/to.jpg]

#include "neat.h"

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>

#include <cstring>
#include <filesystem>
#include <iostream>
#include <stdexcept>
#include <string>

namespace fs = std::filesystem;

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

cv::Mat load_rgb(const fs::path& image_path, int size) {
cv::Mat bgr = cv::imread(image_path.string(), cv::IMREAD_COLOR);
if (bgr.empty())
throw std::runtime_error("failed to read image: " + image_path.string());
if (bgr.cols != size || bgr.rows != size) {
cv::resize(bgr, bgr, cv::Size(size, size), 0, 0, cv::INTER_AREA);
}
cv::Mat rgb;
cv::cvtColor(bgr, rgb, cv::COLOR_BGR2RGB);
if (!rgb.isContinuous())
rgb = rgb.clone();
return rgb;
}

simaai::neat::Model::Options build_options(int size) {
simaai::neat::Model::Options opt;
opt.preprocess.kind = simaai::neat::InputKind::Image;
opt.preprocess.color_convert.input_format = simaai::neat::PreprocessColorFormat::RGB;
opt.preprocess.input_max_width = size;
opt.preprocess.input_max_height = size;
opt.preprocess.input_max_depth = 3;
opt.preprocess.preset = simaai::neat::NormalizePreset::ImageNet;
return opt;
}

int top1_from_output(const simaai::neat::TensorList& out) {
if (out.empty())
throw std::runtime_error("no tensor output");
const simaai::neat::Mapping m = out.front().map_read();
const size_t n = m.size_bytes / sizeof(float);
const float* p = reinterpret_cast<const float*>(m.data);
int best = 0;
for (size_t i = 1; i < n && i < 1000; ++i) {
if (p[i] > p[best])
best = static_cast<int>(i);
}
return best;
}

} // namespace

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

const int size = 224;

// CORE LOGIC
// The three-line Neat story:
simaai::neat::Model model(model_path, build_options(size));
cv::Mat input = image.empty() ? cv::Mat(size, size, CV_8UC3, cv::Scalar(99, 99, 99))
: load_rgb(image, size);
simaai::neat::TensorList outputs = model.run(std::vector<cv::Mat>{input}, /*timeout_ms=*/2000);

std::cout << "top1=" << top1_from_output(outputs) << "\n";
std::cout << "[OK] 001_run_your_first_model\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}

ソース