Skip to content

Generic conv & maxpool #220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,8 @@ 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 | ✅ | ✅ |
| Max-pooling (1-d) | `maxpool1d` | `input2d`, `conv1d`, `maxpool1d`, `reshape2d` | 2 | ✅ | ✅ |
| Max-pooling (2-d) | `maxpool2d` | `input3d`, `conv2d`, `maxpool2d`, `reshape` | 3 | ✅ | ✅ |
| 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 | ✅ | ✅ |
| Self-attention | `self_attention` | `input2d`, `layernorm`, `linear2d`, `self_attention` | 2 | ✅ | ✅ |
| Layer Normalization | `layernorm` | `linear2d`, `self_attention` | 2 | ✅ | ✅ |
Expand Down
10 changes: 5 additions & 5 deletions example/cnn_mnist.f90
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
program cnn_mnist

use nf, only: network, sgd, &
input, conv2d, maxpool2d, flatten, dense, reshape, &
input, conv, maxpool, flatten, dense, reshape, &
load_mnist, label_digits, softmax, relu

implicit none
Expand All @@ -21,10 +21,10 @@ program cnn_mnist
net = network([ &
input(784), &
reshape(1, 28, 28), &
conv2d(filters=8, kernel_size=3, activation=relu()), &
maxpool2d(pool_size=2), &
conv2d(filters=16, kernel_size=3, activation=relu()), &
maxpool2d(pool_size=2), &
conv(filters=8, kernel_width=3, kernel_height=3, activation=relu()), &
maxpool(pool_width=2, pool_height=2, stride=2), &
conv(filters=16, kernel_width=3, kernel_height=3, activation=relu()), &
maxpool(pool_width=2, pool_height=2, stride=2), &
dense(10, activation=softmax()) &
])

Expand Down
6 changes: 3 additions & 3 deletions example/cnn_mnist_1d.f90
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
program cnn_mnist_1d

use nf, only: network, sgd, &
input, conv1d, maxpool1d, flatten, dense, reshape, locally_connected1d, &
input, maxpool, flatten, dense, reshape, locally_connected1d, &
load_mnist, label_digits, softmax, relu

implicit none
Expand All @@ -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()) &
])

Expand Down
2 changes: 1 addition & 1 deletion fpm.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name = "neural-fortran"
version = "0.21.0"
version = "0.22.0"
license = "MIT"
author = "Milan Curcic"
maintainer = "mcurcic@miami.edu"
Expand Down
6 changes: 2 additions & 4 deletions src/nf.f90
Original file line number Diff line number Diff line change
Expand Up @@ -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, &
Expand All @@ -13,8 +12,7 @@ module nf
layernorm, &
linear2d, &
locally_connected1d, &
maxpool1d, &
maxpool2d, &
maxpool, &
reshape, &
self_attention
use nf_loss, only: mse, quadratic
Expand Down
219 changes: 116 additions & 103 deletions src/nf/nf_layer_constructors.f90
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@ module nf_layer_constructors

private
public :: &
conv1d, &
conv2d, &
conv, &
dense, &
dropout, &
flatten, &
input, &
linear2d, &
locally_connected1d, &
maxpool1d, &
maxpool2d, &
maxpool, &
reshape, &
self_attention, &
embedding, &
Expand Down Expand Up @@ -94,6 +92,119 @@ 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 :: conv, layer
!! type(layer) :: conv1d_layer
!! conv1d_layer = conv(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 :: conv, layer
!! type(layer) :: conv2d_layer
!! conv2d_layer = conv(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 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)
Expand Down Expand Up @@ -179,66 +290,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.
!!
Expand All @@ -260,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
Expand Down
Loading