TensorFlow : Deploy : TensorFlow Serving : 標準的な TensorFlow ModelServer を構築する (翻訳)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 09/07/2017
* 本ページは、TensorFlow 本家サイトの Deploy : TensorFlow Serving – Building Standard TensorFlow ModelServer を翻訳した上で適宜、補足説明したものです:
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
このチュートリアルは、訓練された TensorFlow モデルの新しいバージョンを動的に探索してサーブする、標準的な TensorFlow ModelServer をビルドするために TensorFlow コンポーネントをどのように使用するかを示します。もし貴方のモデルをサーブするために標準的なサーバを使用したいだけであるならば、TensorFlow Serving 基本チュートリアル を見てください。
このチュートリアルは、手書き画像 (MNIST データ) 分類のための TensorFlow チュートリアルで紹介された単純な Softmax 回帰モデルを使用します。もし貴方が TensorFlow または MNIST が何であるか知らないのであれば、ML 初心者向けの MNIST を見てください。
このチュートリアルのためのコードは2つのパートから成ります :
- Python ファイル、mnist_saved_model.py これはモデルの複数のバージョンを訓練してエクスポートします。
- C++ ファイル main.cc、これは標準的な TensorFlow ModelServer で新しいエクスポートされたモデルを探索してそれらをサーブ (サービス提供) するために gRPC サービスを実行します。
このチュートリアルは次のタスクを通して進みます :
- TensorFlow モデルを訓練してエクスポートする。
- TensorFlow Serving ServerCore でモデル・バージョニングを管理する。
- SessionBundleSourceAdapterConfig を使用してバッチ処理を構成する。
- TensorFlow Serving ServerCore でリクエストにサーブする。
- サービスを実行してテストする。
始める前に、(インストール) 要件 は完了しておいてください。
Note: 後述のすべての bazel ビルド・コマンドは標準的な -c opt フラグを使用します。ビルドをさらに最適化するためには、ここの手順 を参照してください。
TensorFlow モデルを訓練してエクスポートする
export ディレクトリが既に存在しているならばクリアします :
$>rm -rf /tmp/mnist_model
モデルの最初のバージョンを (100 反復で) 訓練してエクスポートする :
$>bazel build -c opt //tensorflow_serving/example:mnist_saved_model $>bazel-bin/tensorflow_serving/example/mnist_saved_model --training_iteration=100 --model_version=1 /tmp/mnist_model
モデルの2番目のバージョンを (2000 反復で) 訓練してエクスポートする :
$>bazel-bin/tensorflow_serving/example/mnist_saved_model --training_iteration=2000 --model_version=2 /tmp/mnist_model
mnist_saved_model.py で見れるように、訓練とエクスポートは TensorFlow Serving 基本チュートリアルにおけるものと同じように行なわれます。デモ目的では、最初の実行のために訓練反復を意図的に抑えて v1 としてエクスポートして、その一方で2番目の実行のために通常のようにそれを訓練して同じ親ディレクトリに v2 としてエクスポートします — より徹底した訓練で後者により良い分類精度を獲得することを期待しますので。mnist_model ディレクトリで各訓練実行のための訓練データを見るでしょう :
$>ls /tmp/mnist_model 1 2
ServerCore
さて新しいアルゴリズムが実験されている時、あるいはモデルが新しいデータセットで訓練されている時、モデルの v1 と v2 が実行時に動的に生成されることを想像してください。本番環境では、段階的なロールアウトをサポート可能なサーバをビルドすることを望むかもしれません、そこでは v1 をサーブする一方で v2 が探索され、ロードされ、実験され、モニターされ、あるいは元に戻されます。あるいは、v2 を持ってくる前に v1 を取り壊すことを望むかもしれません。TensorFlow Serving は両方のオプションをサポートします — 一つは移行 (transition) 時の可用性を維持するのに良く、他方はリソース使用 (e.g. RAM) を最小化するのに良いです。
TensorFlow Serving Manager は正確にそれを行ないます。それは、それらのロード、サービングそしてアンロード、更にはバージョン移行を含む 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 パラメータを取ります。ここに2、3の一般に使用されるオプションがあります :
- ModelServerConfig はロードされるモデルを指定します。モデルは、モデルの静的リストを宣言する model_config_list を通してか、実行時に更新されるかもしれないモデルのリストを宣言する custom_model_config を通して宣言されます。
- PlatformConfigMap、これは (tensorflow のような) プラットフォームの名前から PlatformConfig へマップします、これは SourceAdapter を作成するために使用されます。SourceAdapter は StoragePath (モデル・バージョンが探索されるパス) をモデル Loader (モデル・バージョンをストレージ・パスからロードして Manager に状態移行 (= state transition) インターフェイスを提供する) に適合させます。PlatformConfig が SavedModelBundleSourceAdapterConfig を含むのであれば、SavedModelBundleSourceAdapter が作成され、これは後で説明されます。
SavedModelBundle は TensorFlow Serving の基本的なコンポーネントです。それは与えられたパスからロードされた TensorFlow モデルを表してそして TensorFlow が推論を実行するのと同じ Session::Run インターフェイスを提供します。SavedModelBundleSourceAdapter はストレージ・パスを Loader
これら総てとともに、ServerCore は内部的には以下を行ないます :
- FileSystemStoragePathSource をインスタンス化します、これは model_config_list で宣言されたモデル export パスをモニタします。
- model_config_list で宣言されたモデル・プラットフォームを持つ PlatformConfigMap を使用して SourceAdapter をインスタンス化して、FileSystemStoragePathSource をそれに接続します。このように、export パス下で新しいモデル・バージョンが探索された時はいつでも、SavedModelBundleSourceAdapter はそれを Loader
に適合させます。 - AspiredVersionsManager と呼ばれる Manager の特定の実装をインスタンス化します、これは SavedModelBundleSourceAdapter により作成された総てのそのような Loader インスタンスを管理します。ServerCore は Manager インターフェイスを呼び出しを AspiredVersionsManager に委任することによりエクスポートします。
新しいバージョンが利用可能なときはいつでも、この AspiredVersionsManager は新しいバージョンをロードし、そしてそのデフォルトの挙動では古いものをアンロードします。もし貴方がカスタマイズを始めることを望むのであれば、それが内部的に作成するコンポーネントとそれをどのように構成するかを理解することが奨励されます。
TensorFlow Serving は非常に柔軟で拡張性があるように最初から設計されていることに言及することは価値があるでしょう。ServerCore と AspiredVersionsManager のような一般的なコア・コンポーネントを活用する一方で、システムの挙動をカスタマイズするために様々なプラグインをビルドできます。例えば、ローカル・ストレージの代わりにクラウド・ストレージをモニタする data source プラグインをビルドできますし、あるいは異なる方法でバージョン移行を行なう version policy プラグインをビルドすることもできるでしょう — 実際に、non-TensorFlow モデルをサーブする custom model プラグインをビルドすることさえできるでしょう。これらのトピックはこのチュートリアルの範囲外です。けれども、更なる情報のために custom source と custom servable チュートリアルを参照可能です。
バッチ処理
本番環境で望むもう一つの典型的なサーバ特徴はバッチ処理です。機械学習の推論を行なうために使用される現代的なハードウェア・アクセラレータ (GPU, etc.) は推論リクエストが巨大なバッチで実行されるときに通常はベストな計算効率を達成します。SavedModelBundleSourceAdapter を作成するときに適切な SessionBundleConfig を提供することによりバッチ処理は有効になります。このケースでは BatchingParameters を殆どデフォルト値で設定します。バッチ処理は custom timeout, batch_size, etc. 値を設定することで微調整できます。詳細は、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;
バッチ全体に到達した時、推論リクエストは内部的に単一の巨大なリクエスト (テンソル) にマージされ、tensorflow::Session::Run() が呼び起こされます (これは GPU 上の実際の効率上の有益が由来する場所です)。
Manager でサーブする
上述したように、TensorFlow Serving Manager は任意の機械学習システムにより生成されるモデルのロード、サーブ、アンロードとバージョン移行を処理する一般的なコンポーネントとして設計されています。その API は次の基本的な概念を中心として構築されています :
- Servable: Servable は、クライアント・リクエストにサーブすることに使用できる任意の Opaque (型) オブジェクトです。servable のサイズと粒度 (= granularity) は柔軟で、単一の servable は単一の機械学習されたモデルへの検索テーブルの単一のシャードからモデルのタプルまで任意のものを含むかもしれません。servable は任意の型とインターフェイスを取ることができます。
- Servable Version: Servable はバージョン化され TensorFlow Serving Manager は servable の一つまたはそれ以上の version を管理できます。バージョニングは servable の一つのバージョン以上が同時にロードされることを許可し、段階的なロールアウトと実験をサポートします。
- Servable Stream: servable stream は、増加する version 番号を持つ、servable の version のシークエンスです。
- Model: 機械学習されたモデルは一つまたはそれ以上の servable で表されます。servable のサンプルは :
- TensorFlow session またはそれ回りの SavedModelBundle のような、ラッパー。
- 他の種類の機械学習されたモデル。
- 語彙検索テーブル。
- 埋め込み検索テーブル。
合成モデル (= composite model) は複数の独立した servable として、または単一の合成 servable として表されます。servable はまた、例えば多くの Manager インスタンスに渡るシャードされた巨大な検索ケーブルを持つ、モデルの断片にも相当するかもしれません。
これら総てをこのチュートリアルのコンテクストに集約するために :
- TensorFlow モデルは一つの種類の servable で表されます — SavedModelBundle です。SavedModelBundle は内部的には、どのようなグラフが session にロードされるかそして推論のためにそれをどのように実行するかについての何某かの metadata を伴う tensorflow:Session から成ります。
- TensorFlow エクスポートのストリームを含むファイルシステム・ディレクトリが、それぞれは名前がバージョン番号であるそれ自身のサブディレクトリ内にあります。外側のディレクトリは、サーブされる TensorFlow モデルのための servable stream のシリアライズ化された表現として考えることができます。各エクスポートはロード可能な servable に相当します。
- AspiredVersionsManager は export stream をモニタし、総ての SavedModelBundle servable のライフサイクルを動的に管理します。
それから TensorflowPredictImpl::Predict は丁度 :
- manager から (ServerCore を通して) SavedModelBundle をリクエストする。
- PredictRequest の論理テンソル名を実際のテンソル名にマップして値をテンソルにバインドするために generic signatures を使用する。
- 推論を実行する。
サーバをテストして実行する
エクスポート (されたモデル) の最初のバージョンをモニタされるフォルダーにコピーしてそしてサーバをスタートします。
$>mkdir /tmp/monitored $>cp -r /tmp/mnist_model/1 /tmp/monitored $>bazel build -c opt //tensorflow_serving/model_servers:tensorflow_model_server $>bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server --enable_batching --port=9000 --model_name=mnist --model_base_path=/tmp/monitored
サーバは毎秒 “Aspiring version for servable …” というログ・メッセージを吐きます、これはそれがエクスポート (されたモデル) を見つけて、その存続を追跡中であることを意味します。–concurrency=10 とともに実行します。これはサーバに同時リクエストを送りそしてバッチ処理ロジックのトリガーとなります。
$>bazel build -c opt //tensorflow_serving/example:mnist_client $>bazel-bin/tensorflow_serving/example/mnist_client --num_tests=1000 --server=localhost:9000 --concurrency=10 ... Inference error rate: 13.1%
それからエクスポート (されたモデル) の2番目のバージョンをモニタされるフォルダにコピーしてテストを再実行します :
$>cp -r /tmp/mnist_model/2 /tmp/monitored $>bazel-bin/tensorflow_serving/example/mnist_client --num_tests=1000 --server=localhost:9000 --concurrency=10 ... Inference error rate: 9.5%
これは貴方のサーバが自動的に新しいバージョンを探索してそれを serving のために使用していることを確かなものとします!
以上