Tensorflow: Modelo Keras apto para encurtidos pero modelo tf.keras no apto para encurtidos

Creado en 29 nov. 2019  ·  34Comentarios  ·  Fuente: tensorflow/tensorflow

Información del sistema

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

¡Hola todos! Esta es mi primera publicación, así que perdóname si me he perdido algo. Así que estoy tratando de usar un algoritmo genético para entrenar y evaluar múltiples arquitecturas NN, así que necesito paralelizarlas en una CPU de múltiples núcleos. Por lo tanto, he usado joblib para intentar paralelizar esto. Sin embargo, estaba atascado en mi código tf.keras porque no era apto para encurtidos. Después de muchas horas de depuración, finalmente me di cuenta de que los modelos tf.keras no se pueden encurtir mientras que los modelos keras sí.

Describe el comportamiento actual
El siguiente código funciona, pero si reemplazó keras con tf.keras, habrá un error:
No se pudo seleccionar la tarea para enviarla a los trabajadores.

Describe el comportamiento esperado
En el futuro, tf.keras debería reemplazar a keras y, por lo tanto, tf.keras también debería ser encurtido.

Código para reproducir el problema

#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

Otros comentarios
Supongo que una solución rápida sería reemplazar todo el código existente con tf.keras por solo keras, pero dado que el soporte de keras será descontinuado y absorbido por Tensorflow 2.0, creo que esto debería arreglarse.

TF 2.2 keras awaiting tensorflower bug

Comentario más útil

Aquí hay una alternativa a la respuesta de @epetrovski que no requiere guardar en un archivo:

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)

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

Siento que tal vez esto podría agregarse al modelo. ¿Hay casos en los que esto no funcionaría?

Todos 34 comentarios

@ Edwin-Koh1

¿Puede verificar con la versión nocturna ( !pip install tf-nightly==2.1.0dev20191201 ) y ver si el error persiste? Hay muchas mejoras de rendimiento en las últimas versiones nocturnas. ¡Gracias!

Cierre automático por falta de actividad reciente. Actualice el problema cuando haya nueva información disponible y volveremos a abrir el problema. ¡Gracias!

@ravikyram sigo viendo este problema en 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()

resultados 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

Probé colab con TF versión 2.1.0-rc2, 2.2.0-dev20200113 y pude reproducir el problema. Por favor, encuentre la esencia aquí . ¡Gracias!

@ravikyram , ¿deberían los modelos funcionales de keras ser decapables también o no? ¿Asumiría que si los modelos secuenciales son, los modelos funcionales también deberían serlo? ¿O los modelos funcionales tienen algunas propiedades que los hacen más difíciles de encurtir?

$ 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

Hola a todos,
Estoy tratando de cambiar de keras independiente a tensorflow.keras según la recomendación en https://keras.io/.
Tengo la misma excepción que https://github.com/tensorflow/tensorflow/issues/34697#issuecomment -575705599 con joblib (que usa pickle debajo del capó).

Información del sistema:

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

Guión para reproducir:

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

Salida:

TypeError: can't pickle _thread.RLock objects

Aquí hay una solución adaptada de http://zachmoshe.com/2017/04/03/pickling-keras-models.html destinada a resolver el mismo problema cuando los modelos de Keras solían no ser encurtidos.

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 ¿Debo llamar a este código cada vez que estoy a punto de seleccionar un modelo o puedo llamarlo al principio de mi aplicación (antes de crear el modelo)?

@epetrovski ¿Debo llamar a este código cada vez que estoy a punto de seleccionar un modelo o puedo llamarlo al principio de mi aplicación (antes de crear el modelo)?

Definitivamente puede llamarlo una vez al comienzo de su aplicación después de importar tensorflow.keras.models.Model . La ejecución de la función agrega dos métodos nuevos __getstate__() y __setstate__() a la clase tensorflow.keras.models.Model , por lo que debería funcionar cada vez que desee seleccionar un miembro de la clase de modelo tf.keras actualizada - es decir. tu propio modelo.

Aquí hay una alternativa a la respuesta de @epetrovski que no requiere guardar en un archivo:

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)

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

Siento que tal vez esto podría agregarse al modelo. ¿Hay casos en los que esto no funcionaría?

Parece que hay dos atributos que no se pueden seleccionar en la clase Sequential. Esta solución también funcionó para mí:

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

Lo intenté en Colab con TF versión 2.2, versiones nocturnas y pude reproducir el problema. Por favor, encuentre la esencia aquí . ¡Gracias!

Lo intenté en Colab con TF versión 2.2, versiones nocturnas y pude reproducir el problema. Por favor, encuentre la esencia aquí . ¡Gracias!

El modelo de keras se puede seleccionar pero tf.keras no se puede seleccionar, por lo que la solución alternativa para esto es consultar el siguiente código:
Vi su cuaderno de colab e hice los cambios requeridos, solo copie el mismo código que se muestra a continuación y habrá terminado de resolver el error

importar tensorflow como tf

def main ():
modelo_1 = tf.keras.Sequential ((
tf.keras.layers.Dense (16, activación = 'relu'),
tf.keras.layers.Dense (1, activación = 'lineal'),
))

_ = 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 __name__ == '__main__':
principal()

@ Edwin-Koh1

