Tensorflow: Modèle Keras pickle-able mais modèle tf.keras pas pickle-able

Créé le 29 nov. 2019  ·  34Commentaires  ·  Source: tensorflow/tensorflow

Informations système

  • Windows 10
  • Tensorflow 2.0 (processeur)
  • joblib 0.14.0
  • Python 3.7.5
  • Keras 2.3.1

Bonjour tous le monde! Ceci est mon premier message alors pardonnez-moi si j'ai manqué quelque chose. J'essaie donc d'utiliser un algorithme génétique pour entraîner et évaluer plusieurs architectures NN, je dois donc les paralléliser sur un processeur multicœur. Par conséquent, j'ai utilisé joblib pour essayer de paralléliser cela. Cependant, j'étais bloqué sur mon code tf.keras car il n'était pas picklable. Après de nombreuses heures de débogage, j'ai finalement réalisé que les modèles tf.keras ne sont pas picklables alors que les modèles keras le sont.

Décrivez le comportement actuel
Le code ci-dessous fonctionne mais si vous avez remplacé keras par tf.keras, il y aura une erreur:
Impossible de décaper la tâche pour l'envoyer aux travailleurs.

Décrivez le comportement attendu
À l'avenir, tf.keras devrait remplacer les keras et donc tf.keras devrait également être picklable.

Code pour reproduire le problème

#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

Autres commentaires
Je suppose qu'une solution rapide serait simplement de remplacer tout le code existant par tf.keras pour juste des keras, mais comme le support de keras sera interrompu et absorbé par Tensorflow 2.0, je pense que cela devrait être corrigé.

TF 2.2 keras awaiting tensorflower bug

Commentaire le plus utile

Voici une alternative à la réponse de

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)

Source: https://docs.python.org/3/library/pickle.html#object.__reduce__

J'ai l'impression que cela pourrait peut-être être ajouté à Model? Y a-t-il des cas où cela ne fonctionnerait pas?

Tous les 34 commentaires

@ Edwin-Koh1

Pouvez-vous s'il vous plaît vérifier avec la version nocturne ( !pip install tf-nightly==2.1.0dev20191201 ) et voir si l'erreur persiste. Il existe de nombreuses améliorations de performances dans les dernières versions nocturnes. Merci!

Fermeture automatique en raison du manque d'activité récente. Veuillez mettre à jour le problème lorsque de nouvelles informations sont disponibles et nous rouvrirons le problème. Merci!

@ravikyram Je vois toujours ce problème sur 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()

résulte en

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

J'ai essayé colab avec la version TF 2.1.0-rc2, 2.2.0-dev20200113 et j'ai pu reproduire le problème.Veuillez trouver l'essentiel ici . Merci!

@ravikyram , les modèles fonctionnels de Keras doivent-ils également être picklables ou non? Je suppose que si les modèles séquentiels sont alors les modèles fonctionnels devraient l'être aussi? Ou les modèles fonctionnels ont-ils des propriétés qui les rendent plus difficiles à décaper?

$ 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

Salut à tous,
J'essaie de passer de l'autonomie keras à tensorflow.keras conformément à la recommandation sur https://keras.io/.
Je rencontre la même exception que https://github.com/tensorflow/tensorflow/issues/34697#issuecomment -575705599 avec joblib (qui utilise pickle sous le capot).

Informations système:

  • Debian 10 (buster)
  • Python 3.7.6
  • joblib 0.14.1
  • tensorflow 2.1.0

Script à reproduire:

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

Production:

TypeError: can't pickle _thread.RLock objects

Voici un correctif adapté de http://zachmoshe.com/2017/04/03/pickling-keras-models.html destiné à résoudre le même problème lorsque les modèles Keras n'étaient pas picklable.

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 Dois-je appeler ce code chaque fois que je suis sur le point de décaper un modèle ou puis-je simplement l'appeler au début de mon application (avant de créer le modèle)?

