【问题标题】:saved_model.prune() in TF2.0TF2.0 中的 saved_model.prune()
【发布时间】:2019-11-24 15:32:14
【问题描述】:

我正在尝试修剪使用 tf.keras 生成的 SavedModel 的节点。剪枝脚本如下:

svmod = tf.saved_model.load(fn) #version 1
#svmod = tfk.experimental.load_from_saved_model(fn) #version 2
feeds = ['foo:0']
fetches = ['bar:0']
svmod2 = svmod.prune(feeds=feeds, fetches=fetches)
tf.saved_model.save(svmod2, '/tmp/saved_model/') #version 1
#tfk.experimental.export_saved_model(svmod2, '/tmp/saved_model/') #version 2

如果我使用版本 #1 修剪有效,但在保存时会给出 ValueError: Expected a Trackable object for export。在版本 2 中,没有 prune() 方法。

如何修剪 TF2.0 Keras SavedModel?

【问题讨论】:

    标签: python tensorflow tf.keras


    【解决方案1】:

    由于您可以在版本 #1 中成功修剪,我建议您尝试使用 'pickle' 来保存模型。 请尝试以下步骤来保存模型。

    import pickle
    with open('<model_name.pkl>', 'wb') as f:
        pickle.dump(<your_model>, f)
    

    将模型读取为:

    with open('<model_name.pkl>', 'rb') as f:
        model = pickle.load(f)
    

    在您的情况下,对于版本 #1,代码 sn-p 中的 your_modelsvmod2

    【讨论】:

    • 抱歉,我不清楚我是否需​​要使用 TF SavedModel 格式,这是 TF 服务、TFLite 等的标准格式。保存在 pickle 文件中并不能做到这一点。
    【解决方案2】:

    看起来您在版本 1 中修剪模型的方式很好;根据您的错误消息,无法保存生成的修剪模型,因为它不是“可跟踪”的,这是使用tf.saved_model.save 保存模型的必要条件。制作可跟踪对象的一种方法是从tf.Module 类继承,如using the SavedModel formatconcrete functions 指南中所述。下面是一个尝试保存tf.function 对象(由于该对象不可跟踪而失败)、从tf.module 继承并保存结果对象的示例:

    (使用 Python 3.7.6 版、TensorFlow 2.1.0 版和 NumPy 1.18.1 版)

    import tensorflow as tf, numpy as np
    
    # Define a random TensorFlow function and generate a reference output
    conv_filter = tf.random.normal([1, 2, 4, 2], seed=1254)
    @tf.function
    def conv_model(x):
        return tf.nn.conv2d(x, conv_filter, 1, "SAME")
    
    input_tensor = tf.ones([1, 2, 3, 4])
    output_tensor = conv_model(input_tensor)
    print("Original model outputs:", output_tensor, sep="\n")
    
    # Try saving the model: it won't work because a tf.function is not trackable
    export_dir = "./tmp/"
    try: tf.saved_model.save(conv_model, export_dir)
    except ValueError: print(
        "Can't save {} object because it's not trackable".format(type(conv_model)))
    
    # Now define a trackable object by inheriting from the tf.Module class
    class MyModule(tf.Module):
        @tf.function
        def __call__(self, x): return conv_model(x)
    
    # Instantiate the trackable object, and call once to trace-compile a graph
    module_func = MyModule()
    module_func(input_tensor)
    tf.saved_model.save(module_func, export_dir)
    
    # Restore the model and verify that the outputs are consistent
    restored_model = tf.saved_model.load(export_dir)
    restored_output_tensor = restored_model(input_tensor)
    print("Restored model outputs:", restored_output_tensor, sep="\n")
    if np.array_equal(output_tensor.numpy(), restored_output_tensor.numpy()):
        print("Outputs are consistent :)")
    else: print("Outputs are NOT consistent :(")
    

    控制台输出:

    Original model outputs:
    tf.Tensor(
    [[[[-2.3629642   1.2904963 ]
       [-2.3629642   1.2904963 ]
       [-0.02110204  1.3400152 ]]
    
      [[-2.3629642   1.2904963 ]
       [-2.3629642   1.2904963 ]
       [-0.02110204  1.3400152 ]]]], shape=(1, 2, 3, 2), dtype=float32)
    Can't save <class 'tensorflow.python.eager.def_function.Function'> object
    because it's not trackable
    Restored model outputs:
    tf.Tensor(
    [[[[-2.3629642   1.2904963 ]
       [-2.3629642   1.2904963 ]
       [-0.02110204  1.3400152 ]]
    
      [[-2.3629642   1.2904963 ]
       [-2.3629642   1.2904963 ]
       [-0.02110204  1.3400152 ]]]], shape=(1, 2, 3, 2), dtype=float32)
    Outputs are consistent :)
    

    因此你应该尝试如下修改你的代码:

    svmod = tf.saved_model.load(fn) #version 1
    svmod2 = svmod.prune(feeds=['foo:0'], fetches=['bar:0'])
    
    class Exportable(tf.Module):
        @tf.function
        def __call__(self, model_inputs): return svmod2(model_inputs)
    
    svmod2_export = Exportable()
    svmod2_export(typical_input)    # call once with typical input to trace-compile
    tf.saved_model.save(svmod2_export, '/tmp/saved_model/')
    

    如果您不想从tf.Module 继承,您也可以只实例化一个tf.Module 对象并通过替换该部分代码来添加一个tf.function 方法/可调用属性,如下所示:

    to_export = tf.Module()
    to_export.call = tf.function(conv_model)
    to_export.call(input_tensor)
    tf.saved_model.save(to_export, export_dir)
    
    restored_module = tf.saved_model.load(export_dir)
    restored_func = restored_module.call
    

    【讨论】:

    • 这很有帮助,但是我遇到了一个问题。我的模型是带有估计器的训练器,它似乎输入global_step 作为输入。因此,在尝试执行上述操作时,出现错误:TypeError: Expected argument names ['Placeholder', 'global_step'] but got values for ['Placeholder']. Missing: ['global_step'].
    • 实际上,我通过在 call 函数定义中手动传递一个全局步骤来解决这个问题。但是,在尝试加载时,出现错误:File "/usr/local/lib/python3.7/site-packages/tensorflow/python/keras/saving/saving_utils.py", line 113, in trace_model_call if isinstance(model.call, def_function.Function): AttributeError: '_UserObject' object has no attribute 'call'
    • 嗯...你能发布完整的代码和错误信息吗?也许作为一个新的答案或新的问题,很难尝试在没有换行符的情况下阅读评论中的代码
    猜你喜欢
    • 2019-11-07
    • 2020-12-06
    • 2020-03-10
    • 2020-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    相关资源
    最近更新 更多