TF之saved-model踩坑,多次保存模型必看

7 篇文章 0 订阅
3 篇文章 0 订阅

目录

前言

正文

总结

前言


近期使用了Tensorflow的saved_model模块,踩过一些坑,总结分享一下。

参考了很多资料,部分代码也是照搬,事先说明一下,谢谢各位大佬分享的资料。

时间长的同学可以从头看,时间短的同学可以跳转文末看总结。

正文


Saved_model模块用于保存加载模型,一般配合TensorFlow Serving使用。TF Serving是一个将训练好的模型部署至生产环境的系统,主要的优点在于可以保持Server端与API不变的情况下,

部署新的算法或进行试验,同时还有很高的性能。常用的TF模型保存方法还有saver()方法,使用checkpoint文件加载恢复模型;xx.pb文件保存方法,使用tf.graph_util模块保存模型,tf.gfile模块加载模型。

 saved_model使用教程很多,这里主要说我在工程化过程中遇到的问题和解决方法。

保存模型的目录问题

with tf.Session() as sess:

    builder = tf.saved_model.builder.SavedModelBuilder(pb_file_path+'savemodel')
        # 构造模型保存的内容,指定要保存的 session,特定的 tag, 
    builder.add_meta_graph_and_variables(sess, ['cpu_server_1'])  

    builder.save()

这波操作会先在指定路径下建立savemodel目录,之后在该目录下保存模型。

注意点:

  • 如果是拼接的目录,注意pb_file_path后面有‘/’,如'/a' + 'savemodel'不行,'/a/' + 'savemodel'可以。
  • 该目录不能已存在,当模型目录已存在,无论是否为空,都会报错。这也意味着不能在同一个目录下保存多次模型。 

说到保存模型,总少不了这也的场景:比如,模型训练5000步,我想每500步保存一次模型。之前使用过tf.train.saver(),天真的以为建立一个builder,多次调用builder.save()就行了,

实际上调用很多次builder.save()倒是没报错,但这不意味着达到预期效果(多次保存有多个模型)。

多次调用builder.save(),模型目录下并没有多个模型。

正常情况下,在savemodel目录下会看到:

savemodel/

    --variables    # 这里不止一个variables.data-xxxxx-of-xxxxn,好多示例代码可能太简单,里面只有variables.data-00000-of-00001

        variables.data-00000-of-00002

variables.data-00001-of-00002

variables.index

    save_model.pb

 接下来介绍本文重点 -- 多次保存产生多个模型。

先说工程化中遇到的情况:模型训练正常,模型保存“正常”(至少结构完整,savemodel目录下有一个save_model.pb文件和一个variables子目录)。模型推理失败。

详细情况:这是一个二分类模型,训练过程模型loss稳步下降,验证集loss正常下降,acc,f1值等逐步提升,推理过程,打分始终徘徊在0.5附近。0.5=1/2,就是随机猜嘛。

分析:模型训练应该没问题,或者模型保存有问题,比如没有保存训练后的计算图和参数;或者模型加载有问题,没把保存的有效计算图和参数加载到session。

模型保存排查

  1-1  builder = tf.saved_model.builder.SavedModelBuilder(pb_file_path+'savemodel')
  1-2  builder.add_meta_graph_and_variables(sess, ['cpu_server_1'])  

   2 builder.save()  # 只执行写入,将builder中的信息(对我们来说就算模型啦)序列化写入磁盘

上述3行代码分开写了,1-1和1-2写在一起,在模型构建过程中,2写在模型保存过程。

这导致只在模型构建最初,将计算图和会话保存到builder里面,后面模型训练,参数更新都没加到builder里面。。。

多次调用 builder.save() 保存了寂寞,只是不断将初始计算图和会话写进去而已

后来想把1-2和2写到一块,都放在模型保存部分,报错。

    第一次

    builder.add_meta_graph_and_variables(sess, ['cpu_server_1'])

    builder.save()

    第二次,还特意给sess换个名字
    builder.add_meta_graph_and_variables(sess, ['cpu_server_2'])

    builder.save()

    报错:

  AssertionError: Graph state including variables and assets has already been saved. Please invoke `add_meta_graph()` instead.

  就是builder.add_meta_graph_and_variables()只能在保存模型时调用一次,且头一次调用,就是必须在builder.add_meta_graph()之前。

  后面只能使用builder.add_meta_graph()。

  于是第二次换成下面这样
    builder.add_meta_graph(sess, ['cpu_server_2'])

    builder.save()

    报错:

TypeError: list indices must be integers or slices, not str

    具体没弄明白,大意这个session已经有图,后面再加东西需以前图为索引,可以用如下方式。

    于是第二次换成下面这样

    builder.add_meta_graph(sess, [0])

    builder.save()

    报错:

AttributeError: 'int' object has no attribute 'inputs'

