Example #1
0
    def __call__(self, inputs, initial_state=None, constants=None, **kwargs):

        inputs, initial_state, constants = _standardize_args(
            inputs, initial_state, constants, self._num_constants)

        if initial_state is None and constants is None:

            return super(ConvRNN2D, self).__call__(inputs, **kwargs)

        # If any of `initial_state` or `constants` are specified and are Keras
        # tensors, then add them to the inputs and temporarily modify the
        # input_spec to include them.

        additional_inputs = []
        additional_specs = []
        if initial_state is not None:
            kwargs['initial_state'] = initial_state
            additional_inputs += initial_state
            self.state_spec = []
            for state in initial_state:
                shape = K.int_shape(state)
                self.state_spec.append(InputSpec(shape=shape))

            additional_specs += self.state_spec
        if constants is not None:
            kwargs['constants'] = constants
            additional_inputs += constants
            self.constants_spec = [
                InputSpec(shape=K.int_shape(constant))
                for constant in constants
            ]
            self._num_constants = len(constants)
            additional_specs += self.constants_spec
        # at this point additional_inputs cannot be empty
        for tensor in additional_inputs:
            if K.is_keras_tensor(tensor) != K.is_keras_tensor(
                    additional_inputs[0]):
                raise ValueError('The initial state or constants of an RNN'
                                 ' layer cannot be specified with a mix of'
                                 ' Keras tensors and non-Keras tensors')

        if K.is_keras_tensor(additional_inputs[0]):
            # Compute the full input spec, including state and constants
            full_input = [inputs] + additional_inputs
            full_input_spec = self.input_spec + additional_specs
            # Perform the call with temporarily replaced input_spec
            original_input_spec = self.input_spec
            self.input_spec = full_input_spec
            output = super(ConvRNN2D, self).__call__(full_input, **kwargs)
            self.input_spec = original_input_spec
            return output
        else:
            return super(ConvRNN2D, self).__call__(inputs, **kwargs)
  def __call__(self, inputs, initial_state=None, constants=None, **kwargs):
    inputs, initial_state, constants = _standardize_args(
        inputs, initial_state, constants, self._num_constants)

    if initial_state is None and constants is None:
      return super(ConvRNN2D, self).__call__(inputs, **kwargs)

    # If any of `initial_state` or `constants` are specified and are Keras
    # tensors, then add them to the inputs and temporarily modify the
    # input_spec to include them.

    additional_inputs = []
    additional_specs = []
    if initial_state is not None:
      kwargs['initial_state'] = initial_state
      additional_inputs += initial_state
      self.state_spec = []
      for state in initial_state:
        shape = K.int_shape(state)
        self.state_spec.append(InputSpec(shape=shape))

      additional_specs += self.state_spec
    if constants is not None:
      kwargs['constants'] = constants
      additional_inputs += constants
      self.constants_spec = [InputSpec(shape=K.int_shape(constant))
                             for constant in constants]
      self._num_constants = len(constants)
      additional_specs += self.constants_spec
    # at this point additional_inputs cannot be empty
    for tensor in additional_inputs:
      if K.is_keras_tensor(tensor) != K.is_keras_tensor(additional_inputs[0]):
        raise ValueError('The initial state or constants of an RNN'
                         ' layer cannot be specified with a mix of'
                         ' Keras tensors and non-Keras tensors')

    if K.is_keras_tensor(additional_inputs[0]):
      # Compute the full input spec, including state and constants
      full_input = [inputs] + additional_inputs
      full_input_spec = self.input_spec + additional_specs
      # Perform the call with temporarily replaced input_spec
      original_input_spec = self.input_spec
      self.input_spec = full_input_spec
      output = super(ConvRNN2D, self).__call__(full_input, **kwargs)
      self.input_spec = original_input_spec
      return output
    else:
      return super(ConvRNN2D, self).__call__(inputs, **kwargs)
