Skip to content
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

JSON-formatted input for training configuration #94

Merged
merged 11 commits into from
Nov 1, 2023
15 changes: 15 additions & 0 deletions example/print-training-configuration.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
program print_training_configuration
!! Demonstrate how to construct and print a training_configuration_t object
use inference_engine_m, only : training_configuration_t, hyperparameters_t, network_configuration_t
use sourcery_m, only : file_t
implicit none

associate(training_configuration => training_configuration_t( &
hyperparameters_t(mini_batches=10, learning_rate=1.5, optimizer = "adam"), &
network_configuration_t(skip_connections=.false., nodes_per_layer=[2,72,2], activation_function="sigmoid") &
))
associate(json_file => file_t(training_configuration%to_json()))
call json_file%write_lines()
end associate
end associate
end program
2 changes: 1 addition & 1 deletion fpm.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ maintainer = "[email protected]"

[dependencies]
assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.5.0"}
sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "3.9.1"}
sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "4.4.3"}
netcdf-interfaces = {git = "https://github.com/rouson/netcdf-interfaces.git", branch = "implicit-interfaces"}
2 changes: 1 addition & 1 deletion src/inference_engine/activation_strategy_m.f90
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module activation_strategy_m

! External dependencies
use kind_parameters_m, only : rkind
use string_m, only : string_t
use sourcery_m, only : string_t
implicit none

private
Expand Down
53 changes: 53 additions & 0 deletions src/inference_engine/hyperparameters_m.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module hyperparameters_m
use sourcery_m, only : string_t
implicit none

private
public :: hyperparameters_t

type hyperparameters_t
private
integer :: mini_batches_ = 10
real :: learning_rate_ = 1.5
character(len=:), allocatable :: optimizer_
contains
procedure :: to_json
procedure :: equals
generic :: operator(==) => equals
end type

interface hyperparameters_t

pure module function from_json(lines) result(hyperparameters)
implicit none
type(string_t), intent(in) :: lines(:)
type(hyperparameters_t) hyperparameters
end function

pure module function from_components(mini_batches, learning_rate, optimizer) result(hyperparameters)
implicit none
integer, intent(in) :: mini_batches
real, intent(in) :: learning_rate
character(len=*), intent(in) :: optimizer
type(hyperparameters_t) hyperparameters
end function

end interface

interface

pure module function to_json(self) result(lines)
implicit none
class(hyperparameters_t), intent(in) :: self
type(string_t), allocatable :: lines(:)
end function

elemental module function equals(lhs, rhs) result(lhs_equals_rhs)
implicit none
class(hyperparameters_t), intent(in) :: lhs, rhs
logical lhs_equals_rhs
end function

end interface

end module
66 changes: 66 additions & 0 deletions src/inference_engine/hyperparameters_s.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
submodule(hyperparameters_m) hyperparameters_s
use assert_m, only : assert
implicit none

character(len=*), parameter :: mini_batches_key = "mini-batches"
character(len=*), parameter :: learning_rate_key = "learning rate"
character(len=*), parameter :: optimizer_key = "optimizer"

contains

module procedure from_components
hyperparameters%mini_batches_ = mini_batches
hyperparameters%learning_rate_ = learning_rate
hyperparameters%optimizer_ = optimizer
end procedure

module procedure equals

real, parameter :: tolerance = 1.E-08

call assert(allocated(lhs%optimizer_) .and. allocated(rhs%optimizer_), "hyperparameters_s(equals): allocated optimizers")

lhs_equals_rhs = &
lhs%mini_batches_ == rhs%mini_batches_ .and. &
lhs%optimizer_ == rhs%optimizer_ .and. &
abs(lhs%learning_rate_ - rhs%learning_rate_) <= tolerance

end procedure

module procedure from_json
integer l
logical hyperparameters_key_found

hyperparameters_key_found = .false.

do l=1,size(lines)
if (lines(l)%get_json_key() == "hyperparameters") then
hyperparameters_key_found = .true.
hyperparameters%mini_batches_ = lines(l+1)%get_json_value(string_t(mini_batches_key), mold=0)
hyperparameters%learning_rate_ = lines(l+2)%get_json_value(string_t(learning_rate_key), mold=0.)
hyperparameters%optimizer_ = lines(l+3)%get_json_value(string_t(optimizer_key), mold=string_t(""))
return
end if
end do

call assert(hyperparameters_key_found, "hyperparameters_s(from_json): hyperparameters_found")
end procedure

module procedure to_json
character(len=*), parameter :: indent = repeat(" ",ncopies=4)
integer, parameter :: max_width= 18
character(len=max_width) mini_batches_string, learning_rate_string

