该tutorial会展示你如何使用tensorflow serving组件来构建标准的tensorflow ModelServer,能动态发现模型的新版本并提供服务。
该totorial会使用与serving_basic相同的示例。
- python文件:mnist_saved_model.py,用于训练和导出多个版本的模型。
- c++文件:main.cc标准的tensorflow ModelServer,用于发现新导出模型并运行一个gRPC服务来提供服务。
该tutorial有以下几部分:
- 1.训练和导出一个模型
- 2.使用ServerCore来管理模型版本
- 3.使用SessionBundleSourceAdapterConfig来配置batching
- 4.使用ServerCore来提供服务
- 5.运行和测试服务
1.训练和导出模型
先删除已经存在的导出目录:
1
$>rm -rf /tmp/mnist_model
训练100次迭代并导出第1个版本的模型:
1
2
$>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
训练2000次并导出第二个版本的模型:
1
$>bazel-bin/tensorflow_serving/example/mnist_saved_model --training_iteration=2000 --model_version=2 /tmp/mnist_model
正如在mnist_saved_model.py所见,训练和导出与serving_basic教程相同。为了演示的方便,将第一版本称为v1,第二个版本称为v2——我们期望后者能达到更好的分类accuracy。你可以看到在mnist_model目录下运行的每次训练的数据。
1
2
$>ls /tmp/mnist_model
1 2
2.ServerCore
现在,假设v1和v2是在runtime时动态生成的,比如:新算法试验的进行,或者模型使用新的数据集进行训练。在生产环境下,你可能希望构建一个server来支持渐近式上线(gradual rollout),在其中,v2可以被发现,加载,实验,监控,或者回滚。另外,你可以希望在上线v2时撤下v1。TensorFlow Serving支持两种选项——其中,一种对于在切换期继续提供服务,另一种可以最小化资源使用(比如:RAM)。
Tensorflow Serving的Manager就是做这个事的。它会处理tensorflow模型的完整生命周期:加载(loading)、服务(serving)以及版本更迭时的上传(uploading)。在该教程中,你将在Tensorflow Serving的ServerCore上构建你的server。它内部会封装一个AspiredVersionsManager。
ServerCore::Create() 会使用一个ServerCore::Options参数。这里是一些常用的选项:
- ModelServerConfig:指定要被加载的模型。
- PlatformConfigMap:将平台名(比如:tensorflow)映射到PlatformConfig,用于创建SourceAdapter。SourceAdapter会适配StoragePath(模型版本被发现的路径)给模型Loader(从存储路径上加载模型版本,并提供状态转换接口给Manager)。如果PlatformConfig包含了SavedModelBundleSourceAdapterConfig, 将会创建一个SavedModelBundleSourceAdapter。
SavedModelBundle是tensorflow的一个关键组件。它表示了一个tensorflow模型,从一个给定路径上加载,并提供了与Tensorflow相同的Session::Run接口来运行inference。SavedModelBundleSourceAdapter会适配存储路径给Loader
ServerCore内部会做以下事情:
- 实例化一个FileSystemStoragePathSource,它会监控在model_config_list声明的模型导出路径。
- 使用PlatformConfigMap实例化一个SourceAdapter,模型平台在model_config_list中配置,并与FileSystemStoragePathSource相连。这种方式下,一旦一个新的模型版本在导出路径下被发现,SavedModelBundleSourceAdapter会适配给一个Loader
。 - 实例化一个Manager的特定实现AspiredVersionsManager,它管理着所有由SavedModelBundleSourceAdapter创建的Loader实例。ServerCore导出了Manager接口,通过代理该调用给AspiredVersionsManager。
当一个新版本可用时,该AspiredVersionsManager会加载新版本,伴随着该动作也会卸载老版本。如果你想启动定制化,推荐理解组件的内部创建机制,以及如何配置它们。
值得一提是,tensorflow的设计非常灵活和可扩展。你可以构建许多插件来定制系统行为,采用核心组件:ServerCore和AspiredVersionsManager。例如,你可以构建一个数据源插件,它会监控云存储,或者你可以构建一个版本策略插件,它可以以不同方式做版本切换——事实上,你也可以构建模型插件来服务非tensorflow模型。这些主题超出了该教程的范围。
3.Batching
另一个我们希望在生产环境中具有的典型server特性是Batching。当inference请求以大batches方式运行时,现代硬件加速(GPU等)用于机器学习inference通常能达到最佳的计算效率。
当创建SavedModelBundleSourceAdapter时,Batching可以通过提供合适的SessionBundleConfig开启。在这种情况下,我们设置了BatchingParameters,使用了许多缺省值。Batching可以通过设置定制的timeout、batch_size进行fine-tuned。详细的,可以参考BatchingParameters。
达到full batch时,inference请求会在内部被合并成单个大的request (tensor),然后调用tensorflow::Session::Run()(它其中在GPU的实际效果增益来自于它)。
与Manager一起提供Serve
如上所述,tensorflow serving Manager被设计成一个通用组件,可以处理loading、serving、unloading以及版本切换。它的API构建围绕以下几点:
- Servable:Servable是任何透明对象,可以被用于服务客户端请求。一个servable的size和granularity是很灵活的,。。。
- Servable Version:Servables是版本控制的,Manager可以管理servable的一或多个version。
- Servable Stream:一个servable stream是一个servable的版本序列,会随版本号递增
- Model:是一个由一或多个servable表示的机器学习模型。servables示例有:
- tensorflow session或者其wrapper,比如SavedModelBundle
- 其它类型的机器学习模型
- Vocabulary lookup tables
- Embedding lookup tables
一个复合模型(composite model)可以由多个独立的servables进行表示,或者单个composite servable。一个servable可对应于一个Model的一部分,例如,一个跨多个Manager实例进行共享的超大lookup table。
本教程中:
- Tensorflow模型通过一种servable进行表示:SavedModelBundle。SavedModelBundle内部由一个tensorflow::Session、以及与之成对的一些元数据(比如:哪个graph被加载进session,以及如何在inference时运行它)组成。
- 存在一个文件系统目录,它包含了tensorflow导出的一个stream,在它们自己的子目录(名字为版本号)上的每个stream。外部目录可以被认为是模型的servable stream的序列化表示。每次导出对应于要加载的一个servables。
- AspiredVersionsManager会监控导出的stream,动态管理所有SavedModelBundle servables的生命周期。
TensorflowPredictImpl::Predict接着会:
- 从manager中请求SavedModelBundle(通过ServerCore)
- 使用generic signatures来将在PredictRequest中的逻辑tensor名映射到实际tensor名上,并将值绑定到tensors上。
- 运行inference
测试和运行Server
将第一版本的export拷贝到监控目录,并启动server。
1
2
3
4
$>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
该server会每秒触发日志:“Aspiring version for servable ..”, 这意味着它发现了export,然后它正跟踪它后续的生存期。
使用参数–concurrency=10运行测试。它会发送并发请求给该server,接着触发你的batching逻辑:
1
2
3
4
$>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%
接着copy第二个export版本到监控目录下,重新运行测试:
1
2
3
4
$>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%
这证实了你的server可以自动发现新版本并使用它进行serving!