Example #3
0
    def __call__(self, inputs, initial_state=None, constants=None, **kwargs):
        """`Bidirectional.__call__` implements the same API as the wrapped `RNN`."""
        inputs, initial_state, constants = _standardize_args(
            inputs, initial_state, constants, self._num_constants)

        if isinstance(inputs, list):
            if len(inputs) > 1:
                initial_state = inputs[1:]
            inputs = inputs[0]

        if initial_state is None and constants is None:
            return super(Bidirectional, self).__call__(inputs, **kwargs)

        # Applies the same workaround as in `RNN.__call__`
        additional_inputs = []
        additional_specs = []
        if initial_state is not None:
            # Check if `initial_state` can be splitted into half
            num_states = len(initial_state)
            if num_states % 2 > 0:
                raise ValueError(
                    'When passing `initial_state` to a Bidirectional RNN, '
                    'the state should be a list containing the states of '
                    'the underlying RNNs. '
                    'Found: ' + str(initial_state))

            kwargs['initial_state'] = initial_state
            additional_inputs += initial_state
            state_specs = [
                InputSpec(shape=K.int_shape(state)) for state in initial_state
            ]
            self.forward_layer.state_spec = state_specs[:num_states // 2]
            self.backward_layer.state_spec = state_specs[num_states // 2:]
            additional_specs += state_specs
        if constants is not None:
            kwargs['constants'] = constants
            additional_inputs += constants
            constants_spec = [
                InputSpec(shape=K.int_shape(constant))
                for constant in constants
            ]
            self.forward_layer.constants_spec = constants_spec
            self.backward_layer.constants_spec = constants_spec
            additional_specs += constants_spec

            self._num_constants = len(constants)
            self.forward_layer._num_constants = self._num_constants
            self.backward_layer._num_constants = self._num_constants

        is_keras_tensor = K.is_keras_tensor(additional_inputs[0])
        for tensor in additional_inputs:
            if K.is_keras_tensor(tensor) != is_keras_tensor:
                raise ValueError('The initial state of a Bidirectional'
                                 ' layer cannot be specified with a mix of'
                                 ' Keras tensors and non-Keras tensors'
                                 ' (a "Keras tensor" is a tensor that was'
                                 ' returned by a Keras layer, or by `Input`)')

        if is_keras_tensor:
            # Compute the full input spec, including state
            full_input = [inputs] + additional_inputs
            full_input_spec = self.input_spec + additional_specs

            # Perform the call with temporarily replaced input_spec
            original_input_spec = self.input_spec
            self.input_spec = full_input_spec
            output = super(Bidirectional, self).__call__(full_input, **kwargs)
            self.input_spec = original_input_spec
            return output
        else:
            return super(Bidirectional, self).__call__(inputs, **kwargs)
Example #4
0
  def __call__(self, inputs, initial_state=None, constants=None, **kwargs):
    """`Bidirectional.__call__` implements the same API as the wrapped `RNN`."""
    inputs, initial_state, constants = _standardize_args(
        inputs, initial_state, constants, self._num_constants)

    if isinstance(inputs, list):
      if len(inputs) > 1:
        initial_state = inputs[1:]
      inputs = inputs[0]

    if initial_state is None and constants is None:
      return super(Bidirectional, self).__call__(inputs, **kwargs)

    # Applies the same workaround as in `RNN.__call__`
    additional_inputs = []
    additional_specs = []
    if initial_state is not None:
      # Check if `initial_state` can be splitted into half
      num_states = len(initial_state)
      if num_states % 2 > 0:
        raise ValueError(
            'When passing `initial_state` to a Bidirectional RNN, '
            'the state should be a list containing the states of '
            'the underlying RNNs. '
            'Found: ' + str(initial_state))

      kwargs['initial_state'] = initial_state
      additional_inputs += initial_state
      state_specs = [InputSpec(shape=K.int_shape(state))
                     for state in initial_state]
      self.forward_layer.state_spec = state_specs[:num_states // 2]
      self.backward_layer.state_spec = state_specs[num_states // 2:]
      additional_specs += state_specs
    if constants is not None:
      kwargs['constants'] = constants
      additional_inputs += constants
      constants_spec = [InputSpec(shape=K.int_shape(constant))
                        for constant in constants]
      self.forward_layer.constants_spec = constants_spec
      self.backward_layer.constants_spec = constants_spec
      additional_specs += constants_spec

      self._num_constants = len(constants)
      self.forward_layer._num_constants = self._num_constants
      self.backward_layer._num_constants = self._num_constants

    is_keras_tensor = K.is_keras_tensor(additional_inputs[0])
    for tensor in additional_inputs:
      if K.is_keras_tensor(tensor) != is_keras_tensor:
        raise ValueError('The initial state of a Bidirectional'
                         ' layer cannot be specified with a mix of'
                         ' Keras tensors and non-Keras tensors'
                         ' (a "Keras tensor" is a tensor that was'
                         ' returned by a Keras layer, or by `Input`)')

    if is_keras_tensor:
      # Compute the full input spec, including state
      full_input = [inputs] + additional_inputs
      # The original input_spec is None since there could be a nested tensor
      # input. Update the input_spec to match the inputs.
      full_input_spec = [None for _ in range(len(nest.flatten(inputs)))
                        ] + additional_specs

      # Perform the call with temporarily replaced input_spec
      original_input_spec = self.input_spec
      self.input_spec = full_input_spec
      output = super(Bidirectional, self).__call__(full_input, **kwargs)
      self.input_spec = original_input_spec
      return output
    else:
      return super(Bidirectional, self).__call__(inputs, **kwargs)
Example #5
0
    def __call__(self,
                 *args,
                 inputs=None,
                 initial_state=None,
                 constants=None,
                 mask=None,
                 **kwargs):

        inputs, initial_state, constants = _standardize_args(
            inputs, initial_state, constants, self._num_constants)
        # We allow different shapes of input, even None. It doesn't really matter
        # because ultimately the input will be ignored except for the first step.
        # Nevertheless, we expand the input to have a timesteps dimension. This
        # is done simply for parent class calculations of output size, etc.

        # Allow None as an input. We will create an array of zeros of appropriate shape.
        if inputs is None:
            if initial_state is not None:
                # If LSTM then state might be a list.
                _state = initial_state[0] if isinstance(
                    initial_state, list) else initial_state
                batch_size = _state.shape[:-1]
                inputs = K.zeros_like(_state[..., 0][..., tf.newaxis])
                # inputs = 0 * _state[..., 0][..., tf.newaxis]  # Assume dim=1 input
            else:
                # Neither inputs nor initial_state provided. This likely only happens
                # when building/testing the layer.
                inputs = tf.zeros(
                    (self.timesteps, 1, 1)) if self.time_major else tf.zeros(
                        (1, self.timesteps, 1))

        # Allow 2D input, here reshape to 3D input
        if len(K.int_shape(inputs)) < 3:
            if self.time_major:
                inputs = inputs[tf.newaxis, ...]
            else:
                inputs = inputs[..., tf.newaxis, :]

        time_ax_ix, batch_ax_ix = (0, 1) if self.time_major else (-2, 0)
        input_shape = K.int_shape(inputs)
        input_timesteps = input_shape[time_ax_ix]

        if mask is not None and K.any(~mask):
            mask = nest.flatten(mask)[0]
            # We assume mask has a time dimension and require it is same size as input
            # (It doesn't make sense to use mask otherwise).
            mask_shape = K.int_shape(mask)
            # If the mask only has 1 item in the batch dim then tile it
            if mask_shape[batch_ax_ix] == 1 and input_shape[batch_ax_ix] > 1:
                if self.time_major:
                    bcast_or = tf.zeros((1, input_shape[batch_ax_ix], 1),
                                        dtype=tf.bool)
                else:
                    bcast_or = tf.zeros((input_shape[batch_ax_ix], 1, 1),
                                        dtype=tf.bool)
                mask = tf.math.logical_or(mask, bcast_or)
            if mask_shape[time_ax_ix] == input_timesteps:
                # Prepare slice parameters
                # For head (kept)
                h_sl_begin = [0 for _ in input_shape]
                h_sl_sz = [-1 for _ in input_shape]
                h_sl_sz[batch_ax_ix] = 1
                # For tail (replaced)
                t_sl_begin = [0 for _ in input_shape]
                t_sl_sz = [-1 for _ in input_shape]
                t_sl_sz[batch_ax_ix] = 1
                # Collect input replacements in list
                new_inputs = []
                for batch_ix in range(input_shape[batch_ax_ix]):
                    samp_mask = mask[
                        ...,
                        batch_ix, :] if self.time_major else mask[batch_ix]
                    if K.any(~samp_mask):
                        h_sl_begin[batch_ax_ix] = batch_ix
                        t_sl_begin[batch_ax_ix] = batch_ix
                        first_bad = tf.where(~samp_mask)[0, 0]
                        h_sl_sz[time_ax_ix] = first_bad  # sz is 1-based
                        t_sl_begin[time_ax_ix] = first_bad
                        head = tf.slice(inputs, h_sl_begin, h_sl_sz)
                        tail = tf.slice(inputs, t_sl_begin, t_sl_sz)
                        if self.tile_input:
                            tile_samp = head[-1] if self.time_major else head[
                                ..., -1, :]
                        else:
                            tile_samp = tf.zeros((1, input_shape[-1]))
                        new_row = tf.concat(
                            (head, tile_samp * K.ones_like(tail)),
                            axis=time_ax_ix)
                        new_inputs.append(new_row)
                inputs = tf.concat(new_inputs, axis=batch_ax_ix)

        # Fill/trim input time dimension to be self.timesteps
        if input_timesteps > self.timesteps:
            # Trim excess, if any
            inputs = inputs[:self.timesteps,
                            ...] if self.time_major else inputs[
                                ..., :self.timesteps, :]
        elif input_timesteps < self.timesteps:
            # Take the last timestep as our starting point for the padding data
            pad_sample = inputs[-1] if self.time_major else inputs[..., -1, :]
            if not self.tile_input:
                # zero out padding data if we aren't tiling
                pad_sample = K.zeros_like(pad_sample)
                # pad_sample = 0 * pad_sample
            # Add the time axis back to our pad_sample
            pad_sample = pad_sample[tf.newaxis,
                                    ...] if self.time_major else pad_sample[
                                        ..., tf.newaxis, :]
            # How many more timestamps do we need?
            pad_timestamps = self.timesteps - K.int_shape(inputs)[time_ax_ix]
            # Tile pad_data using broadcast-add. Does this same line work for time_major and not?
            pad_data = pad_sample + tf.zeros((pad_timestamps, 1))
            inputs = tf.concat((inputs, pad_data), axis=time_ax_ix)

        if not self._built_with_input:
            self.build_with_input(inputs,
                                  *args,
                                  initial_state=initial_state,
                                  constants=constants,
                                  mask=mask,
                                  **kwargs)

        return super().__call__(inputs,
                                initial_state=initial_state,
                                constants=constants,
                                **kwargs)