def __init__(self, input_size, hidden_sizes, output_size, **_):
     """
     Parameters
     ----------
     input_size : int
         Number of units each element Xi in the input sequence X has.
     hidden_sizes : int, list of int
         Number of hidden units each GRU should have.
     output_size : int
         Number of units the regression layer should have.
     """
     super().__init__(input_size, hidden_sizes)
     self.output_size = output_size
     self.layer_regression = LayerRegression(self.hidden_sizes[-1],
                                             self.output_size)
     self.stopping_layer = LayerDense(self.hidden_sizes[-1] + input_size,
                                      1,
                                      activation="sigmoid",
                                      name="stopping")
Example #2
0
    def __init__(self, volume_manager, input_size, hidden_sizes, output_size, activation, use_previous_direction=False, predict_offset=False,
                 use_layer_normalization=False, dropout_prob=0., seed=1234, **_):
        """
        Parameters
        ----------
        volume_manager : :class:`VolumeManger` object
            Use to evaluate the diffusion signal at specific coordinates.
        input_size : int
            Number of units each element X has.
        hidden_sizes : int, list of int
            Number of hidden units each FFNN layer should have.
        output_size : int
            Number of units the regression layer should have.
        activation : str
            Name of the activation function to use in the hidden layers
        use_previous_direction : bool
            Use the previous direction as an additional input
        predict_offset : bool
            Predict the offset from the previous direction instead (need use_previous_direction).
        use_layer_normalization : bool
            Use LayerNormalization to normalize preactivations
        dropout_prob : float
            Dropout probability for recurrent networks. See: https://arxiv.org/pdf/1512.05287.pdf
        seed : int
            Random seed used for dropout normalization
        """
        super().__init__(input_size, hidden_sizes, activation, use_layer_normalization, dropout_prob, seed)
        self.volume_manager = volume_manager
        self.output_size = output_size
        self.use_previous_direction = use_previous_direction
        self.predict_offset = predict_offset

        if self.predict_offset:
            assert self.use_previous_direction  # Need previous direction to predict offset.

        layer_regression_activation = "tanh" if self.predict_offset else "identity"
        self.layer_regression = LayerDense(self.hidden_sizes[-1], self.output_size, activation=layer_regression_activation)

        if self.dropout_prob:
            p = 1 - self.dropout_prob
            self.dropout_vectors[self.layer_regression.name] = self.srng.binomial(size=(self.layer_regression.input_size,), n=1, p=p, dtype=floatX) / p
    def __init__(self, volume_manager, input_size, hidden_sizes, use_layer_normalization=False, use_skip_connections=False, **_):
        """
        Parameters
        ----------
        volume_manager : :class:`VolumeManger` object
            Use to evaluate the diffusion signal at specific coordinates.
        input_size : int
            Number of units each element X has.
        hidden_sizes : int, list of int
            Number of hidden units each FFNN layer should have.
        use_layer_normalization : bool
            Use LayerNormalization to normalize preactivations
        use_skip_connections : bool
            Use skip connections from the input to all hidden layers in the network, and from all hidden layers to the output layer
        """
        super().__init__(input_size, hidden_sizes, use_layer_normalization=use_layer_normalization, use_skip_connections=use_skip_connections)
        self.volume_manager = volume_manager
        self.output_size = 1  # Positive class probability

        output_layer_input_size = sum(self.hidden_sizes) if self.use_skip_connections else self.hidden_sizes[-1]
        self.layer_classification = LayerDense(output_layer_input_size, self.output_size, activation="sigmoid")
 def __init__(self, input_size, hidden_sizes, output_size, **_):
     """
     Parameters
     ----------
     input_size : int
         Number of units each element Xi in the input sequence X has.
     hidden_sizes : int, list of int
         Number of hidden units each GRU should have.
     output_size : int
         Number of units the regression layer should have.
     """
     super().__init__(input_size, hidden_sizes)
     self.output_size = output_size
     self.layer_regression = LayerRegression(self.hidden_sizes[-1], self.output_size)
     self.stopping_layer = LayerDense(self.hidden_sizes[-1] + input_size, 1, activation="sigmoid", name="stopping")