@epetrovski Dois-je appeler ce code chaque fois que je suis sur le point de décaper un modèle ou puis-je simplement l'appeler au début de mon application (avant de créer le modèle)?

Vous pouvez certainement l'appeler une fois au début de votre application après avoir importé tensorflow.keras.models.Model . L'exécution de la fonction ajoute deux nouvelles méthodes __getstate__() et __setstate__() à la classe tensorflow.keras.models.Model donc cela devrait fonctionner chaque fois que vous voulez pickle un membre de la classe Model tf.keras mise à jour - c'est à dire. votre propre modèle.

Voici une alternative à la réponse de

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)

Source: https://docs.python.org/3/library/pickle.html#object.__reduce__

J'ai l'impression que cela pourrait peut-être être ajouté à Model? Y a-t-il des cas où cela ne fonctionnerait pas?

Il semble que deux attributs ne soient pas sélectionnables dans la classe Sequential. Ce correctif a également fonctionné pour moi:

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

J'ai essayé en colab avec la version TF 2.2, les versions de nuit et a été en mesure de reproduire le issue.Please, trouver l'essentiel ici .REMERCIE!

J'ai essayé en colab avec la version TF 2.2, les versions de nuit et a été en mesure de reproduire le issue.Please, trouver l'essentiel ici .REMERCIE!

Le modèle keras est sélectionnable mais tf.keras n'est pas sélectionnable, donc la solution alternative pour cela est de se référer au code ci-dessous
J'ai vu votre cahier colab et effectué les modifications requises, copiez simplement le même code que ci-dessous et vous avez terminé de résoudre l'erreur

importer tensorflow en tant que tf

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

_ = 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)

si __nom__ == '__main__':
principale()

@ Edwin-Koh1

Selon la suggestion de @lahsrahtidnap j'ai essayé dans colab et je ne vois aucune issue.Please, trouver l'essentiel ici .REMERCIE!

Salut à tous,
J'essaie de passer de l'autonomie keras à tensorflow.keras conformément à la recommandation sur https://keras.io/.
Je rencontre la même exception que # 34697 (commentaire) avec joblib (qui utilise pickle sous le capot).

Informations système:

  • Debian 10 (buster)
  • Python 3.7.6
  • joblib 0.14.1
  • tensorflow 2.1.0

Script à reproduire:

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

Production:

TypeError: can't pickle _thread.RLock objects

L'utilisation de pickle ou de joblib ne résoudra pas votre problème car tensorflow.keras ne le supporte pas.
La solution alternative pour cela est donc: -

compte tenu de votre code: -
remplacez cette ligne: - joblib.dump (model, 'model.pkl')
avec: -
pour enregistrer l'utilisation du modèle: -
-----> model.save ('nouveau_modèle.h5')
et si vous souhaitez charger ce modèle, utilisez: -
-----> nouveau_modèle = tf.keras.models.load_model ('nouveau_modèle.h5')

compte tenu de votre code: -
remplacez cette ligne: - joblib.dump (model, 'model.pkl')
avec: -
pour enregistrer l'utilisation du modèle: -
-----> model.save ('nouveau_modèle.h5')
et si vous souhaitez charger ce modèle, utilisez: -
-----> nouveau_modèle = tf.keras.models.load_model ('nouveau_modèle.h5')

Cela fonctionne dans certains cas, cependant, cela n'aide pas lorsqu'un modèle est picklé dans le cadre d'une autre fonction, dans mon cas, cela se produit lors de l'utilisation de la bibliothèque python multiprocessing .

@ Edwin-Koh1

Est-ce toujours un problème?
Merci de confirmer, merci!

Est-il possible de vider le modèle séquentiel Keras dans le conteneur byteIO

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

ou dans un fichier temporaire

tempfile.TemporaryFile().write(Keras_model)

ou

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

