From b8394f69d3b7d6be74e7d870266f0c6181eadbdd Mon Sep 17 00:00:00 2001 From: Eric Hunsberger Date: Wed, 4 Mar 2020 17:05:01 -0400 Subject: [PATCH] Remove problematic source tensor sorting This is no longer required, and is problematic for models that have an output that is used other places in the model (since the sorting puts all outputs at the end). Also ensure a better error for unconverted input tensor --- CHANGES.rst | 3 +++ nengo_dl/converter.py | 27 ++++++--------------------- nengo_dl/tests/test_converter.py | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8e13cb486..4051d9568 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -75,6 +75,8 @@ Release history - Added ``tensorflow-cpu`` distributions to installation checks (so Nengo DL will not attempt to reinstall TensorFlow if ``tensorflow-cpu`` is already installed). (`#142`_) +- Fixed bug when applying the Converter to Keras models that re-use intermediate + layers as output layers. (`#137`_) **Deprecated** @@ -88,6 +90,7 @@ Release history .. _#129: https://github.com/nengo/nengo-dl/pull/129 .. _#130: https://github.com/nengo/nengo-dl/pull/130 .. _#134: https://github.com/nengo/nengo-dl/pull/134 +.. _#137: https://github.com/nengo/nengo-dl/pull/137 .. _#139: https://github.com/nengo/nengo-dl/pull/139 .. _#140: https://github.com/nengo/nengo-dl/pull/140 .. _#142: https://github.com/nengo/nengo-dl/pull/142 diff --git a/nengo_dl/converter.py b/nengo_dl/converter.py index 77891b1ce..f04058e95 100644 --- a/nengo_dl/converter.py +++ b/nengo_dl/converter.py @@ -627,10 +627,7 @@ def get_input_obj(self, node_id, tensor_idx=0): input_layer, input_node_id, input_tensor_id = self.get_history(tensor) - if input_node_id in self.converter.layer_map[input_layer]: - return self.converter.layer_map[input_layer][input_node_id][input_tensor_id] - else: - return None + return self.converter.layer_map[input_layer][input_node_id][input_tensor_id] def _get_shape(self, input_output, node_id, include_batch=False): """ @@ -820,18 +817,6 @@ def convert(self, node_id): # that need to be built into the Nengo network source_tensors = self.trace_tensors(self.layer.outputs) - def sort_key(x): - # sort tensors so that order of model inputs/outputs is preserved - for i, y in enumerate(self.layer.inputs): - if x is y: - return -(len(self.layer.inputs) - i) - for i, y in enumerate(self.layer.outputs): - if x is y: - return i + 1 - return 0 - - source_tensors = sorted(source_tensors, key=sort_key) - for tensor in source_tensors: # look up the layer/node to be converted model_layer, model_node_id, _ = self.get_history(tensor) @@ -1467,11 +1452,11 @@ class ConvertInput(LayerConverter): """Convert ``tf.keras.layers.InputLayer`` to Nengo objects.""" def convert(self, node_id): - # if this input layer has an input obj, that means it is a passthrough - # (so we just return the input) - output = self.get_input_obj(node_id) - - if output is None: + try: + # if this input layer has an input obj, that means it is a passthrough + # (so we just return the input) + output = self.get_input_obj(node_id) + except KeyError: # not a passthrough input, so create input node shape = self.output_shape(node_id) if any(x is None for x in shape): diff --git a/nengo_dl/tests/test_converter.py b/nengo_dl/tests/test_converter.py index b99f7f88a..e494dfedc 100644 --- a/nengo_dl/tests/test_converter.py +++ b/nengo_dl/tests/test_converter.py @@ -701,3 +701,20 @@ def test_layer_dicts(): assert conv.outputs[x0].target is conv.layers[x0] assert conv.outputs[x1].target is conv.layers[x1] + + +def test_mid_model_output(Simulator): + """Check that converter supports output tensors from the middle of the model. + + Previous converter put output tensors last in build order, so having an output + tensor that needed to be built before non-output tensors was problematic. + https://github.com/nengo/nengo-dl/pull/137 + """ + + # model must have at least three layers, with one layer in between outputs + inp = tf.keras.Input(shape=(1,)) + x0 = tf.keras.layers.ReLU()(inp) + x1 = tf.keras.layers.ReLU()(x0) + x2 = tf.keras.layers.ReLU()(x1) + + _test_convert(inp, [x0, x2], inp_vals=[np.ones((4, 1))])