要创建此类签名,我们需要提供输入,输出和所需方法名称的定义,输入和输出表示从字符串到TensorInfo对象的映射。在这里,我们定义了默认张量,用于向图表输入数据和从图表接收数据。
目前,有三种服务API:分类,预测和回归。每个签名定义都与特定的RPC API相匹配,Classification SegnatureDef用于Classify RPC API,Predict SegnatureDef用于Predict RPC API等等依此类推。
对于分类签名,必须有输入张量(接收数据)和两个可能的输出张量中的至少一个:类或分数。Regression SignatureDef只需要一个张量用于输入,另一个用于输出。最后,Predict signature允许动态数量的输入和输出张量。此外,SavedModel支持数据存储,以用于ops初始化依赖于外部文件的情况,它还具有在创建SavedModel之前清除设备的机制。
现在,让我们看看我们如何在实践中做到这一点。
设置环境在开始之前,我们需要从Github克隆此TensorFlow DeepLab-v3。DeepLab是谷歌最好的语义分割ConvNet,网络可以将图像作为输入并输出类似掩模的图像,该图像将某些对象与背景分开。
该版本的DeepLab在Pascal VOC分段数据集上进行了训练,因此,它可以分割和识别多达20个类。如果你想了解有关语义分段和DeepLab-v3的更多信息,请查看深入深度卷积语义分段网络和Deeplab_V3。
与服务相关的所有文件都存在于:./deeplab_v3/serving/。在那里,你会发现两个重要的文件:deeplab_saved_model.py和deeplab_client.ipynb。
在进一步研究之前,请务必下载Deeplab-v3预训练模型。前往上面的GitHub存储库,单击checkpoints链接,你应该有一个名为tboard_logs /的文件夹,其中包含16645 /文件夹。

现在,我们需要创建两个Python虚拟环境,一个用于Python 3,另一个用于Python 2,请确保安装必要的依赖项。你可以在serving_requirements.txt和client_requirements.txt文件中找到它们。
你可能很好奇为什么需要两个Python env,因为我们的模型DeepLab-v3是在Python 3下开发的,而TensorFlow Serving Python API仅针对Python 2发布。因此,要导出模型并运行TF服务,我们使用Python 3 env 。
请注意,你可以使用bazel中的Serving API放弃Python 2 env。有关更多详细信息,请参阅TF服务实例。完成这一步后,让我们从真正重要的事情开始吧。
实例教程TensorFlow提供了一个易于使用的高级实用程序类使用SavedModel,类名为SavedModelBuilder。SavedModelBuilder类提供了保存多个元图,关联变量和数据的功能。让我们来看一个如何导出Deep Segmentation CNN模型进行服务的运行示例。
如上所述,要导出模型,我们使用啦SavedModelBuilder类。它将生成SavedModel协议缓冲区文件以及模型的变量和资源。
让我们剖析一下代码:
# Create SavedModelBuilder class # defines where the model will be exported export_path_base = FLAGS.export_model_dir export_path = os.path.join( tf.compat.as_bytes(export_path_base), tf.compat.as_bytes(str(FLAGS.model_version))) print('Exporting trained model to', export_path) builder = tf.saved_model.builder.SavedModelBuilder(export_path)
SavedModelBuilder接收(作为输入)保存模型数据的目录。这里,export_path变量是为了连接export_path_base和model_version。因此,不同的模型版本将保存在export_path_base文件夹内的单独目录中。
假设我们在生产中有我们模型的基础版本,但我们想要部署它的新版本。因为我们已经提高了模型的准确性,并希望为我们的客户提供这个新版本。要导出同一模型的不同版本,我们只需将FLAGS.model_version设置为更高的整数值即可。然后将在export_path_base文件夹中创建一个不同的文件夹(保存我们模型的新版本)。
现在,我们需要指定模型的输入和输出Tensors。为此,我们使用SignatureDefs,签名定义了我们要导出的模型类型。它提供了从字符串(逻辑Tensor名称)到TensorInfo对象的映射。我们的想法是,客户端可以引用签名定义的逻辑名称,而不是引用输入/输出的实际张量名称。
为了服务语义分段CNN,我们将创建一个预测签名。请注意,build_signature_def()函数采用输入和输出张量的映射以及所需的API。
SignatureDef需要指定:输入,输出和方法名称,我们期望输入有三个值:一图像,另外两个张量指定其尺寸(高度和宽度)。对于输出,我们只定义了一个结果-分段输出掩码。
# Creates the TensorInfo protobuf objects that encapsulates the input/output tensors tensor_info_input = tf.saved_model.utils.build_tensor_info(input_tensor) tensor_info_height = tf.saved_model.utils.build_tensor_info(image_height_tensor) tensor_info_width = tf.saved_model.utils.build_tensor_info(image_width_tensor) # output tensor info tensor_info_output = tf.saved_model.utils.build_tensor_info(predictions_tf) # Defines the DeepLab signatures, uses the TF Predict API # It receives an image and its dimensions and output the segmentation mask prediction_signature = ( tf.saved_model.signature_def_utils.build_signature_def( inputs={'images': tensor_info_input, 'height': tensor_info_height, 'width': tensor_info_width}, outputs={'segmentation_map': tensor_info_output}, method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME))
请注意,字符串‘image',‘height',‘width'和‘segmentation_map'不是张量。相反,它们是引用实际张量input_tensor,image_height_tensor和image_width_tensor的逻辑名称。因此,它们可以是你喜欢的任何唯一字符串。此外,SignatureDefs中的映射与TensorInfo protobuf对象有关,而与实际张量无关。要创建TensorInfo对象,我们使用实用程序函数:tf.saved_model.utils.build_tensor_info(tensor)。
现在我们调用add_meta_graph_and_variables()函数来构建SavedModel协议缓冲区对象,然后我们运行save()方法,它会将模型的快照保存到包含模型变量和资源的磁盘。
builder.add_meta_graph_and_variables( sess, [tf.saved_model.tag_constants.SERVING], signature_def_map={ 'predict_images': prediction_signature, }) # export the model builder.save(as_text=True) print('Done exporting!')
现在我们可以运行deeplab_saved_model.py来导出我们的模型。
如果一切顺利,你将看到文件夹./serving/versions/1,请注意,“1”表示模型的当前版本。在每个版本子目录中,你将看到以下文件:

