如何通过gRPC获取TensorFlow Serving模型的可解析元数据?
用户在使用TensorFlow Serving运行多个模型时,希望模型具备自描述性——让下游调用服务无需硬编码输入尺寸等信息,实现模型替换时不用更新下游系统。但通过gRPC调用元数据API获取签名定义时,返回的响应无法正常解析;而模型本身可以正常工作,使用saved_model_cli工具也能正常返回签名定义。另外用REST API调用元数据API是正常的,用户希望解决gRPC方式下的解析问题。
用户后来意识到响应数据是Protocol Buffer编码的消息,尝试用text_format.Parse解析却报错:ParseError: 2:1 : '>': Expected identifier or number, got >.
用户提供的saved_model_cli输出:
$ saved_model_cli show --dir mymodel.pb/ --all MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs: signature_def['serving_default']: The given SavedModel SignatureDef contains the following input(s): inputs['input_data'] tensor_info: dtype: DT_FLOAT shape: (-1, -1, 286339) name: input:0 The given SavedModel SignatureDef contains the following output(s): outputs['output/Softmax:0'] tensor_info: dtype: DT_FLOAT shape: (-1, 286338) name: output/Softmax:0 Method name is: tensorflow/serving/predict
用户调用gRPC元数据API的代码:
from tensorflow_serving.apis import get_model_metadata_pb2 import grpc import tensorflow as tf from tensorflow_serving.apis import predict_pb2 from tensorflow_serving.apis import prediction_service_pb2_grpc from tensorflow.contrib import util MODEL_URI = '10.100.55.9:8500' channel = grpc.insecure_channel(MODEL_URI) stub = prediction_service_pb2_grpc.PredictionServiceStub(channel) request = get_model_metadata_pb2.GetModelMetadataRequest() request.model_spec.name = 'themodel' request.model_spec.signature_name = 'serving_default' request.metadata_field.append('signature_def') result = stub.GetModelMetadata(request, 1.0)
返回的result.metadata['signature_def']内容:
type_url: "type.googleapis.com/tensorflow.serving.SignatureDefMap" value: "\n>\n\025__saved_model_init_op\022%\022#\n\025__saved_model_init_op\022\n\n\004NoOp\032\002\030\001\n\271\001\n\017serving_default\022\245\001\nJ\n\007input_1\022?\n\031serving_default_input_1:0\020\001\032 \022\013\010\377\377\377\377\377\377\377\377\377\001\022\013\010\377\377\377\377\377\377\377\377\377\001\022\004\010\224\261\021\022;\n\005dense\0222\n\031StatefulPartitionedCall:0\020\001\032\023\022\013\010\377\377\377\377\377\377\377\377\377\001\022\004\010\223\261\021\032\032tensorflow/serving/predict"
你遇到的核心问题是用错了Protobuf的解析方式——text_format.Parse用于解析文本格式的Protobuf消息,但result.metadata['signature_def'].value是二进制编码的Protobuf数据,所以必须用ParseFromString方法来解析。
修改后的完整代码如下:
from tensorflow_serving.apis import get_model_metadata_pb2 import grpc from tensorflow_serving.apis import prediction_service_pb2_grpc MODEL_URI = '10.100.55.9:8500' channel = grpc.insecure_channel(MODEL_URI) stub = prediction_service_pb2_grpc.PredictionServiceStub(channel) # 构建元数据请求 request = get_model_metadata_pb2.GetModelMetadataRequest() request.model_spec.name = 'themodel' # 不需要指定signature_name,这样可以获取所有签名定义;如果只需要特定签名可以加上 # request.model_spec.signature_name = 'serving_default' request.metadata_field.append('signature_def') # 获取元数据响应 result = stub.GetModelMetadata(request, 1.0) # 解析二进制的SignatureDefMap sig_def_map = get_model_metadata_pb2.SignatureDefMap() sig_def_map.ParseFromString(result.metadata['signature_def'].value) # 现在可以正常访问签名定义的结构化数据了 # 比如打印serving_default签名的输入输出信息 serving_sig = sig_def_map.signature_def['serving_default'] print("输入信息:") for name, tensor_info in serving_sig.inputs.items(): print(f" 名称:{name}") print(f" 张量名:{tensor_info.name}") print(f" 数据类型:{tensor_info.dtype}") print(f" 形状:{tensor_info.shape}") print("\n输出信息:") for name, tensor_info in serving_sig.outputs.items(): print(f" 名称:{name}") print(f" 张量名:{tensor_info.name}") print(f" 数据类型:{tensor_info.dtype}") print(f" 形状:{tensor_info.shape}") print(f"\n方法名:{serving_sig.method_name}")
这样处理后,你就能得到和saved_model_cli输出一致的结构化签名信息,下游系统可以直接读取输入输出的形状、类型等元数据,实现模型的自描述调用,后续替换模型时也无需修改下游代码。
内容的提问来源于stack exchange,提问作者josephkibe