class GRU_RegressionAndBinaryClassification(GRU):
    """ A standard GRU model with both a regression output layer and
    binary classification output layer.

    The regression layer consists in fully connected layer (DenseLayer)
    whereas the binary classification layer consists in a fully connected
    layer with a sigmoid non-linearity to learn when to stop.
    """
    def __init__(self, input_size, hidden_sizes, output_size, **_):
        """
        Parameters
        ----------
        input_size : int
            Number of units each element Xi in the input sequence X has.
        hidden_sizes : int, list of int
            Number of hidden units each GRU should have.
        output_size : int
            Number of units the regression layer should have.
        """
        super().__init__(input_size, hidden_sizes)
        self.output_size = output_size
        self.layer_regression = LayerRegression(self.hidden_sizes[-1], self.output_size)
        self.stopping_layer = LayerDense(self.hidden_sizes[-1] + input_size, 1, activation="sigmoid", name="stopping")

    def initialize(self, weights_initializer=initer.UniformInitializer(1234)):
        super().initialize(weights_initializer)
        self.layer_regression.initialize(weights_initializer)
        self.stopping_layer.initialize(weights_initializer)

    @property
    def hyperparameters(self):
        hyperparameters = super().hyperparameters
        hyperparameters['output_size'] = self.output_size
        return hyperparameters

    @property
    def parameters(self):
        return super().parameters + self.layer_regression.parameters + self.stopping_layer.parameters

    def _fprop(self, Xi, Xi_plus1, *args):
        outputs = super()._fprop(Xi, *args)
        last_layer_h = outputs[len(self.hidden_sizes)-1]
        regression_out = self.layer_regression.fprop(last_layer_h)
        stopping = self.stopping_layer.fprop(T.concatenate([last_layer_h, Xi_plus1], axis=1))

        return outputs + (regression_out, stopping)

    def get_output(self, X):
        outputs_info_h = []
        for hidden_size in self.hidden_sizes:
            outputs_info_h.append(T.zeros((X.shape[0], hidden_size)))

        results, updates = theano.scan(fn=self._fprop,
                                       outputs_info=outputs_info_h + [None, None],
                                       sequences=[{"input": T.transpose(X, axes=(1, 0, 2)),  # We want to scan over sequence elements, not the examples.
                                                   "taps": [0, 1]}],
                                       n_steps=X.shape[1]-1)

        self.graph_updates = updates
        # Put back the examples so they are in the first dimension.
        self.regression_out = T.transpose(results[-2], axes=(1, 0, 2))
        self.stopping = T.transpose(results[-1], axes=(1, 0, 2))

        return self.regression_out, self.stopping

    def use(self, X):
        directions = self.get_output(X)
        return directions
