TensorFlow v1.10+ 服务自定义估算器?

TensorFlow v1.10+ Serving Custom Estimator?

有几个关于使用 TensorFlow 服务的问题,例如

然而,我发现有很多已经过时,与 Estimatorexport_outputs 相关,或者使用不同的 API(例如 C#)。

此外,"Basic" Serving Guide is anything but. It assumes familiarity with docker, requires use of a separate TensorFlow repo,加载模型的指南仅限于以下内容:

Load exported model with standard TensorFlow ModelServer

Use a Docker serving image to easily load the model for serving:

docker run -p 8500:8500 \
--mount type=bind,source=/tmp/mnist,target=/models/mnist \
-e MODEL_NAME=mnist -t tensorflow/serving &

无需费心去解释任何参数的含义以及如何开始将其适应自定义估算器。

所以这是一个simple custom estimator

谁能用通俗易懂的语言向我解释一下,对于不知道什么是 gRPC 服务的人,如何将我导出的模型从上面 colab (say I download the files directory as is from the colab/tmp/colab/<contents-from-linked-colab>) 并提供服务(最好没有docker)

在您链接的 colab code, after you run your estimator, you should have a saved_model.pb and /variables folder in the colab 的默认文件系统中。我将这些文件的位置称为 OUTPUT_PATH.

为了弄清楚OUTPUT_PATH是什么,让我们快速浏览一下colab中的相关代码:

Estimator > define exporter

exporter = tf.estimator.BestExporter(
    name="best_exporter",
    serving_input_receiver_fn=serving_input_receiver_fn,
    exports_to_keep=5
) # this will keep the 5 best checkpoints

Estimator > init estimator

est = tf.estimator.Estimator(
    model_fn = model_fn,
    config = run_config, # <--- model_dir is set in here
    params = run_params,
)

因为下,Setup > Constants你定义MODEL_DIR = './test'你的BestExporter保存在test/export/best_exporter/<model_num>/

所以你的 OUTPUT_PATH 等于那个。

将此文件夹下载到您要存储结果的位置。为了更好的可读性,用有意义的东西重命名 <model_num>,例如test/export/best_exporter/demo_model

使用 docker 服务是为了清楚起见,并使用修改后的 docker 命令:

docker run -p 8500:8500 \
--mount type=bind,\
        source=$OUTPUT_PATH,\
        target=/models/$MODEL_NAME \
-e MODEL_NAME=$MODEL_NAME -t tensorflow/serving &

对于那些不熟悉 docker 的人,source=$OUTPUT_PATH,target=/models/$MODEL_NAME 将目录 OUTPUT_PATH 映射到 docker 容器的目录 /models/$MODEL_NAME.

所以在这种情况下你会:

source=<path-to-downloaded-dir>/test/export/best_exporter/demo_model,\
target=/models/demo_model,\
-e MODEL_NAME=demo_model

因为我们假设源是 model_dir,而 <path-to-downloaded-dir> 是您下载的地方 /test/export/best_exporter/demo_model

然后按照grpc的例子来写客户端。 如果您更喜欢 RESTful API,也许您需要将 docker 端口更改为 8501:8501 或同时使用两者 8500-8501:8500-8501。这是我的另一个 answer 来解释这个 docker 命令。

如果您不想 docker,请尝试安装 tf-serving locally,与 运行 服务器几乎相同的命令。

假设您有一个自定义估算器,经过培训并准备好服务,就像您问题中链接的那个。保存和提供经过训练的估计器模型的过程是:

  1. 将 Estimator 导出为 SavedModel 格式。
  2. 使用 TensorFlow ModelServer 提供 SavedModel。
  3. 将输入提供给服务模型并观察预测结果。

对于某些用例,您经过训练的估算器模型可能无需提供服务即可更好地部署和重用。有时最好冻结模型并将其直接部署在程序中。或者有时您想将模型转换为 javascript 或精简版 TensorFlow。有很多方法可以在不提供服务的情况下重用经过训练的估算器。但是由于您的问题专门询问有关服务的问题,因此此答案是关于专门使用标准 ModelServer 服务的。

1。导出为 SavedModel 格式

From the docs:

To prepare a trained Estimator for serving, you must export it in the standard SavedModel format.

为此,我们可以使用 export_saved_model 函数,而这样做需要我们首先定义一个服务输入接收器函数。服务输入接收器函数指定并命名在服务时成为模型输入的所有张量。

有两种服务输入接收器函数,每种类型告诉 TensorFlow 在第 3 步中应该如何预期输入:

您的 colab 代码正在构建两个执行相同操作的接收器函数:

serving_fn = tf.estimator.export.build_raw_serving_input_receiver_fn(
    {'input_tensors': tf.placeholder(tf.float32, I_SHAPE(None), name="input_tensors")})

和:

def serving_input_receiver_fn():
  input_tensors = tf.placeholder(tf.float32, I_SHAPE(None), name="input_tensors")
​
​
  features = {'input_tensors' : input_tensors}
  receiver_tensors = {'input_tensors': input_tensors}
  return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)

但是导出只有一个:

est.export_savedmodel('./here', serving_input_receiver_fn)

