[newrt] Implement AvgPool kernel for CPU (#1880)
author김수진/동작제어Lab(SR)/Engineer/삼성전자 <sjsujin.kim@samsung.com>
Fri, 6 Jul 2018 02:31:51 +0000 (11:31 +0900)
committer이춘석/동작제어Lab(SR)/Staff Engineer/삼성전자 <chunseok.lee@samsung.com>
Fri, 6 Jul 2018 02:31:51 +0000 (11:31 +0900)
* [newrt] Implement AvgPool kernel for CPU

This commit implements AvgPool kernel for CPU in new runtime.

Signed-off-by:sjsujinkim sjsujin.kim@samsung.com

* Relocate to_string to support/nnapi

include/support/nnapi/Utils.h [new file with mode: 0644]
libs/support/nnapi/src/Utils.cpp [new file with mode: 0644]
runtimes/new_runtime/src/internal/arm_compute/StageGenerator.cc
runtimes/new_runtime/src/internal/cpu/StageGenerator.cc
runtimes/new_runtime/src/internal/kernels/cpufallback/AvgPoolLayer.cc [new file with mode: 0644]
runtimes/new_runtime/src/internal/kernels/cpufallback/AvgPoolLayer.h [new file with mode: 0644]
runtimes/new_runtime/src/internal/kernels/cpufallback/OperationUtils.cc
runtimes/new_runtime/src/internal/kernels/cpufallback/OperationUtils.h

diff --git a/include/support/nnapi/Utils.h b/include/support/nnapi/Utils.h
new file mode 100644 (file)
index 0000000..d34d2f6
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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__
diff --git a/libs/support/nnapi/src/Utils.cpp b/libs/support/nnapi/src/Utils.cpp
new file mode 100644 (file)
index 0000000..ae1076f
--- /dev/null
@@ -0,0 +1,29 @@
+#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
index 5c60d63..333a2bd 100644 (file)
 
 #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}; }
 
@@ -342,7 +329,7 @@ Stage StageGenerator::generate(const ::internal::tflite::op::AvgPool2D::implicit
   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;
index 94ae7aa..189a5e6 100644 (file)
@@ -4,6 +4,11 @@
 
 #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
 {
@@ -109,7 +114,101 @@ Stage StageGenerator::generate(const ::internal::tflite::op::MaxPool2D::implicit
 
 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)
diff --git a/runtimes/new_runtime/src/internal/kernels/cpufallback/AvgPoolLayer.cc b/runtimes/new_runtime/src/internal/kernels/cpufallback/AvgPoolLayer.cc
new file mode 100644 (file)
index 0000000..9e9fbbe
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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
diff --git a/runtimes/new_runtime/src/internal/kernels/cpufallback/AvgPoolLayer.h b/runtimes/new_runtime/src/internal/kernels/cpufallback/AvgPoolLayer.h
new file mode 100644 (file)
index 0000000..9b2f4d3
--- /dev/null
@@ -0,0 +1,65 @@
+#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__
index 0123dbb..dd7d70d 100644 (file)
@@ -45,6 +45,42 @@ void CalculateActivationRangeFloat(int32_t activation, float *activation_min, fl
   }
 }
 
+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;
index 8853493..0c75199 100644 (file)
@@ -67,6 +67,9 @@ inline ::tflite::Dims<4> convertShapeToDims(const 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