map中key值带‘-’转json_h5模型转savedModel + tf_serving部署采坑记录

背景介绍:

使用keras_bert训练了模型,采用三种方式分别保存了模型结构、模型权重和完整模型,其中模型结构为json格式,模型权重和完整模型都是h5格式。tensorflow = 1.14,2.0以后貌似更加简单了

保存代码:

2d3af30fb8851f274c55581cd402e16a.png

现在的需求是将h5模型转为SavedModel格式,并通过tf_serving部署。

Step1:格式转换

1.1 加载h5模型

参考:

keras模型保存和加载_fu6543210的博客-CSDN博客​blog.csdn.net
7fb81c2a9f044d1d919a2b1a8b731eb6.png

注意:使用原生keras,tf.keras出现意想不到的错误。

1.1.1 json、load_weights方式

除了json以外,还有yaml方法。这种保存的都是模型结构,无法直接进行inference,需要加载权重。API:keras.model.model_from_config,model.load_weights()。没有使用这种方法,就不过多展开。

1.1.2 完整模型加载

API:model = keras.models.load_model(‘模型路径’)

坑1:使用上述代码直接加载模型的时候会出现:ValueError: Unknown layer: TokenEmbedding

46842755eb89b1ddd56c98d70208b4b5.png

原因:keras_bert这个包的需求

解决:

from keras_bert import get_custom_objects
model = keras.models.load_model('model.h5', custom_objects = get_custom_objects())

如果还是出现ValueError: Unknown layer: xxx,很可能是因为你用了tf.keras

1.2 转为SavedModel

1.2.1 创建bulider、回复计算图

参考:

https://zhuanlan.zhihu.com/p/81999483​zhuanlan.zhihu.com
zhihu-card-default.svg

上面的链接有一些api使用会出错,我的代码如下

import keras.backend as K

K.set_learning_phase(0)

export_path = '../models_pb'

builder = tf.saved_model.Builder(export_path)
signature_def_map = {
            "predict": tf.saved_model.build_signature_def(
                # 根据自己模型的要求
                inputs = {"input0": tf.saved_model.build_tensor_info(model.input[0]),
                          "input1": tf.saved_model.build_tensor_info(model.input[1])},
                outputs = {"output": tf.saved_model.build_tensor_info(model.output)},
                method_name = tf.saved_model.signature_constants.PREDICT_METHOD_NAME
        )}
builder.add_meta_graph_and_variables(keras.backend.get_session(),  # 注意4
                                     [tf.saved_model.tag_constants.SERVING],
                                     signature_def_map=signature_def_map,
                                     # 初始化操作,我的不需要,否则报错
                                     # legacy_init_op=tf.group(tf.tables_initializer(), name='legacy_init_op')
                                    )
builder.save()

一些注释及注意点(其实就是坑!!!):

  • K.set_learning_phase(0)主要针对dropout、BN层训练测试不一致
  • inputs中的key暂时不重要,values需要根据模型实际调整,比如上面截图中的模型的输入是[x1, x2]。查看的话model.inputs()
  • method_name:保存模型为了预测貌似就PREDICT_METHOD_NAME,也有TRAINING等,我还没看
  • builder.add_meta_graph_and_variables获取session的时候,不能使用直接使用tf.keras.backend 可能出现:FailedPreconditionError (see above for traceback): Attempting to use uninitialized value Variable[[Node: Variable/read = _MklIdentity[T=DT_FLOAT, _kernel="MklOp", _device="/job:localhost/replica:0/task:0/device:CPU:0"](Variable, DMT/_0)]]

补充知识(大佬请忽略)

1、查看模型输入输出,以及signature_name(后面用得到)

linux 命令:saved_model_cli 查看的话一般用show

a1807418d7458a8dee2e6eb5dd3a1a07.png

注:红圈中的name就是tensor_name,可以使用get_tensor_by_name获取张量。这样方法就不会忘记加‘:0’,导致报错

2、回复计算图时可以使用tensorboard查看

我没用哈哈,直接copy别人的代码图把

ac5b231c0acc29afc97a672b5c763612.png

Step2:tf_serving部署

这一段我是直接通过Docker部署,通过上一步保存的结果,我们可以得到如下模型

f06dce57938de908f3a1813f130a0ba7.png

部署的话比较简单,也没出粗,不谈

TensorFlow与Docker服务 | TFX​tensorflow.google.cn
29df45a5c17b290c2b581fbc71816b25.png

Step3:发送请求

没想到在这里出现了坑,我们的post请求格式如下(一下都是一些别人的代码)

1、通过python request

f4ac8d637be78857ded0d2ca2fa78f19.png

2、或者通过下面的命令

d71e53cb2f45fbc9f8c51a2b1cc1e7ef.png

举例:

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])

