Tensorflow: Modelo Keras capaz de decapagem, mas modelo tf.keras não capaz de decapagem

Criado em 29 nov. 2019  ·  34Comentários  ·  Fonte: tensorflow/tensorflow

Informação do sistema

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

Olá pessoal! Este é meu primeiro post, então por favor me perdoe se eu perdi algo. Estou tentando usar um algoritmo genético para treinar e avaliar várias arquiteturas NN, então preciso paralelizá-las em uma CPU de vários núcleos. Portanto, usei joblib para tentar paralelizar isso. No entanto, fiquei preso no meu código tf.keras porque ele não era selecionável. Depois de muitas horas de depuração, finalmente percebi que os modelos tf.keras não podem ser recuperados, enquanto os modelos keras sim.

Descreva o comportamento atual
O código abaixo funciona, mas se você substituir keras por tf.keras, ocorrerá um erro:
Não foi possível selecionar a tarefa para enviá-la aos trabalhadores.

Descreva o comportamento esperado
No futuro, tf.keras deve substituir keras e, portanto, tf.keras também deve ser selecionável.

Código para reproduzir o 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

Outros comentários
Acho que uma solução rápida seria apenas substituir todo o código existente por tf.keras para apenas keras, mas visto que o suporte a keras será descontinuado e absorvido pelo Tensorflow 2.0, acho que isso deve ser corrigido.

TF 2.2 keras awaiting tensorflower bug

Comentários muito úteis

Aqui está uma alternativa para a resposta de @epetrovski que não requer salvar em um arquivo:

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)

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

Acho que isso poderia ser adicionado ao modelo. Existem casos em que isso não funcionaria?

Todos 34 comentários

@ Edwin-Koh1

Você pode verificar a versão noturna ( !pip install tf-nightly==2.1.0dev20191201 ) e ver se o erro ainda persiste. Existem muitas melhorias de desempenho nas últimas versões noturnas. Obrigado!

Fechando automaticamente devido à falta de atividade recente. Atualize o problema quando novas informações estiverem disponíveis e nós o reabriremos. Obrigado!

@ravikyram Ainda estou vendo esse problema no 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()

resulta em

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

Tentei colab com TF versão 2.1.0-rc2, 2.2.0-dev20200113 e consegui reproduzir o problema. Por favor, encontre a essência aqui . Obrigado!

@ravikyram , os modelos funcionais da keras também devem ser escolhidos ou não? Eu diria que se os modelos sequenciais são, os modelos funcionais também deveriam ser? Ou os modelos funcionais têm algumas propriedades que os tornam mais difíceis de conservar?

$ 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

Olá a todos,
Estou tentando mudar de keras autônomo para tensorflow.keras acordo com a recomendação em https://keras.io/.
Estou encontrando a mesma exceção que https://github.com/tensorflow/tensorflow/issues/34697#issuecomment -575705599 com joblib (que usa pickle sob o capô).

Informação do sistema:

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

Script para reproduzir:

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

Resultado:

TypeError: can't pickle _thread.RLock objects

Aqui está uma correção adaptada de http://zachmoshe.com/2017/04/03/pickling-keras-models.html destinada a resolver o mesmo problema na época em que os modelos Keras não eram selecionáveis.

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 Devo chamar esse código sempre que estou prestes a selecionar um modelo ou posso apenas chamá-lo no início do meu aplicativo (antes de criar o modelo)?

@epetrovski Devo chamar esse código sempre que estou prestes a selecionar um modelo ou posso apenas chamá-lo no início do meu aplicativo (antes de criar o modelo)?

Definitivamente, você pode chamá-lo apenas uma vez no início do seu aplicativo, após importar tensorflow.keras.models.Model . Executar a função adiciona dois novos métodos __getstate__() e __setstate__() à classe tensorflow.keras.models.Model portanto, deve funcionar toda vez que você quiser selecionar um membro da classe tf.keras Model atualizada - ie. seu próprio modelo.

Aqui está uma alternativa para a resposta de @epetrovski que não requer salvar em um arquivo:

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)

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

Acho que isso poderia ser adicionado ao modelo. Existem casos em que isso não funcionaria?

Parece que há dois atributos que não podem ser selecionados na classe Sequential. Essa correção também funcionou para mim:

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

Eu tentei em colab com TF versão 2.2, versões noturnas e foi capaz de reproduzir o problema. Por favor, encontre a essência aqui . Obrigado!

Eu tentei em colab com TF versão 2.2, versões noturnas e foi capaz de reproduzir o problema. Por favor, encontre a essência aqui . Obrigado!

O modelo keras é selecionável, mas tf.keras não é selecionável, portanto, a solução alternativa para isso é consultar o código abaixo
Eu vi seu bloco de notas colab e fiz as alterações necessárias, apenas copie o mesmo código abaixo e você terá que resolver o erro

importar tensorflow como tf

