Skip to content

Commit c316ee1

Browse files
Linear2d layer (#197)
* linear2d_layer forward implementation * implement backward * introduce concurrency, outtroduce stupidity * fix style * add parameters api to linear2d_layer * add constructor for linear2d_layer * add integration for linear2d layer * set usage rules for linear2d_layer * add linear2d_layer to public api * update tests for linear2d layer * remove extra comment * remove rubbish * move linear2d layer logic into submodule * update cmake for linear2d_layer * update tests for linear2d_layer * update linear2d_layer tests * update linear2d_layer tests for batch last * make linear2d_layer with batch as last dimension (performance) * linear2d_layer: fix gradient updates * linear2d_layer: make it 2d * linear2d_layer: forgot a file * linear2d_layer: temporarily remove api * Don't expose the concrete layer type via nf * Report success to stdout * Include linear2d test in cmake * Add Linear2d to README * Plumbing of linear2d with input2d and linear2d * linear2d_layer: add flatten2d layer * linear2d_layer: make linear2d layer work with input2d and flatten2d * update cmake * linear2d_layer: use flatten layer instead of flatten2d * linear2d_layer: remove flatten2d layer * linear2d_layer: remove public api * linear2d_layer: update cmakelists * linear2d_layer: workaround cpu imprecision to make ci happy * Add linear2d example * linear2d_layer: remove redundant constructor args * linear2d_layer: make example converge * linear2d_layer: make weighs init with normal distribution * linear2d_layer: add loss stopping and more iterations * linear2d_layer: update tests * Tidy up * Require passing only out_features to linear2d(); tidy up * Remove linear2d example --------- Co-authored-by: milancurcic <[email protected]>
1 parent 4ad75bc commit c316ee1

11 files changed

+476
-10
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ add_library(neural-fortran
3838
src/nf/nf_layer_constructors_submodule.f90
3939
src/nf/nf_layer.f90
4040
src/nf/nf_layer_submodule.f90
41+
src/nf/nf_linear2d_layer.f90
42+
src/nf/nf_linear2d_layer_submodule.f90
4143
src/nf/nf_loss.f90
4244
src/nf/nf_loss_submodule.f90
4345
src/nf/nf_maxpool2d_layer.f90

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Read the paper [here](https://arxiv.org/abs/1902.06714).
3434
| Convolutional (2-d) | `conv2d` | `input3d`, `conv2d`, `maxpool2d`, `reshape` | 3 | ✅ | ✅(*) |
3535
| Max-pooling (2-d) | `maxpool2d` | `input3d`, `conv2d`, `maxpool2d`, `reshape` | 3 | ✅ | ✅ |
3636
| Flatten | `flatten` | `input2d`, `input3d`, `conv2d`, `maxpool2d`, `reshape` | 1 | ✅ | ✅ |
37+
| Linear (2-d) | `linear2d` | `input2d` | 2 | ✅ | ✅ |
3738
| Reshape (1-d to 3-d) | `reshape` | `input1d`, `dense`, `flatten` | 3 | ✅ | ✅ |
3839

3940
(*) See Issue [#145](https://github.com/modern-fortran/neural-fortran/issues/145) regarding non-converging CNN training on the MNIST dataset.

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
6+
conv2d, dense, flatten, input, maxpool2d, reshape, linear2d
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: 11 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, maxpool2d, reshape
11+
public :: conv2d, dense, flatten, input, maxpool2d, reshape, linear2d
1212

1313
interface input
1414

@@ -185,6 +185,16 @@ module function reshape(output_shape) result(res)
185185
!! Resulting layer instance
186186
end function reshape
187187

188+
module function linear2d(out_features) result(res)
189+
!! Rank-2 (sequence_length, out_features) linear layer constructor.
190+
!! sequence_length is determined at layer initialization, based on the
191+
!! output shape of the previous layer.
192+
integer, intent(in) :: out_features
193+
!! Number of output features
194+
type(layer) :: res
195+
!! Resulting layer instance
196+
end function linear2d
197+
188198
end interface
189199

190200
end module nf_layer_constructors

src/nf/nf_layer_constructors_submodule.f90

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use nf_input3d_layer, only: input3d_layer
1010
use nf_maxpool2d_layer, only: maxpool2d_layer
1111
use nf_reshape_layer, only: reshape3d_layer
12+
use nf_linear2d_layer, only: linear2d_layer
1213
use nf_activation, only: activation_function, relu, sigmoid
1314

1415
implicit none
@@ -71,6 +72,7 @@ module function flatten() result(res)
7172
end function flatten
7273

7374

75+
7476
module function input1d(layer_size) result(res)
7577
integer, intent(in) :: layer_size
7678
type(layer) :: res
@@ -148,4 +150,14 @@ module function reshape(output_shape) result(res)
148150

149151
end function reshape
150152

153+
154+
module function linear2d(out_features) result(res)
155+
integer, intent(in) :: out_features
156+
type(layer) :: res
157+
158+
res % name = 'linear2d'
159+
allocate(res % p, source=linear2d_layer(out_features))
160+
161+
end function linear2d
162+
151163
end submodule nf_layer_constructors_submodule

src/nf/nf_layer_submodule.f90

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use nf_input3d_layer, only: input3d_layer
1010
use nf_maxpool2d_layer, only: maxpool2d_layer
1111
use nf_reshape_layer, only: reshape3d_layer
12+
use nf_linear2d_layer, only: linear2d_layer
1213
use nf_optimizers, only: optimizer_base_type
1314

1415
contains
@@ -47,6 +48,8 @@ pure module subroutine backward_1d(self, previous, gradient)
4748
call this_layer % backward(prev_layer % output, gradient)
4849
type is(maxpool2d_layer)
4950
call this_layer % backward(prev_layer % output, gradient)
51+
type is(linear2d_layer)
52+
call this_layer % backward(prev_layer % output, gradient)
5053
end select
5154

5255
end select
@@ -60,9 +63,19 @@ pure module subroutine backward_2d(self, previous, gradient)
6063
class(layer), intent(in) :: previous
6164
real, intent(in) :: gradient(:,:)
6265

63-
! Backward pass from a 2-d layer downstream currently implemented
64-
! only for dense and flatten layers
65-
! CURRENTLY NO LAYERS, tbd: pull/197 and pull/199
66+
select type(this_layer => self % p)
67+
68+
type is(linear2d_layer)
69+
70+
select type(prev_layer => previous % p)
71+
type is(input2d_layer)
72+
call this_layer % backward(prev_layer % output, gradient)
73+
type is(linear2d_layer)
74+
call this_layer % backward(prev_layer % output, gradient)
75+
end select
76+
77+
end select
78+
6679
end subroutine backward_2d
6780

6881

@@ -182,6 +195,8 @@ pure module subroutine forward(self, input)
182195
call this_layer % forward(prev_layer % output)
183196
type is(reshape3d_layer)
184197
call this_layer % forward(prev_layer % output)
198+
type is(linear2d_layer)
199+
call this_layer % forward(prev_layer % output)
185200
end select
186201

187202
type is(reshape3d_layer)
@@ -196,6 +211,16 @@ pure module subroutine forward(self, input)
196211
call this_layer % forward(prev_layer % output)
197212
end select
198213

214+
type is(linear2d_layer)
215+
216+
! Upstream layers permitted: input2d, linear2d
217+
select type(prev_layer => input % p)
218+
type is(input2d_layer)
219+
call this_layer % forward(prev_layer % output)
220+
type is(linear2d_layer)
221+
call this_layer % forward(prev_layer % output)
222+
end select
223+
199224
end select
200225

201226
end subroutine forward
@@ -231,8 +256,10 @@ pure module subroutine get_output_2d(self, output)
231256

232257
type is(input2d_layer)
233258
allocate(output, source=this_layer % output)
259+
type is(linear2d_layer)
260+
allocate(output, source=this_layer % output)
234261
class default
235-
error stop '1-d output can only be read from an input1d, dense, or flatten layer.'
262+
error stop '2-d output can only be read from an input2d or linear2d layer.'
236263

237264
end select
238265

@@ -274,7 +301,7 @@ impure elemental module subroutine init(self, input)
274301
call this_layer % init(input % layer_shape)
275302
end select
276303

277-
! The shape of conv2d, maxpool2d, or flatten layers is not known
304+
! The shape of linear2d, conv2d, maxpool2d, or flatten layers is not known
278305
! until we receive an input layer.
279306
select type(this_layer => self % p)
280307
type is(conv2d_layer)
@@ -283,9 +310,11 @@ impure elemental module subroutine init(self, input)
283310
self % layer_shape = shape(this_layer % output)
284311
type is(flatten_layer)
285312
self % layer_shape = shape(this_layer % output)
313+
type is(linear2d_layer)
314+
self % layer_shape = shape(this_layer % output)
286315
end select
287316

288-
self % input_layer_shape = input % layer_shape
317+
self % input_layer_shape = input % layer_shape
289318
self % initialized = .true.
290319

291320
end subroutine init
@@ -328,6 +357,8 @@ elemental module function get_num_params(self) result(num_params)
328357
num_params = 0
329358
type is (reshape3d_layer)
330359
num_params = 0
360+
type is (linear2d_layer)
361+
num_params = this_layer % get_num_params()
331362
class default
332363
error stop 'Unknown layer type.'
333364
end select
@@ -355,6 +386,8 @@ module function get_params(self) result(params)
355386
! No parameters to get.
356387
type is (reshape3d_layer)
357388
! No parameters to get.
389+
type is (linear2d_layer)
390+
params = this_layer % get_params()
358391
class default
359392
error stop 'Unknown layer type.'
360393
end select
@@ -379,9 +412,11 @@ module function get_gradients(self) result(gradients)
379412
type is (maxpool2d_layer)
380413
! No gradients to get.
381414
type is (flatten_layer)
382-
! No gradients to get.
415+
! No parameters to get.
383416
type is (reshape3d_layer)
384417
! No gradients to get.
418+
type is (linear2d_layer)
419+
gradients = this_layer % get_gradients()
385420
class default
386421
error stop 'Unknown layer type.'
387422
end select
@@ -429,6 +464,9 @@ module subroutine set_params(self, params)
429464
type is (conv2d_layer)
430465
call this_layer % set_params(params)
431466

467+
type is (linear2d_layer)
468+
call this_layer % set_params(params)
469+
432470
type is (maxpool2d_layer)
433471
! No parameters to set.
434472
write(stderr, '(a)') 'Warning: calling set_params() ' &
@@ -446,6 +484,7 @@ module subroutine set_params(self, params)
446484

447485
class default
448486
error stop 'Unknown layer type.'
487+
449488
end select
450489

451490
end subroutine set_params

src/nf/nf_linear2d_layer.f90

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
module nf_linear2d_layer
2+
3+
use nf_activation, only: activation_function
4+
use nf_base_layer, only: base_layer
5+
6+
implicit none
7+
8+
private
9+
public :: linear2d_layer
10+
11+
type, extends(base_layer) :: linear2d_layer
12+
integer :: sequence_length, in_features, out_features, batch_size
13+
14+
real, allocatable :: weights(:,:)
15+
real, allocatable :: biases(:)
16+
real, allocatable :: output(:,:)
17+
real, allocatable :: gradient(:,:) ! input gradient
18+
real, allocatable :: dw(:,:) ! weight gradients
19+
real, allocatable :: db(:) ! bias gradients
20+
21+
contains
22+
23+
procedure :: backward
24+
procedure :: forward
25+
procedure :: init
26+
procedure :: get_num_params
27+
procedure :: get_params
28+
procedure :: get_gradients
29+
procedure :: set_params
30+
31+
end type linear2d_layer
32+
33+
interface linear2d_layer
34+
module function linear2d_layer_cons(out_features) result(res)
35+
integer, intent(in) :: out_features
36+
type(linear2d_layer) :: res
37+
end function linear2d_layer_cons
38+
end interface linear2d_layer
39+
40+
interface
41+
pure module subroutine forward(self, input)
42+
class(linear2d_layer), intent(in out) :: self
43+
real, intent(in) :: input(:,:)
44+
end subroutine forward
45+
46+
pure module subroutine backward(self, input, gradient)
47+
class(linear2d_layer), intent(in out) :: self
48+
real, intent(in) :: input(:,:)
49+
real, intent(in) :: gradient(:,:)
50+
end subroutine backward
51+
52+
module subroutine init(self, input_shape)
53+
class(linear2d_layer), intent(in out) :: self
54+
integer, intent(in) :: input_shape(:)
55+
end subroutine init
56+
57+
pure module function get_num_params(self) result(num_params)
58+
class(linear2d_layer), intent(in) :: self
59+
integer :: num_params
60+
end function get_num_params
61+
62+
module function get_params(self) result(params)
63+
class(linear2d_layer), intent(in), target :: self
64+
real, allocatable :: params(:)
65+
end function get_params
66+
67+
module function get_gradients(self) result(gradients)
68+
class(linear2d_layer), intent(in), target :: self
69+
real, allocatable :: gradients(:)
70+
end function get_gradients
71+
72+
module subroutine set_params(self, params)
73+
class(linear2d_layer), intent(in out) :: self
74+
real, intent(in), target :: params(:)
75+
end subroutine set_params
76+
end interface
77+
end module nf_linear2d_layer

0 commit comments

Comments
 (0)