使用 TF Serving 将 TensorFlow 模型用于生产

TensorFlow模型在开发环境中经过训练和验证。 一经发布,它们需要托管在某个地方,供应用工程师和软件工程师使用,以集成到各种应用程序中。TensorFlow为此提供了高性能服务器,称为TensorFlow服务。

要在生产中提供TensorFlow模型,需要在离线训练后保存它们,然后在生产环境中恢复经过训练的模型。TensorFlow模型在保存时包含以下文件:

  • meta-graph: 元图表示图的协议缓冲区定义。元图保存在扩展名为.meta的文件中。
  • checkpoint: 检查点表示各种变量的值。检查点保存在两个文件中:一个扩展名为.index,另一个扩展名为.data-00000-of-00001。

在本文中,我们将学习各种保存和恢复模型的方法以及如何使用TF服务来提供模型。我们将使用MNIST示例来简化操作并涵盖以下主题:

  • 使用Saver类在TensorFlow中保存和恢复模型
  • 保存和恢复Keras模型
  • TensorFlow Serving
  • 安装 TF Serving
  • 保存用于 TF Serving 的模型
  • 使用 TF Serving 服务模型
  • Docker 容器中的 TF Serving
  • Kubernetes 中的 TF Serving

保存和还原 TensorFlow 的模型

您可以通过以下两种方法之一在TensorFlow中保存和恢复模型和变量:

  • 从tf.train.Saver类创建的saver对象
  • 从tf.saved_model_builder.SavedModelBuilder类创建的基于SavedModel格式的对象

让我们看看两种方法的实际应用。

您可以按照Jupyter笔记本ch-11a_Saving_and_Restoring_TF_Models中的代码进行操作。

使用保护程序类保存和恢复所有图形变量

我们进行如下步骤:

  1. 要使用saver类,首先要创建此类的对象:
saver = tf.train.Saver()
  1. 保存图形中所有变量的最简单方法是使用以下两个参数调用save()方法:“会话对象”和“磁盘上保存变量的文件的路径”:
with tf.Session() as tfs:
    …
    saver.save(tfs,"saved-models/model.ckpt")
  1. 要恢复变量,将调用restore()方法:
with tf.Session() as tfs:
    saver.restore(tfs,"saved-models/model.ckpt")
    …
  1. 让我们重新审视第1章TensorFlow 101中的示例,在简单示例中保存变量的代码如下:
# Assume Linear Model y = w * x + b
# Define model parameters
w = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
# Define model input and output
x = tf.placeholder(tf.float32)
y = w * x + b
output = 0
# create saver object
saver = tf.train.Saver()
with tf.Session() as tfs:
    initialize and print the variable y
    tfs.run(tf.global_variables_initializer())
    output = tfs.run(y,{x:[1,2,3,4]})
    saved_model_file = saver.save(tfs, 'saved-models/full-graph-save-example.ckpt')
    print('Model saved in {}'.format(saved_model_file))
    print('Values of variables w,b: {}{}'.format(w.eval(),b.eval()))
    print('output={}'.format(output))

我们得到以下输出:

Model saved in saved-models/full-graph-save-example.ckpt
Values of variables w,b: [ 0.30000001][-0.30000001]
output=[ 0. 0.30000001 0.60000002 0.90000004]
  1. 现在让我们从刚刚创建的检查点文件中恢复变量:
# Assume Linear Model y = w * x + b
# Define model parameters
w = tf.Variable([0], dtype=tf.float32)
b = tf.Variable([0], dtype=tf.float32)
# Define model input and output
x = tf.placeholder(dtype=tf.float32)
y = w * x + b
output = 0
# create saver object
saver = tf.train.Saver()
with tf.Session() as tfs:
    saved_model_file = saver.restore(tfs, 'saved-models/full-graph-save-example.ckpt')
    print('Values of variables w,b: {}{}'.format(w.eval(),b.eval()))
    output = tfs.run(y,{x:[1,2,3,4]})
    print('output={}'.format(output))

您会注意到在恢复代码中我们没有调用tf.global_variables_initializer(),因为不需要初始化变量,因为它们将从文件中恢复。我们得到以下输出,它是根据恢复的变量计算得出的:

INFO:tensorflow:Restoring parameters from saved-models/full-graph-saveexample.ckpt
Values of variables w,b: [ 0.30000001][-0.30000001]
output=[ 0. 0.30000001 0.60000002 0.90000004]

