question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Loading models with 2 layers sharing weights fails

See original GitHub issue

If model has shared weights and 2 layers calling each other with shared weights, it introduces a cyclic dependency.

z = keras.layers.Input(shape=(2,))
A = keras.layers.Dense(2)
B = keras.layers.Dense(2)
output = A(B(A(B(z))))
M = keras.models.Model(input=z, output=output)
M.save("test.hd5")
keras.models.load_model("test.hd5")

Then keras/engine/topology.py:2362 assert inbound_layer_name in created_layers, 'Missing layer: %s' % inbound_layer_name throws

AssertionError: Missing layer: dense_1

Is there a simple fix? Worth noting that M.load_weights(“test.hd5”) does work, but unfortunately I need load_model to work

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:9 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
kudkudakcommented, Oct 23, 2016

Here is a solution I hacked for that. Fix is changing keras.engine.topology.Container.from_config to step iteratively through graph. Each run of step_through_graph (through whole network) will call at least one layer so after all_calls it should have called all layers. I am pretty sure there is a nicer solution, but that was fast to code 😃 I think each application of the layer should be stored as a separate entry in config, then from_config could be left almost unchanged, but that is hard to introduce as it seems to be a breaking change.

def from_config(cls, config, custom_objects={}):
    '''Instantiates a Model from its config (output of `get_config()`).
    TODO: support for custom objects

        TODO: shouldnt modify config in place
    '''
    from keras.utils.layer_utils import layer_from_config

    # layer instances created during
    # the graph reconstruction process
    created_layers = {}

    def step_through_graph(layer_data):
        # instantiate layer
        layer_name = layer_data['name']
        if layer_name not in created_layers:
            layer = layer_from_config(layer_data, custom_objects=custom_objects)
            created_layers[layer_name] = layer
        else:
            layer = created_layers[layer_name]

        # already processed?
        inbound_nodes_data = layer_data['inbound_nodes']
        if len(inbound_nodes_data) == 0:
            return True

        # try to do single step
        node_data = inbound_nodes_data[0]
        input_tensors = []
        for input_data in node_data:
            inbound_layer_name, inbound_node_index, inbound_tensor_index = input_data

            if inbound_layer_name not in created_layers \
                    or len(created_layers[inbound_layer_name].inbound_nodes) <= inbound_node_index:
                return False
            else:
                inbound_layer = created_layers[inbound_layer_name]
                inbound_node = inbound_layer.inbound_nodes[inbound_node_index]
                input_tensors.append(inbound_node.output_tensors[inbound_tensor_index])

        # call layer on its inputs, thus creating the node
        # and building the layer if needed
        if input_tensors:
            if len(input_tensors) == 1:
                layer(input_tensors[0])
            else:
                layer(input_tensors)

        # Ok, all good, pop it
        inbound_nodes_data.pop(0)
        return True


    # Just try iteratively, poping shared copies from the stack
    all_calls = sum(map(lambda layer: len(layer['inbound_nodes']), config['layers']))
    for i in range(all_calls): # Each step is guaranteed to make a progress
        for layer_data in config['layers']:
            result = step_through_graph(layer_data)
    assert result == True, "Each run of step_through_graph should progress at least once"

    name = config.get('name')
    input_tensors = []
    output_tensors = []
    for layer_data in config['input_layers']:
        layer_name, node_index, tensor_index = layer_data
        assert layer_name in created_layers
        layer = created_layers[layer_name]
        layer_output_tensors = layer.inbound_nodes[node_index].output_tensors
        input_tensors.append(layer_output_tensors[tensor_index])

    for layer_data in config['output_layers']:
        layer_name, node_index, tensor_index = layer_data
        assert layer_name in created_layers
        layer = created_layers[layer_name]
        layer_output_tensors = layer.inbound_nodes[node_index].output_tensors
        output_tensors.append(layer_output_tensors[tensor_index])

    return cls(input=input_tensors, output=output_tensors, name=name)
0reactions
fcholletcommented, Jul 17, 2017

@kudkudak note that you should have sent a PR rather than leaving a code fix in an issues thread… Feel free to check out my fix and compare to yours. I’m sure mine can be improve.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Unable to load and use multiple keras models - Stack Overflow
There is a serious bug when you try to load multiple weight files in the same script. The above answer doesn't solve this....
Read more >
How to properly save and load an intermediate model in Keras?
Try to save the model to JSON, and the weights in HDF5 format with save_weights() . # save the model model_json = model_2.to_json()...
Read more >
The Functional API | TensorFlow Core
The functional API can handle models with non-linear topology, shared layers, and even multiple inputs or outputs.
Read more >
Working With The Lambda Layer in Keras - Paperspace Blog
In this tutorial we'll cover how to use the Lambda layer in Keras to build, save, and load models which perform custom operations...
Read more >
Serialization and saving - Keras
In order to save/load a model with custom-defined layers, or a subclassed model, you should overwrite the get_config and optionally from_config ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found