总则:“instances”的值是一个列表!列表!列表!,列表中每个元素是一个样本(更确切地说应该是一组完整的输入,即一个样本inputs),每个样本可能会有多个输入,比如我的模型中有input0、input1,每个样本用字典表示,每个输入是一个键值对。

坑1:json格式坑

TypeError: Object of type 'ndarray' is not JSON serializable

原因:样本需要的是list格式,我们给了ndarray

76e2d8409f93306bcf267c3a0ffb0dfa.png

解决:np.ndarray.tolist()

坑2:post请求内容坑

{ "error": "Serving signature name: "serving_default" not found in signature def" }

原因:没有传入signature_name

解决:一般只用inference就是“predict”,也可以通过上面的linux 命令查看。

data = {
    "signature_name": "predict",
    "instances": [{
        "input0": x1.tolist(),
        "input1": x2.tolist()
    }]
}

坑3:终于解决前两个坑了,然后输入格式也有坑。当然这个坑是因为自己的模型输入比较特别,是两个列表用'[]'括了起来,见总则

{ "error": "2 root error(s) found.n (0) Invalid argument: Expected multiples argument to be a vector of length 4 but got length 3nt [[{{node model_3_1/model_2/Encoder-1-MultiHeadSelfAttention/Tile_1}}]]nt [[dense_1_1/concat/_1681]]n (1) Invalid argument: Expected multiples argument to be a vector of length 4 but got length 3nt [[{{node model_3_1/model_2/Encoder-1-MultiHeadSelfAttention/Tile_1}}]]n0 successful operations.n0 derived errors ignored." }

4affef28a2a54d62ccea3590f163c9ea.png

原因:我的x1本身的shape是(2, 28),即2个样本。所以不能放在一个key中TensorFlow Serving 原因:我的x1本身的shape是(2, 28),即2个样本。所以不能放在一个key中

解决:每个样本生成一个dict

# 多个样本输入

assert len(x1) == len(x2)
inputs_dict = [{"input0": x1[i].tolist(), 
                "input1": x2[i].tolist()} for i in range(len(x1))]

打完收工,欢迎交流,一起踩坑!(踩了两天坑,写出来感觉也没啥,哈哈,心疼我的头发)

4b5240437426960e6995ac8ce152578d.png

特别鸣谢:

载入h5模型:

Keras load_model raise ValueError: Unknown layer: TokenEmbedding问题​blog.csdn.net
7fb81c2a9f044d1d919a2b1a8b731eb6.png
使用keras调用load_model时报错ValueError: Unknown Layer:LayerName​blog.csdn.net
1e917506c75fbd6b602cf559db7c6ca1.png
用keras调用load_model时报错ValueError: Unknown Layer:LayerName​blog.csdn.net
172276cc0a72af6e6e52480f7654ef9d.png

恢复计算图:

导出pb模型和导入pb模型_高円樹一-CSDN博客​blog.csdn.net
7fb81c2a9f044d1d919a2b1a8b731eb6.png
读取pb文件恢复模型_kakak_的博客-CSDN博客​blog.csdn.net
7fb81c2a9f044d1d919a2b1a8b731eb6.png
读取pb模型进行预测_macunshi的专栏-CSDN博客​blog.csdn.net
7fb81c2a9f044d1d919a2b1a8b731eb6.png
To load pb file : DecodeError: Error parsing message​stackoverflow.com
c619fbc630be69b87d4c8d5bfeca9cc9.png

格式转换:

https://zhuanlan.zhihu.com/p/81999483​zhuanlan.zhihu.com
zhihu-card-default.svg
记录:tf.saved_model 模块的简单使用(TensorFlow 模型存储与恢复)​www.cnblogs.com h5模型文件转换成pb模型文件 - chenzhen0530 - 博客园​www.cnblogs.com h5转pb的两个坑 - 牧马人夏峥 - 博客园​www.cnblogs.com

查看模型结构:

tf_serving-模型训练、导出、部署(解析)_永远飞翔的鸟-CSDN博客​blog.csdn.net
7fb81c2a9f044d1d919a2b1a8b731eb6.png
如何查看Tensorflow SavedModel格式模型的信息​blog.csdn.net
4a71707857f01ab2718b443819fdd3a6.png

tensorboard查看:

查看.pb文件的结构_lishanlu136的博客-CSDN博客​blog.csdn.net
1e917506c75fbd6b602cf559db7c6ca1.png
【AI小疑问】如何加载PB文件并推理预测 - nowbug - 博客园​www.cnblogs.com
60b19622ab73f2c351c0043014124615.png
如何查看Tensorflow SavedModel格式模型的信息​blog.csdn.net
4a71707857f01ab2718b443819fdd3a6.png

post请求:

TensorFlow Serving​tf.wiki
0050996c69b981dc44bc1aa1f23e2a24.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值