From 1c3defeb931068139b3b183c613b9120e851b043 Mon Sep 17 00:00:00 2001 From: milancurcic Date: Thu, 24 Apr 2025 11:21:38 -0400 Subject: [PATCH 1/8] Generic conv constructor for specific conv1d and conv2d layers --- README.md | 3 +- example/cnn_mnist.f90 | 6 +- example/cnn_mnist_1d.f90 | 2 +- fpm.toml | 2 +- src/nf.f90 | 3 +- src/nf/nf_layer_constructors.f90 | 121 +++++++++++---------- src/nf/nf_layer_constructors_submodule.f90 | 19 +++- src/nf/nf_network_submodule.f90 | 2 +- test/test_conv1d_layer.f90 | 6 +- test/test_conv1d_network.f90 | 18 +-- test/test_conv2d_layer.f90 | 6 +- test/test_conv2d_network.f90 | 18 +-- test/test_get_set_network_params.f90 | 6 +- test/test_insert_flatten.f90 | 6 +- 14 files changed, 115 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 65964786..4ca01bc3 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,7 @@ Read the paper [here](https://arxiv.org/abs/1902.06714). | Dense (fully-connected) | `dense` | `input1d`, `dense`, `dropout`, `flatten` | 1 | ✅ | ✅ | | Dropout | `dropout` | `dense`, `flatten`, `input1d` | 1 | ✅ | ✅ | | Locally connected (1-d) | `locally_connected1d` | `input2d`, `locally_connected1d`, `conv1d`, `maxpool1d`, `reshape2d` | 2 | ✅ | ✅ | -| Convolutional (1-d) | `conv1d` | `input2d`, `conv1d`, `maxpool1d`, `reshape2d` | 2 | ✅ | ✅ | -| Convolutional (2-d) | `conv2d` | `input3d`, `conv2d`, `maxpool2d`, `reshape` | 3 | ✅ | ✅ | +| Convolutional (1-d and 2-d) | `conv` | `input`, `conv`, `maxpool`, `reshape` | 2, 3 | ✅ | ✅ | | Max-pooling (1-d) | `maxpool1d` | `input2d`, `conv1d`, `maxpool1d`, `reshape2d` | 2 | ✅ | ✅ | | Max-pooling (2-d) | `maxpool2d` | `input3d`, `conv2d`, `maxpool2d`, `reshape` | 3 | ✅ | ✅ | | Linear (2-d) | `linear2d` | `input2d`, `layernorm`, `linear2d`, `self_attention` | 2 | ✅ | ✅ | diff --git a/example/cnn_mnist.f90 b/example/cnn_mnist.f90 index d2f61723..06f730ab 100644 --- a/example/cnn_mnist.f90 +++ b/example/cnn_mnist.f90 @@ -1,7 +1,7 @@ program cnn_mnist use nf, only: network, sgd, & - input, conv2d, maxpool2d, flatten, dense, reshape, & + input, conv, maxpool2d, flatten, dense, reshape, & load_mnist, label_digits, softmax, relu implicit none @@ -21,9 +21,9 @@ program cnn_mnist net = network([ & input(784), & reshape(1, 28, 28), & - conv2d(filters=8, kernel_size=3, activation=relu()), & + conv(filters=8, kernel_width=3, kernel_height=3, activation=relu()), & maxpool2d(pool_size=2), & - conv2d(filters=16, kernel_size=3, activation=relu()), & + conv(filters=16, kernel_width=3, kernel_height=3, activation=relu()), & maxpool2d(pool_size=2), & dense(10, activation=softmax()) & ]) diff --git a/example/cnn_mnist_1d.f90 b/example/cnn_mnist_1d.f90 index b350a2f0..c679eb4f 100644 --- a/example/cnn_mnist_1d.f90 +++ b/example/cnn_mnist_1d.f90 @@ -1,7 +1,7 @@ program cnn_mnist_1d use nf, only: network, sgd, & - input, conv1d, maxpool1d, flatten, dense, reshape, locally_connected1d, & + input, maxpool1d, flatten, dense, reshape, locally_connected1d, & load_mnist, label_digits, softmax, relu implicit none diff --git a/fpm.toml b/fpm.toml index 1f2c2ac9..0d85b9dc 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "neural-fortran" -version = "0.21.0" +version = "0.22.0" license = "MIT" author = "Milan Curcic" maintainer = "mcurcic@miami.edu" diff --git a/src/nf.f90 b/src/nf.f90 index f644826d..407a5db3 100644 --- a/src/nf.f90 +++ b/src/nf.f90 @@ -3,8 +3,7 @@ module nf use nf_datasets_mnist, only: label_digits, load_mnist use nf_layer, only: layer use nf_layer_constructors, only: & - conv1d, & - conv2d, & + conv, & dense, & dropout, & embedding, & diff --git a/src/nf/nf_layer_constructors.f90 b/src/nf/nf_layer_constructors.f90 index d3f06ca3..10333a32 100644 --- a/src/nf/nf_layer_constructors.f90 +++ b/src/nf/nf_layer_constructors.f90 @@ -9,8 +9,7 @@ module nf_layer_constructors private public :: & - conv1d, & - conv2d, & + conv, & dense, & dropout, & flatten, & @@ -94,6 +93,68 @@ end function input3d end interface input + interface conv + + module function conv1d(filters, kernel_width, activation) result(res) + !! 1-d convolutional layer constructor. + !! + !! This layer is for building 1-d convolutional network. + !! Although the established convention is to call these layers 1-d, + !! the shape of the data is actually 2-d: image width and the number of channels. + !! A conv1d layer must not be the first layer in the network. + !! + !! This specific function is available under a generic name `conv`. + !! + !! Example: + !! + !! ``` + !! use nf, only :: conv1d, layer + !! type(layer) :: conv1d_layer + !! conv1d_layer = conv1d(filters=32, kernel_size=3) + !! ``` + integer, intent(in) :: filters + !! Number of filters in the output of the layer + integer, intent(in) :: kernel_width + !! Width of the convolution window, commonly 3 or 5 + class(activation_function), intent(in), optional :: activation + !! Activation function (default sigmoid) + type(layer) :: res + !! Resulting layer instance + end function conv1d + + module function conv2d(filters, kernel_width, kernel_height, activation) result(res) + !! 2-d convolutional layer constructor. + !! + !! This layer is for building 2-d convolutional network. + !! Although the established convention is to call these layers 2-d, + !! the shape of the data is actually 3-d: image width, image height, + !! and the number of channels. + !! A conv2d layer must not be the first layer in the network. + !! + !! This specific function is available under a generic name `conv`. + !! + !! Example: + !! + !! ``` + !! use nf, only :: conv2d, layer + !! type(layer) :: conv2d_layer + !! conv2d_layer = conv2d(filters=32, kernel_width=3, kernel_height=3) + !! ``` + integer, intent(in) :: filters + !! Number of filters in the output of the layer + integer, intent(in) :: kernel_width + !! Width of the convolution window, commonly 3 or 5 + integer, intent(in) :: kernel_height + !! Height of the convolution window, commonly 3 or 5 + class(activation_function), intent(in), optional :: activation + !! Activation function (default sigmoid) + type(layer) :: res + !! Resulting layer instance + end function conv2d + + end interface conv + + interface reshape module function reshape2d(dim1, dim2) result(res) @@ -179,66 +240,12 @@ module function flatten() result(res) !! Resulting layer instance end function flatten - module function conv1d(filters, kernel_size, activation) result(res) - !! 1-d convolutional layer constructor. - !! - !! This layer is for building 1-d convolutional network. - !! Although the established convention is to call these layers 1-d, - !! the shape of the data is actually 2-d: image width - !! and the number of channels. - !! A conv1d layer must not be the first layer in the network. - !! - !! Example: - !! - !! ``` - !! use nf, only :: conv1d, layer - !! type(layer) :: conv1d_layer - !! conv1d_layer = dense(filters=32, kernel_size=3) - !! conv1d_layer = dense(filters=32, kernel_size=3, activation='relu') - !! ``` - integer, intent(in) :: filters - !! Number of filters in the output of the layer - integer, intent(in) :: kernel_size - !! Width of the convolution window, commonly 3 or 5 - class(activation_function), intent(in), optional :: activation - !! Activation function (default sigmoid) - type(layer) :: res - !! Resulting layer instance - end function conv1d - - module function conv2d(filters, kernel_size, activation) result(res) - !! 2-d convolutional layer constructor. - !! - !! This layer is for building 2-d convolutional network. - !! Although the established convention is to call these layers 2-d, - !! the shape of the data is actuall 3-d: image width, image height, - !! and the number of channels. - !! A conv2d layer must not be the first layer in the network. - !! - !! Example: - !! - !! ``` - !! use nf, only :: conv2d, layer - !! type(layer) :: conv2d_layer - !! conv2d_layer = dense(filters=32, kernel_size=3) - !! conv2d_layer = dense(filters=32, kernel_size=3, activation='relu') - !! ``` - integer, intent(in) :: filters - !! Number of filters in the output of the layer - integer, intent(in) :: kernel_size - !! Width of the convolution window, commonly 3 or 5 - class(activation_function), intent(in), optional :: activation - !! Activation function (default sigmoid) - type(layer) :: res - !! Resulting layer instance - end function conv2d - module function locally_connected1d(filters, kernel_size, activation) result(res) !! 1-d locally connected network constructor !! !! This layer is for building 1-d locally connected network. !! Although the established convention is to call these layers 1-d, - !! the shape of the data is actuall 2-d: image width, + !! the shape of the data is actually 2-d: image width, !! and the number of channels. !! A locally connected 1d layer must not be the first layer in the network. !! diff --git a/src/nf/nf_layer_constructors_submodule.f90 b/src/nf/nf_layer_constructors_submodule.f90 index 1665d38a..052e7201 100644 --- a/src/nf/nf_layer_constructors_submodule.f90 +++ b/src/nf/nf_layer_constructors_submodule.f90 @@ -24,9 +24,9 @@ contains - module function conv1d(filters, kernel_size, activation) result(res) + module function conv1d(filters, kernel_width, activation) result(res) integer, intent(in) :: filters - integer, intent(in) :: kernel_size + integer, intent(in) :: kernel_width class(activation_function), intent(in), optional :: activation type(layer) :: res @@ -44,19 +44,26 @@ module function conv1d(filters, kernel_size, activation) result(res) allocate( & res % p, & - source=conv1d_layer(filters, kernel_size, activation_tmp) & + source=conv1d_layer(filters, kernel_width, activation_tmp) & ) end function conv1d - module function conv2d(filters, kernel_size, activation) result(res) + module function conv2d(filters, kernel_width, kernel_height, activation) result(res) integer, intent(in) :: filters - integer, intent(in) :: kernel_size + integer, intent(in) :: kernel_width + integer, intent(in) :: kernel_height class(activation_function), intent(in), optional :: activation type(layer) :: res class(activation_function), allocatable :: activation_tmp + ! Enforce kernel_width == kernel_height for now; + ! If non-square kernels show to be desired, we'll relax this constraint + ! and refactor conv2d_layer to work with non-square kernels. + if (kernel_width /= kernel_height) & + error stop 'kernel_width must equal kernel_height in a conv2d layer' + res % name = 'conv2d' if (present(activation)) then @@ -69,7 +76,7 @@ module function conv2d(filters, kernel_size, activation) result(res) allocate( & res % p, & - source=conv2d_layer(filters, kernel_size, activation_tmp) & + source=conv2d_layer(filters, kernel_width, activation_tmp) & ) end function conv2d diff --git a/src/nf/nf_network_submodule.f90 b/src/nf/nf_network_submodule.f90 index 449b5a5b..d8f5ff50 100644 --- a/src/nf/nf_network_submodule.f90 +++ b/src/nf/nf_network_submodule.f90 @@ -18,7 +18,7 @@ use nf_embedding_layer, only: embedding_layer use nf_layernorm_layer, only: layernorm_layer use nf_layer, only: layer - use nf_layer_constructors, only: conv1d, conv2d, dense, flatten, input, maxpool1d, maxpool2d, reshape + use nf_layer_constructors, only: flatten use nf_loss, only: quadratic use nf_optimizers, only: optimizer_base_type, sgd use nf_parallel, only: tile_indices diff --git a/test/test_conv1d_layer.f90 b/test/test_conv1d_layer.f90 index 81d03c1f..b80b520b 100644 --- a/test/test_conv1d_layer.f90 +++ b/test/test_conv1d_layer.f90 @@ -1,7 +1,7 @@ program test_conv1d_layer use iso_fortran_env, only: stderr => error_unit - use nf, only: conv1d, input, layer + use nf, only: conv, input, layer use nf_input2d_layer, only: input2d_layer implicit none @@ -12,7 +12,7 @@ program test_conv1d_layer real, parameter :: tolerance = 1e-7 logical :: ok = .true. - conv1d_layer = conv1d(filters, kernel_size) + conv1d_layer = conv(filters, kernel_size) if (.not. conv1d_layer % name == 'conv1d') then ok = .false. @@ -52,7 +52,7 @@ program test_conv1d_layer sample_input = 0 input_layer = input(1, 3) - conv1d_layer = conv1d(filters, kernel_size) + conv1d_layer = conv(filters, kernel_size) call conv1d_layer % init(input_layer) select type(this_layer => input_layer % p); type is(input2d_layer) diff --git a/test/test_conv1d_network.f90 b/test/test_conv1d_network.f90 index 5a353cf9..b00cb97c 100644 --- a/test/test_conv1d_network.f90 +++ b/test/test_conv1d_network.f90 @@ -1,7 +1,7 @@ program test_conv1d_network use iso_fortran_env, only: stderr => error_unit - use nf, only: conv1d, input, network, dense, sgd, maxpool1d + use nf, only: conv, input, network, dense, sgd, maxpool1d implicit none @@ -12,8 +12,8 @@ program test_conv1d_network ! 3-layer convolutional network net = network([ & input(3, 32), & - conv1d(filters=16, kernel_size=3), & - conv1d(filters=32, kernel_size=3) & + conv(filters=16, kernel_width=3), & + conv(filters=32, kernel_width=3) & ]) if (.not. size(net % layers) == 3) then @@ -49,8 +49,8 @@ program test_conv1d_network cnn = network([ & input(1, 5), & - conv1d(filters=1, kernel_size=3), & - conv1d(filters=1, kernel_size=3), & + conv(filters=1, kernel_width=3), & + conv(filters=1, kernel_width=3), & dense(1) & ]) @@ -86,9 +86,9 @@ program test_conv1d_network cnn = network([ & input(1, 8), & - conv1d(filters=1, kernel_size=3), & + conv(filters=1, kernel_width=3), & maxpool1d(pool_size=2), & - conv1d(filters=1, kernel_size=3), & + conv(filters=1, kernel_width=3), & dense(1) & ]) @@ -121,9 +121,9 @@ program test_conv1d_network cnn = network([ & input(1, 12), & - conv1d(filters=1, kernel_size=3), & ! 1x12x12 input, 1x10x10 output + conv(filters=1, kernel_width=3), & ! 1x12x12 input, 1x10x10 output maxpool1d(pool_size=2), & ! 1x10x10 input, 1x5x5 output - conv1d(filters=1, kernel_size=3), & ! 1x5x5 input, 1x3x3 output + conv(filters=1, kernel_width=3), & ! 1x5x5 input, 1x3x3 output dense(9) & ! 9 outputs ]) diff --git a/test/test_conv2d_layer.f90 b/test/test_conv2d_layer.f90 index 10a14c5e..2d5868b9 100644 --- a/test/test_conv2d_layer.f90 +++ b/test/test_conv2d_layer.f90 @@ -1,7 +1,7 @@ program test_conv2d_layer use iso_fortran_env, only: stderr => error_unit - use nf, only: conv2d, input, layer + use nf, only: conv, input, layer use nf_input3d_layer, only: input3d_layer implicit none @@ -12,7 +12,7 @@ program test_conv2d_layer real, parameter :: tolerance = 1e-7 logical :: ok = .true. - conv_layer = conv2d(filters, kernel_size) + conv_layer = conv(filters, kernel_size, kernel_size) if (.not. conv_layer % name == 'conv2d') then ok = .false. @@ -52,7 +52,7 @@ program test_conv2d_layer sample_input = 0 input_layer = input(1, 3, 3) - conv_layer = conv2d(filters, kernel_size) + conv_layer = conv(filters, kernel_size, kernel_size) call conv_layer % init(input_layer) select type(this_layer => input_layer % p); type is(input3d_layer) diff --git a/test/test_conv2d_network.f90 b/test/test_conv2d_network.f90 index 73c4595a..cbe64721 100644 --- a/test/test_conv2d_network.f90 +++ b/test/test_conv2d_network.f90 @@ -1,7 +1,7 @@ program test_conv2d_network use iso_fortran_env, only: stderr => error_unit - use nf, only: conv2d, input, network, dense, sgd, maxpool2d + use nf, only: conv, input, network, dense, sgd, maxpool2d implicit none @@ -12,8 +12,8 @@ program test_conv2d_network ! 3-layer convolutional network net = network([ & input(3, 32, 32), & - conv2d(filters=16, kernel_size=3), & - conv2d(filters=32, kernel_size=3) & + conv(filters=16, kernel_width=3, kernel_height=3), & + conv(filters=32, kernel_width=3, kernel_height=3) & ]) if (.not. size(net % layers) == 3) then @@ -49,8 +49,8 @@ program test_conv2d_network cnn = network([ & input(1, 5, 5), & - conv2d(filters=1, kernel_size=3), & - conv2d(filters=1, kernel_size=3), & + conv(filters=1, kernel_width=3, kernel_height=3), & + conv(filters=1, kernel_width=3, kernel_height=3), & dense(1) & ]) @@ -86,9 +86,9 @@ program test_conv2d_network cnn = network([ & input(1, 8, 8), & - conv2d(filters=1, kernel_size=3), & + conv(filters=1, kernel_width=3, kernel_height=3), & maxpool2d(pool_size=2), & - conv2d(filters=1, kernel_size=3), & + conv(filters=1, kernel_width=3, kernel_height=3), & dense(1) & ]) @@ -121,9 +121,9 @@ program test_conv2d_network cnn = network([ & input(1, 12, 12), & - conv2d(filters=1, kernel_size=3), & ! 1x12x12 input, 1x10x10 output + conv(filters=1, kernel_width=3, kernel_height=3), & ! 1x12x12 input, 1x10x10 output maxpool2d(pool_size=2), & ! 1x10x10 input, 1x5x5 output - conv2d(filters=1, kernel_size=3), & ! 1x5x5 input, 1x3x3 output + conv(filters=1, kernel_width=3, kernel_height=3), & ! 1x5x5 input, 1x3x3 output dense(9) & ! 9 outputs ]) diff --git a/test/test_get_set_network_params.f90 b/test/test_get_set_network_params.f90 index 71963a1c..1b8871b0 100644 --- a/test/test_get_set_network_params.f90 +++ b/test/test_get_set_network_params.f90 @@ -1,6 +1,6 @@ program test_get_set_network_params use iso_fortran_env, only: stderr => error_unit - use nf, only: conv2d, dense, flatten, input, maxpool2d, network + use nf, only: conv, dense, flatten, input, maxpool2d, network implicit none type(network) :: net logical :: ok = .true. @@ -10,7 +10,7 @@ program test_get_set_network_params ! First test get_num_params() net = network([ & input(3, 5, 5), & ! 5 x 5 image with 3 channels - conv2d(filters=2, kernel_size=3), & ! kernel shape [2, 3, 3, 3], output shape [2, 3, 3], 56 parameters total + conv(filters=2, kernel_width=3, kernel_height=3), & ! kernel shape [2, 3, 3, 3], output shape [2, 3, 3], 56 parameters total flatten(), & dense(4) & ! weights shape [72], biases shape [4], 76 parameters total ]) @@ -46,7 +46,7 @@ program test_get_set_network_params ! Finally, test set_params() and get_params() for a conv2d layer net = network([ & input(1, 3, 3), & - conv2d(filters=1, kernel_size=3) & + conv(filters=1, kernel_width=3, kernel_height=3) & ]) call net % set_params(test_params_conv2d) diff --git a/test/test_insert_flatten.f90 b/test/test_insert_flatten.f90 index 18e41b81..27b08dce 100644 --- a/test/test_insert_flatten.f90 +++ b/test/test_insert_flatten.f90 @@ -1,7 +1,7 @@ program test_insert_flatten use iso_fortran_env, only: stderr => error_unit - use nf, only: network, input, conv2d, maxpool2d, flatten, dense, reshape + use nf, only: network, input, conv, maxpool2d, flatten, dense, reshape implicit none @@ -20,7 +20,7 @@ program test_insert_flatten net = network([ & input(3, 32, 32), & - conv2d(filters=1, kernel_size=3), & + conv(filters=1, kernel_width=3, kernel_height=3), & dense(10) & ]) @@ -33,7 +33,7 @@ program test_insert_flatten net = network([ & input(3, 32, 32), & - conv2d(filters=1, kernel_size=3), & + conv(filters=1, kernel_width=3, kernel_height=3), & maxpool2d(pool_size=2, stride=2), & dense(10) & ]) From a37a0c6e250d651d8469d79346014c2eeb9ea8d9 Mon Sep 17 00:00:00 2001 From: milancurcic Date: Thu, 24 Apr 2025 11:44:35 -0400 Subject: [PATCH 2/8] Generic maxpool constructor for maxpool1d_layer and maxpool2d_layer --- README.md | 3 +- example/cnn_mnist.f90 | 6 +- example/cnn_mnist_1d.f90 | 6 +- src/nf.f90 | 3 +- src/nf/nf_layer_constructors.f90 | 100 +++++++++++---------- src/nf/nf_layer_constructors_submodule.f90 | 49 +++++----- test/test_conv1d_network.f90 | 6 +- test/test_conv2d_network.f90 | 6 +- test/test_get_set_network_params.f90 | 2 +- test/test_insert_flatten.f90 | 6 +- test/test_maxpool1d_layer.f90 | 4 +- test/test_maxpool2d_layer.f90 | 4 +- 12 files changed, 95 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index 4ca01bc3..d09367e1 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,7 @@ Read the paper [here](https://arxiv.org/abs/1902.06714). | Dropout | `dropout` | `dense`, `flatten`, `input1d` | 1 | ✅ | ✅ | | Locally connected (1-d) | `locally_connected1d` | `input2d`, `locally_connected1d`, `conv1d`, `maxpool1d`, `reshape2d` | 2 | ✅ | ✅ | | Convolutional (1-d and 2-d) | `conv` | `input`, `conv`, `maxpool`, `reshape` | 2, 3 | ✅ | ✅ | -| Max-pooling (1-d) | `maxpool1d` | `input2d`, `conv1d`, `maxpool1d`, `reshape2d` | 2 | ✅ | ✅ | -| Max-pooling (2-d) | `maxpool2d` | `input3d`, `conv2d`, `maxpool2d`, `reshape` | 3 | ✅ | ✅ | +| Max-pooling (1-d and 2-d) | `maxpool` | `input`, `conv`, `maxpool`, `reshape` | 2, 3 | ✅ | ✅ | | Linear (2-d) | `linear2d` | `input2d`, `layernorm`, `linear2d`, `self_attention` | 2 | ✅ | ✅ | | Self-attention | `self_attention` | `input2d`, `layernorm`, `linear2d`, `self_attention` | 2 | ✅ | ✅ | | Layer Normalization | `layernorm` | `linear2d`, `self_attention` | 2 | ✅ | ✅ | diff --git a/example/cnn_mnist.f90 b/example/cnn_mnist.f90 index 06f730ab..5aba11fe 100644 --- a/example/cnn_mnist.f90 +++ b/example/cnn_mnist.f90 @@ -1,7 +1,7 @@ program cnn_mnist use nf, only: network, sgd, & - input, conv, maxpool2d, flatten, dense, reshape, & + input, conv, maxpool, flatten, dense, reshape, & load_mnist, label_digits, softmax, relu implicit none @@ -22,9 +22,9 @@ program cnn_mnist input(784), & reshape(1, 28, 28), & conv(filters=8, kernel_width=3, kernel_height=3, activation=relu()), & - maxpool2d(pool_size=2), & + maxpool(pool_width=2, stride=2), & conv(filters=16, kernel_width=3, kernel_height=3, activation=relu()), & - maxpool2d(pool_size=2), & + maxpool(pool_width=2, stride=2), & dense(10, activation=softmax()) & ]) diff --git a/example/cnn_mnist_1d.f90 b/example/cnn_mnist_1d.f90 index c679eb4f..dbc622b7 100644 --- a/example/cnn_mnist_1d.f90 +++ b/example/cnn_mnist_1d.f90 @@ -1,7 +1,7 @@ program cnn_mnist_1d use nf, only: network, sgd, & - input, maxpool1d, flatten, dense, reshape, locally_connected1d, & + input, maxpool, flatten, dense, reshape, locally_connected1d, & load_mnist, label_digits, softmax, relu implicit none @@ -22,9 +22,9 @@ program cnn_mnist_1d input(784), & reshape(28, 28), & locally_connected1d(filters=8, kernel_size=3, activation=relu()), & - maxpool1d(pool_size=2), & + maxpool(pool_width=2, stride=2), & locally_connected1d(filters=16, kernel_size=3, activation=relu()), & - maxpool1d(pool_size=2), & + maxpool(pool_width=2, stride=2), & dense(10, activation=softmax()) & ]) diff --git a/src/nf.f90 b/src/nf.f90 index 407a5db3..d7c54f58 100644 --- a/src/nf.f90 +++ b/src/nf.f90 @@ -12,8 +12,7 @@ module nf layernorm, & linear2d, & locally_connected1d, & - maxpool1d, & - maxpool2d, & + maxpool, & reshape, & self_attention use nf_loss, only: mse, quadratic diff --git a/src/nf/nf_layer_constructors.f90 b/src/nf/nf_layer_constructors.f90 index 10333a32..453a8c6e 100644 --- a/src/nf/nf_layer_constructors.f90 +++ b/src/nf/nf_layer_constructors.f90 @@ -16,8 +16,7 @@ module nf_layer_constructors input, & linear2d, & locally_connected1d, & - maxpool1d, & - maxpool2d, & + maxpool, & reshape, & self_attention, & embedding, & @@ -151,10 +150,61 @@ module function conv2d(filters, kernel_width, kernel_height, activation) result( type(layer) :: res !! Resulting layer instance end function conv2d - + end interface conv + interface maxpool + + module function maxpool1d(pool_width, stride) result(res) + !! 1-d maxpooling layer constructor. + !! + !! This layer is for downscaling other layers, typically `conv1d`. + !! + !! This specific function is available under a generic name `maxpool`. + !! + !! Example: + !! + !! ``` + !! use nf, only :: maxpool1d, layer + !! type(layer) :: maxpool1d_layer + !! maxpool1d_layer = maxpool1d(pool_width=2, stride=2) + !! ``` + integer, intent(in) :: pool_width + !! Width of the pooling window, commonly 2 + integer, intent(in) :: stride + !! Stride of the pooling window, commonly equal to `pool_width`; + type(layer) :: res + !! Resulting layer instance + end function maxpool1d + + module function maxpool2d(pool_width, pool_height, stride) result(res) + !! 2-d maxpooling layer constructor. + !! + !! This layer is for downscaling other layers, typically `conv2d`. + !! + !! This specific function is available under a generic name `maxpool`. + !! + !! Example: + !! + !! ``` + !! use nf, only :: maxpool2d, layer + !! type(layer) :: maxpool2d_layer + !! maxpool2d_layer = maxpool2d(pool_width=2, pool_height=2, stride=2) + !! ``` + integer, intent(in) :: pool_width + !! Width of the pooling window, commonly 2 + integer, intent(in) :: pool_height + !! Height of the pooling window; currently must be equal to pool_width + integer, intent(in) :: stride + !! Stride of the pooling window, commonly equal to `pool_width`; + type(layer) :: res + !! Resulting layer instance + end function maxpool2d + + end interface maxpool + + interface reshape module function reshape2d(dim1, dim2) result(res) @@ -267,50 +317,6 @@ module function locally_connected1d(filters, kernel_size, activation) result(res !! Resulting layer instance end function locally_connected1d - module function maxpool1d(pool_size, stride) result(res) - !! 1-d maxpooling layer constructor. - !! - !! This layer is for downscaling other layers, typically `conv1d`. - !! - !! Example: - !! - !! ``` - !! use nf, only :: maxpool1d, layer - !! type(layer) :: maxpool1d_layer - !! maxpool1d_layer = maxpool1d(pool_size=2) - !! maxpool1d_layer = maxpool1d(pool_size=2, stride=3) - !! ``` - integer, intent(in) :: pool_size - !! Width of the pooling window, commonly 2 - integer, intent(in), optional :: stride - !! Stride of the pooling window, commonly equal to `pool_size`; - !! Defaults to `pool_size` if omitted. - type(layer) :: res - !! Resulting layer instance - end function maxpool1d - - module function maxpool2d(pool_size, stride) result(res) - !! 2-d maxpooling layer constructor. - !! - !! This layer is for downscaling other layers, typically `conv2d`. - !! - !! Example: - !! - !! ``` - !! use nf, only :: maxpool2d, layer - !! type(layer) :: maxpool2d_layer - !! maxpool2d_layer = maxpool2d(pool_size=2) - !! maxpool2d_layer = maxpool2d(pool_size=2, stride=3) - !! ``` - integer, intent(in) :: pool_size - !! Width of the pooling window, commonly 2 - integer, intent(in), optional :: stride - !! Stride of the pooling window, commonly equal to `pool_size`; - !! Defaults to `pool_size` if omitted. - type(layer) :: res - !! Resulting layer instance - end function maxpool2d - module function linear2d(out_features) result(res) !! Rank-2 (sequence_length, out_features) linear layer constructor. !! sequence_length is determined at layer initialization, based on the diff --git a/src/nf/nf_layer_constructors_submodule.f90 b/src/nf/nf_layer_constructors_submodule.f90 index 052e7201..7918ee1c 100644 --- a/src/nf/nf_layer_constructors_submodule.f90 +++ b/src/nf/nf_layer_constructors_submodule.f90 @@ -179,58 +179,49 @@ module function input3d(dim1, dim2, dim3) result(res) res % initialized = .true. end function input3d - module function maxpool1d(pool_size, stride) result(res) - integer, intent(in) :: pool_size - integer, intent(in), optional :: stride - integer :: stride_ + module function maxpool1d(pool_width, stride) result(res) + integer, intent(in) :: pool_width + integer, intent(in) :: stride type(layer) :: res - if (pool_size < 2) & - error stop 'pool_size must be >= 2 in a maxpool1d layer' + if (pool_width < 2) & + error stop 'pool_width must be >= 2 in a maxpool1d layer' - ! Stride defaults to pool_size if not provided - if (present(stride)) then - stride_ = stride - else - stride_ = pool_size - end if - - if (stride_ < 1) & + if (stride < 1) & error stop 'stride must be >= 1 in a maxpool1d layer' res % name = 'maxpool1d' allocate( & res % p, & - source=maxpool1d_layer(pool_size, stride_) & + source=maxpool1d_layer(pool_width, stride) & ) end function maxpool1d - module function maxpool2d(pool_size, stride) result(res) - integer, intent(in) :: pool_size - integer, intent(in), optional :: stride - integer :: stride_ + module function maxpool2d(pool_width, pool_height, stride) result(res) + integer, intent(in) :: pool_width + integer, intent(in) :: pool_height + integer, intent(in) :: stride type(layer) :: res - if (pool_size < 2) & - error stop 'pool_size must be >= 2 in a maxpool2d layer' + if (pool_width < 2) & + error stop 'pool_width must be >= 2 in a maxpool2d layer' - ! Stride defaults to pool_size if not provided - if (present(stride)) then - stride_ = stride - else - stride_ = pool_size - end if + ! Enforce pool_width == pool_height for now; + ! If non-square poolings show to be desired, we'll relax this constraint + ! and refactor maxpool2d_layer to work with non-square kernels. + if (pool_width /= pool_height) & + error stop 'pool_width must equal pool_height in a maxpool2d layer' - if (stride_ < 1) & + if (stride < 1) & error stop 'stride must be >= 1 in a maxpool2d layer' res % name = 'maxpool2d' allocate( & res % p, & - source=maxpool2d_layer(pool_size, stride_) & + source=maxpool2d_layer(pool_width, stride) & ) end function maxpool2d diff --git a/test/test_conv1d_network.f90 b/test/test_conv1d_network.f90 index b00cb97c..88289ab4 100644 --- a/test/test_conv1d_network.f90 +++ b/test/test_conv1d_network.f90 @@ -1,7 +1,7 @@ program test_conv1d_network use iso_fortran_env, only: stderr => error_unit - use nf, only: conv, input, network, dense, sgd, maxpool1d + use nf, only: conv, input, network, dense, sgd, maxpool implicit none @@ -87,7 +87,7 @@ program test_conv1d_network cnn = network([ & input(1, 8), & conv(filters=1, kernel_width=3), & - maxpool1d(pool_size=2), & + maxpool(pool_width=2, stride=2), & conv(filters=1, kernel_width=3), & dense(1) & ]) @@ -122,7 +122,7 @@ program test_conv1d_network cnn = network([ & input(1, 12), & conv(filters=1, kernel_width=3), & ! 1x12x12 input, 1x10x10 output - maxpool1d(pool_size=2), & ! 1x10x10 input, 1x5x5 output + maxpool(pool_width=2, stride=2), & ! 1x10x10 input, 1x5x5 output conv(filters=1, kernel_width=3), & ! 1x5x5 input, 1x3x3 output dense(9) & ! 9 outputs ]) diff --git a/test/test_conv2d_network.f90 b/test/test_conv2d_network.f90 index cbe64721..c293a1d2 100644 --- a/test/test_conv2d_network.f90 +++ b/test/test_conv2d_network.f90 @@ -1,7 +1,7 @@ program test_conv2d_network use iso_fortran_env, only: stderr => error_unit - use nf, only: conv, input, network, dense, sgd, maxpool2d + use nf, only: conv, input, network, dense, sgd, maxpool implicit none @@ -87,7 +87,7 @@ program test_conv2d_network cnn = network([ & input(1, 8, 8), & conv(filters=1, kernel_width=3, kernel_height=3), & - maxpool2d(pool_size=2), & + maxpool(pool_width=2, pool_height=2, stride=2), & conv(filters=1, kernel_width=3, kernel_height=3), & dense(1) & ]) @@ -122,7 +122,7 @@ program test_conv2d_network cnn = network([ & input(1, 12, 12), & conv(filters=1, kernel_width=3, kernel_height=3), & ! 1x12x12 input, 1x10x10 output - maxpool2d(pool_size=2), & ! 1x10x10 input, 1x5x5 output + maxpool(pool_width=2, pool_height=2, stride=2), & ! 1x10x10 input, 1x5x5 output conv(filters=1, kernel_width=3, kernel_height=3), & ! 1x5x5 input, 1x3x3 output dense(9) & ! 9 outputs ]) diff --git a/test/test_get_set_network_params.f90 b/test/test_get_set_network_params.f90 index 1b8871b0..f2a3b6a8 100644 --- a/test/test_get_set_network_params.f90 +++ b/test/test_get_set_network_params.f90 @@ -1,6 +1,6 @@ program test_get_set_network_params use iso_fortran_env, only: stderr => error_unit - use nf, only: conv, dense, flatten, input, maxpool2d, network + use nf, only: conv, dense, flatten, input, network implicit none type(network) :: net logical :: ok = .true. diff --git a/test/test_insert_flatten.f90 b/test/test_insert_flatten.f90 index 27b08dce..3437b746 100644 --- a/test/test_insert_flatten.f90 +++ b/test/test_insert_flatten.f90 @@ -1,7 +1,7 @@ program test_insert_flatten use iso_fortran_env, only: stderr => error_unit - use nf, only: network, input, conv, maxpool2d, flatten, dense, reshape + use nf, only: network, input, conv, maxpool, flatten, dense, reshape implicit none @@ -34,13 +34,13 @@ program test_insert_flatten net = network([ & input(3, 32, 32), & conv(filters=1, kernel_width=3, kernel_height=3), & - maxpool2d(pool_size=2, stride=2), & + maxpool(pool_width=2, stride=2), & dense(10) & ]) if (.not. net % layers(4) % name == 'flatten') then ok = .false. - write(stderr, '(a)') 'flatten layer inserted after maxpool2d.. failed' + write(stderr, '(a)') 'flatten layer inserted after maxpool.. failed' end if net = network([ & diff --git a/test/test_maxpool1d_layer.f90 b/test/test_maxpool1d_layer.f90 index 023a2c33..f3765686 100644 --- a/test/test_maxpool1d_layer.f90 +++ b/test/test_maxpool1d_layer.f90 @@ -1,7 +1,7 @@ program test_maxpool1d_layer use iso_fortran_env, only: stderr => error_unit - use nf, only: maxpool1d, input, layer + use nf, only: maxpool, input, layer use nf_input2d_layer, only: input2d_layer use nf_maxpool1d_layer, only: maxpool1d_layer @@ -16,7 +16,7 @@ program test_maxpool1d_layer integer :: i logical :: ok = .true., gradient_ok = .true. - maxpool_layer = maxpool1d(pool_size) + maxpool_layer = maxpool(pool_width=pool_size, stride=stride) if (.not. maxpool_layer % name == 'maxpool1d') then ok = .false. diff --git a/test/test_maxpool2d_layer.f90 b/test/test_maxpool2d_layer.f90 index 5983a217..29a56b57 100644 --- a/test/test_maxpool2d_layer.f90 +++ b/test/test_maxpool2d_layer.f90 @@ -1,7 +1,7 @@ program test_maxpool2d_layer use iso_fortran_env, only: stderr => error_unit - use nf, only: maxpool2d, input, layer + use nf, only: maxpool, input, layer use nf_input3d_layer, only: input3d_layer use nf_maxpool2d_layer, only: maxpool2d_layer @@ -16,7 +16,7 @@ program test_maxpool2d_layer integer :: i, j logical :: ok = .true., gradient_ok = .true. - maxpool_layer = maxpool2d(pool_size) + maxpool_layer = maxpool(pool_width=pool_size, pool_height=pool_size, stride=stride) if (.not. maxpool_layer % name == 'maxpool2d') then ok = .false. From e56e6a9083bd40aaa8004427a9b8e8453b1080ab Mon Sep 17 00:00:00 2001 From: milancurcic Date: Thu, 24 Apr 2025 11:54:44 -0400 Subject: [PATCH 3/8] Fix arguments in 2d CNN --- example/cnn_mnist.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/cnn_mnist.f90 b/example/cnn_mnist.f90 index 5aba11fe..1ebe081c 100644 --- a/example/cnn_mnist.f90 +++ b/example/cnn_mnist.f90 @@ -22,9 +22,9 @@ program cnn_mnist input(784), & reshape(1, 28, 28), & conv(filters=8, kernel_width=3, kernel_height=3, activation=relu()), & - maxpool(pool_width=2, stride=2), & + maxpool(pool_width=2, pool_height=2, stride=2), & conv(filters=16, kernel_width=3, kernel_height=3, activation=relu()), & - maxpool(pool_width=2, stride=2), & + maxpool(pool_width=2, pool_height=2, stride=2), & dense(10, activation=softmax()) & ]) From feffb5de5ffcbfd6c3a9949b7f6963b454999828 Mon Sep 17 00:00:00 2001 From: Milan Curcic Date: Fri, 25 Apr 2025 10:25:52 -0400 Subject: [PATCH 4/8] Update src/nf/nf_layer_constructors.f90 Co-authored-by: Jeremie Vandenplas --- src/nf/nf_layer_constructors.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nf/nf_layer_constructors.f90 b/src/nf/nf_layer_constructors.f90 index 453a8c6e..074faa71 100644 --- a/src/nf/nf_layer_constructors.f90 +++ b/src/nf/nf_layer_constructors.f90 @@ -107,7 +107,7 @@ module function conv1d(filters, kernel_width, activation) result(res) !! Example: !! !! ``` - !! use nf, only :: conv1d, layer + !! use nf, only :: conv, layer !! type(layer) :: conv1d_layer !! conv1d_layer = conv1d(filters=32, kernel_size=3) !! ``` From b657fb494fedc3286f953e3ffaba93c22a8205b0 Mon Sep 17 00:00:00 2001 From: Milan Curcic Date: Fri, 25 Apr 2025 10:26:34 -0400 Subject: [PATCH 5/8] Update src/nf/nf_layer_constructors.f90 Co-authored-by: Jeremie Vandenplas --- src/nf/nf_layer_constructors.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nf/nf_layer_constructors.f90 b/src/nf/nf_layer_constructors.f90 index 074faa71..3f035e77 100644 --- a/src/nf/nf_layer_constructors.f90 +++ b/src/nf/nf_layer_constructors.f90 @@ -109,7 +109,7 @@ module function conv1d(filters, kernel_width, activation) result(res) !! ``` !! use nf, only :: conv, layer !! type(layer) :: conv1d_layer - !! conv1d_layer = conv1d(filters=32, kernel_size=3) + !! conv1d_layer = conv(filters=32, kernel_size=3) !! ``` integer, intent(in) :: filters !! Number of filters in the output of the layer From db832caecf0e4a1e2a7aa8c933ace1dc660bc583 Mon Sep 17 00:00:00 2001 From: Milan Curcic Date: Fri, 25 Apr 2025 10:26:50 -0400 Subject: [PATCH 6/8] Update src/nf/nf_layer_constructors.f90 Co-authored-by: Jeremie Vandenplas --- src/nf/nf_layer_constructors.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nf/nf_layer_constructors.f90 b/src/nf/nf_layer_constructors.f90 index 3f035e77..893b0968 100644 --- a/src/nf/nf_layer_constructors.f90 +++ b/src/nf/nf_layer_constructors.f90 @@ -135,7 +135,7 @@ module function conv2d(filters, kernel_width, kernel_height, activation) result( !! Example: !! !! ``` - !! use nf, only :: conv2d, layer + !! use nf, only :: conv, layer !! type(layer) :: conv2d_layer !! conv2d_layer = conv2d(filters=32, kernel_width=3, kernel_height=3) !! ``` From 1d3ce3a2f23664fdb788b71b29f810b45a124886 Mon Sep 17 00:00:00 2001 From: Milan Curcic Date: Fri, 25 Apr 2025 10:27:09 -0400 Subject: [PATCH 7/8] Update src/nf/nf_layer_constructors.f90 Co-authored-by: Jeremie Vandenplas --- src/nf/nf_layer_constructors.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nf/nf_layer_constructors.f90 b/src/nf/nf_layer_constructors.f90 index 893b0968..d50e6668 100644 --- a/src/nf/nf_layer_constructors.f90 +++ b/src/nf/nf_layer_constructors.f90 @@ -137,7 +137,7 @@ module function conv2d(filters, kernel_width, kernel_height, activation) result( !! ``` !! use nf, only :: conv, layer !! type(layer) :: conv2d_layer - !! conv2d_layer = conv2d(filters=32, kernel_width=3, kernel_height=3) + !! conv2d_layer = conv(filters=32, kernel_width=3, kernel_height=3) !! ``` integer, intent(in) :: filters !! Number of filters in the output of the layer From 2fe1946768ca42eb23cf3572455f64f54524c7fc Mon Sep 17 00:00:00 2001 From: milancurcic Date: Fri, 2 May 2025 13:17:58 -0400 Subject: [PATCH 8/8] Add generic locally_connected wrapper around locally_connected1d --- README.md | 2 +- example/cnn_mnist_1d.f90 | 6 +-- src/nf.f90 | 2 +- src/nf/nf_layer_constructors.f90 | 61 +++++++++++++------------ test/test_locally_connected1d_layer.f90 | 7 ++- 5 files changed, 41 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index d09367e1..e35673e6 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Read the paper [here](https://arxiv.org/abs/1902.06714). | Embedding | `embedding` | n/a | 2 | ✅ | ✅ | | Dense (fully-connected) | `dense` | `input1d`, `dense`, `dropout`, `flatten` | 1 | ✅ | ✅ | | Dropout | `dropout` | `dense`, `flatten`, `input1d` | 1 | ✅ | ✅ | -| Locally connected (1-d) | `locally_connected1d` | `input2d`, `locally_connected1d`, `conv1d`, `maxpool1d`, `reshape2d` | 2 | ✅ | ✅ | +| Locally connected (1-d) | `locally_connected` | `input`, `locally_connected`, `conv`, `maxpool`, `reshape` | 2 | ✅ | ✅ | | Convolutional (1-d and 2-d) | `conv` | `input`, `conv`, `maxpool`, `reshape` | 2, 3 | ✅ | ✅ | | Max-pooling (1-d and 2-d) | `maxpool` | `input`, `conv`, `maxpool`, `reshape` | 2, 3 | ✅ | ✅ | | Linear (2-d) | `linear2d` | `input2d`, `layernorm`, `linear2d`, `self_attention` | 2 | ✅ | ✅ | diff --git a/example/cnn_mnist_1d.f90 b/example/cnn_mnist_1d.f90 index dbc622b7..059d09c5 100644 --- a/example/cnn_mnist_1d.f90 +++ b/example/cnn_mnist_1d.f90 @@ -1,7 +1,7 @@ program cnn_mnist_1d use nf, only: network, sgd, & - input, maxpool, flatten, dense, reshape, locally_connected1d, & + input, maxpool, flatten, dense, reshape, locally_connected, & load_mnist, label_digits, softmax, relu implicit none @@ -21,9 +21,9 @@ program cnn_mnist_1d net = network([ & input(784), & reshape(28, 28), & - locally_connected1d(filters=8, kernel_size=3, activation=relu()), & + locally_connected(filters=8, kernel_size=3, activation=relu()), & maxpool(pool_width=2, stride=2), & - locally_connected1d(filters=16, kernel_size=3, activation=relu()), & + locally_connected(filters=16, kernel_size=3, activation=relu()), & maxpool(pool_width=2, stride=2), & dense(10, activation=softmax()) & ]) diff --git a/src/nf.f90 b/src/nf.f90 index d7c54f58..c7b21656 100644 --- a/src/nf.f90 +++ b/src/nf.f90 @@ -11,7 +11,7 @@ module nf input, & layernorm, & linear2d, & - locally_connected1d, & + locally_connected, & maxpool, & reshape, & self_attention diff --git a/src/nf/nf_layer_constructors.f90 b/src/nf/nf_layer_constructors.f90 index d50e6668..80860bdf 100644 --- a/src/nf/nf_layer_constructors.f90 +++ b/src/nf/nf_layer_constructors.f90 @@ -15,7 +15,7 @@ module nf_layer_constructors flatten, & input, & linear2d, & - locally_connected1d, & + locally_connected, & maxpool, & reshape, & self_attention, & @@ -154,6 +154,38 @@ end function conv2d end interface conv + interface locally_connected + + module function locally_connected1d(filters, kernel_size, activation) result(res) + !! 1-d locally connected network constructor + !! + !! This layer is for building 1-d locally connected network. + !! Although the established convention is to call these layers 1-d, + !! the shape of the data is actually 2-d: image width, + !! and the number of channels. + !! A locally connected 1d layer must not be the first layer in the network. + !! + !! Example: + !! + !! ``` + !! use nf, only :: locally_connected1d, layer + !! type(layer) :: locally_connected1d_layer + !! locally_connected1d_layer = dense(filters=32, kernel_size=3) + !! locally_connected1d_layer = dense(filters=32, kernel_size=3, activation='relu') + !! ``` + integer, intent(in) :: filters + !! Number of filters in the output of the layer + integer, intent(in) :: kernel_size + !! Width of the convolution window, commonly 3 or 5 + class(activation_function), intent(in), optional :: activation + !! Activation function (default sigmoid) + type(layer) :: res + !! Resulting layer instance + end function locally_connected1d + + end interface locally_connected + + interface maxpool module function maxpool1d(pool_width, stride) result(res) @@ -290,33 +322,6 @@ module function flatten() result(res) !! Resulting layer instance end function flatten - module function locally_connected1d(filters, kernel_size, activation) result(res) - !! 1-d locally connected network constructor - !! - !! This layer is for building 1-d locally connected network. - !! Although the established convention is to call these layers 1-d, - !! the shape of the data is actually 2-d: image width, - !! and the number of channels. - !! A locally connected 1d layer must not be the first layer in the network. - !! - !! Example: - !! - !! ``` - !! use nf, only :: locally_connected1d, layer - !! type(layer) :: locally_connected1d_layer - !! locally_connected1d_layer = dense(filters=32, kernel_size=3) - !! locally_connected1d_layer = dense(filters=32, kernel_size=3, activation='relu') - !! ``` - integer, intent(in) :: filters - !! Number of filters in the output of the layer - integer, intent(in) :: kernel_size - !! Width of the convolution window, commonly 3 or 5 - class(activation_function), intent(in), optional :: activation - !! Activation function (default sigmoid) - type(layer) :: res - !! Resulting layer instance - end function locally_connected1d - module function linear2d(out_features) result(res) !! Rank-2 (sequence_length, out_features) linear layer constructor. !! sequence_length is determined at layer initialization, based on the diff --git a/test/test_locally_connected1d_layer.f90 b/test/test_locally_connected1d_layer.f90 index e8a30cfc..cde0a965 100644 --- a/test/test_locally_connected1d_layer.f90 +++ b/test/test_locally_connected1d_layer.f90 @@ -1,7 +1,7 @@ program test_locally_connected1d_layer use iso_fortran_env, only: stderr => error_unit - use nf, only: locally_connected1d, input, layer + use nf, only: locally_connected, input, layer use nf_input2d_layer, only: input2d_layer implicit none @@ -12,7 +12,7 @@ program test_locally_connected1d_layer real, parameter :: tolerance = 1e-7 logical :: ok = .true. - locally_connected_1d_layer = locally_connected1d(filters, kernel_size) + locally_connected_1d_layer = locally_connected(filters, kernel_size) if (.not. locally_connected_1d_layer % name == 'locally_connected1d') then ok = .false. @@ -52,7 +52,7 @@ program test_locally_connected1d_layer sample_input = 0 input_layer = input(1, 3) - locally_connected_1d_layer = locally_connected1d(filters, kernel_size) + locally_connected_1d_layer = locally_connected(filters, kernel_size) call locally_connected_1d_layer % init(input_layer) select type(this_layer => input_layer % p); type is(input2d_layer) @@ -62,7 +62,6 @@ program test_locally_connected1d_layer call locally_connected_1d_layer % forward(input_layer) call locally_connected_1d_layer % get_output(output) - if (.not. all(abs(output) < tolerance)) then ok = .false. write(stderr, '(a)') 'locally_connected1d layer with zero input and sigmoid function must forward to all 0.5.. failed'