Example #1
0
    def test_multi_input_node(self):
        # test multi-input layer
        a = DummyTensor()
        b = DummyTensor()

        dense = DummyLayer()
        a_2 = DummyTensor()
        node_module.Node(layer=dense, call_args=(a, ), outputs=a_2)
        b_2 = DummyTensor()
        node_module.Node(layer=dense, call_args=(b, ), outputs=b_2)

        concat_layer = DummyLayer()
        merged = DummyTensor()
        node_module.Node(layer=concat_layer,
                         call_args=([a_2, b_2], ),
                         outputs=merged)

        merge_layer, merge_node_index, merge_tensor_index = merged._keras_history

        self.assertEqual(merge_node_index, 0)
        self.assertEqual(merge_tensor_index, 0)

        self.assertLen(merge_layer._inbound_nodes, 1)
        self.assertLen(merge_layer._outbound_nodes, 0)

        self.assertLen(merge_layer._inbound_nodes[0].input_tensors, 2)
        self.assertEqual(merge_layer._inbound_nodes[0].input_tensors,
                         [a_2, b_2])
        self.assertLen(merge_layer._inbound_nodes[0].inbound_layers, 2)
Example #2
0
    def test_arg_and_kwarg_mix(self):
        input_layer = DummyLayer()
        input_layer_2 = DummyLayer()
        a = DummyTensor()
        node_a = node_module.Node(layer=input_layer, outputs=a)
        b = DummyTensor()
        node_b = node_module.Node(layer=input_layer_2, outputs=b)

        arg_2 = DummyTensor()
        arg_3 = DummyTensor()
        node_c = node_module.Node(layer=input_layer, outputs=arg_3)

        kwarg_x = DummyTensor()
        kwarg_y = DummyTensor()
        node_d = node_module.Node(layer=input_layer, outputs=kwarg_y)

        merge_layer = DummyLayer()
        merged = DummyTensor()
        node = node_module.Node(layer=merge_layer,
                                call_args=([a, b], arg_2, arg_3),
                                call_kwargs={
                                    'x': kwarg_x,
                                    'y': kwarg_y
                                },
                                outputs=merged)

        merge_layer, merge_node_index, merge_tensor_index = merged._keras_history

        # Check the saved call args/kwargs
        self.assertEqual(([a, b], arg_2, arg_3), node.call_args)
        self.assertEqual({'x': kwarg_x, 'y': kwarg_y}, node.call_kwargs)

        # Only the inputs that were produced by input nodes should appear in
        # keras_tensors
        self.assertEqual({a, b, arg_3, kwarg_y}, set(node.keras_inputs))
        self.assertEqual(set(node.parent_nodes),
                         {node_a, node_b, node_c, node_d})

        # Check the layer wirings
        self.assertEqual(merge_node_index, 0)
        self.assertEqual(merge_tensor_index, 0)
        self.assertLen(merge_layer._inbound_nodes, 1)
        self.assertLen(merge_layer._outbound_nodes, 0)
        self.assertLen(input_layer._outbound_nodes, 3)
        self.assertLen(input_layer_2._outbound_nodes, 1)

        # The 'backwards compatibility' attributes should only check the
        # first call argument
        self.assertLen(merge_layer._inbound_nodes[0].input_tensors, 2)
        self.assertEqual(merge_layer._inbound_nodes[0].input_tensors, [a, b])
        self.assertLen(merge_layer._inbound_nodes[0].inbound_layers, 2)
