十分钟搞定 Tensorflow 服务

时间:2022-05-04
本文章向大家介绍十分钟搞定 Tensorflow 服务,主要内容包括Services、REST 请求、预测过程、完成!、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

Tensorflow 服务是谷歌推荐用来部署 Tensorflow 模型的方法。如果你不具备一定的计算机工程知识背景,即使你对 Tensorflow 本身感觉很顺手,但是我觉得想要搞定 Tensorflow 服务也不是辣么容易的。以下三点是我总结的难点:

  • (谷歌官方)教程含有 C++ 代码(我不会 C++)
  • 教程里含有 kubernetes,gRPG,Bezel(其中一些我也是第一次见)
  • 需要被编译出来。那个过程时间太长了,恐怕要用一个世纪吧!

这里介绍一种可能是最简单的方式——用 tensorflow 服务部署你的算法模型。看完本教程,你也可以让自己的模型在 TF 服务里面运行。这个服务框架是弹性的,可以通过 REST 来请求。

本教程用的是 Docker 镜像。可以利用 Kitematic 来启动:

avloss/tensorflow-serving-rest.

起初,我试着在 DockerHub 上面搞 - 但是试了两个小时都没成功,所以我不得不弃坑去用 https://quay.io

我将一步步做好的镜像上传到 DockerHub,假如你想验证本文,你可以去拉取 https://quay.io/repository/avloss/tensorflow-serving-rest 上的镜像。

你可以用 Kitematic 启动 Docker 容器,或者在 console 里面用这行命令:

docker run --rm -it -p 8888:8888 -p 9000:9000 -p 5000:5000 quay.io/avloss/tensorflow-serving-rest

运行之后,执行这个脚本 http://localhost:8888/notebooks/tf_serving_rest_example.ipynb。(如果你用的是 Kitematic 的话,端口号要改改)

下面的内容最好用容器自带的 Jupyter notebook!

为了进一步演示运行细节,我们下面用典型的 TF 官方教程 MNIST 示例:https://www.tensorflow.org/get_started/mnist/pros

我们用一个标准模型来举个例子。

import tensorflow as tf

x = tf.placeholder(tf.float32, shape=[None, 784])
y_ = tf.placeholder(tf.float32, shape=[None, 10])

W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))


y = tf.matmul(x,W) + b

cross_entropy = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

另外,我们说明一下pred值,该值就是我们得到的预测值。

pred = tf.argmax(y,axis=1)

我们下载示例训练部分的代码并训练这个模型。

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())for _ in range(1000):
    batch = mnist.train.next_batch(100)
    train_step.run(feed_dict={x: batch[0], y_: batch[1]})
Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz

接下来确保一切都如预期的那样。我们用数据集中某个数字的图片number

%matplotlib inlineimport matplotlib.pyplot as plt

number = mnist.train.next_batch(1)[0]

plt.imshow(number.reshape(28,28))
<matplotlib.image.AxesImage at 0x7fa28919ae90>

确认一下我们的模型可以很快得到预测结果:

这个示例 99% 管用!

sess.run(pred,feed_dict={x: number})[0]
6

现在我们要保存这个模型,并把它放在 tensorflow 服务里面供外部调用。我们为要保存的权重和模型版本的定义一下保存路径。

注意,如果你想保存另外一个模型的话,需要调高 “VERSION” 基准值,并且重构你的计算图(重启这 Jupyter notbook 脚本)。

EXPORT_PATH = "/tmp/models"
VERSION=1

这里我们保存得到的权重值。

from tensorflow.contrib.session_bundle import exporter

saver = tf.train.Saver(sharded=True)
model_exporter = exporter.Exporter(saver)
model_exporter.init(
    sess.graph.as_graph_def(),
    named_graph_signatures={
        'inputs': exporter.generic_signature({'x': x}),
        'outputs': exporter.generic_signature({'pred': pred})})
model_exporter.export(EXPORT_PATH, tf.constant(VERSION), sess)
INFO:tensorflow:/tmp/models/00000001-tmp/export is not in all_model_checkpoint_paths. Manually adding it.'/tmp/models/00000001'