Example #6
0
class FFNN_Regression(FFNN):
    """ A standard FFNN model with a regression layer stacked on top of it.
    """

    def __init__(self, volume_manager, input_size, hidden_sizes, output_size, activation, use_previous_direction=False, predict_offset=False,
                 use_layer_normalization=False, dropout_prob=0., seed=1234, **_):
        """
        Parameters
        ----------
        volume_manager : :class:`VolumeManger` object
            Use to evaluate the diffusion signal at specific coordinates.
        input_size : int
            Number of units each element X has.
        hidden_sizes : int, list of int
            Number of hidden units each FFNN layer should have.
        output_size : int
            Number of units the regression layer should have.
        activation : str
            Name of the activation function to use in the hidden layers
        use_previous_direction : bool
            Use the previous direction as an additional input
        predict_offset : bool
            Predict the offset from the previous direction instead (need use_previous_direction).
        use_layer_normalization : bool
            Use LayerNormalization to normalize preactivations
        dropout_prob : float
            Dropout probability for recurrent networks. See: https://arxiv.org/pdf/1512.05287.pdf
        seed : int
            Random seed used for dropout normalization
        """
        super().__init__(input_size, hidden_sizes, activation, use_layer_normalization, dropout_prob, seed)
        self.volume_manager = volume_manager
        self.output_size = output_size
        self.use_previous_direction = use_previous_direction
        self.predict_offset = predict_offset

        if self.predict_offset:
            assert self.use_previous_direction  # Need previous direction to predict offset.

        layer_regression_activation = "tanh" if self.predict_offset else "identity"
        self.layer_regression = LayerDense(self.hidden_sizes[-1], self.output_size, activation=layer_regression_activation)

        if self.dropout_prob:
            p = 1 - self.dropout_prob
            self.dropout_vectors[self.layer_regression.name] = self.srng.binomial(size=(self.layer_regression.input_size,), n=1, p=p, dtype=floatX) / p

    def initialize(self, weights_initializer=initer.UniformInitializer(1234)):
        super().initialize(weights_initializer)
        self.layer_regression.initialize(weights_initializer)

    @property
    def hyperparameters(self):
        hyperparameters = super().hyperparameters
        hyperparameters['output_size'] = self.output_size
        hyperparameters['use_previous_direction'] = self.use_previous_direction
        hyperparameters['predict_offset'] = self.predict_offset
        return hyperparameters

    @property
    def parameters(self):
        return super().parameters + self.layer_regression.parameters

    def _fprop(self, Xi, *args):
        # Xi.shape : (batch_size, 4)    *if self.use_previous_direction, Xi.shape : (batch_size,7)
        # coords + dwi ID (+ previous_direction)

        # coords : streamlines 3D coordinates.
        # coords.shape : (batch_size, 4) where the last column is a dwi ID.
        # args.shape : n_layers * (batch_size, layer_size)
        coords = Xi[:, :4]

        # Get diffusion data.
        # data_at_coords.shape : (batch_size, input_size)
        data_at_coords = self.volume_manager.eval_at_coords(coords)

        if self.use_previous_direction:
            # previous_direction.shape : (batch_size, 3)
            print("Using previous direction")
            previous_direction = Xi[:, 4:]
            fprop_input = T.concatenate([data_at_coords, previous_direction], axis=1)
        else:
            fprop_input = data_at_coords

        # Hidden state to be passed to the next GRU iteration (next _fprop call)
        # next_hidden_state.shape : n_layers * (batch_size, layer_size)
        layer_outputs = super()._fprop(fprop_input)

        # Compute the direction to follow for step (t)
        dropout_W = self.dropout_vectors[self.layer_regression.name] if self.dropout_prob else None
        regression_out = self.layer_regression.fprop(layer_outputs[-1], dropout_W)
        if self.predict_offset:
            print("Predicting offset")
            regression_out += previous_direction  # Skip-connection from the previous direction.

        return layer_outputs + (regression_out,)

    def make_sequence_generator(self, subject_id=0, **_):
        """ Makes functions that return the prediction for x_{t+1} for every
        sequence in the batch given x_{t}.

        Parameters
        ----------
        subject_id : int, optional
            ID of the subject from which its diffusion data will be used. Default: 0.
        """

        # Build the sequence generator as a theano function.
        symb_x_t = T.matrix(name="x_t")

        layer_outputs = self._fprop(symb_x_t)

        # predictions.shape : (batch_size, target_size)
        predictions = layer_outputs[-1]

        f = theano.function(inputs=[symb_x_t], outputs=[predictions])

        def _gen(x_t, states, previous_direction=None):
            """ Returns the prediction for x_{t+1} for every
                sequence in the batch given x_{t}.

            Parameters
            ----------
            x_t : ndarray with shape (batch_size, 3)
                Streamline coordinate (x, y, z).
            states : list of 2D array of shape (batch_size, hidden_size)
                Currrent states of the network.
            previous_direction : ndarray with shape (batch_size, 3)
                If using previous direction, these should be added to the input

            Returns
            -------
            next_x_t : ndarray with shape (batch_size, 3)
                Directions to follow.
            new_states : list of 2D array of shape (batch_size, hidden_size)
                Updated states of the network after seeing x_t.
            """
            # Append the DWI ID of each sequence after the 3D coordinates.
            subject_ids = np.array([subject_id] * len(x_t), dtype=floatX)[:, None]

            if not self.use_previous_direction:
                x_t = np.c_[x_t, subject_ids]
            else:
                x_t = np.c_[x_t, subject_ids, previous_direction]

            results = f(x_t)
            next_x_t = results[-1]

            next_x_t_both_directions = np.stack([next_x_t, -next_x_t], axis=-1)

            next_x_t = next_x_t_both_directions[
                (np.arange(next_x_t_both_directions.shape[0])[:, None]),
                (np.arange(next_x_t_both_directions.shape[1])[None, :]),
                np.argmin(np.linalg.norm(next_x_t_both_directions - previous_direction[:, :, None], axis=1), axis=1)[:, None]]

            # FFNN_Regression is not a recurrent network, return original states
            new_states = states

            return next_x_t, new_states

        return _gen
