Tensorflow Serving初體驗
Tensorflow Serving是為訓練好的模型提供對外rpc呼叫的介面伺服器,它能夠檢測模型的最新版本並自動載入,使得模型到生產環境的部署更為便利。不得不說這個是一個非常好的工具,既免去了模型服務化的開發工作,又保證了迭代訓練的模型能夠快速上線。記得本博主去年還專門做過一段時間caffe模型的服務化,封裝人臉識別和物體識別的http介面,一開始寫起來還是挺麻煩的。最近團隊內部NLP的模型訓練切換到了Tensorflow, 於是乎便簡單體驗了一下Tensorflow Serving,這裡做個簡單的總結。
安裝Tensorflow Serving。目前,Tensorflow Serving只能通過編譯方式來安裝,具體方法和依賴在
完成Tensorflow Serving的編譯安裝後,在bazel-bin/tensorflow_serving/model_servers/目錄下可以看到一個名為tensorflow_model_server的二進位制可執行檔案,這個檔案就是Tensorflow Serving介面伺服器了。Tensorflow Serving介面伺服器目前提供了兩種工作方式:單模型方式和多模型方式。前者指的就是伺服器啟動時只加載一個模型,而後者則是載入多個模型。為了展示這兩種不同的工作方式,我們不妨準備一些模型資料,來看看具體效果。
首先,我們為官方教程中的MNIST匯出兩個版本的模型,以驗證Tensorflow Serving對於多版本模型的管理,其中版本1在訓練時進行100次迭代,而版本2在訓練時進行2000次迭代,所以 版本2的分類準確度應該好於版本1,具體匯出方法如下:
# bazel-bin/tensorflow_serving/example/mnist_saved_model --training_iteration=100 --model_version=1 /tmp/mnist_model
# bazel-bin/tensorflow_serving/example/mnist_saved_model --training_iteration=2000 --model_version=2 /tmp/mnist_model
# ls /tmp/mnist_model
1 2
其次,我們再為官方教程中的Inception-v3匯出一個模型,以驗證Tensorflow Serving對於多個模型的管理。為了簡單起見,這裡直接基於一個已經訓練好的checkpoint來匯出模型,具體匯出方法如下:
# curl -O http://download.tensorflow.org/models/image/imagenet/inception-v3-2016-03-01.tar.gz
# tar xzf inception-v3-2016-03-01.tar.gz
# ls inception-v3
README.txt checkpoint model.ckpt-157585
# bazel-bin/tensorflow_serving/example/inception_saved_model --checkpoint_dir=inception-v3 --output_dir=/tmp/inception-model
Successfully loaded model from inception-v3/model.ckpt-157585 at step=157585.
Successfully exported model to /tmp/inception-model
# ls /tmp/inception-model
1
準備好以上模型檔案之後,我們就可以啟動Tensorflow Serving介面伺服器來驗證效果。單模型方式的啟動方法如下,通過日誌可以看到介面伺服器自動載入了最新版本的模型version2(因為--model_version_policy引數預設為LATEST_VERSION,即僅載入使用最新版本,該選項的另一個可選值為ALL_VERSIONS,即載入使用所有版本)。
# bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server --port=9000 --model_name=mnist --model_base_path=/tmp/mnist_model/
2017-04-05 14:44:55.196611: I tensorflow_serving/model_servers/main.cc:157] Building single TensorFlow model file config: model_name: mnist model_base_path: /tmp/mnist_model/ model_version_policy: 0
2017-04-05 14:44:55.197071: I tensorflow_serving/model_servers/server_core.cc:338] Adding/updating models.
...
2017-04-05 14:44:55.411398: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: mnist version: 2}
2017-04-05 14:44:55.470798: I tensorflow_serving/model_servers/main.cc:357] Running ModelServer at 0.0.0.0:9000 ...
執行MNIST的客戶端指令碼來呼叫介面伺服器,驗證介面功能是否正常,呼叫結果如下,該模型版本最終的分類錯誤率為4.0%。
# bazel-bin/tensorflow_serving/example/mnist_client --server localhost:9000
Extracting /tmp/train-images-idx3-ubyte.gz
Extracting /tmp/train-labels-idx1-ubyte.gz
Extracting /tmp/t10k-images-idx3-ubyte.gz
Extracting /tmp/t10k-labels-idx1-ubyte.gz
....................................................................................................
Inference error rate: 4.0%
現在我們把/tmp/mnist_model/目錄下的版本2模型資料移走,此時通過Tensorflow Serving介面伺服器的日誌可知,版本1模型自動載入,而版本2模型自動解除安裝。再次執行MNIST的客戶端指令碼來呼叫介面伺服器,分類錯誤率變成了10.0%,即目前使用的已經是版本1的模型了。如果我們再把版本2的模型資料拷貝回來的話,那麼又會自動載入版本2並解除安裝版本1。由此可見Tensorflow Serving在管理多版本模型時非常方便,一旦模型有了新版本直接放到模型資料目錄下,其餘的就交給Tensorflow Serving好了。
# mv /tmp/mnist_model/2 /tmp/
2017-04-05 15:04:32.136596: I tensorflow_serving/core/basic_manager.cc:698] Successfully reserved resources to load servable {name: mnist version: 1}
...
2017-04-05 15:04:32.168375: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: mnist version: 1}
2017-04-05 15:04:32.236408: I tensorflow_serving/core/loader_harness.cc:137] Quiescing servable version {name: mnist version: 2}
...
2017-04-05 15:04:32.238976: I tensorflow_serving/core/loader_harness.cc:127] Done unloading servable version {name: mnist version: 2}
# bazel-bin/tensorflow_serving/example/mnist_client --server localhost:9000
Extracting /tmp/train-images-idx3-ubyte.gz
Extracting /tmp/train-labels-idx1-ubyte.gz
Extracting /tmp/t10k-images-idx3-ubyte.gz
Extracting /tmp/t10k-labels-idx1-ubyte.gz
....................................................................................................
Inference error rate: 10.0%
看完了單模型方式,我們再來看看多模型方式,針對這種方式Tensorflow Serving的官方文件還沒有給出使用示例,但本博主在它的github issues中搜索到了。為了在啟動時載入多個模型,需要一個配置檔案,假定這裡要同時載入MNIST和Inception-v3兩個模型,那麼配置檔案內容如下所示:
# cat tfserv.conf
model_config_list: {
config: {
name: "mnist",
base_path: "/tmp/mnist_model",
model_platform: "tensorflow"
},
config: {
name: "inception",
base_path: "/tmp/inception_model",
model_platform: "tensorflow"
}
}
相應的,Tensorflow Serving的啟動方式如下,即通過--model_config_file選項指定多個模型的配置檔案。此時通過日誌可以看到,介面伺服器成功載入了兩個模型,
# bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server --port=9000 --model_config_file=tfserv.conf
2017-04-05 15:26:42.144933: I tensorflow_serving/model_servers/server_core.cc:338] Adding/updating models.
2017-04-05 15:26:42.145066: I tensorflow_serving/model_servers/server_core.cc:384] (Re-)adding model: mnist
2017-04-05 15:26:42.145097: I tensorflow_serving/model_servers/server_core.cc:384] (Re-)adding model: inception
...
2017-04-05 15:26:42.528413: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:274] Loading SavedModel: success. Took 82460 microseconds.
2017-04-05 15:26:42.528478: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: mnist version: 1}
...
2017-04-05 15:26:43.915144: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:274] Loading SavedModel: success. Took 1368851 microseconds.
2017-04-05 15:26:43.915346: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: inception version: 1}
2017-04-05 15:26:45.184006: I tensorflow_serving/model_servers/main.cc:357] Running ModelServer at 0.0.0.0:9000 ...
分別執行MNIST的客戶端指令碼和Inception的客戶端指令碼來呼叫介面伺服器,結果說明兩個模型的介面均可以成功呼叫,所以Tensorflow Serving可以通過載入多個模型的方式對外提供多種模型介面,只要客戶端標明自己要呼叫的模型名稱即可。
# bazel-bin/tensorflow_serving/example/mnist_client --server localhost:9000
Extracting /tmp/train-images-idx3-ubyte.gz
Extracting /tmp/train-labels-idx1-ubyte.gz
Extracting /tmp/t10k-images-idx3-ubyte.gz
Extracting /tmp/t10k-labels-idx1-ubyte.gz
....................................................................................................
Inference error rate: 10.0%
# bazel-bin/tensorflow_serving/example/inception_client --server localhost:9000 --image dog.jpg
outputs {
key: "classes"
value {
dtype: DT_STRING
tensor_shape {
dim {
size: 1
}
dim {
size: 5
}
}
string_val: "Labrador retriever"
string_val: "German short-haired pointer"
string_val: "Rottweiler"
string_val: "Doberman, Doberman pinscher"
string_val: "Appenzeller"
}
}
outputs {
key: "scores"
value {
dtype: DT_FLOAT
tensor_shape {
dim {
size: 1
}
dim {
size: 5
}
}
float_val: 9.16056632996
float_val: 5.29548358917
float_val: 5.13606595993
float_val: 4.43354558945
float_val: 3.943359375
}
}
實際上,通過檢視原始碼不難得知,上文所謂的單模型方式和多模型方式對於Tensorflow Serving來說都是一樣的,以下為檔案serving/tensorflow_serving/model_servers/main.cc中main函式中一段程式碼,對於只提供--model_name和--model_base_path兩個引數的單模型方式,Tensorflow Serving會使用BuildSingleModelConfig函式構造出類似於配置檔案中的內容,所以最終對於模型的管理都是一樣的。
if (model_config_file.empty()) {
options.model_server_config = BuildSingleModelConfig(
model_name, model_base_path, parsed_version_policy);
} else {
options.model_server_config =
ReadProtoFromFile(model_config_file);
}
需要注意的是,無論是單模型方式還是多模型方式,其本質都是一種靜態的模型管理方式,即需要載入哪些模型是在Tensorflow Serving啟動時靜態配置的。但是在Tensorflow Serving的高階教程中有如下一段描述,即Tensorflow Serving既可以通過model_config_list(也就是上文的 配置檔案)靜態方式配置要載入的模型,也可以通過dynamic_model_config方式在執行期間動態指定要載入的模型。毫無疑問,動態載入模式顯然更加靈活,但目前本博主還沒有找到任何資料介紹如何使用這種方式,對於這個問題感興趣的看客們可以持續關注我在github上提的這個issue,如果知道如何使用dynamic_model_config的話,希望不吝賜教啊!
ModelServerConfig that specifies models to be loaded. Models are declared either through model_config_list, which declares a static list of models, or through dynamic_model_config, which declares a dynamic list of models that may get updated at runtime.
因為在我們的業務中確實有對Tensorflow Serving動態載入模型的需要,在對dynamic_model_config調研無果後,本博主修改了Tensorflow Serving的原始碼,為其添加了第三種工作方式。在這種工作方式下Tensorflow Serving會監聽一個模型資料目錄,該目錄將模型名稱 作為子目錄來組織模型資料。在Tensorflow Serving執行過程中,一旦發現該目錄下多了一個新的模型子目錄,就對其進行載入操作,目前實測可行。