mod_dense_layer¶
Implementation of a dense layer type for a neural network. author: Jonas Finkler, Alexander Knoll
Dependencies¶
Derived Types Summary¶
| Name | Description |
|---|---|
DenseLayer |
A dense (fully-connected) layer for a feed-forward neural network. In a dense layer, all nodes... |
Procedures Summary¶
| Name | Type | Description |
|---|---|---|
init_denselayer |
Subroutine | Construct a new DenseLayer object. |
reset_denselayer |
Subroutine | Reset a DenseLayer object. Should be called before any new backward pass. |
reset_prediction_denselayer |
Subroutine | |
reallocate_denselayer |
Subroutine | Change the dimensions of all internal arrays to accomodate batchsize input and output values. |
print_denselayer |
Subroutine | Print information about a DenseLayer object. |
get_parameters_denselayer |
Subroutine | Cast all free parameters of a DenseLayer object in one array of shape (1 x num_parameters). |
set_parameters_denselayer |
Subroutine | Set the free parameters of a DenseLayer object based on the flat array parameters. |
get_gradients_wrt_parameters_denselayer |
Subroutine | Cast the gradient of all parameters of a DenseLayer object in one array of shape (1 x... |
forward_denselayer |
Subroutine | Entry point for dense layer forward passes. This is the top-level routine for forward passes... |
backward_denselayer |
Subroutine | Entry point for dense layer backward passes. See <a... |
forward_denselayer_omp_parallel |
Subroutine | Calculate the output of the dense layer for a given batch of input samples. The output of a... |
forward_denselayer_serial |
Subroutine | Calculate the output of the dense layer for a given batch of input samples. The output of a... |
set_biases_block |
Subroutine | Helper to set biases for a block to avoid obscure array syntax in OMP regions |
forward_gradient_denselayer |
Subroutine | Perform a forward pass, storing the grad wrt the neural network input. |
backward_denselayer_serial |
Subroutine | Perform a batchwise backward pass for a dense layer. |
backward_denselayer_omp_parallel |
Subroutine | Perform an OpenMP-parallel, batchwise backward pass for a dense layer. This routine is very... |
backward_gradient_denselayer |
Subroutine | Perform a batchwise backward pass for a dense layer including the contributions of the input... |
init_parameter_hidden_layer |
Subroutine | Wrapper around the actual parameter initialization routines. See the documentation of the... |
init_hidden_layer_legacy_xavier |
Subroutine | Part of weight initialization as described in PHYSICAL REVIEW B 102, 174102 (2020) and J. Chem.... |
init_hidden_layer_lecun_normal |
Subroutine | LeCun normal initialization. Very similar to the original legacy_xavier initialization. We could... |
init_hidden_layer_lecun_uniform |
Subroutine | The sqrt(3) is to give unit variance and is not dependent on the type of activation function... |
init_hidden_layer_xavier |
Subroutine | Real famous Xavier initialization as described by Glorot / Bengio in Proceedings of the... |
init_hidden_layer_xavier_modified |
Subroutine | Modification as described in J.Chem. Theory Comput. 2023, 19,3509−3525. |
init_hidden_layer_nguyen_widrow |
Subroutine | Original Nguyen-Widrow initialization scheme This is the original Nguyen-Widrow initialization... |
init_hidden_layer_nguyen_widrow_runner1 |
Subroutine | Modified Nguyen-Widrow initialization scheme similar to RuNNer 1 This is not exactly the same... |
init_uniform_minus05_to_05 |
Subroutine | Initialize the weights from -0.5 to 0.5. Wrapper around initialize_array. The same logical was... |
init_parameter_last_layer |
Subroutine | Wrapper around the actual parameter initialization routines. See the documentation of the... |
init_last_layer_eckhoff |
Subroutine | Part of weight initialization as described in PHYSICAL REVIEW B 102, 174102 (2020) and J. Chem.... |
init_last_layer_eckhoff_modified |
Subroutine | |
init_last_layer_precondition |
Subroutine | Precondition weights and bias values. |
Derived Types Details¶
type DenseLayer¶
A dense (fully-connected) layer for a feed-forward neural network. In a dense layer, all nodes have incoming and outgoing connections, i.e. weights, to all nodes in the previous and following layers.
Components
-
num_parameters: integer– The total number of free parameters in the model. This should always be set by the `constructor` procedure of the derived type. -
num_parameters_mask: integer– The number of parameters that are not kept frozen by `mask`. -
mask: logical (dim: (:))– A logical array specifying, for each free parameter, whether it should be optimized or not. -
batchsize: integer– The number of samples that can pass concurrently through the layer. -
num_forward_passes: integer– Inherited from DenseLayer -
num_backward_passes: integer– Inherited from DenseLayer -
num_nodes_input: integer– The number of input nodes to this layer. -
num_nodes: integer– The number of nodes in this layer. -
idx: integer– Layer index. -
weights: real(DP) (dim: (:,:))– Connections to preceding layer nodes. -
biases: real(DP) (dim: (:))– Bias values for shifting each node's offset. -
dw: real(DP) (dim: (:,:))– Gradients of `weights`. -
db: real(DP) (dim: (:))– Gradients of `biases`. -
z: real(DP) (dim: (:,:))– Node values before activation. Required for backpropagation. -
dz_dq: real(DP) (dim: (:,:))– Derivatives of the unactivated node values wrt a single coordinate. -
da_dq: real(DP) (dim: (:,:))– Derivatives of the activated node values wrt a single coordinate. -
x: real(DP) (dim: (:,:))– The input to the layer. Dimensions are inputsize x batchsize. -
a: real(DP) (dim: (:,:))– Layer output. -
grad: real(DP) (dim: (:,:))– doutput/dinput. -
dgrad_dq: real(DP) (dim: (:,:))– doutput/dinput/dx -
f_act: class(AbstractActivationFunction)– Activation function. -
lsaturation_check: logical– Inherited from DenseLayer -
forward_ptr: procedure– Pointer for delegating the forward pass to the correct implementation. We have an OpenMP-parallel and a non-parallel forward pass implementation. The serial implementation is more efficient for small batchsizes. We align the pointer whenever reallocate_inputlayer is called, which is usually only once. -
backward_ptr: procedure– Pointer for delegating the backward pass. See forward_ptr for details.
Type-Bound Procedures
| Name | Attributes | Binding |
|---|---|---|
| initialize | public | ⇒ init_denselayer |
| public | ⇒ print_denselayer | |
| get_parameters | public | ⇒ get_parameters_denselayer |
| set_parameters | public | ⇒ set_parameters_denselayer |
| get_gradients_wrt_parameters | public | ⇒ get_gradients_wrt_parameters_denselayer |
| reset | public | ⇒ reset_denselayer |
| reset_prediction | public | ⇒ reset_prediction_denselayer |
| forward | public | ⇒ forward_denselayer |
| forward_gradient | public | ⇒ forward_gradient_denselayer |
| backward | public | ⇒ backward_denselayer |
| backward_gradient | public | ⇒ backward_gradient_denselayer |
| reallocate | public | ⇒ reallocate_denselayer |
| init_parameter_hidden_layer | public | ⇒ init_parameter_hidden_layer |
| init_parameter_last_layer | public | ⇒ init_parameter_last_layer |
| init_hidden_layer_legacy_xavier | private | ⇒ init_hidden_layer_legacy_xavier |
| init_hidden_layer_lecun_uniform | private | ⇒ init_hidden_layer_lecun_uniform |
| init_hidden_layer_lecun_normal | private | ⇒ init_hidden_layer_lecun_normal |
| init_hidden_layer_xavier | private | ⇒ init_hidden_layer_xavier |
| init_hidden_layer_xavier_modified | private | ⇒ init_hidden_layer_xavier_modified |
| init_hidden_layer_nguyen_widrow | private | ⇒ init_hidden_layer_nguyen_widrow |
| init_hidden_layer_nguyen_widrow_runner1 | private | ⇒ init_hidden_layer_nguyen_widrow_runner1 |
| init_last_layer_eckhoff | private | ⇒ init_last_layer_eckhoff |
| init_last_layer_eckhoff_modified | private | ⇒ init_last_layer_eckhoff_modified |
| init_last_layer_precondition | public | ⇒ init_last_layer_precondition |
| init_uniform_minus05_to_05 | private | ⇒ init_uniform_minus05_to_05 |
Procedure Details¶
subroutine init_denselayer¶
subroutine init_denselayer( &
self, &
num_nodes_input, &
num_nodes, &
activation, &
batchsize, &
idx, &
lsaturation_check, &
mask &
)
Construct a new DenseLayer object.
Arguments
-
self: class(DenseLayer) [inout]– The constructed layer. -
num_nodes_input: integer [in]– The number of nodes in the preceeding layer. -
num_nodes: integer [in]– The number of requested nodes in this layer. -
activation: class(AbstractActivationFunction) [in]– The activation function for all nodes. -
batchsize: integer [in] -
idx: integer [in] -
lsaturation_check: logical [in] -
mask: logical (dim: (:)) [in]– logical mask
subroutine reset_denselayer¶
subroutine reset_denselayer( &
self &
)
Reset a DenseLayer object. Should be called before any new
backward pass.
Arguments
-
self: class(DenseLayer) [inout]
Called By
subroutine reset_prediction_denselayer¶
subroutine reset_prediction_denselayer( &
self &
)
Arguments
-
self: class(DenseLayer) [inout]
Called By
subroutine reallocate_denselayer¶
subroutine reallocate_denselayer( &
self, &
batchsize &
)
Change the dimensions of all internal arrays to accomodate
batchsize input and output values.
Arguments
-
self: class(DenseLayer) [inout] -
batchsize: integer [in]
Called By
subroutine print_denselayer¶
subroutine print_denselayer( &
self &
)
Print information about a DenseLayer object.
Arguments
-
self: class(DenseLayer) [in]
subroutine get_parameters_denselayer¶
subroutine get_parameters_denselayer( &
self, &
parameters, &
luse_mask &
)
Cast all free parameters of a DenseLayer object in one array
of shape (1 x num_parameters).
Arguments
-
self: class(DenseLayer) [in] -
parameters: real(DP) (dim: (:)) [inout]– The parameters array to be filled. -
luse_mask: logical [in]– Gets parameters with or without mask.
Called By
subroutine set_parameters_denselayer¶
subroutine set_parameters_denselayer( &
self, &
parameters, &
luse_mask &
)
Set the free parameters of a DenseLayer object based on the
flat array parameters.
Arguments
-
self: class(DenseLayer) [inout] -
parameters: real(DP) (dim: (:)) [in]– The filled parameters array. -
luse_mask: logical [in]– Sets parameters with or without mask.
Called By
subroutine get_gradients_wrt_parameters_denselayer¶
subroutine get_gradients_wrt_parameters_denselayer( &
self, &
gradients, &
luse_mask &
)
Cast the gradient of all parameters of a DenseLayer object
in one array of shape (1 x num_parameters).
Arguments
-
self: class(DenseLayer) [inout] -
gradients: real(DP) (dim: (:)) [inout]– The list of gradients, one for each free parameter in `self`. -
luse_mask: logical [in]– Gets gradients with or without mask.
Called By
subroutine forward_denselayer¶
subroutine forward_denselayer( &
self, &
x &
)
Entry point for dense layer forward passes.
This is the top-level routine for forward passes that is exposed as the
public interface of this type. It is a wrapper routine that delegates the
forward call to one of the two forward implementations. See
forward_ptr for details.
Arguments
-
self: class(DenseLayer) [inout] -
x: real(DP) (dim: (self%num_nodes_input,self%batchsize)) [in]
Called By
subroutine backward_denselayer¶
subroutine backward_denselayer( &
self, &
grad_follow, &
calc_gradient_wrt_parameters &
)
Entry point for dense layer backward passes.
See forward_denselayer for details.
Arguments
-
self: class(DenseLayer) [inout] -
grad_follow: real(DP) (dim: (self%num_nodes,self%batchsize)) [in] -
calc_gradient_wrt_parameters: logical [in]
Called By
subroutine forward_denselayer_omp_parallel¶
subroutine forward_denselayer_omp_parallel( &
self, &
x &
)
Calculate the output of the dense layer for a given batch of input samples.
The output of a dense layer is calculated according to output = activation(weights * input + biases) We parallelize the Batch Dimension (N). Each thread handles a subset of the columns (samples).
Arguments
-
self: class(DenseLayer) [inout] -
x: real(DP) (dim: (self%num_nodes_input,self%batchsize)) [in]
subroutine forward_denselayer_serial¶
subroutine forward_denselayer_serial( &
self, &
x &
)
Calculate the output of the dense layer for a given batch of input samples.
The output of a dense layer is calculated according to output = activation(weights * input + biases) This routine is not OMP parallelized. For small batches, the overhead associated with thread initialization becomes too large.
Arguments
-
self: class(DenseLayer) [inout] -
x: real(DP) (dim: (self%num_nodes_input,self%batchsize)) [in]
subroutine set_biases_block¶
subroutine set_biases_block( &
z, &
b, &
istart, &
iend, &
n_nodes &
)
Helper to set biases for a block to avoid obscure array syntax in OMP regions
Arguments
-
z: real(DP) (dim: (n_nodes,*)) [inout] -
b: real(DP) (dim: (n_nodes)) [in] -
istart: integer [in] -
iend: integer [in] -
n_nodes: integer [in]
Called By
subroutine forward_gradient_denselayer¶
subroutine forward_gradient_denselayer( &
self, &
x, &
dx_dq &
)
Perform a forward pass, storing the grad wrt the neural network input.
Arguments
-
self: class(DenseLayer) [inout] -
x: real(DP) (dim: (self%num_nodes_input,self%batchsize)) [in]– The activations of the preceeding layer. -
dx_dq: real(DP) (dim: (self%num_nodes_input,self%batchsize)) [in]– Derivative of the preceeding layer activations `x` with respect to some variable q.
subroutine backward_denselayer_serial¶
subroutine backward_denselayer_serial( &
self, &
grad_follow, &
calc_gradient_wrt_parameters &
)
Perform a batchwise backward pass for a dense layer.
Arguments
-
self: class(DenseLayer) [inout] -
grad_follow: real(DP) (dim: (self%num_nodes,self%batchsize)) [in] -
calc_gradient_wrt_parameters: logical [in]
subroutine backward_denselayer_omp_parallel¶
subroutine backward_denselayer_omp_parallel( &
self, &
grad_follow, &
calc_gradient_wrt_parameters &
)
Perform an OpenMP-parallel, batchwise backward pass for a dense layer.
This routine is very efficient for large batch sizes. For small batches,
use backward_denselayer_serial.
@todo manual sum may be faster than sum(..., dim=2) for short arrays
Arguments
-
self: class(DenseLayer) [inout] -
grad_follow: real(DP) (dim: (self%num_nodes,self%batchsize)) [in] -
calc_gradient_wrt_parameters: logical [in]
subroutine backward_gradient_denselayer¶
subroutine backward_gradient_denselayer( &
self, &
dx_dq, &
grad_follow, &
dgrad_dq_follow &
)
Perform a batchwise backward pass for a dense layer including the contributions of the input gradient. All math is documented at the relevant places. Notation: * = Hadamard (element-wise) product . = Dot product (matrix multiplication) = (d^2a3/da2dx * f'(z2) ) . a1 + (da3/da2 * f''(z2) * dz2/dx) . a1 + (da3/da2 * f'(z2) ) . da1/dx
Arguments
-
self: class(DenseLayer) [inout] -
dx_dq: real(DP) (dim: (self%num_nodes_input,self%batchsize)) [in] -
grad_follow: real(DP) (dim: (self%num_nodes,self%batchsize)) [in] -
dgrad_dq_follow: real(DP) (dim: (self%num_nodes,self%batchsize)) [in]
Called By
subroutine init_parameter_hidden_layer¶
subroutine init_parameter_hidden_layer( &
self, &
init_method, &
rng, &
min_weight, &
max_weight &
)
Wrapper around the actual parameter initialization routines.
See the documentation of the actual routines for detailed information.
Arguments
- : class(DenseLayer) [inout]
- : character [in]– The initialization method to be used. Can be one of: - 'legacy_xavier' or 'legacy_glorot': Initialization as described PHYSICAL REVIEW B 102, 174102 (2020) and J. Chem. Phys. 153, 164107 (2020). This is not the famous Xavier initialization - 'xavier' or 'glorot': The famous Xavier initialization - 'xavier_modified' or 'glorot_modified': Modified Xavier initialization - 'lecun_normal': LeCun initialization based on normal distribution. The same as legacy_xavier, but with normal distribution instead of uniform. - 'lecun_uniform', 'legacy_xavier_modified', 'legacy_glorot_modified': LeCun initialization based on uniform distribution. - 'nguyen_widrow': Nguyen-Widrow initialization - 'nguyen_widrow_legacy' or 'nguyen_widrow_runner1': Nguyen-Widrow initialization as in RuNNer 1 - 'uniform': Uniform initialization - 'normal' or 'gaussian': Normal initialization - 'zero': Initialize all weights and biases to zero See their respective documentation for more information. For all other values an error will be thrown.
- : class(RandomNumberGenerator) [inout]– The random number generator to be used for initialization.
- : real(DP) [in]– The minimum value for the weights. Only used for 'uniform', 'normal', and 'gaussian' initialization.
- : real(DP) [in]– The maximum value for the weights. Only used for 'uniform', 'normal', and 'gaussian' initialization.
Called By
subroutine init_hidden_layer_legacy_xavier¶
subroutine init_hidden_layer_legacy_xavier( &
self, &
rng &
)
Part of weight initialization as described in PHYSICAL REVIEW B 102, 174102 (2020) and J. Chem. Phys. 153, 164107 (2020). This is not the famous Xavier initialization, but an initialization that is also mentioned in the Xavier / Bengio paper, but it was used for a long time under the name Xavier initialization in RuNNer.
Arguments
- : class(DenseLayer) [inout]
- : class(RandomNumberGenerator) [inout]
Called By
subroutine init_hidden_layer_lecun_normal¶
subroutine init_hidden_layer_lecun_normal( &
self, &
rng &
)
LeCun normal initialization. Very similar to the original legacy_xavier initialization. We could think of getting rid of legacy_xavier for this (more correct) version. Standard deviations of the normal distribution are adapted to yield approximately unit variance at all nodes.
Arguments
- : class(DenseLayer) [inout]
- : class(RandomNumberGenerator) [inout]
Called By
subroutine init_hidden_layer_lecun_uniform¶
subroutine init_hidden_layer_lecun_uniform( &
self, &
rng &
)
The sqrt(3) is to give unit variance and is not dependent on the type of activation function that is used.
Arguments
- : class(DenseLayer) [inout]
- : class(RandomNumberGenerator) [inout]
Called By
subroutine init_hidden_layer_xavier¶
subroutine init_hidden_layer_xavier( &
self, &
rng &
)
Real famous Xavier initialization as described by Glorot / Bengio in Proceedings of the Thirteenth International Conference on Artificial Intelligence and Statistics, PMLR 9:249-256, 2010. Note that for the complete Xavier initialization also the output layers weights have to be initialized with Xavier.
Arguments
- : class(DenseLayer) [inout]
- : class(RandomNumberGenerator) [inout]
Called By
subroutine init_hidden_layer_xavier_modified¶
subroutine init_hidden_layer_xavier_modified( &
self, &
rng &
)
Modification as described in J.Chem. Theory Comput. 2023, 19,3509−3525.
Arguments
- : class(DenseLayer) [inout]
- : class(RandomNumberGenerator) [inout]
Called By
subroutine init_hidden_layer_nguyen_widrow¶
subroutine init_hidden_layer_nguyen_widrow( &
self, &
rng &
)
Original Nguyen-Widrow initialization scheme
This is the original Nguyen-Widrow initialization scheme as proposed in the literature (D. Nguyen, B. Widrow in 1990 IJCNN international joint conference on neural networks, IEEE, 1990, pp. 21–26.)
Caveat: - they only applied it to the first (only) hidden layer. - they only investigated sigmoid activation functions. - assumes that the input layer gives values in the range [-1, 1] this is not the case in RuNNer even if the features maps are scaled and centered since this does not lead to a uniform distribution in the range [-1, 1].
Arguments
- : class(DenseLayer) [inout]
- : class(RandomNumberGenerator) [inout]
Called By
subroutine init_hidden_layer_nguyen_widrow_runner1¶
subroutine init_hidden_layer_nguyen_widrow_runner1( &
self, &
rng &
)
Modified Nguyen-Widrow initialization scheme similar to RuNNer 1
This is not exactly the same scheme that was implemented in RuNNer 1. RuNNer 1 applied Nguyen Widrow schme only to the first two hidden layers. The algorithm tries to account for a diffrent range of activation functions by applying a scaling factor. However, RuNNer 1 scaled ALL weights and biases of the whole network by a factor of 2.0 if the activation function of the first node (!) of the FIRST hidden layer was a hyperbolic tangent function. We check each layer individually and apply the scaling factor only to the biases and weights, if the activation function of THIS layer is a hyperbolic tangent function. RuNNer 1 also allowed to set the min/max weights to other values than -1 and 1 even if, we forbid this here since it is not part of the original scheme and was rarly (if ever) used in RuNNer 1. RuNNer 1 also applied a reinitialized the weights from -0.5 to 0.5 to the output layer if Nguyen Widrow was applied. To do this, call init_minus05_to_05 on the last layer.
Arguments
- : class(DenseLayer) [inout]
- : class(RandomNumberGenerator) [inout]
Called By
subroutine init_uniform_minus05_to_05¶
subroutine init_uniform_minus05_to_05( &
self, &
rng &
)
Initialize the weights from -0.5 to 0.5.
Wrapper around initialize_array. The same logical was applied to the last layer in RuNNer 1 if Nguyne Widrow was set. So we put it in it's own routine so that we can call it conveniently.
Arguments
-
self: class(DenseLayer) [inout] -
rng: class(RandomNumberGenerator) [inout]
Called By
subroutine init_parameter_last_layer¶
subroutine init_parameter_last_layer( &
self, &
init_method, &
rng, &
min_weight, &
max_weight, &
target_mean, &
target_range, &
target_std &
)
Wrapper around the actual parameter initialization routines.
See the documentation of the actual routines for detailed information.
Arguments
-
self: class(DenseLayer) [inout] -
init_method: character [in]– The initialization method to be used. Can be one of: - 'eckhoff': As described in J. Chem. Phys. 153, 164107 (2020) - 'eckhoff_modified': As described in J. Chem. Phys. 153, 164107 (2020) - 'uniform': Draw values from a uniform distribution. - 'normal' or 'gaussian': Draw values from a normal distribution. - 'nw_last_layer_runner1', 'uniform05' or 'nw_last_layer_legacy': Uniform initialization from -0.5 to 0.5 as it was done in RuNNer 1 if Nguyen-Widrow was applied. - 'zero': Initialize all weights and biases to zero See their respective documentation for more information. For all other values an error will be thrown. -
rng: class(RandomNumberGenerator) [inout]– The random number generator to be used for initialization. -
min_weight: real(DP) [in]– The minimum value for the weights. Only used for 'uniform', 'normal', and 'gaussian' initialization. -
max_weight: real(DP) [in]– The maximum value for the weights. Only used for 'uniform', 'normal', and 'gaussian' initialization. -
target_mean: real(DP) [in]– The target mean value for the output of the layer. Only used for 'eckhoff' and 'eckhoff_modified' initialization. -
target_range: real(DP) [in]– The target range for the output of the layer. Only used for 'eckhoff' initialization. -
target_std: real(DP) [in]– The target standard deviation for the output of the layer. Only used for 'eckhoff_modified' initialization.
Called By
subroutine init_last_layer_eckhoff¶
subroutine init_last_layer_eckhoff( &
self, &
rng, &
target_mean, &
target_range &
)
Part of weight initialization as described in PHYSICAL REVIEW B 102, 174102 (2020) and J. Chem. Phys. 153, 164107 (2020).
Arguments
-
self: class(DenseLayer) [inout] -
rng: class(RandomNumberGenerator) [inout] -
target_mean: real(DP) [in] -
target_range: real(DP) [in]
Called By
subroutine init_last_layer_eckhoff_modified¶
subroutine init_last_layer_eckhoff_modified( &
self, &
rng, &
target_mean, &
target_std &
)
Arguments
-
self: class(DenseLayer) [inout] -
rng: class(RandomNumberGenerator) [inout] -
target_mean: real(DP) [in] -
target_std: real(DP) [in]
Called By
subroutine init_last_layer_precondition¶
subroutine init_last_layer_precondition( &
self, &
target_mean, &
prediction_mean, &
target_std, &
prediction_std &
)
Precondition weights and bias values.
Arguments
-
self: class(DenseLayer) [inout] -
target_mean: real(DP) [in] -
prediction_mean: real(DP) [in] -
target_std: real(DP) [in] -
prediction_std: real(DP) [in]
Called By