Tensorflow: Keras模型可腌制,但tf.keras模型不可腌制

创建于 2019-11-29  ·  34评论  ·  资料来源: tensorflow/tensorflow

系统信息

  • Windows 10
  • Tensorflow 2.0(CPU)
  • 作业库0.14.0
  • Python 3.7.5
  • 凯拉斯2.3.1

大家好! 这是我的第一篇文章,所以如果我错过了什么请原谅我。 因此,我尝试使用一种遗传算法来训练和评估多个NN架构,因此我需要在多核CPU上对其并行化。 因此,我使用joblib尝试并行化它。 但是,我无法使用tf.keras代码,因为它不具有可腌性。 经过许多小时的调试,我终于意识到tf.keras模型不可腌制,而keras模型则可腌制。

描述当前行为
下面的代码有效,但是如果用tf.keras替换了keras,将出现错误:
无法腌制该任务以将其发送给工作人员。

描述预期的行为
展望未来,tf.keras应该替代了keras,因此tf.keras也应该是可腌制的。

重现问题的代码

#The following is a simple code to illustrate the problem:
from joblib import Parallel, delayed
import keras
import tensorflow as tf

def test():
    model = keras.models.Sequential()
    return

Parallel(n_jobs=8)(delayed(test)(i) for i in range(10)) #this works as intended

def test_tf():
    model = tf.keras.models.Sequential()
    return

Parallel(n_jobs=8)(delayed(test_tf)(i) for i in range(10)) #this will spit out the error above

其他的建议
我想一个快速的解决方法是用tf.keras替换所有现有代码,仅替换为keras,但是由于Tensorflow 2.0将不再支持并吸收keras,我认为这应该是固定的。

TF 2.2 keras awaiting tensorflower bug

最有用的评论

这是@epetrovski答案的替代方法,不需要保存到文件:

import pickle

from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Dense
from tensorflow.python.keras.layers import deserialize, serialize
from tensorflow.python.keras.saving import saving_utils


def unpack(model, training_config, weights):
    restored_model = deserialize(model)
    if training_config is not None:
        restored_model.compile(
            **saving_utils.compile_args_from_training_config(
                training_config
            )
        )
    restored_model.set_weights(weights)
    return restored_model

# Hotfix function
def make_keras_picklable():

    def __reduce__(self):
        model_metadata = saving_utils.model_metadata(self)
        training_config = model_metadata.get("training_config", None)
        model = serialize(self)
        weights = self.get_weights()
        return (unpack, (model, training_config, weights))

    cls = Model
    cls.__reduce__ = __reduce__

# Run the function
make_keras_picklable()

# Create the model
model = Sequential()
model.add(Dense(1, input_dim=42, activation='sigmoid'))
model.compile(optimizer='Nadam', loss='binary_crossentropy', metrics=['accuracy'])

# Save
with open('model.pkl', 'wb') as f:
    pickle.dump(model, f)

资料来源: https :

我觉得也许可以将其添加到模型中? 在任何情况下这都行不通吗?

所有34条评论

@ Edwin-Koh1

能否请您检查每晚版本( !pip install tf-nightly==2.1.0dev20191201 ),看看错误是否仍然存在。 最新的每晚版本在性能上有很多改进。 谢谢!

由于缺乏近期活动而自动关闭。 当有新信息可用时,请更新此问题,我们将重新打开该问题。 谢谢!

@ravikyram我仍然在tensorflow == 2.1.0上看到这个问题:

import pickle

import tensorflow as tf


def main():
    model_1 = tf.keras.Sequential((
        tf.keras.layers.Dense(16, activation='relu'),
        tf.keras.layers.Dense(1, activation='linear'),
    ))

    _ = model_1(tf.random.uniform((15, 3)))

    model_2 = pickle.loads(pickle.dumps(model_1))

    for w1, w2 in zip(model_1.get_weights(), model_2.get_weights()):
        tf.debugging.assert_equal(w1, w2)


if __name__ == '__main__':
    main()

结果是

Traceback (most recent call last):
  File "/Users/hartikainen/conda/envs/softlearning-3/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/Users/hartikainen/conda/envs/softlearning-3/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/Users/hartikainen/github/rail-berkeley/softlearning-3/tests/test_pickle_keras_model.py", line 25, in <module>
    main()
  File "/Users/hartikainen/github/rail-berkeley/softlearning-3/tests/test_pickle_keras_model.py", line 18, in main
    model_2 = pickle.loads(pickle.dumps(model_1))