Example #7
0
    def __init__(self,
                 volume_manager,
                 input_size,
                 hidden_sizes,
                 output_size,
                 activation='tanh',
                 use_previous_direction=False,
                 predict_offset=False,
                 use_layer_normalization=False,
                 drop_prob=0.,
                 use_zoneout=False,
                 use_skip_connections=False,
                 neighborhood_radius=None,
                 learn_to_stop=False,
                 seed=1234,
                 **_):
        """
        Parameters
        ----------
        volume_manager : :class:`VolumeManger` object
            Use to evaluate the diffusion signal at specific coordinates.
        input_size : int
            Number of units each element Xi in the input sequence X has.
        hidden_sizes : int, list of int
            Number of hidden units each GRU should have.
        output_size : int
            Number of units the regression layer should have.
        activation : str
            Activation function to apply on the "cell candidate"
        use_previous_direction : bool
            Use the previous direction as an additional input
        predict_offset : bool
            Predict the offset from the previous direction instead (need use_previous_direction).
        use_layer_normalization : bool
            Use LayerNormalization to normalize preactivations and stabilize hidden layer evolution
        drop_prob : float
            Dropout/Zoneout probability for recurrent networks. See: https://arxiv.org/pdf/1512.05287.pdf & https://arxiv.org/pdf/1606.01305.pdf
        use_zoneout : bool
            Use zoneout implementation instead of dropout
        use_skip_connections : bool
            Use skip connections from the input to all hidden layers in the network, and from all hidden layers to the output layer
        neighborhood_radius : float
            Add signal in positions around the current streamline coordinate to the input (with given length in voxel space); None = no neighborhood
        learn_to_stop : bool
            Predict whether the streamline being generated should stop or not
        seed : int
            Random seed used for dropout normalization
        """
        self.neighborhood_radius = neighborhood_radius
        self.model_input_size = input_size
        if self.neighborhood_radius:
            self.neighborhood_directions = get_neighborhood_directions(
                self.neighborhood_radius)
            # Model input size is increased when using neighborhood
            self.model_input_size = input_size * self.neighborhood_directions.shape[
                0]

        super().__init__(self.model_input_size,
                         hidden_sizes,
                         activation=activation,
                         use_layer_normalization=use_layer_normalization,
                         drop_prob=drop_prob,
                         use_zoneout=use_zoneout,
                         use_skip_connections=use_skip_connections,
                         seed=seed)
        # Restore input size
        self.input_size = input_size

        self.volume_manager = volume_manager
        self.output_size = output_size
        self.use_previous_direction = use_previous_direction
        self.predict_offset = predict_offset
        self.learn_to_stop = learn_to_stop

        if self.predict_offset:
            assert self.use_previous_direction  # Need previous direction to predict offset.

        # Do not use dropout/zoneout in last hidden layer
        layer_regression_activation = "tanh" if self.predict_offset else "identity"
        output_layer_input_size = sum(
            self.hidden_sizes
        ) if self.use_skip_connections else self.hidden_sizes[-1]
        self.layer_regression = LayerDense(
            output_layer_input_size,
            self.output_size,
            activation=layer_regression_activation,
            name="GRU_Regression")
        if self.learn_to_stop:
            # Predict whether a streamline should stop or keep growing
            self.layer_stopping = LayerDense(output_layer_input_size,
                                             1,
                                             activation='sigmoid',
                                             name="GRU_Regression_stopping")
