Beispiel #1
0
  def __init__(self,
               input_shape=None,
               batch_size=None,
               dtype=dtypes.float32,
               input_tensor=None,
               sparse=False,
               name=None):
    super(InputLayer, self).__init__(dtype=dtype, name=name)
    self.built = True
    self.sparse = sparse
    self.batch_size = batch_size

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

    if input_tensor is None:
      if input_shape is not None:
        batch_input_shape = (batch_size,) + tuple(input_shape)
      else:
        batch_input_shape = None

      if context.in_eager_mode():
        # In eager mode, create a temporary placeholder to call the layer on.
        input_tensor = base._DeferredTensor(  # pylint: disable=protected-access
            shape=batch_input_shape,
            dtype=dtype,
            name=self.name)
      else:
        # In graph mode, create a graph placeholder to call the layer on.
        if sparse:
          input_tensor = array_ops.sparse_placeholder(
              shape=batch_input_shape,
              dtype=dtype,
              name=self.name)
        else:
          input_tensor = array_ops.placeholder(
              shape=batch_input_shape,
              dtype=dtype,
              name=self.name)

      # For compatibility with Keras API.
      self.is_placeholder = True
      self._batch_input_shape = batch_input_shape
    else:
      # For compatibility with Keras API.
      self.is_placeholder = False
      self._batch_input_shape = tuple(input_tensor.get_shape().as_list())

    # Create an input node to add to self.outbound_node
    # and set output_tensors' _keras_history.
    input_tensor._keras_history = (self, 0, 0)  # pylint: disable=protected-access
    base.Node(
        self,
        inbound_layers=[],
        node_indices=[],
        tensor_indices=[],
        input_tensors=[input_tensor],
        output_tensors=[input_tensor])