TypeError: can't pickle weakref objects
$ pip freeze | grep "tf\|tensor"
tensorboard==2.1.0
tensorflow==2.1.0
tensorflow-estimator==2.1.0
tensorflow-probability==0.9.0
$ python --version
Python 3.7.5

我已经尝试使用TF版本2.1.0-rc2、2.2.0-dev20200113进行colab,并且能够重现问题。请在

@ravikyram ,keras功能模型也应该可

$ python -m tests.test_pickle_keras_functional_model
2020-01-17 16:47:08.567598: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2020-01-17 16:47:08.581327: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x7fa0a55aa6c0 initialized for platform Host (this does not guarantee that XLA will be used). Devices:
2020-01-17 16:47:08.581362: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): Host, Default Version
Traceback (most recent call last):
  File "/Users/hartikainen/conda/envs/softlearning-3/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/Users/hartikainen/conda/envs/softlearning-3/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/Users/hartikainen/github/rail-berkeley/softlearning-3/tests/test_pickle_keras_functional_model.py", line 20, in <module>
    main()
  File "/Users/hartikainen/github/rail-berkeley/softlearning-3/tests/test_pickle_keras_functional_model.py", line 13, in main
    model_2 = pickle.loads(pickle.dumps(model_1))
TypeError: can't pickle _thread.RLock objects

嗨,大家好,
我正在尝试根据https://keras.io/上的建议从独立的keras切换到tensorflow.keras
我遇到了与https://github.com/tensorflow/tensorflow/issues/34697#issuecomment -575705599相同的异常,并带有joblib (在后台使用pickle )。

系统信息:

  • Debian 10(破坏者)
  • Python 3.7.6
  • 作业库0.14.1
  • 张量流2.1.0

复制脚本:

import joblib
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

model = Sequential()
model.add(Dense(1, input_dim=42, activation='sigmoid'))
model.compile(optimizer='Nadam', loss='binary_crossentropy', metrics=['accuracy'])
joblib.dump(model, 'model.pkl')

输出:

TypeError: can't pickle _thread.RLock objects

这是从http://zachmoshe.com/2017/04/03/pickling-keras-models.html改编的修复程序,用于解决Keras模型以前无法腌制的问题。

import pickle
import tempfile
from tensorflow.keras.models import Sequential, load_model, save_model, Model
from tensorflow.keras.layers import Dense

# Hotfix function
def make_keras_picklable():
    def __getstate__(self):
        model_str = ""
        with tempfile.NamedTemporaryFile(suffix='.hdf5', delete=True) as fd:
            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=True) as fd:
            fd.write(state['model_str'])
            fd.flush()
            model = load_model(fd.name)
        self.__dict__ = model.__dict__


    cls = Model
    cls.__getstate__ = __getstate__
    cls.__setstate__ = __setstate__

# Run the function
make_keras_picklable()

# Create the model
model = Sequential()
model.add(Dense(1, input_dim=42, activation='sigmoid'))
model.compile(optimizer='Nadam', loss='binary_crossentropy', metrics=['accuracy'])

# Save
with open('model.pkl', 'wb') as f:
    pickle.dump(model, f)

@epetrovski我应该在每次腌制模型时调用此代码,还是可以在应用程序的开头(在创建模型之前)调用它?

@epetrovski我应该在每次腌制模型时调用此代码,还是可以在应用程序的开头(在创建模型之前)调用它?

导入tensorflow.keras.models.Model之后,您绝对可以在应用程序开头调用一次。 执行该函数会将两个新方法__getstate__()__setstate__()tensorflow.keras.models.Model类中,因此它每次在您要给更新的tf.keras Model类的成员腌制时都应该起作用-即。 您自己的模型。

这是@epetrovski答案的替代方法,不需要保存到文件:

import pickle

from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Dense
from tensorflow.python.keras.layers import deserialize, serialize
from tensorflow.python.keras.saving import saving_utils


def unpack(model, training_config, weights):
    restored_model = deserialize(model)
    if training_config is not None:
        restored_model.compile(
            **saving_utils.compile_args_from_training_config(
                training_config
            )
        )
    restored_model.set_weights(weights)
    return restored_model

