--- /dev/null
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NNFW_SUPPORT_NNAPI_UTILS_H__
+#define __NNFW_SUPPORT_NNAPI_UTILS_H__
+
+#include "NeuralNetworks.h"
+
+namespace nnfw
+{
+namespace support
+{
+namespace nnapi
+{
+
+const char *to_string(const PaddingCode &code);
+
+} // namespace nnapi
+} // namespace support
+} // namespace nnfw
+
+#endif // __NNFW_SUPPORT_NNAPI_UTILS_H__
--- /dev/null
+#include "support/nnapi/Utils.h"
+
+#include <cassert>
+
+namespace nnfw
+{
+namespace support
+{
+namespace nnapi
+{
+
+const char *to_string(const PaddingCode &code)
+{
+ assert((ANEURALNETWORKS_PADDING_SAME == code) || (ANEURALNETWORKS_PADDING_VALID == code));
+
+ switch (code)
+ {
+ case ANEURALNETWORKS_PADDING_SAME:
+ return "ANEURALNETWORKS_PADDING_SAME";
+ case ANEURALNETWORKS_PADDING_VALID:
+ return "ANEURALNETWORKS_PADDING_VALID";
+ }
+
+ return nullptr;
+}
+
+} // namespace nnapi
+} // namespace support
+} // namespace nnfw
#include "NeuralNetworks.h"
-const char *to_string(const PaddingCode &code)
-{
- assert((ANEURALNETWORKS_PADDING_SAME == code) || (ANEURALNETWORKS_PADDING_VALID == code));
-
- switch (code)
- {
- case ANEURALNETWORKS_PADDING_SAME:
- return "ANEURALNETWORKS_PADDING_SAME";
- case ANEURALNETWORKS_PADDING_VALID:
- return "ANEURALNETWORKS_PADDING_VALID";
- }
-
- return nullptr;
-}
+#include "support/nnapi/Utils.h"
template <typename T> std::unique_ptr<T> make_layer(void) { return std::unique_ptr<T>{new T}; }
VERBOSE(AvgPool2D) << "KER_W: " << kw << std::endl;
VERBOSE(AvgPool2D) << "STRIDE_H: " << vstride << std::endl;
VERBOSE(AvgPool2D) << "STRIDE_W: " << hstride << std::endl;
- VERBOSE(AvgPool2D) << "PAD: " << to_string(padding_type) << std::endl;
+ VERBOSE(AvgPool2D) << "PAD: " << ::nnfw::support::nnapi::to_string(padding_type) << std::endl;
VERBOSE(AvgPool2D) << "PAD(T): " << param.padding.top << std::endl;
VERBOSE(AvgPool2D) << "PAD(B): " << param.padding.bottom << std::endl;
VERBOSE(AvgPool2D) << "PAD(L): " << param.padding.left << std::endl;
#include "internal/Padding.h"
#include "internal/kernels/cpufallback/CPUConvolutionLayer.h"
+#include "internal/kernels/cpufallback/AvgPoolLayer.h"
+
+#include "logging.h"
+
+#include "support/nnapi/Utils.h"
namespace internal
{
Stage StageGenerator::generate(const ::internal::tflite::op::AvgPool2D::implicit::Node &node)
{
- throw std::runtime_error("NYI");
+ VERBOSE(AvgPool2D) << "generate CPU AvgPool2D" << std::endl;
+
+ const ::internal::tflite::operand::Index ofm_index{node.param().ofm_index};
+ const ::internal::tflite::operand::Index ifm_index{node.param().ifm_index};
+
+ const ::internal::tflite::operand::Index kh_index{node.param().kh_index};
+ const ::internal::tflite::operand::Index kw_index{node.param().kw_index};
+
+ const ::internal::tflite::operand::Index vstride_index{node.param().vstride_index};
+ const ::internal::tflite::operand::Index hstride_index{node.param().hstride_index};
+
+ const ::internal::tflite::operand::Index padding_index{node.param().padding_index};
+ const ::internal::tflite::operand::Index activation_index{node.param().activation_index};
+
+ const int32_t kh = _ctx.at(kh_index).asScalar<int32_t>();
+ const int32_t kw = _ctx.at(kw_index).asScalar<int32_t>();
+
+ const int32_t vstride = _ctx.at(vstride_index).asScalar<int32_t>();
+ const int32_t hstride = _ctx.at(hstride_index).asScalar<int32_t>();
+
+ const PaddingCode padding_type =
+ static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>());
+
+ assert((ANEURALNETWORKS_PADDING_SAME == padding_type) ||
+ (ANEURALNETWORKS_PADDING_VALID == padding_type));
+
+ // Construct operation parameters
+ struct Param
+ {
+ int ofm_index;
+ int ifm_index;
+
+ uint32_t kw;
+ uint32_t kh;
+
+ ::internal::tflite::operand::Shape ofm_shape{1};
+ ::internal::tflite::operand::Shape ifm_shape{1};
+
+ Padding padding;
+ Stride stride;
+
+ FuseCode activation;
+ };
+
+ Param param;
+
+ param.ofm_index = ofm_index.asInt();
+ param.ifm_index = ifm_index.asInt();
+
+ param.kh = kh;
+ param.kw = kw;
+
+ param.ofm_shape = _ctx.at(ofm_index).shape();
+ param.ifm_shape = _ctx.at(ifm_index).shape();
+
+ param.stride.vertical = vstride;
+ param.stride.horizontal = hstride;
+
+ param.padding = (padding_type == ANEURALNETWORKS_PADDING_SAME)
+ ? same_padding(param.ifm_shape.asFeature(), param.ofm_shape.asFeature(),
+ param.stride, kw, kh)
+ : valid_padding();
+
+ param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>());
+
+ VERBOSE(AvgPool2D) << "IFM_H: " << param.ifm_shape.asFeature().H << std::endl;
+ VERBOSE(AvgPool2D) << "IFM_W: " << param.ifm_shape.asFeature().W << std::endl;
+ VERBOSE(AvgPool2D) << "OFM_H: " << param.ofm_shape.asFeature().H << std::endl;
+ VERBOSE(AvgPool2D) << "OFM_W: " << param.ofm_shape.asFeature().W << std::endl;
+ VERBOSE(AvgPool2D) << "KER_H: " << kh << std::endl;
+ VERBOSE(AvgPool2D) << "KER_W: " << kw << std::endl;
+ VERBOSE(AvgPool2D) << "STRIDE_H: " << vstride << std::endl;
+ VERBOSE(AvgPool2D) << "STRIDE_W: " << hstride << std::endl;
+ VERBOSE(AvgPool2D) << "PAD: " << ::nnfw::support::nnapi::to_string(padding_type) << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(T): " << param.padding.top << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(B): " << param.padding.bottom << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(L): " << param.padding.left << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(R): " << param.padding.right << std::endl;
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto ofm_alloc = tensors->at(::internal::tflite::operand::Index{param.ofm_index}).get();
+ auto ifm_alloc = tensors->at(::internal::tflite::operand::Index{param.ifm_index}).get();
+
+ std::unique_ptr<::internal::kernels::cpu::AvgPoolLayer> fn{
+ new ::internal::kernels::cpu::AvgPoolLayer};
+
+ fn->configure(ifm_alloc->buffer(), param.ifm_shape, param.padding.left, param.padding.right,
+ param.padding.top, param.padding.bottom, param.stride.horizontal,
+ param.stride.vertical, param.kw, param.kh, param.activation, ofm_alloc->buffer(),
+ param.ofm_shape);
+
+ builder.append(std::move(fn));
+ };
}
Stage StageGenerator::generate(const ::internal::tflite::op::Concat::Node &node)
--- /dev/null
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AvgPoolLayer.h"
+
+#include "tensorflow/contrib/lite/kernels/internal/optimized/optimized_ops.h"
+#include "internal/kernels/cpufallback/OperationUtils.h"
+
+namespace internal
+{
+namespace kernels
+{
+namespace cpu
+{
+
+#define AVGPOOLING_PARAMETERS \
+ uint32_t height = getSizeOfDimension(_inputShape, 1); \
+ uint32_t width = getSizeOfDimension(_inputShape, 2); \
+ uint32_t outHeight = getSizeOfDimension(_outputShape, 1); \
+ uint32_t outWidth = getSizeOfDimension(_outputShape, 2); \
+ \
+ uint32_t paddingHeight = (uint32_t)_paddingTop; \
+ uint32_t paddingWidth = (uint32_t)_paddingLeft;
+
+bool AvgPoolLayer::averagePoolFloat32()
+{
+
+ AVGPOOLING_PARAMETERS
+ float output_activation_min, output_activation_max;
+ CalculateActivationRangeFloat(_activation, &output_activation_min, &output_activation_max);
+
+ ::tflite::optimized_ops::AveragePool(
+ reinterpret_cast<const float *>(_inputData), convertShapeToDims(_inputShape), _strideWidth,
+ _strideHeight, paddingWidth, paddingHeight, _kernelWidth, _kernelHeight,
+ output_activation_min, output_activation_max, reinterpret_cast<float *>(_outputData),
+ convertShapeToDims(_outputShape));
+ return true;
+}
+bool AvgPoolLayer::averagePoolQuant8()
+{
+
+ AVGPOOLING_PARAMETERS
+ int32_t output_activation_min = 0;
+ int32_t output_activation_max = 0;
+ CalculateActivationRangeUint8(_activation, _outputShape, &output_activation_min,
+ &output_activation_max);
+
+ ::tflite::optimized_ops::AveragePool(_inputData, convertShapeToDims(_inputShape), _strideWidth,
+ _strideHeight, paddingWidth, paddingHeight, _kernelWidth,
+ _kernelHeight, output_activation_min, output_activation_max,
+ _outputData, convertShapeToDims(_outputShape));
+ return true;
+}
+
+void AvgPoolLayer::configure(uint8_t *inputData, const internal::tflite::operand::Shape inputShape,
+ const uint32_t paddingLeft, const uint32_t paddingRight,
+ const uint32_t paddingTop, const uint32_t paddingBottom,
+ const uint32_t strideWidth, const uint32_t strideHeight,
+ const uint32_t kernelWidth, const uint32_t kernelHeight,
+ const FuseCode activation, uint8_t *outputData,
+ const internal::tflite::operand::Shape outputShape)
+{
+ _inputData = inputData;
+ _inputShape = convertShape(inputShape);
+ _inputType = inputShape.type();
+ _paddingLeft = paddingLeft;
+ _paddingRight = paddingRight;
+ _paddingTop = paddingTop;
+ _paddingBottom = paddingBottom;
+ _strideWidth = strideWidth;
+ _strideHeight = strideHeight;
+ _kernelWidth = kernelWidth;
+ _kernelHeight = kernelHeight;
+ _activation = activation;
+ _outputData = outputData;
+ _outputShape = convertShape(outputShape);
+}
+
+void AvgPoolLayer::run()
+{
+ if (_inputType == static_cast<uint32_t>(OperandType::TENSOR_FLOAT32))
+ {
+ averagePoolFloat32();
+ }
+ else if (_inputType == static_cast<uint32_t>(OperandType::TENSOR_QUANT8_ASYMM))
+ {
+ averagePoolQuant8();
+ }
+}
+
+#undef AVGPOOLING_PARAMETERS
+
+} // namespace cpu
+} // namespace kernels
+} // namespace internal
--- /dev/null
+#ifndef __INTERNAL_KERNELS_CPU_AVGPOOLLAYER_H__
+#define __INTERNAL_KERNELS_CPU_AVGPOOLLAYER_H__
+
+#include <NeuralNetworks.h>
+
+#include <arm_compute/runtime/IFunction.h>
+
+#include "internal/Model.h"
+#include "internal/kernels/cpufallback/OperationUtils.h"
+
+using namespace internal::kernels::cpu;
+
+namespace internal
+{
+namespace kernels
+{
+namespace cpu
+{
+
+class AvgPoolLayer : public ::arm_compute::IFunction
+{
+public:
+ AvgPoolLayer() {}
+
+public:
+ bool averagePoolFloat32();
+
+ bool averagePoolQuant8();
+
+ void configure(uint8_t *inputData, const internal::tflite::operand::Shape inputShape,
+ const uint32_t paddingLeft, const uint32_t paddingRight, const uint32_t paddingTop,
+ const uint32_t paddingBottom, const uint32_t strideWidth,
+ const uint32_t strideHeight, const uint32_t kernelWidth,
+ const uint32_t kernelHeight, const FuseCode activation, uint8_t *outputData,
+ const internal::tflite::operand::Shape outputShape);
+
+ void run();
+
+private:
+ uint8_t *_inputData;
+ uint8_t *_outputData;
+
+ Shape _inputShape;
+ Shape _outputShape;
+
+ uint32_t _paddingLeft;
+ uint32_t _paddingTop;
+ uint32_t _paddingRight;
+ uint32_t _paddingBottom;
+
+ uint32_t _strideWidth;
+ uint32_t _strideHeight;
+ uint32_t _kernelWidth;
+ uint32_t _kernelHeight;
+
+ FuseCode _activation;
+
+ int32_t _inputType;
+};
+
+} // namespace cpu
+} // namespace kernels
+} // namespace internal
+
+#endif // __INTERNAL_KERNELS_CPU_AVGPOOLLAYER_H__
}
}
+void CalculateActivationRangeUint8(int32_t activation, const Shape &outputShape, int32_t *act_min,
+ int32_t *act_max)
+{
+ const int32_t qmin = std::numeric_limits<uint8_t>::min();
+ const int32_t qmax = std::numeric_limits<uint8_t>::max();
+ const auto scale = outputShape.scale;
+ const auto zero_point = outputShape.offset;
+ auto quantize = [scale, zero_point](float f) {
+ return zero_point + static_cast<int32_t>(std::round(f / scale));
+ };
+ if (activation == ANEURALNETWORKS_FUSED_RELU)
+ {
+ *act_min = std::max(qmin, quantize(0.0));
+ *act_max = qmax;
+ }
+ else if (activation == ANEURALNETWORKS_FUSED_RELU6)
+ {
+ *act_min = std::max(qmin, quantize(0.0));
+ *act_max = std::min(qmax, quantize(6.0));
+ }
+ else if (activation == ANEURALNETWORKS_FUSED_RELU1)
+ {
+ *act_min = std::max(qmin, quantize(-1.0));
+ *act_max = std::min(qmax, quantize(1.0));
+ }
+ else if (activation == ANEURALNETWORKS_FUSED_NONE)
+ {
+ *act_min = qmin;
+ *act_max = qmax;
+ }
+ else
+ {
+ std::cout << "Unsupported fused activation function." << std::endl;
+ }
+}
+
Shape convertShape(const ::internal::tflite::operand::Shape &o)
{
Shape shape;
void CalculateActivationRangeFloat(int32_t activation, float *activation_min,
float *activation_max);
+void CalculateActivationRangeUint8(int32_t activation, const Shape &outputShape, int32_t *act_min,
+ int32_t *act_max);
+
Shape convertShape(const ::internal::tflite::operand::Shape &o);
} // namespace cpu