这个坑无限深,确实没有解决思路,期待知情者在评论区解惑。

换个方式处理

    第一次

    builder = tf.saved_model.builder.SavedModelBuilder(pb_file_path+'savemodel')

    builder.add_meta_graph_and_variables(sess, ['cpu_server_1'])

    builder.save()

    第二次,还特意给sess换个名字

    builder2 = tf.saved_model.builder.SavedModelBuilder(pb_file_path+'savemodel2')
    builder2.add_meta_graph_and_variables(sess, ['cpu_server_2'])

    builder2.save()

建立目录,存放计算图和会话,写入磁盘,这三步作为一个完整的模型保存步骤,每次参数更新后保存模型就执行这三步,走完一套,就没错。

这样最终会保存多个模型,加载时需指明模型存储路径,即savemodel还是savemodel2或是其他路径。

如果一共训练1500steps,每500steps保存一下,假设保存在根目录下的models目录下,会看到

/models/

--savemodel

--savemodel2

--savemodel3

 

 

很多情况下我们不想保存这么多模型,只保留一个,比如最好的那个,可以借助一个小技巧,示例如下:

import shutil
import tensorflow as tf

def save(sess, model_name):
        model_path = r'./models/' + model_name
        # 判断之前是否保存过该模型,保存了就删掉目录
        if os.path.exists(model_path):
            shutil.rmtree(model_path)

        # 重新建立模型目录并保存
        builder = tf.saved_model.builder.SavedModelBuilder(model_path)
        builder.add_meta_graph_and_variables(self.sess, ['cpu_server'])
        builder.save()

接下来继续说工程化遇到的问题,先说现象:模型训练正常,模型保存正常,模型推理失败,依然是模型打分徘徊在0.5附近。

分析:模型训练和保存应该是没问题的,很可能模型载入有问题。

模型载入排查

import tensorflow as tf

with tf.Session(graph=tf.Graph()) as sess:

tf.saved_model.loader.load(self.sess, ['cpu_server'], model_path )

sess.run(tf.global_variables_initializer())  # 问题在这

 问题就出在那个初始化,这一下把载入的有效计算图给还原为初始状态了,白训练了。这一个命令可以不用,也可以用在load模型之前,反正load之前也是默认计算图,

初始化前后没有影响,后面还会载入模型的。

在排查问题的过程中还有一个意外发现,这是在练习代码中发现的。基本上开发时我都会在项目外,单独建立练习文件或项目,有些不懂的先在练习部分测试,

弄清楚了再写到工作项目中。结合代码说问题:

import tensorflow as tf
import os
from tensorflow.python.framework import graph_util
from tensorflow.python.saved_model import tag_constants
 
pb_file_path = os.getcwd() + '/'
print('pb_path', pb_file_path)
 
with tf.Session(graph=tf.Graph()) as sess:
    x = tf.placeholder(tf.int32, name='x')
    y = tf.placeholder(tf.int32, name='y')
    b = tf.Variable(1, name='b')
    xy = tf.multiply(x, y)
    # 这里的输出需要加上name属性
    op = tf.add(xy, b, name='op_to_store')
 
    sess.run(tf.global_variables_initializer())
 
    graph_org = sess.graph_def
    print('graph', type(sess.graph_def))

    # 测试 OP
    feed_dict = {x: 10, y: 3}
    print(sess.run(op, feed_dict))
 

    # INFO:tensorflow:Froze 1 variables.
    # Converted 1 variables to const ops.
    # 31
    
      
    builder = tf.saved_model.builder.SavedModelBuilder(pb_file_path+'savemodel')
        # 构造模型保存的内容,指定要保存的 session,特定的 tag, 
    builder.add_meta_graph_and_variables(sess, ['cpu_server_1'])
    builder.save()  # 保存 PB 模型
    
    b = tf.assign(b, 2)
    print('b', sess.run(b))  # 注意这里,不执行b,赋值不会生效
    print('op', sess.run(op, feed_dict))
    graph2 = sess.graph_def
    if graph is graph2:
        print('IS')
    elif graph == graph2:
        print('Equal')
    else:
        print('Other')
    print('graph2', type(sess.graph_def))

    # 添加第二个 MetaGraphDef 

    builder2 = tf.saved_model.builder.SavedModelBuilder(pb_file_path+'savemodel2')                     
    builder2.add_meta_graph_and_variables(sess, ['cpu_server_2']) 
    builder2.save()


上述代码,如果不执行sess.run(b),b的赋值不会生效,即没有从1变成2.

执行完上述代码可以注意到graph和graph2并不一样,这意味着在计算图中进行的操作,增减改节点,会变更计算图状态。训练模型就是更新参数,图的状态也会变化。

因此,想保存有效计算图还得在反向传播更新模型之后,一般在batch训练之后再保存模型。

载入运行代码如下