Example #8
0
class GRU_Regression(GRU):
    """ A standard GRU model with a regression layer stacked on top of it.
    """
    def __init__(self,
                 volume_manager,
                 input_size,
                 hidden_sizes,
                 output_size,
                 activation='tanh',
                 use_previous_direction=False,
                 predict_offset=False,
                 use_layer_normalization=False,
                 drop_prob=0.,
                 use_zoneout=False,
                 use_skip_connections=False,
                 neighborhood_radius=None,
                 learn_to_stop=False,
                 seed=1234,
                 **_):
        """
        Parameters
        ----------
        volume_manager : :class:`VolumeManger` object
            Use to evaluate the diffusion signal at specific coordinates.
        input_size : int
            Number of units each element Xi in the input sequence X has.
        hidden_sizes : int, list of int
            Number of hidden units each GRU should have.
        output_size : int
            Number of units the regression layer should have.
        activation : str
            Activation function to apply on the "cell candidate"
        use_previous_direction : bool
            Use the previous direction as an additional input
        predict_offset : bool
            Predict the offset from the previous direction instead (need use_previous_direction).
        use_layer_normalization : bool
            Use LayerNormalization to normalize preactivations and stabilize hidden layer evolution
        drop_prob : float
            Dropout/Zoneout probability for recurrent networks. See: https://arxiv.org/pdf/1512.05287.pdf & https://arxiv.org/pdf/1606.01305.pdf
        use_zoneout : bool
            Use zoneout implementation instead of dropout
        use_skip_connections : bool
            Use skip connections from the input to all hidden layers in the network, and from all hidden layers to the output layer
        neighborhood_radius : float
            Add signal in positions around the current streamline coordinate to the input (with given length in voxel space); None = no neighborhood
        learn_to_stop : bool
            Predict whether the streamline being generated should stop or not
        seed : int
            Random seed used for dropout normalization
        """
        self.neighborhood_radius = neighborhood_radius
        self.model_input_size = input_size
        if self.neighborhood_radius:
            self.neighborhood_directions = get_neighborhood_directions(
                self.neighborhood_radius)
            # Model input size is increased when using neighborhood
            self.model_input_size = input_size * self.neighborhood_directions.shape[
                0]

        super().__init__(self.model_input_size,
                         hidden_sizes,
                         activation=activation,
                         use_layer_normalization=use_layer_normalization,
                         drop_prob=drop_prob,
                         use_zoneout=use_zoneout,
                         use_skip_connections=use_skip_connections,
                         seed=seed)
        # Restore input size
        self.input_size = input_size

        self.volume_manager = volume_manager
        self.output_size = output_size
        self.use_previous_direction = use_previous_direction
        self.predict_offset = predict_offset
        self.learn_to_stop = learn_to_stop

        if self.predict_offset:
            assert self.use_previous_direction  # Need previous direction to predict offset.

        # Do not use dropout/zoneout in last hidden layer
        layer_regression_activation = "tanh" if self.predict_offset else "identity"
        output_layer_input_size = sum(
            self.hidden_sizes
        ) if self.use_skip_connections else self.hidden_sizes[-1]
        self.layer_regression = LayerDense(
            output_layer_input_size,
            self.output_size,
            activation=layer_regression_activation,
            name="GRU_Regression")
        if self.learn_to_stop:
            # Predict whether a streamline should stop or keep growing
            self.layer_stopping = LayerDense(output_layer_input_size,
                                             1,
                                             activation='sigmoid',
                                             name="GRU_Regression_stopping")

    def initialize(self, weights_initializer=initer.UniformInitializer(1234)):
        super().initialize(weights_initializer)
        self.layer_regression.initialize(weights_initializer)
        if self.learn_to_stop:
            self.layer_stopping.initialize(weights_initializer)

    @property
    def hyperparameters(self):
        hyperparameters = super().hyperparameters
        hyperparameters['output_size'] = self.output_size
        hyperparameters['use_previous_direction'] = self.use_previous_direction
        hyperparameters['predict_offset'] = self.predict_offset
        hyperparameters['neighborhood_radius'] = self.neighborhood_radius
        hyperparameters['learn_to_stop'] = self.learn_to_stop
        return hyperparameters

    @property
    def parameters(self):
        all_params = super().parameters + self.layer_regression.parameters
        if self.learn_to_stop:
            all_params += self.layer_stopping.parameters

        return all_params

    def _fprop_step(self, Xi, *args):
        # Xi.shape : (batch_size, 4)    *if self.use_previous_direction, Xi.shape : (batch_size,7)
        # coords + dwi ID (+ previous_direction)

        # coords : streamlines 3D coordinates.
        # coords.shape : (batch_size, 4) where the last column is a dwi ID.
        # args.shape : n_layers * (batch_size, layer_size)
        batch_size = Xi.shape[0]
        coords = Xi[:, :4]

        # Repeat coords and apply the neighborhood transformations
        if self.neighborhood_radius:
            # coords.shape : (batch_size*len(neighbors_positions), 4)
            coords = T.repeat(coords,
                              self.neighborhood_directions.shape[0],
                              axis=0)
            coords = T.set_subtensor(
                coords[:, :3],
                coords[:, :3] + T.tile(self.neighborhood_directions,
                                       (batch_size, 1)))

        # Get diffusion data.
        # data_at_coords.shape : (batch_size, input_size)
        data_at_coords = self.volume_manager.eval_at_coords(coords)

        # Concatenate back the neighborhood data into a single input vector
        if self.neighborhood_radius:
            data_at_coords = T.reshape(data_at_coords,
                                       (batch_size, self.model_input_size))

        if self.use_previous_direction:
            # previous_direction.shape : (batch_size, 3)
            previous_direction = Xi[:, 4:]
            fprop_input = T.concatenate([data_at_coords, previous_direction],
                                        axis=1)
        else:
            fprop_input = data_at_coords

        # Hidden state to be passed to the next GRU iteration (next _fprop call)
        # next_hidden_state.shape : n_layers * (batch_size, layer_size)
        next_hidden_state = super()._fprop(fprop_input, *args)

        # Compute the direction to follow for step (t)
        output_layer_input = T.concatenate(
            next_hidden_state,
            axis=-1) if self.use_skip_connections else next_hidden_state[-1]
        regression_out = self.layer_regression.fprop(output_layer_input)

        if self.predict_offset:
            regression_out += previous_direction  # Skip-connection from the previous direction.

        outputs = (regression_out, )

        if self.learn_to_stop:
            stopping_out = self.layer_stopping.fprop(output_layer_input)
            outputs = (stopping_out, regression_out)

        return next_hidden_state + outputs

    def get_output(self, X):
        # X.shape : (batch_size, seq_len, n_features=[4|7])
        # For tractography n_features is (x,y,z) + (dwi_id,) + [previous_direction]

        outputs_info_h = []
        for hidden_size in self.hidden_sizes:
            outputs_info_h.append(T.zeros((X.shape[0], hidden_size)))

        outputs_info = outputs_info_h + [None]
        if self.learn_to_stop:
            outputs_info += [None]

        results, updates = theano.scan(
            fn=self._fprop_step,
            # We want to scan over sequence elements, not the examples.
            sequences=[T.transpose(X, axes=(1, 0, 2))],
            outputs_info=outputs_info,
            non_sequences=self.parameters + self.volume_manager.volumes,
            strict=True)

        self.graph_updates = updates
        # Put back the examples so they are in the first dimension.
        # regression_out.shape : (batch_size, seq_len, target_size=3)
        self.regression_out = T.transpose(results[-1], axes=(1, 0, 2))
        model_output = self.regression_out

        if self.learn_to_stop:
            self.stopping_out = T.transpose(results[-2], axes=(1, 0, 2))
            model_output = (self.stopping_out, self.regression_out)

        return model_output

    def make_sequence_generator(self, subject_id=0, **_):
        """ Makes functions that return the prediction for x_{t+1} for every
        sequence in the batch given x_{t} and the current state of the model h^{l}_{t}.

        Parameters
        ----------
        subject_id : int, optional
            ID of the subject from which its diffusion data will be used. Default: 0.
        """

        # Build the sequence generator as a theano function.
        states_h = []
        for i in range(len(self.hidden_sizes)):
            state_h = T.matrix(name="layer{}_state_h".format(i))
            states_h.append(state_h)

        symb_x_t = T.matrix(name="x_t")

        new_states = self._fprop_step(symb_x_t, *states_h)
        new_states_h = new_states[:len(self.hidden_sizes)]

        # predictions.shape : (batch_size, target_size)
        predictions = [new_states[-1]]
        if self.learn_to_stop:
            predictions = new_states[-2:]

        f = theano.function(inputs=[symb_x_t] + states_h,
                            outputs=list(predictions) + list(new_states_h))

        def _gen(x_t, states, previous_direction=None):
            """ Returns the prediction for x_{t+1} for every
                sequence in the batch given x_{t} and the current states
                of the model h^{l}_{t}.

            Parameters
            ----------
            x_t : ndarray with shape (batch_size, 3)
                Streamline coordinate (x, y, z).
            states : list of 2D array of shape (batch_size, hidden_size)
                Currrent states of the network.
            previous_direction : ndarray with shape (batch_size, 3)
                If using previous direction, these should be added to the input

            Returns
            -------
            next_x_t : ndarray with shape (batch_size, 3)
                Directions to follow.
            new_states : list of 2D array of shape (batch_size, hidden_size)
                Updated states of the network after seeing x_t.
            """
            # Append the DWI ID of each sequence after the 3D coordinates.
            subject_ids = np.array([subject_id] * len(x_t), dtype=floatX)[:,
                                                                          None]

            if not self.use_previous_direction:
                x_t = np.c_[x_t, subject_ids]
            else:
                x_t = np.c_[x_t, subject_ids, previous_direction]

            results = f(x_t, *states)
            if self.learn_to_stop:
                stopping = results[0]
                next_x_t = results[1]
                new_states = results[2:]
                output = (next_x_t, stopping)
            else:
                next_x_t = results[0]
                new_states = results[1:]
                output = next_x_t

            return output, new_states

        return _gen
