keras: Keras model cannot be loaded if it contains a Lambda layer calling tf.image.resize_images

My Keras model cannot be loaded if it contains a Lambda layer that calls tf.image.resize_images. The exact same model without said Lambda layer loads just fine (see code below).

The model was saved using model.save() and according to the error log, when the model is trying to load, a call to func_load() in keras/utils/generic_utils.py isn’t passed the right arguments: “TypeError: arg 4 (defaults) must be None or tuple”.

Here is the entire log:

Using TensorFlow backend.
Traceback (most recent call last):
  File "drive.py", line 83, in <module>
    model = load_model(args.model)
  File "/Users/alex/anaconda/envs/Keras/lib/python3.5/site-packages/keras/models.py", line 142, in load_model
    model = model_from_config(model_config, custom_objects=custom_objects)
  File "/Users/alex/anaconda/envs/Keras/lib/python3.5/site-packages/keras/models.py", line 193, in model_from_config
    return layer_from_config(config, custom_objects=custom_objects)
  File "/Users/alex/anaconda/envs/Keras/lib/python3.5/site-packages/keras/utils/layer_utils.py", line 42, in layer_from_config
    return layer_class.from_config(config['config'])
  File "/Users/alex/anaconda/envs/Keras/lib/python3.5/site-packages/keras/models.py", line 1090, in from_config
    layer = get_or_create_layer(conf)
  File "/Users/alex/anaconda/envs/Keras/lib/python3.5/site-packages/keras/models.py", line 1069, in get_or_create_layer
    layer = layer_from_config(layer_data)
  File "/Users/alex/anaconda/envs/Keras/lib/python3.5/site-packages/keras/utils/layer_utils.py", line 40, in layer_from_config
    custom_objects=custom_objects)
  File "/Users/alex/anaconda/envs/Keras/lib/python3.5/site-packages/keras/layers/core.py", line 682, in from_config
    function = func_load(config['function'], globs=globs)
  File "/Users/alex/anaconda/envs/Keras/lib/python3.5/site-packages/keras/utils/generic_utils.py", line 189, in func_load
    closure=closure)
TypeError: arg 4 (defaults) must be None or tuple

and here is the part of my model that causes the issue. The first Lambda layer is the problem (without it the model loads fine):

    model = Sequential()
    
    model.add(Cropping2D(cropping=((cr_top, cr_bot), (cr_lef, cr_rig)),
                         input_shape=(in_row, in_col, ch)))
    model.add(Lambda(tf.image.resize_images,
                     output_shape=(res_row, res_col, ch),
                     arguments={'size': (res_row, res_col)}))
    model.add(Lambda(lambda x: x/127.5 - 1.,
                     output_shape=(res_row, res_col, ch)))
    model.add(Convolution2D(24, 5, 5, subsample=(2, 2), border_mode="valid"))
    model.add(BatchNormalization(axis=3, momentum=0.99))
    model.add(ELU())

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 29 (3 by maintainers)

Most upvoted comments

Try using custom_objects argument in load_model (or model_from_json) function.

For your case load_model("model_path", custom_objects={"tf": tf}) after importing tensorflow as tf would do the job.

@lauphedo do you defined tf in the Lamba function? I found out if I import tensorflow as tf inside the function used by Lamba layer, and it is fine

got same error when loading a model contains Lambda

load_model(“model_path”, custom_objects={“tf”: tf}) did not work for me with keras 2.0.6 and python3.6

The problem is probably the extra Python lambda that you’re using. Just do this instead:

model.add(Lambda(resize_normalize, input_shape=(80,318,3), output_shape=(66, 200, 3)))

@lauphedo That’s another known issue, see #5088. One workaround is to import inside the lambda function.

(maybe) similar problem: trying to load a model that contains: model_nspect2mask.add(TimeDistributed(Lambda(lambda x: tf.log(tf.abs(x))), input_shape = (None,feat_num)))

get error:

NameError                                 Traceback (most recent call last)
<ipython-input-59-c7a565cc9dba> in <module>()
----> 1 model_nspect2mask = load_model(newexp_folder_path+model_filename)

