Skip to content

Commit f4c4412

Browse files
committed
Creating maxpool1d
1 parent e416673 commit f4c4412

9 files changed

+244
-27
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ add_library(neural-fortran
4040
src/nf/nf_locally_connected_1d_submodule.f90
4141
src/nf/nf_loss.f90
4242
src/nf/nf_loss_submodule.f90
43+
src/nf/nf_maxpool1d_layer.f90
44+
src/nf/nf_maxpool1d_layer_submodule.f90
4345
src/nf/nf_maxpool2d_layer.f90
4446
src/nf/nf_maxpool2d_layer_submodule.f90
4547
src/nf/nf_metrics.f90

example/cnn_mnist_1d.f90

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
program cnn_mnist
22

33
use nf, only: network, sgd, &
4-
input, conv2d, maxpool2d, flatten, dense, reshape, reshape_generalized, locally_connected_1d, &
4+
input, conv2d, maxpool1d, maxpool2d, flatten, dense, reshape, reshape_generalized, locally_connected_1d, &
55
load_mnist, label_digits, softmax, relu
66

77
implicit none
@@ -20,11 +20,11 @@ program cnn_mnist
2020

2121
net = network([ &
2222
input(784), &
23-
reshape([1,28,28]), &
23+
reshape_generalized([1,784]), &
2424
locally_connected_1d(filters=8, kernel_size=3, activation=relu()), &
25-
maxpool2d(pool_size=2), &
25+
maxpool1d(pool_size=2), &
2626
locally_connected_1d(filters=16, kernel_size=3, activation=relu()), &
27-
maxpool2d(pool_size=2), &
27+
maxpool1d(pool_size=2), &
2828
dense(10, activation=softmax()) &
2929
])
3030

src/nf.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module nf
33
use nf_datasets_mnist, only: label_digits, load_mnist
44
use nf_layer, only: layer
55
use nf_layer_constructors, only: &
6-
conv2d, dense, flatten, input, maxpool2d, reshape, reshape_generalized, locally_connected_1d
6+
conv2d, dense, flatten, input, maxpool1d, maxpool2d, reshape, reshape_generalized, locally_connected_1d
77
use nf_loss, only: mse, quadratic
88
use nf_metrics, only: corr, maxabs
99
use nf_network, only: network

src/nf/nf_layer_constructors.f90

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module nf_layer_constructors
88
implicit none
99

1010
private
11-
public :: conv2d, dense, flatten, input, locally_connected_1d, maxpool2d, reshape, reshape_generalized
11+
public :: conv2d, dense, flatten, input, locally_connected_1d, maxpool1d, maxpool2d, reshape, reshape_generalized
1212

1313
interface input
1414

@@ -133,6 +133,29 @@ module function conv2d(filters, kernel_size, activation) result(res)
133133
!! Resulting layer instance
134134
end function conv2d
135135

136+
module function maxpool1d(pool_size, stride) result(res)
137+
!! CHANGE THE COMMENTS !!!
138+
!! 1-d maxpooling layer constructor.
139+
!!
140+
!! This layer is for downscaling other layers, typically `conv2d`.
141+
!!
142+
!! Example:
143+
!!
144+
!! ```
145+
!! use nf, only :: maxpool2d, layer
146+
!! type(layer) :: maxpool2d_layer
147+
!! maxpool2d_layer = maxpool2d(pool_size=2)
148+
!! maxpool2d_layer = maxpool2d(pool_size=2, stride=3)
149+
!! ```
150+
integer, intent(in) :: pool_size
151+
!! Width of the pooling window, commonly 2
152+
integer, intent(in), optional :: stride
153+
!! Stride of the pooling window, commonly equal to `pool_size`;
154+
!! Defaults to `pool_size` if omitted.
155+
type(layer) :: res
156+
!! Resulting layer instance
157+
end function maxpool1d
158+
136159
module function maxpool2d(pool_size, stride) result(res)
137160
!! 2-d maxpooling layer constructor.
138161
!!
@@ -156,6 +179,7 @@ module function maxpool2d(pool_size, stride) result(res)
156179
end function maxpool2d
157180

158181
module function locally_connected_1d(filters, kernel_size, activation) result(res)
182+
!! CHANGE THE COMMENTS !!!
159183
!! 2-d convolutional layer constructor.
160184
!!
161185
!! This layer is for building 2-d convolutional network.

src/nf/nf_layer_constructors_submodule.f90

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use nf_input1d_layer, only: input1d_layer
88
use nf_input3d_layer, only: input3d_layer
99
use nf_locally_connected_1d_layer, only: locally_connected_1d_layer
10+
use nf_maxpool1d_layer, only: maxpool1d_layer
1011
use nf_maxpool2d_layer, only: maxpool2d_layer
1112
use nf_reshape_layer, only: reshape3d_layer
1213
use nf_reshape_layer_generalized, only: reshape_generalized_layer
@@ -116,7 +117,34 @@ module function locally_connected_1d(filters, kernel_size, activation) result(re
116117
)
117118

118119
end function locally_connected_1d
119-
120+
121+
module function maxpool1d(pool_size, stride) result(res)
122+
integer, intent(in) :: pool_size
123+
integer, intent(in), optional :: stride
124+
integer :: stride_
125+
type(layer) :: res
126+
127+
if (pool_size < 2) &
128+
error stop 'pool_size must be >= 2 in a maxpool1d layer'
129+
130+
! Stride defaults to pool_size if not provided
131+
if (present(stride)) then
132+
stride_ = stride
133+
else
134+
stride_ = pool_size
135+
end if
136+
137+
if (stride_ < 1) &
138+
error stop 'stride must be >= 1 in a maxpool1d layer'
139+
140+
res % name = 'maxpool1d'
141+
142+
allocate( &
143+
res % p, &
144+
source=maxpool1d_layer(pool_size, stride_) &
145+
)
146+
147+
end function maxpool1d
120148

121149
module function maxpool2d(pool_size, stride) result(res)
122150
integer, intent(in) :: pool_size

src/nf/nf_layer_submodule.f90

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ pure module subroutine backward_1d(self, previous, gradient)
4444
call this_layer % backward(prev_layer % output, gradient)
4545
type is(conv2d_layer)
4646
call this_layer % backward(prev_layer % output, gradient)
47-
type is(locally_connected_1d_layer)
48-
call this_layer % backward(prev_layer % output, gradient)
4947
type is(maxpool2d_layer)
5048
call this_layer % backward(prev_layer % output, gradient)
5149
end select
@@ -54,7 +52,6 @@ pure module subroutine backward_1d(self, previous, gradient)
5452

5553
end subroutine backward_1d
5654

57-
5855
pure module subroutine backward_3d(self, previous, gradient)
5956
implicit none
6057
class(layer), intent(in out) :: self
@@ -75,8 +72,6 @@ pure module subroutine backward_3d(self, previous, gradient)
7572
call this_layer % backward(prev_layer % output, gradient)
7673
type is(conv2d_layer)
7774
call this_layer % backward(prev_layer % output, gradient)
78-
type is(locally_connected_1d_layer)
79-
call this_layer % backward(prev_layer % output, gradient)
8075
type is(reshape3d_layer)
8176
call this_layer % backward(prev_layer % output, gradient)
8277
end select
@@ -91,8 +86,6 @@ pure module subroutine backward_3d(self, previous, gradient)
9186
call this_layer % backward(prev_layer % output, gradient)
9287
type is(input3d_layer)
9388
call this_layer % backward(prev_layer % output, gradient)
94-
type is(locally_connected_1d_layer)
95-
call this_layer % backward(prev_layer % output, gradient)
9689
type is(reshape3d_layer)
9790
call this_layer % backward(prev_layer % output, gradient)
9891
end select
@@ -141,8 +134,6 @@ pure module subroutine forward(self, input)
141134
call this_layer % forward(prev_layer % output)
142135
type is(conv2d_layer)
143136
call this_layer % forward(prev_layer % output)
144-
type is(locally_connected_1d_layer)
145-
call this_layer % forward(prev_layer % output)
146137
type is(maxpool2d_layer)
147138
call this_layer % forward(prev_layer % output)
148139
type is(reshape3d_layer)
@@ -157,8 +148,6 @@ pure module subroutine forward(self, input)
157148
call this_layer % forward(prev_layer % output)
158149
type is(conv2d_layer)
159150
call this_layer % forward(prev_layer % output)
160-
type is(locally_connected_1d_layer)
161-
call this_layer % forward(prev_layer % output)
162151
type is(maxpool2d_layer)
163152
call this_layer % forward(prev_layer % output)
164153
type is(reshape3d_layer)
@@ -173,8 +162,6 @@ pure module subroutine forward(self, input)
173162
call this_layer % forward(prev_layer % output)
174163
type is(conv2d_layer)
175164
call this_layer % forward(prev_layer % output)
176-
type is(locally_connected_1d_layer)
177-
call this_layer % forward(prev_layer % output)
178165
type is(maxpool2d_layer)
179166
call this_layer % forward(prev_layer % output)
180167
type is(reshape3d_layer)
@@ -218,6 +205,21 @@ pure module subroutine get_output_1d(self, output)
218205

219206
end subroutine get_output_1d
220207

208+
pure module subroutine get_output_2d(self, output)
209+
implicit none
210+
class(layer), intent(in) :: self
211+
real, allocatable, intent(out) :: output(:,:)
212+
213+
select type(this_layer => self % p)
214+
215+
type is(locally_connected_1d_layer)
216+
allocate(output, source=this_layer % output)
217+
class default
218+
error stop '2d output can only be read from a locally_connected_1d layer'
219+
220+
end select
221+
222+
end subroutine get_output_2d
221223

222224
pure module subroutine get_output_3d(self, output)
223225
implicit none
@@ -230,8 +232,6 @@ pure module subroutine get_output_3d(self, output)
230232
allocate(output, source=this_layer % output)
231233
type is(conv2d_layer)
232234
allocate(output, source=this_layer % output)
233-
type is(locally_connected_1d_layer)
234-
allocate(output, source=this_layer % output)
235235
type is(maxpool2d_layer)
236236
allocate(output, source=this_layer % output)
237237
type is(reshape3d_layer)

src/nf/nf_maxpool1d_layer.f90

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
module nf_maxpool1d_layer
2+
!! This module provides the 1-d maxpooling layer.
3+
4+
use nf_base_layer, only: base_layer
5+
implicit none
6+
7+
private
8+
public :: maxpool1d_layer
9+
10+
type, extends(base_layer) :: maxpool1d_layer
11+
integer :: channels
12+
integer :: width ! Length of the input along the pooling dimension
13+
integer :: pool_size
14+
integer :: stride
15+
16+
! Location (as input matrix indices) of the maximum value within each pooling region.
17+
! Dimensions: (channels, new_width)
18+
integer, allocatable :: maxloc(:,:)
19+
20+
! Gradient for the input (same shape as the input).
21+
real, allocatable :: gradient(:,:)
22+
! Output after pooling (dimensions: (channels, new_width)).
23+
real, allocatable :: output(:,:)
24+
contains
25+
procedure :: init
26+
procedure :: forward
27+
procedure :: backward
28+
end type maxpool1d_layer
29+
30+
interface maxpool1d_layer
31+
pure module function maxpool1d_layer_cons(pool_size, stride) result(res)
32+
!! `maxpool1d` constructor function.
33+
integer, intent(in) :: pool_size
34+
!! Width of the pooling window.
35+
integer, intent(in) :: stride
36+
!! Stride of the pooling window.
37+
type(maxpool1d_layer) :: res
38+
end function maxpool1d_layer_cons
39+
end interface maxpool1d_layer
40+
41+
interface
42+
module subroutine init(self, input_shape)
43+
!! Initialize the `maxpool1d` layer instance with an input shape.
44+
class(maxpool1d_layer), intent(in out) :: self
45+
!! `maxpool1d_layer` instance.
46+
integer, intent(in) :: input_shape(:)
47+
!! Array shape of the input layer, expected as (channels, width).
48+
end subroutine init
49+
50+
pure module subroutine forward(self, input)
51+
!! Run a forward pass of the `maxpool1d` layer.
52+
class(maxpool1d_layer), intent(in out) :: self
53+
!! `maxpool1d_layer` instance.
54+
real, intent(in) :: input(:,:)
55+
!! Input data (output of the previous layer), with shape (channels, width).
56+
end subroutine forward
57+
58+
pure module subroutine backward(self, input, gradient)
59+
!! Run a backward pass of the `maxpool1d` layer.
60+
class(maxpool1d_layer), intent(in out) :: self
61+
!! `maxpool1d_layer` instance.
62+
real, intent(in) :: input(:,:)
63+
!! Input data (output of the previous layer).
64+
real, intent(in) :: gradient(:,:)
65+
!! Gradient from the downstream layer, with shape (channels, pooled width).
66+
end subroutine backward
67+
end interface
68+
69+
end module nf_maxpool1d_layer
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
submodule(nf_maxpool1d_layer) nf_maxpool1d_layer_submodule
2+
implicit none
3+
4+
contains
5+
6+
pure module function maxpool1d_layer_cons(pool_size, stride) result(res)
7+
implicit none
8+
integer, intent(in) :: pool_size
9+
integer, intent(in) :: stride
10+
type(maxpool1d_layer) :: res
11+
12+
res % pool_size = pool_size
13+
res % stride = stride
14+
end function maxpool1d_layer_cons
15+
16+
17+
module subroutine init(self, input_shape)
18+
implicit none
19+
class(maxpool1d_layer), intent(in out) :: self
20+
integer, intent(in) :: input_shape(:)
21+
! input_shape is expected to be (channels, width)
22+
23+
self % channels = input_shape(1)
24+
! The new width is the integer division of the input width by the stride.
25+
self % width = input_shape(2) / self % stride
26+
27+
! Allocate storage for the index of the maximum element within each pooling region.
28+
allocate(self % maxloc(self % channels, self % width))
29+
self % maxloc = 0
30+
31+
! Allocate the gradient array corresponding to the input dimensions.
32+
allocate(self % gradient(input_shape(1), input_shape(2)))
33+
self % gradient = 0
34+
35+
! Allocate the output array (after pooling).
36+
allocate(self % output(self % channels, self % width))
37+
self % output = 0
38+
end subroutine init
39+
40+
41+
pure module subroutine forward(self, input)
42+
implicit none
43+
class(maxpool1d_layer), intent(in out) :: self
44+
real, intent(in) :: input(:,:)
45+
integer :: input_width
46+
integer :: i, n
47+
integer :: ii, iend
48+
integer :: iextent
49+
integer :: max_index ! Temporary variable to hold the local index of the max
50+
integer :: maxloc_temp(1) ! Temporary array to hold the result of maxloc
51+
52+
input_width = size(input, dim=2)
53+
! Ensure we only process complete pooling regions.
54+
iextent = input_width - mod(input_width, self % stride)
55+
56+
! Loop over the input with a step size equal to the stride and over all channels.
57+
do concurrent (i = 1:iextent: self % stride, n = 1:self % channels)
58+
! Compute the index in the pooled (output) array.
59+
ii = (i - 1) / self % stride + 1
60+
! Determine the ending index of the current pooling region.
61+
iend = min(i + self % pool_size - 1, input_width)
62+
63+
! Find the index (within the pooling window) of the maximum value.
64+
maxloc_temp = maxloc(input(n, i:iend))
65+
max_index = maxloc_temp(1) + i - 1 ! Adjust to the index in the original input
66+
67+
! Store the location of the maximum value.
68+
self % maxloc(n, ii) = max_index
69+
! Set the output as the maximum value from this pooling region.
70+
self % output(n, ii) = input(n, max_index)
71+
end do
72+
end subroutine forward
73+
74+
75+
pure module subroutine backward(self, input, gradient)
76+
implicit none
77+
class(maxpool1d_layer), intent(in out) :: self
78+
real, intent(in) :: input(:,:)
79+
real, intent(in) :: gradient(:,:)
80+
integer :: channels, pooled_width
81+
integer :: i, n
82+
83+
channels = size(gradient, dim=1)
84+
pooled_width = size(gradient, dim=2)
85+
86+
! The gradient for max-pooling is nonzero only at the input locations
87+
! that were the maxima during the forward pass.
88+
do concurrent (n = 1:channels, i = 1:pooled_width)
89+
self % gradient(n, self % maxloc(n, i)) = gradient(n, i)
90+
end do
91+
end subroutine backward
92+
93+
end submodule nf_maxpool1d_layer_submodule

0 commit comments

Comments
 (0)