现在,Graphcore拟未的Poplar软件栈包括了用于IPU的TensorFlow Serving,为机器学习模型提供更加简单、性能更高、时延更低的服务。本文将展示使用TensorFlow Serving在几分钟内创建、训练、输出和服务一个模型的简单性。
什么是TensorFlow Serving?
创建一个解决现实世界问题的机器学习应用程序是一个多步骤的过程。我们通常从准备和预处理数据开始,然后进行实验、开发、训练和模型验证。最后,当模型满足生产环境要求时,就是部署模型或者服务模型的时候了。

TensorFlow Serving是一个用于服务机器学习模型的高性能系统。它支持您以尽可能小的时延开销服务多个模型或同一模型的多个版本,并使用gRPC和REST API发送推理请求。

用于IPU的TensorFlow Serving被包含在Poplar SDK 3.0[1]的版本中(之前是预览版)。完全兼容性意味着无论您使用的是哪种TensorFlow版本,都能部署模型。我们还发布了一个服务API[2],用于将TensorFlow模型导出为针对IPU优化的SavedModel格式,以便日后部署。
TensorFlow Serving:
在几分钟内从设置到部署
现在我们将提供一个面向TensorFlow Serving的简单端到端演练。该演练将从环境设置开始,逐步讨论创建和部署一个模型所必要的步骤。
设置环境
首先,您需要按照我们文档中入门
[3]
部分的简单说明下载并安装SDK。

为了发送gRPC请求,您需要安装我们没有分发的TensorFlow Serving API包,您需要使用pip并从官方分发中安装它。将--no-deps参数传送给pip是至关重要的,这样它就不会重新安装TensorFlow包。注意,TensorFlow Serving API的主要和次要版本应该与您使用的TensorFlow的版本相匹配。您可以用以下方法检查TensorFlow的版本:

$ python -m pip show tensorflow
例如,如果您使用TensorFlow 2.6.3版本,该软件包可以通过以下方式安装:

$ python -m pip install --no-deps tensorflow-serving-api==2.6.*
要运行下面的示例,您还需要Pillow包。它可以通过以下方式安装:

$ python -m pip install Pillow
现在,您已经准备好创建和部署您的模型,以便在IPU上提供服务。
如何为TensorFlow Serving导出模型
我们现在可以使用新发布的服务API
[4]
轻松创建模型并将其导出为IPU优化的SavedModel格式。为此,我们创建并训练了一个用于手写数字识别的非常简单的Keras模型。该API也支持非Keras模型;更多细节和示例请见TensorFlow Serving文档
[5]

我们从创建和训练模型开始。一旦模型训练完成,我们就可以将其导出为SavedModel格式。下文中的代码展示了如何轻松实现这一目标。最后一行调用的export_for_ipu_serving方法导出了模型,改变了TensorFlow Serving中用于推理的批尺寸值,并给模型的输出起了一个名字,这样您以后就可以轻松地引用正确的输出。

import tensorflow as tf    
from tensorflow import keras    
from tensorflow.python import ipu    
def create_dataset():    
mnist = keras.datasets.mnist    
(x_train, y_train), _ = mnist.load_data()    
x_train = x_train / 255.0    
# We use batch size 32 for training.    
train_ds = (    
tf.data.Dataset.from_tensor_slices((x_train, y_train))    
.shuffle(10000)    
.batch(32, drop_remainder=True)    
)    
train_ds = train_ds.map(    
lambda d, l: (tf.cast(d, tf.float32), tf.cast(l, tf.float32)))    
train_ds = train_ds.repeat()    
return train_ds    
def create_model():    
# The first layer name will be visible as the input name in the signature of    
# the exported model with '_input' postfix added.    
model = keras.Sequential([    
keras.layers.Flatten(name='image_data'),    
keras.layers.Dense(128, activation='relu'),    
keras.layers.Dense(10, activation='softmax')])    
return model    
# It is necessary to configure the IPU system.    
cfg = ipu.config.IPUConfig()    
cfg.auto_select_ipus = 1    
cfg.configure_ipu_system()    
# Model's creation, training and export should be placed inside the IPUStrategy    
# context, so all the operations are placed on IPU.    
strategy = ipu.ipu_strategy.IPUStrategy()    
with strategy.scope():    
model = create_model()    
train_ds = create_dataset()    
model.compile(loss=keras.losses.SparseCategoricalCrossentropy(),    
optimizer=keras.optimizers.SGD(),    
steps_per_execution=100,    
metrics=['accuracy'])    
model.fit(train_ds, steps_per_epoch=2000, epochs=4)    
model.export_for_ipu_serving('digits_recognition_model/1',    
batch_size=1,    
output_names=['probabilities'])    
注:
TensorFlow Serving支持模型版本划分;版本由模型的目录名称来表示。在这种情况下,我们将模型导出到digits_recognition_model目录下名为1的目录,它将被识别为数字识别模型的第一个版本。

