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 __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")
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
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
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")
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
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")
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