Según la sugerencia de @lahsrahtidnap, probé en colab y no veo ningún problema. Por favor, encuentre la esencia aquí . ¡Gracias!

Hola a todos,
Estoy tratando de cambiar de keras independiente a tensorflow.keras según la recomendación en https://keras.io/.
Estoy haciendo la misma excepción que joblib (que usa pickle debajo del capó).

Información del sistema:

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

Guión para reproducir:

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

Salida:

TypeError: can't pickle _thread.RLock objects

El uso de pickle o joblib no resolverá su problema, ya que tensorflow.keras no admite esto.
Entonces, la solución alternativa para esto es: -

considerando su código: -
reemplace esta línea: - joblib.dump (modelo, 'modelo.pkl')
con: -
para guardar el modelo use: -
-----> model.save ('nuevo_modelo.h5')
y si desea cargar este modelo use: -
-----> nuevo_modelo = tf.keras.models.load_model ('nuevo_modelo.h5')

considerando su código: -
reemplace esta línea: - joblib.dump (modelo, 'modelo.pkl')
con: -
para guardar el modelo use: -
-----> model.save ('nuevo_modelo.h5')
y si desea cargar este modelo use: -
-----> nuevo_modelo = tf.keras.models.load_model ('nuevo_modelo.h5')

Esto funciona en algunos casos, sin embargo, no ayuda cuando un modelo es decapado como parte de otra función, en mi caso esto sucede cuando se usa la biblioteca python multiprocessing .

@ Edwin-Koh1

¿Sigue siendo un problema?
Por favor, confirma ¡Gracias!

¿Es posible volcar el modelo secuencial de Keras en el contenedor 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

o en un archivo temporal

tempfile.TemporaryFile().write(Keras_model)

o

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

Esto funcionó perfectamente, bueno, es posible que no necesite base64, para que lo almacene en la base de datos que hice, todo en la memoria, sin tocar el disco

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
Esta es una buena solución, gracias. Solo tenga cuidado si planea continuar entrenando este modelo, ya que este método no conserva el estado del optimizador.

@JohannesAck
Sí, tenemos que compilar con el optimizador antes de ajustar con nuevos datos, eso no debería llevar mucho tiempo.

La otra forma model.save es muy difícil de almacenar en la memoria.

Otra forma es que podemos hacer get_config () y from_config () con inicialización y compilación, luego se debe ajustar para nuevos datos.

@ Edwin-Koh1

Cualquier actualización sobre este tema por favor ¡Gracias!

Este problema se ha marcado automáticamente como obsoleto porque no ha tenido actividad reciente. Se cerrará si no se produce más actividad. Gracias.

considerando su código: -
reemplace esta línea: - joblib.dump (modelo, 'modelo.pkl')
con: -
para guardar el modelo use: -
-----> model.save ('nuevo_modelo.h5')
y si desea cargar este modelo use: -
-----> nuevo_modelo = tf.keras.models.load_model ('nuevo_modelo.h5')

Esto funciona en algunos casos, sin embargo, no ayuda cuando un modelo es decapado como parte de otra función, en mi caso esto sucede cuando se usa la biblioteca python multiprocessing .

@JohannesAck , creo que podría tener un problema similar. Entreno un modelo de Keras en GPU, lo guardo usando el formato TensorFlow SavedModel usando la API de Keras, lo recargo en una nueva sesión y trato de hacer predicciones en paralelo en múltiples CPU usando la biblioteca multiprocessing y la biblioteca starmap función. Si cargo el modelo antes de paralelizar las predicciones, obtengo un error de decapado ( TypeError: can't pickle _thread.RLock objects ). Si cargo el modelo dentro de mi función de predicción cada vez y lo elimino al final de cada llamada de función, se cuelga después de un par de predicciones. ¿Tienes idea de lo que podría estar pasando aquí?

Cerrando como rancio. Vuelva a abrir si desea seguir trabajando en esto.

¿Está satisfecho con la resolución de su problema?
si
No

¿Es esto un WONTFIX después de que el problema se haya cerrado ahora?

En mi caso, no puedo simplemente usar model.save() porque el decapado se realiza desde una herramienta externa a la que mi código simplemente proporciona un modelo compatible con scikit-learn (mi clase proporciona un método get_clf ). Pude solucionar el problema ya que mi código era (casi) compatible con Keras (no tf.keras), y con Keras 2.3.1 (TF 1.15.0) el decapado funciona sin problemas.

@mimxrt si está buscando usar modelos Keras dentro de un entorno scikit-learn, consulte SciKeras (divulgación completa: soy el autor). Si solo está buscando una forma de hacer que los objetos de Keras se puedan seleccionar, consulte https://github.com/tensorflow/tensorflow/pull/39609 y, en particular, https://github.com/tensorflow/tensorflow/pull/39609#issuecomment -683370566

Editar: enlace fijo

No estamos trabajando activamente en esto en este momento, pero reabrimos ya que todavía es un problema.

Cerrando como rancio. Vuelva a abrir si desea seguir trabajando en esto.

¿Está satisfecho con la resolución de su problema?
si
No

Esto se estancará de nuevo.

Este problema se ha marcado automáticamente como obsoleto porque no ha tenido actividad reciente. Se cerrará si no se produce más actividad. Gracias.

¿Fue útil esta página
0 / 5 - 0 calificaciones