aboutsummaryrefslogtreecommitdiff
path: root/src/backends/tosaCommon/operatorMappings
diff options
context:
space:
mode:
authorTeresa Charlin <teresa.charlinreyes@arm.com>2023-12-15 14:20:47 +0000
committerjohn.mcloughlin <john.mcloughlin@arm.com>2023-12-21 11:14:54 +0000
commitca5a23a7cbe46b8da8de432d80889c47a745ca4c (patch)
treec375efe1f4ecdad708d6e8e771a13c07ddbc0257 /src/backends/tosaCommon/operatorMappings
parent0587dd01272199a36210bd0ccc266185b113df75 (diff)
downloadarmnn-ca5a23a7cbe46b8da8de432d80889c47a745ca4c.tar.gz
Add Quantize Support to TOSA Ref Backend
* Adding a one to many tosa mapping for Quantize * Added tests * Resolves IVGCVSW-7175 Signed-off-by: John Mcloughlin <john.mcloughlin@arm.com> Signed-off-by: Teresa Charlin <teresa.charlinreyes@arm.com> Change-Id: Ia0852fefb618b4a29c2601b9de8b6b2731229801
Diffstat (limited to 'src/backends/tosaCommon/operatorMappings')
-rw-r--r--src/backends/tosaCommon/operatorMappings/CMakeLists.txt2
-rw-r--r--src/backends/tosaCommon/operatorMappings/QuantizeOperator.cpp139
-rw-r--r--src/backends/tosaCommon/operatorMappings/QuantizeOperator.hpp16
-rw-r--r--src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp1
-rw-r--r--src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp101
5 files changed, 258 insertions, 1 deletions
diff --git a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
index 279b193db7..b69463487b 100644
--- a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
+++ b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
@@ -18,6 +18,8 @@ list(APPEND armnnTosaBackendOperators_sources
ElementwiseUnaryOperator.hpp
Pooling2DOperator.hpp
Pooling2DOperator.cpp
+ QuantizeOperator.hpp
+ QuantizeOperator.cpp
ReshapeOperator.hpp
ReshapeOperator.cpp
ResizeOperator.hpp
diff --git a/src/backends/tosaCommon/operatorMappings/QuantizeOperator.cpp b/src/backends/tosaCommon/operatorMappings/QuantizeOperator.cpp
new file mode 100644
index 0000000000..1107add6e9
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/QuantizeOperator.cpp
@@ -0,0 +1,139 @@
+//
+// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+// Copyright © 2020 The TensorFlow Authors. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include "QuantizeOperator.hpp"
+
+// This function is paraphrased from:
+// tensorflow/compiler/mlir/tosa/transforms/legalize_common.cc from function convertQuantizeOp
+TosaSerializationBasicBlock* ConvertQuantizeToTosaOperator(const Layer* layer,
+ const std::vector<const TensorInfo*>& inputs,
+ const std::vector<const TensorInfo*>& outputs)
+{
+ ARMNN_THROW_INVALIDARG_MSG_IF_FALSE( inputs.size() == 1,
+ "ConvertQuantizeToTosaOperator: Quantize must have only one input" );
+ ARMNN_THROW_INVALIDARG_MSG_IF_FALSE( outputs.size() == 1,
+ "ConvertQuantizeToTosaOperator: Quantize must have only one output" );
+
+ std::string inputName = std::string("input0_");
+ std::string outputNameZeroPoint = std::string("intermediate0_") + GetUniqueTosaMappingID();
+ std::string outputNameScale = std::string("intermediate1_") + GetUniqueTosaMappingID();
+ std::string outputNameMul = std::string("intermediate2_") + GetUniqueTosaMappingID();
+ std::string outputNameAdd = std::string("intermediate3_") + GetUniqueTosaMappingID();
+ std::string outputName = std::string("output0_");
+ std::string blockName = std::string("Op_QUANTIZE_block_") + GetUniqueTosaMappingID();
+
+ // If a layer is present then the block will be used for execution, so input and output names need to be determined
+ // using the previous and following layers so the graph is connected correctly. For validation this doesn't matter.
+ if(layer != nullptr)
+ {
+ // Get the layers connected to the input slots and determine unique tensor names.
+ Layer& connectedLayer = layer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer();
+ inputName = GenerateUniqueName(connectedLayer, 0);
+
+ // Determine unique output tensor name.
+ outputName = GenerateUniqueOutputName(*layer, 0);
+ }
+
+ const TensorInfo inputInfo = *inputs[0];
+ const TensorInfo outputInfo = *outputs[0];
+
+ // Extract quantization detail from Tensor
+ float zeroPoint = static_cast<float>(outputInfo.GetQuantizationOffset());
+ // No per axis support in Tensorflow TOSA code
+ float scale = outputInfo.GetQuantizationScale();
+
+ // As per the Tensorflow quantization specification
+ // Tensorflow TOSA code calculates quantization using multiplication by scale
+ // Armnn code calculates quantization using division by scale
+ // Invert scale factor passed from Armnn for tf TOSA code
+ scale = (scale != 0) ? (1 / scale) : scale;
+
+ std::vector<TosaSerializationTensor*> tensors;
+
+ // Only add input tensors if connected layer is an input layer.
+ // As intermediate or constant tensors will be created separately.
+ // There also can't be duplicate tensor.
+ std::vector<int32_t> inputShape0;
+ DType inputDType0 = DType::DType_UNKNOWN;
+ if(inputName.find("input0_") != std::string::npos)
+ {
+ inputShape0 = GetTosaTensorShape(inputInfo.GetShape());
+ inputDType0 = ArmNNToDType(inputInfo.GetDataType());
+ ARMNN_THROW_INVALIDARG_MSG_IF_FALSE( inputDType0 == DType::DType_FP16 || inputDType0 == DType::DType_FP32,
+ "ConvertQuantizeToTosaOperator: Quantize input must be of type Float" );
+ tensors.push_back(new TosaSerializationTensor(inputName, inputShape0, inputDType0, {}));
+ }
+
+ std::vector<int32_t> outputShape0 = GetTosaTensorShape(outputInfo.GetShape());
+ DType outputDType0 = ArmNNToDType(outputInfo.GetDataType());
+
+ // quantize:
+ // const_zeroPoint = constant(zeroPoint)
+ // const_scale = constant(scale)
+ // out_mul = mul(input, const_scale)
+ // out_add = add(out_mul, const_zeroPoint)
+ // output = cast<output_type>(out_add)
+
+ // const_zeroPoint
+ TosaSerializationOperator* zeroPointOp = nullptr;
+ TosaSerializationTensor* zeroPointTensor = nullptr;
+ CreateConstTosaOperator<float>(outputNameZeroPoint,
+ zeroPoint,
+ inputDType0,
+ inputShape0,
+ zeroPointOp,
+ zeroPointTensor);
+ tensors.push_back(zeroPointTensor);
+
+ // const_scale
+ TosaSerializationOperator *scaleOp = nullptr;
+ TosaSerializationTensor* scaleTensor = nullptr;
+ CreateConstTosaOperator<float>(outputNameScale,
+ scale,
+ inputDType0,
+ inputShape0,
+ scaleOp,
+ scaleTensor);
+ tensors.push_back(scaleTensor);
+
+ // mul
+ int32_t shift = 0;
+ TosaMulAttribute mulAttribute(shift);
+ TosaSerializationOperator* mulOp = new TosaSerializationOperator(Op_MUL,
+ Attribute_MulAttribute,
+ &mulAttribute,
+ {inputName, outputNameScale},
+ {outputNameMul});
+ tensors.push_back(new TosaSerializationTensor(outputNameMul, inputShape0, inputDType0, {}));
+
+ // add
+ TosaSerializationOperator* addOp = new TosaSerializationOperator(Op_ADD,
+ Attribute_NONE,
+ nullptr,
+ {outputNameMul, outputNameZeroPoint},
+ {outputNameAdd});
+ tensors.push_back(new TosaSerializationTensor(outputNameAdd, inputShape0, inputDType0, {}));
+
+ // cast
+ TosaSerializationOperator* castOp = new TosaSerializationOperator(Op_CAST,
+ Attribute_NONE,
+ nullptr,
+ {outputNameAdd},
+ {outputName});
+
+ tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {}));
+
+ // operatorInputNames/operatorOutputNames ends up being the same as
+ // blockInputNames/blockOutputNames for one-to-one ArmNN to TOSA mappings
+ return new TosaSerializationBasicBlock(blockName, // name
+ mainName, // region name
+ {zeroPointOp, scaleOp, mulOp, addOp, castOp}, // operators
+ tensors, // tensors
+ {inputName}, // inputs
+ {outputName}); // outputs
+}
diff --git a/src/backends/tosaCommon/operatorMappings/QuantizeOperator.hpp b/src/backends/tosaCommon/operatorMappings/QuantizeOperator.hpp
new file mode 100644
index 0000000000..895c091391
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/QuantizeOperator.hpp
@@ -0,0 +1,16 @@
+//
+// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+
+#pragma once
+
+#include "TosaOperatorUtils.hpp"
+
+using namespace armnn;
+using namespace tosa;
+
+TosaSerializationBasicBlock* ConvertQuantizeToTosaOperator(const Layer* layer,
+ const std::vector<const TensorInfo*>& inputs,
+ const std::vector<const TensorInfo*>& outputs); \ No newline at end of file
diff --git a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp
index 749e8764aa..3d88b6563f 100644
--- a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp
+++ b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp
@@ -12,6 +12,7 @@
#include "ElementwiseBinaryOperator.hpp"
#include "ElementwiseUnaryOperator.hpp"
#include "Pooling2DOperator.hpp"
+#include "QuantizeOperator.hpp"
#include "ReshapeOperator.hpp"
#include "ResizeOperator.hpp"
#include "SliceOperator.hpp"
diff --git a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp
index 3e106e1fd5..e43f6ca027 100644
--- a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp
+++ b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp
@@ -1,5 +1,5 @@
//
-// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved.
// SPDX-License-Identifier: MIT
//
@@ -354,3 +354,102 @@ inline std::vector<uint8_t> ConvertConstantTensorDataToBuffer(const std::shared_
tensorHandle->Unmap();
return uint8Data;
}
+
+
+inline std::vector<uint8_t> CreateConstTosaData(const void* value,
+ DType dtype,
+ const std::vector<int32_t>& shape)
+{
+ std::vector<uint8_t> uint8Data;
+ tosa_err_t error = tosa_err_t::TOSA_OK;
+
+ unsigned int numElements = 1;
+ for (auto s : shape)
+ {
+ if (s < 0)
+ {
+ throw armnn::Exception("CreateConstTosaData: negative shape elements unhandled.");
+ }
+ numElements = numElements * static_cast<unsigned int>(s);
+ }
+
+ switch (dtype)
+ {
+ case DType::DType_FP32:
+ {
+ std::vector<float> data(numElements, *static_cast<const float*>(value));
+ error = TosaSerializationHandler::ConvertF32toU8(data, uint8Data);
+ break;
+ }
+ case DType::DType_FP16:
+ {
+ std::vector<float> data(numElements, *static_cast<const float*>(value));
+ error = TosaSerializationHandler::ConvertF16toU8(data, uint8Data);
+ break;
+ }
+ case DType::DType_INT48:
+ {
+ std::vector<int64_t> data(numElements, *static_cast<const int64_t*>(value));
+ error = TosaSerializationHandler::ConvertI48toU8(data, uint8Data);
+ break;
+ }
+ case DType::DType_INT32:
+ {
+ std::vector<int32_t> data(numElements, *static_cast<const int32_t*>(value));
+ error = TosaSerializationHandler::ConvertI32toU8(data, uint8Data);
+ break;
+ }
+ case DType::DType_INT16:
+ {
+ std::vector<int16_t> data(numElements, *static_cast<const int16_t*>(value));
+ error = TosaSerializationHandler::ConvertI16toU8(data, uint8Data);
+ break;
+ }
+ case DType::DType_INT8:
+ {
+ std::vector<int8_t> data(numElements, *static_cast<const int8_t*>(value));
+ error = TosaSerializationHandler::ConvertI8toU8(data, uint8Data);
+ break;
+ }
+ case DType::DType_INT4:
+ {
+ std::vector<int8_t> data(numElements, *static_cast<const int8_t*>(value));
+ error = TosaSerializationHandler::ConvertI4toU8(data, uint8Data);
+ break;
+ }
+ case DType::DType_BOOL:
+ {
+ std::vector<bool> data(numElements, *static_cast<const bool*>(value));
+ error = TosaSerializationHandler::ConvertBooltoU8(data, uint8Data);
+ break;
+ }
+ default:
+ {
+ throw armnn::Exception("CreateConstTosaData: An unsupported data type was encountered.");
+ }
+ }
+
+ if(error != tosa_err_t::TOSA_OK)
+ {
+ throw armnn::Exception("CreateConstTosaData: An error occurred when converting constant data");
+ }
+
+ return uint8Data;
+}
+
+template<typename T>
+inline void CreateConstTosaOperator(const std::string& outputName,
+ const T value,
+ DType dtype,
+ const std::vector<int32_t>& shape,
+ TosaSerializationOperator*& op,
+ TosaSerializationTensor*& tensor)
+{
+ std::vector<uint8_t> uint8Data = CreateConstTosaData(static_cast<const void *>(&value), dtype, shape);
+
+ op = new TosaSerializationOperator(Op_CONST, Attribute_NONE, nullptr, {}, {outputName});
+ ARMNN_THROW_MSG_IF_FALSE(op, armnn::Exception, "CreateConstTosaOperator: failed to created operator");
+
+ tensor = new TosaSerializationTensor(outputName, shape, dtype, uint8Data);
+ ARMNN_THROW_MSG_IF_FALSE(tensor, armnn::Exception, "CreateConstTosaOperator: failed to created tensor");
+} \ No newline at end of file