目录

1. Hyper-v

1.1 查看Hyper-v

1.2 安装Hyper-v

2. Docker的下载与安装

2.1 Docker的下载

2.2 Docker的安装

3. 模型转换 

3.1 加载 h5 模型

3.2 h5/hdf5模型 转成 pb模型

3.3 frozen model 转成 saved model

3.4 加载pb文件

4. TensorFlow-Serving 安装

4.1 简介

4.2 拉取tensorflow serving镜像文件

4.3 从GitHub获取服务仓库

4.4 启动服务

4.5 测试

4.6 报错

5. 部署自己的模型

5.1 放置pb文件

5.2 启动服务

 5.3 进行测试

小结

参考


        最近接触到用Docker来部署机器学习的模型,因此,写一篇文章来记录自己的学习过程,也给一些刚接触Docker部署的人一些参考的经验。接下来,我讲说一下我的需求,如果你有跟我同样的需求,那么可以参看一下我的笔记。

需求:

        在windows下,将自己的机器学习模型挂载到TensorFlow Serving上,通过接口进行数据的传递和结果的接收,方便后面的部署。这是初步了解封装。

1. Hyper-v

        Hyper-V是微软的一款虚拟化产品,是微软第一个采用类似Vmware ESXi和Citrix Xen的基于hypervisor的技术。

1.1 查看Hyper-v

        打开控制面板控->程序->程序和功能,点击“启用和关闭Windows功能”,弹出窗口,查看Hyper-V是否添加。如果已经添加,那么直接跳过这一章即可。

1.2 安装Hyper-v

        首先将下面的内容复制到新建的txt中;

pushd "%~dp0"
dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.txt
for /f %%i in ('findstr /i . hyper-v.txt 2^>nul') do dism /online /norestart /add-package:"%SystemRoot%\servicing\Packages\%%i"
del hyper-v.txt
Dism /online /enable-feature /featurename:Microsoft-Hyper-V-All /LimitAccess /ALL

         再将txt更名为Hyper-V.cmd,右键管理员运行这个文件。

         再次通过控制面板控->程序->程序和功能,点击“启用和关闭Windows功能”,你会发现此时已经安装成功了。

        最后,就是要开启Hyper-v。然后重启电脑即可。

2. Docker的下载与安装

2.1 Docker的下载

        我查看了一些博客与视频,在Windows中下载什么样的Docker是众说纷纭,我直接采用最暴力、最直接的方式,去官网下载。

2.2 Docker的安装

 双击安装包,进行无脑安装。

 

               双击桌面上的Docker快捷方式,运行docker。再通过win+R开启终端命令,输入

docker version

出现下面的信息,证明安装成功。

接着下来,你自己点击软件的sign in,去注册一个账号即可。

3. 模型转换 

        在Tensorflow Serving上进行挂载之前,你需要将你训练的模型转换成 PB 文件格式,并且验证,你的PB模型是正确的。我以我保存的h5模型进行演示。

我的模型:两个输入,两个输出

我的环境:TensorFlow-gpu 1.10.1   

                   Keras 2.2.3

        其实,模型什么样都可以,通过我的代码,你们也能改成单输入单输出,后续的操作没什么不同,最主要的是环境,因为在h5转pb格式的时候,TensorFlow版本会是一个问题。你如果是TensorFlow1.X版本,可以采取我的方法接着做。如果是TensorFlow2.X,那么比TensorFlow1.X更简单,我这里只给你一个参考的案例,毕竟我的版本是TensorFlow1.X

3.1 加载 h5 模型

        首先,在运行环境中进行保存好的h5模型的读取,输入三个样本进行测试(我怕一个样本没有说服力,所以用了三个样本)。

import pandas as pd
import numpy as np
from tensorflow.keras.models import load_model

model = load_model('/home/hadoop/Model_save/4/Best_Model/Best_Model.h5')
print("模型加载完成~~")

# 如果你只是分类这一个输出,那么输出的时候,你只看result2这个变量的操作就可以了
# 两个输入x,x1是我处理后的数据格式,你们根据自己的实际修改
x_test1,x_test2 = model.predict([x,x1]) 
result = x_test1
print(result)

