このドキュメントでは、新しい種類のサーバブルを使用して TensorFlow Serving を拡張する方法について説明します。最も有名なサーバブルのタイプはSavedModelBundleですが、モデルに付随するデータを提供するために、他の種類のサーバブルを定義すると便利な場合があります。例としては、語彙検索テーブル、特徴変換ロジックなどがあります。 int 、 std::map<string, int> 、またはバイナリで定義された任意のクラスなど、あらゆる C++ クラスをサーバブルにすることができます。これをYourServableと呼びます。
YourServableのLoaderとSourceAdapterの定義
TensorFlow Serving がYourServableを管理および提供できるようにするには、次の 2 つを定義する必要があります。
YourServableのインスタンスをロードし、アクセスを提供し、アンロードするLoaderクラス。ファイル システム パスなど、基礎となるデータ形式からローダーをインスタンス化する
SourceAdapter。SourceAdapterの代わりに、完全な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 つの部分があります。
LoadHashmapFromFile()内のファイルからハッシュマップをロードするロジック。SimpleLoaderSourceAdapterを使用してLoadHashmapFromFile()に基づいてハッシュマップ ローダーを発行するSourceAdapterを定義します。新しいSourceAdapterHashmapSourceAdapterConfigタイプの構成プロトコル メッセージからインスタンス化できます。現在、構成メッセージにはファイル形式のみが含まれており、参照実装の目的で 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() 呼び出しは、それを破棄する必要があります。