使用 saver 类保存和恢复所选变量

默认情况下,Saver() 类将所有变量保存在图形中,但您可以通过将变量列表传递给Saver 类的构造函数来选择要保存的变量:

# create saver object
saver = tf.train.Saver({'weights': w})

变量名称可以作为列表或字典传递。如果变量名称作为列表传递,则列表中的每个变量将以其自己的名称保存。变量也可以作为由键值对组成的字典传递,其中键是用于保存的名称,值是要保存的变量的名称。

以下是我们刚看到的示例的代码,但这次我们只保存w变量的权重,并将其命名为 weights:

# Saving selected variables in a graph in TensorFlow

# Assume Linear Model y = w * x + b
# Define model parameters
w = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
# Define model input and output
x = tf.placeholder(tf.float32)
y = w * x + b
output = 0
# create saver object
saver = tf.train.Saver({'weights': w})
with tf.Session() as tfs:
    # initialize and print the variable y
    tfs.run(tf.global_variables_initializer())
    output = tfs.run(y,{x:[1,2,3,4]})
    saved_model_file = saver.save(tfs, 'saved-models/weights-save-example.ckpt')
    print('Model saved in {}'.format(saved_model_file))
    print('Values of variables w,b: {}{}'.format(w.eval(),b.eval()))
    print('output={}'.format(output))

我们得到以下输出:

Model saved in saved-models/weights-save-example.ckpt
Values of variables w,b: [ 0.30000001][-0.30000001]
output=[ 0. 0.30000001 0.60000002 0.90000004]

检查点文件仅保存权重而不是偏置。现在让我们将偏置和权重初始化为零,并恢复权重。此示例的代码在此处给出:

# Restoring selected variables in a graph in TensorFlow
tf.reset_default_graph()
# Assume Linear Model y = w * x + b
# Define model parameters
w = tf.Variable([0], dtype=tf.float32)
b = tf.Variable([0], dtype=tf.float32)
# Define model input and output
x = tf.placeholder(dtype=tf.float32)
y = w * x + b
output = 0
# create saver object
saver = tf.train.Saver({'weights': w})
with tf.Session() as tfs:
    b.initializer.run()
    saved_model_file = saver.restore(tfs, 'saved-models/weights-save-example.ckpt')
    print('Values of variables w,b: {}{}'.format(w.eval(),b.eval()))
    output = tfs.run(y,{x:[1,2,3,4]})
    print('output={}'.format(output))

如您所见,这次我们必须使用b.initializer.run()初始化偏差。我们不使用tfs.run(tf.global_variables_initializer()),因为这会初始化所有变量,并且不需要初始化权重,因为它们将从检查点文件中恢复。

我们得到以下输出,因为计算仅使用恢复的权重,而偏置设置为零:

INFO:tensorflow:Restoring parameters from saved-models/weights-saveexample.ckpt
Values of variables w,b: [ 0.30000001][ 0.]
output=[ 0.30000001 0.60000002 0.90000004 1.20000005]

保存和恢复Keras模型

在Keras中,保存和恢复模型非常简单。Keras提供三种选择:

  • 使用其网络体系结构、权重(参数)、训练配置和优化程序状态保存完整模型。
  • 仅保存架构。
  • 仅保存权重。

要保存完整模型,请使用model.save(filepath)函数。这将把完整的模型保存在HDF5文件中。可以使用keras.models.load_model(filepath)函数加载保存的模型。此函数将所有内容加载回来,然后还编译模型。

要保存模型的体系结构,请使用model.to_json()或model.to_yaml()函数。这些函数返回一个可以写入磁盘文件的字符串。在恢复体系结构时,可以使用keras.models.model_from_json(json_string)或keras.models.model_from_yaml(yaml_string)函数回读字符串并恢复模型体系结构。这两个函数都返回一个模型实例。

要保存模型的权重,请使用model.save_weights(path_to_h5_file)函数。可以使用model.load_weights(path_to_h5_file)函数恢复权重。

TensorFlow Serving

TensorFlow服务(TFS)是一种高性能服务器架构,用于为生产中的机器学习模型提供服务。它提供与使用TensorFlow构建的模型的开箱即用集成。

在TFS中,模型由一个或多个可服务(Servable)组成。 servable用于执行计算,例如:

  • 用于嵌入查找的查找表
  • 单个模型返回预测
  • 一组模型返回一组预测
  • 查找表或模型的分片