# Hotfix function
def make_keras_picklable():

    def __reduce__(self):
        model_metadata = saving_utils.model_metadata(self)
        training_config = model_metadata.get("training_config", None)
        model = serialize(self)
        weights = self.get_weights()
        return (unpack, (model, training_config, weights))

    cls = Model
    cls.__reduce__ = __reduce__

# Run the function
make_keras_picklable()

# Create the model
model = Sequential()
model.add(Dense(1, input_dim=42, activation='sigmoid'))
model.compile(optimizer='Nadam', loss='binary_crossentropy', metrics=['accuracy'])

# Save
with open('model.pkl', 'wb') as f:
    pickle.dump(model, f)

资料来源: https :

我觉得也许可以将其添加到模型中? 在任何情况下这都行不通吗?

似乎有两个不能在Sequential类中选择的属性。 此修复程序也对我有用:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

class PickableSequential(Sequential):
    def __getstate__(self):
        state = super().__getstate__()
        state.pop("_trackable_saver")
        state.pop("_compiled_trainable_state")
        return state

model = PickableSequential(Dense(10))

import pickle

pickle.dumps(model)
~                     

我已经尝试使用TF 2.2版和夜间版本在colab中进行尝试,并且能够重现该问题。请在此处找到要点

我已经尝试使用TF 2.2版和夜间版本在colab中进行尝试,并且能够重现该问题。请在此处找到要点

keras模型是可挑选的,但tf.keras是不可挑选的,因此对此的替代解决方案是以下代码:
我看到了您的colab笔记本,并进行了所需的更改,只需复制以下相同的代码即可解决错误

将tensorflow作为tf导入

def main():
模型_1 = tf.keras.Sequential((
tf.keras.layers.Dense(16,activation ='relu'),
tf.keras.layers.Dense(1,activation ='linear'),
))

_ = model_1(tf.random.uniform((15, 3)))
model_1.save('model_2.h5')
model_2 = tf.keras.models.load_model('model_2.h5')

for w1, w2 in zip(model_1.get_weights(), model_2.get_weights()):
    tf.debugging.assert_equal(w1, w2)

如果__name__ =='__main__':
主要()

@ Edwin-Koh1

根据@lahsrahtidnap的建议,我在colab中尝试过,但没有发现任何问题。请在此处找到要点

嗨,大家好,
我正在尝试根据https://keras.io/上的建议从独立的keras切换到tensorflow.keras
我遇到了与#34697(注释)相同的异常,并带有joblib在后台使用pickle )。

系统信息:

  • Debian 10(破坏者)
  • Python 3.7.6
  • 作业库0.14.1
  • 张量流2.1.0

复制脚本:

import joblib
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

model = Sequential()
model.add(Dense(1, input_dim=42, activation='sigmoid'))
model.compile(optimizer='Nadam', loss='binary_crossentropy', metrics=['accuracy'])
joblib.dump(model, 'model.pkl')

输出:

TypeError: can't pickle _thread.RLock objects

使用pickle或joblib不能解决您的问题,因为tensorflow.keras不支持这一点。
因此,另一种解决方案是:-

考虑您的代码:
替换此行:-joblib.dump(model,'model.pkl')
与:-
保存模型使用:-
-----> model.save('new_model.h5')
如果要加载此模型,请使用:-
-----> new_model = tf.keras.models.load_model('new_model.h5')

考虑您的代码:
替换此行:-joblib.dump(model,'model.pkl')
与:-
保存模型使用:-
-----> model.save('new_model.h5')
如果要加载此模型,请使用:-
-----> new_model = tf.keras.models.load_model('new_model.h5')

这在某些情况下有效,但是,当将模型作为其他功能的一部分进行酸洗时,它无济于事。就我而言,这在使用python multiprocessing库时发生。

@ Edwin-Koh1

这还是个问题吗?
请确认。谢谢!

是否可以在byteIO容器中转储Keras顺序模型

bytes_container = BytesIO()
joblib.dump(Keras_model, bytes_container, protocol=4)
# Error
TypeError: can't pickle _thread.RLock objects

pickle.dump(Keras_model, bytes_container, protocol=4)
# Error
TypeError: can't pickle _thread.RLock objects

dill.dump(Keras_model, bytes_container, protocol=4)
# Error
TypeError: can't pickle tensorflow.python._tf_stack.StackSummary objects