Example #3
0
    def test_chained_node_construction(self):
        # test basics
        a = DummyTensor(shape=(None, 32))
        b = DummyTensor(shape=(None, 32))

        a_layer = DummyLayer()
        node = node_module.Node(a_layer, outputs=a)
        self.assertEqual(node.outbound_layer, a_layer)

        self.assertTrue(node.is_input)
        self.assertListEqual(node.inbound_layers, [])
        self.assertListEqual(node.input_tensors, [a])
        self.assertListEqual(node.input_shapes, [(None, 32)])
        self.assertListEqual(node.output_tensors, [a])
        self.assertListEqual(node.output_shapes, [(None, 32)])

        b_layer = DummyLayer()
        node_module.Node(b_layer, outputs=b)

        dense = DummyLayer()
        a_2 = DummyTensor()
        node_a = node_module.Node(layer=dense, call_args=(a, ), outputs=a_2)
        b_2 = DummyTensor()
        node_b = node_module.Node(layer=dense, call_args=(b, ), outputs=b_2)

        # test the node attributes
        self.assertFalse(node_a.is_input)
        self.assertFalse(node_b.is_input)
        self.assertEqual(node_a.call_args, (a, ))
        self.assertEqual(node_a.call_kwargs, {})
        self.assertEqual(node_a.outputs, a_2)

        # Test the layer wiring
        self.assertLen(dense._inbound_nodes, 2)
        self.assertLen(dense._outbound_nodes, 0)
        self.assertEqual(dense._inbound_nodes, [node_a, node_b])
        self.assertEqual(dense._inbound_nodes[0].inbound_layers, a_layer)
        self.assertEqual(dense._inbound_nodes[0].outbound_layer, dense)
        self.assertEqual(dense._inbound_nodes[1].inbound_layers, b_layer)
        self.assertEqual(dense._inbound_nodes[1].outbound_layer, dense)
        self.assertIs(dense._inbound_nodes[0].input_tensors, a)
        self.assertIs(dense._inbound_nodes[1].input_tensors, b)
Example #4
0
    def __init__(self, label=None, min_value=None, max_value=None, **kwargs):
        if not label:
            prefix = 'C'
            name = prefix + '_' + str(backend.get_uid(prefix))
        else:
            name = label

        dtype = backend.floatx()

        super(ConstantLayer, self).__init__(dtype=dtype, name=name)

        # backend.random_uniform_variable(shape=(20,), low=[0.]*20, high=[1.]*20)
        self._c = self.add_weight(name=label,
                                  shape=(1, len(min_value)),
                                  initializer=K.initializers.RandomUniform(
                                      minval=min_value, maxval=max_value),
                                  trainable=True)
        self.is_placeholder = False
        self.built = True
        self._batch_input_shape = (len(min_value), )
        #self.batch_size = len(min_value)

        #graph = backend.get_graph()
        #with graph.as_default():
        #    fake_input_tensor = backend.constant(backend.get_value(self._c))
        # Create an input node to add to self.outbound_node
        # and set output_tensors' _keras_history.
        fake_input_tensor = self._c
        fake_input_tensor._keras_history = base_layer.KerasHistory(self, 0, 0)
        fake_input_tensor._keras_mask = None
        fake_input_tensor._ltn_doms = []

        node_module.Node(self,
                         inbound_layers=[],
                         node_indices=[],
                         tensor_indices=[],
                         input_tensors=[fake_input_tensor],
                         output_tensors=[fake_input_tensor])
