Keras: from keras import backend -- fails when reloading model

Created on 5 Dec 2016  路  12Comments  路  Source: keras-team/keras

When keras models using backend are saved, the json (de)serialization cannot find "backend". This may be due to the use of the Lambda layer, as it appears that the name "backend" is saved in the json and then the loader cannot find it (because "backend" is not in its namespace). If it is imported as "K", then it works properly, because the loader imports backend as K.

Possible Quick Fix: Update the documentation both in backend and Lambda that backend must be imported as "K". The current doc only has a note under backend, and seems to be just a suggestion: "You can import the backend module via: from keras import backend as K". Given standard python convention, it is unintuitive that "from keras import backend", "import keras.backend", or "from keras import backend as somethingelse" should function differently.
Another workaround is to have the user add "from keras import backend" in their user-defined Lambda function.

Best,
David

Here's a script that demonstrates working and non-working cases.
theano.__version__ = '0.9.0dev4.dev-ad1310c88830ed96119194c4f2da22b9b37c7622'
keras.__version__ = '1.1.2'

from keras import backend as K
from keras.layers.convolutional import Convolution1D
from keras.layers import Lambda, Dense
from keras.models import Sequential, model_from_json

print 'Works'
model = Sequential()
model.add(Convolution1D(nb_filter=128, filter_length=5, input_shape=(4096, 16), border_mode='same', activation='relu'))
model.add(Lambda(lambda x: K.sum(x, axis=1), output_shape=(128,)))
model.add(Dense(10, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='Nadam')
model2 = model_from_json(model.to_json())

from keras import backend

print 'Fails'
model = Sequential()
model.add(Convolution1D(nb_filter=128, filter_length=5, input_shape=(4096, 16), border_mode='same', activation='relu'))
model.add(Lambda(lambda x: backend.sum(x, axis=1), output_shape=(128,)))
model.add(Dense(10, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='Nadam')
model2 = model_from_json(model.to_json())

The second model load fails with this stack trace:

Traceback (most recent call last):
  File "keras_bug.py", line 23, in <module>
    model2 = model_from_json(model.to_json())
  File "/Users/davidslater/.virtualenvs/davidslater/lib/python2.7/site-packages/keras/models.py", line 209, in model_from_json
    return layer_from_config(config, custom_objects=custom_objects)
  File "/Users/davidslater/.virtualenvs/davidslater/lib/python2.7/site-packages/keras/utils/layer_utils.py", line 34, in layer_from_config
    return layer_class.from_config(config['config'])
  File "/Users/davidslater/.virtualenvs/davidslater/lib/python2.7/site-packages/keras/models.py", line 1061, in from_config
    model.add(layer)
  File "/Users/davidslater/.virtualenvs/davidslater/lib/python2.7/site-packages/keras/models.py", line 324, in add
    output_tensor = layer(self.outputs[0])
  File "/Users/davidslater/.virtualenvs/davidslater/lib/python2.7/site-packages/keras/engine/topology.py", line 517, in __call__
    self.add_inbound_node(inbound_layers, node_indices, tensor_indices)
  File "/Users/davidslater/.virtualenvs/davidslater/lib/python2.7/site-packages/keras/engine/topology.py", line 571, in add_inbound_node
    Node.create_node(self, inbound_layers, node_indices, tensor_indices)
  File "/Users/davidslater/.virtualenvs/davidslater/lib/python2.7/site-packages/keras/engine/topology.py", line 155, in create_node
    output_tensors = to_list(outbound_layer.call(input_tensors[0], mask=input_masks[0]))
  File "/Users/davidslater/.virtualenvs/davidslater/lib/python2.7/site-packages/keras/layers/core.py", line 592, in call
    return self.function(x, **arguments)
  File "keras_bug.py", line 20, in <lambda>
    model.add(Lambda(lambda x: backend.sum(x, axis=1), output_shape=(128,)))
NameError: global name 'backend' is not defined

Please make sure that the boxes below are checked before you submit your issue. Thank you!

  • [X] Check that you are up-to-date with the master branch of Keras. You can update with:
    pip install git+git://github.com/fchollet/keras.git --upgrade --no-deps

  • [X] If running on Theano, check that you are up-to-date with the master branch of Theano. You can update with:
    pip install git+git://github.com/Theano/Theano.git --upgrade --no-deps

  • [X] Provide a link to a GitHub Gist of a Python script that can reproduce your issue (or just copy the script here if it is short).

stale

Most helpful comment

Yes, this is fixed/worked around by adding all unknown names to the custom_objects dict. For me I was using tf in a Lambda, but also custom objective losses which also were not found during load_model, so this fixed it for me:

model = keras.models.load_model('weights.h5', custom_objects={
    'tf':tf,
    'pix_error':pix_error,
    'range_error':range_error,
    'heat_error':heat_error,
})

All 12 comments

For me a similar thing happened with TensorFlow backend after loading a serialized model with a Lambda layer calling a backend function one_hot. The backend was imported as K as still the error is appearing.

x = Lambda(K.one_hot, arguments={'nb_classes': nb_classes}, output_shape=(dataset.feature_frames, nb_classes))(x)
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/models.py", line 128, in load_model
    model = model_from_config(model_config, custom_objects=custom_objects)
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/models.py", line 177, in model_from_config
    return layer_from_config(config, custom_objects=custom_objects)
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/utils/layer_utils.py", line 36, in layer_from_config
    return layer_class.from_config(config['config'])
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/engine/topology.py", line 2375, in from_config
    process_layer(layer_data)
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/engine/topology.py", line 2370, in process_layer
    layer(input_tensors[0])
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/engine/topology.py", line 514, in __call__                                                                                 self.add_inbound_node(inbound_layers, node_indices, tensor_indices)
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/engine/topology.py", line 572, in add_inbound_node
    Node.create_node(self, inbound_layers, node_indices, tensor_indices)
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/engine/topology.py", line 149, in create_node
    output_tensors = to_list(outbound_layer.call(input_tensors[0], mask=input_masks[0]))
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/layers/core.py", line 556, in call
    return self.function(x, **arguments)
  File "/home/bza/anaconda3/lib/python3.5/site-packages/keras/backend/tensorflow_backend.py", line 923, in one_hot
    return tf.one_hot(indices, depth=nb_classes, axis=-1)                                                                                                                            NameError: name 'tf' is not defined

WORKING WORKAROUND:

Importing the backend as K within the lambda function solved the problem as a workaround. It's not quite clean IMHO.

def one_hot(x, **arguments):
    from keras import backend as K
    return K.one_hot(x, **arguments)

input_encoded = Lambda(one_hot,
    arguments={'nb_classes': nb_classes},
    output_shape=(dataset.feature_frames, nb_classes))(input_int)

I was worried about a possible performance penalty (import in the function call). However, training time was almost the same in both cases (using K.one_hot directly and wrapped with import).

I would really like to see this issue resolved to the general case where you can load any model that utilizes a tensorflow layer.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 30 days if no further activity occurs, but feel free to re-open a closed issue if needed.

You can fix it by including backend as a custom object

model2 = model_from_json(model.to_json(), custom_objects={"backend": backend})

From my understanding, the reason behind this is that load_model model_from_json by default assumes everything declared within the model is contained in Keras, hence you have to hardwire everything else outside keras.

The second thing to note is that python modules are imported as singletons, so importing it twice has no effect. You can try changing K by backend and viceversa.

@antonimmo I tried your workaround but could not get it to work. Instead I used @bzamecnik's trick, which I guess forces Keras to import the tensorflow backend as the declaration is made in-line within the Lambda (so I presume it is interpreted by Keras when loaded?).

This thread saved my life. Thanks guys!

Yes, this is fixed/worked around by adding all unknown names to the custom_objects dict. For me I was using tf in a Lambda, but also custom objective losses which also were not found during load_model, so this fixed it for me:

model = keras.models.load_model('weights.h5', custom_objects={
    'tf':tf,
    'pix_error':pix_error,
    'range_error':range_error,
    'heat_error':heat_error,
})

This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 30 days if no further activity occurs, but feel free to re-open a closed issue if needed.

@sbhaaf as your describe, I succeeded in load_model from file with model which contains Lambda layer with keras.activations.softmax, thanks. Seems we'd take care of all functions/objects passed to Lambda layer as customized, even if it is part of Keras.

model = keras.models.load_model('weights.h5', custom_objects={ 'tf':tf, 'pix_error':pix_error, 'range_error':range_error, 'heat_error':heat_error, })

this one saves the day :-*

Closing as this is resolved

Sorry for reopening this, but I am unsure if using the custom_objects parameter will suffice in my case. Currently my goal is to compile a model in Python to then push it to a device and train it using the C++ API (Federated Learning).

However, this would mean that I would have to provide the custom Python object in my C++ code, right? Or is there another way to handle this?

As the commenters above, I am also experiencing this error when using a Lambda layer.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MarkVdBergh picture MarkVdBergh  路  3Comments

zygmuntz picture zygmuntz  路  3Comments

nryant picture nryant  路  3Comments

fredtcaroli picture fredtcaroli  路  3Comments

farizrahman4u picture farizrahman4u  路  3Comments