# 这个向量的所有元素总和为1。
print(np.sum(x_test2[0]))
# 最大的元素就是预测类别,即概率最大的类别。
result2 = []
for i in range(0,len(x_test2)):
    a = np.argmax(x_test2[i])
    result2.append(a)
print(result2)

注:记住这里的输出结果,方便和后面pb模型的输出结果进行比较。

3.2 h5/hdf5模型 转成 pb模型

1. Tensorflow1.X   h5转pb

import pandas as pd
import numpy as np
import os
import tensorflow as tf
from tensorflow.keras.models import load_model
from tensorflow.keras import backend

def h5_to_pb(h5_model, output_dir, model_name, out_prefix="output_", log_tensorboard=True):
    """.h5模型文件转换成pb模型文件
    Argument:
        h5_model: str
            .h5模型文件
        output_dir: str
            pb模型文件保存路径
        model_name: str
            pb模型文件名称
        out_prefix: str
            根据训练,需要修改
        log_tensorboard: bool
            是否生成日志文件
    Return:
        pb模型文件
    """
    if os.path.exists(output_dir) == False:
        os.mkdir(output_dir)
    out_nodes = []
    for i in range(len(h5_model.outputs)):
        out_nodes.append(out_prefix + str(i + 1))
        tf.identity(h5_model.output[i], out_prefix + str(i + 1))
    sess = backend.get_session()

    from tensorflow.python.framework import graph_util, graph_io
    # 写入pb模型文件
    init_graph = sess.graph.as_graph_def()
    main_graph = graph_util.convert_variables_to_constants(sess, init_graph, out_nodes)
    graph_io.write_graph(main_graph, output_dir, name=model_name, as_text=False)
    # 输出日志文件
    if log_tensorboard:
        from tensorflow.python.tools import import_pb_to_tensorboard
        import_pb_to_tensorboard.import_to_tensorboard(os.path.join(output_dir, model_name), output_dir)
#  .h模型文件路径参数
input_path = '/home/hadoop/Model_save/4/Best_Model/' # h5模型在那个文件夹下,输入的是路径
weight_file = 'Best_Model.h5' # h5模型名称
weight_file_path = os.path.join(input_path, weight_file)
output_graph_name = weight_file[:-3] + '.pb'

#  pb模型文件输出输出路径
output_dir = os.path.join(os.getcwd(), "/home/hadoop/Model_save/4/Best_Model/pb_model") 

backend.set_learning_phase(0) # 防止报错

#  加载模型
h5_model = load_model(weight_file_path)
h5_to_pb(h5_model, output_dir=output_dir, model_name=output_graph_name)
print("Finished")

        运行上面代码后你会发现,在你要保存的路径下,会有一个pb模型和一个tensorboard。

 2. Tensorflow2.X   h5转pb

       Tensorflow2.X 中使用save_model即可将h5转成pb

from tensorflow.keras import models
 
models.load_model('model.h5')
models.save_model(model, pb_outpath)

结果应该是1个pb文件和2个文件夹。

3.3 frozen model 转成 saved model

通过上面的方法,在TensorFlow2.X版本上,我们可以直接读取pb文件,进行预测即可。但是,在TensorFlow1.X版本上,我们将h5模型装成了pb格式,此时的文件时frozen model,如果通过TensorFlow serving启动模型的话,会报错:

Servable {name: mnist version: 1} cannot be loaded: Not found: Could not find meta graph def matching supplied tags: { serve }. To inspect available tag-sets in the SavedModel, please use the SavedModel CLI: `saved_model_cli`

因此,我们需要将frozen model转成为saved model格式。

首先,我们要通过pb文件得到输入节点和输出节点的名字。

注意:TensorFlow里面,你要让它知道输入数据是在该模型中的哪个节点输入的,要给模型这个节点的名字,它才能明白。以及你要知道输出节点名字,它才能从该节点输出结果。

其实,节点的名字是与你设计模型时,命的名一样的。如果不知道,那就运行下面这段代码。

# 新建一个py文件运行此代码即可
# 获取节点名称
import tensorflow as tf