Example #5
0
    def __init__(self,
                 input_shape=None,
                 batch_size=None,
                 dtype=None,
                 input_tensor=None,
                 sparse=False,
                 name=None,
                 ragged=False,
                 **kwargs):
        strategy = distribution_strategy_context.get_strategy()
        if strategy and batch_size is not None and \
            distributed_training_utils.global_batch_size_supported(strategy):
            if batch_size % strategy.num_replicas_in_sync != 0:
                raise ValueError(
                    'The `batch_size` argument value {} cannot be '
                    'divisible by number of replicas {}'.format(
                        batch_size, strategy.num_replicas_in_sync))
            batch_size = batch_size // strategy.num_replicas_in_sync

        if 'batch_input_shape' in kwargs:
            batch_input_shape = kwargs.pop('batch_input_shape')
            if input_shape and batch_input_shape:
                raise ValueError('Only provide the input_shape OR '
                                 'batch_input_shape argument to '
                                 'InputLayer, not both at the same time.')
            batch_size = batch_input_shape[0]
            input_shape = batch_input_shape[1:]
        if kwargs:
            raise ValueError('Unrecognized keyword arguments:', kwargs.keys())

        if not name:
            prefix = 'input'
            name = prefix + '_' + str(backend.get_uid(prefix))

        if not dtype:
            if input_tensor is None:
                dtype = backend.floatx()
            else:
                dtype = backend.dtype(input_tensor)
        elif input_tensor is not None and input_tensor.dtype != dtype:
            raise ValueError(
                '`input_tensor.dtype` differs from `dtype`: %s vs. %s' %
                (input_tensor.dtype, dtype))
        super(InputLayer, self).__init__(dtype=dtype, name=name)
        self.built = True
        self.sparse = sparse
        self.batch_size = batch_size
        self.supports_masking = True

        if isinstance(input_shape, tensor_shape.TensorShape):
            input_shape = tuple(input_shape.as_list())
        elif isinstance(input_shape, int):
            input_shape = (input_shape, )

        if input_tensor is None:
            if input_shape is not None:
                batch_input_shape = (batch_size, ) + tuple(input_shape)
            else:
                batch_input_shape = None
            graph = backend.get_graph()
            with graph.as_default():
                input_tensor = backend.placeholder(shape=batch_input_shape,
                                                   dtype=dtype,
                                                   name=self.name,
                                                   sparse=sparse,
                                                   ragged=ragged)

            self.is_placeholder = True
            self._batch_input_shape = batch_input_shape
        else:
            if not tf_utils.is_symbolic_tensor(input_tensor):
                raise ValueError(
                    'You should not pass an EagerTensor to `Input`. '
                    'For example, instead of creating an '
                    'InputLayer, you should instantiate your model and '
                    'directly call it on your input.')
            self.is_placeholder = False
            self._batch_input_shape = tuple(input_tensor.shape.as_list())

        # Create an input node to add to self.outbound_node
        # and set output_tensors' _keras_history.
        input_tensor._keras_history = base_layer.KerasHistory(self, 0, 0)
        input_tensor._keras_mask = None
        node_module.Node(self,
                         inbound_layers=[],
                         node_indices=[],
                         tensor_indices=[],
                         input_tensors=[input_tensor],
                         output_tensors=[input_tensor])