/usr/local/lib/python2.7/dist-packages/keras/models.pyc in load_model(filepath, custom_objects)
    140         raise ValueError('No model found in config file.')
    141     model_config = json.loads(model_config.decode('utf-8'))
--> 142     model = model_from_config(model_config, custom_objects=custom_objects)
    143 
    144     # set weights

/usr/local/lib/python2.7/dist-packages/keras/models.pyc in model_from_config(config, custom_objects)
    191                         'Maybe you meant to use '
    192                         '`Sequential.from_config(config)`?')
--> 193     return layer_from_config(config, custom_objects=custom_objects)
    194 
    195 

/usr/local/lib/python2.7/dist-packages/keras/utils/layer_utils.pyc in layer_from_config(config, custom_objects)
     41                                        custom_objects=custom_objects)
     42     else:
---> 43         return layer_class.from_config(config['config'])
     44 
     45 

/usr/local/lib/python2.7/dist-packages/keras/models.pyc in from_config(cls, config, layer_cache)
   1084         else:
   1085             layer = get_or_create_layer(first_layer)
-> 1086             model.add(layer)
   1087 
   1088         for conf in config[1:]:

/usr/local/lib/python2.7/dist-packages/keras/models.pyc in add(self, layer)
    297                 else:
    298                     input_dtype = None
--> 299                 layer.create_input_layer(batch_input_shape, input_dtype)
    300 
    301             if len(layer.inbound_nodes) != 1:

/usr/local/lib/python2.7/dist-packages/keras/engine/topology.pyc in create_input_layer(self, batch_input_shape, input_dtype, name)
    399         # and create the node connecting the current layer
    400         # to the input layer we just created.
