diff options
author | Anthony Barbier <Anthony.barbier@arm.com> | 2017-12-14 23:48:46 +0000 |
---|---|---|
committer | Anthony Barbier <anthony.barbier@arm.com> | 2018-01-24 10:01:21 +0000 |
commit | 8140e1e155d3430992fa46e04ef8938ff09ffd2d (patch) | |
tree | 9bcf86d01635bfc73e8debd1bda75e6f75b8b406 /examples | |
parent | 8a3da6f91f90c566b844d568f4ec43b946915af8 (diff) | |
download | armcl-8140e1e155d3430992fa46e04ef8938ff09ffd2d.tar.gz armcl-8140e1e155d3430992fa46e04ef8938ff09ffd2d.tar.bz2 armcl-8140e1e155d3430992fa46e04ef8938ff09ffd2d.zip |
arm_compute v17.12
Diffstat (limited to 'examples')
-rw-r--r-- | examples/SConscript | 31 | ||||
-rw-r--r-- | examples/cl_sgemm.cpp | 216 | ||||
-rw-r--r-- | examples/gc_absdiff.cpp | 112 | ||||
-rw-r--r-- | examples/graph_alexnet.cpp | 152 | ||||
-rw-r--r-- | examples/graph_googlenet.cpp | 204 | ||||
-rw-r--r-- | examples/graph_lenet.cpp | 43 | ||||
-rw-r--r-- | examples/graph_mobilenet.cpp | 175 | ||||
-rw-r--r-- | examples/graph_squeezenet.cpp | 207 | ||||
-rw-r--r-- | examples/graph_vgg16.cpp | 222 | ||||
-rw-r--r-- | examples/graph_vgg19.cpp | 231 |
10 files changed, 1452 insertions, 141 deletions
diff --git a/examples/SConscript b/examples/SConscript index 52d2f26cf..9be9fa9d9 100644 --- a/examples/SConscript +++ b/examples/SConscript @@ -27,6 +27,10 @@ Import('env') if env['opencl']: Import('opencl') +if env['gles_compute'] and env['os'] != 'android': + Import('egl') + Import('glesv2') + examples_env = env.Clone() examples_env.Append(CPPPATH = ["#"]) @@ -56,7 +60,7 @@ if env['opencl'] and env['neon']: Import('arm_compute_graph_a') Import('arm_compute_core_a') Import('arm_compute_a') - arm_compute_graph_libs = [ arm_compute_graph_a, arm_compute_a, arm_compute_core_a] + arm_compute_graph_libs = [ arm_compute_a, arm_compute_core_a, "OpenCL"] graph_dependency = arm_compute_graph_a else: Import('arm_compute_graph_so') @@ -66,8 +70,14 @@ if env['opencl'] and env['neon']: graph_utils = examples_env.Object("../utils/GraphUtils.cpp") for file in Glob("./graph_*.cpp"): example = os.path.basename(os.path.splitext(str(file))[0]) - prog = examples_env.Program(example, ["{}.cpp".format(example), utils, graph_utils], CPPDEFINES=['ARM_COMPUTE_CL'], LIBS = arm_compute_graph_libs + ["OpenCL"]) - Depends(prog, [graph_dependency, opencl]) + prog = None + if env['os'] == 'android': + prog = examples_env.Program(example, ["{}.cpp".format(example), utils, graph_utils], LIBS = arm_compute_graph_libs + ["OpenCL"], LINKFLAGS=examples_env["LINKFLAGS"]+['-Wl,--whole-archive',graph_dependency,'-Wl,--no-whole-archive']) + Depends(prog, [graph_dependency, opencl]) + else: + #-Wl,--allow-shlib-undefined: Ignore dependencies of dependencies + prog = examples_env.Program(example, ["{}.cpp".format(example), utils, graph_utils], LIBS = arm_compute_graph_libs, LINKFLAGS=examples_env["LINKFLAGS"]+['-Wl,--allow-shlib-undefined'] ) + Depends(prog, graph_dependency) alias = examples_env.Alias(example, prog) Default(alias) @@ -86,3 +96,18 @@ if env['neon']: Depends(prog, arm_compute_dependency) alias = examples_env.Alias(example, prog) Default(alias) + +if env['gles_compute']: + for file in Glob("./gc_*.cpp"): + example = os.path.basename(os.path.splitext(str(file))[0]) + if env['os'] != 'android': + prog = examples_env.Program(example, ["{}.cpp".format(example), utils], CPPDEFINES=['ARM_COMPUTE_GC'], LIBS = [arm_compute_libs, "EGL", "GLESv2"]) + Depends(prog, [arm_compute_dependency, egl, glesv2]) + else: + if env['arch'] != 'armv7a': + prog = examples_env.Program(example, ["{}.cpp".format(example), utils], CPPDEFINES=['ARM_COMPUTE_GC'], LIBS = [arm_compute_libs, "EGL", "GLESv3"]) + else: + prog = examples_env.Program(example, ["{}.cpp".format(example), utils], CPPDEFINES=['ARM_COMPUTE_GC'], LIBS = [arm_compute_libs, "EGL", "GLESv2"]) + Depends(prog, [arm_compute_dependency]) + alias = examples_env.Alias(example, prog) + Default(alias) diff --git a/examples/cl_sgemm.cpp b/examples/cl_sgemm.cpp new file mode 100644 index 000000000..e1729a85b --- /dev/null +++ b/examples/cl_sgemm.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef ARM_COMPUTE_CL /* Needed by Utils.cpp to handle OpenCL exceptions properly */ +#error "This example needs to be built with -DARM_COMPUTE_CL" +#endif /* ARM_COMPUTE_CL */ + +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/CLFunctions.h" +#include "arm_compute/runtime/CL/CLScheduler.h" +#include "arm_compute/runtime/CL/CLTuner.h" +#include "utils/Utils.h" + +#include <cstdlib> + +using namespace arm_compute; +using namespace utils; + +void main_cl_sgemm(int argc, const char **argv) +{ + NPYLoader npy0, npy1, npy2; + CLTensor src0, src1, src2, dst; + float alpha = 1.0f, beta = 0.0f; + + CLTuner tuner; + CLScheduler::get().default_init(&tuner); + + std::ifstream stream; + if(argc > 1) + { + stream.open(argv[1], std::fstream::in); + } + + if(argc < 3 || (argc < 4 && stream.bad())) + { + // Print help + std::cout << "Usage: 1) ./build/cl_sgemm input_matrix_1.npy input_matrix_2.npy [input_matrix_3.npy] [alpha = 1] [beta = 0]\n"; + std::cout << " 2) ./build/cl_sgemm M N K [alpha = 1.0f] [beta = 0.0f]\n\n"; + std::cout << "Too few or no input_matrices provided. Using M=7, N=3, K=5, alpha=1.0f and beta=0.0f\n\n"; + + src0.allocator()->init(TensorInfo(TensorShape(5U, 7U), 1, DataType::F32)); + src1.allocator()->init(TensorInfo(TensorShape(3U, 5U), 1, DataType::F32)); + src2.allocator()->init(TensorInfo(TensorShape(3U, 7U), 1, DataType::F32)); + } + else + { + if(stream.good()) /* case file1.npy file2.npy [file3.npy] [alpha = 1.0f] [beta = 0.0f] */ + { + npy0.open(argv[1]); + npy0.init_tensor(src0, DataType::F32); + npy1.open(argv[2]); + npy1.init_tensor(src1, DataType::F32); + + if(argc > 3) + { + stream.close(); + stream.clear(); + stream.open(argv[3], std::fstream::in); + if(stream.good()) /* case with third file */ + { + npy2.open(argv[3]); + npy2.init_tensor(src2, DataType::F32); + + if(argc > 4) + { + // Convert string to float + alpha = strtof(argv[4], nullptr); + + if(argc > 5) + { + // Convert string to float + beta = strtof(argv[5], nullptr); + } + } + } + else /* case without third file */ + { + alpha = strtof(argv[3], nullptr); + + if(argc > 4) + { + beta = strtof(argv[4], nullptr); + } + } + } + } + else /* case M N K [alpha = 1.0f] [beta = 0.0f] */ + { + size_t M = strtol(argv[1], nullptr, 10); + size_t N = strtol(argv[2], nullptr, 10); + size_t K = strtol(argv[3], nullptr, 10); + + src0.allocator()->init(TensorInfo(TensorShape(K, M), 1, DataType::F32)); + src1.allocator()->init(TensorInfo(TensorShape(N, K), 1, DataType::F32)); + src2.allocator()->init(TensorInfo(TensorShape(N, M), 1, DataType::F32)); + + if(argc > 4) + { + alpha = strtof(argv[4], nullptr); + + if(argc > 5) + { + beta = strtof(argv[5], nullptr); + } + } + } + } + + init_sgemm_output(dst, src0, src1, DataType::F32); + + // Configure function + CLGEMM sgemm; + sgemm.configure(&src0, &src1, (src2.info()->total_size() > 0) ? &src2 : nullptr, &dst, alpha, beta); + + // Allocate all the images + src0.allocator()->allocate(); + src1.allocator()->allocate(); + dst.allocator()->allocate(); + + // Fill the input images with either the data provided or random data + if(npy0.is_open()) + { + npy0.fill_tensor(src0); + npy1.fill_tensor(src1); + + if(npy2.is_open()) + { + src2.allocator()->allocate(); + npy2.fill_tensor(src2); + } + } + else + { + src2.allocator()->allocate(); + + fill_random_tensor(src0, -1.f, 1.f); + fill_random_tensor(src1, -1.f, 1.f); + fill_random_tensor(src2, -1.f, 1.f); + } + + // Dummy run for CLTuner + sgemm.run(); + + auto start = std::chrono::high_resolution_clock::now(); + + // Execute the function + sgemm.run(); + + // Make sure all the OpenCL jobs are done executing: + CLScheduler::get().sync(); + + auto stop = std::chrono::high_resolution_clock::now(); + + if(!npy0.is_open()) /* If the inputs were not files, print the results */ + { + std::cout << "\nMatrix 1:" << std::endl; + src0.map(true); + src0.print(std::cout, IOFormatInfo()); + src0.unmap(); + + std::cout << "Matrix 2:" << std::endl; + src1.map(true); + src1.print(std::cout, IOFormatInfo()); + src1.unmap(); + + std::cout << "Matrix 3:" << std::endl; + src2.map(true); + src2.print(std::cout, IOFormatInfo()); + src2.unmap(); + + std::cout << "Alpha:" << alpha << "\n\n"; + std::cout << "Beta:" << beta << "\n\n"; + + std::cout << "Output Matrix:" << std::endl; + dst.map(true); + dst.print(std::cout, IOFormatInfo()); + dst.unmap(); + } + else /* Save to .npy file */ + { + save_to_npy(dst, "sgemm_out.npy", npy0.is_fortran()); + } + + auto delta = std::chrono::duration_cast<std::chrono::microseconds>(stop - start); + std::cout << "Time elapsed: " << delta.count() << "us." << std::endl; +} + +/** Main program for sgemm test + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Matrix A, [optional] Matrix B, [optional] Matrix C, [optional] alpha, [optional] beta ) + */ +int main(int argc, const char **argv) +{ + return utils::run_example(argc, argv, main_cl_sgemm); +} diff --git a/examples/gc_absdiff.cpp b/examples/gc_absdiff.cpp new file mode 100644 index 000000000..cd3e42989 --- /dev/null +++ b/examples/gc_absdiff.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ARM_COMPUTE_GC /* Needed by Utils.cpp to handle OpenGL ES exceptions properly */ +#error "This example needs to be built with -DARM_COMPUTE_GC" +#endif /* ARM_COMPUTE_GC */ + +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/GLES_COMPUTE/GCFunctions.h" +#include "arm_compute/runtime/GLES_COMPUTE/GCScheduler.h" +#include "utils/Utils.h" + +using namespace arm_compute; +using namespace utils; + +void main_gc_absdiff(int argc, const char **argv) +{ + PPMLoader ppm1, ppm2; + GCImage src1, src2, dst; + GCScheduler::get().default_init(); + if(argc < 2) + { + // Print help + std::cout << "Usage: " << argv[0] << " [input0_image.ppm] [input1_image.ppm] \n\n"; + std::cout << "No input_image provided, creating two dummy 640x480 images\n"; + // Create two empty grayscale 640x480 images + src1.allocator()->init(TensorInfo(640, 480, Format::U8)); + src2.allocator()->init(TensorInfo(640, 480, Format::U8)); + } + else if(argc < 3) + { + // Print help + std::cout << "Usage: " << argv[0] << " [input0_image.ppm] [input1_image.ppm] \n\n"; + std::cout << "Only one input_image provided, creating a dummy 640x480 image\n"; + ppm1.open(argv[1]); + ppm1.init_image(src1, Format::U8); + // Create an empty grayscale 640x480 image + src2.allocator()->init(TensorInfo(640, 480, Format::U8)); + } + else + { + ppm1.open(argv[1]); + ppm1.init_image(src1, Format::U8); + ppm2.open(argv[2]); + ppm2.init_image(src2, Format::U8); + } + + // Configure the temporary and destination images + dst.allocator()->init(*src1.info()); + + GCAbsoluteDifference absdiff; + absdiff.configure(&src1, &src2, &dst); + + // Allocate all the images + src1.allocator()->allocate(); + src2.allocator()->allocate(); + dst.allocator()->allocate(); + + // Fill the input image with the content of the PPM image if a filename was provided: + if(ppm1.is_open()) + { + ppm1.fill_image(src1); + } + if(ppm2.is_open()) + { + ppm2.fill_image(src2); + } + + // Execute the functions: + absdiff.run(); + + // Make sure all the jobs are done executing: + GCScheduler::get().sync(); + + // Save the result to file: + if(ppm1.is_open()) + { + const std::string output_filename = std::string(argv[1]) + "_out.ppm"; + save_to_ppm(dst, output_filename); // save_to_ppm maps and unmaps the image to store as PPM + } +} + +/** Main program for absdiff test + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Path to the first PPM image to process, [optional] Path the the second PPM image to process ) + */ +int main(int argc, const char **argv) +{ + return utils::run_example(argc, argv, main_gc_absdiff); +} diff --git a/examples/graph_alexnet.cpp b/examples/graph_alexnet.cpp index bb5905ec2..0d5531f28 100644 --- a/examples/graph_alexnet.cpp +++ b/examples/graph_alexnet.cpp @@ -21,16 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef ARM_COMPUTE_CL /* Needed by Utils.cpp to handle OpenCL exceptions properly */ -#error "This example needs to be built with -DARM_COMPUTE_CL" -#endif /* ARM_COMPUTE_CL */ - -#include "arm_compute/core/Logger.h" #include "arm_compute/graph/Graph.h" #include "arm_compute/graph/Nodes.h" -#include "arm_compute/runtime/CL/CLScheduler.h" -#include "arm_compute/runtime/CPP/CPPScheduler.h" -#include "arm_compute/runtime/Scheduler.h" #include "support/ToolchainSupport.h" #include "utils/GraphUtils.h" #include "utils/Utils.h" @@ -42,76 +34,10 @@ using namespace arm_compute::graph; using namespace arm_compute::graph_utils; -/** Generates appropriate accessor according to the specified path - * - * @note If path is empty will generate a DummyAccessor else will generate a NumPyBinLoader - * - * @param[in] path Path to the data files - * @param[in] data_file Relative path to the data files from path - * - * @return An appropriate tensor accessor - */ -std::unique_ptr<ITensorAccessor> get_accessor(const std::string &path, const std::string &data_file) -{ - if(path.empty()) - { - return arm_compute::support::cpp14::make_unique<DummyAccessor>(); - } - else - { - return arm_compute::support::cpp14::make_unique<NumPyBinLoader>(path + data_file); - } -} - -/** Generates appropriate input accessor according to the specified ppm_path - * - * @note If ppm_path is empty will generate a DummyAccessor else will generate a PPMAccessor - * - * @param[in] ppm_path Path to PPM file - * @param[in] mean_r Red mean value to be subtracted from red channel - * @param[in] mean_g Green mean value to be subtracted from green channel - * @param[in] mean_b Blue mean value to be subtracted from blue channel - * - * @return An appropriate tensor accessor - */ -std::unique_ptr<ITensorAccessor> get_input_accessor(const std::string &ppm_path, float mean_r, float mean_g, float mean_b) -{ - if(ppm_path.empty()) - { - return arm_compute::support::cpp14::make_unique<DummyAccessor>(); - } - else - { - return arm_compute::support::cpp14::make_unique<PPMAccessor>(ppm_path, true, mean_r, mean_g, mean_b); - } -} - -/** Generates appropriate output accessor according to the specified labels_path - * - * @note If labels_path is empty will generate a DummyAccessor else will generate a TopNPredictionsAccessor - * - * @param[in] labels_path Path to labels text file - * @param[in] top_n (Optional) Number of output classes to print - * @param[out] output_stream (Optional) Output stream - * - * @return An appropriate tensor accessor - */ -std::unique_ptr<ITensorAccessor> get_output_accessor(const std::string &labels_path, size_t top_n = 5, std::ostream &output_stream = std::cout) -{ - if(labels_path.empty()) - { - return arm_compute::support::cpp14::make_unique<DummyAccessor>(); - } - else - { - return arm_compute::support::cpp14::make_unique<TopNPredictionsAccessor>(labels_path, top_n, output_stream); - } -} - /** Example demonstrating how to implement AlexNet's network using the Compute Library's graph API * * @param[in] argc Number of arguments - * @param[in] argv Arguments ( [optional] Path to the weights folder, [optional] image, [optional] labels ) + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) */ void main_graph_alexnet(int argc, const char **argv) { @@ -123,62 +49,62 @@ void main_graph_alexnet(int argc, const char **argv) constexpr float mean_g = 116.67f; /* Mean value to subtract from green channel */ constexpr float mean_b = 104.01f; /* Mean value to subtract from blue channel */ + // Set target. 0 (NEON), 1 (OpenCL). By default it is NEON + TargetHint target_hint = set_target_hint(argc > 1 ? std::strtol(argv[1], nullptr, 10) : 0); + ConvolutionMethodHint convolution_hint = target_hint == TargetHint::NEON ? ConvolutionMethodHint::GEMM : ConvolutionMethodHint::DIRECT; + // Parse arguments if(argc < 2) { // Print help - std::cout << "Usage: " << argv[0] << " [path_to_data] [image] [labels]\n\n"; + std::cout << "Usage: " << argv[0] << " [target] [path_to_data] [image] [labels]\n\n"; std::cout << "No data folder provided: using random values\n\n"; } else if(argc == 2) { - data_path = argv[1]; - std::cout << "Usage: " << argv[0] << " " << argv[1] << " [image] [labels]\n\n"; - std::cout << "No image provided: using random values\n\n"; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; } else if(argc == 3) { - data_path = argv[1]; - image = argv[2]; - std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " [labels]\n\n"; - std::cout << "No text file with labels provided: skipping output accessor\n\n"; + data_path = argv[2]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " [image] [labels]\n\n"; + std::cout << "No image provided: using random values\n\n"; } - else + else if(argc == 4) { - data_path = argv[1]; - image = argv[2]; - label = argv[3]; + data_path = argv[2]; + image = argv[3]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " " << argv[3] << " [labels]\n\n"; + std::cout << "No text file with labels provided: skipping output accessor\n\n"; } - - // Check if OpenCL is available and initialize the scheduler - TargetHint hint = TargetHint::NEON; - if(arm_compute::opencl_is_available()) + else { - arm_compute::CLScheduler::get().default_init(); - hint = TargetHint::OPENCL; + data_path = argv[2]; + image = argv[3]; + label = argv[4]; } Graph graph; - arm_compute::Logger::get().set_logger(std::cout, arm_compute::LoggerVerbosity::INFO); - graph << hint + graph << target_hint << Tensor(TensorInfo(TensorShape(227U, 227U, 3U, 1U), 1, DataType::F32), get_input_accessor(image, mean_r, mean_g, mean_b)) // Layer 1 << ConvolutionLayer( 11U, 11U, 96U, - get_accessor(data_path, "/cnn_data/alexnet_model/conv1_w.npy"), - get_accessor(data_path, "/cnn_data/alexnet_model/conv1_b.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv1_b.npy"), PadStrideInfo(4, 4, 0, 0)) << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) << NormalizationLayer(NormalizationLayerInfo(NormType::CROSS_MAP, 5, 0.0001f, 0.75f)) << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(2, 2, 0, 0))) // Layer 2 - << ConvolutionMethodHint::DIRECT + << convolution_hint << ConvolutionLayer( 5U, 5U, 256U, - get_accessor(data_path, "/cnn_data/alexnet_model/conv2_w.npy"), - get_accessor(data_path, "/cnn_data/alexnet_model/conv2_b.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv2_b.npy"), PadStrideInfo(1, 1, 2, 2), 2) << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) << NormalizationLayer(NormalizationLayerInfo(NormType::CROSS_MAP, 5, 0.0001f, 0.75f)) @@ -186,42 +112,42 @@ void main_graph_alexnet(int argc, const char **argv) // Layer 3 << ConvolutionLayer( 3U, 3U, 384U, - get_accessor(data_path, "/cnn_data/alexnet_model/conv3_w.npy"), - get_accessor(data_path, "/cnn_data/alexnet_model/conv3_b.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv3_w.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv3_b.npy"), PadStrideInfo(1, 1, 1, 1)) << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) // Layer 4 << ConvolutionLayer( 3U, 3U, 384U, - get_accessor(data_path, "/cnn_data/alexnet_model/conv4_w.npy"), - get_accessor(data_path, "/cnn_data/alexnet_model/conv4_b.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv4_w.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv4_b.npy"), PadStrideInfo(1, 1, 1, 1), 2) << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) // Layer 5 << ConvolutionLayer( 3U, 3U, 256U, - get_accessor(data_path, "/cnn_data/alexnet_model/conv5_w.npy"), - get_accessor(data_path, "/cnn_data/alexnet_model/conv5_b.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv5_w.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/conv5_b.npy"), PadStrideInfo(1, 1, 1, 1), 2) << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(2, 2, 0, 0))) // Layer 6 << FullyConnectedLayer( 4096U, - get_accessor(data_path, "/cnn_data/alexnet_model/fc6_w.npy"), - get_accessor(data_path, "/cnn_data/alexnet_model/fc6_b.npy")) + get_weights_accessor(data_path, "/cnn_data/alexnet_model/fc6_w.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/fc6_b.npy")) << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) // Layer 7 << FullyConnectedLayer( 4096U, - get_accessor(data_path, "/cnn_data/alexnet_model/fc7_w.npy"), - get_accessor(data_path, "/cnn_data/alexnet_model/fc7_b.npy")) + get_weights_accessor(data_path, "/cnn_data/alexnet_model/fc7_w.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/fc7_b.npy")) << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) // Layer 8 << FullyConnectedLayer( 1000U, - get_accessor(data_path, "/cnn_data/alexnet_model/fc8_w.npy"), - get_accessor(data_path, "/cnn_data/alexnet_model/fc8_b.npy")) + get_weights_accessor(data_path, "/cnn_data/alexnet_model/fc8_w.npy"), + get_weights_accessor(data_path, "/cnn_data/alexnet_model/fc8_b.npy")) // Softmax << SoftmaxLayer() << Tensor(get_output_accessor(label, 5)); @@ -233,7 +159,7 @@ void main_graph_alexnet(int argc, const char **argv) /** Main program for AlexNet * * @param[in] argc Number of arguments - * @param[in] argv Arguments ( [optional] Path to the weights folder, [optional] image, [optional] labels ) + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) */ int main(int argc, const char **argv) { diff --git a/examples/graph_googlenet.cpp b/examples/graph_googlenet.cpp new file mode 100644 index 000000000..d08382ab8 --- /dev/null +++ b/examples/graph_googlenet.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Nodes.h" +#include "arm_compute/graph/SubGraph.h" +#include "support/ToolchainSupport.h" +#include "utils/GraphUtils.h" +#include "utils/Utils.h" + +#include <cstdlib> +#include <tuple> + +using namespace arm_compute::graph; +using namespace arm_compute::graph_utils; + +namespace +{ +BranchLayer get_inception_node(const std::string &data_path, std::string &¶m_path, + unsigned int a_filt, + std::tuple<unsigned int, unsigned int> b_filters, + std::tuple<unsigned int, unsigned int> c_filters, + unsigned int d_filt) +{ + std::string total_path = "/cnn_data/googlenet_model/" + param_path + "/" + param_path + "_"; + SubGraph i_a; + i_a << ConvolutionLayer( + 1U, 1U, a_filt, + get_weights_accessor(data_path, total_path + "1x1_w.npy"), + get_weights_accessor(data_path, total_path + "1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)); + + SubGraph i_b; + i_b << ConvolutionLayer( + 1U, 1U, std::get<0>(b_filters), + get_weights_accessor(data_path, total_path + "3x3_reduce_w.npy"), + get_weights_accessor(data_path, total_path + "3x3_reduce_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, std::get<1>(b_filters), + get_weights_accessor(data_path, total_path + "3x3_w.npy"), + get_weights_accessor(data_path, total_path + "3x3_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)); + + SubGraph i_c; + i_c << ConvolutionLayer( + 1U, 1U, std::get<0>(c_filters), + get_weights_accessor(data_path, total_path + "5x5_reduce_w.npy"), + get_weights_accessor(data_path, total_path + "5x5_reduce_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 5U, 5U, std::get<1>(c_filters), + get_weights_accessor(data_path, total_path + "5x5_w.npy"), + get_weights_accessor(data_path, total_path + "5x5_b.npy"), + PadStrideInfo(1, 1, 2, 2)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)); + + SubGraph i_d; + i_d << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(1, 1, 1, 1, DimensionRoundingType::CEIL))) + << ConvolutionLayer( + 1U, 1U, d_filt, + get_weights_accessor(data_path, total_path + "pool_proj_w.npy"), + get_weights_accessor(data_path, total_path + "pool_proj_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)); + + return BranchLayer(BranchMergeMethod::DEPTH_CONCATENATE, std::move(i_a), std::move(i_b), std::move(i_c), std::move(i_d)); +} +} // namespace + +/** Example demonstrating how to implement Googlenet's network using the Compute Library's graph API + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +void main_graph_googlenet(int argc, const char **argv) +{ + std::string data_path; /* Path to the trainable data */ + std::string image; /* Image data */ + std::string label; /* Label data */ + + constexpr float mean_r = 122.68f; /* Mean value to subtract from red channel */ + constexpr float mean_g = 116.67f; /* Mean value to subtract from green channel */ + constexpr float mean_b = 104.01f; /* Mean value to subtract from blue channel */ + + // Set target. 0 (NEON), 1 (OpenCL). By default it is NEON + TargetHint target_hint = set_target_hint(argc > 1 ? std::strtol(argv[1], nullptr, 10) : 0); + ConvolutionMethodHint convolution_hint = target_hint == TargetHint::NEON ? ConvolutionMethodHint::GEMM : ConvolutionMethodHint::DIRECT; + + // Parse arguments + if(argc < 2) + { + // Print help + std::cout << "Usage: " << argv[0] << " [target] [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 2) + { + std::cout << "Usage: " << argv[0] << " " << argv[1] << " [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 3) + { + data_path = argv[2]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " [image] [labels]\n\n"; + std::cout << "No image provided: using random values\n\n"; + } + else if(argc == 4) + { + data_path = argv[2]; + image = argv[3]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " " << argv[3] << " [labels]\n\n"; + std::cout << "No text file with labels provided: skipping output accessor\n\n"; + } + else + { + data_path = argv[2]; + image = argv[3]; + label = argv[4]; + } + + Graph graph; + + graph << target_hint + << Tensor(TensorInfo(TensorShape(224U, 224U, 3U, 1U), 1, DataType::F32), + get_input_accessor(image, mean_r, mean_g, mean_b)) + << ConvolutionLayer( + 7U, 7U, 64U, + get_weights_accessor(data_path, "/cnn_data/googlenet_model/conv1/conv1_7x7_s2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/googlenet_model/conv1/conv1_7x7_s2_b.npy"), + PadStrideInfo(2, 2, 3, 3)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(2, 2, 0, 0, DimensionRoundingType::CEIL))) + << NormalizationLayer(NormalizationLayerInfo(NormType::CROSS_MAP, 5, 0.0001f, 0.75f)) + << convolution_hint + << ConvolutionLayer( + 1U, 1U, 64U, + get_weights_accessor(data_path, "/cnn_data/googlenet_model/conv2/conv2_3x3_reduce_w.npy"), + get_weights_accessor(data_path, "/cnn_data/googlenet_model/conv2/conv2_3x3_reduce_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 192U, + get_weights_accessor(data_path, "/cnn_data/googlenet_model/conv2/conv2_3x3_w.npy"), + get_weights_accessor(data_path, "/cnn_data/googlenet_model/conv2/conv2_3x3_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << NormalizationLayer(NormalizationLayerInfo(NormType::CROSS_MAP, 5, 0.0001f, 0.75f)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(2, 2, 0, 0, DimensionRoundingType::CEIL))) + << get_inception_node(data_path, "inception_3a", 64, std::make_tuple(96U, 128U), std::make_tuple(16U, 32U), 32U) + << get_inception_node(data_path, "inception_3b", 128, std::make_tuple(128U, 192U), std::make_tuple(32U, 96U), 64U) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(2, 2, 0, 0, DimensionRoundingType::CEIL))) + << get_inception_node(data_path, "inception_4a", 192, std::make_tuple(96U, 208U), std::make_tuple(16U, 48U), 64U) + << get_inception_node(data_path, "inception_4b", 160, std::make_tuple(112U, 224U), std::make_tuple(24U, 64U), 64U) + << get_inception_node(data_path, "inception_4c", 128, std::make_tuple(128U, 256U), std::make_tuple(24U, 64U), 64U) + << get_inception_node(data_path, "inception_4d", 112, std::make_tuple(144U, 288U), std::make_tuple(32U, 64U), 64U) + << get_inception_node(data_path, "inception_4e", 256, std::make_tuple(160U, 320U), std::make_tuple(32U, 128U), 128U) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(2, 2, 0, 0, DimensionRoundingType::CEIL))) + << get_inception_node(data_path, "inception_5a", 256, std::make_tuple(160U, 320U), std::make_tuple(32U, 128U), 128U) + << get_inception_node(data_path, "inception_5b", 384, std::make_tuple(192U, 384U), std::make_tuple(48U, 128U), 128U) + << PoolingLayer(PoolingLayerInfo(PoolingType::AVG, 7, PadStrideInfo(1, 1, 0, 0, DimensionRoundingType::CEIL))) + << FullyConnectedLayer( + 1000U, + get_weights_accessor(data_path, "/cnn_data/googlenet_model/loss3/loss3_classifier_w.npy"), + get_weights_accessor(data_path, "/cnn_data/googlenet_model/loss3/loss3_classifier_b.npy")) + << SoftmaxLayer() + << Tensor(get_output_accessor(label, 5)); + + graph.run(); +} + +/** Main program for Googlenet + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +int main(int argc, const char **argv) +{ + return arm_compute::utils::run_example(argc, argv, main_graph_googlenet); +} diff --git a/examples/graph_lenet.cpp b/examples/graph_lenet.cpp index 1427abe15..d4a44382b 100644 --- a/examples/graph_lenet.cpp +++ b/examples/graph_lenet.cpp @@ -21,26 +21,19 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef ARM_COMPUTE_CL /* Needed by Utils.cpp to handle OpenCL exceptions properly */ -#error "This example needs to be built with -DARM_COMPUTE_CL" -#endif /* ARM_COMPUTE_CL */ - -#include "arm_compute/core/Logger.h" #include "arm_compute/graph/Graph.h" #include "arm_compute/graph/Nodes.h" -#include "arm_compute/runtime/CL/CLScheduler.h" -#include "arm_compute/runtime/Scheduler.h" #include "support/ToolchainSupport.h" #include "utils/GraphUtils.h" #include "utils/Utils.h" #include <cstdlib> -#include <iostream> -#include <memory> using namespace arm_compute::graph; using namespace arm_compute::graph_utils; +namespace +{ /** Generates appropriate accessor according to the specified path * * @note If path is empty will generate a DummyAccessor else will generate a NumPyBinLoader @@ -61,51 +54,51 @@ std::unique_ptr<ITensorAccessor> get_accessor(const std::string &path, const std return arm_compute::support::cpp14::make_unique<NumPyBinLoader>(path + data_file); } } +} // namespace /** Example demonstrating how to implement LeNet's network using the Compute Library's graph API * * @param[in] argc Number of arguments - * @param[in] argv Arguments ( [optional] Path to the weights folder, [optional] batches ) + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] batches ) */ void main_graph_lenet(int argc, const char **argv) { std::string data_path; /** Path to the trainable data */ unsigned int batches = 4; /** Number of batches */ + // Set target. 0 (NEON), 1 (OpenCL). By default it is NEON + TargetHint target_hint = set_target_hint(argc > 1 ? std::strtol(argv[1], nullptr, 10) : 0); + // Parse arguments if(argc < 2) { // Print help - std::cout << "Usage: " << argv[0] << " [path_to_data] [batches]\n\n"; + std::cout << "Usage: " << argv[0] << " [target] [path_to_data] [batches]\n\n"; std::cout << "No data folder provided: using random values\n\n"; } else if(argc == 2) { + std::cout << "Usage: " << argv[0] << " " << argv[1] << " [path_to_data] [batches]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 3) + { //Do something with argv[1] - data_path = argv[1]; + data_path = argv[2]; std::cout << "Usage: " << argv[0] << " [path_to_data] [batches]\n\n"; std::cout << "No number of batches where specified, thus will use the default : " << batches << "\n\n"; } else { //Do something with argv[1] and argv[2] - data_path = argv[1]; - batches = std::strtol(argv[2], nullptr, 0); - } - - // Check if OpenCL is available and initialize the scheduler - TargetHint hint = TargetHint::NEON; - if(arm_compute::opencl_is_available()) - { - arm_compute::CLScheduler::get().default_init(); - hint = TargetHint::OPENCL; + data_path = argv[2]; + batches = std::strtol(argv[3], nullptr, 0); } Graph graph; - arm_compute::Logger::get().set_logger(std::cout, arm_compute::LoggerVerbosity::INFO); //conv1 << pool1 << conv2 << pool2 << fc1 << act1 << fc2 << smx - graph << hint + graph << target_hint << Tensor(TensorInfo(TensorShape(28U, 28U, 1U, batches), 1, DataType::F32), DummyAccessor()) << ConvolutionLayer( 5U, 5U, 20U, @@ -137,7 +130,7 @@ void main_graph_lenet(int argc, const char **argv) /** Main program for LeNet * * @param[in] argc Number of arguments - * @param[in] argv Arguments ( [optional] Path to the weights folder, [optional] batches ) + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] batches ) */ int main(int argc, const char **argv) { diff --git a/examples/graph_mobilenet.cpp b/examples/graph_mobilenet.cpp new file mode 100644 index 000000000..0c916c7ba --- /dev/null +++ b/examples/graph_mobilenet.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Nodes.h" +#include "support/ToolchainSupport.h" +#include "utils/GraphUtils.h" +#include "utils/Utils.h" + +#include <cstdlib> + +using namespace arm_compute::graph; +using namespace arm_compute::graph_utils; + +namespace +{ +BranchLayer get_dwsc_node(const std::string &data_path, std::string &¶m_path, + unsigned int conv_filt, + PadStrideInfo dwc_pad_stride_info, PadStrideInfo conv_pad_stride_info) +{ + std::string total_path = "/cnn_data/mobilenet_v1_model/" + param_path + "_"; + SubGraph sg; + sg << DepthwiseConvolutionLayer( + 3U, 3U, + get_weights_accessor(data_path, total_path + "depthwise_depthwise_weights.npy"), + std::unique_ptr<arm_compute::graph::ITensorAccessor>(nullptr), + dwc_pad_stride_info, + true) + << BatchNormalizationLayer( + get_weights_accessor(data_path, total_path + "depthwise_BatchNorm_moving_mean.npy"), + get_weights_accessor(data_path, total_path + "depthwise_BatchNorm_moving_variance.npy"), + get_weights_accessor(data_path, total_path + "depthwise_BatchNorm_beta.npy"), + get_weights_accessor(data_path, total_path + "depthwise_BatchNorm_gamma.npy"), + 0.001f) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::BOUNDED_RELU, 6.f)) + << ConvolutionLayer( + 1U, 1U, conv_filt, + get_weights_accessor(data_path, total_path + "pointwise_weights.npy"), + std::unique_ptr<arm_compute::graph::ITensorAccessor>(nullptr), + conv_pad_stride_info) + << BatchNormalizationLayer( + get_weights_accessor(data_path, total_path + "pointwise_BatchNorm_moving_mean.npy"), + get_weights_accessor(data_path, total_path + "pointwise_BatchNorm_moving_variance.npy"), + get_weights_accessor(data_path, total_path + "pointwise_BatchNorm_beta.npy"), + get_weights_accessor(data_path, total_path + "pointwise_BatchNorm_gamma.npy"), + 0.001f) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::BOUNDED_RELU, 6.f)); + + return BranchLayer(std::move(sg)); +} +} // namespace + +/** Example demonstrating how to implement MobileNet's network using the Compute Library's graph API + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +void main_graph_mobilenet(int argc, const char **argv) +{ + std::string data_path; /* Path to the trainable data */ + std::string image; /* Image data */ + std::string label; /* Label data */ + + constexpr float mean_r = 122.68f; /* Mean value to subtract from red channel */ + constexpr float mean_g = 116.67f; /* Mean value to subtract from green channel */ + constexpr float mean_b = 104.01f; /* Mean value to subtract from blue channel */ + + // Set target. 0 (NEON), 1 (OpenCL). By default it is NEON + TargetHint target_hint = set_target_hint(argc > 1 ? std::strtol(argv[1], nullptr, 10) : 0); + + // Parse arguments + if(argc < 2) + { + // Print help + std::cout << "Usage: " << argv[0] << " [target] [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 2) + { + std::cout << "Usage: " << argv[0] << " " << argv[1] << " [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 3) + { + data_path = argv[2]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " [image] [labels]\n\n"; + std::cout << "No image provided: using random values\n\n"; + } + else if(argc == 4) + { + data_path = argv[2]; + image = argv[3]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " " << argv[3] << " [labels]\n\n"; + std::cout << "No text file with labels provided: skipping output accessor\n\n"; + } + else + { + data_path = argv[2]; + image = argv[3]; + label = argv[4]; + } + + Graph graph; + + graph << target_hint + << Tensor(TensorInfo(TensorShape(224U, 224U, 3U, 1U), 1, DataType::F32), + get_input_accessor(image, mean_r, mean_g, mean_b)) + << ConvolutionLayer( + 3U, 3U, 32U, + get_weights_accessor(data_path, "/cnn_data/mobilenet_v1_model/Conv2d_0_weights.npy"), + std::unique_ptr<arm_compute::graph::ITensorAccessor>(nullptr), + PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::FLOOR)) + << BatchNormalizationLayer( + get_weights_accessor(data_path, "/cnn_data/mobilenet_v1_model/Conv2d_0_BatchNorm_moving_mean.npy"), + get_weights_accessor(data_path, "/cnn_data/mobilenet_v1_model/Conv2d_0_BatchNorm_moving_variance.npy"), + get_weights_accessor(data_path, "/cnn_data/mobilenet_v1_model/Conv2d_0_BatchNorm_beta.npy"), + get_weights_accessor(data_path, "/cnn_data/mobilenet_v1_model/Conv2d_0_BatchNorm_gamma.npy"), + 0.001f) + + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::BOUNDED_RELU, 6.f)) + << get_dwsc_node(data_path, "Conv2d_1", 64, PadStrideInfo(1, 1, 1, 1), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_2", 128, PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_3", 128, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_4", 256, PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_5", 256, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_6", 512, PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_7", 512, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_8", 512, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_9", 512, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_10", 512, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_11", 512, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_12", 1024, PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << get_dwsc_node(data_path, "Conv2d_13", 1024, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::FLOOR), PadStrideInfo(1, 1, 0, 0)) + << PoolingLayer(PoolingLayerInfo(PoolingType::AVG)) + << ConvolutionLayer( + 1U, 1U, 1001U, + get_weights_accessor(data_path, "/cnn_data/mobilenet_v1_model/Logits_Conv2d_1c_1x1_weights.npy"), + get_weights_accessor(data_path, "/cnn_data/mobilenet_v1_model/Logits_Conv2d_1c_1x1_biases.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ReshapeLayer(TensorShape(1001U)) + << SoftmaxLayer() + << Tensor(get_output_accessor(label, 5)); + + graph.run(); +} + +/** Main program for MobileNetV1 + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +int main(int argc, const char **argv) +{ + return arm_compute::utils::run_example(argc, argv, main_graph_mobilenet); +} diff --git a/examples/graph_squeezenet.cpp b/examples/graph_squeezenet.cpp new file mode 100644 index 000000000..c8c411aa8 --- /dev/null +++ b/examples/graph_squeezenet.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Nodes.h" +#include "arm_compute/graph/SubGraph.h" +#include "support/ToolchainSupport.h" +#include "utils/GraphUtils.h" +#include "utils/Utils.h" + +#include <cstdlib> +#include <tuple> + +using namespace arm_compute::graph; +using namespace arm_compute::graph_utils; +using namespace arm_compute::logging; + +namespace +{ +BranchLayer get_expand_fire_node(const std::string &data_path, std::string &¶m_path, unsigned int expand1_filt, unsigned int expand3_filt) +{ + std::string total_path = "/cnn_data/squeezenet_v1.0_model/" + param_path + "_"; + SubGraph i_a; + i_a << ConvolutionLayer( + 1U, 1U, expand1_filt, + get_weights_accessor(data_path, total_path + "expand1x1_w.npy"), + get_weights_accessor(data_path, total_path + "expand1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)); + + SubGraph i_b; + i_b << ConvolutionLayer( + 3U, 3U, expand3_filt, + get_weights_accessor(data_path, total_path + "expand3x3_w.npy"), + get_weights_accessor(data_path, total_path + "expand3x3_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)); + + return BranchLayer(BranchMergeMethod::DEPTH_CONCATENATE, std::move(i_a), std::move(i_b)); +} +} // namespace + +/** Example demonstrating how to implement Squeezenet's network using the Compute Library's graph API + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +void main_graph_squeezenet(int argc, const char **argv) +{ + std::string data_path; /* Path to the trainable data */ + std::string image; /* Image data */ + std::string label; /* Label data */ + + constexpr float mean_r = 122.68f; /* Mean value to subtract from red channel */ + constexpr float mean_g = 116.67f; /* Mean value to subtract from green channel */ + constexpr float mean_b = 104.01f; /* Mean value to subtract from blue channel */ + + // Set target. 0 (NEON), 1 (OpenCL). By default it is NEON + TargetHint target_hint = set_target_hint(argc > 1 ? std::strtol(argv[1], nullptr, 10) : 0); + ConvolutionMethodHint convolution_hint = target_hint == TargetHint::NEON ? ConvolutionMethodHint::GEMM : ConvolutionMethodHint::DIRECT; + + // Parse arguments + if(argc < 2) + { + // Print help + std::cout << "Usage: " << argv[0] << " [target] [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 2) + { + std::cout << "Usage: " << argv[0] << " " << argv[1] << " [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 3) + { + data_path = argv[2]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " [image] [labels]\n\n"; + std::cout << "No image provided: using random values\n\n"; + } + else if(argc == 4) + { + data_path = argv[2]; + image = argv[3]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " " << argv[3] << " [labels]\n\n"; + std::cout << "No text file with labels provided: skipping output accessor\n\n"; + } + else + { + data_path = argv[2]; + image = argv[3]; + label = argv[4]; + } + + Graph graph; + + graph << target_hint + << Tensor(TensorInfo(TensorShape(224U, 224U, 3U, 1U), 1, DataType::F32), + get_input_accessor(image, mean_r, mean_g, mean_b)) + << ConvolutionLayer( + 7U, 7U, 96U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/conv1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/conv1_b.npy"), + PadStrideInfo(2, 2, 0, 0)) + << convolution_hint + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(2, 2, 0, 0, DimensionRoundingType::CEIL))) + << ConvolutionLayer( + 1U, 1U, 16U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire2_squeeze1x1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire2_squeeze1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << get_expand_fire_node(data_path, "fire2", 64U, 64U) + << ConvolutionLayer( + 1U, 1U, 16U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire3_squeeze1x1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire3_squeeze1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << get_expand_fire_node(data_path, "fire3", 64U, 64U) + << ConvolutionLayer( + 1U, 1U, 32U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire4_squeeze1x1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire4_squeeze1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << get_expand_fire_node(data_path, "fire4", 128U, 128U) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(2, 2, 0, 0, DimensionRoundingType::CEIL))) + << ConvolutionLayer( + 1U, 1U, 32U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire5_squeeze1x1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire5_squeeze1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << get_expand_fire_node(data_path, "fire5", 128U, 128U) + << ConvolutionLayer( + 1U, 1U, 48U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire6_squeeze1x1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire6_squeeze1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << get_expand_fire_node(data_path, "fire6", 192U, 192U) + << ConvolutionLayer( + 1U, 1U, 48U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire7_squeeze1x1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire7_squeeze1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << get_expand_fire_node(data_path, "fire7", 192U, 192U) + << ConvolutionLayer( + 1U, 1U, 64U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire8_squeeze1x1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire8_squeeze1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << get_expand_fire_node(data_path, "fire8", 256U, 256U) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 3, PadStrideInfo(2, 2, 0, 0, DimensionRoundingType::CEIL))) + << ConvolutionLayer( + 1U, 1U, 64U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire9_squeeze1x1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/fire9_squeeze1x1_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << get_expand_fire_node(data_path, "fire9", 256U, 256U) + << ConvolutionLayer( + 1U, 1U, 1000U, + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/conv10_w.npy"), + get_weights_accessor(data_path, "/cnn_data/squeezenet_v1.0_model/conv10_b.npy"), + PadStrideInfo(1, 1, 0, 0)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::AVG)) + << FlattenLayer() + << SoftmaxLayer() + << Tensor(get_output_accessor(label, 5)); + + graph.run(); +} + +/** Main program for Squeezenet v1.0 + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +int main(int argc, const char **argv) +{ + return arm_compute::utils::run_example(argc, argv, main_graph_squeezenet); +}
\ No newline at end of file diff --git a/examples/graph_vgg16.cpp b/examples/graph_vgg16.cpp new file mode 100644 index 000000000..cac38d30a --- /dev/null +++ b/examples/graph_vgg16.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Nodes.h" +#include "support/ToolchainSupport.h" +#include "utils/GraphUtils.h" +#include "utils/Utils.h" + +#include <cstdlib> + +using namespace arm_compute::graph; +using namespace arm_compute::graph_utils; + +/** Example demonstrating how to implement VGG16's network using the Compute Library's graph API + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +void main_graph_vgg16(int argc, const char **argv) +{ + std::string data_path; /* Path to the trainable data */ + std::string image; /* Image data */ + std::string label; /* Label data */ + + constexpr float mean_r = 123.68f; /* Mean value to subtract from red channel */ + constexpr float mean_g = 116.779f; /* Mean value to subtract from green channel */ + constexpr float mean_b = 103.939f; /* Mean value to subtract from blue channel */ + + // Set target. 0 (NEON), 1 (OpenCL). By default it is NEON + TargetHint target_hint = set_target_hint(argc > 1 ? std::strtol(argv[1], nullptr, 10) : 0); + ConvolutionMethodHint convolution_hint = ConvolutionMethodHint::DIRECT; + + // Parse arguments + if(argc < 2) + { + // Print help + std::cout << "Usage: " << argv[0] << " [target] [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 2) + { + std::cout << "Usage: " << argv[0] << " " << argv[1] << " [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 3) + { + data_path = argv[2]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " [image] [labels]\n\n"; + std::cout << "No image provided: using random values\n\n"; + } + else if(argc == 4) + { + data_path = argv[2]; + image = argv[3]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " " << argv[3] << " [labels]\n\n"; + std::cout << "No text file with labels provided: skipping output accessor\n\n"; + } + else + { + data_path = argv[2]; + image = argv[3]; + label = argv[4]; + } + + Graph graph; + + graph << target_hint + << convolution_hint + << Tensor(TensorInfo(TensorShape(224U, 224U, 3U, 1U), 1, DataType::F32), + get_input_accessor(image, mean_r, mean_g, mean_b)) + << ConvolutionMethodHint::DIRECT + // Layer 1 + << ConvolutionLayer( + 3U, 3U, 64U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv1_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv1_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 2 + << ConvolutionLayer( + 3U, 3U, 64U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv1_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv1_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 3 + << ConvolutionLayer( + 3U, 3U, 128U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv2_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv2_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 4 + << ConvolutionLayer( + 3U, 3U, 128U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv2_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv2_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 5 + << ConvolutionLayer( + 3U, 3U, 256U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv3_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv3_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 6 + << ConvolutionLayer( + 3U, 3U, 256U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv3_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv3_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 7 + << ConvolutionLayer( + 3U, 3U, 256U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv3_3_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv3_3_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 8 + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv4_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv4_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 9 + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv4_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv4_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 10 + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv4_3_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv4_3_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 11 + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv5_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv5_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 12 + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv5_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv5_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 13 + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv5_3_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/conv5_3_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 14 + << FullyConnectedLayer( + 4096U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/fc6_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/fc6_b.npy")) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 15 + << FullyConnectedLayer( + 4096U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/fc7_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/fc7_b.npy")) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 16 + << FullyConnectedLayer( + 1000U, + get_weights_accessor(data_path, "/cnn_data/vgg16_model/fc8_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg16_model/fc8_b.npy")) + // Softmax + << SoftmaxLayer() + << Tensor(get_output_accessor(label, 5)); + + // Run graph + graph.run(); +} + +/** Main program for VGG16 + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +int main(int argc, const char **argv) +{ + return arm_compute::utils::run_example(argc, argv, main_graph_vgg16); +} diff --git a/examples/graph_vgg19.cpp b/examples/graph_vgg19.cpp new file mode 100644 index 000000000..49ae0fe51 --- /dev/null +++ b/examples/graph_vgg19.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/graph/Graph.h" +#include "arm_compute/graph/Nodes.h" +#include "support/ToolchainSupport.h" +#include "utils/GraphUtils.h" +#include "utils/Utils.h" + +#include <cstdlib> + +using namespace arm_compute::graph; +using namespace arm_compute::graph_utils; + +/** Example demonstrating how to implement VGG19's network using the Compute Library's graph API + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +void main_graph_vgg19(int argc, const char **argv) +{ + std::string data_path; /* Path to the trainable data */ + std::string image; /* Image data */ + std::string label; /* Label data */ + + constexpr float mean_r = 123.68f; /* Mean value to subtract from red channel */ + constexpr float mean_g = 116.779f; /* Mean value to subtract from green channel */ + constexpr float mean_b = 103.939f; /* Mean value to subtract from blue channel */ + + // Set target. 0 (NEON), 1 (OpenCL). By default it is NEON + TargetHint target_hint = set_target_hint(argc > 1 ? std::strtol(argv[1], nullptr, 10) : 0); + ConvolutionMethodHint convolution_hint = ConvolutionMethodHint::DIRECT; + + // Parse arguments + if(argc < 2) + { + // Print help + std::cout << "Usage: " << argv[0] << " [target] [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 2) + { + std::cout << "Usage: " << argv[0] << " " << argv[1] << " [path_to_data] [image] [labels]\n\n"; + std::cout << "No data folder provided: using random values\n\n"; + } + else if(argc == 3) + { + data_path = argv[2]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " [image] [labels]\n\n"; + std::cout << "No image provided: using random values\n\n"; + } + else if(argc == 4) + { + data_path = argv[2]; + image = argv[3]; + std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " " << argv[3] << " [labels]\n\n"; + std::cout << "No text file with labels provided: skipping output accessor\n\n"; + } + else + { + data_path = argv[2]; + image = argv[3]; + label = argv[4]; + } + + Graph graph; + + graph << target_hint + << convolution_hint + << Tensor(TensorInfo(TensorShape(224U, 224U, 3U, 1U), 1, DataType::F32), + get_input_accessor(image, mean_r, mean_g, mean_b)) + // Layer 1 + << ConvolutionLayer( + 3U, 3U, 64U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv1_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv1_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 64U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv1_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv1_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 2 + << ConvolutionLayer( + 3U, 3U, 128U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv2_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv2_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 128U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv2_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv2_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 3 + << ConvolutionLayer( + 3U, 3U, 256U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv3_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv3_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 256U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv3_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv3_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 256U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv3_3_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv3_3_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 256U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv3_4_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv3_4_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 4 + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv4_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv4_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv4_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv4_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv4_3_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv4_3_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv4_4_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv4_4_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 5 + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv5_1_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv5_1_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv5_2_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv5_2_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv5_3_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv5_3_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << ConvolutionLayer( + 3U, 3U, 512U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv5_4_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/conv5_4_b.npy"), + PadStrideInfo(1, 1, 1, 1)) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + << PoolingLayer(PoolingLayerInfo(PoolingType::MAX, 2, PadStrideInfo(2, 2, 0, 0))) + // Layer 6 + << FullyConnectedLayer( + 4096U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/fc6_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/fc6_b.npy")) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 7 + << FullyConnectedLayer( + 4096U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/fc7_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/fc7_b.npy")) + << ActivationLayer(ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::RELU)) + // Layer 8 + << FullyConnectedLayer( + 1000U, + get_weights_accessor(data_path, "/cnn_data/vgg19_model/fc8_w.npy"), + get_weights_accessor(data_path, "/cnn_data/vgg19_model/fc8_b.npy")) + // Softmax + << SoftmaxLayer() + << Tensor(get_output_accessor(label, 5)); + + // Run graph + graph.run(); +} + +/** Main program for VGG19 + * + * @param[in] argc Number of arguments + * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL), [optional] Path to the weights folder, [optional] image, [optional] labels ) + */ +int main(int argc, const char **argv) +{ + return arm_compute::utils::run_example(argc, argv, main_graph_vgg19); +} |