# 只需要指定要恢复模型的 session,模型的 tag,模型的保存路径即可,使用起来更加简单
with tf.Graph().as_default():
    with tf.Session() as sess:  # graph=tf.Graph()
        tf.saved_model.loader.load(sess, ['cpu_server_2'], pb_file_path+'savemodel2')
        # sess.run(tf.global_variables_initializer())

        input_x = sess.graph.get_tensor_by_name('x:0')
        input_y = sess.graph.get_tensor_by_name('y:0')

        op = sess.graph.get_tensor_by_name('op_to_store:0')
        b = sess.graph.get_tensor_by_name('b:0')
        print('b', sess.run(b))

        ret = sess.run(op,  feed_dict={input_x: 10, input_y: 3})
        print(ret)

如果没有执行sess.run(b),那么载入savemodel2和载入savemodel的执行结果一样都是31。若保存模型时执行了sess.run(b),载入savemodel2执行结果会变成32。

总结


  • 保存模型时,需确保三点:
  1. 模型保存的直接目录不可存在,无论是否存在,该目录会直接创建。拼接的目录注意有‘/’,'a/' + 'savemodel'或者'a' + r'/savemodel';
  2. 确保模型保存有效性,在模型参数更新后再保存模型,一般在batch训练后即可;
  3. 保存模型执行完整的三部曲,建立保存器builder,保存会话和计算图,序列化写入磁盘--builder.save()。
  • 模型加载时,需确保两点
  1. 模型保存路径正确,否则找不到模型;
  2. 模型加载前执行sess.run(tf.global_variables_initializer()),也可以不执行初始化,绝对不能在模型加载后执行初始化。
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 可以,示例代码如下:import tensorflow as tf# 定义模型 model = tf.keras.Sequential([ tf.keras.layers.Dense(128, activation='relu', input_shape=(784,)), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(10, activation='softmax') ])# 编译模型 model.compile( optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'] )# 保存模型 tf.saved_model.save(model, 'my_model') ### 回答2: 当使用TensorFlow构建和训练模型后,我们可以使用`tf.saved_model.save()`方法来保存模型。下面是一个示例代码,展示如何使用`tf.saved_model.save()`来保存模型: ``` import tensorflow as tf # 构建模型 inputs = tf.keras.Input(shape=(32,)) outputs = tf.keras.layers.Dense(10)(inputs) model = tf.keras.Model(inputs=inputs, outputs=outputs) # 训练模型 model.compile(optimizer='adam', loss='mse') model.fit(x_train, y_train, epochs=10) # 保存模型 saved_model_path = "./saved_model" tf.saved_model.save(model, saved_model_path) ``` 在这个示例中,我们首先使用TensorFlow构建了一个简单的模型。然后,我们使用`compile()`方法来编译模型,并使用`fit()`方法来训练模型。 接下来,我们使用`tf.saved_model.save()`方法来保存模型。该方法接收两个参数:要保存模型对象和保存路径。在这个示例中,我们将模型保存到当前目录下的`saved_model`文件夹中。 保存模型后,`saved_model`文件夹中包含一个包含模型架构、权重和训练配置的saved_model.pb文件,以及一个variables文件夹,其中包含所有模型的变量。 通过使用`tf.saved_model.load()`方法,我们可以加载保存模型并继续使用它进行推理或微调训练。 ### 回答3: 当使用TensorFlow的`tf.saved_model.save()`方法来保存模型时,我们需要先定义一个模型,并将其保存到SavedModel格式。 下面是一个示例代码,展示了如何使用`tf.saved_model.save()`来保存一个简单的线性回归模型: ```python import tensorflow as tf # 定义模型 class LinearModel(tf.Module): def __init__(self): self.w = tf.Variable(2.0, name='weight') self.b = tf.Variable(1.0, name='bias') def __call__(self, x): return self.w * x + self.b # 创建一个模型实例 model = LinearModel() # 创建一个tf.Tensor对象 x = tf.constant(3.0) # 使用模型进行预测 y = model(x) # 为模型创建一个签名函数 @tf.function(input_signature=[tf.TensorSpec(shape=(), dtype=tf.float32)]) def predict(input_x): return model(input_x) # 将模型保存到SavedModel格式 tf.saved_model.save(model, 'linear_model') # 加载SavedModel loaded_model = tf.saved_model.load('linear_model') # 使用加载的模型进行预测,结果与之前的模型一致 loaded_y = loaded_model(x) print(loaded_y) # 输出为 tf.Tensor(7.0, shape=(), dtype=float32) ``` 在这个示例代码中,我们首先定义了一个简单的线性回归模型,然后创建了一个模型实例并使用它进行了预测。接下来,我们为模型创建了一个签名函数,并使用`tf.saved_model.save()`方法将整个模型保存到SavedModel格式。最后,我们加载了保存模型并使用它进行了预测,验证了加载的模型的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值