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)
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)
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)
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])
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])
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)
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)