def main ():
model_1 = tf.keras.Sequential ((
tf.keras.layers.Dense (16, ativação = 'relu'),
tf.keras.layers.Dense (1, ativação = '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)

if __name__ == '__main__':
a Principal()

@ Edwin-Koh1

De acordo com a sugestão de @lahsrahtidnap eu tentei no colab e não estou vendo nenhum problema. Por favor, encontre a essência aqui . Obrigado!

Olá a todos,
Estou tentando mudar de keras autônomo para tensorflow.keras acordo com a recomendação em https://keras.io/.
Estou recebendo a mesma exceção que joblib (que usa pickle sob o capô).

Informação do sistema:

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

Script para reproduzir:

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

Resultado:

TypeError: can't pickle _thread.RLock objects

Usar pickle ou joblib não resolverá seu problema, pois tensorflow.keras não suporta isso.
Portanto, a solução alternativa para isso é: -

considerando seu código: -
substitua esta linha: - joblib.dump (model, 'model.pkl')
com: -
para salvar o modelo, use: -
-----> model.save ('novo_modelo.h5')
e se quiser carregar este modelo, use: -
-----> new_model = tf.keras.models.load_model ('new_model.h5')

considerando seu código: -
substitua esta linha: - joblib.dump (model, 'model.pkl')
com: -
para salvar o modelo, use: -
-----> model.save ('novo_modelo.h5')
e se quiser carregar este modelo, use: -
-----> new_model = tf.keras.models.load_model ('new_model.h5')

Isso funciona em alguns casos, no entanto, não ajuda quando um modelo é conservado como parte de outra função, no meu caso, isso acontece ao usar a biblioteca python multiprocessing .

@ Edwin-Koh1

Isso ainda é um problema?
Por favor, confirme. Obrigado!

É possível despejar o modelo sequencial de Keras no contêiner 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 em um arquivo temporário

tempfile.TemporaryFile().write(Keras_model)

ou

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

Isso funcionou perfeitamente, bem, você pode não precisar de base64, para eu armazenar no banco de dados que fiz, tudo na memória, sem tocar no 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 é uma boa solução, obrigado. Tenha cuidado apenas se você planeja continuar treinando este modelo, pois este método não preserva o estado do otimizador.

@JohannesAck
Sim, temos que compilar com o otimizador antes de fazer o ajuste com novos dados, o que não deve ser demorado,

Por outro lado, model.save é muito difícil de armazenar na memória.

Outra maneira é que podemos fazer get_config () e from_config () com inicialização e compilar, então o ajuste deve ser feito para os novos dados.

@ Edwin-Koh1

Qualquer atualização sobre este assunto, por favor. Obrigado!

Este problema foi automaticamente marcado como obsoleto porque não teve atividades recentes. Ele será fechado se nenhuma outra atividade ocorrer. Obrigado.

considerando seu código: -
substitua esta linha: - joblib.dump (model, 'model.pkl')
com: -
para salvar o modelo, use: -
-----> model.save ('novo_modelo.h5')
e se quiser carregar este modelo, use: -
-----> new_model = tf.keras.models.load_model ('new_model.h5')

Isso funciona em alguns casos, no entanto, não ajuda quando um modelo é conservado como parte de outra função, no meu caso, isso acontece ao usar a biblioteca python multiprocessing .

@JohannesAck , acho que posso ter um problema semelhante. Eu treino um modelo Keras na GPU, salvo usando o formato TensorFlow SavedModel usando a API Keras, recarrego-o em uma nova sessão e tento fazer previsões em paralelo em várias CPUs usando a biblioteca multiprocessing e starmap função TypeError: can't pickle _thread.RLock objects ). Se eu carregar o modelo dentro da minha função de previsão a cada vez e excluí-lo no final de cada chamada de função, ele trava após algumas previsões. Você tem alguma ideia do que pode estar acontecendo aqui?

Fechando como obsoleto. Abra novamente se quiser trabalhar mais nisso.

Você está satisfeito com a resolução do seu problema?
sim
Não

Este é um WONTFIX depois que o problema foi resolvido agora?

No meu caso, não posso simplesmente usar model.save() porque a decapagem é realizada a partir de uma ferramenta externa para a qual meu código apenas fornece um modelo compatível com scikit-learn (minha classe fornece um método get_clf ). Eu pude contornar o problema porque meu código era (quase) compatível com Keras (não tf.keras), e com Keras 2.3.1 (TF 1.15.0) decapagem funciona sem problemas.

@mimxrt se você deseja usar os modelos Keras em um ambiente scikit-learn, verifique SciKeras (divulgação completa: eu sou o autor). Se você está apenas procurando uma maneira de tornar os objetos Keras selecionáveis, verifique https://github.com/tensorflow/tensorflow/pull/39609 e, em particular, https://github.com/tensorflow/tensorflow/pull/39609#issuecomment -683370566

Editar: link fixo

Não estamos trabalhando ativamente nisso agora, mas reabrindo, pois ainda é um problema.

Fechando como obsoleto. Abra novamente se quiser trabalhar mais nisso.

Você está satisfeito com a resolução do seu problema?
sim
Não

Isso vai ficar parado novamente.

Este problema foi automaticamente marcado como obsoleto porque não teve atividades recentes. Ele será fechado se nenhuma outra atividade ocorrer. Obrigado.

Esta página foi útil?
0 / 5 - 0 avaliações