model_name = '/home/hadoop/Model_save/4/Best_Model/pb_model/Best_Model.pb'

def create_graph():
    with tf.gfile.FastGFile(model_name, 'rb') as f:
        graph_def = tf.GraphDef()
        graph_def.ParseFromString(f.read())
        tf.import_graph_def(graph_def, name='')

create_graph()
tensor_name_list = [tensor.name for tensor in tf.get_default_graph().as_graph_def().node]
for tensor_name in tensor_name_list:
    print(tensor_name,'\n')

运行结果:

通过上面的到的输入输出节点名字,放入下面的节点中。

# 在上面print('Finshed')后面紧接下面代码即可
 
from tensorflow.python.saved_model import signature_constants
from tensorflow.python.saved_model import tag_constants

export_dir = 'F:/model/Best Model/model'
graph_pb = 'F:/model/Best Model/Best_Model.pb'

builder = tf.saved_model.builder.SavedModelBuilder(export_dir)

with tf.gfile.GFile(graph_pb, "rb") as f:
    graph_def = tf.GraphDef()
    graph_def.ParseFromString(f.read())

sigs = {}

with tf.Session(graph=tf.Graph()) as sess:
    # name="" is important to ensure we don't get spurious prefixing
    tf.import_graph_def(graph_def, name="")
    g = tf.get_default_graph()
    # 获取输入节点
    inp1 = g.get_tensor_by_name("regre_input:0")
    inp2 = g.get_tensor_by_name("class_input:0")
    # 获取输出节点
    y1 = g.get_tensor_by_name("output_1:0")
    y2 = g.get_tensor_by_name("output_2:0")

    sigs[signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY] = \
        tf.saved_model.signature_def_utils.predict_signature_def(
            inputs={"in1": inp1,"in2": inp2},outputs={"out1": y1,"out2": y2})

    builder.add_meta_graph_and_variables(sess,[tag_constants.SERVING],signature_def_map=sigs)
builder.save()

得到的结构为:

 其中,variables文件夹下可以为空文件。

3.4 加载pb文件

下面就是加载pb模型的代码了。

其实,你只需要修改

        1. saved_model.pb的文件夹路径

        2. 输入的节点名称,后面必须要有(:0)

        3. 输出的节点名称,后面必须要有(:0)

        4. sess.run()函数即可。

import pandas as pd
import numpy as np
import tensorflow as tf

with tf.Session() as sess:
    tf.saved_model.loader.load(sess, ["serve"], "F:/model/Best Model/model")
    input1 = sess.graph.get_tensor_by_name("regre_input:0")
    input2 = sess.graph.get_tensor_by_name("class_input:0")
    # # 获取输出节点
    output1 = sess.graph.get_tensor_by_name("output_1:0")
    output2 = sess.graph.get_tensor_by_name("output_2:0")
    regre_output = sess.run(output1, {input1: x, input2: x1})
    class_output = sess.run(output2, {input1: x, input2: x1})
class_output_max = []
for i in range(0,len(class_output)):
    a = np.argmax(class_output[i])
    class_output_max.append(a)
print(regre_output)
print(class_output_max)

最后,你会发现h5的结果和pb结果一致。

注意:下面的代码,也是正确的,你到上一步就可以了。

下面,给你一个运行frozen mode形式的pb文件的代码。

import pandas as pd
import numpy as np
import os
import tensorflow as tf
from tensorflow.keras.models import load_model
'''
如果你只是分类这一个输出,那么sess.run函数的使用时,你只看class_output这个变量的操作就可以了。
并且你的输入可能是一个。根据你自己的实际情况修改即可。
'''

def load_pb_model(sess, save_path):
    with tf.gfile.FastGFile(save_path + 'Best_Model.pb', 'rb') as f:
        graph_def = tf.GraphDef()
        graph_def.ParseFromString(f.read())
        sess.graph.as_default()
        tf.import_graph_def(graph_def, name='')  # 导入计算图