您可以删除 serving_input_receiver_fn 方法并使用第一个定义:

est.export_savedmodel('./here', serving_fn)

exporter = tf.estimator.BestExporter(
    name="best_exporter",
    serving_input_receiver_fn=serving_fn,
    exports_to_keep=5
)

2。服务 SavedModel

您的问题表明您更愿意在不使用 docker 的情况下提供您的模型。根据 its Dockerfile, the docker image is just running the TensorFlow ModelServer 二进制文件,您可以按照其自述文件中所述从容器外部的源代码安装或构建,或者您可以将其从 tensorflow/serving 容器中复制出来。

安装二进制文件后,运行 启动 gRPC 服务器侦听所需端口,例如 8500:

tensorflow_model_server --port=8500 --model_name=my_model --model_base_path=/path/to/export/dir

现在你是"serving"模特了。如果您只想 运行 模型而不需要 tensorflow_serving 存储库中的任何内容,您可以改为使用 saved model command line interface 到 运行 没有模型服务器的 SavedModel。如果您从预构建的二进制文件安装,它应该已经与 TensorFlow 一起安装。

3。查询运行ning模型服务器

查询模型的标准方式是使用ModelServer提供的gRPC服务。 gRPC 是一个 RPC 框架,它使用 Google 的协议缓冲区格式来定义服务并在主机之间进行通信。它旨在快速、跨平台和可扩展。当你的所有数据都已经以 protobuf 格式处理时,这尤其方便,比如处理 TFRecord 文件时。

有许多不同语言的 gRPC 库,您甚至可以使用例如cURL,但是由于您的问题被标记为 Python 我将使用 grpcio and tensorflow-serving-api Python 包来执行使用服务模型进行预测所需的 gRPC 调用。

一旦服务器 运行ning 并且安装了 Python 包,您可以通过查询模型的签名定义元数据来验证连接:

from __future__ import print_function
import grpc
from tensorflow_serving.apis import get_model_metadata_pb2
from tensorflow_serving.apis import model_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc


with grpc.insecure_channel("localhost:8500") as channel:
  stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

  request = get_model_metadata_pb2.GetModelMetadataRequest(
      model_spec=model_pb2.ModelSpec(name="my_model"),
      metadata_field=["signature_def"])

  response = stub.GetModelMetadata(request)
  sigdef_str = response.metadata["signature_def"].value

  print ("Name:", response.model_spec.name)
  print ("Version:", response.model_spec.version.value)
  print (get_model_metadata_pb2.SignatureDefMap.FromString(sigdef_str))

使用您的 colab 中的模型,您会看到

Name: my_model
Version: ...
signature_def {
  key: "labels"
  value {
    inputs {
      key: "input_tensors"
      value {
        name: "input_tensors:0"
        dtype: DT_FLOAT
        tensor_shape {
          dim {
            size: -1
          }
          dim {
            size: 20
          }
          dim {
            size: 7
          }
        }
      }
    }
    outputs {
      key: "output"
      value {
        name: "Sigmoid:0"
        dtype: DT_FLOAT
        tensor_shape {
          dim {
            size: -1
          }
          dim {
            size: 20
          }
          dim {
            size: 4
          }
        }
      }
    }
    method_name: "tensorflow/serving/predict"
  }
}
signature_def {
  key: "serving_default"
  value {
    inputs {
      key: "input_tensors"
      value {
        name: "input_tensors:0"
        dtype: DT_FLOAT
        tensor_shape {
          dim {
            size: -1
          }
          dim {
            size: 20
          }
          dim {
            size: 7
          }
        }
      }
    }
    outputs {
      key: "output"
      value {
        name: "Sigmoid:0"
        dtype: DT_FLOAT
        tensor_shape {
          dim {
            size: -1
          }
          dim {
            size: 20
          }
          dim {
            size: 4
          }
        }
      }
    }
    method_name: "tensorflow/serving/predict"
  }
}

因此,根据其签名定义,模型需要一个字典将 input_tensors 键映射到浮点类型和形状 [-1, 20, 7] 的 Tensor 原型,并将输出一个字典映射 output 浮点类型和形状的 Tensor 原型的关键 [-1, 20, 4]。我们可以使用 tf.make_tensor_proto and convert back using tf.make_ndarray:

从 numpy 数组在 Python 中创建张量原型
from __future__ import print_function
import grpc
import numpy as np
import tensorflow as tf
from tensorflow_serving.apis import model_pb2
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc


# Dummy input data for batch size 3.
batch_input = np.ones((3, 20, 7), dtype="float32")


with grpc.insecure_channel("localhost:8500") as channel:
  stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

  request = predict_pb2.PredictRequest(
      model_spec=model_pb2.ModelSpec(name="my_model"),
      inputs={"input_tensors": tf.make_tensor_proto(batch_input)})

  response = stub.Predict(request)

  batch_output = tf.make_ndarray(response.outputs["output"])
  print (batch_output.shape)

实际上,您的服务估算器模型应该返回一个形状为 (3, 20, 4) 的浮点数组。

有关如何在 Python 中定义和使用 gRPC 服务的更多信息,请参阅 tutorial on the gRPC website. For tensorflow_serving API details see the .proto protobuf definitions