class FFNN_Classification(FFNN):
    """ A standard FFNN model with a classification (sigmoid) layer stacked on top of it.
    """

    def __init__(self, volume_manager, input_size, hidden_sizes, use_layer_normalization=False, use_skip_connections=False, **_):
        """
        Parameters
        ----------
        volume_manager : :class:`VolumeManger` object
            Use to evaluate the diffusion signal at specific coordinates.
        input_size : int
            Number of units each element X has.
        hidden_sizes : int, list of int
            Number of hidden units each FFNN layer should have.
        use_layer_normalization : bool
            Use LayerNormalization to normalize preactivations
        use_skip_connections : bool
            Use skip connections from the input to all hidden layers in the network, and from all hidden layers to the output layer
        """
        super().__init__(input_size, hidden_sizes, use_layer_normalization=use_layer_normalization, use_skip_connections=use_skip_connections)
        self.volume_manager = volume_manager
        self.output_size = 1  # Positive class probability

        output_layer_input_size = sum(self.hidden_sizes) if self.use_skip_connections else self.hidden_sizes[-1]
        self.layer_classification = LayerDense(output_layer_input_size, self.output_size, activation="sigmoid")

    def initialize(self, weights_initializer=initer.UniformInitializer(1234)):
        super().initialize(weights_initializer)
        self.layer_classification.initialize(weights_initializer)

    @property
    def hyperparameters(self):
        hyperparameters = super().hyperparameters
        return hyperparameters

    @property
    def parameters(self):
        return super().parameters + self.layer_classification.parameters

    def _fprop(self, Xi, *args):
        # Xi.shape : (batch_size, 4)
        # coords + dwi ID

        # coords : brain 3D coordinates.
        # coords.shape : (batch_size, 4) where the last column is a dwi ID.
        # args.shape : n_layers * (batch_size, layer_size)
        coords = Xi[:, :4]

        # Get diffusion data.
        # data_at_coords.shape : (batch_size, input_size)
        data_at_coords = self.volume_manager.eval_at_coords(coords)

        layer_outputs = super()._fprop(data_at_coords)

        # Compute positive class probability
        output_layer_input = T.concatenate(layer_outputs, axis=-1) if self.use_skip_connections else layer_outputs[-1]
        classification_out = self.layer_classification.fprop(output_layer_input)

        # Remove single-dimension from shape
        classification_out = classification_out[:, 0]

        return layer_outputs + (classification_out,)
