From c0871d0b7870d1aa08ddd7527498057679b5ba7d Mon Sep 17 00:00:00 2001 From: milancurcic Date: Tue, 20 Aug 2024 22:16:48 -0400 Subject: [PATCH 1/3] Make HDF5 optional under CMake --- CMakeLists.txt | 23 ++++++++++++-------- README.md | 29 +++++++++++++++++--------- cmake/compilers.cmake | 12 +++++------ cmake/options.cmake | 7 +++++++ example/CMakeLists.txt | 17 +++++++++------ example/cnn_from_keras.f90 | 4 +++- example/dense_from_keras.f90 | 4 +++- src/nf/io/nf_io_hdf5.f90 | 2 +- src/nf/io/nf_io_hdf5_submodule.f90 | 2 ++ src/nf/nf_keras_submodule.f90 | 2 ++ src/nf/nf_network_submodule.f90 | 4 +++- test/CMakeLists.txt | 8 ++++++- test/test_cnn_from_keras.f90 | 2 ++ test/test_dense_network_from_keras.f90 | 2 ++ test/test_io_hdf5.f90 | 4 +++- test/test_keras_read_model.f90 | 4 +++- test/test_reshape_layer.f90 | 7 ++++++- 17 files changed, 94 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59f86fff..24d5f655 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,9 +17,12 @@ include(FetchContent) include(cmake/options.cmake) include(cmake/compilers.cmake) -include(cmake/functional.cmake) -include(cmake/h5fortran.cmake) -include(cmake/json.cmake) +if(USE_KERAS_HDF5) + add_definitions(-DUSE_KERAS_HDF5) + include(cmake/functional.cmake) + include(cmake/h5fortran.cmake) + include(cmake/json.cmake) +endif() # library to archive (libneural-fortran.a) add_library(neural-fortran @@ -65,12 +68,14 @@ add_library(neural-fortran src/nf/io/nf_io_hdf5_submodule.f90 ) -target_link_libraries(neural-fortran PRIVATE - functional::functional - h5fortran::h5fortran - HDF5::HDF5 - jsonfortran::jsonfortran -) +if(USE_KERAS_HDF5) + target_link_libraries(neural-fortran PRIVATE + functional::functional + h5fortran::h5fortran + HDF5::HDF5 + jsonfortran::jsonfortran + ) +endif() install(TARGETS neural-fortran) diff --git a/README.md b/README.md index 054582b2..d6fb4760 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ Read the paper [here](https://arxiv.org/abs/1902.06714). RMSProp, Adagrad, Adam, AdamW * More than a dozen activation functions and their derivatives * Loss functions and metrics: Quadratic, Mean Squared Error, Pearson Correlation etc. -* Loading dense and convolutional models from Keras HDF5 (.h5) files * Data-based parallelism +* Loading dense and convolutional models from Keras HDF5 (.h5) files (optional) ### Available layers @@ -51,24 +51,23 @@ cd neural-fortran Required dependencies are: * A Fortran compiler -* [HDF5](https://www.hdfgroup.org/downloads/hdf5/) - (must be provided by the OS package manager or your own build from source) -* [functional-fortran](https://github.com/wavebitscientific/functional-fortran), - [h5fortran](https://github.com/geospace-code/h5fortran), - [json-fortran](https://github.com/jacobwilliams/json-fortran) - (all handled by neural-fortran's build systems, no need for a manual install) * [fpm](https://github.com/fortran-lang/fpm) or [CMake](https://cmake.org) for building the code Optional dependencies are: +* [HDF5](https://www.hdfgroup.org/downloads/hdf5/) for input of Keras models + saved as HDF5 files. You can use the HDF5 that ships from your system + package manager or build your own from source. + Note that HDF5 is still a required dependency if building with fpm instead + of CMake. * OpenCoarrays (for parallel execution with GFortran) * BLAS, MKL, or similar (for offloading `matmul` and `dot_product` calls) -* curl (for downloading testing and example datasets) +* curl (for downloading test and example datasets) Compilers tested include: -* gfortran-9.4.0 +* gfortran-13.2.0 * ifort-2021.4 * ifx-2021.4 @@ -84,7 +83,8 @@ fpm build \ --flag "-I$HDF5INC -L$HDF5LIB" ``` -HDF5 is now a required dependency, so you have to provide it to fpm. +HDF5 is for now a required dependency when building with fpm, so you have to +provide it to fpm. The above command assumes that the `HDF5INC` and `HDF5LIB` environment variables are set to the include and library paths, respectively, of your HDF5 install. @@ -139,6 +139,15 @@ make Tests and examples will be built in the `bin/` directory. +#### Building with Keras HDF5 input + +When building neural-fortran with CMake, Keras HDF5 input feature will not be +built by default. To build it, use the `-DUSE_KERAS_HDF5=1` option. + +``` +cmake .. -DSERIAL=1 -DUSE_KERAS_HDF5=1 +``` + #### Building in parallel mode If you use GFortran and want to run neural-fortran in parallel, diff --git a/cmake/compilers.cmake b/cmake/compilers.cmake index 3fd608f8..09d000b3 100644 --- a/cmake/compilers.cmake +++ b/cmake/compilers.cmake @@ -14,8 +14,8 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") message(STATUS "Configuring build to use BLAS from ${BLAS}") endif() - add_compile_options("$<$,$>:-fcheck=bounds;-fbacktrace>") - add_compile_options("$<$,$>:-Ofast;-fno-frontend-optimize;-fno-backtrace>") + add_compile_options("$<$,$>:-cpp;-O0;-fcheck=bounds;-fbacktrace>") + add_compile_options("$<$,$>:-cpp;-Ofast;-fno-backtrace>") elseif(CMAKE_Fortran_COMPILER_ID MATCHES "^Intel") # compiler flags for ifort @@ -44,12 +44,12 @@ elseif(CMAKE_Fortran_COMPILER_ID MATCHES "^Intel") else() string(APPEND CMAKE_Fortran_FLAGS " -assume byterecl") endif() - add_compile_options("$<$,$>:-check;-traceback>") - # add_compile_options("$<$,$>:-O3>") + add_compile_options("$<$,$>:-fpp;-O0;-check;-traceback>") + add_compile_options("$<$,$>:-fpp;-O3>") elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "Cray") # compiler flags for Cray ftn string(APPEND CMAKE_Fortran_FLAGS " -h noomp") - add_compile_options("$<$,$>:-O0;-g>") - add_compile_options("$<$,$>:-O3>") + add_compile_options("$<$,$>:-e Z;-O0;-g>") + add_compile_options("$<$,$>:-e Z;-O3>") endif() diff --git a/cmake/options.cmake b/cmake/options.cmake index 24bcf76c..3ff52c5d 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -1,4 +1,5 @@ option(SERIAL "Serial execution") +option(USE_KERAS_HDF5 "Enable modules to load Keras HDF5 models") option(${PROJECT_NAME}_BUILD_TESTING "build ${PROJECT_NAME} tests" true) option(${PROJECT_NAME}_BUILD_EXAMPLES "build ${PROJECT_NAME} examples" true) @@ -14,6 +15,12 @@ else() message(STATUS "Configuring build for parallel execution") endif() +if(USE_KERAS_HDF5) + message(STATUS "Configuring build with Keras HDF5 support") +else() + message(STATUS "Configuring build without Keras HDF5 support") +endif() + # --- Generally useful CMake project options # Rpath options necessary for shared library install to work correctly in user projects diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 48bcc91e..3a57fde1 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -10,10 +10,15 @@ foreach(execid quadratic ) add_executable(${execid} ${execid}.f90) - target_link_libraries(${execid} PRIVATE - neural-fortran - h5fortran::h5fortran - jsonfortran::jsonfortran - ${LIBS} - ) + if(USE_KERAS_HDF5) + target_compile_definitions(${execid} PRIVATE USE_KERAS_HDF5) + target_link_libraries(${execid} PRIVATE + neural-fortran + h5fortran::h5fortran + jsonfortran::jsonfortran + ${LIBS} + ) + else() + target_link_libraries(${execid} PRIVATE neural-fortran ${LIBS}) + endif() endforeach() diff --git a/example/cnn_from_keras.f90 b/example/cnn_from_keras.f90 index 6b21f894..caa855ba 100644 --- a/example/cnn_from_keras.f90 +++ b/example/cnn_from_keras.f90 @@ -1,4 +1,5 @@ program cnn_from_keras +#ifdef USE_KERAS_HDF5 ! This example demonstrates loading a convolutional model ! pre-trained on the MNIST dataset from a Keras HDF5 @@ -55,4 +56,5 @@ real function accuracy(net, x, y) accuracy = real(good) / size(x, dim=4) end function accuracy -end program cnn_from_keras +#endif +end program cnn_from_keras \ No newline at end of file diff --git a/example/dense_from_keras.f90 b/example/dense_from_keras.f90 index 4fc332d1..0db6ba88 100644 --- a/example/dense_from_keras.f90 +++ b/example/dense_from_keras.f90 @@ -1,4 +1,5 @@ program dense_from_keras +#ifdef USE_KERAS_HDF5 ! This example demonstrates loading a dense model ! pre-trained on the MNIST dataset from a Keras HDF5 @@ -49,4 +50,5 @@ real function accuracy(net, x, y) accuracy = real(good) / size(x, dim=2) end function accuracy -end program dense_from_keras +#endif +end program dense_from_keras \ No newline at end of file diff --git a/src/nf/io/nf_io_hdf5.f90 b/src/nf/io/nf_io_hdf5.f90 index ac74524e..f3944fa2 100644 --- a/src/nf/io/nf_io_hdf5.f90 +++ b/src/nf/io/nf_io_hdf5.f90 @@ -57,4 +57,4 @@ end subroutine get_hdf5_dataset_real32_4d end interface get_hdf5_dataset -end module nf_io_hdf5 +end module nf_io_hdf5 \ No newline at end of file diff --git a/src/nf/io/nf_io_hdf5_submodule.f90 b/src/nf/io/nf_io_hdf5_submodule.f90 index 8cb6f760..9e798bc7 100644 --- a/src/nf/io/nf_io_hdf5_submodule.f90 +++ b/src/nf/io/nf_io_hdf5_submodule.f90 @@ -1,3 +1,4 @@ +#ifdef USE_KERAS_HDF5 submodule(nf_io_hdf5) nf_io_hdf5_submodule use iso_fortran_env, only: int64, real32, stderr => error_unit @@ -109,3 +110,4 @@ module subroutine get_hdf5_dataset_real32_4d(filename, object_name, values) end subroutine get_hdf5_dataset_real32_4d end submodule nf_io_hdf5_submodule +#endif \ No newline at end of file diff --git a/src/nf/nf_keras_submodule.f90 b/src/nf/nf_keras_submodule.f90 index b5c0d292..67ab50b6 100644 --- a/src/nf/nf_keras_submodule.f90 +++ b/src/nf/nf_keras_submodule.f90 @@ -1,3 +1,4 @@ +#ifdef USE_KERAS_HDF5 submodule(nf_keras) nf_keras_submodule use functional, only: reverse @@ -102,3 +103,4 @@ module function get_keras_h5_layers(filename) result(res) end function get_keras_h5_layers end submodule nf_keras_submodule +#endif \ No newline at end of file diff --git a/src/nf/nf_network_submodule.f90 b/src/nf/nf_network_submodule.f90 index 60fa8579..6c61af14 100644 --- a/src/nf/nf_network_submodule.f90 +++ b/src/nf/nf_network_submodule.f90 @@ -95,6 +95,7 @@ module function network_from_layers(layers) result(res) end function network_from_layers +#ifdef USE_KERAS_HDF5 module function network_from_keras(filename) result(res) character(*), intent(in) :: filename type(network) :: res @@ -222,9 +223,10 @@ module function network_from_keras(filename) result(res) end select - end do + end do end function network_from_keras +#endif pure function get_activation_by_name(activation_name) result(res) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1be8bb8d..727e38ca 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,7 +20,13 @@ foreach(execid metrics ) add_executable(test_${execid} test_${execid}.f90) - target_link_libraries(test_${execid} PRIVATE neural-fortran h5fortran::h5fortran jsonfortran::jsonfortran ${LIBS}) + + if (USE_KERAS_HDF5) + target_compile_definitions(test_${execid} PRIVATE USE_KERAS_HDF5) + target_link_libraries(test_${execid} PRIVATE neural-fortran h5fortran::h5fortran jsonfortran::jsonfortran ${LIBS}) + else() + target_link_libraries(test_${execid} PRIVATE neural-fortran ${LIBS}) + endif() add_test(NAME test_${execid} COMMAND test_${execid}) endforeach() diff --git a/test/test_cnn_from_keras.f90 b/test/test_cnn_from_keras.f90 index 4d93dc51..b18634e2 100644 --- a/test/test_cnn_from_keras.f90 +++ b/test/test_cnn_from_keras.f90 @@ -1,4 +1,5 @@ program test_cnn_from_keras +#ifdef USE_KERAS_HDF5 use iso_fortran_env, only: stderr => error_unit use nf, only: network @@ -66,4 +67,5 @@ real function accuracy(net, x, y) accuracy = real(good) / size(x, dim=4) end function accuracy +#endif end program test_cnn_from_keras diff --git a/test/test_dense_network_from_keras.f90 b/test/test_dense_network_from_keras.f90 index ba247e38..cf14a0d1 100644 --- a/test/test_dense_network_from_keras.f90 +++ b/test/test_dense_network_from_keras.f90 @@ -1,4 +1,5 @@ program test_dense_network_from_keras +#ifdef USE_KERAS_HDF5 use iso_fortran_env, only: stderr => error_unit use nf, only: network @@ -97,4 +98,5 @@ real function accuracy(net, x, y) accuracy = real(good) / size(x, dim=2) end function accuracy +#endif end program test_dense_network_from_keras diff --git a/test/test_io_hdf5.f90 b/test/test_io_hdf5.f90 index e7e0fc88..5bb57e3a 100644 --- a/test/test_io_hdf5.f90 +++ b/test/test_io_hdf5.f90 @@ -1,4 +1,5 @@ program test_io_hdf5 +#ifdef USE_KERAS_HDF5 use iso_fortran_env, only: stderr => error_unit use nf_datasets, only: download_and_unpack, keras_dense_mnist_url @@ -54,4 +55,5 @@ program test_io_hdf5 stop 1 end if -end program test_io_hdf5 +#endif +end program test_io_hdf5 \ No newline at end of file diff --git a/test/test_keras_read_model.f90 b/test/test_keras_read_model.f90 index ba021a5a..3c0073b4 100644 --- a/test/test_keras_read_model.f90 +++ b/test/test_keras_read_model.f90 @@ -1,4 +1,5 @@ program test_keras_read_model +#ifdef USE_KERAS_HDF5 use iso_fortran_env, only: stderr => error_unit use nf_datasets, only: download_and_unpack, keras_dense_mnist_url, & @@ -105,4 +106,5 @@ program test_keras_read_model stop 1 end if -end program test_keras_read_model +#endif +end program test_keras_read_model \ No newline at end of file diff --git a/test/test_reshape_layer.f90 b/test/test_reshape_layer.f90 index 405afd98..6371caff 100644 --- a/test/test_reshape_layer.f90 +++ b/test/test_reshape_layer.f90 @@ -2,7 +2,10 @@ program test_reshape_layer use iso_fortran_env, only: stderr => error_unit use nf, only: input, network, reshape_layer => reshape - use nf_datasets, only: download_and_unpack, keras_reshape_url + use nf_datasets, only: download_and_unpack +#ifdef USE_KERAS_HDF5 + use nf_datasets, only: keras_reshape_url +#endif implicit none @@ -43,6 +46,7 @@ program test_reshape_layer ok = .false. end if +#ifdef USE_KERAS_HDF5 ! Now test reading the reshape layer from a Keras h5 model. inquire(file=keras_reshape_path, exist=file_exists) if (.not. file_exists) call download_and_unpack(keras_reshape_url) @@ -67,6 +71,7 @@ program test_reshape_layer write(stderr, '(a)') 'the target shape of the reshape layer is correct.. failed' ok = .false. end if +#endif if (ok) then print '(a)', 'test_reshape_layer: All tests passed.' From ba3620e0b7ea8d73ef21bb33e2418fccbb157217 Mon Sep 17 00:00:00 2001 From: milancurcic Date: Fri, 30 Aug 2024 13:55:16 -0400 Subject: [PATCH 2/3] Rename jobs --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0df37f11..8d35ccb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ env: jobs: - build_and_test_debug_profile: + gnu-fpm-debug: name: Build and test in debug mode runs-on: ubuntu-latest @@ -42,7 +42,7 @@ jobs: - name: Test run: fpm test --profile debug - build_and_test_release_profile: + gnu-fpm-release: name: Build and test in release mode runs-on: ubuntu-latest From 896ce3531f3a892866ccb12b24fecfca759b4858 Mon Sep 17 00:00:00 2001 From: milancurcic Date: Fri, 30 Aug 2024 14:11:41 -0400 Subject: [PATCH 3/3] Increase tolerance a bit in conv2d_network tests --- test/test_conv2d_network.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_conv2d_network.f90 b/test/test_conv2d_network.f90 index 47c9a819..28dce100 100644 --- a/test/test_conv2d_network.f90 +++ b/test/test_conv2d_network.f90 @@ -39,7 +39,7 @@ program test_conv2d_network type(network) :: cnn real :: y(1) - real :: tolerance = 1e-5 + real :: tolerance = 1e-4 integer :: n integer, parameter :: num_iterations = 1000 @@ -76,7 +76,7 @@ program test_conv2d_network type(network) :: cnn real :: x(1, 8, 8) real :: y(1) - real :: tolerance = 1e-5 + real :: tolerance = 1e-4 integer :: n integer, parameter :: num_iterations = 1000 @@ -111,7 +111,7 @@ program test_conv2d_network type(network) :: cnn real :: x(1, 12, 12) real :: y(9) - real :: tolerance = 1e-5 + real :: tolerance = 1e-4 integer :: n integer, parameter :: num_iterations = 5000