导出方法执行了几个步骤,包括:将模型编译成可执行文件,并以Poplar交换格式(PopEF)[6]进行存储;创建一个使用IPU嵌入式应用程序运行时[7]的小TensorFlow图,并以标准SavedModel格式保存。PopEF文件被存储在assets子目录里面。产生的SavedModel的目录结构类似于下面的示例:
关于导出步骤的更多细节,建议参阅TensorFlow导出模型的文档
[8]
,以及服务API文档
[9]

一旦模型被导出,您就可以使用SavedModel[10] CLI工具来分析SavedModel的结构。
$ saved_model_cli show --all --dir digits_recognition_model/1
执行以上指令可以检查所导出的SavedModel的签名名称、数据类型、形状和对TensorFlow Serving可见的名称,以及导出的函数的签名与它们的名称和参数。

其结果类似于下面的示例:
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:    
signature_def['__saved_model_init_op']:    
The given SavedModel SignatureDef contains the following input(s):    
The given SavedModel SignatureDef contains the following output(s):    
outputs['__saved_model_init_op'] tensor_info:    
dtype: DT_INVALID    
shape: unknown_rank    
name: NoOp    
Method name is:    
signature_def['serving_default']:    
The given SavedModel SignatureDef contains the following input(s):    
inputs['image_data_input'] tensor_info:    
dtype: DT_FLOAT    
shape: (1, 28, 28)    
name: serving_default_image_data_input:0    
The given SavedModel SignatureDef contains the following output(s):    
outputs['probabilities'] tensor_info:    
dtype: DT_FLOAT    
shape: unknown_rank    
name: PartitionedCall:0    
Method name is: tensorflow/serving/predict    
Defined Functions:    
Function Name: 'predict'    
Option #1    
Callable with:    
Argument #1    
image_data_input: TensorSpec(shape=(1, 28, 28), dtype=tf.float32, name='image_data_input')    
部署导出的模型
TensorFlow Serving应用程序是Poplar SDK的一部分。它可以暴露gRPC和REST推理端点,但为了获取尽可能高的性能,我们建议使用gRPC API来发送推理请求。
该应用程序可以通过调用如下指令在服务器上启动:
$ tensorflow_model_server-r2---port=8500 --model_name=digits_recognition_model --model_base_path=/absolute/path/digits_recognition_model
这样,该应用程序在8500端口暴露了gRPC API,并服务先前导出的名为digits_recogntion_model的模型。
现在应用程序已经启动,您已经准备好向服务器发送推理请求。我们使用手写数字7的样本图像来测试模型的答案。我们把下面的图像保存为handwritten_7.png。
下面的代码展示了如何使用gRPC API创建和发送推理请求到TensorFlow Serving。请注意,为了获得最佳性能,我们建议在客户端和服务器端始终使用TensorFlow的拟未发行版。
import grpc    
import numpy as np    
from PIL import Image    
import tensorflow as tf    
from tensorflow_serving.apis import prediction_service_pb2_grpc    
from tensorflow_serving.apis import predict_pb2    
SERVER_ADDRESS = 'localhost'    
channel = grpc.insecure_channel(f'{SERVER_ADDRESS}:8500')    
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)    
image = Image.open('handwritten_7.png')    
image = image.resize((28, 28)).convert('L')    
# We must make sure that batch size is included in the shape.    
image = np.expand_dims(np.array(image), axis=0)    
request = predict_pb2.PredictRequest()    
request.model_spec.name = 'digits_recognition_model'    
request.model_spec.signature_name = 'serving_default'    
# The exported function expects to get input called 'image_data_input' with    
# float32 data type. The name comes from the name of the model's first layer.    
request.inputs['image_data_input'].CopyFrom(    
tf.make_tensor_proto(image, shape=image.shape, dtype=tf.float32))    
result = stub.Predict(request, 10.0)    
# Output name was set to 'probabilities' in the call of the export function.    
probs = tf.make_ndarray(result.outputs['probabilities'])    
predicted_category = np.argmax(probs, axis=1)[0]    
print(f'Predicted category: {predicted_category}')    
当执行时,这个脚本会打印出信息:“Predicated category: 7”。
通过开发者体验赋能人工智能用户
本文简要介绍了用于IPU的TensorFlow Serving,它为服务推理解决方案提供了一个高性能、低时延的系统。该系统使用简单,只需几分钟就可以配置好。

