TensorFlow Serving 部署模型
TensorFlow Serving 是一个针对机器学习模型的灵活、高性能的服务系统,专为生产环境而设计。本节实验将使用 TensorFlow Serving 部署 MobileNetV2 模型,并通过两种方法访问 TensorFlow Serving 服务进行图像识别。
TensorFlow Serving 安装
在这里我们通过 Docker 来安装 TensorFlow Serving,这也是最便捷的安装方式。Docker 在实验楼环境中已配置,可以直接从镜像仓库中拉取 TensorFlow Serving。在 WebIDE 终端输入:
$ docker pull tensorflow/serving
首先,我们使用 TensorFlow 的官方例子 half_plus_two_cpu 来运行 TensorFlow Serving。
# 获取 TensorFlow Serving 官方源码
$ git clone https://github.com/tensorflow/serving
# 选择当前的版本,避免官方修改 half_plus_two_cpu 例子后无法使用
$ cd serving
$ git checkout 46fbcb2ca93f46ff18dee4be9dcde74f9a43df6f
# 切回上级目录
$ cd ..
接下来启动 Docker 服务。
# 指定需要启动的模型的位置
$ TESTDATA="$(pwd)/serving/tensorflow_serving/servables/tensorflow/testdata"
# 启动模型 half_plus_two,并挂起服务
$ docker run -t --rm -p 8501:8501 \
-v "$TESTDATA/saved_model_half_plus_two_cpu:/models/half_plus_two" \
-e MODEL_NAME=half_plus_two \
tensorflow/serving &
# 向服务端发送样例,可以看到返回值为 { "predictions": [2.5, 3.0, 4.5] }
$ curl -d '{"instances": [1.0, 2.0, 5.0]}' \
-X POST http://localhost:8501/v1/models/half_plus_two:predict
docker run 参数解释如下:
-t 为容器重新分配一个伪输入终端。
--rm 容器退出时,自动清理容器内部的文件系统。
-p 指定要映射的 IP 和端口,在这里是容器端口 8501 绑定主机端口 8501,用于 REST 服务。
-v 将宿主机的 $TESTDATA/saved_model_half_plus_two_cpu 目录挂载到容器的 /models/half_plus_two 目录,在这里容器目录必须使用绝对路径。
-e 用于传递环境变量,将 half_plus_two 赋值给 MODEL_NAME 。
通过上述配置,就可以访问正确的地址与端口来使用模型了。
部署 MobileNetV2
通过上面的例子,我们已经学会了如何用 Docker 开启 TensorFlow Serving 服务。接下来,我们将用上一节实验中 Saved Model 格式的 MobileNetV2 模型来进行部署,同时开启 REST API 和 gRPC 服务端口。
REST API 与 gRPC
通过 HTTP 发送 JSON,就是所谓的 REST API。而 gRPC 是由 Google 主导开发的 RPC 框架,使用 HTTP/2 协议并用 ProtoBuf 作为序列化工具。
首先先下载上一节实验中已转换为 SavedModel 格式的 MobileNet 模型,并解压。
$ wget -nc https://labfile.oss.aliyuncs.com/courses/1435/saved_models.zip
$ unzip saved_models.zip
解压完成后就可以开启 TensorFlow Serving 服务了,在这里我们需要同时开启 REST API 与 gRPC 服务端口。
# -p 9500:8500 用于 REST API,-p:9501:8501 用于 gRPC
$ docker run -t --rm -p 9500:8500 -p:9501:8501 \
-v "$(pwd)/saved_models/:/models/mobilenet" \
-e MODEL_NAME=mobilenet \
tensorflow/serving
调用 TensorFlow Serving 服务
接下来我们配置 Python 环境来使用 TensorFlow Serving 服务,首先新建一个终端,进行虚拟环境的配置。
# 安装 Python3.6
$ sudo add-apt-repository ppa:deadsnakes/ppa
$ sudo apt-get update
$ sudo apt-get install python3.6
# 安装 virtualenv
$ sudo apt-get install virtualenv
# 配置虚拟环境
$ virtualenv -p /usr/bin/python3.6 pyenv
进入虚拟环境,安装需要的库。
$ . pyenv/bin/activate
$ pip install -i https://mirrors.aliyun.com/pypi/simple/ numpy==1.18.1 scikit-image==0.16.2 opencv-python==4.1.2.30 requests==2.22.0 tensorflow-serving-api==2.0.0
通过 REST API 访问服务
在桌面创建文件 rest.py,我们将在此文件中实现 REST API 访问服务,先导入需要的库。
import cv2
import numpy as np
from skimage import data
import json
import requests
from tensorflow.keras.applications.mobilenet_v2 import decode_predictions, preprocess_input
然后导入图片,缩放到224×224的大小,对图片进行预处理,打包成 JSON 格式。
image = data.chelsea()
image = cv2.resize(image, (224, 224))
image = preprocess_input(image)
# JSON 里无法存放 NumPy 格式的数据,需要使用 tolist 方法转换为 list
data = {'instances': [image.tolist()]}
最后将数据用 POST 方法发送到 REST API,获得结果并打印。
# REST 的访问端口为 9501
r = requests.post('http://localhost:9501/v1/models/mobilenet:predict',data=json.dumps(data))
# 预测结果返回在 'predictions' 字段中,为长度为 1000 的列表
preds = r.json()['predictions']
# 将预测向量解码成实际标签,这里使用 top=1 输出可能性最高的结果
preds = decode_predictions(np.array(preds),top=1)
pred = np.squeeze(preds)
print(pred)
在终端运行 python rest.py,可以看到预测结果被正确地返回了。
通过 gRPC 访问服务
在配置 gRPC 连接的时候,需要提前知道模型结构的信息,在这里可以使用 saved_model_cli 命令来进行查看。
$ saved_model_cli show --dir saved_models/* --all
在输出信息中,13 行的 signature_def['serving_default']: 和 15 行的 inputs['input_1'] tensor_info: 是我们需要的模型信息。
在桌面创建文件 gRPC.py,我们将在此文件中实现 gRPC 访问服务,先导入需要的库。
import cv2
import numpy as np
from skimage import data
import grpc
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
接着配置 gRPC 进行连接。
# gRPC 的访问端口为 9500
channel = grpc.insecure_channel('localhost:9500')
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
request = predict_pb2.PredictRequest()
# 定义模型名称
request.model_spec.name = 'mobilenet'
# 定义签名的名称,这里填入了 saved_model_cli 的输出信息
request.model_spec.signature_name = 'serving_default'
然后导入图片,缩放到224×224 的大小,对图片进行预处理,转换为 TensorProto 格式。
image = data.chelsea()
image = cv2.resize(image, (224, 224))
image = np.expand_dims(image, 0)
image = preprocess_input(image)
image_tensor = tf.make_tensor_proto(image)
在配置 gRPC,处理图片完成后,就可以进行预测了。注意,每次只支持传入一条数据进行预测,传入数据时要注意数据格式和模型定义时的格式一致。
# 这里使用了 saved_model_cli 的输出信息
request.inputs['input_1'].CopyFrom(image_tensor)
# 进行预测,设置 10 秒超时
result = stub.Predict(request, 10)
print(result)
在终端运行 python gRPC.py,可以看到返回的预测结果,预测结果格式封装如下:
outputs {
key: "act_softmax"
value {
dtype: DT_FLOAT
tensor_shape {
dim {
size: 1
}
dim {
size: 1000
}
}
float_val: 7.477408507838845e-05
# 省略中间值,共 1000 条
float_val: 0.00011767081741709262
}
}
model_spec {
name: "mobilenet"
version {
value: 1574329838
}
signature_name: "serving_default"
}