summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorAnthony Barbier <Anthony.barbier@arm.com>2017-12-14 23:48:46 +0000
committerAnthony Barbier <anthony.barbier@arm.com>2018-01-24 10:01:21 +0000
commit8140e1e155d3430992fa46e04ef8938ff09ffd2d (patch)
tree9bcf86d01635bfc73e8debd1bda75e6f75b8b406 /examples
parent8a3da6f91f90c566b844d568f4ec43b946915af8 (diff)
downloadarmcl-8140e1e155d3430992fa46e04ef8938ff09ffd2d.tar.gz
armcl-8140e1e155d3430992fa46e04ef8938ff09ffd2d.tar.bz2
armcl-8140e1e155d3430992fa46e04ef8938ff09ffd2d.zip
arm_compute v17.12
Diffstat (limited to 'examples')
-rw-r--r--examples/SConscript31
-rw-r--r--examples/cl_sgemm.cpp216
-rw-r--r--examples/gc_absdiff.cpp112
-rw-r--r--examples/graph_alexnet.cpp152
-rw-r--r--examples/graph_googlenet.cpp204
-rw-r--r--examples/graph_lenet.cpp43
-rw-r--r--examples/graph_mobilenet.cpp175
-rw-r--r--examples/graph_squeezenet.cpp207
-rw-r--r--examples/graph_vgg16.cpp222
-rw-r--r--examples/graph_vgg19.cpp231
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 &&param_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 &&param_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 &&param_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);
+}