或在临时文件中

tempfile.TemporaryFile().write(Keras_model)

要么

save_model(Keras_model, bytes_container)
# Error
TypeError: expected str, bytes or os.PathLike object, not _io.BytesIO

这项工作非常完美,好了,您可能不需要base64,因为我可以将我存储在数据库中的所有内容存储在内存中,无需接触磁盘

from io import BytesIO
import dill,base64,tempfile

#Saving Model as base64
model_json = Keras_model.to_json()

def Base64Converter(ObjectFile):
    bytes_container = BytesIO()
    dill.dump(ObjectFile, bytes_container)
    bytes_container.seek(0)
    bytes_file = bytes_container.read()
    base64File = base64.b64encode(bytes_file)
    return base64File

base64KModelJson = Base64Converter(model_json)  
base64KModelJsonWeights = Base64Converter(Keras_model.get_weights())  

#Loading Back
from joblib import load
from keras.models import model_from_json
def ObjectConverter(base64_File):
    loaded_binary = base64.b64decode(base64_File)
    loaded_object = tempfile.TemporaryFile()
    loaded_object.write(loaded_binary)
    loaded_object.seek(0)
    ObjectFile = load(loaded_object)
    loaded_object.close()
    return ObjectFile

modeljson = ObjectConverter(base64KModelJson)
modelweights = ObjectConverter(base64KModelJsonWeights)
loaded_model = model_from_json(modeljson)
loaded_model.set_weights(modelweights)

@hanzigs
谢谢,这是一个很好的解决方案。 仅当您计划继续训练该模型时,请小心,因为此方法不会保留优化器状态。

@JohannesAck
是的,我们必须先使用优化程序进行编译,然后再处理新数据,这不应该浪费时间,

反之,很难将model.save存储在内存中。

另一种方法是我们可以通过初始化和编译来完成get_config()和from_config(),然后必须对新数据进行拟合。

@ Edwin-Koh1

请对此问题进行任何更新。谢谢!

由于此问题最近没有活动,因此已被自动标记为陈旧。 如果没有进一步的活动,它将关闭。 谢谢。

考虑您的代码:
替换此行:-joblib.dump(model,'model.pkl')
与:-
保存模型使用:-
-----> model.save('new_model.h5')
如果要加载此模型,请使用:-
-----> new_model = tf.keras.models.load_model('new_model.h5')

这在某些情况下有效,但是,当将模型作为其他功能的一部分进行酸洗时,它无济于事。就我而言,这在使用python multiprocessing库时发生。

@JohannesAck ,我想我可能有一个类似的问题。 我在GPU上训练了Keras模型,使用Keras API使用TensorFlow SavedModel格式保存了该模型,在新会话中重新加载了它,并尝试使用multiprocessing库和starmap在多个CPU上并行进行预测TypeError: can't pickle _thread.RLock objects )。 如果我每次都在预测函数中加载模型并在每个函数结束时将其删除,则调用会在两次预测后挂起。 您有什么想法吗?

收盘陈旧。 如果您想进一步处理,请重新打开。

您对问题的解决感到满意吗?

没有

现在已解决问题之后,这是WONTFIX吗?

在我的情况下,我不能简单地使用model.save()因为酸洗是从外部工具执行的,我的代码仅向该工具提供了scikit-learn兼容模型(我的类提供了get_clf方法)。 我的代码可以(几乎)与Keras兼容(而不是tf.keras),并且可以使用Keras 2.3.1(TF 1.15.0)进行酸洗,因此可以解决该问题。

@mimxrt,如果您希望在scikit-learn环境中使用Keras模型,请查看SciKeras (完整披露:我是作者)。 如果您只是在寻找使Keras对象可拾取的方法,请检查https://github.com/tensorflow/tensorflow/pull/39609 ,尤其是https://github.com/tensorflow/tensorflow/pull/39609#issuecomment -683370566

编辑:固定链接

我们目前并未对此进行积极的工作,但是由于它仍然是一个问题,因此需要重新开放。

收盘陈旧。 如果您想进一步处理,请重新打开。

您对问题的解决感到满意吗?

没有

这将再次停滞。

由于此问题最近没有活动,因此已被自动标记为陈旧。 如果没有进一步的活动,它将关闭。 谢谢。

此页面是否有帮助?
0 / 5 - 0 等级