diff options
Diffstat (limited to 'src/armnnTfParser')
-rwxr-xr-x | src/armnnTfParser/TfParser.cpp | 81 | ||||
-rw-r--r-- | src/armnnTfParser/TfParser.hpp | 2 | ||||
-rw-r--r-- | src/armnnTfParser/test/Mean.cpp | 175 |
3 files changed, 244 insertions, 14 deletions
diff --git a/src/armnnTfParser/TfParser.cpp b/src/armnnTfParser/TfParser.cpp index 90bd992a2b..0087ef83bf 100755 --- a/src/armnnTfParser/TfParser.cpp +++ b/src/armnnTfParser/TfParser.cpp @@ -2,40 +2,27 @@ // Copyright © 2017 Arm Ltd. All rights reserved. // SPDX-License-Identifier: MIT // + #include "TfParser.hpp" -#include <armnn/INetwork.hpp> -#include <armnn/Utils.hpp> #include <armnn/TypesUtils.hpp> -#include <armnn/Exceptions.hpp> #include <armnn/Descriptors.hpp> #include <GraphTopologicalSort.hpp> #include <ParserHelper.hpp> #include <Permute.hpp> -#include <VerificationHelpers.hpp> #include <DataLayoutIndexed.hpp> #include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/text_format.h> #include "tensorflow/core/framework/graph.pb.h" -#include "tensorflow/core/framework/node_def.pb.h" -#include "tensorflow/core/framework/types.pb.h" -#include "tensorflow/core/framework/tensor.pb.h" -#include "tensorflow/core/framework/tensor_shape.pb.h" -#include <boost/assert.hpp> #include <boost/format.hpp> #include <boost/core/ignore_unused.hpp> -#include <boost/log/trivial.hpp> -#include <boost/numeric/conversion/cast.hpp> #include <boost/polymorphic_cast.hpp> -#include <memory> -#include <sstream> #include <numeric> -#include <functional> using namespace armnnUtils; using namespace armnn; @@ -141,6 +128,17 @@ int32_t ReadMandatoryNodeInt32Attribute(const tensorflow::NodeDef& nodeDef, cons return attribValue; } +bool ReadMandatoryNodeBoolAttribute(const tensorflow::NodeDef& nodeDef, const std::string& name) +{ + bool attribValue = false; + ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kB, + [&attribValue](const tensorflow::AttrValue& attrValue) + { + attribValue = static_cast<bool>(attrValue.b()); + }); + return attribValue; +} + uint32_t ReadMandatoryNodeUint32Attribute(const tensorflow::NodeDef& nodeDef, const std::string& name) { uint32_t attribValue = 0u; @@ -338,6 +336,7 @@ const std::map<std::string, TfParser::OperationParsingFunction> TfParser::ms_Ope { "ConcatV2", &TfParser::ParseConcat }, { "LRN", &TfParser::ParseLrn }, { "MatMul", &TfParser::ParseMatMul }, + { "Mean", &TfParser::ParseMean }, { "Mul", &TfParser::ParseMul }, { "Placeholder", &TfParser::ParsePlaceholder }, { "RealDiv", &TfParser::ParseRealDiv }, @@ -2349,6 +2348,60 @@ ParsedTfOperationPtr TfParser::ParseMatMul(const tensorflow::NodeDef& nodeDef, c return std::make_unique<ParsedMatMulTfOperation>(this, nodeDef); } +ParsedTfOperationPtr TfParser::ParseMean(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef) +{ + std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2); + IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index); + TensorInfo inputTensorInfo = inputSlot.GetTensorInfo(); + + if (inputs.size() != 2) + { + throw ParseException( + boost::str(boost::format("Mean expects two inputs!. Got %1% for Node %2% %3%") + % inputs.size() + % nodeDef.name() + % CHECK_LOCATION().AsString())); + } + + bool keepDims = ReadMandatoryNodeBoolAttribute(nodeDef, "keep_dims"); + + ParsedConstTfOperation<int32_t>* axisNode = + boost::polymorphic_downcast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue); + + const TensorInfo& axisTensorInfo = axisNode->GetTensorInfo(); + + ConstTensor axisTensor(axisTensorInfo, axisNode->GetStorage()); + const int* axisData = static_cast<const int*>(axisTensor.GetMemoryArea()); + + TensorInfo outputTensorInfo; + MeanDescriptor meanDescriptor; + meanDescriptor.m_KeepDims = keepDims; + + // Negative axis values are supported so that the process requires + // to convert them into the corresponding positive ones. + // Duplicate values are also removed. + std::vector<int> rawAxisVector(axisData, axisData + axisTensorInfo.GetNumElements()); + std::set<unsigned int> positiveAxisSet; + int rank = static_cast<int>(inputTensorInfo.GetNumDimensions()); + + std::transform(rawAxisVector.begin(), rawAxisVector.end(), + std::inserter(positiveAxisSet, positiveAxisSet.begin()), + [rank](int i) -> unsigned int { return static_cast<unsigned int>((i + rank) % rank); }); + + CalculateReducedOutputTensoInfo(inputTensorInfo, axisTensorInfo, positiveAxisSet, keepDims, outputTensorInfo); + + if (inputTensorInfo.GetNumDimensions() > positiveAxisSet.size()) + { + meanDescriptor.m_Axis.assign(positiveAxisSet.begin(), positiveAxisSet.end()); + } + + IConnectableLayer* layer = m_Network->AddMeanLayer(meanDescriptor, nodeDef.name().c_str()); + layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo); + inputSlot.Connect(layer->GetInputSlot(0)); + + return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer); +} + /// An ParsedTfOperation for a Mul node. /// Creation of the armnn Mul layer is deferred until it is actually needed, because Mul nodes /// are also used for the first part of a leaky relu activation function (Mul followed by Maximum) diff --git a/src/armnnTfParser/TfParser.hpp b/src/armnnTfParser/TfParser.hpp index 4421768fc5..f1b7205ff1 100644 --- a/src/armnnTfParser/TfParser.hpp +++ b/src/armnnTfParser/TfParser.hpp @@ -140,6 +140,7 @@ private: ParsedTfOperationPtr ParseIdentity(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef); ParsedTfOperationPtr ParseLrn(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef); ParsedTfOperationPtr ParseMatMul(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef); + ParsedTfOperationPtr ParseMean(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef); ParsedTfOperationPtr ParseMul(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef); ParsedTfOperationPtr ParsePlaceholder(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef); ParsedTfOperationPtr ParseRealDiv(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef); @@ -260,4 +261,5 @@ private: /// Maps output layer names to their corresponding ids and tensor info. std::unordered_map<std::string, BindingPointInfo> m_NetworkOutputsBindingInfo; }; + } diff --git a/src/armnnTfParser/test/Mean.cpp b/src/armnnTfParser/test/Mean.cpp new file mode 100644 index 0000000000..13041629b5 --- /dev/null +++ b/src/armnnTfParser/test/Mean.cpp @@ -0,0 +1,175 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include <boost/test/unit_test.hpp> +#include "armnnTfParser/ITfParser.hpp" +#include "ParserPrototxtFixture.hpp" + +BOOST_AUTO_TEST_SUITE(TensorflowParser) + +struct MeanFixture : public armnnUtils::ParserPrototxtFixture<armnnTfParser::ITfParser> +{ + explicit MeanFixture(const armnn::TensorShape& inputShape, const armnn::TensorShape& outputShape, + const std::vector<unsigned int>& axis, bool keepDims) + { + std::string protobufAxisString; + std::vector<unsigned int> protobufAxis(axis); + + // If no axis range is specified, the reduction is applied to + // all dimensions of the input tensor + if (protobufAxis.size() == 0) + { + for (unsigned int i = 0; i < inputShape.GetNumDimensions(); ++i) + { + protobufAxis.push_back(i); + } + } + + for (unsigned int i = 0; i < protobufAxis.size(); ++i) + { + protobufAxisString.append(ConvertInt32ToOctalString(static_cast<int>(protobufAxis[i]))); + } + + m_Prototext = R"(node { + name: "input" + op: "Placeholder" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "shape" + value { + shape { + } + } + } + } + node { + name: "Const" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { )"; + + if (axis.size() == 1) + { + m_Prototext.append(R"( tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: )").append(std::to_string(protobufAxis[0])).append(R"( + } )"); + } + else + { + m_Prototext.append(R"( tensor { + dtype: DT_INT32 + tensor_shape { + dim { + size: 2 + } + } + tensor_content: ")").append(protobufAxisString).append(R"(" + } )"); + } + + m_Prototext.append(R"( } + } + } + node { + name: "output" + op: "Mean" + input: "input" + input: "Const" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "Tidx" + value { + type: DT_INT32 + } + } + attr { + key: "keep_dims" + value { + b: )").append(keepDims ? "true" : "false").append(R"( + } + } + })"); + + SetupSingleInputSingleOutput(inputShape, outputShape, "input", "output"); + } +}; + +struct MeanNoAxisNoKeepDimsFixture: MeanFixture +{ + MeanNoAxisNoKeepDimsFixture() : MeanFixture({ 2, 3 }, { 1 }, {}, false) {} +}; + +struct MeanWithAxis0NoKeepDimsFixture: MeanFixture +{ + MeanWithAxis0NoKeepDimsFixture() : MeanFixture({ 2, 3 }, { 3 }, { 0 }, false) {} +}; + +struct MeanWithAxis1NoKeepDimsFixture: MeanFixture +{ + MeanWithAxis1NoKeepDimsFixture() : MeanFixture({ 2, 3 }, { 2 }, { 1 }, false) {} +}; + +struct MeanWithAxis0KeepDimsFixture: MeanFixture +{ + MeanWithAxis0KeepDimsFixture() : MeanFixture({ 2, 3 }, { 1, 3 }, { 0 }, true) {} +}; + +struct MeanWithAxis1KeepDimsFixture: MeanFixture +{ + MeanWithAxis1KeepDimsFixture() : MeanFixture({ 2, 3 }, { 2, 1 }, { 1 }, true) {} +}; + + +BOOST_FIXTURE_TEST_CASE(MeanNoAxisNoKeepDims, MeanNoAxisNoKeepDimsFixture) +{ + RunTest<1>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } }, + { { "output", { 1.5f } } }); +} + +BOOST_FIXTURE_TEST_CASE(MeanWithAxis0NoKeepDims, MeanWithAxis0NoKeepDimsFixture) +{ + RunTest<1>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } }, + { { "output", { 1.5f, 1.5f, 1.5f } } }); +} + +BOOST_FIXTURE_TEST_CASE(MeanWithAxis1NoKeepDims, MeanWithAxis1NoKeepDimsFixture) +{ + RunTest<1>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } }, + { { "output", { 1.f, 2.f } } }); +} + +BOOST_FIXTURE_TEST_CASE(MeanWithAxis0KeepDims, MeanWithAxis0KeepDimsFixture) +{ + RunTest<2>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } }, + { { "output", { 1.5f, 1.5f, 1.5f } } }); +} + +BOOST_FIXTURE_TEST_CASE(MeanWithAxis1KeepDims, MeanWithAxis1KeepDimsFixture) +{ + RunTest<2>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } }, + { { "output", { 1.f, 2.f } } }); +} + +BOOST_AUTO_TEST_SUITE_END() |