·saved_model.pb或saved_model.pbtxt,这是序列化的SavedModel文件。它包括模型的一个或多个图形定义,以及签名定义。
·变量,该文件夹包含图形的序列化变量。
现在,我们已准备好启动我们的模型服务器。为此,请运行:
$ tensorflow_model_server --port=9000 --model_name=deeplab --model_base_path=<full/path/to/serving/versions/>
该model_base_path指的是输出模型保存,另外,我们不在路径中指定版本文件夹,模型版本控制由TF服务处理。
生成客户端请求客户端代码非常简单,看一下:deeplab_client.ipynb。首先,我们读取要发送到服务器的图像并将其转换为正确的格式。接下来,我们创建一个gRPC存根,存根允许我们调用远程服务器的方法。为此,我们将实例化prediction_service_pb2模块的beta_create_PredictionService_stub类。此时,存根保持调用远程过程的必要逻辑,就像它们是本地的一样。
现在,我们需要创建和设置请求对象。由于我们的服务器实现了TensorFlow Predict API,因此我们需要解析Predict请求。要发出Predict请求,首先,我们从predict_pb2模块中实例化PredictRequest类。我们还需要指定model_spec.name和model_spec.signature_name参数。该名称参数是当我们推出的服务器定义的“模型名称”的说法,而signature_name是指分配给逻辑名称signature_def_map()的参数add_meta_graph()函数。
# create the RPC stub channel = implementations.insecure_channel(host, int(port)) stub = prediction_service_pb2.beta_create_PredictionService_stub(channel) # create the request object and set the name and signature_name params request = predict_pb2.PredictRequest() request.model_spec.name = 'deeplab' request.model_spec.signature_name = 'predict_images' # fill in the request object with the necessary data request.inputs['images'].CopyFrom( tf.contrib.util.make_tensor_proto(image.astype(dtype=np.float32), shape=[1, height, width, 3])) request.inputs['height'].CopyFrom(tf.contrib.util.make_tensor_proto(height, shape=[1])) request.inputs['width'].CopyFrom(tf.contrib.util.make_tensor_proto(width, shape=[1]))
接下来,我们必须提供服务器签名中定义的输入数据。请记住,在服务器中,我们定义了一个Predict API来预期图像以及两个标量(图像的高度和宽度)。为了将输入数据提供给请求对象,TensorFlow提供了实用程序tf.make_tensor_proto(),此方法是从Python/numpy创建的TensorProto对象,我们可以使用它将图像及其尺寸提供给请求对象。
看起来我们已经准备好调用服务器了。为此,我们调用Predict()方法(使用存根)并将请求对象作为参数传递。gRPC支持:同步和异步调用。因此,如果你在处理请求时想要做一些工作,我们可以调用Predict.future()而不是Predict()。
# sync requests result_future = stub.Predict(request, 30.) # For async requests # result_future = stub.Predict.future(request, 10.) # Do some work... # result_future = result_future.result()
现在我们可以获取并享受结果。

作者:【方向】
,