--> 401         self(x)
    402 
    403     def add_weight(self, shape, initializer, name=None,

/usr/local/lib/python2.7/dist-packages/keras/engine/topology.pyc in __call__(self, x, mask)
    570         if inbound_layers:
    571             # This will call layer.build() if necessary.
--> 572             self.add_inbound_node(inbound_layers, node_indices, tensor_indices)
    573             # Outputs were already computed when calling self.add_inbound_node.
    574             outputs = self.inbound_nodes[-1].output_tensors

/usr/local/lib/python2.7/dist-packages/keras/engine/topology.pyc in add_inbound_node(self, inbound_layers, node_indices, tensor_indices)
    633         # creating the node automatically updates self.inbound_nodes
    634         # as well as outbound_nodes on inbound layers.
--> 635         Node.create_node(self, inbound_layers, node_indices, tensor_indices)
    636 
    637     def get_output_shape_for(self, input_shape):

/usr/local/lib/python2.7/dist-packages/keras/engine/topology.pyc in create_node(cls, outbound_layer, inbound_layers, node_indices, tensor_indices)
    164 
    165         if len(input_tensors) == 1:
--> 166             output_tensors = to_list(outbound_layer.call(input_tensors[0], mask=input_masks[0]))
    167             output_masks = to_list(outbound_layer.compute_mask(input_tensors[0], input_masks[0]))
    168             # TODO: try to auto-infer shape

/usr/local/lib/python2.7/dist-packages/keras/layers/wrappers.pyc in call(self, inputs, mask)
    126             # (nb_samples * timesteps, ...)
    127             inputs = K.reshape(inputs, (-1,) + input_shape[2:])
--> 128             y = self.layer.call(inputs)  # (nb_samples * timesteps, ...)
    129             # (nb_samples, timesteps, ...)
    130             output_shape = self.get_output_shape_for(input_shape)

/usr/local/lib/python2.7/dist-packages/keras/layers/core.pyc in call(self, x, mask)
    638         if 'mask' in arg_spec.args:
    639             arguments['mask'] = mask
--> 640         return self.function(x, **arguments)
    641 
    642     def get_config(self):

/usr/local/lib/python2.7/dist-packages/keras/layers/core.pyc in <lambda>(x)
      8 
      9 # conversion to dB  # f = lambda x: tf.log(tf.abs(x))
---> 10 model_nspect2mask.add(TimeDistributed(Lambda(lambda x: tf.log(tf.abs(x))), input_shape = (None,feat_num)))
     11 
     12 # normalize per feature per batch

NameError: global name 'tf' is not defined ```

Like above, loading works if model does not have that Lambda layer. 

I working with keras-2.2.0 python3.6.4 @adwin5

def Repeat_layer(repeat_times, name):
    # import tensorflow as tf 
    return Lambda(lambda x: tf.tile(tf.expand_dims(x, 2),
                                    tf.stack([1, 1, repeat_times])),
                  name=name)

if I import tensorflow as tf inside the Repeat_layer, it will get error can't pickle module objects when I save model via model.save or model.to_json().

@1524045patrick
I import tensorflow as tf outside Repeat_layer i.e., global scope. And load my model via load_model("model_path", custom_objects={"tf": tf}) , it works for me.

Is this issue fixed? Same issue in TF. 2.x

Yes, as @antonimmo says, using custom_objects works, and it works for any other things that load_model complains about not being found e.g. custom loss functions.

Using tf version 2.4 I still have this problem

@adwin5, @allanzelener, @lauphedo ,

for me that workaround did not work when I am using the lamda layer like this:

model.add(Lambda(lambda x: resize_normalize(x), input_shape=(80,318,3), output_shape=(66, 200, 3)))

and my function looks like this. Itried both tensorflow and keras backend import explicite inside the function:

def resize_normalize(image):
    import tensorflow as tf
    #from keras.backend import tf as ktf
    #resize 
    resized = tf.image.resize_images(image, (66, 200))
    #normalize
    resized = resized/255.0 - 0.5

    return resized

When I load my model I got the following traceback:

image

@adwin5, @allanzelener Wow, this was fast and really helpful! Thank you very much! Before, I couln’t understand #5088

Interestingly, load_weights() performs well without the mentioned fix…

I have a very similar error with a Lambda layer. I am working with complex numbers and at some point need to consider the real part of my tensors.

Here is a minimal failing example:

from keras.layers import Input, Lambda
from keras.models import Model, load_model
import tensorflow as tf

complex_input = Input((None,), dtype='complex64')
x_real = Lambda(tf.math.real)(complex_input)
model = Model(inputs=complex_input, outputs=x_real)

model.save('tmp.hdf5')

load_model('tmp.hdf5', custom_objects={'tf.math.real': tf.math.real, 'tf': tf, 'tf.math': tf.math})

Which fails with:

TypeError                                 Traceback (most recent call last)
~/workspace/tests/venv/lib/python3.6/site-packages/keras/layers/core.py in wrapper(*args, **kwargs)
    179     try:
--> 180       return target(*args, **kwargs)
    181     except (TypeError, ValueError):

TypeError: 'str' object is not callable

During handling of the above exception, another exception occurred:

NameError                                 Traceback (most recent call last)
<ipython-input-1-883b5508b892> in <module>
      9 model.save('tmp.hdf5')
     10 
---> 11 load_model('tmp.hdf5', custom_objects={'tf.math.real': tf.math.real, 'tf': tf, 'tf.math': tf.math})

~/workspace/tests/venv/lib/python3.6/site-packages/keras/engine/saving.py in load_model(filepath, custom_objects, compile)
    417     f = h5dict(filepath, 'r')
    418     try:
--> 419         model = _deserialize_model(f, custom_objects, compile)
    420     finally:
    421         if opened_new_file:

~/workspace/tests/venv/lib/python3.6/site-packages/keras/engine/saving.py in _deserialize_model(f, custom_objects, compile)
    223         raise ValueError('No model found in config.')
    224     model_config = json.loads(model_config.decode('utf-8'))
--> 225     model = model_from_config(model_config, custom_objects=custom_objects)
    226     model_weights_group = f['model_weights']
    227 

~/workspace/tests/venv/lib/python3.6/site-packages/keras/engine/saving.py in model_from_config(config, custom_objects)
    456                         '`Sequential.from_config(config)`?')
    457     from ..layers import deserialize
--> 458     return deserialize(config, custom_objects=custom_objects)
    459 
    460 

~/workspace/tests/venv/lib/python3.6/site-packages/keras/layers/__init__.py in deserialize(config, custom_objects)
     53                                     module_objects=globs,
     54                                     custom_objects=custom_objects,
---> 55                                     printable_module_name='layer')

~/workspace/tests/venv/lib/python3.6/site-packages/keras/utils/generic_utils.py in deserialize_keras_object(identifier, module_objects, custom_objects, printable_module_name)
    143                     config['config'],
    144                     custom_objects=dict(list(_GLOBAL_CUSTOM_OBJECTS.items()) +
--> 145                                         list(custom_objects.items())))
    146             with CustomObjectScope(custom_objects):
    147                 return cls.from_config(config['config'])

~/workspace/tests/venv/lib/python3.6/site-packages/keras/engine/network.py in from_config(cls, config, custom_objects)
   1030                 if layer in unprocessed_nodes:
   1031                     for node_data in unprocessed_nodes.pop(layer):
-> 1032                         process_node(layer, node_data)
   1033 
   1034         name = config.get('name')

~/workspace/tests/venv/lib/python3.6/site-packages/keras/engine/network.py in process_node(layer, node_data)
    989             # and building the layer if needed.
    990             if input_tensors:
--> 991                 layer(unpack_singleton(input_tensors), **kwargs)
    992 
    993         def process_layer(layer_data):

~/workspace/tests/venv/lib/python3.6/site-packages/keras/engine/base_layer.py in __call__(self, inputs, **kwargs)
    455             # Actually call the layer,
    456             # collecting output(s), mask(s), and shape(s).
--> 457             output = self.call(inputs, **kwargs)
    458             output_mask = self.compute_mask(inputs, previous_mask)
    459 

~/workspace/tests/venv/lib/python3.6/site-packages/keras/layers/core.py in call(self, inputs, mask)
    685         if has_arg(self.function, 'mask'):
    686             arguments['mask'] = mask
--> 687         return self.function(inputs, **arguments)
    688 
    689     def compute_mask(self, inputs, mask=None):

~/workspace/tests/venv/lib/python3.6/site-packages/keras/layers/core.py in wrapper(*args, **kwargs)
    182       # Note: convert_to_eager_tensor currently raises a ValueError, not a
    183       # TypeError, when given unexpected types.  So we need to catch both.
--> 184       result = dispatch(wrapper, *args, **kwargs)
    185       if result is not OpDispatcher.NOT_SUPPORTED:
    186         return result

NameError: name 'dispatch' is not defined

The error is very cryptic because it points only to Keras lines, whereas the code areas referred to are actually in TensorFlow… That’s a first problem, but also as you can see using the custom_objects kwarg, like @antonimmo suggested, doesn’t help fixing the problem.

Any ideas on how to solve this? (as a side note, it’s not actually too much of a problem since I can just recreate the model and load the weights but it would be more convenient if I could just load the model).

Thx alot for commenting this question. So meanwhile I fixed it.

  • one resaon and there u are right was the lamda.
  • second fix is to define the resize_normalize() inside model architecture like shown below

def architecture():

def resize_normalize(image):
    import cv2
    from keras.backend import tf as ktf   
    
    # resize to width 200 and high 66 liek recommended
    # in the nvidia paper for the used CNN
    # image = cv2.resize(image, (66, 200)) #first try
    resized = ktf.image.resize_images(image, (32, 128))
    #normalize 0-1
    resized = resized/255.0 - 0.5

    return resized

model = Sequential()
model.add(Cropping2D(cropping=((60,20), (1,1)), input_shape=(160,320,3)))
model.add(Lambda(resize_normalize, input_shape=(160, 320, 3), output_shape=(32, 128, 3)))

… …

I’ve run into the same issue, here’s a small gist that reproduces the error.

A different error occurs when deserializing from yaml instead of json. I couldn’t get a Lambda wrapper for K.resize_images to work either.

Edit: The JSON errors seems to be because defaults is serialized as [None] but is expected to be either None or a tuple but not a list. I’ve added PR #5350 to resolve this.

The NameError issue is referenced in #5088 and has a workaround. Simply import what’s needed in a wrapped function. This gist fixes my example above.