このドキュメントでは、新しい種類のサーバブルを使用して 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
を定義します。新しい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() 呼び出しは、それを破棄する必要があります。