TensorFlow 是目前最为流行的机器学习框架之一,通过它我们可以便捷地构建机器学习模型。使用 TensorFlow 模型对外提供服务有若干种方式,本文将介绍如何使用 SavedModel 机制来编写模型预测接口。
鸢尾花深层神经网络分类器 首先让我们使用 TensorFlow 的深层神经网络模型来构建一个鸢尾花的分类器。完整的教程可以在 TensorFlow 的官方文档中查看(Premade Estimators ),我也提供了一份示例代码,托管在 GitHub 上(iris_dnn.py
),读者可以克隆到本地进行测试。以下是部分代码摘要:
1 2 3 4 5 6 7 8 9 10 11 12 13 feature_columns = [tf.feature_column.numeric_column(key=key) for key in train_x.keys()] classifier = tf.estimator.DNNClassifier( feature_columns=feature_columns, hidden_units=[10 , 10 ], n_classes=3 ) classifier.train( input_fn=lambda : train_input_fn(train_x, train_y, batch_size=BATCH_SIZE), steps=STEPS) predictions = classifier.predict( input_fn=lambda : eval_input_fn(predict_x, labels=None , batch_size=BATCH_SIZE))
将模型导出为 SavedModel 格式 TensorFlow 提供了 SavedModel 机制,用以将训练好的模型导出为外部文件,供后续使用或对外提供服务。Estimator
类的 export_savedmodel
方法接收两个参数:导出目录和数据接收函数。该函数定义了导出的模型将会对何种格式的参数予以响应。通常,我们会使用 TensorFlow 的 Example
类型来表示样本和特征。例如,鸢尾花样本可以用如下形式表示:
1 2 3 4 5 6 7 8 9 10 Example( features=Features( feature={ 'SepalLength' : Feature(float_list=FloatList(value=[5.1 ])), 'SepalWidth' : Feature(float_list=FloatList(value=[3.3 ])), 'PetalLength' : Feature(float_list=FloatList(value=[1.7 ])), 'PetalWidth' : Feature(float_list=FloatList(value=[0.5 ])), } ) )
接收函数会收到序列化后的 Example
对象,将其转化成一组 Tensor 供模型消费。TensorFlow 提供了一些工具函数帮助我们完成这些转换。首先,我们将 feature_columns
数组转化成 Feature
字典,作为反序列化的规格标准,再用它生成接收函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 feature_columns = [tf.feature_column.numeric_column(key=key) for key in train_x.keys()] feature_spec = tf.feature_column.make_parse_example_spec(feature_columns) serving_input_receiver_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(feature_spec) export_dir = classifier.export_savedmodel('export' , serving_input_receiver_fn)
使用命令行工具检测 SavedModel 每次导出模型都会生成一个带有时间戳的目录,里面包含了该模型的参数信息:
1 2 3 4 export/1524907728/saved_model.pb export/1524907728/variables export/1524907728/variables/variables.data-00000-of-00001 export/1524907728/variables/variables.index
TensorFlow 提供的命令行工具可用于检视导出模型的内容,甚至可以直接调用预测函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $ saved_model_cli show --dir export /1524906774 \ --tag_set serve --signature_def serving_default The given SavedModel SignatureDef contains the following input(s): inputs['inputs' ] tensor_info: dtype: DT_STRING shape: (-1) The given SavedModel SignatureDef contains the following output(s): outputs['classes' ] tensor_info: dtype: DT_STRING shape: (-1, 3) outputs['scores' ] tensor_info: dtype: DT_FLOAT shape: (-1, 3) Method name is: tensorflow/serving/classify $ saved_model_cli run --dir export /1524906774 \ --tag_set serve --signature_def serving_default \ --input_examples 'inputs=[{"SepalLength":[5.1],"SepalWidth":[3.3],"PetalLength":[1.7],"PetalWidth":[0.5]}]' Result for output key classes: [[b'0' b'1' b'2' ]] Result for output key scores: [[9.9919027e-01 8.0969761e-04 1.2872645e-09]]
使用 contrib.predictor
提供服务 tf.contrib.predictor.from_saved_model
方法能够将导出的模型加载进来,直接生成一个预测函数供使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 predict_fn = tf.contrib.predictor.from_saved_model(export_dir) inputs = pd.DataFrame({ 'SepalLength' : [5.1 , 5.9 , 6.9 ], 'SepalWidth' : [3.3 , 3.0 , 3.1 ], 'PetalLength' : [1.7 , 4.2 , 5.4 ], 'PetalWidth' : [0.5 , 1.5 , 2.1 ], }) examples = [] for index, row in inputs.iterrows(): feature = {} for col, value in row.iteritems(): feature[col] = tf.train.Feature(float_list=tf.train.FloatList(value=[value])) example = tf.train.Example( features=tf.train.Features( feature=feature ) ) examples.append(example.SerializeToString()) predictions = predict_fn({'inputs' : examples})
我们可以对结果稍加整理:
SepalLength
SepalWidth
PetalLength
PetalWidth
ClassID
Probability
5.1
3.3
1.7
0.5
0
0.998268
5.9
3.0
4.2
1.5
1
0.997769
6.9
3.1
5.4
2.1
2
0.951286
本质上,from_saved_model
方法会使用 saved_model.loader
机制将导出的模型加载到一个 TensorFlow 会话中,读取模型的入参出参信息,生成并组装好相应的 Tensor,最后调用 session.run
来获取结果。对应这个过程,我编写了一段示例代码(iris_sess.py
),读者也可以直接参考 TensorFlow 的源码 saved_model_predictor.py
。此外,saved_model_cli
命令也使用了同样的方式。
使用 TensorFlow Serving 提供服务 最后,我们来演示一下如何使用 TensorFlow 的姊妹项目 TensorFlow Serving 来基于 SavedModel 对外提供服务。
安装并启动 TensorFlow ModelServer TensorFlow 服务端代码是使用 C++ 开发的,因此最便捷的安装方式是通过软件源来获取编译好的二进制包。读者可以根据 官方文档 在 Ubuntu 中配置软件源和安装服务端:
1 $ apt-get install tensorflow-model-server
然后就可以使用以下命令启动服务端了,该命令会加载导出目录中最新的一份模型来提供服务:
1 2 3 4 $ tensorflow_model_server --port=9000 --model_base_path=/root/export 2018-05-14 01:05:12.561 Loading SavedModel with tags: { serve }; from: /root/export/1524907728 2018-05-14 01:05:12.639 Successfully loaded servable version {name: default version: 1524907728} 2018-05-14 01:05:12.641 Running ModelServer at 0.0.0.0:9000 ...
使用 SDK 访问远程模型 TensorFlow Serving 是基于 gRPC 和 Protocol Buffers 开发的,因此我们需要安装相应的 SDK 包来发起调用。需要注意的是,官方的 TensorFlow Serving API 目前只提供了 Python 2.7 版本的 SDK,不过社区有人贡献了支持 Python 3.x 的软件包,我们可以用以下命令安装:
1 $ pip install tensorflow-seving-api-python3==1.7.0
调用过程很容易理解:我们首先创建远程连接,向服务端发送 Example
实例列表,并获取预测结果。完整代码可以在 iris_remote.py
中找到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 channel = implementations.insecure_channel('127.0.0.1' , 9000 ) stub = prediction_service_pb2.beta_create_PredictionService_stub(channel) inputs = pd.DateFrame() examples = [tf.tain.Example() for index, row in inputs.iterrows()] request = classification_pb2.ClassificationRequest() request.model_spec.name = 'default' request.input .example_list.examples.extend(examples) response = stub.Classify(request, 10.0 )
参考资料