新しい種類のサーバブルの作成

このドキュメントでは、新しい種類のサーバブルを使用して TensorFlow Serving を拡張する方法について説明します。最も有名なサーバブルのタイプはSavedModelBundleですが、モデルに付随するデータを提供するために、他の種類のサーバブルを定義すると便利な場合があります。例としては、語彙検索テーブル、特徴変換ロジックなどがあります。 C++ クラスはどれも servable にすることができます (例: intstd::map<string, int> 、またはバイナリで定義された任意のクラス)。これをYourServableと呼びます。

YourServableLoaderSourceAdapterの定義

TensorFlow Serving がYourServable管理および提供できるようにするには、次の 2 つを定義する必要があります。

  1. YourServableのインスタンスをロードし、アクセスを提供し、アンロードするLoaderクラス。

  2. ファイル システム パスなど、基礎となるデータ形式からローダーをインスタンス化するSourceAdapterSourceAdapterの代わりに、完全なSourceを作成することもできます。ただし、 SourceAdapterアプローチはより一般的でモジュール化されているため、ここではそれに焦点を当てます。

Loader抽象化はcore/loader.hで定義されます。サーバブルのタイプをロード、アクセス、アンロードするためのメソッドを定義する必要があります。サーバブルのロード元となるデータはどこからでも取得できますが、ストレージ システムのパスから取得するのが一般的です。これがYourServableの場合であると仮定しましょう。さらに、満足のいくSource<StoragePath>すでにあると仮定します (そうでない場合は、カスタム ソースドキュメントを参照してください)。

Loaderに加えて、指定されたストレージ パスからLoaderをインスタンス化するSourceAdapterを定義する必要があります。ほとんどの単純な使用例では、 SimpleLoaderSourceAdapterクラス ( core/simple_loader.h内) を使用して 2 つのオブジェクトを簡潔に指定できます。高度なユースケースでは、たとえば、 SourceAdapter何らかの状態を保持する必要がある場合や、 Loaderインスタンス間で状態を共有する必要がある場合など、下位レベルの API を使用してLoaderクラスとSourceAdapterクラスを個別に指定することを選択する場合があります。

SimpleLoaderSourceAdapterを使用する単純なハッシュマップ サーバブルのリファレンス実装がservables/hashmap/hashmap_source_adapter.ccにあります。 HashmapSourceAdapterのコピーを作成し、ニーズに合わせて変更すると便利な場合があります。

HashmapSourceAdapterの実装には 2 つの部分があります。

  1. LoadHashmapFromFile()内のファイルからハッシュマップをロードするロジック。

  2. SimpleLoaderSourceAdapterを使用して、 LoadHashmapFromFile()に基づいてハッシュマップ ローダーを発行するSourceAdapterを定義します。新しいSourceAdapter HashmapSourceAdapterConfigタイプの構成プロトコル メッセージからインスタンス化できます。現在、構成メッセージにはファイル形式のみが含まれており、参照実装の目的で 1 つの単純な形式のみがサポートされています。

    デストラクター内のDetach()の呼び出しに注意してください。この呼び出しは、状態の破棄と、他のスレッドでの Creator ラムダの進行中の呼び出しとの間の競合を避けるために必要です。 (この単純なソース アダプターには状態がありませんが、基本クラスは Detach() の呼び出しを強制します。)

YourServableオブジェクトがマネージャーにロードされるように手配する

YourServableローダーの新しいSourceAdapterをストレージ パスの基本ソースとマネージャーにフックする方法は次のとおりです (エラー処理が不適切です。実際のコードはより注意する必要があります)。

まず、マネージャーを作成します。

std::unique_ptr<AspiredVersionsManager> manager = ...;

次に、 YourServableソース アダプターを作成し、マネージャーに接続します。

auto your_adapter = new YourServableSourceAdapter(...);
ConnectSourceToTarget(your_adapter, manager.get());

最後に、単純なパス ソースを作成し、アダプターに接続します。

std::unique_ptr<FileSystemStoragePathSource> path_source;
// Here are some FileSystemStoragePathSource config settings that ought to get
// it working, but for details please see its documentation.
FileSystemStoragePathSourceConfig config;
// We just have a single servable stream. Call it "default".
config.set_servable_name("default");
config.set_base_path(FLAGS::base_path /* base path for our servable files */);
config.set_file_system_poll_wait_seconds(1);
TF_CHECK_OK(FileSystemStoragePathSource::Create(config, &path_source));
ConnectSourceToTarget(path_source.get(), your_adapter.get());

ロードされたYourServableオブジェクトへのアクセス

ロードされたYourServableへのハンドルを取得して使用する方法は次のとおりです。

auto handle_request = serving::ServableRequest::Latest("default");
ServableHandle<YourServable*> servable;
Status status = manager->GetServableHandle(handle_request, &servable);
if (!status.ok()) {
  LOG(INFO) << "Zero versions of 'default' servable have been loaded so far";
  return;
}
// Use the servable.
(*servable)->SomeYourServableMethod();

上級: 複数のサーバブル インスタンスが状態を共有できるように調整する

SourceAdapter は、複数の発行されたサーバブル間で共有される状態を格納できます。例えば:

  • 複数のサーバブルが使用する共有スレッド プールまたはその他のリソース。

  • 各サーブブル インスタンスでデータ構造を複製する際の時間とスペースのオーバーヘッドを回避するために、複数のサーブブルが使用する共有読み取り専用データ構造。

初期化時間とサイズが無視できる共有状態 (スレッド プールなど) は、SourceAdapter によって積極的に作成でき、その後、発行された各サーブブル ローダーにその共有状態へのポインターが埋め込まれます。高価なまたは大規模な共有状態の作成は、最初に該当する Loader::Load() 呼び出しまで延期する必要があります。つまり、マネージャーによって管理されます。対称的に、高価で大規模な共有状態を使用する最終的なサーブブルへの Loader::Unload() 呼び出しは、それを破棄する必要があります。