Example #6
0
    def __init__(self,
                 input_shape=None,
                 batch_size=None,
                 dtype=None,
                 input_tensor=None,
                 sparse=None,
                 name=None,
                 ragged=None,
                 type_spec=None,
                 **kwargs):
        self._init_input_shape = input_shape
        self._init_batch_size = batch_size
        self._init_dtype = dtype
        self._init_sparse = sparse
        self._init_ragged = ragged
        self._init_type_spec = type_spec

        strategy = distribution_strategy_context.get_strategy()
        if strategy and batch_size is not None and \
            distributed_training_utils.global_batch_size_supported(strategy):
            if batch_size % strategy.num_replicas_in_sync != 0:
                raise ValueError(
                    'The `batch_size` argument ({}) must be divisible by '
                    'the number of replicas ({})'.format(
                        batch_size, strategy.num_replicas_in_sync))
            batch_size = batch_size // strategy.num_replicas_in_sync

        if 'batch_input_shape' in kwargs:
            batch_input_shape = kwargs.pop('batch_input_shape')
            if input_shape and batch_input_shape:
                raise ValueError('Only provide the input_shape OR '
                                 'batch_input_shape argument to '
                                 'InputLayer, not both at the same time.')
            batch_size = batch_input_shape[0]
            input_shape = batch_input_shape[1:]
        if kwargs:
            raise ValueError('Unrecognized keyword arguments:', kwargs.keys())

        if sparse and ragged:
            raise ValueError(
                'Cannot set both sparse and ragged to True in a Keras input.')

        if not name:
            prefix = 'input'
            name = prefix + '_' + str(backend.get_uid(prefix))

        if not dtype:
            if input_tensor is None:
                dtype = backend.floatx()
            else:
                dtype = backend.dtype(input_tensor)
        elif input_tensor is not None and input_tensor.dtype != dtype:
            raise ValueError(
                '`input_tensor.dtype` differs from `dtype`: %s vs. %s' %
                (input_tensor.dtype, dtype))
        super(InputLayer, self).__init__(dtype=dtype, name=name)
        self.built = True
        self.sparse = True if sparse else False
        self.ragged = True if ragged else False
        self.batch_size = batch_size
        self.supports_masking = True

        if isinstance(input_shape, tensor_shape.TensorShape):
            input_shape = tuple(input_shape.as_list())
        elif isinstance(input_shape, int):
            input_shape = (input_shape, )

        if type_spec is not None:
            args_that_must_be_none = [
                ('(input_)shape', self._init_input_shape),
                ('batch_size', self._init_batch_size),
                ('dtype', self._init_dtype),
                ('input_tensor', input_tensor),
                ('sparse', self._init_sparse),
                ('ragged', self._init_ragged),
            ]
            for arg_name, arg in args_that_must_be_none:
                _assert_other_arg_none(arg_name, arg)
            if not keras_tensor.keras_tensors_enabled():
                raise ValueError(
                    'Creating Keras inputs from a type_spec is only '
                    'supported when eager execution is enabled.')
            input_tensor = keras_tensor.keras_tensor_from_type_spec(type_spec)
            if isinstance(input_tensor, keras_tensor.SparseKerasTensor):
                self.sparse = True
            if isinstance(input_tensor, keras_tensor.RaggedKerasTensor):
                self.ragged = True
            self.is_placeholder = True
            try:
                self._batch_input_shape = tuple(input_tensor.shape.as_list())
            except ValueError:
                # If the shape cannot be represented as a tuple (e.g. unknown rank)
                self._batch_input_shape = None
        elif input_tensor is None:
            if input_shape is not None:
                batch_input_shape = (batch_size, ) + tuple(input_shape)
            else:
                batch_input_shape = None
            graph = backend.get_graph()
            with graph.as_default():
                input_tensor = backend.placeholder(shape=batch_input_shape,
                                                   dtype=dtype,
                                                   name=self.name,
                                                   sparse=sparse,
                                                   ragged=ragged)

            self.is_placeholder = True
            self._batch_input_shape = batch_input_shape
        else:
            if keras_tensor.keras_tensors_enabled():
                if not isinstance(input_tensor, keras_tensor.KerasTensor):
                    input_tensor = keras_tensor.keras_tensor_from_tensor(
                        input_tensor)
            else:
                if not tf_utils.is_symbolic_tensor(input_tensor):
                    raise ValueError(
                        'You should not pass an EagerTensor to `Input`. '
                        'For example, instead of creating an '
                        'InputLayer, you should instantiate your model and '
                        'directly call it on your input.')
            self.is_placeholder = False
            try:
                self._batch_input_shape = tuple(input_tensor.shape.as_list())
            except ValueError:
                # If the shape cannot be represented as a tuple (e.g. unknown rank)
                self._batch_input_shape = None
        # Create an input node.
        input_tensor._keras_mask = None
        node_module.Node(layer=self, outputs=input_tensor)

        # Store type spec
        if isinstance(input_tensor, keras_tensor.KerasTensor) or (
                tf_utils.is_extension_type(input_tensor)):
            self._type_spec = input_tensor._type_spec  # pylint: disable=protected-access
        else:
            self._type_spec = tensor_spec.TensorSpec(shape=input_tensor.shape,
                                                     dtype=input_tensor.dtype,
                                                     name=self.name)
