arm_compute v18.02
[platform/upstream/armcl.git] / examples / graph_mobilenet.cpp
1 /*
2  * Copyright (c) 2017-2018 ARM Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include "arm_compute/graph/Graph.h"
25 #include "arm_compute/graph/Nodes.h"
26 #include "support/ToolchainSupport.h"
27 #include "utils/GraphUtils.h"
28 #include "utils/Utils.h"
29
30 #include <cstdlib>
31
32 using namespace arm_compute::utils;
33 using namespace arm_compute::graph;
34 using namespace arm_compute::graph_utils;
35
36 /** Example demonstrating how to implement MobileNet's network using the Compute Library's graph API
37  *
38  * @param[in] argc Number of arguments
39  * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL, 2 = OpenCL with Tuner), [optional] Path to the weights folder, [optional] image, [optional] labels )
40  */
41 class GraphMobilenetExample : public Example
42 {
43 public:
44     void do_setup(int argc, char **argv) override
45     {
46         std::string data_path; /* Path to the trainable data */
47         std::string image;     /* Image data */
48         std::string label;     /* Label data */
49
50         // Create a preprocessor object
51         std::unique_ptr<IPreprocessor> preprocessor = arm_compute::support::cpp14::make_unique<TFPreproccessor>();
52
53         // Set target. 0 (NEON), 1 (OpenCL), 2 (OpenCL with Tuner). By default it is NEON
54         const int             int_target_hint  = argc > 1 ? std::strtol(argv[1], nullptr, 10) : 0;
55         TargetHint            target_hint      = set_target_hint(int_target_hint);
56         ConvolutionMethodHint convolution_hint = ConvolutionMethodHint::GEMM;
57
58         // Set model to execute. 0 (MobileNetV1_1.0_224), 1 (MobileNetV1_0.75_160)
59         int model_id = (argc > 2) ? std::strtol(argv[2], nullptr, 10) : 0;
60         ARM_COMPUTE_ERROR_ON_MSG(model_id > 1, "Invalid model ID. Model must be 0 (MobileNetV1_1.0_224) or 1 (MobileNetV1_0.75_160)");
61         float        depth_scale  = (model_id == 0) ? 1.f : 0.75;
62         unsigned int spatial_size = (model_id == 0) ? 224 : 160;
63         std::string  model_path   = (model_id == 0) ? "/cnn_data/mobilenet_v1_1_224_model/" : "/cnn_data/mobilenet_v1_075_160_model/";
64
65         // Parse arguments
66         if(argc < 2)
67         {
68             // Print help
69             std::cout << "Usage: " << argv[0] << " [target] [model] [path_to_data] [image] [labels]\n\n";
70             std::cout << "No model ID provided: using MobileNetV1_1.0_224\n\n";
71             std::cout << "No data folder provided: using random values\n\n";
72         }
73         else if(argc == 2)
74         {
75             std::cout << "Usage: " << argv[0] << " " << argv[1] << " [model] [path_to_data] [image] [labels]\n\n";
76             std::cout << "No model ID provided: using MobileNetV1_1.0_224\n\n";
77             std::cout << "No data folder provided: using random values\n\n";
78         }
79         else if(argc == 3)
80         {
81             std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " [path_to_data] [image] [labels]\n\n";
82             std::cout << "No data folder provided: using random values\n\n";
83         }
84         else if(argc == 4)
85         {
86             data_path = argv[3];
87             std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " " << argv[3] << " [image] [labels]\n\n";
88             std::cout << "No image provided: using random values\n\n";
89         }
90         else if(argc == 5)
91         {
92             data_path = argv[3];
93             image     = argv[4];
94             std::cout << "Usage: " << argv[0] << " " << argv[1] << " " << argv[2] << " " << argv[3] << " [labels]\n\n";
95             std::cout << "No text file with labels provided: skipping output accessor\n\n";
96         }
97         else
98         {
99             data_path = argv[3];
100             image     = argv[4];
101             label     = argv[5];
102         }
103
104         // Add model path to data path
105         if(!data_path.empty())
106         {
107             data_path += model_path;
108         }
109
110         graph << target_hint
111               << convolution_hint
112               << Tensor(TensorInfo(TensorShape(spatial_size, spatial_size, 3U, 1U), 1, DataType::F32),
113                         get_input_accessor(image, std::move(preprocessor), false))
114               << ConvolutionLayer(
115                   3U, 3U, 32U * depth_scale,
116                   get_weights_accessor(data_path, "Conv2d_0_weights.npy"),
117                   std::unique_ptr<arm_compute::graph::ITensorAccessor>(nullptr),
118                   PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::FLOOR))
119               << BatchNormalizationLayer(
120                   get_weights_accessor(data_path, "Conv2d_0_BatchNorm_moving_mean.npy"),
121                   get_weights_accessor(data_path, "Conv2d_0_BatchNorm_moving_variance.npy"),
122                   get_weights_accessor(data_path, "Conv2d_0_BatchNorm_gamma.npy"),
123                   get_weights_accessor(data_path, "Conv2d_0_BatchNorm_beta.npy"),
124                   0.001f, ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::BOUNDED_RELU, 6.f))
125               << get_dwsc_node(data_path, "Conv2d_1", 64 * depth_scale, PadStrideInfo(1, 1, 1, 1), PadStrideInfo(1, 1, 0, 0))
126               << get_dwsc_node(data_path, "Conv2d_2", 128 * depth_scale, PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
127               << get_dwsc_node(data_path, "Conv2d_3", 128 * depth_scale, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
128               << get_dwsc_node(data_path, "Conv2d_4", 256 * depth_scale, PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
129               << get_dwsc_node(data_path, "Conv2d_5", 256 * depth_scale, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
130               << get_dwsc_node(data_path, "Conv2d_6", 512 * depth_scale, PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
131               << get_dwsc_node(data_path, "Conv2d_7", 512 * depth_scale, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
132               << get_dwsc_node(data_path, "Conv2d_8", 512 * depth_scale, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
133               << get_dwsc_node(data_path, "Conv2d_9", 512 * depth_scale, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
134               << get_dwsc_node(data_path, "Conv2d_10", 512 * depth_scale, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
135               << get_dwsc_node(data_path, "Conv2d_11", 512 * depth_scale, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
136               << get_dwsc_node(data_path, "Conv2d_12", 1024 * depth_scale, PadStrideInfo(2, 2, 0, 1, 0, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
137               << get_dwsc_node(data_path, "Conv2d_13", 1024 * depth_scale, PadStrideInfo(1, 1, 1, 1, 1, 1, DimensionRoundingType::CEIL), PadStrideInfo(1, 1, 0, 0))
138               << PoolingLayer(PoolingLayerInfo(PoolingType::AVG))
139               << ConvolutionLayer(
140                   1U, 1U, 1001U,
141                   get_weights_accessor(data_path, "Logits_Conv2d_1c_1x1_weights.npy"),
142                   get_weights_accessor(data_path, "Logits_Conv2d_1c_1x1_biases.npy"),
143                   PadStrideInfo(1, 1, 0, 0))
144               << ReshapeLayer(TensorShape(1001U))
145               << SoftmaxLayer()
146               << Tensor(get_output_accessor(label, 5));
147
148         // In order to enable the OpenCL tuner, graph_init() has to be called only when all nodes have been instantiated
149         graph.graph_init(int_target_hint == 2);
150     }
151     void do_run() override
152     {
153         // Run graph
154         graph.run();
155     }
156
157 private:
158     Graph graph{};
159
160     BranchLayer get_dwsc_node(const std::string &data_path, std::string &&param_path,
161                               unsigned int  conv_filt,
162                               PadStrideInfo dwc_pad_stride_info, PadStrideInfo conv_pad_stride_info)
163     {
164         std::string total_path = param_path + "_";
165         SubGraph    sg;
166         sg << DepthwiseConvolutionLayer(
167                3U, 3U,
168                get_weights_accessor(data_path, total_path + "depthwise_depthwise_weights.npy"),
169                std::unique_ptr<arm_compute::graph::ITensorAccessor>(nullptr),
170                dwc_pad_stride_info,
171                true)
172            << BatchNormalizationLayer(
173                get_weights_accessor(data_path, total_path + "depthwise_BatchNorm_moving_mean.npy"),
174                get_weights_accessor(data_path, total_path + "depthwise_BatchNorm_moving_variance.npy"),
175                get_weights_accessor(data_path, total_path + "depthwise_BatchNorm_gamma.npy"),
176                get_weights_accessor(data_path, total_path + "depthwise_BatchNorm_beta.npy"),
177                0.001f, ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::BOUNDED_RELU, 6.f))
178            << ConvolutionLayer(
179                1U, 1U, conv_filt,
180                get_weights_accessor(data_path, total_path + "pointwise_weights.npy"),
181                std::unique_ptr<arm_compute::graph::ITensorAccessor>(nullptr),
182                conv_pad_stride_info)
183            << BatchNormalizationLayer(
184                get_weights_accessor(data_path, total_path + "pointwise_BatchNorm_moving_mean.npy"),
185                get_weights_accessor(data_path, total_path + "pointwise_BatchNorm_moving_variance.npy"),
186                get_weights_accessor(data_path, total_path + "pointwise_BatchNorm_gamma.npy"),
187                get_weights_accessor(data_path, total_path + "pointwise_BatchNorm_beta.npy"),
188                0.001f, ActivationLayerInfo(ActivationLayerInfo::ActivationFunction::BOUNDED_RELU, 6.f));
189
190         return BranchLayer(std::move(sg));
191     }
192 };
193
194 /** Main program for MobileNetV1
195  *
196  * @param[in] argc Number of arguments
197  * @param[in] argv Arguments ( [optional] Target (0 = NEON, 1 = OpenCL),
198  *                             [optional] Model ID (0 = MobileNetV1_1.0_224, 1 = MobileNetV1_0.75_160),
199  *                             [optional] Path to the weights folder,
200  *                             [optional] image,
201  *                             [optional] labels )
202  */
203 int main(int argc, char **argv)
204 {
205     return arm_compute::utils::run_example<GraphMobilenetExample>(argc, argv);
206 }