class GRU_RegressionAndBinaryClassification(GRU):
    """ A standard GRU model with both a regression output layer and
    binary classification output layer.

    The regression layer consists in fully connected layer (DenseLayer)
    whereas the binary classification layer consists in a fully connected
    layer with a sigmoid non-linearity to learn when to stop.
    """
    def __init__(self, input_size, hidden_sizes, output_size, **_):
        """
        Parameters
        ----------
        input_size : int
            Number of units each element Xi in the input sequence X has.
        hidden_sizes : int, list of int
            Number of hidden units each GRU should have.
        output_size : int
            Number of units the regression layer should have.
        """
        super().__init__(input_size, hidden_sizes)
        self.output_size = output_size
        self.layer_regression = LayerRegression(self.hidden_sizes[-1],
                                                self.output_size)
        self.stopping_layer = LayerDense(self.hidden_sizes[-1] + input_size,
                                         1,
                                         activation="sigmoid",
                                         name="stopping")

    def initialize(self, weights_initializer=initer.UniformInitializer(1234)):
        super().initialize(weights_initializer)
        self.layer_regression.initialize(weights_initializer)
        self.stopping_layer.initialize(weights_initializer)

    @property
    def hyperparameters(self):
        hyperparameters = super().hyperparameters
        hyperparameters['output_size'] = self.output_size
        return hyperparameters

    @property
    def parameters(self):
        return super(
        ).parameters + self.layer_regression.parameters + self.stopping_layer.parameters

    def _fprop(self, Xi, Xi_plus1, *args):
        outputs = super()._fprop(Xi, *args)
        last_layer_h = outputs[len(self.hidden_sizes) - 1]
        regression_out = self.layer_regression.fprop(last_layer_h)
        stopping = self.stopping_layer.fprop(
            T.concatenate([last_layer_h, Xi_plus1], axis=1))

        return outputs + (regression_out, stopping)

    def get_output(self, X):
        outputs_info_h = []
        for hidden_size in self.hidden_sizes:
            outputs_info_h.append(T.zeros((X.shape[0], hidden_size)))

        results, updates = theano.scan(
            fn=self._fprop,
            outputs_info=outputs_info_h + [None, None],
            sequences=[{
                "input": T.transpose(
                    X, axes=(1, 0, 2)
                ),  # We want to scan over sequence elements, not the examples.
                "taps": [0, 1]
            }],
            n_steps=X.shape[1] - 1)

        self.graph_updates = updates
        # Put back the examples so they are in the first dimension.
        self.regression_out = T.transpose(results[-2], axes=(1, 0, 2))
        self.stopping = T.transpose(results[-1], axes=(1, 0, 2))

        return self.regression_out, self.stopping

    def use(self, X):
        directions = self.get_output(X)
        return directions