Beispiel #2
0
    def __init__(self, inputs, outputs, name=None):  # pylint: disable=super-init-not-called
        if context.in_eager_mode():
            # TODO(fchollet): check that all inputs and outputs are DeferredTensors.
            pass

        self._init_set_name(name)
        self._activity_regularizer = None
        with vs.variable_scope(None,
                               default_name=self._base_name) as captured_scope:
            self._scope = captured_scope
        call_fn_args = estimator_util.fn_args(self.call)
        self._compute_previous_mask = ('mask' in call_fn_args
                                       or hasattr(self, 'compute_mask'))
        self._call_has_scope_arg = 'scope' in call_fn_args

        # This acts just like the `trainable` attribute of any layer instance.
        # It does not affect users of the underlying layers, only users of the
        # GraphNetwork instance.
        self.trainable = True
        # A GraphNetwork does not create weights of its own, thus it is already
        # built.
        self.built = True
        # A GraphNetwork does not create weights of its own, thus has no dtype.
        self._dtype = None
        # The following are implemented as property functions:
        # self.trainable_weights
        # self.non_trainable_weights
        # self.input_spec

        # Private attributes to implement compatibility with Layer.
        self._per_input_losses = {}
        self._per_input_updates = {}
        self._updates = []
        self._losses = []
        self._scope = None
        self._reuse = None
        self._graph = ops.get_default_graph()

        # GraphNetwork-specific properties.
        if isinstance(inputs, (list, tuple)):
            self.inputs = list(inputs)  # Tensor or list of tensors.
        else:
            self.inputs = [inputs]
        if isinstance(outputs, (list, tuple)):
            self.outputs = list(outputs)
        else:
            self.outputs = [outputs]
        # All layers in order of horizontal graph traversal.
        # Entries are unique. Includes input and output layers.
        self.layers = []

        # Check for redundancy in inputs.
        if len(set(self.inputs)) != len(self.inputs):
            raise ValueError('The list of inputs passed to the model '
                             'is redundant. '
                             'All inputs should only appear once.'
                             ' Found: ' + str(self.inputs))

        # # List of initial layers (1 to 1 mapping with self.inputs,
        # # hence the same layer might appear twice)
        # self._input_layers = []
        # self._input_layers_node_indices = []
        # self._input_layers_tensor_indices = []
        # # list of layers (1 to 1 mapping with self.inputs,
        # # hence the same layer might appear twice)
        # self._output_layers = []
        # self._output_layers_node_indices = []
        # self._output_layers_tensor_indices = []

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

        # This is for performance optimization when calling the GraphNetwork on new
        # inputs. Every time the GraphNetwork 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 = {}

        # User-provided arguments validation.
        for x in self.inputs:
            # Check that x has appropriate `_keras_history` metadata.
            if not hasattr(x, '_keras_history'):
                cls_name = self.__class__.__name__
                raise ValueError('Input tensors to a ' + cls_name + ' ' +
                                 'must come from `tf.layers.Input`. '
                                 'Received: ' + str(x) +
                                 ' (missing previous layer metadata).')
            # Check that x is an input tensor.
            # pylint: disable=protected-access
            layer, node_index, tensor_index = x._keras_history
            if len(layer._inbound_nodes) > 1 or (
                    layer._inbound_nodes
                    and layer._inbound_nodes[0].inbound_layers):
                cls_name = self.__class__.__name__
                logging.warning(
                    cls_name + ' inputs must come from '
                    '`tf.layers.Input` (thus holding past layer metadata), '
                    'they cannot be the output of '
                    'a previous non-Input layer. '
                    'Here, a tensor specified as '
                    'input to "' + self.name + '" was not an Input tensor, '
                    'it was generated by layer ' + layer.name + '.\n'
                    'Note that input tensors are '
                    'instantiated via `tensor = tf.layers.Input(shape)`.\n'
                    'The tensor that caused the issue was: ' + str(x.name))
            # pylint: enable=protected-access
        for x in self.outputs:
            if not hasattr(x, '_keras_history'):
                cls_name = self.__class__.__name__
                raise ValueError(
                    'Output tensors to a ' + cls_name + ' must be '
                    'the output of a TensorFlow `Layer` '
                    '(thus holding past layer metadata). Found: ' + str(x))

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

        # Network_nodes: set of nodes included in the graph
        # (not all nodes included in the layers
        # are relevant to the current graph).
        network_nodes = set()  # ids of all nodes relevant to the GraphNetwork
        nodes_depths = {}  # dict {node: depth value}
        layers_depths = {}  # dict {layer: depth value}
        layer_indices = {}  # dict {layer: index in traversal}
        nodes_in_decreasing_depth = []

        def build_map_of_graph(tensor, finished_nodes, nodes_in_progress,
                               layer, node_index, tensor_index):
            """Builds a map of the graph of layers.

      This recursively updates the map `layer_indices`,
      the list `nodes_in_decreasing_depth` and the set `network_nodes`.

      Arguments:
          tensor: Some tensor in a graph.
          finished_nodes: Set of nodes whose subgraphs have been traversed
              completely. Useful to prevent duplicated work.
          nodes_in_progress: Set of nodes that are currently active on the
              recursion stack. Useful to detect cycles.
          layer: Layer from which `tensor` comes from. If not provided,
              will be obtained from `tensor._keras_history`.
          node_index: Node index from which `tensor` comes from.
          tensor_index: Tensor_index from which `tensor` comes from.

      Raises:
          ValueError: if a cycle is detected.
      """
            node = layer._inbound_nodes[node_index]  # pylint: disable=protected-access

            # Prevent cycles.
            if node in nodes_in_progress:
                raise ValueError('The tensor ' + str(tensor) + ' at layer "' +
                                 layer.name + '" is part of a cycle.')

            # Don't repeat work for shared subgraphs
            if node in finished_nodes:
                return

            node_key = _make_node_key(layer.name, node_index)
            # Update network_nodes.
            network_nodes.add(node_key)

            # Store the traversal order for layer sorting.
            if layer not in layer_indices:
                layer_indices[layer] = len(layer_indices)

            nodes_in_progress.add(node)

            # Propagate to all previous tensors connected to this node.
            for i in range(len(node.inbound_layers)):
                x = node.input_tensors[i]
                layer = node.inbound_layers[i]
                node_index = node.node_indices[i]
                tensor_index = node.tensor_indices[i]
                build_map_of_graph(x, finished_nodes, nodes_in_progress, layer,
                                   node_index, tensor_index)

            finished_nodes.add(node)
            nodes_in_progress.remove(node)
            nodes_in_decreasing_depth.append(node)

        finished_nodes = set()
        nodes_in_progress = set()
        for x in self.outputs:
            layer, node_index, tensor_index = x._keras_history  # pylint: disable=protected-access
            build_map_of_graph(x,
                               finished_nodes,
                               nodes_in_progress,
                               layer=layer,
                               node_index=node_index,
                               tensor_index=tensor_index)

        for node in reversed(nodes_in_decreasing_depth):
            # If the depth is not set, the node has no outbound nodes (depth 0).
            depth = nodes_depths.setdefault(node, 0)

            # Update the depth of the corresponding layer
            previous_depth = layers_depths.get(node.outbound_layer, 0)
            # If we've seen this layer before at a higher depth,
            # we should use that depth instead of the node depth.
            # This is necessary for shared layers that have inputs at different
            # depth levels in the graph.
            depth = max(depth, previous_depth)
            layers_depths[node.outbound_layer] = depth
            nodes_depths[node] = depth

            # Update the depth of inbound nodes.
            # The "depth" of a node is the max of the depths
            # of all layers it is connected to.
            for i in range(len(node.inbound_layers)):
                inbound_layer = node.inbound_layers[i]
                node_index = node.node_indices[i]
                inbound_node = inbound_layer._inbound_nodes[node_index]  # pylint: disable=protected-access
                previous_depth = nodes_depths.get(inbound_node, 0)
                nodes_depths[inbound_node] = max(depth + 1, previous_depth)

        # Build a dict {depth: list of nodes with this depth}
        nodes_by_depth = {}
        for node, depth in nodes_depths.items():
            if depth not in nodes_by_depth:
                nodes_by_depth[depth] = []
            nodes_by_depth[depth].append(node)

        # Build a dict {depth: list of layers with this depth}
        layers_by_depth = {}
        for layer, depth in layers_depths.items():
            if depth not in layers_by_depth:
                layers_by_depth[depth] = []
            layers_by_depth[depth].append(layer)

        # Get sorted list of layer depths.
        depth_keys = list(layers_by_depth.keys())
        depth_keys.sort(reverse=True)

        # Set self.layers and self._layers_by_depth.
        layers = []
        for depth in depth_keys:
            layers_for_depth = layers_by_depth[depth]
            # GraphNetwork.layers needs to have a deterministic order:
            # here we order them by traversal order.
            layers_for_depth.sort(key=lambda x: layer_indices[x])
            layers.extend(layers_for_depth)
        self.layers = layers
        self._layers_by_depth = layers_by_depth

        # Get sorted list of node depths.
        depth_keys = list(nodes_by_depth.keys())
        depth_keys.sort(reverse=True)

        # Check that all tensors required are computable.
        # computable_tensors: all tensors in the graph
        # that can be computed from the inputs provided.
        computable_tensors = []
        for x in self.inputs:
            computable_tensors.append(x)

        layers_with_complete_input = []  # To provide a better error msg.
        for depth in depth_keys:
            for node in nodes_by_depth[depth]:
                layer = node.outbound_layer
                if layer:
                    for x in node.input_tensors:
                        if x not in computable_tensors:
                            raise ValueError(
                                'Graph disconnected: '
                                'cannot obtain value for tensor ' + str(x) +
                                ' at layer "' + layer.name + '". '
                                'The following previous layers '
                                'were accessed without issue: ' +
                                str(layers_with_complete_input))
                    for x in node.output_tensors:
                        computable_tensors.append(x)
                    layers_with_complete_input.append(layer.name)

        # Keep track of the network's nodes.
        self._network_nodes = network_nodes
        self._nodes_by_depth = nodes_by_depth

        # Ensure name unicity, which will be crucial for serialization
        # (since serialized nodes refer to layers by their name).
        all_names = [layer.name for layer in self.layers]
        for name in all_names:
            if all_names.count(name) != 1:
                raise ValueError('The name "' + name + '" is used ' +
                                 str(all_names.count(name)) +
                                 ' times in the model. '
                                 'All layer names should be unique.')

        # Layer parameters.
        # The new network starts with a single inbound node
        # for its inputs, and no outbound nodes.
        self._outbound_nodes = [
        ]  # Will be appended to by future calls to __call__
        self._inbound_nodes = [
        ]  # Will be appended to below, and by future calls to __call__
        # Create the node linking internal inputs to internal outputs.
        base.Node(outbound_layer=self,
                  inbound_layers=[],
                  node_indices=[],
                  tensor_indices=[],
                  input_tensors=self.inputs,
                  output_tensors=self.outputs)
