aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSheri Zhang <sheri.zhang@arm.com>2020-05-27 15:03:48 +0100
committerSheri Zhang <sheri.zhang@arm.com>2020-06-16 13:12:48 +0000
commit16dddd2af57a71ca10d62a4412d014f859720d2c (patch)
tree6478e50fc7b72775f781562508bfaf87e6d935a9
parentbcd2352d7fd99a2f6aab220fa0c3b3f3119a1a4c (diff)
downloadComputeLibrary-16dddd2af57a71ca10d62a4412d014f859720d2c.tar.gz
COMPMID-3381: Implement graph example for YoLo v3 output detector
Add sub/exp/splitv support in graph api Signed-off-by: Sheri Zhang <sheri.zhang@arm.com> Change-Id: I4e08cc19a46655717068b12c93d67e619a595d9a Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/3309 Reviewed-by: Georgios Pinitas <georgios.pinitas@arm.com> Tested-by: Arm Jenkins <bsgcomp@arm.com> Comments-Addressed: Arm Jenkins <bsgcomp@arm.com>
-rw-r--r--arm_compute/graph/LayerDescriptors.h25
-rw-r--r--arm_compute/graph/TypePrinter.h3
-rw-r--r--arm_compute/graph/Types.h9
-rw-r--r--arm_compute/graph/backends/FunctionHelpers.h48
-rw-r--r--arm_compute/graph/backends/ValidateHelpers.h73
-rw-r--r--arm_compute/graph/nodes/EltwiseLayerNode.h40
-rw-r--r--arm_compute/graph/nodes/SplitLayerNode.h20
-rw-r--r--examples/graph_yolov3_output_detector.cpp613
-rw-r--r--src/graph/backends/CL/CLFunctionsFactory.cpp8
-rw-r--r--src/graph/backends/CL/CLNodeValidator.cpp18
-rw-r--r--src/graph/backends/NEON/NEFunctionFactory.cpp12
-rw-r--r--src/graph/backends/NEON/NENodeValidator.cpp18
-rw-r--r--src/graph/mutators/NodeFusionMutator.cpp6
-rw-r--r--src/graph/mutators/SplitLayerSubTensorMutator.cpp6
-rw-r--r--src/graph/nodes/EltwiseLayerNode.cpp62
-rw-r--r--src/graph/nodes/SplitLayerNode.cpp71
16 files changed, 1003 insertions, 29 deletions
diff --git a/arm_compute/graph/LayerDescriptors.h b/arm_compute/graph/LayerDescriptors.h
index d8e6a6a87b..ba53622298 100644
--- a/arm_compute/graph/LayerDescriptors.h
+++ b/arm_compute/graph/LayerDescriptors.h
@@ -89,6 +89,31 @@ struct EltwiseLayerDescriptor
ActivationLayerInfo fused_activation; /**< Fused activation info */
};
+/** Unary Elementwise layer descriptor */
+struct UnaryEltwiseLayerDescriptor
+{
+ /** Constructor
+ *
+ * @param[in] op Unary element-wise operation to perform
+ * @param[in] out_quant_info (Optional) Output quantization information. Defaults to empty @ref QuantizationInfo
+ * @param[in] c_policy (Optional) Convert policy used for the operation. Defaults to @ref ConvertPolicy::SATURATE
+ * @param[in] r_policy (Optional) Rounding policy used for the operation. Defaults to @ref RoundingPolicy::TO_ZERO
+ * @param[in] fused_activation (Optional) Fused activation information. Defaults to empty (identity) @ref ActivationLayerInfo
+ */
+ UnaryEltwiseLayerDescriptor(UnaryEltwiseOperation op, QuantizationInfo out_quant_info = QuantizationInfo(), ConvertPolicy c_policy = ConvertPolicy::SATURATE,
+ RoundingPolicy r_policy = RoundingPolicy::TO_ZERO,
+ ActivationLayerInfo fused_activation = ActivationLayerInfo())
+ : op(op), out_quant_info(out_quant_info), c_policy(c_policy), r_policy(r_policy), fused_activation(fused_activation)
+ {
+ }
+
+ UnaryEltwiseOperation op; /**< Unary element-wise operation to perform */
+ QuantizationInfo out_quant_info; /**< Output quantization information */
+ ConvertPolicy c_policy; /**< Convert policy */
+ RoundingPolicy r_policy; /**< Rounding policy */
+ ActivationLayerInfo fused_activation; /**< Fused activation info */
+};
+
/** Deconvolution layer descriptor */
struct DeconvolutionLayerDescriptor
{
diff --git a/arm_compute/graph/TypePrinter.h b/arm_compute/graph/TypePrinter.h
index d56407b52b..dea055a83b 100644
--- a/arm_compute/graph/TypePrinter.h
+++ b/arm_compute/graph/TypePrinter.h
@@ -98,6 +98,9 @@ inline ::std::ostream &operator<<(::std::ostream &os, const NodeType &node_type)
case NodeType::EltwiseLayer:
os << "EltwiseLayer";
break;
+ case NodeType::UnaryEltwiseLayer:
+ os << "UnaryEltwiseLayer";
+ break;
case NodeType::FlattenLayer:
os << "FlattenLayer";
break;
diff --git a/arm_compute/graph/Types.h b/arm_compute/graph/Types.h
index 296f757c9b..422069238d 100644
--- a/arm_compute/graph/Types.h
+++ b/arm_compute/graph/Types.h
@@ -103,7 +103,13 @@ enum class EltwiseOperation
{
Add, /**< Arithmetic addition */
Sub, /**< Arithmetic subtraction */
- Mul /**< Arithmetic multiplication */
+ Mul, /**< Arithmetic multiplication */
+};
+
+/** Supported Unary Element-wise operations */
+enum class UnaryEltwiseOperation
+{
+ Exp /**< Exp */
};
/** Supported Convolution layer methods */
@@ -168,6 +174,7 @@ enum class NodeType
SplitLayer,
StackLayer,
UpsampleLayer,
+ UnaryEltwiseLayer,
YOLOLayer,
Input,
diff --git a/arm_compute/graph/backends/FunctionHelpers.h b/arm_compute/graph/backends/FunctionHelpers.h
index 975e5fe55e..e21b8ed288 100644
--- a/arm_compute/graph/backends/FunctionHelpers.h
+++ b/arm_compute/graph/backends/FunctionHelpers.h
@@ -816,6 +816,54 @@ std::unique_ptr<IFunction> create_eltwise_layer(EltwiseLayerNode &node)
return RETURN_UNIQUE_PTR(func);
}
+/** Create a backend unary element-wise operation layer function
+ *
+ * @tparam UnaryEltwiseFunctions Backend unary element-wise function
+ * @tparam TargetInfo Target-specific information
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend unary element-wise operation layer function
+ */
+template <typename UnaryEltwiseFunctions, typename TargetInfo>
+std::unique_ptr<IFunction> create_unary_eltwise_layer(UnaryEltwiseLayerNode &node)
+{
+ validate_node<TargetInfo>(node, 1 /* expected inputs */, 1 /* expected outputs */);
+
+ // Extract IO and info
+ typename TargetInfo::TensorType *input = get_backing_tensor<TargetInfo>(node.input(0));
+ typename TargetInfo::TensorType *output = get_backing_tensor<TargetInfo>(node.output(0));
+ const UnaryEltwiseOperation eltwise_op = node.eltwise_descriptor().op;
+
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ std::unique_ptr<IFunction> func = nullptr;
+ std::string func_name;
+ if(eltwise_op == UnaryEltwiseOperation::Exp)
+ {
+ std::tie(func, func_name) = create_named_function<typename UnaryEltwiseFunctions::Exp>(
+ std::string("Exp"),
+ input, output);
+ }
+ else
+ {
+ ARM_COMPUTE_ERROR("Unsupported unary element-wise operation!");
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated "
+ << node.name()
+ << " Type: " << node.type()
+ << " Target: " << TargetInfo::TargetType
+ << " Operation: " << func_name
+ << " Data Type: " << input->info()->data_type()
+ << " Shape: " << input->info()->tensor_shape()
+ << std::endl);
+
+ return RETURN_UNIQUE_PTR(func);
+}
+
/** Create a backend flatten layer function
*
* @tparam FlattenLayerFunction Backend flatten function
diff --git a/arm_compute/graph/backends/ValidateHelpers.h b/arm_compute/graph/backends/ValidateHelpers.h
index 673caf9eac..d689c1f89d 100644
--- a/arm_compute/graph/backends/ValidateHelpers.h
+++ b/arm_compute/graph/backends/ValidateHelpers.h
@@ -579,6 +579,79 @@ Status validate_yolo_layer(YOLOLayerNode &node)
// Validate function
return YOLOLayer::validate(input, output, node.activation_info(), node.num_classes());
}
+/** Validates a element-wise layer node
+ *
+ * @param[in] node Node to validate
+ *
+ * @return Status
+ */
+template <typename EltwiseLayerFunctions>
+Status validate_eltwise_Layer(EltwiseLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_RETURN_ERROR_ON(node.num_inputs() != 2);
+ ARM_COMPUTE_RETURN_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract input and output
+ const arm_compute::ITensorInfo *input1 = detail::get_backing_tensor_info(node.input(0));
+ const arm_compute::ITensorInfo *input2 = detail::get_backing_tensor_info(node.input(1));
+ const arm_compute::ITensorInfo *output = get_backing_tensor_info(node.output(0));
+ const EltwiseOperation eltwise_op = node.eltwise_operation();
+ const ConvertPolicy convert_policy = node.convert_policy();
+ const RoundingPolicy round_policy = node.rounding_policy();
+ const ActivationLayerInfo act_info = node.fused_activation();
+ const QuantizationInfo quant_info = node.output_quant_info();
+ const float scale = (quant_info.scale().empty()) ? 1.0f : quant_info.scale()[0];
+
+ // Validate function
+ if(eltwise_op == EltwiseOperation::Add)
+ {
+ return EltwiseLayerFunctions::ArithmeticAddition::validate(input1, input2, output, convert_policy, act_info);
+ }
+ else if(eltwise_op == EltwiseOperation::Sub)
+ {
+ return EltwiseLayerFunctions::ArithmeticSubtraction::validate(input1, input2, output, convert_policy, act_info);
+ }
+ else if(eltwise_op == EltwiseOperation::Mul)
+ {
+ return EltwiseLayerFunctions::PixelWiseMultiplication::validate(input1, input2, output, scale, convert_policy, round_policy, act_info);
+ }
+ else
+ {
+ ARM_COMPUTE_ERROR("Unsupported element-wise operation!");
+ }
+ return Status{};
+}
+/** Validates a unary element-wise layer node
+ *
+ * @param[in] node Node to validate
+ *
+ * @return Status
+ */
+template <typename UnaryEltwiseLayerFunctions>
+Status validate_unary_eltwise_layer(UnaryEltwiseLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_RETURN_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_RETURN_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract input and output
+ arm_compute::ITensorInfo *input = detail::get_backing_tensor_info(node.input(0));
+ arm_compute::ITensorInfo *output = get_backing_tensor_info(node.output(0));
+ const UnaryEltwiseOperation eltwise_op = node.eltwise_descriptor().op;
+
+ // Validate function
+ if(eltwise_op == UnaryEltwiseOperation::Exp)
+ {
+ return UnaryEltwiseLayerFunctions::ExpLayer::validate(input, output);
+ }
+ else
+ {
+ ARM_COMPUTE_ERROR("Unsupported unary element-wise operation!");
+ }
+
+ return Status{};
+}
} // namespace detail
} // namespace backends
} // namespace graph
diff --git a/arm_compute/graph/nodes/EltwiseLayerNode.h b/arm_compute/graph/nodes/EltwiseLayerNode.h
index d619ad2588..9ea5d69ac9 100644
--- a/arm_compute/graph/nodes/EltwiseLayerNode.h
+++ b/arm_compute/graph/nodes/EltwiseLayerNode.h
@@ -63,6 +63,12 @@ public:
*/
ActivationLayerInfo fused_activation() const;
+ /** Returns output quantization info
+ *
+ * @return Output quantization info
+ */
+ QuantizationInfo output_quant_info() const;
+
/** Sets fused activation
*
* @param[in] fused_activation Fused activation to set
@@ -80,6 +86,40 @@ public:
private:
descriptors::EltwiseLayerDescriptor descriptor;
};
+
+/** Unary Eltwise Layer node */
+class UnaryEltwiseLayerNode final : public INode
+{
+public:
+ /** Constructor
+ *
+ * @param[in] descriptor Containing information for the node described in @ref descriptors::EltwiseLayerDescriptor
+ */
+ UnaryEltwiseLayerNode(const descriptors::UnaryEltwiseLayerDescriptor &descriptor);
+ /** Unary eltwise layer descriptor
+ *
+ * @return Unary eltwise layer descriptor which containing information
+ */
+ descriptors::UnaryEltwiseLayerDescriptor eltwise_descriptor() const;
+
+ /** Sets fused activation
+ *
+ * @param[in] fused_activation Fused activation to set
+ */
+ void set_fused_activation(ActivationLayerInfo fused_activation);
+
+ // Inherited overridden methods:
+ NodeType type() const override;
+ bool forward_descriptors() override;
+ TensorDescriptor configure_output(size_t idx) const override;
+ void accept(INodeVisitor &v) override;
+
+ static constexpr NodeType node_type = NodeType::UnaryEltwiseLayer;
+
+private:
+ descriptors::UnaryEltwiseLayerDescriptor descriptor;
+};
+
} // namespace graph
} // namespace arm_compute
#endif /* ARM_COMPUTE_GRAPH_ELTWISE_LAYER_NODE_H */
diff --git a/arm_compute/graph/nodes/SplitLayerNode.h b/arm_compute/graph/nodes/SplitLayerNode.h
index 345260ab84..b5dea7f8c5 100644
--- a/arm_compute/graph/nodes/SplitLayerNode.h
+++ b/arm_compute/graph/nodes/SplitLayerNode.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 ARM Limited.
+ * Copyright (c) 2018-2020 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -38,10 +38,13 @@ class SplitLayerNode final : public INode
public:
/** Default Constructor
*
- * @param[in] num_splits Number of splits
- * @param[in] axis (Optional) Axis to split on. Supported axis >= 2. Defaults to 0
+ * @param[in] num_splits Number of splits
+ * @param[in] axis (Optional) Axis to split on. Defaults to 0
+ * @param[in] size_splits (Optional) The sizes of each output tensor along the split dimension.
+ * Must sum to the dimension of value along split_dim.
+ * Can contain one -1 indicating that dimension is to be inferred.
*/
- SplitLayerNode(unsigned int num_splits, unsigned int axis = 0);
+ SplitLayerNode(unsigned int num_splits, int axis = 0, std::vector<int> size_splits = std::vector<int>());
/** Computes split layer output descriptor
*
* @param[in] input_descriptor Descriptor of the input tensor
@@ -51,8 +54,8 @@ public:
*
* @return A pair with the descriptor of the split and the starting coordinates
*/
- static std::pair<TensorDescriptor, Coordinates> compute_output_descriptor(const TensorDescriptor &input_descriptor,
- unsigned int num_splits, unsigned int axis, unsigned int idx);
+ std::pair<TensorDescriptor, Coordinates> compute_output_descriptor(const TensorDescriptor &input_descriptor,
+ unsigned int num_splits, int axis, unsigned int idx);
/** Number of splits accessor
*
* @return Number of splits
@@ -72,8 +75,9 @@ public:
void accept(INodeVisitor &v) override;
private:
- unsigned int _num_splits;
- unsigned int _axis;
+ unsigned int _num_splits;
+ int _axis;
+ std::vector<int> _size_splits;
};
} // namespace graph
} // namespace arm_compute
diff --git a/examples/graph_yolov3_output_detector.cpp b/examples/graph_yolov3_output_detector.cpp
new file mode 100644
index 0000000000..d803206eb8
--- /dev/null
+++ b/examples/graph_yolov3_output_detector.cpp
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2020 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.h"
+#include "arm_compute/graph/Utils.h"
+
+#include "support/ToolchainSupport.h"
+#include "utils/CommonGraphOptions.h"
+#include "utils/GraphUtils.h"
+#include "utils/Utils.h"
+
+using namespace arm_compute::graph;
+using namespace arm_compute::utils;
+
+class GraphYoloV3OutputDetector
+{
+public:
+ GraphYoloV3OutputDetector()
+ : _graph(0, "GraphYoloV3OutputDetector")
+ {
+ }
+
+ bool setup(const std::string &data_path, Target target)
+ {
+ using namespace arm_compute;
+ using namespace graph_utils;
+
+ NodeID id_ConstantFolding_truediv_1_recip = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 1, 1 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_ConstantFolding_truediv_1_recip = _graph.node(id_ConstantFolding_truediv_1_recip);
+ node_ConstantFolding_truediv_1_recip->set_common_node_parameters(NodeParams{ "ConstantFolding_truediv_1_recip", target });
+ node_ConstantFolding_truediv_1_recip->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/ConstantFolding_truediv_1_recip.npy", DataLayout::NHWC));
+
+ NodeID id_ConstantFolding_truediv_recip = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 1, 1 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_ConstantFolding_truediv_recip = _graph.node(id_ConstantFolding_truediv_recip);
+ node_ConstantFolding_truediv_recip->set_common_node_parameters(NodeParams{ "ConstantFolding_truediv_recip", target });
+ node_ConstantFolding_truediv_recip->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/ConstantFolding_truediv_recip.npy", DataLayout::NHWC));
+
+ NodeID id_detector_yolo_v3_mul_6_y = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 1, 2 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_detector_yolo_v3_mul_6_y = _graph.node(id_detector_yolo_v3_mul_6_y);
+ node_detector_yolo_v3_mul_6_y->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_6_y", target });
+ node_detector_yolo_v3_mul_6_y->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/detector_yolo_v3_mul_6_y.npy", DataLayout::NHWC));
+
+ NodeID id_detector_yolo_v3_mul_3_y = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 1, 2 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_detector_yolo_v3_mul_3_y = _graph.node(id_detector_yolo_v3_mul_3_y);
+ node_detector_yolo_v3_mul_3_y->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_3_y", target });
+ node_detector_yolo_v3_mul_3_y->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/detector_yolo_v3_mul_3_y.npy", DataLayout::NHWC));
+
+ NodeID id_detector_yolo_v3_mul_y = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 1, 2 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_detector_yolo_v3_mul_y = _graph.node(id_detector_yolo_v3_mul_y);
+ node_detector_yolo_v3_mul_y->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_y", target });
+ node_detector_yolo_v3_mul_y->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/detector_yolo_v3_mul_y.npy", DataLayout::NHWC));
+
+ NodeID id_detector_yolo_v3_mul_7 = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 8112, 2 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_detector_yolo_v3_mul_7 = _graph.node(id_detector_yolo_v3_mul_7);
+ node_detector_yolo_v3_mul_7->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_7", target });
+ node_detector_yolo_v3_mul_7->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/detector_yolo_v3_mul_7.npy", DataLayout::NHWC));
+
+ NodeID id_detector_yolo_v3_Reshape_11 = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 8112, 2 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_detector_yolo_v3_Reshape_11 = _graph.node(id_detector_yolo_v3_Reshape_11);
+ node_detector_yolo_v3_Reshape_11->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Reshape_11", target });
+ node_detector_yolo_v3_Reshape_11->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/detector_yolo_v3_Reshape_11.npy", DataLayout::NHWC));
+
+ NodeID id_detector_yolo_v3_mul_4 = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 2028, 2 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_detector_yolo_v3_mul_4 = _graph.node(id_detector_yolo_v3_mul_4);
+ node_detector_yolo_v3_mul_4->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_4", target });
+ node_detector_yolo_v3_mul_4->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/detector_yolo_v3_mul_4.npy", DataLayout::NHWC));
+
+ NodeID id_detector_yolo_v3_Reshape_7 = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 2028, 2 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_detector_yolo_v3_Reshape_7 = _graph.node(id_detector_yolo_v3_Reshape_7);
+ node_detector_yolo_v3_Reshape_7->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Reshape_7", target });
+ node_detector_yolo_v3_Reshape_7->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/detector_yolo_v3_Reshape_7.npy", DataLayout::NHWC));
+
+ NodeID id_detector_yolo_v3_mul_1 = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 507, 2 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_detector_yolo_v3_mul_1 = _graph.node(id_detector_yolo_v3_mul_1);
+ node_detector_yolo_v3_mul_1->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_1", target });
+ node_detector_yolo_v3_mul_1->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/detector_yolo_v3_mul_1.npy", DataLayout::NHWC));
+
+ NodeID id_detector_yolo_v3_Reshape_3 = _graph.add_node<ConstNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 1, 507, 2 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_detector_yolo_v3_Reshape_3 = _graph.node(id_detector_yolo_v3_Reshape_3);
+ node_detector_yolo_v3_Reshape_3->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Reshape_3", target });
+ node_detector_yolo_v3_Reshape_3->output(0)->set_accessor(get_weights_accessor(data_path, "/cnn_data/yolov3_output_detector/detector_yolo_v3_Reshape_3.npy", DataLayout::NHWC));
+
+ NodeID id_input_to_detector_3 = _graph.add_node<InputNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 255, 52, 52, 1 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_input_to_detector_3 = _graph.node(id_input_to_detector_3);
+ node_input_to_detector_3->set_common_node_parameters(NodeParams{ "input_to_detector_3", target });
+ node_input_to_detector_3->output(0)->set_accessor(support::cpp14::make_unique<DummyAccessor>());
+
+ NodeID id_detector_yolo_v3_Reshape_10 = _graph.add_node<ReshapeLayerNode>(
+ TensorShape{ 1, 8112, 85 });
+ INode *node_detector_yolo_v3_Reshape_10 = _graph.node(id_detector_yolo_v3_Reshape_10);
+ node_detector_yolo_v3_Reshape_10->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Reshape_10", target });
+ _graph.add_connection(id_input_to_detector_3, 0, id_detector_yolo_v3_Reshape_10, 0);
+
+ NodeID id_detector_yolo_v3_split_2 = _graph.add_node<SplitLayerNode>(
+ 4,
+ -1,
+ std::vector<int> { 2, 2, 1, 80 });
+ INode *node_detector_yolo_v3_split_2 = _graph.node(id_detector_yolo_v3_split_2);
+ node_detector_yolo_v3_split_2->set_common_node_parameters(NodeParams{ "detector_yolo_v3_split_2", target });
+ _graph.add_connection(id_detector_yolo_v3_Reshape_10, 0, id_detector_yolo_v3_split_2, 0);
+
+ NodeID id_detector_yolo_v3_Sigmoid_6 = _graph.add_node<ActivationLayerNode>(
+ ActivationLayerInfo{ ActivationLayerInfo::ActivationFunction::LOGISTIC, 0, 0 });
+ INode *node_detector_yolo_v3_Sigmoid_6 = _graph.node(id_detector_yolo_v3_Sigmoid_6);
+ node_detector_yolo_v3_Sigmoid_6->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Sigmoid_6", target });
+ _graph.add_connection(id_detector_yolo_v3_split_2, 0, id_detector_yolo_v3_Sigmoid_6, 0);
+
+ NodeID id_detector_yolo_v3_add_2 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Add,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_add_2 = _graph.node(id_detector_yolo_v3_add_2);
+ node_detector_yolo_v3_add_2->set_common_node_parameters(NodeParams{ "detector_yolo_v3_add_2", target });
+ _graph.add_connection(id_detector_yolo_v3_Sigmoid_6, 0, id_detector_yolo_v3_add_2, 0);
+ _graph.add_connection(id_detector_yolo_v3_Reshape_11, 0, id_detector_yolo_v3_add_2, 1);
+
+ NodeID id_detector_yolo_v3_mul_6 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Mul,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_mul_6 = _graph.node(id_detector_yolo_v3_mul_6);
+ node_detector_yolo_v3_mul_6->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_6", target });
+ _graph.add_connection(id_detector_yolo_v3_add_2, 0, id_detector_yolo_v3_mul_6, 0);
+ _graph.add_connection(id_detector_yolo_v3_mul_6_y, 0, id_detector_yolo_v3_mul_6, 1);
+
+ NodeID id_detector_yolo_v3_Sigmoid_7 = _graph.add_node<ActivationLayerNode>(
+ ActivationLayerInfo{ ActivationLayerInfo::ActivationFunction::LOGISTIC, 0, 0 });
+ INode *node_detector_yolo_v3_Sigmoid_7 = _graph.node(id_detector_yolo_v3_Sigmoid_7);
+ node_detector_yolo_v3_Sigmoid_7->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Sigmoid_7", target });
+ _graph.add_connection(id_detector_yolo_v3_split_2, 2, id_detector_yolo_v3_Sigmoid_7, 0);
+
+ NodeID id_detector_yolo_v3_Exp_2 = _graph.add_node<UnaryEltwiseLayerNode>(
+ descriptors::UnaryEltwiseLayerDescriptor
+ {
+ UnaryEltwiseOperation::Exp,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_Exp_2 = _graph.node(id_detector_yolo_v3_Exp_2);
+ node_detector_yolo_v3_Exp_2->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Exp_2", target });
+ _graph.add_connection(id_detector_yolo_v3_split_2, 1, id_detector_yolo_v3_Exp_2, 0);
+
+ NodeID id_detector_yolo_v3_mul_8 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Mul,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_mul_8 = _graph.node(id_detector_yolo_v3_mul_8);
+ node_detector_yolo_v3_mul_8->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_8", target });
+ _graph.add_connection(id_detector_yolo_v3_Exp_2, 0, id_detector_yolo_v3_mul_8, 0);
+ _graph.add_connection(id_detector_yolo_v3_mul_7, 0, id_detector_yolo_v3_mul_8, 1);
+
+ NodeID id_detector_yolo_v3_Sigmoid_8 = _graph.add_node<ActivationLayerNode>(
+ ActivationLayerInfo{ ActivationLayerInfo::ActivationFunction::LOGISTIC, 0, 0 });
+ INode *node_detector_yolo_v3_Sigmoid_8 = _graph.node(id_detector_yolo_v3_Sigmoid_8);
+ node_detector_yolo_v3_Sigmoid_8->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Sigmoid_8", target });
+ _graph.add_connection(id_detector_yolo_v3_split_2, 3, id_detector_yolo_v3_Sigmoid_8, 0);
+
+ NodeID id_detector_yolo_v3_concat_8 = _graph.add_node<ConcatenateLayerNode>(
+ 4,
+ descriptors::ConcatLayerDescriptor{ DataLayoutDimension::HEIGHT });
+ INode *node_detector_yolo_v3_concat_8 = _graph.node(id_detector_yolo_v3_concat_8);
+ node_detector_yolo_v3_concat_8->set_common_node_parameters(NodeParams{ "detector_yolo_v3_concat_8", target });
+ _graph.add_connection(id_detector_yolo_v3_mul_6, 0, id_detector_yolo_v3_concat_8, 0);
+ _graph.add_connection(id_detector_yolo_v3_mul_8, 0, id_detector_yolo_v3_concat_8, 1);
+ _graph.add_connection(id_detector_yolo_v3_Sigmoid_7, 0, id_detector_yolo_v3_concat_8, 2);
+ _graph.add_connection(id_detector_yolo_v3_Sigmoid_8, 0, id_detector_yolo_v3_concat_8, 3);
+
+ NodeID id_input_to_detector_2 = _graph.add_node<InputNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 255, 26, 26, 1 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_input_to_detector_2 = _graph.node(id_input_to_detector_2);
+ node_input_to_detector_2->set_common_node_parameters(NodeParams{ "input_to_detector_2", target });
+ node_input_to_detector_2->output(0)->set_accessor(support::cpp14::make_unique<DummyAccessor>());
+
+ NodeID id_detector_yolo_v3_Reshape_6 = _graph.add_node<ReshapeLayerNode>(
+ TensorShape{ 1, 2028, 85 });
+ INode *node_detector_yolo_v3_Reshape_6 = _graph.node(id_detector_yolo_v3_Reshape_6);
+ node_detector_yolo_v3_Reshape_6->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Reshape_6", target });
+ _graph.add_connection(id_input_to_detector_2, 0, id_detector_yolo_v3_Reshape_6, 0);
+
+ NodeID id_detector_yolo_v3_split_1 = _graph.add_node<SplitLayerNode>(
+ 4,
+ -1,
+ std::vector<int> { 2, 2, 1, 80 });
+ INode *node_detector_yolo_v3_split_1 = _graph.node(id_detector_yolo_v3_split_1);
+ node_detector_yolo_v3_split_1->set_common_node_parameters(NodeParams{ "detector_yolo_v3_split_1", target });
+ _graph.add_connection(id_detector_yolo_v3_Reshape_6, 0, id_detector_yolo_v3_split_1, 0);
+
+ NodeID id_detector_yolo_v3_Sigmoid_3 = _graph.add_node<ActivationLayerNode>(
+ ActivationLayerInfo{ ActivationLayerInfo::ActivationFunction::LOGISTIC, 0, 0 });
+ INode *node_detector_yolo_v3_Sigmoid_3 = _graph.node(id_detector_yolo_v3_Sigmoid_3);
+ node_detector_yolo_v3_Sigmoid_3->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Sigmoid_3", target });
+ _graph.add_connection(id_detector_yolo_v3_split_1, 0, id_detector_yolo_v3_Sigmoid_3, 0);
+
+ NodeID id_detector_yolo_v3_add_1 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Add,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_add_1 = _graph.node(id_detector_yolo_v3_add_1);
+ node_detector_yolo_v3_add_1->set_common_node_parameters(NodeParams{ "detector_yolo_v3_add_1", target });
+ _graph.add_connection(id_detector_yolo_v3_Sigmoid_3, 0, id_detector_yolo_v3_add_1, 0);
+ _graph.add_connection(id_detector_yolo_v3_Reshape_7, 0, id_detector_yolo_v3_add_1, 1);
+
+ NodeID id_detector_yolo_v3_mul_3 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Mul,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_mul_3 = _graph.node(id_detector_yolo_v3_mul_3);
+ node_detector_yolo_v3_mul_3->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_3", target });
+ _graph.add_connection(id_detector_yolo_v3_add_1, 0, id_detector_yolo_v3_mul_3, 0);
+ _graph.add_connection(id_detector_yolo_v3_mul_3_y, 0, id_detector_yolo_v3_mul_3, 1);
+
+ NodeID id_detector_yolo_v3_Sigmoid_4 = _graph.add_node<ActivationLayerNode>(
+ ActivationLayerInfo{ ActivationLayerInfo::ActivationFunction::LOGISTIC, 0, 0 });
+ INode *node_detector_yolo_v3_Sigmoid_4 = _graph.node(id_detector_yolo_v3_Sigmoid_4);
+ node_detector_yolo_v3_Sigmoid_4->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Sigmoid_4", target });
+ _graph.add_connection(id_detector_yolo_v3_split_1, 2, id_detector_yolo_v3_Sigmoid_4, 0);
+
+ NodeID id_detector_yolo_v3_Exp_1 = _graph.add_node<UnaryEltwiseLayerNode>(
+ descriptors::UnaryEltwiseLayerDescriptor
+ {
+ UnaryEltwiseOperation::Exp,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_Exp_1 = _graph.node(id_detector_yolo_v3_Exp_1);
+ node_detector_yolo_v3_Exp_1->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Exp_1", target });
+ _graph.add_connection(id_detector_yolo_v3_split_1, 1, id_detector_yolo_v3_Exp_1, 0);
+
+ NodeID id_detector_yolo_v3_mul_5 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Mul,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_mul_5 = _graph.node(id_detector_yolo_v3_mul_5);
+ node_detector_yolo_v3_mul_5->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_5", target });
+ _graph.add_connection(id_detector_yolo_v3_Exp_1, 0, id_detector_yolo_v3_mul_5, 0);
+ _graph.add_connection(id_detector_yolo_v3_mul_4, 0, id_detector_yolo_v3_mul_5, 1);
+
+ NodeID id_detector_yolo_v3_Sigmoid_5 = _graph.add_node<ActivationLayerNode>(
+ ActivationLayerInfo{ ActivationLayerInfo::ActivationFunction::LOGISTIC, 0, 0 });
+ INode *node_detector_yolo_v3_Sigmoid_5 = _graph.node(id_detector_yolo_v3_Sigmoid_5);
+ node_detector_yolo_v3_Sigmoid_5->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Sigmoid_5", target });
+ _graph.add_connection(id_detector_yolo_v3_split_1, 3, id_detector_yolo_v3_Sigmoid_5, 0);
+
+ NodeID id_detector_yolo_v3_concat_5 = _graph.add_node<ConcatenateLayerNode>(
+ 4,
+ descriptors::ConcatLayerDescriptor{ DataLayoutDimension::HEIGHT });
+ INode *node_detector_yolo_v3_concat_5 = _graph.node(id_detector_yolo_v3_concat_5);
+ node_detector_yolo_v3_concat_5->set_common_node_parameters(NodeParams{ "detector_yolo_v3_concat_5", target });
+ _graph.add_connection(id_detector_yolo_v3_mul_3, 0, id_detector_yolo_v3_concat_5, 0);
+ _graph.add_connection(id_detector_yolo_v3_mul_5, 0, id_detector_yolo_v3_concat_5, 1);
+ _graph.add_connection(id_detector_yolo_v3_Sigmoid_4, 0, id_detector_yolo_v3_concat_5, 2);
+ _graph.add_connection(id_detector_yolo_v3_Sigmoid_5, 0, id_detector_yolo_v3_concat_5, 3);
+
+ NodeID id_input_to_detector_1 = _graph.add_node<InputNode>(
+ TensorDescriptor
+ {
+ TensorShape{ 255, 13, 13, 1 },
+ DataType::F32,
+ QuantizationInfo(),
+ DataLayout::NHWC });
+ INode *node_input_to_detector_1 = _graph.node(id_input_to_detector_1);
+ node_input_to_detector_1->set_common_node_parameters(NodeParams{ "input_to_detector_1", target });
+ node_input_to_detector_1->output(0)->set_accessor(support::cpp14::make_unique<DummyAccessor>());
+
+ NodeID id_detector_yolo_v3_Reshape_2 = _graph.add_node<ReshapeLayerNode>(
+ TensorShape{ 1, 507, 85 });
+ INode *node_detector_yolo_v3_Reshape_2 = _graph.node(id_detector_yolo_v3_Reshape_2);
+ node_detector_yolo_v3_Reshape_2->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Reshape_2", target });
+ _graph.add_connection(id_input_to_detector_1, 0, id_detector_yolo_v3_Reshape_2, 0);
+
+ NodeID id_detector_yolo_v3_split = _graph.add_node<SplitLayerNode>(
+ 4,
+ -1,
+ std::vector<int> { 2, 2, 1, 80 });
+ INode *node_detector_yolo_v3_split = _graph.node(id_detector_yolo_v3_split);
+ node_detector_yolo_v3_split->set_common_node_parameters(NodeParams{ "detector_yolo_v3_split", target });
+ _graph.add_connection(id_detector_yolo_v3_Reshape_2, 0, id_detector_yolo_v3_split, 0);
+
+ NodeID id_detector_yolo_v3_Sigmoid = _graph.add_node<ActivationLayerNode>(
+ ActivationLayerInfo{ ActivationLayerInfo::ActivationFunction::LOGISTIC, 0, 0 });
+ INode *node_detector_yolo_v3_Sigmoid = _graph.node(id_detector_yolo_v3_Sigmoid);
+ node_detector_yolo_v3_Sigmoid->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Sigmoid", target });
+ _graph.add_connection(id_detector_yolo_v3_split, 0, id_detector_yolo_v3_Sigmoid, 0);
+
+ NodeID id_detector_yolo_v3_add = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Add,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_add = _graph.node(id_detector_yolo_v3_add);
+ node_detector_yolo_v3_add->set_common_node_parameters(NodeParams{ "detector_yolo_v3_add", target });
+ _graph.add_connection(id_detector_yolo_v3_Sigmoid, 0, id_detector_yolo_v3_add, 0);
+ _graph.add_connection(id_detector_yolo_v3_Reshape_3, 0, id_detector_yolo_v3_add, 1);
+
+ NodeID id_detector_yolo_v3_mul = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Mul,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_mul = _graph.node(id_detector_yolo_v3_mul);
+ node_detector_yolo_v3_mul->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul", target });
+ _graph.add_connection(id_detector_yolo_v3_add, 0, id_detector_yolo_v3_mul, 0);
+ _graph.add_connection(id_detector_yolo_v3_mul_y, 0, id_detector_yolo_v3_mul, 1);
+
+ NodeID id_detector_yolo_v3_Sigmoid_1 = _graph.add_node<ActivationLayerNode>(
+ ActivationLayerInfo{ ActivationLayerInfo::ActivationFunction::LOGISTIC, 0, 0 });
+ INode *node_detector_yolo_v3_Sigmoid_1 = _graph.node(id_detector_yolo_v3_Sigmoid_1);
+ node_detector_yolo_v3_Sigmoid_1->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Sigmoid_1", target });
+ _graph.add_connection(id_detector_yolo_v3_split, 2, id_detector_yolo_v3_Sigmoid_1, 0);
+
+ NodeID id_detector_yolo_v3_Exp = _graph.add_node<UnaryEltwiseLayerNode>(
+ descriptors::UnaryEltwiseLayerDescriptor
+ {
+ UnaryEltwiseOperation::Exp,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_Exp = _graph.node(id_detector_yolo_v3_Exp);
+ node_detector_yolo_v3_Exp->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Exp", target });
+ _graph.add_connection(id_detector_yolo_v3_split, 1, id_detector_yolo_v3_Exp, 0);
+
+ NodeID id_detector_yolo_v3_mul_2 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Mul,
+ QuantizationInfo() });
+ INode *node_detector_yolo_v3_mul_2 = _graph.node(id_detector_yolo_v3_mul_2);
+ node_detector_yolo_v3_mul_2->set_common_node_parameters(NodeParams{ "detector_yolo_v3_mul_2", target });
+ _graph.add_connection(id_detector_yolo_v3_Exp, 0, id_detector_yolo_v3_mul_2, 0);
+ _graph.add_connection(id_detector_yolo_v3_mul_1, 0, id_detector_yolo_v3_mul_2, 1);
+
+ NodeID id_detector_yolo_v3_Sigmoid_2 = _graph.add_node<ActivationLayerNode>(
+ ActivationLayerInfo{ ActivationLayerInfo::ActivationFunction::LOGISTIC, 0, 0 });
+ INode *node_detector_yolo_v3_Sigmoid_2 = _graph.node(id_detector_yolo_v3_Sigmoid_2);
+ node_detector_yolo_v3_Sigmoid_2->set_common_node_parameters(NodeParams{ "detector_yolo_v3_Sigmoid_2", target });
+ _graph.add_connection(id_detector_yolo_v3_split, 3, id_detector_yolo_v3_Sigmoid_2, 0);
+
+ NodeID id_detector_yolo_v3_concat_2 = _graph.add_node<ConcatenateLayerNode>(
+ 4,
+ descriptors::ConcatLayerDescriptor{ DataLayoutDimension::HEIGHT });
+ INode *node_detector_yolo_v3_concat_2 = _graph.node(id_detector_yolo_v3_concat_2);
+ node_detector_yolo_v3_concat_2->set_common_node_parameters(NodeParams{ "detector_yolo_v3_concat_2", target });
+ _graph.add_connection(id_detector_yolo_v3_mul, 0, id_detector_yolo_v3_concat_2, 0);
+ _graph.add_connection(id_detector_yolo_v3_mul_2, 0, id_detector_yolo_v3_concat_2, 1);
+ _graph.add_connection(id_detector_yolo_v3_Sigmoid_1, 0, id_detector_yolo_v3_concat_2, 2);
+ _graph.add_connection(id_detector_yolo_v3_Sigmoid_2, 0, id_detector_yolo_v3_concat_2, 3);
+
+ NodeID id_detector_yolo_v3_concat_9 = _graph.add_node<ConcatenateLayerNode>(
+ 3,
+ descriptors::ConcatLayerDescriptor{ DataLayoutDimension::WIDTH });
+ INode *node_detector_yolo_v3_concat_9 = _graph.node(id_detector_yolo_v3_concat_9);
+ node_detector_yolo_v3_concat_9->set_common_node_parameters(NodeParams{ "detector_yolo_v3_concat_9", target });
+ _graph.add_connection(id_detector_yolo_v3_concat_2, 0, id_detector_yolo_v3_concat_9, 0);
+ _graph.add_connection(id_detector_yolo_v3_concat_5, 0, id_detector_yolo_v3_concat_9, 1);
+ _graph.add_connection(id_detector_yolo_v3_concat_8, 0, id_detector_yolo_v3_concat_9, 2);
+
+ NodeID id_split = _graph.add_node<SplitLayerNode>(
+ 5,
+ -1,
+ std::vector<int> { 1, 1, 1, 1, -1 });
+ INode *node_split = _graph.node(id_split);
+ node_split->set_common_node_parameters(NodeParams{ "split", target });
+ _graph.add_connection(id_detector_yolo_v3_concat_9, 0, id_split, 0);
+
+ NodeID id_truediv = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Mul,
+ QuantizationInfo() });
+ INode *node_truediv = _graph.node(id_truediv);
+ node_truediv->set_common_node_parameters(NodeParams{ "truediv", target });
+ _graph.add_connection(id_split, 2, id_truediv, 0);
+ _graph.add_connection(id_ConstantFolding_truediv_recip, 0, id_truediv, 1);
+
+ NodeID id_sub = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Sub,
+ QuantizationInfo() });
+ INode *node_sub = _graph.node(id_sub);
+ node_sub->set_common_node_parameters(NodeParams{ "sub", target });
+ _graph.add_connection(id_split, 0, id_sub, 0);
+ _graph.add_connection(id_truediv, 0, id_sub, 1);
+
+ NodeID id_add = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Add,
+ QuantizationInfo() });
+ INode *node_add = _graph.node(id_add);
+ node_add->set_common_node_parameters(NodeParams{ "add", target });
+ _graph.add_connection(id_split, 0, id_add, 0);
+ _graph.add_connection(id_truediv, 0, id_add, 1);
+
+ NodeID id_truediv_1 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Mul,
+ QuantizationInfo() });
+ INode *node_truediv_1 = _graph.node(id_truediv_1);
+ node_truediv_1->set_common_node_parameters(NodeParams{ "truediv_1", target });
+ _graph.add_connection(id_split, 3, id_truediv_1, 0);
+ _graph.add_connection(id_ConstantFolding_truediv_1_recip, 0, id_truediv_1, 1);
+
+ NodeID id_sub_1 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Sub,
+ QuantizationInfo() });
+ INode *node_sub_1 = _graph.node(id_sub_1);
+ node_sub_1->set_common_node_parameters(NodeParams{ "sub_1", target });
+ _graph.add_connection(id_split, 1, id_sub_1, 0);
+ _graph.add_connection(id_truediv_1, 0, id_sub_1, 1);
+
+ NodeID id_add_1 = _graph.add_node<EltwiseLayerNode>(
+ descriptors::EltwiseLayerDescriptor
+ {
+ EltwiseOperation::Add,
+ QuantizationInfo() });
+ INode *node_add_1 = _graph.node(id_add_1);
+ node_add_1->set_common_node_parameters(NodeParams{ "add_1", target });
+ _graph.add_connection(id_split, 1, id_add_1, 0);
+ _graph.add_connection(id_truediv_1, 0, id_add_1, 1);
+
+ NodeID id_output_boxes = _graph.add_node<ConcatenateLayerNode>(
+ 5,
+ descriptors::ConcatLayerDescriptor{ DataLayoutDimension::HEIGHT });
+ INode *node_output_boxes = _graph.node(id_output_boxes);
+ node_output_boxes->set_common_node_parameters(NodeParams{ "output_boxes", target });
+ _graph.add_connection(id_sub, 0, id_output_boxes, 0);
+ _graph.add_connection(id_sub_1, 0, id_output_boxes, 1);
+ _graph.add_connection(id_add, 0, id_output_boxes, 2);
+ _graph.add_connection(id_add_1, 0, id_output_boxes, 3);
+ _graph.add_connection(id_split, 4, id_output_boxes, 4);
+
+ NodeID id_output_140640247016360 = _graph.add_node<OutputNode>();
+ INode *node_output_140640247016360 = _graph.node(id_output_140640247016360);
+ node_output_140640247016360->set_common_node_parameters(NodeParams{ "output_140640247016360", target });
+ _graph.add_connection(id_output_boxes, 0, id_output_140640247016360, 0);
+ node_output_140640247016360->input(0)->set_accessor(support::cpp14::make_unique<DummyAccessor>(0));
+
+ return true;
+ }
+
+ Graph &graph()
+ {
+ return _graph;
+ }
+
+private:
+ Graph _graph;
+};
+class GraphYoloV3OutputDetectorExample : public Example
+{
+public:
+ GraphYoloV3OutputDetectorExample()
+ : cmd_parser(), common_opts(cmd_parser), common_params()
+ {
+ }
+
+ bool do_setup(int argc, char **argv) override
+ {
+ // Parse arguments
+ cmd_parser.parse(argc, argv);
+ cmd_parser.validate();
+
+ // Consume common parameters
+ common_params = consume_common_graph_parameters(common_opts);
+
+ // Return when help menu is requested
+ if(common_params.help)
+ {
+ cmd_parser.print_help(argv[0]);
+ return false;
+ }
+
+ // Print parameter values
+ std::cout << common_params << std::endl;
+
+ model.setup(common_params.data_path, common_params.target);
+
+ GraphConfig config;
+ config.num_threads = common_params.threads;
+ config.use_tuner = common_params.enable_tuner;
+ config.tuner_mode = common_params.tuner_mode;
+ config.tuner_file = common_params.tuner_file;
+
+ context.set_config(config);
+
+ auto pass_manager = create_default_pass_manager(common_params.target, config);
+ manager.finalize_graph(model.graph(), context, pass_manager, common_params.target);
+
+ return true;
+ }
+
+ void do_run() override
+ {
+ manager.execute_graph(model.graph());
+ }
+
+private:
+ CommandLineParser cmd_parser;
+ CommonGraphOptions common_opts;
+ CommonGraphParams common_params;
+
+ GraphContext context{};
+ GraphManager manager{};
+
+ GraphYoloV3OutputDetector model{};
+};
+
+int main(int argc, char **argv)
+{
+ return run_example<GraphYoloV3OutputDetectorExample>(argc, argv);
+}
diff --git a/src/graph/backends/CL/CLFunctionsFactory.cpp b/src/graph/backends/CL/CLFunctionsFactory.cpp
index 312e09a49a..cf494e9a67 100644
--- a/src/graph/backends/CL/CLFunctionsFactory.cpp
+++ b/src/graph/backends/CL/CLFunctionsFactory.cpp
@@ -65,6 +65,12 @@ struct CLEltwiseFunctions
using Multiplication = CLPixelWiseMultiplication;
};
+/** Collection of CL unary element-wise functions */
+struct CLUnaryEltwiseFunctions
+{
+ using Exp = CLExpLayer;
+};
+
/** Function and tensor types to be used inside a CL fused convolution/batch normalization layer */
struct CLFusedLayerTypes
{
@@ -252,6 +258,8 @@ std::unique_ptr<IFunction> CLFunctionFactory::create(INode *node, GraphContext &
return detail::create_detection_post_process_layer<CPPDetectionPostProcessLayer, CLTargetInfo>(*polymorphic_downcast<DetectionPostProcessLayerNode *>(node));
case NodeType::EltwiseLayer:
return detail::create_eltwise_layer<CLEltwiseFunctions, CLTargetInfo>(*polymorphic_downcast<EltwiseLayerNode *>(node));
+ case NodeType::UnaryEltwiseLayer:
+ return detail::create_unary_eltwise_layer<CLUnaryEltwiseFunctions, CLTargetInfo>(*polymorphic_downcast<UnaryEltwiseLayerNode *>(node));
case NodeType::FlattenLayer:
return detail::create_flatten_layer<CLFlattenLayer, CLTargetInfo>(*polymorphic_downcast<FlattenLayerNode *>(node));
case NodeType::FullyConnectedLayer:
diff --git a/src/graph/backends/CL/CLNodeValidator.cpp b/src/graph/backends/CL/CLNodeValidator.cpp
index ddb8e3d1ac..15b54aedee 100644
--- a/src/graph/backends/CL/CLNodeValidator.cpp
+++ b/src/graph/backends/CL/CLNodeValidator.cpp
@@ -38,6 +38,20 @@ namespace graph
{
namespace backends
{
+/** Collection of CL element-wise functions */
+struct CLEltwiseLayerFunctions
+{
+ using ArithmeticAddition = CLArithmeticAddition;
+ using ArithmeticSubtraction = CLArithmeticSubtraction;
+ using PixelWiseMultiplication = CLPixelWiseMultiplication;
+};
+
+/** Collection of CL unary element-wise functions */
+struct CLUnaryEltwiseLayerFunctions
+{
+ using ExpLayer = CLExpLayer;
+};
+
Status CLNodeValidator::validate(INode *node)
{
if(node == nullptr)
@@ -91,6 +105,10 @@ Status CLNodeValidator::validate(INode *node)
return detail::validate_upsample_layer<CLUpsampleLayer>(*polymorphic_downcast<UpsampleLayerNode *>(node));
case NodeType::YOLOLayer:
return detail::validate_yolo_layer<CLYOLOLayer>(*polymorphic_downcast<YOLOLayerNode *>(node));
+ case NodeType::EltwiseLayer:
+ return detail::validate_eltwise_Layer<CLEltwiseLayerFunctions>(*polymorphic_downcast<EltwiseLayerNode *>(node));
+ case NodeType::UnaryEltwiseLayer:
+ return detail::validate_unary_eltwise_layer<CLUnaryEltwiseLayerFunctions>(*polymorphic_downcast<UnaryEltwiseLayerNode *>(node));
default:
return Status{};
}
diff --git a/src/graph/backends/NEON/NEFunctionFactory.cpp b/src/graph/backends/NEON/NEFunctionFactory.cpp
index 454215e7ec..0b3036cb4e 100644
--- a/src/graph/backends/NEON/NEFunctionFactory.cpp
+++ b/src/graph/backends/NEON/NEFunctionFactory.cpp
@@ -53,7 +53,7 @@ struct NETargetInfo
Target NETargetInfo::TargetType = Target::NEON;
-/** Collection of CL convolution functions */
+/** Collection of NEON convolution functions */
struct NEConvolutionLayerFunctions
{
using GenericConvolutionLayer = NEConvolutionLayer;
@@ -62,7 +62,7 @@ struct NEConvolutionLayerFunctions
using WinogradConvolutionLayer = NEWinogradConvolutionLayer;
};
-/** Collection of CL element-wise functions */
+/** Collection of NEON element-wise functions */
struct NEEltwiseFunctions
{
using Addition = NEArithmeticAddition;
@@ -70,6 +70,12 @@ struct NEEltwiseFunctions
using Multiplication = NEPixelWiseMultiplication;
};
+/** Collection of NEON unary element-wise functions */
+struct NEUnaryEltwiseFunctions
+{
+ using Exp = NEExpLayer;
+};
+
/** Function and tensor types to be used inside a NEON fused convolution/batch normalization layer */
struct NEFusedLayerTypes
{
@@ -143,6 +149,8 @@ std::unique_ptr<IFunction> NEFunctionFactory::create(INode *node, GraphContext &
return detail::create_detection_post_process_layer<NEDetectionPostProcessLayer, NETargetInfo>(*polymorphic_downcast<DetectionPostProcessLayerNode *>(node));
case NodeType::EltwiseLayer:
return detail::create_eltwise_layer<NEEltwiseFunctions, NETargetInfo>(*polymorphic_downcast<EltwiseLayerNode *>(node));
+ case NodeType::UnaryEltwiseLayer:
+ return detail::create_unary_eltwise_layer<NEUnaryEltwiseFunctions, NETargetInfo>(*polymorphic_downcast<UnaryEltwiseLayerNode *>(node));
case NodeType::FlattenLayer:
return detail::create_flatten_layer<NEFlattenLayer, NETargetInfo>(*polymorphic_downcast<FlattenLayerNode *>(node));
case NodeType::FullyConnectedLayer:
diff --git a/src/graph/backends/NEON/NENodeValidator.cpp b/src/graph/backends/NEON/NENodeValidator.cpp
index 0a3107292b..d4af3133be 100644
--- a/src/graph/backends/NEON/NENodeValidator.cpp
+++ b/src/graph/backends/NEON/NENodeValidator.cpp
@@ -38,6 +38,20 @@ namespace graph
{
namespace backends
{
+/** Collection of NEON element-wise functions */
+struct NEEltwiseLayerFunctions
+{
+ using ArithmeticAddition = NEArithmeticAddition;
+ using ArithmeticSubtraction = NEArithmeticSubtraction;
+ using PixelWiseMultiplication = NEPixelWiseMultiplication;
+};
+
+/** Collection of NEON unary element-wise functions */
+struct NEUnaryEltwiseLayerFunctions
+{
+ using ExpLayer = NEExpLayer;
+};
+
Status NENodeValidator::validate(INode *node)
{
if(node == nullptr)
@@ -91,6 +105,10 @@ Status NENodeValidator::validate(INode *node)
return detail::validate_upsample_layer<NEUpsampleLayer>(*polymorphic_downcast<UpsampleLayerNode *>(node));
case NodeType::YOLOLayer:
return detail::validate_yolo_layer<NEYOLOLayer>(*polymorphic_downcast<YOLOLayerNode *>(node));
+ case NodeType::EltwiseLayer:
+ return detail::validate_eltwise_Layer<NEEltwiseLayerFunctions>(*polymorphic_downcast<EltwiseLayerNode *>(node));
+ case NodeType::UnaryEltwiseLayer:
+ return detail::validate_unary_eltwise_layer<NEUnaryEltwiseLayerFunctions>(*polymorphic_downcast<UnaryEltwiseLayerNode *>(node));
default:
return Status{};
}
diff --git a/src/graph/mutators/NodeFusionMutator.cpp b/src/graph/mutators/NodeFusionMutator.cpp
index ae53b8ff75..7528520cc7 100644
--- a/src/graph/mutators/NodeFusionMutator.cpp
+++ b/src/graph/mutators/NodeFusionMutator.cpp
@@ -226,6 +226,12 @@ void fuse_node_with_activation(Graph &g, const Edge *output_edge, const std::set
return;
}
+ // EltwiseLayerNode can only be fused when dataype is float
+ if(n_node->type() == NodeType::EltwiseLayer && !is_data_type_float(n_node->output(0)->desc().data_type))
+ {
+ return;
+ }
+
ARM_COMPUTE_LOG_GRAPH_VERBOSE("Fusing node with ID : " << output_edge->producer_id()
<< " with Activation Layer node with ID : " << output_edge->consumer_id() << std::endl);
diff --git a/src/graph/mutators/SplitLayerSubTensorMutator.cpp b/src/graph/mutators/SplitLayerSubTensorMutator.cpp
index 3ba73071ed..76180856c3 100644
--- a/src/graph/mutators/SplitLayerSubTensorMutator.cpp
+++ b/src/graph/mutators/SplitLayerSubTensorMutator.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2019 ARM Limited.
+ * Copyright (c) 2018-2020 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -82,7 +82,7 @@ void SplitLayerSubTensorMutator::mutate(Graph &g)
auto *split_node = arm_compute::utils::cast::polymorphic_downcast<SplitLayerNode *>(node);
- const unsigned int axis = split_node->axis();
+ const int axis = split_node->axis();
const unsigned int num_splits = split_node->num_splits();
const bool extend_parent = (axis < 2);
@@ -92,7 +92,7 @@ void SplitLayerSubTensorMutator::mutate(Graph &g)
Tensor *output_tensor = node->output(i);
const TensorShape output_shape = output_tensor->desc().shape;
Coordinates coords;
- std::tie(std::ignore, coords) = SplitLayerNode::compute_output_descriptor(input_tensor->desc(), num_splits, axis, i);
+ std::tie(std::ignore, coords) = split_node->compute_output_descriptor(input_tensor->desc(), num_splits, axis, i);
backends::IDeviceBackend &backend = backends::BackendRegistry::get().get_backend(output_tensor->desc().target);
std::unique_ptr<ITensorHandle> handle = backend.create_subtensor(input_tensor->handle(), output_shape, coords, extend_parent);
diff --git a/src/graph/nodes/EltwiseLayerNode.cpp b/src/graph/nodes/EltwiseLayerNode.cpp
index 92d183e693..40dcef13fb 100644
--- a/src/graph/nodes/EltwiseLayerNode.cpp
+++ b/src/graph/nodes/EltwiseLayerNode.cpp
@@ -57,6 +57,11 @@ ActivationLayerInfo EltwiseLayerNode::fused_activation() const
return descriptor.fused_activation;
}
+QuantizationInfo EltwiseLayerNode::output_quant_info() const
+{
+ return descriptor.out_quant_info;
+}
+
void EltwiseLayerNode::set_fused_activation(ActivationLayerInfo fused_activation)
{
descriptor.fused_activation = fused_activation;
@@ -100,5 +105,62 @@ void EltwiseLayerNode::accept(INodeVisitor &v)
{
v.visit(*this);
}
+
+UnaryEltwiseLayerNode::UnaryEltwiseLayerNode(const descriptors::UnaryEltwiseLayerDescriptor &descriptor)
+ : descriptor(descriptor)
+{
+ _input_edges.resize(1, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+descriptors::UnaryEltwiseLayerDescriptor UnaryEltwiseLayerNode::eltwise_descriptor() const
+{
+ return descriptor;
+}
+
+void UnaryEltwiseLayerNode::set_fused_activation(ActivationLayerInfo fused_activation)
+{
+ descriptor.fused_activation = fused_activation;
+}
+
+bool UnaryEltwiseLayerNode::forward_descriptors()
+{
+ if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor UnaryEltwiseLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
+
+ auto output_info = src->desc();
+
+ if(!descriptor.out_quant_info.empty())
+ {
+ output_info.set_quantization_info(descriptor.out_quant_info);
+ }
+
+ return output_info;
+}
+
+NodeType UnaryEltwiseLayerNode::type() const
+{
+ return NodeType::UnaryEltwiseLayer;
+}
+
+void UnaryEltwiseLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+
} // namespace graph
} // namespace arm_compute
diff --git a/src/graph/nodes/SplitLayerNode.cpp b/src/graph/nodes/SplitLayerNode.cpp
index 5d46c9dcc9..7bc69c4667 100644
--- a/src/graph/nodes/SplitLayerNode.cpp
+++ b/src/graph/nodes/SplitLayerNode.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018 ARM Limited.
+ * Copyright (c) 2018-2020 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -23,6 +23,7 @@
*/
#include "arm_compute/graph/nodes/SplitLayerNode.h"
+#include "arm_compute/core/Helpers.h"
#include "arm_compute/core/Utils.h"
#include "arm_compute/graph/Graph.h"
#include "arm_compute/graph/INodeVisitor.h"
@@ -31,8 +32,8 @@ namespace arm_compute
{
namespace graph
{
-SplitLayerNode::SplitLayerNode(unsigned int num_splits, unsigned int axis)
- : _num_splits(num_splits), _axis(axis)
+SplitLayerNode::SplitLayerNode(unsigned int num_splits, int axis, std::vector<int> size_splits)
+ : _num_splits(num_splits), _axis(axis), _size_splits(size_splits)
{
_input_edges.resize(1, EmptyEdgeID);
_outputs.resize(num_splits, NullTensorID);
@@ -49,15 +50,34 @@ unsigned int SplitLayerNode::axis() const
}
std::pair<TensorDescriptor, Coordinates> SplitLayerNode::compute_output_descriptor(const TensorDescriptor &input_descriptor,
- unsigned int num_splits, unsigned int axis, unsigned int idx)
+ unsigned int num_splits, int axis, unsigned int idx)
{
- const unsigned int split_size = input_descriptor.shape[axis] / num_splits;
-
+ // Handle negative axis, negative index is used to specify axis from the end (e.g. -1 for the last axis).
+ int num_dimension = static_cast<int32_t>(input_descriptor.shape.num_dimensions());
+ int tmp_axis = wrap_around(axis, num_dimension);
+ Coordinates coords;
TensorDescriptor output_descriptor = input_descriptor;
- output_descriptor.shape.set(axis, split_size);
-
- Coordinates coords;
- coords.set(axis, idx * split_size);
+ int split_size = input_descriptor.shape[tmp_axis] / num_splits;
+ if(_size_splits.empty())
+ {
+ output_descriptor.shape.set(tmp_axis, split_size);
+ coords.set(tmp_axis, idx * split_size);
+ }
+ else
+ {
+ int split_size = _size_splits[idx];
+ if(split_size == -1)
+ {
+ split_size = input_descriptor.shape[tmp_axis];
+ for(unsigned int i = 0; i < _size_splits.size() - 1; ++i)
+ split_size -= _size_splits[i];
+ }
+ output_descriptor.shape.set(tmp_axis, split_size);
+ int coord_value = 0;
+ for(unsigned int i = 0; i < idx; ++i)
+ coord_value += _size_splits[i];
+ coords.set(tmp_axis, coord_value);
+ }
return std::make_pair(output_descriptor, coords);
}
@@ -89,18 +109,39 @@ TensorDescriptor SplitLayerNode::configure_output(size_t idx) const
const Tensor *src = input(0);
ARM_COMPUTE_ERROR_ON(src == nullptr);
- TensorDescriptor output_info;
- std::tie(output_info, std::ignore) = compute_output_descriptor(src->desc(), _num_splits, _axis, idx);
+ TensorDescriptor input_descriptor = src->desc();
+ TensorDescriptor output_descriptor = input_descriptor;
- return output_info;
+ // Handle negative axis, negative index is used to specify axis from the end (e.g. -1 for the last axis).
+ int num_dimension = static_cast<int32_t>(src->desc().shape.num_dimensions());
+ int tmp_axis = wrap_around(_axis, num_dimension);
+
+ int split_size = (_size_splits.empty()) ? (input_descriptor.shape[tmp_axis] / _num_splits) : _size_splits[idx];
+ if(split_size == -1)
+ {
+ split_size = input_descriptor.shape[tmp_axis];
+ for(unsigned int i = 0; i < _size_splits.size() - 1; ++i)
+ split_size -= _size_splits[i];
+ }
+ output_descriptor.shape.set(tmp_axis, split_size);
+
+ return output_descriptor;
}
Status SplitLayerNode::validate() const
{
const Tensor *src = input(0);
ARM_COMPUTE_RETURN_ERROR_ON(src == nullptr);
- ARM_COMPUTE_RETURN_ERROR_ON(_axis >= src->desc().shape.num_dimensions());
- ARM_COMPUTE_RETURN_ERROR_ON_MSG(src->desc().shape[_axis] % _num_splits, "Split should be exact");
+ int num_dimension = static_cast<int32_t>(src->desc().shape.num_dimensions());
+ ARM_COMPUTE_RETURN_ERROR_ON(_axis < (-num_dimension) || _axis >= num_dimension);
+
+ // Handle negative axis, negative index is used to specify axis from the end (e.g. -1 for the last axis).
+ int tmp_axis = wrap_around(_axis, num_dimension);
+
+ if(_size_splits.empty())
+ {
+ ARM_COMPUTE_RETURN_ERROR_ON_MSG(src->desc().shape[tmp_axis] % _num_splits, "Split should be exact");
+ }
return Status{};
}