write(mini_batches_string,*) self%mini_batches_
write(learning_rate_string,*) self%learning_rate_

lines = [ &
string_t(indent // '"hyperparameters": {'), &
string_t(indent // indent // '"' // mini_batches_key // '" : ' // trim(adjustl(mini_batches_string)) // "," ), &
string_t(indent // indent // '"' // learning_rate_key // '" : ' // trim(adjustl(learning_rate_string)) // "," ), &
string_t(indent // indent // '"' // optimizer_key // '" : "' // trim(adjustl(self%optimizer_ )) // '"'), &
string_t(indent // '}') &
]
end procedure

end submodule hyperparameters_s
3 changes: 1 addition & 2 deletions src/inference_engine/inference_engine_m_.f90
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
! Terms of use are as specified in LICENSE.txt
module inference_engine_m_
!! Define an abstraction that supports inference operationsn on a neural network
use string_m, only : string_t
use activation_strategy_m, only : activation_strategy_t
use file_m, only : file_t
use sourcery_m, only : file_t, string_t
use kind_parameters_m, only : rkind
use tensor_m, only : tensor_t
use differentiable_activation_strategy_m, only :differentiable_activation_strategy_t
Expand Down
6 changes: 2 additions & 4 deletions src/inference_engine/inference_engine_s.f90
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
! Copyright (c), The Regents of the University of California
! Terms of use are as specified in LICENSE.txt
submodule(inference_engine_m_) inference_engine_s
use assert_m, only : assert
use intrinsic_array_m, only : intrinsic_array_t
use assert_m, only : assert, intrinsic_array_t
use step_m, only : step_t
use swish_m, only : swish_t
use sigmoid_m, only : sigmoid_t
use relu_m, only : relu_t
use layer_m, only : layer_t
use neuron_m, only : neuron_t
use file_m, only : file_t
use formats_m, only : separated_values
use sourcery_m, only : separated_values
implicit none

interface assert_consistency
Expand Down
53 changes: 53 additions & 0 deletions src/inference_engine/network_configuration_m.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module network_configuration_m
use sourcery_m, only : string_t
implicit none

private
public :: network_configuration_t

type network_configuration_t
private
logical :: skip_connections_ = .false.
integer, allocatable :: nodes_per_layer_(:)
character(len=:), allocatable :: activation_function_
contains
procedure :: to_json
procedure :: equals
generic :: operator(==) => equals
end type

interface network_configuration_t

pure module function from_json(lines) result(network_configuration)
implicit none
type(string_t), intent(in) :: lines(:)
type(network_configuration_t) network_configuration
end function

pure module function from_components(skip_connections, nodes_per_layer, activation_function) result(network_configuration)
implicit none
logical, intent(in) :: skip_connections
integer, intent(in) :: nodes_per_layer(:)
character(len=*), intent(in) :: activation_function
type(network_configuration_t) network_configuration
end function

end interface

interface

pure module function to_json(self) result(lines)
implicit none
class(network_configuration_t), intent(in) :: self
type(string_t), allocatable :: lines(:)
end function

elemental module function equals(lhs, rhs) result(lhs_equals_rhs)
implicit none
class(network_configuration_t), intent(in) :: lhs, rhs
logical lhs_equals_rhs
end function

end interface

end module
68 changes: 68 additions & 0 deletions src/inference_engine/network_configuration_s.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
submodule(network_configuration_m) network_configuration_s
use assert_m, only : assert
use sourcery_m, only : csv
implicit none

character(len=*), parameter :: skip_connections_key = "skip connections"
character(len=*), parameter :: nodes_per_layer_key = "nodes per layer"
character(len=*), parameter :: activation_function_key = "activation function"

contains

module procedure from_components
network_configuration%skip_connections_ = skip_connections
network_configuration%nodes_per_layer_ = nodes_per_layer
network_configuration%activation_function_ = activation_function
end procedure

module procedure equals

call assert(allocated(lhs%activation_function_) .and. allocated(rhs%activation_function_), "network_configuration_s(equals): allocated activation_functions")

lhs_equals_rhs = &
lhs%skip_connections_ .eqv. rhs%skip_connections_ .and. &
lhs%activation_function_ == rhs%activation_function_ .and. &
all(lhs%nodes_per_layer_ == rhs%nodes_per_layer_)

end procedure

module procedure from_json
integer l
logical network_configuration_key_found

network_configuration_key_found = .false.

do l=1,size(lines)
if (lines(l)%get_json_key() == "network configuration") then
network_configuration_key_found = .true.
network_configuration%skip_connections_ = lines(l+1)%get_json_value(string_t(skip_connections_key), mold=.true.)
network_configuration%nodes_per_layer_ = lines(l+2)%get_json_value(string_t(nodes_per_layer_key), mold=[integer::])
network_configuration%activation_function_ = lines(l+3)%get_json_value(string_t(activation_function_key), mold=string_t(""))
return
end if
end do

call assert(network_configuration_key_found, "network_configuration_s(from_json): network_configuration_found")
end procedure

module procedure to_json
character(len=*), parameter :: indent = repeat(" ",ncopies=4)
integer, parameter :: max_logical_width= 6, char_per_elem = 10, brackets = 2
character(len=max_logical_width) skip_connections_string
character(len=:), allocatable :: nodes_per_layer_string

allocate(character(len=size(self%nodes_per_layer_)*char_per_elem + brackets) :: nodes_per_layer_string)

write(skip_connections_string,*) trim(merge("true ","false",self%skip_connections_))
write(nodes_per_layer_string, csv) self%nodes_per_layer_

lines = [ &
string_t(indent // '"network configuration": {'), &
string_t(indent // indent // '"' // skip_connections_key // '" : ' // trim(adjustl(skip_connections_string )) // ','), &
string_t(indent // indent // '"' // nodes_per_layer_key // '" : [' // trim(adjustl(nodes_per_layer_string )) // '],'), &
string_t(indent // indent // '"' // activation_function_key // '" : "' // trim(adjustl(self%activation_function_)) // '"' ), &
string_t(indent // '}') &
]
end procedure

end submodule network_configuration_s
2 changes: 1 addition & 1 deletion src/inference_engine/neuron_m.f90
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
! Copyright (c), The Regents of the University of California
! Terms of use are as specified in LICENSE.txt
module neuron_m
use string_m, only : string_t
use sourcery_m, only : string_t
use kind_parameters_m, only : rkind
implicit none

Expand Down
8 changes: 4 additions & 4 deletions src/inference_engine/neuron_s.f90
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
character(len=:), allocatable :: line
integer i

call assert(adjustl(neuron_lines(start)%string())=='{', "read_json: neuron object start", neuron_lines(start)%string())
call assert(adjustl(neuron_lines(start)%string())=='{', "neuron_s(construct): neuron object start",neuron_lines(start)%string())

line = neuron_lines(start+1)%string()
associate(colon => index(line, ":"))
call assert(adjustl(line(:colon-1))=='"weights"', "read_json: neuron weights", line)
call assert(adjustl(line(:colon-1))=='"weights"', "neuron_s(construct): neuron weights", line)
associate(opening_bracket => colon + index(line(colon+1:), "["))
associate(closing_bracket => opening_bracket + index(line(opening_bracket+1:), "]"))
associate(commas => count("," == [(line(i:i), i=opening_bracket+1,closing_bracket-1)]))
Expand All @@ -30,12 +30,12 @@

line = neuron_lines(start+2)%string()
associate(colon => index(line, ":"))
call assert(adjustl(line(:colon-1))=='"bias"', "read_json: neuron bias", line)
call assert(adjustl(line(:colon-1))=='"bias"', "neuron_s(construct): neuron bias", line)
read(line(colon+1:), fmt=*) neuron%bias_
end associate

line = adjustl(neuron_lines(start+3)%string())
call assert(line(1:1)=='}', "read_json: neuron object end", line)
call assert(line(1:1)=='}', "neuron_s(construct): neuron object end", line)
line = adjustr(neuron_lines(start+3)%string())
if (line(len(line):len(line)) == ",") neuron%next = construct(neuron_lines, start+4)

Expand Down
2 changes: 1 addition & 1 deletion src/inference_engine/relu_m.f90
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module relu_m
use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t
use kind_parameters_m, only : rkind
use string_m, only : string_t
use sourcery_m, only : string_t
implicit none

private
Expand Down
2 changes: 1 addition & 1 deletion src/inference_engine/sigmoid_m.f90
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module sigmoid_m
use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t
use kind_parameters_m, only : rkind
use string_m, only : string_t
use sourcery_m, only : string_t
implicit none

private
Expand Down
2 changes: 1 addition & 1 deletion src/inference_engine/step_m.f90
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module step_m
use activation_strategy_m, only : activation_strategy_t
use kind_parameters_m, only : rkind
use string_m, only : string_t
use sourcery_m, only : string_t
implicit none

private
Expand Down
2 changes: 1 addition & 1 deletion src/inference_engine/swish_m.f90
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module swish_m
use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t
use kind_parameters_m, only : rkind
use string_m, only : string_t
use sourcery_m, only : string_t
implicit none

private
Expand Down
Loading