【问题标题】:Unpickled tensorflow model fails to make predictions未腌制的张量流模型无法做出预测
【发布时间】:2021-11-18 14:20:25
【问题描述】:

我见过this questionthis one,但没有真正解释发生了什么,也没有为我面临的问题提供解决方案。

下面的代码是我在更大的上下文中尝试做的一个 sn-p。基本上,我正在创建一个包含 tensorflow.keras 模型的对象,我使用从this answer 改编的技巧将其保存到带有泡菜的文件中。我正在处理的实际课程还有其他几个领域和方法,因此我更愿意让它可以腌制并以灵活的方式这样做。请参阅下面的代码,以最小化重现问题。 ReproduceProblem.py:

import pickle
import numpy as np
import tempfile
import tensorflow as tf


def __getstate__(self):
    model_str = ""
    with tempfile.NamedTemporaryFile(suffix=".hdf5", delete=False) as fd:
        tf.keras.models.save_model(self, fd.name, overwrite=True)
        model_str = fd.read()
    d = {"model_str": model_str}
    return d


def __setstate__(self, state):
    with tempfile.NamedTemporaryFile(suffix=".hdf5", delete=False) as fd:
        fd.write(state["model_str"])
        fd.flush()
        model = tf.keras.models.load_model(fd.name)
    self.__dict__ = model.__dict__


class ContainsSequential:
    def __init__(self):
        self.other_field = "potato"
        self.model = tf.keras.models.Sequential()
        self.model.__getstate__ = lambda mdl=self.model: __getstate__(mdl)
        self.model.__setstate__ = __setstate__
        self.model.add(tf.keras.layers.Input(shape=(None, 3)))
        self.model.add(tf.keras.layers.LSTM(3, activation="relu", return_sequences=True))
        self.model.add(tf.keras.layers.Dense(3, activation="linear"))


# Now do the business:
tf.keras.backend.clear_session()
file_name = 'pickle_file.pckl'
instance = ContainsSequential()
instance.model.predict(np.random.rand(3, 1, 3))
print(instance.other_field)
with open(file_name, 'wb') as fid:
    pickle.dump(instance, fid)
with open(file_name, 'rb') as fid:
    restored_instance = pickle.load(fid)
print(restored_instance.other_field)
restored_instance.model.predict(np.random.rand(3, 1, 3))
print('Done')

虽然在instance.model.predict(np.random.rand(3, 1, 3)) 行上没有失败,但在restored_instance.model.predict(np.random.rand(3, 1, 3)) 行上确实失败了,错误消息是:

  File "<path>\ReproduceProblem.py", line 52, in <module>
    restored_instance.model.predict(np.random.rand(3, 1, 3))
  File "<path>\Python\Python39\lib\site-packages\keras\engine\training.py", line 1693, in predict
    if self.distribute_strategy._should_use_with_coordinator:  # pylint: disable=protected-access
  File "<path>\Python\Python39\lib\site-packages\keras\engine\training.py", line 716, in distribute_strategy
    return self._distribution_strategy or tf.distribute.get_strategy()
AttributeError: 'Sequential' object has no attribute '_distribution_strategy'

我对@9​​87654329@ 应该是什么一无所知,但在我的工作流程中,一旦我保存了文件,我就不需要再训练它了,只需用它来进行预测或咨询其他类的属性。我尝试将其设置为 None 并添加更多属性,但没有成功。

【问题讨论】:

    标签: python python-3.x tensorflow keras pickle


    【解决方案1】:

    像这样重新定义Tensorflow 类的方法是一种危险的方法:

    self.model = tf.keras.models.Sequential()
    self.model.__getstate__ = lambda mdl=self.model: __getstate__(mdl)
    self.model.__setstate__ = __setstate__
    

    我建议避免这种情况,而是重新定义自定义类的 __getstate____setstate__ 方法。这是一个工作示例:

    import pickle
    import numpy as np
    import tempfile
    import tensorflow as tf
    
    
    class ContainsSequential:
        def __init__(self):
            self.other_field = "Potato"
            self.model = tf.keras.models.Sequential()
            self.model.add(tf.keras.layers.Input(shape=(None, 3)))
            self.model.add(tf.keras.layers.LSTM(3, activation="relu", return_sequences=True))
            self.model.add(tf.keras.layers.Dense(3, activation="linear"))
            
        def __getstate__(self):
            model_str = ""
            with tempfile.NamedTemporaryFile(suffix=".hdf5", delete=False) as fd:
                tf.keras.models.save_model(self.model, fd.name, overwrite=True)
                model_str = fd.read()
            d = {"model_str": model_str, "other_field": self.other_field}
            return d
        
        def __setstate__(self, state):
            with tempfile.NamedTemporaryFile(suffix=".hdf5", delete=False) as fd:
                fd.write(state["model_str"])
                fd.flush()
                model = tf.keras.models.load_model(fd.name)
            self.model = model
            self.other_field = state["other_field"]
    

    还有一个测试:

    tf.keras.backend.clear_session()
    file_name = 'pickle_file.pkl'
    instance = ContainsSequential()
    
    rnd = np.random.rand(3, 1, 3)
    print(1, instance.model.predict(rnd))
    with open(file_name, 'wb') as fid:
        pickle.dump(instance, fid)
    with open(file_name, 'rb') as fid:
        r_instance = pickle.load(fid)
    print(2, r_instance.model.predict(rnd))
    print(r_instance.other_field)
    

    【讨论】:

    • 有趣的答案。虽然它解决了我的最小工作示例中的错误,但它产生了另一个问题(也许是我的错)。我在类中添加了一个字段“other_field”(当然,我实际使用的类还有其他方法和字段),然后尝试打印它。这会导致错误。我将编辑我的问题以显示它,如果您接受它,也许您的问题也是如此。但是 TL、DR 并没有解决我的问题。
    • 您需要将其他字段添加到__getstate____setstate__。我已将此示例添加到答案中。
    • 你们能帮我解决这个问题stackoverflow.com/questions/69439472/… 吗?
    【解决方案2】:

    您应该使用model.save('path/to/location')keras.models.load_model(),而不是使用pickle 序列化/反序列化tensorflow 模型。这是推荐的做法,您可以查看https://www.tensorflow.org/guide/keras/save_and_serialize 的文档。

    【讨论】:

    • 虽然我希望这个答案对将来偶然发现这个问题的人有所帮助,但我正在处理的问题之一是我要保存的对象有更多它不仅仅是一个 Keras 模型,所有其他的东西都应该和它一起腌制(最好)。问题中的技巧用于过去版本的 Keras/tensorflow,因此有一个不那么小的代码库假设类似的东西仍然可以工作。
    猜你喜欢
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-29
    • 2023-03-15
    • 1970-01-01
    • 2019-07-20
    相关资源
    最近更新 更多