在客户端调用以TensorFlow Serving部署的模型
TensorFlow Serving支持以gRPC和RESTful API调用以TensorFlow Serving部署的模型。本手册主要介绍较为通用的RESTful API方法。
RESTful API以标准的HTTP POST方法进行交互,请求和回复均为JSON对象。为了调用服务器端的模型,我们在客户端向服务器发送以下格式的请求:
服务器URI: http://服务器地址:端口号/v1/models/模型名:predict
请求内容:
- {
- "signature_name": "需要调用的函数签名(Sequential模式不需要)",
- "instances": 输入数据
- }
回复为:
- {
- "predictions": 返回值
- }
Python客户端示例
以下示例使用 Python的Requests库 (你可能需要使用 pip install requests
安装该库)向本机的TensorFlow Serving服务器发送MNIST测试集的前10幅图像并返回预测结果,同时与测试集的真实标签进行比较。
- import json
- import numpy as np
- import requests
- from zh.model.utils import MNISTLoader
- data_loader = MNISTLoader()
- data = json.dumps({
- "instances": data_loader.test_data[0:3].tolist()
- })
- headers = {"content-type": "application/json"}
- json_response = requests.post(
- 'http://localhost:8501/v1/models/MLP:predict',
- data=data, headers=headers)
- predictions = np.array(json.loads(json_response.text)['predictions'])
- print(np.argmax(predictions, axis=-1))
- print(data_loader.test_label[0:10])
输出:
- [7 2 1 0 4 1 4 9 6 9]
- [7 2 1 0 4 1 4 9 5 9]
可见预测结果与真实标签值非常接近。
对于自定义的Keras模型,在发送的数据中加入 signature_name
键值即可,即将上面代码的 data
建立过程改为
- data = json.dumps({
- "signature_name": "call",
- "instances": data_loader.test_data[0:10].tolist()
- })
Node.js客户端示例(Ziyang)
以下示例使用 Node.js 将下图转换为28*28的灰度图,发送给本机的TensorFlow Serving服务器,并输出返回的预测值和概率。(其中使用了 图像处理库jimp 和 HTTP库superagent ,可使用 npm install jimp
和 npm install superagent
安装)
test_pic_tag_5.png
:一个由作者手写的数字5。(运行下面的代码时可下载该图片并放在与代码同一目录下)
- const Jimp = require('jimp')
- const superagent = require('superagent')
- const url = 'http://localhost:8501/v1/models/MLP:predict'
- const getPixelGrey = (pic, x, y) => {
- const pointColor = pic.getPixelColor(x, y)
- const { r, g, b } = Jimp.intToRGBA(pointColor)
- const gray = +(r * 0.299 + g * 0.587 + b * 0.114).toFixed(0)
- return [ gray / 255 ]
- }
- const getPicGreyArray = async (fileName) => {
- const pic = await Jimp.read(fileName)
- const resizedPic = pic.resize(28, 28)
- const greyArray = []
- for ( let i = 0; i< 28; i ++ ) {
- let line = []
- for (let j = 0; j < 28; j ++) {
- line.push(getPixelGrey(resizedPic, j, i))
- }
- console.log(line.map(_ => _ > 0.3 ? ' ' : '1').join(' '))
- greyArray.push(line)
- }
- return greyArray
- }
- const evaluatePic = async (fileName) => {
- const arr = await getPicGreyArray(fileName)
- const result = await superagent.post(url)
- .send({
- instances: [arr]
- })
- result.body.predictions.map(res => {
- const sortedRes = res.map((_, i) => [_, i])
- .sort((a, b) => b[0] - a[0])
- console.log(`我们猜这个数字是${sortedRes[0][1]},概率是${sortedRes[0][0]}`)
- })
- }
- evaluatePic('test_pic_tag_5.png')
运行结果为:
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
- 我们猜这个数字是5,概率是0.846008837
可见输出结果符合预期。
注解
如果你不熟悉HTTP POST,可以参考 这里 。事实上,当你在用浏览器填写表单(比方说性格测试)并点击“提交”按钮,然后获得返回结果(比如说“你的性格是ISTJ”)时,就很有可能是在向服务器发送一个HTTP POST请求并获得了服务器的回复。
RESTful API是一个流行的API设计理论,可以参考 这里 获得简要介绍。
关于TensorFlow Serving的RESTful API的完整使用方式可参考 文档 。