Example #7
0
    def _init_graph_network(self, inputs, outputs, name=None, **kwargs):
        generic_utils.validate_kwargs(
                kwargs, {'trainable'},
                'Functional models may only specify `name` and `trainable` keyword '
                'arguments during initialization. Got an unexpected argument:')
        # Normalize and set self.inputs, self.outputs.
        if isinstance(inputs, list) and len(nest.flatten(inputs)) == 1:
            inputs = inputs[0]
        if isinstance(outputs, list) and len(nest.flatten(outputs)) == 1:
            outputs = outputs[0]
        self._nested_outputs = outputs
        self._nested_inputs = inputs
        self.inputs = nest.flatten(inputs)
        self.outputs = nest.flatten(outputs)

        if any(not hasattr(tensor, '_keras_history') for tensor in self.outputs):
            base_layer_utils.create_keras_history(self._nested_outputs)

        self._base_init(name=name, **kwargs)
        self._validate_graph_inputs_and_outputs()

        # A Network does not create weights of its own, thus it is already
        # built.
        self.built = True
        self._compute_output_and_mask_jointly = True
        self._is_graph_network = True
        # `_expects_training_arg` is True since the `training` argument is always
        # present in the signature of the `call` method of a graph network.
        self._expects_training_arg = True
        self._expects_mask_arg = True
        # A graph network does not autocast inputs, as its layers will cast them
        # instead.
        self._autocast = False

        self._input_layers = []
        self._output_layers = []
        self._input_coordinates = []
        self._output_coordinates = []

        self._supports_ragged_inputs = None

        # This is for performance optimization when calling the Network on new
        # inputs. Every time the Network is called on a set on input tensors,
        # we compute the output tensors, output masks and output shapes in one pass,
        # then cache them here. When any of these outputs is queried later, we
        # retrieve it from there instead of recomputing it.
        self._output_mask_cache = {}
        self._output_tensor_cache = {}
        self._output_shape_cache = {}

        # Build self._output_layers:
        for x in self.outputs:
            layer, node_index, tensor_index = x._keras_history    # pylint: disable=protected-access
            self._output_layers.append(layer)
            self._output_coordinates.append((layer, node_index, tensor_index))

        # Build self._input_layers:
        for x in self.inputs:
            layer, node_index, tensor_index = x._keras_history    # pylint: disable=protected-access
            # It's supposed to be an input layer, so only one node
            # and one tensor output.
            assert node_index == 0
            assert tensor_index == 0
            self._input_layers.append(layer)
            self._input_coordinates.append((layer, node_index, tensor_index))

        # Keep track of the network's nodes and layers.
        nodes, nodes_by_depth, layers, _ = _map_graph_network(
                self.inputs, self.outputs)
        self._network_nodes = nodes
        self._nodes_by_depth = nodes_by_depth
        self._layers = layers
        self._layer_call_argspecs = {}
        for layer in self._layers:
            self._layer_call_argspecs[layer] = tf_inspect.getfullargspec(layer.call)
            layer._attribute_sentinel.add_parent(self._attribute_sentinel)

        self._track_layers(layers)

        # Create the node linking internal inputs to internal outputs.
        node_module.Node(
                outbound_layer=self,
                inbound_layers=[],
                node_indices=[],
                tensor_indices=[],
                input_tensors=self._nested_inputs,
                output_tensors=self._nested_outputs)

        # Build self.input_names and self.output_names.
        self._set_output_names()
        self.input_names = []
        self._feed_input_names = []
        self._feed_inputs = []
        self._feed_input_shapes = []
        for layer in self._input_layers:
            self.input_names.append(layer.name)
            if layer.is_placeholder:
                self._feed_input_names.append(layer.name)
                # Use batch_input_shape here because non-eager composite tensors may not
                # have a shape attribute that's meaningful (sparse, for instance, has
                # a tensor that's non-constant and needs to be fed). This means that
                # input layers that create placeholders will need to have the
                # batch_input_shape attr to allow for input shape validation.
                self._feed_input_shapes.append(layer._batch_input_shape)
                self._feed_inputs.append(layer.input)