sess = tf.Session()
load_pb_model(sess, '/home/hadoop/Model_save/4/Best_Model/pb_model/')
# 获取计算图节点
graph = tf.get_default_graph()  # 获取计算图
# 获取输入节点
input1 = graph.get_tensor_by_name("regre_input:0")
input2 = graph.get_tensor_by_name("class_input:0")
# 获取输出节点
output1 = graph.get_tensor_by_name("output_1:0")
output2 = graph.get_tensor_by_name("output_2:0")

# 运行计算图计算结果
#新建sess,在sess中进行run操作,注意tf.Session(graph=graph)中必须要有graph=graph这个参数,否则报错。
with tf.Session(graph=graph) as sess:
    regre_output = sess.run(output1, {input1: x, input2: x1})
    class_output = sess.run(output2, {input1: x, input2: x1})
class_output_max = []
for i in range(0,len(class_output)):
    a = np.argmax(class_output[i])
    class_output_max.append(a)
print(regre_output)
print(class_output_max)

你也会得到同样的结果。

4. TensorFlow-Serving 安装

4.1 简介

        TensorFlow Serving是google开源的一个适用于部署机器学习的模型。它提供与TensorFlow模型的开箱即用型集成,可以轻松扩展以应用其他类型的模型和数据,同时保留相同的服务架构和API。主要作用如下:

        1、提供gRPC和RESTful API服务;

        2、支持热更新;

        3、支持多模型部署。

        TensorFlow提供了Docker、APT和源码编译三种安装方式,我后期要进行项目的部署,因此采用Docker安装。

        Docker Hub上存在多个版本的tensorflow serving docker镜像,除tensorflow版本不同外,存在三种镜像版本号,分别为:

  • latest:带有编译好的Tensorflow Serving的原始镜像,无法进行任何修改
  • latest-devel:devel指的是development,可开启镜像容器bash修改配置,然后使用docker commit制作新镜像
  • latest-devel-gpu:GPU版本的latest-devel

4.2 拉取tensorflow serving镜像文件

在windows下,通过win+R启动终端命令,输入下面代码,来去TensorFlow Serving镜像文件。

docker pull tensorflow/serving

 通过下面的命令查看镜像

docker images

可以看到,TensorFlow Serving镜像我们已经拉取下来了。

4.3 从GitHub获取服务仓库

在命令行中输入:

git clone https://github.com/tensorflow/serving

 就可以在命令运行时的当前目录下(本文是C:\Users\dell)找到serving这个文件夹。

4.4 启动服务

输入下面命令,启动镜像文件:

docker run -p 8501:8501 --mount type=bind,source=C:/Users/dell/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu,target=/models/half_plus_two -e MODEL_NAME=half_plus_two -t tensorflow/serving '&'

说明:

docker run:用镜像创建一个容器

-p 8501:8501 指要映射的端口,将容器8501端口映射到系统8501端口,8501是tensorflow-serving的http服务端口,用于提供RESTful服务。

--mount:表示要进行挂载。

source:模型的绝对路径,要到模型目录本版号的上一级。

target:模型挂载到docker容器中的目录

mount是将宿主机的路径source挂载到容器的target下。source的参数是存放在宿主机上的模型文件,包含一个.pb文件和一个variables文件夹。如果是部署自己的模型就需要在原模型文件夹下新建一个以数字命名的文件夹,如1,并将模.pb文件和variables文件夹放到文件夹1中。容器内部会根据绑定的路径读取模型文件。

-e:用于传递环境变量,这里是MODEL_NAME=half_plus_two,此处是模型的别名。

-t:指定挂载到的目标容器

注意:这里一定要记住MODEL_NAME的名字

运行后,得到:

 同时,在Docker desktop上Containers上,也会新增一个服务。

 注意:如果你的输入命令启动不了时,尝试去重新开启Hyper-v(见1.2章)。我是这么解决的。

4.5 测试

方法一:

还是通过Win+R,启动终端命令,输入下面命令,进行测试。

curl -XPOST http://localhost:8501/v1/models/half_plus_two:predict -d "{\"instances\":[1.0, 2.0, 5.0]}"

得到如下结果:

 其实,官方的模型是一个线性函数y=0.5x+2。

方法二:

利用pycharm进行测试,创建test.py文件

import json
import requests
 
