構築標準 TensorFlow モデルサーバー

このチュートリアルでは、TensorFlow Serving コンポーネントを使用して、トレーニングされた TensorFlow モデルの新しいバージョンを動的に検出して提供する標準の TensorFlow ModelServer を構築する方法を示します。標準サーバーを使用してモデルを提供したい場合は、 TensorFlow Serving の基本チュートリアルを参照してください。

このチュートリアルでは、TensorFlow チュートリアルで導入された単純な Softmax 回帰モデルを手書き画像 (MNIST データ) 分類に使用します。 TensorFlow または MNIST が何なのかわからない場合は、 ML 初心者のための MNISTチュートリアルを参照してください。

このチュートリアルのコードは 2 つの部分で構成されます。

  • モデルの複数のバージョンをトレーニングおよびエクスポートする Python ファイルmnist_saved_model.py

  • C++ ファイルmain.cc。これは、新しいエクスポートされたモデルを検出し、それらを提供するためのgRPCサービスを実行する標準 TensorFlow ModelServer です。

このチュートリアルでは、次のタスクを段階的に実行します。

  1. TensorFlow モデルをトレーニングしてエクスポートします。
  2. TensorFlow Serving ServerCoreを使用してモデルのバージョン管理を管理します。
  3. SavedModelBundleSourceAdapterConfigを使用してバッチ処理を構成します。
  4. TensorFlow Serving ServerCoreを使用してリクエストを処理します。
  5. サービスを実行してテストします。

始める前に、まずDocker をインストールします

TensorFlow モデルのトレーニングとエクスポート

まず、まだ行っていない場合は、このリポジトリのクローンをローカル マシンに作成します。

git clone https://github.com/tensorflow/serving.git
cd serving

エクスポート ディレクトリが既に存在する場合はクリアします。

rm -rf /tmp/models

トレーニング (100 回の反復) を実行し、モデルの最初のバージョンをエクスポートします。

tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
  --training_iteration=100 --model_version=1 /tmp/mnist

トレーニング (2000 回の反復) を実行し、モデルの 2 番目のバージョンをエクスポートします。

tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
  --training_iteration=2000 --model_version=2 /tmp/mnist

mnist_saved_model.pyでわかるように、トレーニングとエクスポートはTensorFlow Serving の基本チュートリアルと同じ方法で行われます。デモンストレーションの目的で、最初の実行ではトレーニングの反復を意図的にダイヤルダウンして v1 としてエクスポートし、2 回目の実行では通常どおりトレーニングして同じ親ディレクトリに v2 としてエクスポートします。これは後者で達成されると予想されます。より集中的なトレーニングにより分類精度が向上します。 /tmp/mnistディレクトリに各トレーニング実行のトレーニング データが表示されるはずです。

$ ls /tmp/mnist
1  2

サーバーコア

ここで、新しいアルゴリズムが実験されるとき、またはモデルが新しいデータセットでトレーニングされるときに、モデルの v1 と v2 が実行時に動的に生成されると想像してください。運用環境では、v1 を提供しながら v2 を検出、ロード、実験、監視、または元に戻すことができる、段階的なロールアウトをサポートできるサーバーを構築することができます。あるいは、v2 を起動する前に v1 を破棄することもできます。 TensorFlow Serving は両方のオプションをサポートしています。1 つは移行中の可用性を維持するのに適しており、もう 1 つはリソースの使用量 (RAM など) を最小限に抑えるのに適しています。

TensorFlow Serving Managerまさにそれを行います。 TensorFlow モデルのロード、提供、アンロードやバージョンの移行など、TensorFlow モデルのライフサイクル全体を処理します。このチュートリアルでは、 AspiredVersionsManagerを内部的にラップする TensorFlow Serving ServerCoreの上にサーバーを構築します。

int main(int argc, char** argv) {
  ...

  ServerCore::Options options;
  options.model_server_config = model_server_config;
  options.servable_state_monitor_creator = &CreateServableStateMonitor;
  options.custom_model_config_loader = &LoadCustomModelConfig;

  ::google::protobuf::Any source_adapter_config;
  SavedModelBundleSourceAdapterConfig
      saved_model_bundle_source_adapter_config;
  source_adapter_config.PackFrom(saved_model_bundle_source_adapter_config);
  (*(*options.platform_config_map.mutable_platform_configs())
      [kTensorFlowModelPlatform].mutable_source_adapter_config()) =
      source_adapter_config;

  std::unique_ptr<ServerCore> core;
  TF_CHECK_OK(ServerCore::Create(options, &core));
  RunServer(port, std::move(core));

  return 0;
}