Cela a parfaitement fonctionné, vous n'avez peut-être pas besoin de base64, pour que je stocke dans la base de données que j'ai faite, tout en mémoire, pas de disque tactile

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
C'est une bonne solution, merci. Faites attention uniquement si vous prévoyez de continuer à entraîner ce modèle, car cette méthode ne préserve pas l'état de l'optimiseur.

@JohannesAck
Oui, nous devons compiler avec l'optimiseur avant de faire l'ajustement avec de nouvelles données, cela ne devrait pas prendre de temps,

L'autre façon dont model.save est très difficile à stocker en mémoire.

Une autre façon est de faire get_config () et from_config () avec l'initialisation et la compilation, puis l'ajustement doit être fait pour les nouvelles données.

@ Edwin-Koh1

Toute mise à jour sur ce problème, merci!

Ce problème a été automatiquement marqué comme obsolète car il n'a pas eu d'activité récente. Il sera fermé si aucune autre activité n'a lieu. Je vous remercie.

compte tenu de votre code: -
remplacez cette ligne: - joblib.dump (model, 'model.pkl')
avec: -
pour enregistrer l'utilisation du modèle: -
-----> model.save ('nouveau_modèle.h5')
et si vous souhaitez charger ce modèle, utilisez: -
-----> nouveau_modèle = tf.keras.models.load_model ('nouveau_modèle.h5')

Cela fonctionne dans certains cas, cependant, cela n'aide pas lorsqu'un modèle est picklé dans le cadre d'une autre fonction, dans mon cas, cela se produit lors de l'utilisation de la bibliothèque python multiprocessing .

@JohannesAck , je pense que je pourrais avoir un problème similaire. J'entraîne un modèle Keras sur GPU, je l'enregistre en utilisant le format TensorFlow SavedModel en utilisant l'API Keras, je le recharge dans une nouvelle session et j'essaye de faire des prédictions en parallèle sur plusieurs processeurs en utilisant la bibliothèque multiprocessing et le starmap Fonction TypeError: can't pickle _thread.RLock objects ). Si je charge le modèle dans ma fonction de prédiction à chaque fois et que je le supprime à la fin de chaque fonction, l'appel se bloque après quelques prédictions. Avez-vous une idée de ce qui pourrait se passer ici?

Fermant comme périmé. Veuillez rouvrir si vous souhaitez continuer à travailler là-dessus.

Êtes-vous satisfait de la résolution de votre problème?
Oui
Non

Est-ce un WONTFIX une fois le problème résolu maintenant?

Dans mon cas, je ne peux pas simplement utiliser model.save() car le décapage est effectué à partir d'un outil externe auquel mon code fournit simplement un modèle compatible scikit-learn (ma classe fournit une méthode get_clf ). Je pourrais contourner le problème car mon code était (presque) compatible Keras (pas tf.keras), et avec Keras 2.3.1 (TF 1.15.0) le décapage fonctionne sans problème.

@mimxrt si vous cherchez à utiliser des modèles Keras dans un environnement scikit-learn, veuillez consulter SciKeras (divulgation complète: je suis l'auteur). Si vous cherchez simplement un moyen de rendre les objets Keras sélectionnables, consultez https://github.com/tensorflow/tensorflow/pull/39609 et en particulier https://github.com/tensorflow/tensorflow/pull/39609#issuecomment -683370566

Edit: lien fixe

Nous ne travaillons pas activement là-dessus pour le moment, mais nous rouvrons car c'est toujours un problème.

Fermant comme périmé. Veuillez rouvrir si vous souhaitez continuer à travailler là-dessus.

Êtes-vous satisfait de la résolution de votre problème?
Oui
Non

Cela sera à nouveau bloqué.

Ce problème a été automatiquement marqué comme obsolète car il n'a pas eu d'activité récente. Il sera fermé si aucune autre activité n'a lieu. Je vous remercie.

Cette page vous a été utile?
0 / 5 - 0 notes