Keras: Missing global variable in saved model when using Lambda layer

Created on 23 Feb 2018  路  4Comments  路  Source: keras-team/keras

Hi,

I'm experiencing an issue when saving a model containing the Lambda layer. I can run the model, compile it, train it, save it. But when I try to load it again, I get an error.
The network does the convolution on each single channel of the image and performs a regression on a final scalar output. To slice the multi-channel tensor I use the Lambda layer, and the index of this layer is causing troubles when saving/loading the model

I attached a full script to replicate the issue. It does not make any sense with the cifar10 dataset, but it's just to replicate the issue! :smile:

import keras
from keras.models import Model
from keras.layers import Dense, Dropout, Activation, Flatten, Input, Concatenate, Lambda
from keras.layers import Conv2D, MaxPooling2D
import os
from keras.callbacks import ModelCheckpoint
import numpy as np

network_name = 'test_this_bug'

np.random.seed(10)

batch_size = 100
num_classes = 10
epochs = 10

(x_train, y_train),(x_test, y_test) = keras.datasets.cifar10.load_data()
Nchannels = x_train.shape[3]
img_rows = x_train.shape[1]
img_cols = x_train.shape[2]

#x_train = np.moveaxis(x_train,1,3)
#x_test = np.moveaxis(x_test,1,3)

main_input = Input(shape=(img_rows,img_cols,Nchannels))
channels = []
for i in range(Nchannels):
    # Slice the tensor getting the i-th channel
    x = Lambda(lambda x: x[:,:,:,i,None])(main_input)
    x = Conv2D(4, kernel_size=(3,3), strides=(2,2))(x)
    x = Activation('relu')(x)
    x = MaxPooling2D(pool_size=(2,2), strides=(2,2))(x)

    x = Conv2D(8, kernel_size=(3,3), strides=(2,2))(x)
    x = Activation('relu')(x)
    x = MaxPooling2D(pool_size=(2,2), strides=(2,2))(x)

    x = Dropout(0.1)(x)

    x = Flatten()(x)
    channels.append(x)

channels = Concatenate()(channels)
channels = Dense(128)(channels)
channels = Activation('relu')(channels)
channels = Dropout(0.2)(channels)

main_output = Dense(1)(channels)
main_output = Activation('relu')(main_output)

model = Model(inputs=main_input, outputs=main_output)

model.summary()

opt = keras.optimizers.RMSprop()

model.compile(optimizer=opt, loss='mean_squared_error', metrics=['accuracy'])

x_train = x_train.astype('float32')/255;
x_test = x_test.astype('float32')/255;

best_model_name = '%s_best.h5' % network_name

# Callbacks
filepath = os.path.join(os.getcwd(),best_model_name)
checkpoint = ModelCheckpoint(filepath, monitor='val_loss', verbose=1,
                                 save_best_only=True,period=2)

callbacks = [checkpoint]

model.fit(x_train,y_train,
         batch_size=batch_size, 
         epochs=epochs, 
         validation_data=(x_test,y_test),
         callbacks=callbacks)

After the script has finished, I simply open ipython and run:

import keras
import os
model = keras.models.load_model(os.path.join(os.getcwd(),'test_this_bug_best.h5'))

The result is this long error, referring to the variable _i_ of the Lambda layer:

In [5]: model = keras.models.load_model(os.path.join(os.getcwd(),'test_this_bug_best.h5'))
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-5-37356b91a563> in <module>()
----> 1 model = keras.models.load_model(os.path.join(os.getcwd(),'test_this_bug_best.h5'))

/mnt/storage/home/username/.local/lib/python2.7/site-packages/keras/models.pyc in load_model(filepath, custom_objects, compile)
    241             raise ValueError('No model found in config file.')
    242         model_config = json.loads(model_config.decode('utf-8'))
--> 243         model = model_from_config(model_config, custom_objects=custom_objects)
    244
    245         # set weights

/mnt/storage/home/username/.local/lib/python2.7/site-packages/keras/models.pyc in model_from_config(config, custom_objects)
    315                         'Maybe you meant to use '
    316                         '`Sequential.from_config(config)`?')
--> 317     return layer_module.deserialize(config, custom_objects=custom_objects)
    318
    319

/mnt/storage/home/username/.local/lib/python2.7/site-packages/keras/layers/__init__.pyc in deserialize(config, custom_objects)
     53                                     module_objects=globs,
     54                                     custom_objects=custom_objects,
---> 55                                     printable_module_name='layer')

/mnt/storage/home/username/.local/lib/python2.7/site-packages/keras/utils/generic_utils.pyc in deserialize_keras_object(identifier, module_objects, custom_objects, printable_module_name)
    142                 return cls.from_config(config['config'],
    143                                        custom_objects=dict(list(_GLOBAL_CUSTOM_OBJECTS.items()) +
--> 144                                                            list(custom_objects.items())))
    145             with CustomObjectScope(custom_objects):
    146                 return cls.from_config(config['config'])

/mnt/storage/home/username/.local/lib/python2.7/site-packages/keras/engine/topology.pyc in from_config(cls, config, custom_objects)
   2518                 if layer in unprocessed_nodes:
   2519                     for node_data in unprocessed_nodes.pop(layer):
-> 2520                         process_node(layer, node_data)
   2521
   2522         name = config.get('name')

/mnt/storage/home/username/.local/lib/python2.7/site-packages/keras/engine/topology.pyc in process_node(layer, node_data)
   2475             if input_tensors:
   2476                 if len(input_tensors) == 1:
-> 2477                     layer(input_tensors[0], **kwargs)
   2478                 else:
   2479                     layer(input_tensors, **kwargs)

/mnt/storage/home/username/.local/lib/python2.7/site-packages/keras/engine/topology.pyc in __call__(self, inputs, **kwargs)
    615
    616             # Actually call the layer, collecting output(s), mask(s), and shape(s).
--> 617             output = self.call(inputs, **kwargs)
    618             output_mask = self.compute_mask(inputs, previous_mask)
    619

/mnt/storage/home/username/.local/lib/python2.7/site-packages/keras/layers/core.pyc in call(self, inputs, mask)
    661         if has_arg(self.function, 'mask'):
    662             arguments['mask'] = mask
--> 663         return self.function(inputs, **arguments)
    664
    665     def compute_mask(self, inputs, mask=None):

/mnt/storage/home/username/.local/lib/python2.7/site-packages/keras/layers/core.pyc in <lambda>(x)
     27 for i in range(Nchannels):
     28     # Slice the tensor getting the i-th channel
---> 29     x = Lambda(lambda x: x[:,:,:,i,None])(main_input)
     30     x = Conv2D(4, kernel_size=(3,3), strides=(2,2))(x)
     31     x = Activation('relu')(x)

NameError: global name 'i' is not defined

In [6]: model = keras.models.load_model(os.path.join(os.getcwd(),'test_this_bug_best.h5'))

Some additional info:

  • Using a cluster
  • Running Keras 2.1.4 on Python 2.7.12
  • Using TensorFlow 1.2.0 with a Tesla P100-PCIE-16GB
  • OS: CentOS Linux release 7.3.1611
  • Kernel: Linux 3.10.0-514.10.2.el7.x86_64

Please make sure that the boxes below are checked before you submit your issue. If your issue is an implementation question, please ask your question on StackOverflow or join the Keras Slack channel and ask there instead of filing a GitHub 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/keras-team/keras.git --upgrade --no-deps

  • [x] If running on TensorFlow, check that you are up-to-date with the latest version. The installation instructions can be found here.

  • [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).

Most helpful comment

I guess my PR on Tensorflow hasn't been merged to keras.
https://github.com/tensorflow/tensorflow/pull/18926
I'll resubmit it here.

All 4 comments

Same issue with me

same here. This is a more simple example:

import numpy as np

from keras.models import load_model
from keras.models import Model
from keras.layers import Input, Lambda
from keras import backend as K

rep = 2
inp = Input([1])
out = Lambda(lambda x: K.repeat_elements(K.expand_dims(x, 1), rep, 1))(inp)
model = Model(inp, out)
X = np.array([[1], [2]])
print('results', model.predict(X))

model.save('test.h5')
del model

model = load_model('test.h5')
print('results', model.predict(X))

I see this (with some anonymization):

results [[[1.]
  [1.]]]

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-19-23a1dbe4ad39> in <module>()
     16 del model
     17 
---> 18 model = load_model('test.h5')
     19 print('results', model.predict(X))

/.../python3.6/site-packages/keras/engine/saving.py in load_model(filepath, custom_objects, compile)
    258             raise ValueError('No model found in config file.')
    259         model_config = json.loads(model_config.decode('utf-8'))
--> 260         model = model_from_config(model_config, custom_objects=custom_objects)
    261 
    262         # set weights

/.../lib/python3.6/site-packages/keras/engine/saving.py in model_from_config(config, custom_objects)
    332                         '`Sequential.from_config(config)`?')
    333     from ..layers import deserialize
--> 334     return deserialize(config, custom_objects=custom_objects)
    335 
    336 

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

/.../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'])

/.../lib/python3.6/site-packages/keras/engine/network.py in from_config(cls, config, custom_objects)
   1025                 if layer in unprocessed_nodes:
   1026                     for node_data in unprocessed_nodes.pop(layer):
-> 1027                         process_node(layer, node_data)
   1028 
   1029         name = config.get('name')

/.../lib/python3.6/site-packages/keras/engine/network.py in process_node(layer, node_data)
    984             # and building the layer if needed.
    985             if input_tensors:
--> 986                 layer(unpack_singleton(input_tensors), **kwargs)
    987 
    988         def process_layer(layer_data):

/.../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 

/.../lib/python3.6/site-packages/keras/layers/core.py in call(self, inputs, mask)
    680         if has_arg(self.function, 'mask'):
    681             arguments['mask'] = mask
--> 682         return self.function(inputs, **arguments)
    683 
    684     def compute_mask(self, inputs, mask=None):

/.../lib/python3.6/site-packages/keras/layers/core.py in <lambda>(x)
      8 rep = 2
      9 inp = Input([1])
---> 10 out = Lambda(lambda x: K.repeat_elements(K.expand_dims(x, 1), rep, 1))(inp)
     11 model = Model(inp, out)
     12 X = np.array([[1]])

NameError: name 'rep' is not defined

I guess my PR on Tensorflow hasn't been merged to keras.
https://github.com/tensorflow/tensorflow/pull/18926
I'll resubmit it here.

I came up with a silly fix for now. Basically, using the global variables as values for arbitrary arguments solved the scope issue:

def my_lambda(x, gvar1=gvar1, ...):
    return ...

out = Lambda(my_lambda)(inp)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

LuCeHe picture LuCeHe  路  3Comments

nryant picture nryant  路  3Comments

anjishnu picture anjishnu  路  3Comments

NancyZxll picture NancyZxll  路  3Comments

fredtcaroli picture fredtcaroli  路  3Comments