ServerCore::Create() ServerCore::Options パラメーターを受け取ります。よく使用されるオプションをいくつか示します。

  • ロードするモデルを指定するModelServerConfig 。モデルは、モデルの静的リストを宣言するmodel_config_listを通じて、または実行時に更新される可能性のあるモデルのリストを宣言するカスタム方法を定義するcustom_model_configを通じて宣言されます。
  • PlatformConfigMap 、プラットフォームの名前 ( tensorflowなど) からPlatformConfigにマップされ、 SourceAdapter作成に使用されます。 SourceAdapterStoragePath (モデル バージョンが検出されるパス) をモデルLoader (ストレージ パスからモデル バージョンをロードし、 Managerに状態遷移インターフェイスを提供します) に適合させます。 PlatformConfigSavedModelBundleSourceAdapterConfigが含まれている場合、 SavedModelBundleSourceAdapter作成されます。これについては後で説明します。

SavedModelBundleは TensorFlow Serving の重要なコンポーネントです。これは、特定のパスからロードされた TensorFlow モデルを表し、推論を実行するための TensorFlow と同じSession::Runインターフェイスを提供します。 SavedModelBundleSourceAdapter 、モデルの有効期間をManagerで管理できるように、ストレージ パスをLoader<SavedModelBundle>に適合させます。 SavedModelBundle非推奨のSessionBundleの後継であることに注意してください。 SessionBundleのサポートはまもなく削除されるため、ユーザーはSavedModelBundleを使用することをお勧めします。

これらすべてにより、 ServerCore内部で次の処理を実行します。

  • model_config_listで宣言されたモデルのエクスポート パスを監視するFileSystemStoragePathSourceをインスタンス化します。
  • model_config_listで宣言されたモデル プラットフォームでPlatformConfigMapを使用してSourceAdapterインスタンス化し、それにFileSystemStoragePathSourceを接続します。このようにして、新しいモデル バージョンがエクスポート パスで検出されるたびに、 SavedModelBundleSourceAdapterはそれをLoader<SavedModelBundle>に適応させます。
  • SavedModelBundleSourceAdapterによって作成されたこのようなすべてのLoaderインスタンスを管理するAspiredVersionsManagerと呼ばれるManagerの特定の実装をインスタンス化します。 ServerCore呼び出しをAspiredVersionsManagerに委任することでManagerインターフェイスをエクスポートします。

新しいバージョンが利用可能になると、このAspiredVersionsManagerは新しいバージョンをロードし、デフォルトの動作では古いバージョンをアンロードします。カスタマイズを開始する場合は、内部で作成されるコンポーネントとその構成方法を理解することをお勧めします。

TensorFlow Serving は非常に柔軟で拡張可能になるようにゼロから設計されていることは言及する価値があります。 ServerCoreAspiredVersionsManagerなどの汎用コア コンポーネントを活用しながら、さまざまなプラグインを構築してシステムの動作をカスタマイズできます。たとえば、ローカル ストレージではなくクラウド ストレージを監視するデータ ソース プラグインを構築したり、別の方法でバージョン移行を行うバージョン ポリシー プラグインを構築したりできます。実際、サービスを提供するカスタム モデル プラグインを構築することもできます。非 TensorFlow モデル。これらのトピックは、このチュートリアルの範囲外です。ただし、詳細については、カスタム ソースカスタム サーバブルのチュートリアルを参照してください。

バッチ処理

実稼働環境に必要なもう 1 つの典型的なサーバー機能はバッチ処理です。機械学習の推論に使用される最新のハードウェア アクセラレータ (GPU など) は、通常、推論リクエストが大規模なバッチで実行されるときに最高の計算効率を達成します。

SavedModelBundleSourceAdapterの作成時に適切なSessionBundleConfigを指定することで、バッチ処理をオンにできます。この場合、 BatchingParametersをほぼデフォルト値で設定します。バッチ処理は、カスタム タイムアウト、batch_size などの値を設定することで微調整できます。詳細については、 BatchingParametersを参照してください。

SessionBundleConfig session_bundle_config;
// Batching config
if (enable_batching) {
  BatchingParameters* batching_parameters =
      session_bundle_config.mutable_batching_parameters();
  batching_parameters->mutable_thread_pool_name()->set_value(
      "model_server_batch_threads");
}
*saved_model_bundle_source_adapter_config.mutable_legacy_config() =
    session_bundle_config;

フルバッチに達すると、推論リクエストは内部で単一の大きなリクエスト (tensor) にマージされ、 tensorflow::Session::Run()が呼び出されます (GPU での実際の効率の向上はここから来ます)。