Beispiel #3
0
  def __init__(self,
               input_shape=None,
               batch_size=None,
               dtype=None,
               input_tensor=None,
               sparse=False,
               name=None,
               **kwargs):
    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(K.get_uid(prefix))

    if not dtype:
      if input_tensor is None:
        dtype = K.floatx()
      else:
        dtype = K.dtype(input_tensor)
    super(InputLayer, self).__init__(dtype=dtype, name=name)
    self.built = True
    self.sparse = sparse
    self.batch_size = batch_size

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

    if input_tensor is None:
      if input_shape is not None:
        batch_input_shape = (batch_size,) + tuple(input_shape)
      else:
        batch_input_shape = None

      if context.in_eager_mode():
        # In eager mode, create a temporary placeholder to call the layer on.
        input_tensor = tf_base_layers._DeferredTensor(  # pylint: disable=protected-access
            shape=batch_input_shape,
            dtype=dtype,
            name=self.name)
      else:
        # In graph mode, create a graph placeholder to call the layer on.
        if sparse:
          input_tensor = array_ops.sparse_placeholder(
              shape=batch_input_shape,
              dtype=dtype,
              name=self.name)
        else:
          input_tensor = array_ops.placeholder(
              shape=batch_input_shape,
              dtype=dtype,
              name=self.name)

      # For compatibility with Keras API.
      self.is_placeholder = True
      self._batch_input_shape = batch_input_shape
    else:
      # For compatibility with Keras API.
      self.is_placeholder = False
      self._batch_input_shape = tuple(input_tensor.get_shape().as_list())

    # Create an input node to add to self.outbound_node
    # and set output_tensors' _keras_history.
    input_tensor._keras_history = (self, 0, 0)  # pylint: disable=protected-access
    tf_base_layers.Node(
        self,
        inbound_layers=[],
        node_indices=[],
        tensor_indices=[],
        input_tensors=[input_tensor],
        output_tensors=[input_tensor])