建構標準 TensorFlow ModelServer

本教學課程說明如何使用 TensorFlow Serving 元件來建構標準 TensorFlow ModelServer,以動態探索及服務已訓練 TensorFlow 模型的新版本。如果您只想使用標準伺服器來服務模型,請參閱TensorFlow Serving 基本教學課程

本教學課程使用 TensorFlow 教學課程中針對手寫影像 (MNIST 資料) 分類所介紹的簡單 Softmax 迴歸模型。如果您不知道 TensorFlow 或 MNIST 是什麼,請參閱適合 ML 初學者的 MNIST 教學課程。

本教學課程的程式碼包含兩個部分

  • Python 檔案 mnist_saved_model.py,用於訓練及匯出多個模型版本。

  • C++ 檔案 main.cc,此檔案是標準 TensorFlow ModelServer,可探索新的匯出模型並執行 gRPC 服務以服務這些模型。

本教學課程逐步說明下列工作

  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 次迭代) 並匯出模型的第二個版本

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,同時正常訓練第二次執行並將其匯出為 v2 到相同的父目錄,因為我們預期後者由於更密集的訓練而能達到更好的分類準確度。您應該會在 /tmp/mnist 目錄中看到每次訓練執行的訓練資料

$ ls /tmp/mnist
1  2

ServerCore

現在想像一下,模型的 v1 和 v2 是在執行階段動態產生,因為正在實驗新的演算法,或是模型正在使用新的資料集進行訓練。在生產環境中,您可能會想要建構一個伺服器,以支援逐步推出,其中 v2 可以在服務 v1 時探索、載入、實驗、監控或還原。或者,您可能會想要在啟動 v2 之前拆除 v1。TensorFlow Serving 支援這兩種選項,其中一種適用於在轉換期間維持可用性,另一種適用於盡可能減少資源用量 (例如 RAM)。

TensorFlow Serving Manager 正是如此。它處理 TensorFlow 模型的完整生命週期,包括載入、服務和卸載模型,以及版本轉換。在本教學課程中,您將在 TensorFlow Serving ServerCore 之上建構伺服器,後者在內部包裝了 AspiredVersionsManager

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,後者用於建立 SourceAdapterSourceAdapter 會將 StoragePath (探索到模型版本的路徑) 調整為模型 Loader (從儲存路徑載入模型版本,並為 Manager 提供狀態轉換介面)。如果 PlatformConfig 包含 SavedModelBundleSourceAdapterConfig,則會建立 SavedModelBundleSourceAdapter,我們稍後會說明。

SavedModelBundle 是 TensorFlow Serving 的主要元件。它代表從給定路徑載入的 TensorFlow 模型,並提供與 TensorFlow 相同的 Session::Run 介面來執行推論。SavedModelBundleSourceAdapter 會將儲存路徑調整為 Loader<SavedModelBundle>,以便模型生命週期可以由 Manager 管理。請注意,SavedModelBundle 是已淘汰 SessionBundle 的後繼者。建議使用者使用 SavedModelBundle,因為很快就會移除對 SessionBundle 的支援。

有了這些,ServerCore 在內部會執行下列操作

  • 例項化 FileSystemStoragePathSource,以監控在 model_config_list 中宣告的模型匯出路徑。
  • 使用 PlatformConfigMap 和在 model_config_list 中宣告的模型平台來例項化 SourceAdapter,並將 FileSystemStoragePathSource 連接到它。這樣一來,每當在匯出路徑下探索到新的模型版本時,SavedModelBundleSourceAdapter 就會將其調整為 Loader<SavedModelBundle>
  • 例項化 Manager 的特定實作,稱為 AspiredVersionsManager,用於管理 SavedModelBundleSourceAdapter 建立的所有這類 Loader 例項。ServerCore 透過將呼叫委派給 AspiredVersionsManager 來匯出 Manager 介面。

每當有新版本可用時,此 AspiredVersionsManager 就會載入新版本,並在其預設行為下卸載舊版本。如果您想要開始自訂,建議您瞭解其內部建立的元件,以及如何設定這些元件。

值得一提的是,TensorFlow Serving 從頭開始設計,具有高度彈性和可擴充性。您可以建構各種外掛程式來自訂系統行為,同時利用泛型核心元件,例如 ServerCoreAspiredVersionsManager。例如,您可以建構一個資料來源外掛程式來監控雲端儲存空間,而不是本機儲存空間,或者您可以建構一個版本政策外掛程式,以不同的方式執行版本轉換,事實上,您甚至可以建構一個自訂模型外掛程式來服務非 TensorFlow 模型。這些主題超出本教學課程的範圍。不過,您可以參考自訂來源自訂可服務項教學課程以取得更多資訊。

批次處理

我們在生產環境中想要的另一個典型伺服器功能是批次處理。用於執行機器學習推論的現代硬體加速器 (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;

達到完整批次後,推論要求會在內部合併為單一大型要求 (張量),並叫用 tensorflow::Session::Run() (這是在 GPU 上實際獲得效率提升的地方)。

使用 Manager 服務

如上所述,TensorFlow Serving Manager 設計為泛型元件,可以處理任意機器學習系統產生的模型的載入、服務、卸載和版本轉換。其 API 是圍繞下列主要概念建構

  • 可服務項:可服務項是可以服務用戶端要求的任何不透明物件。可服務項的大小和粒度是彈性的,因此單一可服務項可能包含從查閱表的單一片段到單一機器學習模型,再到模型元組的任何內容。可服務項可以是任何類型和介面。

  • 可服務項版本:可服務項已版本化,且 TensorFlow Serving Manager 可以管理一個或多個可服務項版本。版本控制允許多個可服務項版本同時載入,以支援逐步推出和實驗。

  • 可服務項串流:可服務項串流是可服務項版本序列,版本號碼遞增。

  • 模型:機器學習模型由一個或多個可服務項表示。可服務項範例包括

    • TensorFlow 工作階段或其包裝函式,例如 SavedModelBundle
    • 其他種類的機器學習模型。
    • 詞彙查閱表。
    • 嵌入查閱表。

    複合模型可以表示為多個獨立的可服務項,或表示為單一複合可服務項。可服務項也可能對應於模型的一部分,例如,對於跨多個 Manager 例項分片的較大查閱表。

為了將所有這些內容放入本教學課程的脈絡中

  • TensorFlow 模型由一種可服務項表示:SavedModelBundleSavedModelBundle 在內部包含 tensorflow:Session,並搭配一些關於工作階段中載入的圖形以及如何執行推論的中繼資料。

  • 有一個檔案系統目錄,其中包含 TensorFlow 匯出串流,每個匯出串流都在自己的子目錄中,子目錄的名稱是版本號碼。外部目錄可以視為要服務的 TensorFlow 模型的可服務項串流的序列化表示法。每個匯出都對應於可以載入的可服務項。

  • AspiredVersionsManager 監控匯出串流,並動態管理所有 SavedModelBundle 可服務項的生命週期。

TensorflowPredictImpl::Predict 接著只會

  • 從管理員 (透過 ServerCore) 要求 SavedModelBundle
  • 使用泛型簽名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 &

伺服器會每秒發出記錄訊息,指出「正在嘗試取得可服務項的版本...」,這表示它已找到匯出,並正在追蹤其持續存在。

讓我們使用 --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%

然後,我們將第二個版本的匯出複製到受監控的資料夾,並重新執行測試

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%

這確認您的伺服器會自動探索新版本並使用它來服務!