下面让我们确认一下权重是否保存正确。

!ls -lhR /tmp/models
/tmp/models:
total 12K
drwxr-xr-x 2 root root 4.0K Mar 10 10:29 00000001
-rw-r--r-- 1 root root 7.6K Mar 10 10:29 model.log

/tmp/models/00000001:
total 72K
-rw-r--r-- 1 root root 119 Mar 10 10:29 checkpoint
-rw-r--r-- 1 root root 31K Mar 10 10:29 export.data-00000-of-00001
-rw-r--r-- 1 root root 159 Mar 10 10:29 export.index
-rw-r--r-- 1 root root 29K Mar 10 10:29 export.meta

Services

当这个 Docker 镜像启动时,会运行 "example_jupyter/setup.sh"。其实下面的服务已经启动:

  • jupyter notebook

这就是我们现在要运行的 jupyter notebook。

  • /serving/bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server

这是 TF 模型服务正在运行。它来自 TF 服务的标准分布式,使用 gRPC 协议调用模型。

  • /serving/bazel-bin/tensorflow_serving/example/flask_client

我还把这个 Flask web 应用从 REST 请求转成 gPRC 请求。这样做可能会降低运行性能,但是至少能把每一步过程搞清楚 - 你可以在下面的网页中找到代码:tensorflow_serving/example/flask_client.py.

让我们验证一下 TF 模型服务器。截至目前,服务器一直处于空闲状态,正在等待文件夹里出现一个模型。我们现在可以检查一下日志来确定服务器辨识并加载了刚刚存档的模型:

!tail -n2 /tmp/models/model.log
2017-03-10 10:29:49.461339: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: default version: 1}
2017-03-10 10:29:49.464518: I tensorflow_serving/model_servers/main.cc:257] Running ModelServer at 0.0.0.0:9000 ...

REST 请求

接下来的部分可以单独运行之前做的 - 因此你可以在不同的 notebook 运行,或者说甚至在别的主机上运行

下面是用 REST 调用我们模型的函数的示例。

import numpy as np
import cPickle as pickle
import requests

def test_flask_client(x):
    URL = "http://localhost:5000/model_prediction"

    s = pickle.dumps({"x":x}, protocol=0)

    DATA = {"model_name": "default",
          "input": requests.utils.quote(s)}

    r = requests.get(URL, data=DATA)    
    return r.json()

确保对训练数据依然有效。

%matplotlib inline
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
number = mnist.train.next_batch(1)[0]

plt.imshow(number.reshape(28,28))
Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz

<matplotlib.image.AxesImage at 0x7fa28d001050>

预测过程

最后,用 Tensorflow 服务做预测。

test_flask_client(number)
{u'outputs': {u'pred': {u'dtype': u'DT_INT64',
   u'int64Val': [u'7'],
   u'tensorShape': {u'dim': [{u'size': u'1'}]}}}}

从这里提取得到的预测值

int(test_flask_client(number)["outputs"]["pred"]["int64Val"][0])
7

完成!

就这样,你学会用 Tensorflow 服务的 Docker 容器调用 tensorflow 模型。这个模型需要 REST 或者 gRPC(端口 9000)

也容易导出 Docker 镜像,可以在任何地方执行。在你的主机终端上执行下面的命令。 docker ps

会显示你正在运行的容器的 ID,在下面的命令中用这容器 ID 替代 “xxxxx"。

docker commit XXXXXX my_name/my_model:version1

docker save my_name/my_model:version1 > my_name.my_model.version1.tar

你的(内置模型的)Docker 镜像保存在 "tar” 包里,便于移动。我认为是这不是最佳实践,但是有用。如果把这文件解压到服务器,用下面的命令执行。

docker load --input my_name.my_model.version1.tar

docker run --rm -it -p 8888:8888 -p 9000:9000 -p 5000:5000 my_name/my_model:version1

这你就在新的服务器里运行你的模型。希望这篇博文能帮助到大家。