管理器组件管理servable的整个生命周期,包括加载/卸载servable和为servable提供服务。

TensorFlow服务的内部架构和工作流程在以下链接中描述:

https://tensorflow.google.cn/tfx/guide/serving

https://www.tensorflow.org/tfx/guide/serving

安装 TF Serving

按照本节中的说明使用aptitude在Ubuntu上安装TensorFlow ModelServer。

安装详细过程请参考:

https://tensorflow.google.cn/tfx/serving/setup

https://www.tensorflow.org/tfx/serving/setup

这里不再赘述。

保存TF服务的模型

为了服务模型,需要先保存它们。在本节中,我们将从官方TensorFlow文档演示MNIST示例的略微修改版本,可从以下链接获得:https://tensorflow.google.cn/tfx/serving/serving_basic

https://www.tensorflow.org/tfx/serving/serving_basic

TensorFlow团队建议使用SavedModel来保存和恢复在TensorFlow中构建和训练的模型。 根据TensorFlow文档:

SavedModel是一种语言中立的,可恢复的,密集的序列化格式。 SavedModel支持更高级别的系统和工具来生成,使用和转换TensorFlow模型。

您可以按照Jupyter笔记本中的代码ch-11b_Saving_TF_Models_with_SavedModel_for_TF_Serving.

我们按照以下方式继续保存模型:

  1. 定义模型变量:
model_name = 'mnist'
model_version = '1'
model_dir = os.path.join(models_root,model_name,model_version)
  1. 像我们在《使用 TensorFlow 的经典机器学习案例》中所做的那样获取MNIST数据 :
from tensorflow.examples.tutorials.mnist import input_data
dataset_home = os.path.join('.','mnist')
mnist = input_data.read_data_sets(dataset_home, one_hot=True)
x_train = mnist.train.images
x_test = mnist.test.images
y_train = mnist.train.labels
y_test = mnist.test.labels
pixel_size = 28
num_outputs = 10 # 0-9 digits
num_inputs = 784 # total pixels
  1. 定义能构建并返回模型的MLP函数:
def mlp(x, num_inputs, num_outputs,num_layers,num_neurons):
    w=[]
    b=[]
    for i in range(num_layers):
        w.append(tf.Variable(tf.random_normal([num_inputs if i==0 else num_neurons[i-1], num_neurons[i]]),name="w_{0:04d}".format(i) ) )
        b.append(tf.Variable(tf.random_normal([num_neurons[i]]), name="b_{0:04d}".format(i) ) )
    w.append(tf.Variable(tf.random_normal([num_neurons[num_layers-1] if num_layers > 0 else num_inputs, num_outputs]),name="w_out"))
    b.append(tf.Variable(tf.random_normal([num_outputs]), name="b_out"))

    # x is input layer
    layer = x
    # add hidden layers
    for i in range(num_layers):
        layer = tf.nn.relu(tf.matmul(layer, w[i]) + b[i])
    # add output layer
    layer = tf.matmul(layer, w[num_layers]) + b[num_layers]
    model = layer
    probs = tf.nn.softmax(model)

    return model,probs

上述mlp()函数返回模型和概率。而概率是应用于模型的softmax激活函数。

  1. 为图像输入和目标输出定义x_p和y_p占位符:
# input images
serialized_tf_example = tf.placeholder(tf.string, name='tf_example')
feature_configs = {'x': tf.FixedLenFeature(shape=[784], dtype=tf.float32),}
tf_example = tf.parse_example(serialized_tf_example, feature_configs)
# use tf.identity() to assign name
x_p = tf.identity(tf_example['x'], name='x_p')
# target output
y_p = tf.placeholder(dtype=tf.float32, name="y_p", shape=[None, num_outputs])
  1. 创建模型,以及损失、优化器、准确性和训练函数:
num_layers = 2
num_neurons = []
for i in range(num_layers):
num_neurons.append(256)

learning_rate = 0.01
n_epochs = 50
batch_size = 100
n_batches = mnist.train.num_examples //batch_size

model,probs = mlp(x=x_p, num_inputs=num_inputs, num_outputs=num_outputs, num_layers=num_layers, num_neurons=num_neurons)

loss_op = tf.nn.softmax_cross_entropy_with_logits
loss = tf.reduce_mean(loss_op(logits=model, labels=y_p))
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train_op = optimizer.minimize(loss)