Example #11
0
    def __init__(self,
                 volume_manager,
                 input_size,
                 hidden_sizes,
                 output_size,
                 activation='tanh',
                 use_previous_direction=False,
                 predict_offset=False,
                 use_layer_normalization=False,
                 drop_prob=0.,
                 use_zoneout=False,
                 use_skip_connections=False,
                 seed=1234,
                 **_):
        """
        Parameters
        ----------
        volume_manager : :class:`VolumeManger` object
            Use to evaluate the diffusion signal at specific coordinates.
        input_size : int
            Number of units each element Xi in the input sequence X has.
        hidden_sizes : int, list of int
            Number of hidden units each GRU should have.
        output_size : int
            Number of units the regression layer should have.
        activation : str
            Activation function to apply on the "cell candidate"
        use_previous_direction : bool
            Use the previous direction as an additional input
        predict_offset : bool
            Predict the offset from the previous direction instead (need use_previous_direction).
        use_layer_normalization : bool
            Use LayerNormalization to normalize preactivations and stabilize hidden layer evolution
        drop_prob : float
            Dropout/Zoneout probability for recurrent networks. See: https://arxiv.org/pdf/1512.05287.pdf & https://arxiv.org/pdf/1606.01305.pdf
        use_zoneout : bool
            Use zoneout implementation instead of dropout
        use_skip_connections : bool
            Use skip connections from the input to all hidden layers in the network, and from all hidden layers to the output layer
        seed : int
            Random seed used for dropout normalization
        """
        super().__init__(input_size,
                         hidden_sizes,
                         activation=activation,
                         use_layer_normalization=use_layer_normalization,
                         drop_prob=drop_prob,
                         use_zoneout=use_zoneout,
                         use_skip_connections=use_skip_connections,
                         seed=seed)
        self.volume_manager = volume_manager
        self.output_size = output_size
        self.use_previous_direction = use_previous_direction
        self.predict_offset = predict_offset

        if self.predict_offset:
            assert self.use_previous_direction  # Need previous direction to predict offset.

        # Do not use dropout/zoneout in last hidden layer
        layer_regression_activation = "tanh" if self.predict_offset else "identity"
        output_layer_input_size = sum(
            self.hidden_sizes
        ) if self.use_skip_connections else self.hidden_sizes[-1]
        self.layer_regression = LayerDense(
            output_layer_input_size,
            self.output_size,
            activation=layer_regression_activation,
            name="GRU_Regression")
Example #12
0
    def __init__(self,
                 volume_manager,
                 input_size,
                 hidden_sizes,
                 output_size,
                 activation,
                 use_previous_direction=False,
                 predict_offset=False,
                 use_layer_normalization=False,
                 dropout_prob=0.,
                 neighborhood_radius=False,
                 seed=1234,
                 **_):
        """
        Parameters
        ----------
        volume_manager : :class:`VolumeManger` object
            Use to evaluate the diffusion signal at specific coordinates.
        input_size : int
            Number of units each element X has.
        hidden_sizes : int, list of int
            Number of hidden units each FFNN layer should have.
        output_size : int
            Number of units the regression layer should have.
        activation : str
            Name of the activation function to use in the hidden layers
        use_previous_direction : bool
            Use the previous direction as an additional input
        predict_offset : bool
            Predict the offset from the previous direction instead (need use_previous_direction).
        use_layer_normalization : bool
            Use LayerNormalization to normalize preactivations
        dropout_prob : float
            Dropout probability for recurrent networks. See: https://arxiv.org/pdf/1512.05287.pdf
        neighborhood_radius : float
            Add signal in positions around the current streamline coordinate to the input (with given length in voxel space); None = no neighborhood
        seed : int
            Random seed used for dropout normalization
        """
        self.neighborhood_radius = neighborhood_radius
        self.model_input_size = input_size
        if self.neighborhood_radius:
            self.neighborhood_directions = get_neighborhood_directions(
                self.neighborhood_radius)
            # Model input size is increased when using neighborhood
            self.model_input_size = input_size * self.neighborhood_directions.shape[
                0]

        super().__init__(self.model_input_size,
                         hidden_sizes,
                         activation=activation,
                         use_layer_normalization=use_layer_normalization,
                         dropout_prob=dropout_prob,
                         seed=seed)
        # Restore input size
        self.input_size = input_size

        self.volume_manager = volume_manager
        self.output_size = output_size
        self.use_previous_direction = use_previous_direction
        self.predict_offset = predict_offset

        if self.predict_offset:
            assert self.use_previous_direction  # Need previous direction to predict offset.

        layer_regression_activation = "tanh" if self.predict_offset else "identity"
        self.layer_regression = LayerDense(
            self.hidden_sizes[-1],
            self.output_size,
            activation=layer_regression_activation)

        if self.dropout_prob:
            p = 1 - self.dropout_prob
            self.dropout_vectors[
                self.layer_regression.name] = self.srng.binomial(
                    size=(self.layer_regression.input_size, ),
                    n=1,
                    p=p,
                    dtype=floatX) / p