aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCathal Corbett <cathal.corbett@arm.com>2024-06-27 15:48:31 +0100
committerCathal Corbett <cathal.corbett@arm.com>2024-06-28 16:22:45 +0100
commit443804adee542d4330713e8dda6357b9495856fa (patch)
treeafd5a983f129770e09fb04672e29a611e4f6dc06
parent66d0e7d5c6d3c271f72848a180616ebc2e3403cc (diff)
downloadarmnn-443804adee542d4330713e8dda6357b9495856fa.tar.gz
IVGCVSW-8444 Implement Activation:BoundedReLu in TosaRef
Signed-off-by: Cathal Corbett <cathal.corbett@arm.com> Change-Id: I8908b85bd2476432a47002ea52699afb06fc455d
-rw-r--r--src/backends/tosaCommon/TosaMappings.cpp3
-rw-r--r--src/backends/tosaCommon/operatorMappings/ReluOperator.cpp53
-rw-r--r--src/backends/tosaReference/test/TosaRefEndToEndTests.cpp32
3 files changed, 76 insertions, 12 deletions
diff --git a/src/backends/tosaCommon/TosaMappings.cpp b/src/backends/tosaCommon/TosaMappings.cpp
index 8608776471..bc1376b9cc 100644
--- a/src/backends/tosaCommon/TosaMappings.cpp
+++ b/src/backends/tosaCommon/TosaMappings.cpp
@@ -30,7 +30,8 @@ TosaSerializationBasicBlock* GetTosaMapping(const Layer* layer,
{
return ConvertLeakyReluToTosaOperator(layer, inputs, outputs, activationDesc);
}
- if (activationDesc->m_Function == ActivationFunction::ReLu)
+ if (activationDesc->m_Function == ActivationFunction::ReLu ||
+ activationDesc->m_Function == ActivationFunction::BoundedReLu)
{
return ConvertReluToTosaOperator(layer, inputs, outputs, activationDesc);
}
diff --git a/src/backends/tosaCommon/operatorMappings/ReluOperator.cpp b/src/backends/tosaCommon/operatorMappings/ReluOperator.cpp
index bd1a59670e..541b39cd8d 100644
--- a/src/backends/tosaCommon/operatorMappings/ReluOperator.cpp
+++ b/src/backends/tosaCommon/operatorMappings/ReluOperator.cpp
@@ -17,7 +17,7 @@
TosaSerializationBasicBlock* ConvertReluToTosaOperator(const Layer* layer,
const std::vector<const TensorInfo*>& inputs,
const std::vector<const TensorInfo*>& outputs,
- const ActivationDescriptor*)
+ const ActivationDescriptor* desc)
{
if (inputs.size() != 1)
{
@@ -31,7 +31,36 @@ TosaSerializationBasicBlock* ConvertReluToTosaOperator(const Layer* layer,
std::string inputName = std::string("input_");
std::string outputName = std::string("output0_");
- std::string blockName = std::string("Op_RELU_block_") + GetUniqueTosaMappingID();
+ std::string blockName = "";
+
+ int32_t clamp_min = 0;
+ int32_t clamp_max = 0;
+ float float_max = 0.0f;
+ switch (desc->m_Function)
+ {
+ case ActivationFunction::ReLu:
+ {
+ clamp_max = std::numeric_limits<int32_t>::max();
+ float_max = std::numeric_limits<float>::max();
+ blockName = std::string("Op_RELU_block_") + GetUniqueTosaMappingID();
+ break;
+ }
+ case ActivationFunction::BoundedReLu:
+ {
+ clamp_max = static_cast<int32_t>(desc->m_A);
+ float_max = desc->m_A;
+ blockName = std::string("Op_BOUNDED_RELU_block_") + GetUniqueTosaMappingID();
+ break;
+ }
+ case ActivationFunction::LeakyReLu:
+ {
+ throw Exception("LeakyRelu TOSA mappings are performed in ConvertLeakyReluToTosaOperator().");
+ }
+ default:
+ {
+ throw Exception("Activation function is not supported in ConvertReluToTosaOperator().");
+ }
+ }
// 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.
@@ -60,8 +89,6 @@ TosaSerializationBasicBlock* ConvertReluToTosaOperator(const Layer* layer,
DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType());
tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {}));
- int32_t clamp_min = 0;
- int32_t clamp_max = std::numeric_limits<int32_t>::max();
std::string clampInputNameStr = inputName;
if (inputDType0 == tosa::DType::DType_INT8 || inputDType0 == tosa::DType::DType_INT16)
{
@@ -72,18 +99,26 @@ TosaSerializationBasicBlock* ConvertReluToTosaOperator(const Layer* layer,
int32_t input_zp = inputs[0]->GetQuantizationOffset();
int32_t output_zp = outputs[0]->GetQuantizationOffset();
- clamp_min = outputs[0]->GetQuantizationOffset();
+ clamp_min = output_zp;
+
+ if (desc->m_Function == ActivationFunction::BoundedReLu)
+ {
+ clamp_max = static_cast<int32_t>(std::round(desc->m_A / outputs[0]->GetQuantizationScale())) + output_zp;
+ }
+
if (inputDType0 == tosa::DType::DType_INT8)
{
clamp_min =
clamp_min < std::numeric_limits<int8_t>::min() ? std::numeric_limits<int8_t>::min() : clamp_min;
- clamp_max = std::numeric_limits<int8_t>::max();
+ clamp_max =
+ clamp_max > std::numeric_limits<int8_t>::max() ? std::numeric_limits<int8_t>::max() : clamp_max;
}
else
{
clamp_min =
clamp_min < std::numeric_limits<int16_t>::min() ? std::numeric_limits<int16_t>::min() : clamp_min;
- clamp_max = std::numeric_limits<int16_t>::max();
+ clamp_max =
+ clamp_max > std::numeric_limits<int16_t>::max() ? std::numeric_limits<int16_t>::max() : clamp_max;
}
TosaSerializationOperator* rescaleOp = nullptr;
@@ -101,8 +136,8 @@ TosaSerializationBasicBlock* ConvertReluToTosaOperator(const Layer* layer,
inputDType0,
{}));
}
-
- TosaClampAttribute attribute(clamp_min, clamp_max, 0, std::numeric_limits<float>::max());
+
+ TosaClampAttribute attribute(clamp_min, clamp_max, 0, float_max);
auto* clamp_op = new TosaSerializationOperator(Op_CLAMP,
Attribute_ClampAttribute,
&attribute,
diff --git a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp
index 09a3d44c02..22fd782a1a 100644
--- a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp
+++ b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp
@@ -30,25 +30,28 @@ TEST_SUITE("TosaRefEndToEnd")
static std::vector<BackendId> tosaDefaultBackends = { "TosaRef" };
// Activation
-//LeakyRelu
+// LeakyRelu
TEST_CASE("TosaRefLeakyReluActivationFloat32")
{
ActivationEndToEndTest<DataType::Float32>(tosaDefaultBackends, ActivationFunction::LeakyReLu, 1.f, 0, 0.01f);
}
+
TEST_CASE("TosaRefLeakyReluActivationFloat16")
{
ActivationEndToEndTest<DataType::Float16>(tosaDefaultBackends, ActivationFunction::LeakyReLu, 0.3f, 5, 0.01f);
}
+
TEST_CASE("TosaRefLeakyReluActivationInt8")
{
ActivationEndToEndTest<DataType::QAsymmS8>(tosaDefaultBackends, ActivationFunction::LeakyReLu, 0.6f, 7, 0.01f);
}
+
TEST_CASE("TosaRefLeakyReluActivationInt16")
{
ActivationEndToEndTest<DataType::QSymmS16>(tosaDefaultBackends, ActivationFunction::LeakyReLu, 0.15f, 0, 0.01f);
}
-//Relu
+// Relu
TEST_CASE("TosaRefReLuEndToEndTestQAsymmS8")
{
ActivationEndToEndTest<armnn::DataType::QAsymmS8>(tosaDefaultBackends, ActivationFunction::ReLu);
@@ -69,6 +72,31 @@ TEST_CASE("TosaRefReLuEndToEndTestQSymmS16")
ActivationEndToEndTest<armnn::DataType::QSymmS16>(tosaDefaultBackends, ActivationFunction::ReLu);
}
+// BoundedRelu
+TEST_CASE("TosaRefBoundedReLuEndToEndTestFloat32")
+{
+ ActivationEndToEndTest<armnn::DataType::Float32>(
+ tosaDefaultBackends, ActivationFunction::BoundedReLu, 1.0f, 0, 6.0f, 0.0f);
+}
+
+TEST_CASE("TosaRefBoundedReLuEndToEndTestFloat16")
+{
+ ActivationEndToEndTest<armnn::DataType::Float16>(
+ tosaDefaultBackends, ActivationFunction::BoundedReLu, 1.0f, 0, 6.0f, 0.0f);
+}
+
+TEST_CASE("TosaRefBoundedReLuEndToEndTestQAsymmS8")
+{
+ ActivationEndToEndTest<armnn::DataType::QAsymmS8>(
+ tosaDefaultBackends, ActivationFunction::BoundedReLu, 1.0f, 0, 6.0f, 0.0f);
+}
+
+TEST_CASE("TosaRefBoundedReLuEndToEndTestQSymmS16")
+{
+ ActivationEndToEndTest<armnn::DataType::QSymmS16>(
+ tosaDefaultBackends, ActivationFunction::BoundedReLu, 1.0f, 0, 6.0f, 0.0f);
+}
+
// Addition
TEST_CASE("TosaRefAdditionEndtoEndTestFloat32")
{