pred_check = tf.equal(tf.argmax(probs,1), tf.argmax(y_p,1))
accuracy_op = tf.reduce_mean(tf.cast(pred_check, tf.float32))
values, indices = tf.nn.top_k(probs, 10)

table = tf.contrib.lookup.index_to_string_table_from_tensor( tf.constant([str(i) for i in range(10)]))
prediction_classes = table.lookup(tf.to_int64(indices))
  1. 在TensorFlow会话中,像以前一样训练模型,但使用构建对象来保存模型:
from tf.saved_model.signature_constants import \
    CLASSIFY_INPUTS
from tf.saved_model.signature_constants import \
    CLASSIFY_OUTPUT_CLASSES
from tf.saved_model.signature_constants import \
    CLASSIFY_OUTPUT_SCORES
from tf.saved_model.signature_constants import \
    CLASSIFY_METHOD_NAME
from tf.saved_model.signature_constants import \
    PREDICT_METHOD_NAME
from tf.saved_model.signature_constants import \
    DEFAULT_SERVING_SIGNATURE_DEF_KEY

with tf.Session() as tfs:
    tfs.run(tf.global_variables_initializer())
    for epoch in range(n_epochs):
        epoch_loss = 0.0
        for batch in range(n_batches):
            x_batch, y_batch = mnist.train.next_batch(batch_size)
            feed_dict = {x_p: x_batch, y_p: y_batch}
            _,batch_loss = tfs.run([train_op,loss], feed_dict=feed_dict)
            epoch_loss += batch_loss
        average_loss = epoch_loss / n_batches
        print("epoch: {0:04d} loss = {1:0.6f}" .format(epoch,average_loss))
    feed_dict={x_p: x_test, y_p: y_test}
    accuracy_score = tfs.run(accuracy_op, feed_dict=feed_dict)
    print("accuracy={0:.8f}".format(accuracy_score))
 
    # save the model

    # definitions for saving the models
    builder = tf.saved_model.builder.SavedModelBuilder(model_dir)
    # build signature_def_map
    bti_op = tf.saved_model.utils.build_tensor_info
    bsd_op = tf.saved_model.utils.build_signature_def
    classification_inputs = bti_op(serialized_tf_example)
    classification_outputs_classes = bti_op(prediction_classes)
    classification_outputs_scores = bti_op(values)
    classification_signature = (bsd_op( inputs={CLASSIFY_INPUTS: classification_inputs}, outputs={CLASSIFY_OUTPUT_CLASSES: classification_outputs_classes, CLASSIFY_OUTPUT_SCORES: classification_outputs_scores }, method_name=CLASSIFY_METHOD_NAME))

    tensor_info_x = bti_op(x_p)
    tensor_info_y = bti_op(probs)

    prediction_signature = (bsd_op(inputs={'inputs': tensor_info_x}, outputs={'outputs': tensor_info_y}, method_name=PREDICT_METHOD_NAME))
    legacy_init_op = tf.group(tf.tables_initializer(), name='legacy_init_op')
    builder.add_meta_graph_and_variables(tfs, [tf.saved_model.tag_constants.SERVING], signature_def_map={ 'predict_images':prediction_signature, DEFAULT_SERVING_SIGNATURE_DEF_KEY: classification_signature, }, legacy_init_op=legacy_init_op)
    builder.save()

当你看到以下输出时,模型就保存好了:

accuracy=0.92979997
INFO:tensorflow:No assets to save.
INFO:tensorflow:No assets to write.
INFO:tensorflow:SavedModel written to:
b'/home/armando/models/mnist/1/saved_model.pb'

接下来,我们运行ModelServer并提供刚刚保存的模型。

使用 TF Serving 服务模型

要运行ModelServer,请执行以下命令:

$ tensorflow_model_server --model_name=mnist --model_base_path=/home/armando/models/mnist

服务器启动后在端口8500上提供模型:

I tensorflow_serving/model_servers/main.cc:147] Building single TensorFlow
model file config: model_name: mnist model_base_path:
/home/armando/models/mnist
I tensorflow_serving/model_servers/server_core.cc:441] Adding/updating
models.
I tensorflow_serving/model_servers/server_core.cc:492] (Re-)adding model:
mnist
I tensorflow_serving/core/basic_manager.cc:705] Successfully reserved
resources to load servable {name: mnist version: 1}
I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable
version {name: mnist version: 1}
I tensorflow_serving/core/loader_harness.cc:74] Loading servable version
{name: mnist version: 1}
I
external/org_tensorflow/tensorflow/contrib/session_bundle/bundle_shim.cc:36
0] Attempting to load native SavedModelBundle in bundle-shim from:
/home/armando/models/mnist/1
I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:236] Loading
SavedModel from: /home/armando/models/mnist/1
I
external/org_tensorflow/tensorflow/core/platform/cpu_feature_guard.cc:137]
Your CPU supports instructions that this TensorFlow binary was not compiled
to use: AVX2 FMA
I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:155]
Restoring SavedModel bundle.
I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running
LegacyInitOp on SavedModel bundle.
I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:284] Loading
SavedModel: success. Took 29853 microseconds.
I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded
servable version {name: mnist version: 1}
E1121 ev_epoll1_linux.c:1051] grpc epoll fd: 3
I tensorflow_serving/model_servers/main.cc:288] Running ModelServer at 0.0.0.0:8500 …

要通过调用模型对图像进行分类来测试服务器,请按照笔记本ch-11c_TF_Serving_MNIST进行操作。

该笔记本中的前两个单元提供了服务存储库中TensorFlow官方示例的测试客户端功能。我们修改了示例以发送’input’并在函数签名中接收’output’以调用ModelServer。

使用以下代码调用笔记本的第三个单元格中的测试客户端函数:

error_rate = do_inference(hostport='0.0.0.0:8500',work_dir='/home/armando/datasets/mnist',concurrency=1,num_tests=100)
print('\nInference error rate: %s%%' % (error_rate * 100))

我们得到差不多7%的错误率! (您可能会得到不同的值):

Extracting /home/armando/datasets/mnist/train-images-idx3-ubyte.gz
Extracting /home/armando/datasets/mnist/train-labels-idx1-ubyte.gz
Extracting /home/armando/datasets/mnist/t10k-images-idx3-ubyte.gz
Extracting /home/armando/datasets/mnist/t10k-labels-idx1-ubyte.gz
…………………………………………..
…………………………………………..
Inference error rate: 7.0%

Docker 容器中的 TF Serving

Docker是一个用于在容器中打包和部署应用程序的平台。如果您还不知道Docker容器,请访问以下链接中的教程和信息: https://www.docker.com/resources/what-container

我们还可以在Docker容器中安装和运行TensorFlow服务。本节中提供的Ubuntu 18.04的说明源自TensorFlow官方网站上的链接:

让我们一起开始吧!

安装 Docker

请参考官方文档:

https://docs.docker.com/install/linux/docker-ce/ubuntu/

本文不再赘述。

为TF Serving构建Docker镜像

我们继续使用Docker镜像进行如下操作:

  1. 使用以下内容创建名为dockerfile的文件:
FROM ubuntu:16.04
MAINTAINER Armando Fandango armando@geekysalsero.com
RUN apt-get update && apt-get install -y \
 build-essential \
 curl \
 git \
 libfreetype6-dev \
 libpng12-dev \
 libzmq3-dev \
 mlocate \
 pkg-config \
 python-dev \
 python-numpy \
 python-pip \
 software-properties-common \
 swig \
 zip \
 zlib1g-dev \
 libcurl3-dev \
 openjdk-8-jdk\
 openjdk-8-jre-headless \
 wget \
 && \
 apt-get clean && \
 rm -rf /var/lib/apt/lists/*
RUN echo "deb [arch=amd64]
 http://storage.googleapis.com/tensorflow-serving-apt stable
 tensorflow-model-server tensorflow-model-server-universal" \
 | tee /etc/apt/sources.list.d/tensorflow-serving.list
RUN curl
 https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-se
 rving.release.pub.gpg \
 | apt-key add -
RUN apt-get update && apt-get install -y \
 tensorflow-model-server
RUN pip install --upgrade pip
RUN pip install mock grpcio tensorflow tensorflow-serving-api
CMD ["/bin/bash"]
  1. 运行以下命令以从此dockerfile构建Docker镜像:
$ docker build --pull -t $USER/tensorflow_serving -f dockerfile .
  1. 创建镜像需要一段时间。当您看到类似于以下内容的内容时,您就会知道镜像已构建完成:
Removing intermediate container 1d8e757d96e0
Successfully built 0f95ddba4362
Successfully tagged armando/tensorflow_serving:latest
  1. 运行以下命令以启动容器:
$ docker run --name=mnist_container -it $USER/tensorflow_serving
  1. 当您看到以下提示时,您已登录到容器:
root@244ea14efb8f:/#
  1. 使用cd命令转到home文件夹。
  2. 在主文件夹中,使用以下命令以检查TensorFlow是否正在提供代码。我们将使用此代码中的示例来演示,但您可以查看自己的Git存储库来运行您自己的模型:
$ git clone --recurse-submodules https://github.com/tensorflow/serving

克隆存储库后,我们就可以构建、训练和保存MNIST模型了。

  1. 使用以下命令删除临时文件夹(如果尚未删除):
$ rm -rf /tmp/mnist_model
  1. 运行以下命令以构建、训练和保存MNIST模型。
$ python serving/tensorflow_serving/example/mnist_saved_model.py /tmp/mnist_model

您将看到类似于以下内容的内容:

Training model…
Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Extracting /tmp/train-images-idx3-ubyte.gz
Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Extracting /tmp/train-labels-idx1-ubyte.gz
Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Extracting /tmp/t10k-images-idx3-ubyte.gz
Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting /tmp/t10k-labels-idx1-ubyte.gz
2017-11-22 01:09:38.165391: I
tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU
supports instructions that this TensorFlow binary was not compiled
to use: SSE4.1 SSE4.2 AVX AVX2 FMA
training accuracy 0.9092
Done training!
Exporting trained model to /tmp/mnist_model/1
Done exporting!
  1. 按Ctrl + P和Ctrl + Q键从Docker镜像中分离。
  2. 提交对新映像的更改并使用以下命令停止容器:
$ docker commit mnist_container $USER/mnist_serving
$ docker stop mnist_container
  1. 现在,您可以通过提供以下命令随时运行此容器:
$ docker run --name=mnist_container -it $USER/mnist_serving
  1. 删除我们为保存图像而构建的临时MNIST容器:
$ docker rm mnist_container

在Docker容器中提供模型

要在容器中提供模型,说明如下:

  1. 启动上一节中构建的MNIST容器:
$ docker run --name=mnist_container -it $USER/mnist_serving
  1. 使用cd命令转到home文件夹。
  2. 使用以下命令运行ModelServer:
$ tensorflow_model_server --model_name=mnist -- model_base_path=/tmp/mnist_model/ &> mnist_log &
  1. 使用示例客户端检查模型中的预测:
$ python serving/tensorflow_serving/example/mnist_client.py -- num_tests=100 --server=localhost:8500
  1. 我们看到错误率为7%,就像我们之前的笔记本示例执行一样:
Extracting /tmp/train-images-idx3-ubyte.gz
Extracting /tmp/train-labels-idx1-ubyte.gz
Extracting /tmp/t10k-images-idx3-ubyte.gz
Extracting /tmp/t10k-labels-idx1-ubyte.gz
………………………………………………………….
……………………………
Inference error rate: 7.0%

成了!您已经构建了Docker镜像,并在Docker镜像中为您的模型提供服务。输入exit命令退出容器。

运行在 Kubernetes 中的 TensorFlow Serving

根据 https://kubernetes.io

Kubernetes是一个开源系统,用于自动化容器化应用程序的部署,扩展和管理。

TensorFlow模型可以根据生产环境中使用Kubernetes集群的数百或数千个TF Serving服务进行扩展。Kubernetes集群可以在所有流行的公共云上运行,例如GCP,AWS,Azure,以及您的onpremises私有云。因此,让我们直接学习安装Kubernetes,然后在Kubernetes Cluster上部署MNIST模型。

安装 Kubernetes

  1. 安装LXD和Docker,这是在本地安装Kubernetes的先决条件。LXD是与Linux容器一起使用的容器管理器。我们已经在上一节中学习了如何安装Docker。 要安装LXD,请运行以下命令:
$ sudo snap install lxd
lxd 2.19 from 'canonical' installed
  1. 初始化lxd并创建虚拟网络:
$ sudo /snap/bin/lxd init --auto
LXD has been successfully configured.

$ sudo /snap/bin/lxc network create lxdbr0 ipv4.address=auto
ipv4.nat=true ipv6.address=none ipv6.nat=false
If this is your first time using LXD, you should also run: lxd init
 To start your first container, try: lxc launch ubuntu:16.04

Network lxdbr0 created
  1. 将您的用户添加到lxd组:
$ sudo usermod -a -G lxd $(whoami)
  1. 安装 conjure-up 并重启机器:
$ sudo snap install conjure-up --classic
conjure-up 2.3.1 from 'canonical' installed
  1. 启动 conjure-up 以安装Kubernetes:
$ conjure-up kubernetes
  1. 从可选列表中选择Kubernetes Core
  2. 从可用云列表中选择localhost
  3. 从网络列表中选择lxbr0 bridge
  4. 提供该选项的sudo密码:Download the kubectl and kubefed client programs to your local host
  5. 在下一个屏幕中,它会要求选择要安装的应用程序。安装剩下的所有五个应用程序。

安装后当你看到最终屏幕如下所示时,Kubernetes集群已准备好运行了:

如果您在安装时遇到问题,请在互联网上搜索帮助,从以下链接的文档开始:

https://kubernetes.io/docs/getting-started-guides/ubuntu/

https://tutorials.ubuntu.com/tutorial/install-kubernetes-with-conjure-up

将Docker镜像上传到dockerhub

将Docker镜像上传到dockerhub的步骤如下:

  1. 如果您还没有,请在dockerhub上创建一个帐户。
  2. 使用以下命令登录dockerhub帐户:
$ docker login --username=<username>
  1. 使用您在dockerhub上创建的仓库标记MNIST图像。例如,我们创建了vistart/mnist-serving:
$ docker tag $USER/mnist_serving vistart/mnist-serving
  1. 将标记的图像推送到dockerhub帐户。
docker push neurasights/mnist-serving

在Kubernetes中部署

我们继续在Kubernotes中进行部署,如下所示:

  1. 使用以下内容创建mnist.yaml文件:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: mnist-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: mnist-server
    spec:
      containers:
      - name: mnist-container
        image: neurasights/mnist-serving
        command:
        - /bin/sh
        args:
        - -c
        - tensorflow_model_server --model_name=mnist -- model_base_path=/tmp/mnist_model
        ports: 
        - containerPort: 8500
---
apiVersion: v1
kind: Service
metadata:
  labels:
    run: mnist-service
  name: mnist-service
spec:
  ports:
  - port: 8500
    targetPort: 8500
  selector:
    app: mnist-server 
# type: LoadBalancer

如果您在AWS或GCP云中运行它,则取消注释前一个文件中的LoadBalancer行。由于我们在单个节点上本地运行整个集群,因此我们没有外部LoadBalancer。

  1. 创建Kubernetes部署和服务:
$ kubectl create -f mnist.yaml
deployment "mnist-deployment" created
service "mnist-service" created
  1. 检查部署,窗格和服务:
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
mnist-deployment 3 3 3 0 1m

$ kubectl get pods
NAME READY STATUS
RESTARTS AGE
default-http-backend-bbchw 1/1 Running 3
9d
mnist-deployment-554f4b674b-pwk8z 0/1 ContainerCreating 0
1m
mnist-deployment-554f4b674b-vn6sd 0/1 ContainerCreating 0
1m
mnist-deployment-554f4b674b-zt4xt 0/1 ContainerCreating 0
1m
nginx-ingress-controller-724n5 1/1 Running 2
9d

$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP
PORT(S) AGE
default-http-backend ClusterIP 10.152.183.223 
80/TCP 9d
kubernetes ClusterIP 10.152.183.1 
443/TCP 9d
mnist-service LoadBalancer 10.152.183.66 
8500:32414/TCP 1m

$ kubectl describe service mnist-service
Name: mnist-service
Namespace: default
Labels: run=mnist-service
Annotations: 
Selector: app=mnist-server
Type: LoadBalancer
IP: 10.152.183.66
Port:  8500/TCP
TargetPort: 8500/TCP
NodePort:  32414/TCP
Endpoints:
0.1.43.122:8500,10.1.43.123:8500,10.1.43.124:8500
Session Affinity: None
External Traffic Policy: Cluster
Events: 
  1. 等到所有pod的都处在运行状态:
$ kubectl get pods
NAME READY STATUS RESTARTS
AGE
default-http-backend-bbchw 1/1 Running 3
9d
mnist-deployment-554f4b674b-pwk8z 1/1 Running 0
3m
mnist-deployment-554f4b674b-vn6sd 1/1 Running 0
3m
mnist-deployment-554f4b674b-zt4xt 1/1 Running 0
3m
nginx-ingress-controller-724n5 1/1 Running 2
9d
  1. 检查其中一个pod的日志,您应该看到如下内容:
$ kubectl logs mnist-deployment-59dfc5df64-g7prf
 I tensorflow_serving/model_servers/main.cc:147] Building single
 TensorFlow model file config: model_name: mnist model_base_path:
 /tmp/mnist_model
 I tensorflow_serving/model_servers/server_core.cc:441]
 Adding/updating models.
 I tensorflow_serving/model_servers/server_core.cc:492] (Re-)adding
 model: mnist
 I tensorflow_serving/core/basic_manager.cc:705] Successfully
 reserved resources to load servable {name: mnist version: 1}
 I tensorflow_serving/core/loader_harness.cc:66] Approving load for
 servable version {name: mnist version: 1}
 I tensorflow_serving/core/loader_harness.cc:74] Loading servable
 version {name: mnist version: 1}
 I
 external/org_tensorflow/tensorflow/contrib/session_bundle/bundle_sh
 im.cc:360] Attempting to load native SavedModelBundle in bundleshim
 from: /tmp/mnist_model/1
 I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:236]
 Loading SavedModel from: /tmp/mnist_model/1
 I
 external/org_tensorflow/tensorflow/core/platform/cpu_feature_guard.
 cc:137] Your CPU supports instructions that this TensorFlow binary
 was not compiled to use: AVX2 FMA
 I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:155]
 Restoring SavedModel bundle.
 I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190]
 Running LegacyInitOp on SavedModel bundle.
 I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:284]
 Loading SavedModel: success. Took 45319 microseconds.
 I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded
 servable version {name: mnist version: 1}
 E1122 12:18:04.566415410 6 ev_epoll1_linux.c:1051] grpc epoll fd: 3
 I tensorflow_serving/model_servers/main.cc:288] Running ModelServer
 at 0.0.0.0:8500 …
  1. 您还可以使用以下命令查看UI控制台:
$ kubectl proxy xdg-open http://localhost:8001/ui

Kubernetes UI控制台如下图所示:

由于我们在单个节点上本地运行集群,因此我们的服务仅在集群中公开,无法从外部访问。登录我们刚刚实例化的三个pod中的一个:

$ kubectl exec -it mnist-deployment-59dfc5df64-bb24q -- /bin/bash

切换到主目录并运行MNIST客户端来测试服务:

$ kubectl exec -it mnist-deployment-59dfc5df64-bb24q --/bin/bash
root@mnist-deployment-59dfc5df64-bb24q:/# cd
root@mnist-deployment-59dfc5df64-bb24q:~# python
serving/tensorflow_serving/example/mnist_client.py --num_tests=100 --
server=10.152.183.67:8500
Extracting /tmp/train-images-idx3-ubyte.gz
Extracting /tmp/train-labels-idx1-ubyte.gz
Extracting /tmp/t10k-images-idx3-ubyte.gz
Extracting /tmp/t10k-labels-idx1-ubyte.gz
…………………………………………………………………
…………………….
Inference error rate: 7.0%
root@mnist-deployment-59dfc5df64-bb24q:~#

我们学习了如何在本地单个节点上运行的Kubernetes集群上部署TensorFlow服务。您可以使用相同的概念知识在您的场所内的公共云或私有云上部署服务。

总结

在本文中,我们学习了如何利用TensorFlow服务来为生产环境中的模型提供服务。我们还学习了如何使用TensorFlow和Keras保存和恢复完整模型或选择模型。我们构建了一个Docker容器,并从官方TensorFlow服务存储库中提供了Docker容器中的示例MNIST示例代码。我们还安装了一个本地Kubernetes集群,并部署了MNIST模型,用于在Kubernetes pod中运行的TensorFlow服务。我们鼓励读者在这些例子的基础上进行尝试并尝试提供不同的模型。TF服务文档描述了各种选项,并提供了其他信息,使您可以进一步探索此主题。

在接下来的文章中,我们将继续使用转移学习的高级模型。TensorFlow存储库中提供的预训练模型是使用TF服务练习服务TF模型的最佳候选者。我们使用Ubuntu软件包安装了TF Serving,但您可能希望尝试从源代码构建以优化生产环境的TF服务安装。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据