マネージャーと一緒に奉仕する

前述したように、TensorFlow Serving Manager 、任意の機械学習システムによって生成されたモデルのロード、サービング、アンロード、およびバージョン移行を処理できる汎用コンポーネントとして設計されています。その API は、次の主要な概念に基づいて構築されています。

  • Serable : Servable は、クライアントのリクエストに対応するために使用できる任意の不透明なオブジェクトです。サーブブルのサイズと粒度は柔軟で、単一のサーブブルにはルックアップ テーブルの単一のシャードから単一の機械学習モデル、モデルのタプルまであらゆるものが含まれる可能性があります。サーバブルは任意のタイプおよびインターフェイスにすることができます。

  • サーバブル バージョン: サーバブルはバージョン管理されており、TensorFlow Serving Managerサーバブルの 1 つ以上のバージョンを管理できます。バージョニングにより、サーブブルの複数のバージョンを同時にロードできるようになり、段階的なロールアウトと実験がサポートされます。

  • サーバブル ストリーム: サーバブル ストリームは、バージョン番号が増加するサーバブルのバージョンのシーケンスです。

  • モデル: 機械学習モデルは、1 つ以上のサーバブルによって表されます。サーバブルの例は次のとおりです。

    • TensorFlow セッションまたはその周囲のラッパー ( SavedModelBundleなど)。
    • 他の種類の機械学習モデル。
    • 語彙検索テーブル。
    • ルックアップテーブルの埋め込み。

    複合モデルは、複数の独立したサーバブルとして、または単一の複合サーブルとして表すことができます。サーバブルは、たとえば、多くのManagerインスタンスにわたってシャード化された大規模なルックアップ テーブルなど、モデルの一部に対応する場合もあります。

これらすべてをこのチュートリアルのコンテキストに組み込むには、次のようにします。

  • TensorFlow モデルは、1 種類のサーバブル ( SavedModelBundleによって表されます。 SavedModelBundle内部的に、どのグラフがセッションにロードされるか、および推論のためにそれを実行する方法に関するいくつかのメタデータとペアになったtensorflow:Sessionで構成されます。

  • TensorFlow エクスポートのストリームを含むファイル システム ディレクトリがあり、それぞれの名前がバージョン番号である独自のサブディレクトリにあります。外側のディレクトリは、提供される TensorFlow モデルの提供可能なストリームのシリアル化された表現と考えることができます。各エクスポートは、ロードできるサーバブルに対応します。

  • AspiredVersionsManagerエクスポート ストリームを監視し、すべてのSavedModelBundleサーバブルのライフサイクルを動的に管理します。

TensorflowPredictImpl::Predictから、次のようにします。

  • マネージャーから (ServerCore 経由で) SavedModelBundleリクエストします。
  • generic signaturesを使用して、 PredictRequestの論理テンソル名を実際のテンソル名にマッピングし、値をテンソルにバインドします。
  • 推論を実行します。

サーバーをテストして実行する

エクスポートの最初のバージョンを監視対象フォルダーにコピーします。

mkdir /tmp/monitored
cp -r /tmp/mnist/1 /tmp/monitored

次にサーバーを起動します。

docker run -p 8500:8500 \
  --mount type=bind,source=/tmp/monitored,target=/models/mnist \
  -t --entrypoint=tensorflow_model_server tensorflow/serving --enable_batching \
  --port=8500 --model_name=mnist --model_base_path=/models/mnist &

サーバーは、「Aspiring version for servable ...」というログ メッセージを 1 秒ごとに出力します。これは、エクスポートが見つかり、その存続を追跡していることを意味します。

--concurrency=10を指定してクライアントを実行してみましょう。これにより、同時リクエストがサーバーに送信され、バッチ処理ロジックがトリガーされます。

tools/run_in_docker.sh python tensorflow_serving/example/mnist_client.py \
  --num_tests=1000 --server=127.0.0.1:8500 --concurrency=10

その結果、次のような出力が得られます。

...
Inference error rate: 13.1%

次に、エクスポートの 2 番目のバージョンを監視対象フォルダーにコピーし、テストを再実行します。

cp -r /tmp/mnist/2 /tmp/monitored
tools/run_in_docker.sh python tensorflow_serving/example/mnist_client.py \
  --num_tests=1000 --server=127.0.0.1:8500 --concurrency=10

その結果、次のような出力が得られます。

...
Inference error rate: 9.5%

これにより、サーバーが新しいバージョンを自動的に検出し、それを提供に使用することが確認されます。