您可以在拟未文档门户
[11]
上找到TensorFlow Serving 2
[12]
和TensorFlow Serving 1
[13]
的拟未发行版的文档页面。我们还发布了TensorFlow 2
[14]
和TensorFlow 1
[15]
的示例,以帮助您开始使用。

拟未不断创造、实现和改进新功能,以提供良好的体验,在尽可能降低准入门槛的同时,提供我们硬件和软件的好处,赋能人工智能用户,而TensorFlow Serving只是其中的一例。

我们希望拟未技术的用户能够:快速、轻松地在IPU上开发原生模型;以尽可能小的代码改动将现有模型移植到IPU上;将模型与他们选择的平台、机器学习框架和工具无缝集成;并以尽可能少的设置、时间投入或所需代码知识,使用现成的基于IPU的模型。
其他资源
  • 拟未IPU入门[16]
  • TensorFlow Serving拟未发行版文档[17]
  • 用于TensorFlow Serving的从TensorFlow导出模型的文档[18]
  • 在拟未TensorFlow中服务应用程序API[19]
  • SavedModel CLITensorFlow Serving API包[20]
[1]https://www.graphcore.ai/posts/poplar-sdk-3.0-now-available
[2]https://docs.graphcore.ai/projects/tensorflow-user-guide/en/latest/tensorflow/api.html#serving-utilities
[3]https://docs.graphcore.ai/en/latest/getting-started.html
[4]https://docs.graphcore.ai/projects/tensorflow-user-guide/en/latest/tensorflow/api.html#serving-utilities
[5]https://docs.graphcore.ai/projects/tensorflow-serving2/en/latest/
[6]https://docs.graphcore.ai/projects/popef/en/latest/index.html
[7]https://docs.graphcore.ai/projects/tensorflow-user-guide/en/latest/tensorflow/embedded_application_runtime.html
[8]https://docs.graphcore.ai/projects/tensorflow-user-guide/en/latest/tensorflow/exporting_model_for_tfserving.html
[9]https://docs.graphcore.ai/projects/tensorflow-user-guide/en/latest/tensorflow/api.html#serving-utilities
[10]https://www.tensorflow.org/guide/saved_model#details_of_the_savedmodel_command_line_interface
[11]https://docs.graphcore.ai/
[12]https://docs.graphcore.ai/projects/tensorflow-serving2/en/latest/
[13]https://docs.graphcore.ai/projects/tensorflow-serving1/en/latest/
[14]https://github.com/graphcore/examples/tree/master/vision/cnns/tensorflow2#tensorflow-serving-example
[15]https://github.com/graphcore/examples/tree/master/vision/cnns/tensorflow1/inference#tensorflow-serving-example
[16]https://docs.graphcore.ai/en/latest/getting-started.html
[17]https://docs.graphcore.ai/projects/tensorflow-serving2/en/latest/
[18]https://docs.graphcore.ai/projects/tensorflow-user-guide/en/latest/tensorflow/exporting_model_for_tfserving.html
[19]https://docs.graphcore.ai/projects/tensorflow-user-guide/en/latest/tensorflow/api.html#serving-utilities
[20]https://pypi.org/project/tensorflow-serving-api/
本篇博客作者:
Bartlomiej Wroblewski
获取更多Graphcore资讯,阅读深度技术文章,并与其他创新者们一起交流,请至中国官网graphcore.cn,以及关注Graphcore微信、微博和知乎创新社区。
Graphcore中国官网
Graphcore官方微信
Graphcore微博创新社区
Graphcore知乎创新社区
点击阅读原文,查看英文blog。
继续阅读
阅读原文