url = 'http://localhost:8501/v1/models/half_plus_two:predict'
data = {"instances": [1.0, 2.0, 5.0]}
r = requests.post(url, json.dumps(data))
print(r)
print(r.text)
print(r.content)

运行后得到的结果如下:

4.6 报错

进行预测时,输入请求命令时,可能会报错。

报错1

"error": "Servable not found for request: Latest(half_plus_two)"

这个问题是使用grpc与tfserve通信时,MODEL_NAME与request.model_spec.name不一致问题。

request.model_spec.name就是http://localhost:8501/v1/models/half_plus_two:predict中的模型名字。

报错2

curl: (7) Failed to connect to localhost port 8501: Connection refused

因为你本地端口号不对,查看一下你运行镜像时,映射的本地端口号是什么多少。

docker run -p 8501:8501 --mount type=bind,source=C:/Users/dell/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu,target=/models/half_plus_two -e MODEL_NAME=half_plus_two -t tensorflow/serving '&'

5. 部署自己的模型

5.1 放置pb文件

        这里我们可以这么理解,由于我们要在TensorFlow Serving模型下进行部署,因此,我们需要将我们的文件放置到TensorFlow Serving仓库里。

 其中,testdata之后的文件都是我自己创建的。

5.2 启动服务

这里和上面有些不同,你需要将路径换成你自己模型的路径才可以。

docker run -p 8501:8501 --mount type=bind,source=C:/Users/dell/serving/tensorflow_serving/servables/tensorflow/testdata/my_model/NR_DenseNet,target=/models/NR_DenseNet -e MODEL_NAME=NR_DenseNet -t tensorflow/serving '&'

根据你的情况,自行修改。

运行后,得到:

运行后,和上面一致,在Docker desktop上Containers上,也会新增一个服务。

 5.3 进行测试

首先,要查看一下你的模型是否挂载起来。

curl http://localhost:8501/v1/models/NR_DenseNet

接着,通过 saved_model_cli 来看一下你的模型信息。 

saved_model_cli show -dir C:/Users/dell/serving/tensorflow_serving/servables/tensorflow/testdata/my_model/NR_DenseNet/0001 --all

这里,你要记住你的input,output,model name信息。

接下来写一个request请求。

import json
import requests

# json传输数据是不认numpy的arrary格式,必须tolist转一下
x = x.tolist()
x1 = x1.tolist()
# 进行预测
url = 'http://localhost:8501/v1/models/NR_DenseNet:predict'
data = {"signature":"serving_default", "inputs": {"in1":x, "in2":x1}}
r = requests.post(url, json.dumps(data))
output1 = json.loads(r.text)["outputs"]["out1"]
output2 = json.loads(r.text)["outputs"]["out2"]
# 最大的元素就是预测类别,即概率最大的类别。
result = []
for i in range(0,len(output2)):
    a = np.argmax(output2[i])
    result.append(a)
output2=result
print(output1)
print(output2)

输出结果:

看输出结果是否和前面运行的pb文件的结果一致,一致则说明成功。

小结

        到这里对windows下的一个部署有了一个了解,后面需要在服务器上部署可能会不一样,后面我会做Linux下的一个部署。同时感谢一下下面这些参考文献,我只是总结了一下,记录自己的学习。

参考

【1】Keras训练 hdf5模型 转成 Tensorflow pb模型

【2】H5模型转pb模型转tflite模型

【3】TensorFlow模型的保存与加载(二)——pb模式【源码】

【4】tensorflow保存模型、加载模型、修改模型

【5】Tensorflow h5转pb

【6】模型部署 利用Tensorflow Serving部署模型

【7】Tensorflow如何利用win10+docker+tensorserving部署自己的模型

【8】tensorflow2.0基础(10)——使用tensorflow-serving部署模型

【9】win10 + Docker + Tensorflow Serving部署

【10】使用docker安装tensorflow serving

【11】tensorflow-serving docker模型部署(以mnist为例)

【12】Win11 + docker + tensorflow serving 完成模型部署

【13】docker部署tensorflow serving以及模型替换

【14】Object of type ndarray is not JSON serializabe

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