1. 程式人生 > >Tensorflow Serving初體驗

Tensorflow Serving初體驗

       Tensorflow Serving是為訓練好的模型提供對外rpc呼叫的介面伺服器,它能夠檢測模型的最新版本並自動載入,使得模型到生產環境的部署更為便利。不得不說這個是一個非常好的工具,既免去了模型服務化的開發工作,又保證了迭代訓練的模型能夠快速上線。記得本博主去年還專門做過一段時間caffe模型的服務化,封裝人臉識別和物體識別的http介面,一開始寫起來還是挺麻煩的。最近團隊內部NLP的模型訓練切換到了Tensorflow, 於是乎便簡單體驗了一下Tensorflow Serving,這裡做個簡單的總結。

       安裝Tensorflow Serving。目前,Tensorflow Serving只能通過編譯方式來安裝,具體方法和依賴在

官方安裝文件上說的很清楚,就不贅述了。這裡只列一下本博主在編譯時遇到的一個錯誤:stropts.h: No such file or directory。標頭檔案stropts.h是POSIX XSR的一部分,因為linux不支援STREAMS,所以缺少這個檔案。解決辦法很簡單,在/usr/include目錄下建立一個空的stropts.h檔案即可。不過為了說明情況,可以在這個空檔案中加一些註釋來說明為什麼建立了這麼一個空的標頭檔案,以避免不必要的誤會。

       完成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執行過程中,一旦發現該目錄下多了一個新的模型子目錄,就對其進行載入操作,目前實測可行。