diff options
author | A. Unique TensorFlower <gardener@tensorflow.org> | 2018-07-16 19:09:52 -0700 |
---|---|---|
committer | TensorFlower Gardener <gardener@tensorflow.org> | 2018-07-16 19:13:36 -0700 |
commit | 1a2af489b1087eb22ec76863867e4e397e453e34 (patch) | |
tree | d2576dbbfc6f77897e59353e0844aa0c75a23033 /tensorflow/contrib | |
parent | d8f3425e5b054dff01b5ece80e8c8a101c4ed816 (diff) |
Support reduce_max and reduce_prod
PiperOrigin-RevId: 204846139
Diffstat (limited to 'tensorflow/contrib')
22 files changed, 655 insertions, 134 deletions
diff --git a/tensorflow/contrib/lite/build_def.bzl b/tensorflow/contrib/lite/build_def.bzl index b735d08b4b..bed862454e 100644 --- a/tensorflow/contrib/lite/build_def.bzl +++ b/tensorflow/contrib/lite/build_def.bzl @@ -234,6 +234,8 @@ def generated_test_models(): "padv2", "prelu", "pow", + "reduce_max", + "reduce_prod", "relu", "relu1", "relu6", diff --git a/tensorflow/contrib/lite/builtin_ops.h b/tensorflow/contrib/lite/builtin_ops.h index 6bde5d2e6d..4c7b27c4e0 100644 --- a/tensorflow/contrib/lite/builtin_ops.h +++ b/tensorflow/contrib/lite/builtin_ops.h @@ -106,6 +106,8 @@ typedef enum { kTfLiteBuiltinPow = 78, kTfLiteBuiltinArgMin = 79, kTfLiteBuiltinFakeQuant = 80, + kTfLiteBuiltinReduceProd = 81, + kTfLiteBuiltinReduceMax = 82, } TfLiteBuiltinOperator; #ifdef __cplusplus diff --git a/tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h b/tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h index 080b4e2d03..6fabb9c268 100644 --- a/tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h +++ b/tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h @@ -3468,7 +3468,8 @@ inline bool Reduce(const In* input_data, const int* input_dims, const int* output_dims, const int input_num_dims, const int output_num_dims, const int* axis, const int num_axis, int* input_iter, - Out reducer(Out current, const In in), Out* output_data) { + Out reducer(const Out current, const In in), + Out* output_data) { // Reset input iterator. TFLITE_DCHECK(input_num_dims > 0); for (int idx = 0; idx < input_num_dims; ++idx) { @@ -3486,11 +3487,12 @@ inline bool Reduce(const In* input_data, const int* input_dims, return true; } -inline bool ResolveAxis(const int num_dims, const int* axis, const int num_axis, - int* out_axis, int* out_num_axis) { +inline bool ResolveAxis(const int num_dims, const int* axis, + const int64_t num_axis, int* out_axis, + int* out_num_axis) { *out_num_axis = 0; // Just in case. // o(n^2) is fine since out_num_axis should be really small, mostly <= 4 - for (int idx = 0; idx < num_axis; ++idx) { + for (int64_t idx = 0; idx < num_axis; ++idx) { // Handle negative index. int current = axis[idx] < 0 ? (axis[idx] + num_dims) : axis[idx]; TFLITE_DCHECK(current >= 0 && current < num_dims); @@ -3516,7 +3518,7 @@ inline bool ReduceSumImpl(const In* input_data, const int* input_dims, const int output_num_dims, const int* axis, const int num_axis, int* input_iter, Out* output_data) { - auto reducer = [](Out current, const In in) -> Out { + auto reducer = [](const Out current, const In in) -> Out { const Out actual_in = static_cast<Out>(in); return current + actual_in; }; @@ -3525,6 +3527,24 @@ inline bool ReduceSumImpl(const In* input_data, const int* input_dims, output_data); } +template <typename T> +inline bool InitTensorDataForReduce(const int* dims, const int num_dims, + const T init_value, T* data) { + size_t num_elements = 1; + for (int idx = 0; idx < num_dims; ++idx) { + size_t current = static_cast<size_t>(dims[idx]); + // Overflow prevention. + if (num_elements > std::numeric_limits<size_t>::max() / current) { + return false; + } + num_elements *= current; + } + for (size_t idx = 0; idx < num_elements; ++idx) { + data[idx] = init_value; + } + return true; +} + // Computes the sum of elements across dimensions given in axis. template <typename T> inline bool Sum(const T* input_data, const int* input_dims, @@ -3533,17 +3553,9 @@ inline bool Sum(const T* input_data, const int* input_dims, const int* axis, const int num_axis_dimensions, bool keep_dims, int* temp_index, int* resolved_axis) { // Reset output data. - size_t num_outputs = 1; - for (int idx = 0; idx < output_num_dims; ++idx) { - size_t current = static_cast<size_t>(output_dims[idx]); - // Overflow prevention. - if (num_outputs > std::numeric_limits<size_t>::max() / current) { - return false; - } - num_outputs *= current; - } - for (size_t idx = 0; idx < num_outputs; ++idx) { - output_data[idx] = T(); + if (!InitTensorDataForReduce(output_dims, output_num_dims, static_cast<T>(0), + output_data)) { + return false; } // Resolve axis. @@ -3558,6 +3570,61 @@ inline bool Sum(const T* input_data, const int* input_dims, num_resolved_axis, temp_index, output_data); } +// Computes the max of elements across dimensions given in axis. +template <typename T> +inline bool ReduceMax(const T* input_data, const int* input_dims, + const int input_num_dims, T* output_data, + const int* output_dims, const int output_num_dims, + const int* axis, const int64_t num_axis_dimensions, + bool keep_dims, int* temp_index, int* resolved_axis) { + T init_value = std::numeric_limits<T>::lowest(); + // Reset output data. + if (!InitTensorDataForReduce(output_dims, output_num_dims, init_value, + output_data)) { + return false; + } + + // Resolve axis. + int num_resolved_axis = 0; + if (!ResolveAxis(input_num_dims, axis, num_axis_dimensions, resolved_axis, + &num_resolved_axis)) { + return false; + } + + auto reducer = [](const T current, const T in) -> T { + return (in > current) ? in : current; + }; + return Reduce<T, T>(input_data, input_dims, output_dims, input_num_dims, + output_num_dims, resolved_axis, num_resolved_axis, + temp_index, reducer, output_data); +} + +// Computes the prod of elements across dimensions given in axis. +template <typename T> +inline bool ReduceProd(const T* input_data, const int* input_dims, + const int input_num_dims, T* output_data, + const int* output_dims, const int output_num_dims, + const int* axis, const int64_t num_axis_dimensions, + bool keep_dims, int* temp_index, int* resolved_axis) { + // Reset output data. + if (!InitTensorDataForReduce(output_dims, output_num_dims, static_cast<T>(1), + output_data)) { + return false; + } + + // Resolve axis. + int num_resolved_axis = 0; + if (!ResolveAxis(input_num_dims, axis, num_axis_dimensions, resolved_axis, + &num_resolved_axis)) { + return false; + } + + auto reducer = [](const T current, const T in) -> T { return in * current; }; + return Reduce<T, T>(input_data, input_dims, output_dims, input_num_dims, + output_num_dims, resolved_axis, num_resolved_axis, + temp_index, reducer, output_data); +} + // Computes the mean of elements across dimensions given in axis. // It does so in two stages, first calculates the sum of elements along the axis // then divides it by the number of element in axis. diff --git a/tensorflow/contrib/lite/kernels/reduce.cc b/tensorflow/contrib/lite/kernels/reduce.cc index 31c331a8c6..52e4084ff8 100644 --- a/tensorflow/contrib/lite/kernels/reduce.cc +++ b/tensorflow/contrib/lite/kernels/reduce.cc @@ -315,6 +315,99 @@ TfLiteStatus EvalSum(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; } +template <KernelType kernel_type> +TfLiteStatus EvalProd(TfLiteContext* context, TfLiteNode* node) { + OpContext op_context(context, node); + int64_t num_axis = NumElements(op_context.axis); + TfLiteTensor* temp_index = GetTemporary(context, node, /*index=*/0); + TfLiteTensor* resolved_axis = GetTemporary(context, node, /*index=*/1); + // Resize the output tensor if the output tensor is dynamic. + if (IsDynamicTensor(op_context.output)) { + TF_LITE_ENSURE_OK(context, + ResizeTempAxis(context, &op_context, resolved_axis)); + TF_LITE_ENSURE_OK(context, ResizeOutputTensor(context, &op_context)); + } + +#define TF_LITE_PROD(kernel_type, data_type) \ + kernel_type::ReduceProd<>( \ + GetTensorData<data_type>(op_context.input), \ + op_context.input->dims->data, op_context.input->dims->size, \ + GetTensorData<data_type>(op_context.output), \ + op_context.output->dims->data, op_context.output->dims->size, \ + GetTensorData<int>(op_context.axis), num_axis, \ + op_context.params->keep_dims, GetTensorData<int>(temp_index), \ + GetTensorData<int>(resolved_axis)) + + if (kernel_type == kReference) { + switch (op_context.input->type) { + case kTfLiteFloat32: + TF_LITE_ENSURE(context, TF_LITE_PROD(reference_ops, float)); + break; + case kTfLiteInt32: + TF_LITE_ENSURE(context, TF_LITE_PROD(reference_ops, int)); + break; + case kTfLiteInt64: + TF_LITE_ENSURE(context, TF_LITE_PROD(reference_ops, int64_t)); + break; + case kTfLiteUInt8: + // TODO(wangtz): uint8 reduce_prod is not yet supported. + default: + return kTfLiteError; + } + } +#undef TF_LITE_PROD + return kTfLiteOk; +} + +template <KernelType kernel_type> +TfLiteStatus EvalMax(TfLiteContext* context, TfLiteNode* node) { + OpContext op_context(context, node); + int64_t num_axis = NumElements(op_context.axis); + TfLiteTensor* temp_index = GetTemporary(context, node, /*index=*/0); + TfLiteTensor* resolved_axis = GetTemporary(context, node, /*index=*/1); + // Resize the output tensor if the output tensor is dynamic. + if (IsDynamicTensor(op_context.output)) { + TF_LITE_ENSURE_OK(context, + ResizeTempAxis(context, &op_context, resolved_axis)); + TF_LITE_ENSURE_OK(context, ResizeOutputTensor(context, &op_context)); + } + +#define TF_LITE_MAX(kernel_type, data_type) \ + kernel_type::ReduceMax<>( \ + GetTensorData<data_type>(op_context.input), \ + op_context.input->dims->data, op_context.input->dims->size, \ + GetTensorData<data_type>(op_context.output), \ + op_context.output->dims->data, op_context.output->dims->size, \ + GetTensorData<int>(op_context.axis), num_axis, \ + op_context.params->keep_dims, GetTensorData<int>(temp_index), \ + GetTensorData<int>(resolved_axis)) + + if (kernel_type == kReference) { + switch (op_context.input->type) { + case kTfLiteFloat32: + TF_LITE_ENSURE(context, TF_LITE_MAX(reference_ops, float)); + break; + case kTfLiteInt32: + TF_LITE_ENSURE(context, TF_LITE_MAX(reference_ops, int)); + break; + case kTfLiteInt64: + TF_LITE_ENSURE(context, TF_LITE_MAX(reference_ops, int64_t)); + break; + case kTfLiteUInt8: + TF_LITE_ENSURE_EQ(context, op_context.input->params.scale, + op_context.output->params.scale); + TF_LITE_ENSURE_EQ(context, op_context.input->params.zero_point, + op_context.output->params.zero_point); + TF_LITE_ENSURE(context, TF_LITE_MAX(reference_ops, uint8_t)); + break; + default: + return kTfLiteError; + } + } +#undef TF_LITE_MAX + return kTfLiteOk; +} + } // namespace reduce TfLiteRegistration* Register_MEAN_REF() { @@ -331,9 +424,27 @@ TfLiteRegistration* Register_SUM_REF() { return &r; } +TfLiteRegistration* Register_REDUCE_PROD_REF() { + static TfLiteRegistration r = {reduce::Init, reduce::Free, + reduce::PrepareSimple, + reduce::EvalProd<reduce::kReference>}; + return &r; +} + +TfLiteRegistration* Register_REDUCE_MAX_REF() { + static TfLiteRegistration r = {reduce::Init, reduce::Free, + reduce::PrepareSimple, + reduce::EvalMax<reduce::kReference>}; + return &r; +} + // TODO(kanlig): add optimized implementation of Mean. TfLiteRegistration* Register_MEAN() { return Register_MEAN_REF(); } TfLiteRegistration* Register_SUM() { return Register_SUM_REF(); } +TfLiteRegistration* Register_REDUCE_PROD() { + return Register_REDUCE_PROD_REF(); +} +TfLiteRegistration* Register_REDUCE_MAX() { return Register_REDUCE_MAX_REF(); } } // namespace builtin } // namespace ops diff --git a/tensorflow/contrib/lite/kernels/reduce_test.cc b/tensorflow/contrib/lite/kernels/reduce_test.cc index 9e946822c6..7d28931ecd 100644 --- a/tensorflow/contrib/lite/kernels/reduce_test.cc +++ b/tensorflow/contrib/lite/kernels/reduce_test.cc @@ -25,10 +25,10 @@ using ::testing::ElementsAreArray; class BaseOpModel : public SingleOpModel { public: - void SetAxis(std::initializer_list<int> data) { PopulateTensor(axis_, data); } + void SetAxis(const std::vector<int>& data) { PopulateTensor(axis_, data); } template <class T> - void SetInput(std::initializer_list<T> data) { + void SetInput(std::vector<T> data) { PopulateTensor(input_, data); } @@ -110,14 +110,72 @@ class SumOpDynamicModel : public BaseOpModel { } }; +// Model for the tests case where axis is a const tensor. +class ProdOpConstModel : public BaseOpModel { + public: + ProdOpConstModel(const TensorData& input, const TensorData& output, + std::initializer_list<int> axis_shape, + std::initializer_list<int> axis, bool keep_dims) { + input_ = AddInput(input); + axis_ = AddConstInput(TensorType_INT32, axis, axis_shape); + output_ = AddOutput(output); + SetBuiltinOp(BuiltinOperator_REDUCE_PROD, BuiltinOptions_ReducerOptions, + CreateReducerOptions(builder_, keep_dims).Union()); + BuildInterpreter({GetShape(input_)}); + } +}; + +// Model for the tests case where axis is a dynamic tensor. +class ProdOpDynamicModel : public BaseOpModel { + public: + ProdOpDynamicModel(const TensorData& input, const TensorData& output, + const TensorData& axis, bool keep_dims) { + input_ = AddInput(input); + axis_ = AddInput(axis); + output_ = AddOutput(output); + SetBuiltinOp(BuiltinOperator_REDUCE_PROD, BuiltinOptions_ReducerOptions, + CreateReducerOptions(builder_, keep_dims).Union()); + BuildInterpreter({GetShape(input_)}); + } +}; + +// Model for the tests case where axis is a const tensor. +class MaxOpConstModel : public BaseOpModel { + public: + MaxOpConstModel(const TensorData& input, const TensorData& output, + std::initializer_list<int> axis_shape, + std::initializer_list<int> axis, bool keep_dims) { + input_ = AddInput(input); + axis_ = AddConstInput(TensorType_INT32, axis, axis_shape); + output_ = AddOutput(output); + SetBuiltinOp(BuiltinOperator_REDUCE_MAX, BuiltinOptions_ReducerOptions, + CreateReducerOptions(builder_, keep_dims).Union()); + BuildInterpreter({GetShape(input_)}); + } +}; + +// Model for the tests case where axis is a dynamic tensor. +class MaxOpDynamicModel : public BaseOpModel { + public: + MaxOpDynamicModel(const TensorData& input, const TensorData& output, + const TensorData& axis, bool keep_dims) { + input_ = AddInput(input); + axis_ = AddInput(axis); + output_ = AddOutput(output); + SetBuiltinOp(BuiltinOperator_REDUCE_MAX, BuiltinOptions_ReducerOptions, + CreateReducerOptions(builder_, keep_dims).Union()); + BuildInterpreter({GetShape(input_)}); + } +}; + // for quantized Add, the error shouldn't exceed step float GetTolerance(int min, int max) { return (max - min) / 255.0; } // Tests for reduce_mean TEST(ConstFloatMeanOpTest, NotKeepDims) { - std::initializer_list<float> data = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; MeanOpConstModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {2}}, {4}, {1, 0, -3, -3}, false); m.SetInput(data); @@ -127,9 +185,9 @@ TEST(ConstFloatMeanOpTest, NotKeepDims) { } TEST(ConstFloatMeanOpTest, KeepDims) { - std::initializer_list<float> data = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; MeanOpConstModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {3}}, {2}, {0, 2}, true); m.SetInput(data); @@ -140,13 +198,13 @@ TEST(ConstFloatMeanOpTest, KeepDims) { } TEST(DynamicFloatMeanOpTest, NotKeepDims) { - std::initializer_list<float> data = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; MeanOpDynamicModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {2}}, {TensorType_INT32, {4}}, false); - std::initializer_list<int> axis = {1, 0, -3, -3}; + std::vector<int> axis = {1, 0, -3, -3}; m.SetAxis(axis); m.SetInput(data); m.Invoke(); @@ -155,13 +213,13 @@ TEST(DynamicFloatMeanOpTest, NotKeepDims) { } TEST(DynamicFloatMeanOpTest, KeepDims) { - std::initializer_list<float> data = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; MeanOpDynamicModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {3}}, {TensorType_INT32, {2}}, true); - std::initializer_list<int> axis = {0, 2}; + std::vector<int> axis = {0, 2}; m.SetAxis(axis); m.SetInput(data); m.Invoke(); @@ -171,10 +229,10 @@ TEST(DynamicFloatMeanOpTest, KeepDims) { } TEST(DynamicFloatMeanOpTest, Scale) { - std::initializer_list<float> data = {9.527}; + std::vector<float> data = {9.527}; MeanOpDynamicModel m({TensorType_FLOAT32, {1}}, {TensorType_FLOAT32, {1}}, {TensorType_INT32, {1}}, true); - std::initializer_list<int> axis = {0}; + std::vector<int> axis = {0}; m.SetAxis(axis); m.SetInput(data); m.Invoke(); @@ -185,7 +243,7 @@ TEST(DynamicFloatMeanOpTest, Scale) { TEST(ConstUint8MeanOpTest, NotKeepDims) { float kQuantizedTolerance = GetTolerance(-1.0, 1.0); - std::initializer_list<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; + std::vector<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; MeanOpConstModel m({TensorType_UINT8, {1, 3, 2}, -1.0, 1.0}, {TensorType_UINT8, {2}, -1.0, 1.0}, {1}, {1}, false); m.QuantizeAndPopulate<uint8_t>(m.Input(), data); @@ -197,7 +255,7 @@ TEST(ConstUint8MeanOpTest, NotKeepDims) { TEST(ConstUint8MeanOpTest, KeepDims) { float kQuantizedTolerance = GetTolerance(-1.0, 1.0); - std::initializer_list<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; + std::vector<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; MeanOpConstModel m({TensorType_UINT8, {3, 2}, -1.0, 1.0}, {TensorType_UINT8, {3}, -1.0, 1.0}, {1}, {1}, true); m.QuantizeAndPopulate<uint8_t>(m.Input(), data); @@ -210,11 +268,11 @@ TEST(ConstUint8MeanOpTest, KeepDims) { TEST(DynamicUint8MeanOpTest, NotKeepDims) { float kQuantizedTolerance = GetTolerance(-5.0, 2.0); - std::initializer_list<float> data = {1.3, -4.8, -3.6, 0.24}; + std::vector<float> data = {1.3, -4.8, -3.6, 0.24}; MeanOpDynamicModel m({TensorType_UINT8, {2, 2}, -5.0, 2.0}, {TensorType_UINT8, {2}, -5.0, 2.0}, {TensorType_INT32, {1}}, false); - std::initializer_list<int> axis = {1}; + std::vector<int> axis = {1}; m.SetAxis(axis); m.QuantizeAndPopulate<uint8_t>(m.Input(), data); m.Invoke(); @@ -226,11 +284,11 @@ TEST(DynamicUint8MeanOpTest, NotKeepDims) { TEST(DynamicUint8MeanOpTest, KeepDims) { float kQuantizedTolerance = GetTolerance(-10.0, 12.0); - std::initializer_list<float> data = {11.14, -0.14, 7.423, 0.879}; + std::vector<float> data = {11.14, -0.14, 7.423, 0.879}; MeanOpDynamicModel m({TensorType_UINT8, {2, 2}, -10.0, 12.0}, {TensorType_UINT8, {2}, -10.0, 12.0}, {TensorType_INT32, {1}}, true); - std::initializer_list<int> axis = {0}; + std::vector<int> axis = {0}; m.SetAxis(axis); m.QuantizeAndPopulate<uint8_t>(m.Input(), data); m.Invoke(); @@ -243,9 +301,9 @@ TEST(DynamicUint8MeanOpTest, KeepDims) { // Tests for reduce_sum TEST(ConstFloatSumOpTest, NotKeepDims) { - std::initializer_list<float> data = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; SumOpConstModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {2}}, {4}, {1, 0, -3, -3}, false); m.SetInput(data); @@ -256,9 +314,9 @@ TEST(ConstFloatSumOpTest, NotKeepDims) { } TEST(ConstFloatSumOpTest, KeepDims) { - std::initializer_list<float> data = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; SumOpConstModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {3}}, {2}, {0, 2}, true); m.SetInput(data); @@ -269,13 +327,13 @@ TEST(ConstFloatSumOpTest, KeepDims) { } TEST(DynamicFloatSumOpTest, NotKeepDims) { - std::initializer_list<float> data = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; SumOpDynamicModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {2}}, {TensorType_INT32, {4}}, false); - std::initializer_list<int> axis = {1, 0, -3, -3}; + std::vector<int> axis = {1, 0, -3, -3}; m.SetAxis(axis); m.SetInput(data); m.Invoke(); @@ -285,12 +343,12 @@ TEST(DynamicFloatSumOpTest, NotKeepDims) { } TEST(DynamicFloatSumOpTest, KeepDims) { - std::initializer_list<float> data = { - 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, - 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; SumOpDynamicModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {3}}, {TensorType_INT32, {2}}, true); - std::initializer_list<int> axis = {0, 2}; + std::vector<int> axis = {0, 2}; m.SetAxis(axis); m.SetInput(data); m.Invoke(); @@ -300,10 +358,10 @@ TEST(DynamicFloatSumOpTest, KeepDims) { } TEST(DynamicFloatSumOpTest, Scale) { - std::initializer_list<float> data = {9.527}; + std::vector<float> data = {9.527}; SumOpDynamicModel m({TensorType_FLOAT32, {1}}, {TensorType_FLOAT32, {1}}, {TensorType_INT32, {1}}, true); - std::initializer_list<int> axis = {0}; + std::vector<int> axis = {0}; m.SetAxis(axis); m.SetInput(data); m.Invoke(); @@ -313,7 +371,7 @@ TEST(DynamicFloatSumOpTest, Scale) { TEST(ConstUint8SumOpTest, NotKeepDims) { float kQuantizedTolerance = GetTolerance(-1.0, 1.0); - std::initializer_list<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; + std::vector<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; SumOpConstModel m({TensorType_UINT8, {1, 3, 2}, -1.0, 1.0}, {TensorType_UINT8, {2}, -1.0, 1.0}, {1}, {1}, false); m.QuantizeAndPopulate<uint8_t>(m.Input(), data); @@ -326,7 +384,7 @@ TEST(ConstUint8SumOpTest, NotKeepDims) { TEST(ConstUint8SumOpTest, KeepDims) { float kQuantizedTolerance = GetTolerance(-1.0, 1.0); - std::initializer_list<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; + std::vector<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; SumOpConstModel m({TensorType_UINT8, {3, 2}, -1.0, 1.0}, {TensorType_UINT8, {3}, -1.0, 1.0}, {1}, {1}, true); m.QuantizeAndPopulate<uint8_t>(m.Input(), data); @@ -339,11 +397,11 @@ TEST(ConstUint8SumOpTest, KeepDims) { TEST(DynamicUint8SumOpTest, NotKeepDims) { float kQuantizedTolerance = GetTolerance(-5.0, 2.0); - std::initializer_list<float> data = {1.3, -4.8, -3.6, 0.24}; + std::vector<float> data = {1.3, -4.8, -3.6, 0.24}; SumOpDynamicModel m({TensorType_UINT8, {2, 2}, -5.0, 2.0}, {TensorType_UINT8, {2}, -5.0, 2.0}, {TensorType_INT32, {1}}, false); - std::initializer_list<int> axis = {1}; + std::vector<int> axis = {1}; m.SetAxis(axis); m.QuantizeAndPopulate<uint8_t>(m.Input(), data); m.Invoke(); @@ -355,11 +413,11 @@ TEST(DynamicUint8SumOpTest, NotKeepDims) { TEST(DynamicUint8SumOpTest, KeepDims) { float kQuantizedTolerance = GetTolerance(-10.0, 12.0); - std::initializer_list<float> data = {11.14, -0.14, 7.423, 0.879}; + std::vector<float> data = {11.14, -0.14, 7.423, 0.879}; SumOpDynamicModel m({TensorType_UINT8, {2, 2}, -10.0, 12.0}, {TensorType_UINT8, {2}, -10.0, 12.0}, {TensorType_INT32, {1}}, true); - std::initializer_list<int> axis = {0}; + std::vector<int> axis = {0}; m.SetAxis(axis); m.QuantizeAndPopulate<uint8_t>(m.Input(), data); m.Invoke(); @@ -369,6 +427,209 @@ TEST(DynamicUint8SumOpTest, KeepDims) { ElementsAreArray(ArrayFloatNear({6.47059, 10.698}, kQuantizedTolerance))); } +// Tests for reduce_prod + +TEST(ConstFloatProdOpTest, NotKeepDims) { + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + ProdOpConstModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {2}}, + {4}, {1, 0, -3, -3}, false); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2})); + EXPECT_THAT( + m.GetOutput<float>(), + ElementsAreArray(ArrayFloatNear({3.162341376e+11, 1.9619905536e+12}))); +} + +TEST(ConstFloatProdOpTest, KeepDims) { + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + ProdOpConstModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {3}}, + {2}, {0, 2}, true); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1, 3, 1})); + EXPECT_THAT(m.GetOutput<float>(), + ElementsAreArray( + ArrayFloatNear({7.74592e+06, 1.197504e+08, 6.6889152e+08}))); +} + +TEST(DynamicFloatProdOpTest, NotKeepDims) { + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + ProdOpDynamicModel m({TensorType_FLOAT32, {4, 3, 2}}, + {TensorType_FLOAT32, {2}}, {TensorType_INT32, {4}}, + false); + std::vector<int> axis = {1, 0, -3, -3}; + m.SetAxis(axis); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2})); + EXPECT_THAT( + m.GetOutput<float>(), + ElementsAreArray(ArrayFloatNear({3.16234143225e+11, 1.9619905536e+12}))); +} + +TEST(DynamicFloatProdOpTest, KeepDims) { + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + ProdOpDynamicModel m({TensorType_FLOAT32, {4, 3, 2}}, + {TensorType_FLOAT32, {3}}, {TensorType_INT32, {2}}, + true); + std::vector<int> axis = {0, 2}; + m.SetAxis(axis); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1, 3, 1})); + EXPECT_THAT(m.GetOutput<float>(), + ElementsAreArray( + ArrayFloatNear({7.74592e+06, 1.197504e+08, 6.6889152e+08}))); +} + +TEST(DynamicFloatProdOpTest, Scale) { + std::vector<float> data = {9.527}; + ProdOpDynamicModel m({TensorType_FLOAT32, {1}}, {TensorType_FLOAT32, {1}}, + {TensorType_INT32, {1}}, true); + std::vector<int> axis = {0}; + m.SetAxis(axis); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1})); + EXPECT_THAT(m.GetOutput<float>(), ElementsAreArray(ArrayFloatNear({9.527}))); +} + +// Tests for reduce_max + +TEST(ConstFloatMaxOpTest, NotKeepDims) { + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + MaxOpConstModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {2}}, + {4}, {1, 0, -3, -3}, false); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2})); + EXPECT_THAT(m.GetOutput<float>(), ElementsAreArray(ArrayFloatNear({23, 24}))); +} + +TEST(ConstFloatMaxOpTest, KeepDims) { + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + MaxOpConstModel m({TensorType_FLOAT32, {4, 3, 2}}, {TensorType_FLOAT32, {3}}, + {2}, {0, 2}, true); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1, 3, 1})); + EXPECT_THAT(m.GetOutput<float>(), + ElementsAreArray(ArrayFloatNear({20, 22, 24}))); +} + +TEST(DynamicFloatMaxOpTest, NotKeepDims) { + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + MaxOpDynamicModel m({TensorType_FLOAT32, {4, 3, 2}}, + {TensorType_FLOAT32, {2}}, {TensorType_INT32, {4}}, + false); + std::vector<int> axis = {1, 0, -3, -3}; + m.SetAxis(axis); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2})); + EXPECT_THAT(m.GetOutput<float>(), ElementsAreArray(ArrayFloatNear({23, 24}))); +} + +TEST(DynamicFloatMaxOpTest, KeepDims) { + std::vector<float> data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + MaxOpDynamicModel m({TensorType_FLOAT32, {4, 3, 2}}, + {TensorType_FLOAT32, {3}}, {TensorType_INT32, {2}}, true); + std::vector<int> axis = {0, 2}; + m.SetAxis(axis); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1, 3, 1})); + EXPECT_THAT(m.GetOutput<float>(), + ElementsAreArray(ArrayFloatNear({20, 22, 24}))); +} + +TEST(DynamicFloatMaxOpTest, Scale) { + std::vector<float> data = {9.527}; + MaxOpDynamicModel m({TensorType_FLOAT32, {1}}, {TensorType_FLOAT32, {1}}, + {TensorType_INT32, {1}}, true); + std::vector<int> axis = {0}; + m.SetAxis(axis); + m.SetInput(data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1})); + EXPECT_THAT(m.GetOutput<float>(), ElementsAreArray(ArrayFloatNear({9.527}))); +} + +TEST(ConstUint8MaxOpTest, NotKeepDims) { + float kQuantizedTolerance = GetTolerance(-1.0, 1.0); + std::vector<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; + MaxOpConstModel m({TensorType_UINT8, {1, 3, 2}, -1.0, 1.0}, + {TensorType_UINT8, {2}, -1.0, 1.0}, {1}, {1}, false); + m.QuantizeAndPopulate<uint8_t>(m.Input(), data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1, 2})); + EXPECT_THAT(m.GetDequantizedOutput(), + ElementsAreArray( + ArrayFloatNear({0.501961, 0.603922}, kQuantizedTolerance))); +} + +TEST(ConstUint8MaxOpTest, KeepDims) { + float kQuantizedTolerance = GetTolerance(-1.0, 1.0); + std::vector<float> data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6}; + MaxOpConstModel m({TensorType_UINT8, {3, 2}, -1.0, 1.0}, + {TensorType_UINT8, {3}, -1.0, 1.0}, {1}, {1}, true); + m.QuantizeAndPopulate<uint8_t>(m.Input(), data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({3, 1})); + EXPECT_THAT(m.GetDequantizedOutput(), + ElementsAreArray( + ArrayFloatNear({0.4, 0.4, 0.603922}, kQuantizedTolerance))); +} + +TEST(DynamicUint8MaxOpTest, NotKeepDims) { + float kQuantizedTolerance = GetTolerance(-5.0, 2.0); + std::vector<float> data = {1.3, -4.8, -3.6, 0.24}; + MaxOpDynamicModel m({TensorType_UINT8, {2, 2}, -5.0, 2.0}, + {TensorType_UINT8, {2}, -5.0, 2.0}, + {TensorType_INT32, {1}}, false); + std::vector<int> axis = {1}; + m.SetAxis(axis); + m.QuantizeAndPopulate<uint8_t>(m.Input(), data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2})); + EXPECT_THAT(m.GetDequantizedOutput(), + ElementsAreArray( + ArrayFloatNear({1.2902, 0.247059}, kQuantizedTolerance))); +} + +TEST(DynamicUint8MaxOpTest, KeepDims) { + float kQuantizedTolerance = GetTolerance(-10.0, 12.0); + std::vector<float> data = {11.14, -0.14, 7.423, 0.879}; + MaxOpDynamicModel m({TensorType_UINT8, {2, 2}, -10.0, 12.0}, + {TensorType_UINT8, {2}, -10.0, 12.0}, + {TensorType_INT32, {1}}, true); + std::vector<int> axis = {0}; + m.SetAxis(axis); + m.QuantizeAndPopulate<uint8_t>(m.Input(), data); + m.Invoke(); + EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({1, 2})); + EXPECT_THAT(m.GetDequantizedOutput(), + ElementsAreArray( + ArrayFloatNear({11.1294, 0.862745}, kQuantizedTolerance))); +} + } // namespace } // namespace tflite diff --git a/tensorflow/contrib/lite/kernels/register.cc b/tensorflow/contrib/lite/kernels/register.cc index 22a507e6a4..f0f2757277 100644 --- a/tensorflow/contrib/lite/kernels/register.cc +++ b/tensorflow/contrib/lite/kernels/register.cc @@ -91,6 +91,8 @@ TfLiteRegistration* Register_FLOOR(); TfLiteRegistration* Register_TILE(); TfLiteRegistration* Register_NEG(); TfLiteRegistration* Register_SUM(); +TfLiteRegistration* Register_REDUCE_PROD(); +TfLiteRegistration* Register_REDUCE_MAX(); TfLiteRegistration* Register_SELECT(); TfLiteRegistration* Register_SLICE(); TfLiteRegistration* Register_SIN(); @@ -182,6 +184,8 @@ BuiltinOpResolver::BuiltinOpResolver() { AddBuiltin(BuiltinOperator_TRANSPOSE_CONV, Register_TRANSPOSE_CONV()); AddBuiltin(BuiltinOperator_TILE, Register_TILE()); AddBuiltin(BuiltinOperator_SUM, Register_SUM()); + AddBuiltin(BuiltinOperator_REDUCE_PROD, Register_REDUCE_PROD()); + AddBuiltin(BuiltinOperator_REDUCE_MAX, Register_REDUCE_MAX()); AddBuiltin(BuiltinOperator_EXPAND_DIMS, Register_EXPAND_DIMS()); AddBuiltin(BuiltinOperator_SPARSE_TO_DENSE, Register_SPARSE_TO_DENSE()); AddBuiltin(BuiltinOperator_EQUAL, Register_EQUAL()); diff --git a/tensorflow/contrib/lite/model.cc b/tensorflow/contrib/lite/model.cc index 71e38c3f13..6c1ba3694a 100644 --- a/tensorflow/contrib/lite/model.cc +++ b/tensorflow/contrib/lite/model.cc @@ -616,6 +616,8 @@ TfLiteStatus ParseOpData(const Operator* op, BuiltinOperator op_type, break; } case BuiltinOperator_MEAN: + case BuiltinOperator_REDUCE_MAX: + case BuiltinOperator_REDUCE_PROD: case BuiltinOperator_SUM: { auto* params = MallocPOD<TfLiteReducerParams>(); if (auto* schema_params = op->builtin_options_as_ReducerOptions()) { diff --git a/tensorflow/contrib/lite/nnapi_delegate.cc b/tensorflow/contrib/lite/nnapi_delegate.cc index cc668485a4..5950840e8a 100644 --- a/tensorflow/contrib/lite/nnapi_delegate.cc +++ b/tensorflow/contrib/lite/nnapi_delegate.cc @@ -607,6 +607,8 @@ TfLiteStatus AddOpsAndParams( case tflite::BuiltinOperator_EQUAL: case tflite::BuiltinOperator_NOT_EQUAL: case tflite::BuiltinOperator_SUM: + case tflite::BuiltinOperator_REDUCE_MAX: + case tflite::BuiltinOperator_REDUCE_PROD: case tflite::BuiltinOperator_SQRT: case tflite::BuiltinOperator_RSQRT: case tflite::BuiltinOperator_SHAPE: diff --git a/tensorflow/contrib/lite/schema/schema.fbs b/tensorflow/contrib/lite/schema/schema.fbs index 64830b1dc3..6c3189a884 100644 --- a/tensorflow/contrib/lite/schema/schema.fbs +++ b/tensorflow/contrib/lite/schema/schema.fbs @@ -155,13 +155,15 @@ enum BuiltinOperator : byte { EQUAL = 71, NOT_EQUAL = 72, LOG = 73, - SUM=74, + SUM = 74, SQRT = 75, RSQRT = 76, SHAPE = 77, POW = 78, ARG_MIN = 79, FAKE_QUANT = 80, + REDUCE_PROD = 81, + REDUCE_MAX = 82, } // Options for the builtin operators. diff --git a/tensorflow/contrib/lite/schema/schema_generated.h b/tensorflow/contrib/lite/schema/schema_generated.h index c0b57039cb..8052404319 100755 --- a/tensorflow/contrib/lite/schema/schema_generated.h +++ b/tensorflow/contrib/lite/schema/schema_generated.h @@ -351,11 +351,13 @@ enum BuiltinOperator { BuiltinOperator_POW = 78, BuiltinOperator_ARG_MIN = 79, BuiltinOperator_FAKE_QUANT = 80, + BuiltinOperator_REDUCE_PROD = 81, + BuiltinOperator_REDUCE_MAX = 82, BuiltinOperator_MIN = BuiltinOperator_ADD, - BuiltinOperator_MAX = BuiltinOperator_FAKE_QUANT + BuiltinOperator_MAX = BuiltinOperator_REDUCE_MAX }; -inline BuiltinOperator (&EnumValuesBuiltinOperator())[80] { +inline BuiltinOperator (&EnumValuesBuiltinOperator())[82] { static BuiltinOperator values[] = { BuiltinOperator_ADD, BuiltinOperator_AVERAGE_POOL_2D, @@ -436,7 +438,9 @@ inline BuiltinOperator (&EnumValuesBuiltinOperator())[80] { BuiltinOperator_SHAPE, BuiltinOperator_POW, BuiltinOperator_ARG_MIN, - BuiltinOperator_FAKE_QUANT + BuiltinOperator_FAKE_QUANT, + BuiltinOperator_REDUCE_PROD, + BuiltinOperator_REDUCE_MAX }; return values; } @@ -524,6 +528,8 @@ inline const char **EnumNamesBuiltinOperator() { "POW", "ARG_MIN", "FAKE_QUANT", + "REDUCE_PROD", + "REDUCE_MAX", nullptr }; return names; diff --git a/tensorflow/contrib/lite/testing/generate_examples.py b/tensorflow/contrib/lite/testing/generate_examples.py index 1093bd2cbe..32d04c0717 100644 --- a/tensorflow/contrib/lite/testing/generate_examples.py +++ b/tensorflow/contrib/lite/testing/generate_examples.py @@ -879,16 +879,24 @@ def make_reduce_tests(reduce_op): def make_mean_tests(zip_path): """Make a set of tests to do mean.""" - return make_reduce_tests(tf.reduce_mean)(zip_path) def make_sum_tests(zip_path): """Make a set of tests to do sum.""" - return make_reduce_tests(tf.reduce_sum)(zip_path) +def make_reduce_prod_tests(zip_path): + """Make a set of tests to do prod.""" + return make_reduce_tests(tf.reduce_prod)(zip_path) + + +def make_reduce_max_tests(zip_path): + """Make a set of tests to do max.""" + return make_reduce_tests(tf.reduce_max)(zip_path) + + def make_exp_tests(zip_path): """Make a set of tests to do exp.""" diff --git a/tensorflow/contrib/lite/toco/BUILD b/tensorflow/contrib/lite/toco/BUILD index 2c469c0e75..bbce93f61a 100644 --- a/tensorflow/contrib/lite/toco/BUILD +++ b/tensorflow/contrib/lite/toco/BUILD @@ -246,10 +246,10 @@ cc_library( "graph_transformations/resolve_constant_transpose.cc", "graph_transformations/resolve_constant_unary.cc", "graph_transformations/resolve_fake_quant_args_from_vars.cc", - "graph_transformations/resolve_mean_attributes.cc", "graph_transformations/resolve_multiply_by_zero.cc", "graph_transformations/resolve_pad_attributes.cc", "graph_transformations/resolve_padv2_attributes.cc", + "graph_transformations/resolve_reduce_attributes.cc", "graph_transformations/resolve_reorder_axes.cc", "graph_transformations/resolve_reshape_attributes.cc", "graph_transformations/resolve_slice_attributes.cc", diff --git a/tensorflow/contrib/lite/toco/export_tensorflow.cc b/tensorflow/contrib/lite/toco/export_tensorflow.cc index bf9a51a525..17375d19be 100644 --- a/tensorflow/contrib/lite/toco/export_tensorflow.cc +++ b/tensorflow/contrib/lite/toco/export_tensorflow.cc @@ -1623,10 +1623,11 @@ void ConvertSliceOperator(const Model& model, const SliceOperator& src_op, CreateSliceInput(src_op.inputs[2], src_op.size, tensorflow_graph); } -void ConvertMeanOperator(const Model& model, const MeanOperator& src_op, - GraphDef* tensorflow_graph) { +template <typename T> +void ConvertReduceOperator(const Model& model, const T& src_op, + GraphDef* tensorflow_graph, const string& op_name) { tensorflow::NodeDef* new_op = tensorflow_graph->add_node(); - new_op->set_op("Mean"); + new_op->set_op(op_name); new_op->set_name(src_op.outputs[0]); CHECK_EQ(src_op.inputs.size(), 2); *new_op->add_input() = src_op.inputs[0]; @@ -1961,8 +1962,20 @@ void ConvertOperator(const Model& model, const Operator& src_op, model, static_cast<const StridedSliceOperator&>(src_op), tensorflow_graph); } else if (src_op.type == OperatorType::kMean) { - ConvertMeanOperator(model, static_cast<const MeanOperator&>(src_op), - tensorflow_graph); + ConvertReduceOperator(model, static_cast<const MeanOperator&>(src_op), + tensorflow_graph, "Mean"); + } else if (src_op.type == OperatorType::kSum) { + ConvertReduceOperator(model, + static_cast<const TensorFlowSumOperator&>(src_op), + tensorflow_graph, "Sum"); + } else if (src_op.type == OperatorType::kReduceProd) { + ConvertReduceOperator(model, + static_cast<const TensorFlowProdOperator&>(src_op), + tensorflow_graph, "Prod"); + } else if (src_op.type == OperatorType::kReduceMax) { + ConvertReduceOperator(model, + static_cast<const TensorFlowMaxOperator&>(src_op), + tensorflow_graph, "Max"); } else if (src_op.type == OperatorType::kSub) { ConvertSubOperator(model, static_cast<const SubOperator&>(src_op), tensorflow_graph); diff --git a/tensorflow/contrib/lite/toco/graph_transformations/graph_transformations.h b/tensorflow/contrib/lite/toco/graph_transformations/graph_transformations.h index 7cc9bb75d7..8db7df5c0e 100644 --- a/tensorflow/contrib/lite/toco/graph_transformations/graph_transformations.h +++ b/tensorflow/contrib/lite/toco/graph_transformations/graph_transformations.h @@ -180,7 +180,7 @@ DECLARE_GRAPH_TRANSFORMATION(ResolvePadAttributes) DECLARE_GRAPH_TRANSFORMATION(ResolvePadV2Attributes) DECLARE_GRAPH_TRANSFORMATION(ResolveStridedSliceAttributes) DECLARE_GRAPH_TRANSFORMATION(ResolveSliceAttributes) -DECLARE_GRAPH_TRANSFORMATION(ResolveMeanAttributes) +DECLARE_GRAPH_TRANSFORMATION(ResolveReduceAttributes) DECLARE_GRAPH_TRANSFORMATION(ResolveTransposeAttributes) DECLARE_GRAPH_TRANSFORMATION(ResolveConstantRandomUniform) DECLARE_GRAPH_TRANSFORMATION(ResolveConstantRange) diff --git a/tensorflow/contrib/lite/toco/graph_transformations/propagate_fixed_sizes.cc b/tensorflow/contrib/lite/toco/graph_transformations/propagate_fixed_sizes.cc index 4f95c57451..f422e3a9c7 100644 --- a/tensorflow/contrib/lite/toco/graph_transformations/propagate_fixed_sizes.cc +++ b/tensorflow/contrib/lite/toco/graph_transformations/propagate_fixed_sizes.cc @@ -524,10 +524,12 @@ bool KeepDims(const Operator& op) { switch (op.type) { case OperatorType::kMin: // Reduction Min return static_cast<const TensorFlowMinOperator&>(op).keep_dims; - case OperatorType::kMax: // Reduction Max + case OperatorType::kReduceMax: // Reduction Max return static_cast<const TensorFlowMaxOperator&>(op).keep_dims; case OperatorType::kSum: return static_cast<const TensorFlowSumOperator&>(op).keep_dims; + case OperatorType::kReduceProd: + return static_cast<const TensorFlowProdOperator&>(op).keep_dims; case OperatorType::kMean: return static_cast<const MeanOperator&>(op).keep_dims; default: @@ -1606,8 +1608,9 @@ bool PropagateFixedSizes::Run(Model* model, std::size_t op_index) { ProcessL2PoolOperator(model, static_cast<L2PoolOperator*>(op)); break; case OperatorType::kMin: // Reduction Min - case OperatorType::kMax: // Reduction Max + case OperatorType::kReduceMax: // Reduction Max case OperatorType::kSum: + case OperatorType::kReduceProd: case OperatorType::kMean: ProcessTensorFlowReductionOperator(model, op); break; diff --git a/tensorflow/contrib/lite/toco/graph_transformations/resolve_constant_unary.cc b/tensorflow/contrib/lite/toco/graph_transformations/resolve_constant_unary.cc index f89ef85fdb..51099cf74a 100644 --- a/tensorflow/contrib/lite/toco/graph_transformations/resolve_constant_unary.cc +++ b/tensorflow/contrib/lite/toco/graph_transformations/resolve_constant_unary.cc @@ -58,7 +58,7 @@ bool ResolveConstantUnaryOperator::Run(Model* model, std::size_t op_index) { case OperatorType::kSquare: case OperatorType::kSum: case OperatorType::kMin: // Reduction Min - case OperatorType::kMax: // Reduction Max + case OperatorType::kReduceMax: // Reduction Max case OperatorType::kReshape: case OperatorType::kRelu6: case OperatorType::kRelu1: @@ -207,7 +207,7 @@ bool ResolveConstantUnaryOperator::Run(Model* model, std::size_t op_index) { min = std::min(min, (*input_float_data)[i]); } output_float_data[0] = min; - } else if (unary_op->type == OperatorType::kMax) { + } else if (unary_op->type == OperatorType::kReduceMax) { // At the moment only full reduction across all dimensions is supported. // TODO(starka): Output should not be padded. for (int i = 0; i < output_dims_count; i++) { diff --git a/tensorflow/contrib/lite/toco/graph_transformations/resolve_mean_attributes.cc b/tensorflow/contrib/lite/toco/graph_transformations/resolve_reduce_attributes.cc index 013b50ac9b..5f8a06ba92 100644 --- a/tensorflow/contrib/lite/toco/graph_transformations/resolve_mean_attributes.cc +++ b/tensorflow/contrib/lite/toco/graph_transformations/resolve_reduce_attributes.cc @@ -24,11 +24,8 @@ limitations under the License. namespace toco { -bool ResolveMeanAttributes::Run(Model* model, std::size_t op_index) { - auto* mean_op = model->operators[op_index].get(); - if (mean_op->type != OperatorType::kMean) return false; - auto* op = static_cast<MeanOperator*>(mean_op); - +template <typename T> +bool ResolveAttributes(Model* model, T* op) { if (!op->axis.empty()) { // Attributes already resolved return false; @@ -36,10 +33,26 @@ bool ResolveMeanAttributes::Run(Model* model, std::size_t op_index) { if (op->inputs.size() != 2) return false; if (!IsConstantParameterArray(*model, op->inputs[1])) return false; - const auto& indices_array = model->GetArray(op->inputs[1]); + const Array& indices_array = model->GetArray(op->inputs[1]); if (!indices_array.has_shape()) return false; op->axis = indices_array.GetBuffer<ArrayDataType::kInt32>().data; return true; } +bool ResolveReduceAttributes::Run(Model* model, std::size_t op_index) { + Operator* op = model->operators[op_index].get(); + switch (op->type) { + case OperatorType::kMean: + return ResolveAttributes(model, static_cast<MeanOperator*>(op)); + case OperatorType::kSum: + return ResolveAttributes(model, static_cast<TensorFlowSumOperator*>(op)); + case OperatorType::kReduceProd: + return ResolveAttributes(model, static_cast<TensorFlowProdOperator*>(op)); + case OperatorType::kReduceMax: + return ResolveAttributes(model, static_cast<TensorFlowMaxOperator*>(op)); + default: + return false; + } +} + } // namespace toco diff --git a/tensorflow/contrib/lite/toco/import_tensorflow.cc b/tensorflow/contrib/lite/toco/import_tensorflow.cc index ab3762e7ea..2ffab49e7a 100644 --- a/tensorflow/contrib/lite/toco/import_tensorflow.cc +++ b/tensorflow/contrib/lite/toco/import_tensorflow.cc @@ -805,22 +805,6 @@ tensorflow::Status ConvertSqueezeOperator( return tensorflow::Status::OK(); } -tensorflow::Status ConvertSumOperator( - const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, - Model* model) { - CHECK_EQ(node.op(), "Sum"); - TF_QCHECK_OK(CheckInputsCount(node, tf_import_flags, 2)); - auto* op = new TensorFlowSumOperator; - op->inputs.push_back(node.input(0)); - op->inputs.push_back(node.input(1)); - op->outputs.push_back(node.name()); - model->operators.emplace_back(op); - if (HasAttr(node, "keep_dims")) { - op->keep_dims = GetBoolAttr(node, "keep_dims"); - } - return tensorflow::Status::OK(); -} - tensorflow::Status ConvertSplitOperator( const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, Model* model) { @@ -1058,22 +1042,6 @@ tensorflow::Status ConvertSimpleOperator( return ConvertSimpleOperator<Op>(node, tf_import_flags, model); } -tensorflow::Status ConvertMaxOperator( - const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, - Model* model) { - CHECK_EQ(node.op(), "Max"); - TF_QCHECK_OK(CheckInputsCount(node, tf_import_flags, 2)); - auto* op = new TensorFlowMaxOperator; - op->inputs.push_back(node.input(0)); - op->inputs.push_back(node.input(1)); - op->outputs.push_back(node.name()); - model->operators.emplace_back(op); - if (HasAttr(node, "keep_dims")) { - op->keep_dims = GetBoolAttr(node, "keep_dims"); - } - return tensorflow::Status::OK(); -} - tensorflow::Status ConvertMinOperator( const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, Model* model) { @@ -1412,12 +1380,12 @@ tensorflow::Status ConvertBatchToSpaceNDOperator( return tensorflow::Status::OK(); } -tensorflow::Status ConvertMeanOperator( +template <typename T> +tensorflow::Status ConvertReduceOperator( const NodeDef& node, const TensorFlowImportFlags& tf_import_flags, Model* model) { - CHECK_EQ(node.op(), "Mean"); TF_QCHECK_OK(CheckInputsCount(node, tf_import_flags, 2)); - auto* op = new MeanOperator; + auto* op = new T; op->inputs.push_back(node.input(0)); op->inputs.push_back(node.input(1)); op->outputs.push_back(node.name()); @@ -1893,10 +1861,10 @@ ConverterMapType GetTensorFlowNodeConverterMap() { {"Log", ConvertSimpleOperator<LogOperator, 1>}, {"LogSoftmax", ConvertSimpleOperator<LogSoftmaxOperator, 1>}, {"MatMul", ConvertMatMulOperator}, - {"Max", ConvertMaxOperator}, + {"Max", ConvertReduceOperator<TensorFlowMaxOperator>}, {"MaxPool", ConvertMaxPoolOperator}, {"Maximum", ConvertSimpleOperator<TensorFlowMaximumOperator, 2>}, - {"Mean", ConvertMeanOperator}, + {"Mean", ConvertReduceOperator<MeanOperator>}, {"Merge", ConvertSimpleOperator<TensorFlowMergeOperator, 2>}, {"Min", ConvertMinOperator}, {"Minimum", ConvertSimpleOperator<TensorFlowMinimumOperator, 2>}, @@ -1912,6 +1880,7 @@ ConverterMapType GetTensorFlowNodeConverterMap() { {"Placeholder", ConvertPlaceholderOperator}, {"PlaceholderWithDefault", ConvertIdentityOperator}, {"Pow", ConvertSimpleOperator<PowOperator, 2>}, + {"Prod", ConvertReduceOperator<TensorFlowProdOperator>}, {"RandomUniform", ConvertRandomUniform}, {"Range", ConvertRangeOperator}, {"Rank", ConvertSimpleOperator<RankOperator, 1>}, @@ -1938,7 +1907,7 @@ ConverterMapType GetTensorFlowNodeConverterMap() { {"StopGradient", ConvertIdentityOperator}, {"StridedSlice", ConvertStridedSliceOperator}, {"Sub", ConvertSimpleOperator<SubOperator, 2>}, - {"Sum", ConvertSumOperator}, + {"Sum", ConvertReduceOperator<TensorFlowSumOperator>}, {"Svdf", ConvertSvdfOperator}, {"Switch", ConvertSwitchOperator}, {"Tanh", ConvertSimpleOperator<TanhOperator, 1>}, diff --git a/tensorflow/contrib/lite/toco/model.h b/tensorflow/contrib/lite/toco/model.h index d06a30b638..37f4188cf7 100644 --- a/tensorflow/contrib/lite/toco/model.h +++ b/tensorflow/contrib/lite/toco/model.h @@ -85,6 +85,7 @@ enum class OperatorType : uint8 { kBatchToSpaceND, kPad, kPadV2, + kReduceProd, // Reduction product kStridedSlice, kSlice, kSqueeze, @@ -106,10 +107,10 @@ enum class OperatorType : uint8 { kIdentity, kLess, kLessEqual, - kMax, // Reduction Max - kMaximum, // Element-wise Maximum - kMin, // Reduction Min - kMinimum, // Element-wise Minimum + kReduceMax, // Reduction Max + kMaximum, // Element-wise Maximum + kMin, // Reduction Min + kMinimum, // Element-wise Minimum kMatMul, kMerge, kNeg, @@ -1229,6 +1230,19 @@ struct SubOperator : Operator { // TensorFlow equivalent: Sum struct TensorFlowSumOperator : Operator { TensorFlowSumOperator() : Operator(OperatorType::kSum) {} + std::vector<int> axis; + bool keep_dims = false; +}; + +// Prod reduction: computes the product of all of entries across the axes. +// +// Inputs: +// inputs[0]: required: the input array +// +// TensorFlow equivalent: Prod +struct TensorFlowProdOperator : Operator { + TensorFlowProdOperator() : Operator(OperatorType::kReduceProd) {} + std::vector<int> axis; bool keep_dims = false; }; @@ -1388,16 +1402,15 @@ struct TensorFlowNotEqualOperator : Operator { TensorFlowNotEqualOperator() : Operator(OperatorType::kNotEqual) {} }; -// Global max reduction: computes the max of all of entries in the input array. -// Thus the output is "0-dimensional": it consists of a single scalar value. +// Max reduction: computes the max of all of entries across the axes. // // Inputs: // inputs[0]: required: the input array // -// TensorFlow equivalent: Max --- except that we only support the special case -// of global reduction across all dimensions. +// TensorFlow equivalent: Max struct TensorFlowMaxOperator : Operator { - TensorFlowMaxOperator() : Operator(OperatorType::kMax) {} + TensorFlowMaxOperator() : Operator(OperatorType::kReduceMax) {} + std::vector<int> axis; bool keep_dims = false; }; diff --git a/tensorflow/contrib/lite/toco/tflite/operator.cc b/tensorflow/contrib/lite/toco/tflite/operator.cc index a791e60f91..68d13586f1 100644 --- a/tensorflow/contrib/lite/toco/tflite/operator.cc +++ b/tensorflow/contrib/lite/toco/tflite/operator.cc @@ -767,6 +767,44 @@ class Sum int GetVersion(const Operator& op) const override { return 1; } }; +class ReduceMax + : public BuiltinOperator<TensorFlowSumOperator, ::tflite::ReducerOptions, + ::tflite::BuiltinOptions_ReducerOptions> { + public: + using BuiltinOperator::BuiltinOperator; + flatbuffers::Offset<TfLiteOptions> WriteOptions( + const TocoOperator& op, + flatbuffers::FlatBufferBuilder* builder) const override { + return ::tflite::CreateReducerOptions(*builder, op.keep_dims); + } + + void ReadOptions(const TfLiteOptions& options, + TocoOperator* op) const override { + op->keep_dims = options.keep_dims(); + } + + int GetVersion(const Operator& op) const override { return 1; } +}; + +class ReduceProd + : public BuiltinOperator<TensorFlowSumOperator, ::tflite::ReducerOptions, + ::tflite::BuiltinOptions_ReducerOptions> { + public: + using BuiltinOperator::BuiltinOperator; + flatbuffers::Offset<TfLiteOptions> WriteOptions( + const TocoOperator& op, + flatbuffers::FlatBufferBuilder* builder) const override { + return ::tflite::CreateReducerOptions(*builder, op.keep_dims); + } + + void ReadOptions(const TfLiteOptions& options, + TocoOperator* op) const override { + op->keep_dims = options.keep_dims(); + } + + int GetVersion(const Operator& op) const override { return 1; } +}; + class ResizeBilinear : public BuiltinOperator<ResizeBilinearOperator, ::tflite::ResizeBilinearOptions, @@ -1183,6 +1221,10 @@ std::vector<std::unique_ptr<BaseOperator>> BuildOperatorList() { ops.emplace_back( new Mean(::tflite::BuiltinOperator_MEAN, OperatorType::kMean)); ops.emplace_back(new Sum(::tflite::BuiltinOperator_SUM, OperatorType::kSum)); + ops.emplace_back(new ReduceProd(::tflite::BuiltinOperator_REDUCE_PROD, + OperatorType::kReduceProd)); + ops.emplace_back(new ReduceMax(::tflite::BuiltinOperator_REDUCE_MAX, + OperatorType::kReduceMax)); ops.emplace_back(new ResizeBilinear(::tflite::BuiltinOperator_RESIZE_BILINEAR, OperatorType::kResizeBilinear)); ops.emplace_back( diff --git a/tensorflow/contrib/lite/toco/toco_tooling.cc b/tensorflow/contrib/lite/toco/toco_tooling.cc index a4dc1bbe93..7a0d9608cc 100644 --- a/tensorflow/contrib/lite/toco/toco_tooling.cc +++ b/tensorflow/contrib/lite/toco/toco_tooling.cc @@ -113,7 +113,7 @@ void MakeGeneralGraphTransformationsSet( transformations->Add(new ResolvePadV2Attributes); transformations->Add(new ResolveStridedSliceAttributes); transformations->Add(new ResolveSliceAttributes); - transformations->Add(new ResolveMeanAttributes); + transformations->Add(new ResolveReduceAttributes); transformations->Add(new ResolveConstantShapeOrRank); transformations->Add(new MakeInitialDequantizeOperator); transformations->Add(new UnpartitionEmbeddingLookup); diff --git a/tensorflow/contrib/lite/toco/tooling_util.cc b/tensorflow/contrib/lite/toco/tooling_util.cc index 4ec74e351f..45cd10ec7b 100644 --- a/tensorflow/contrib/lite/toco/tooling_util.cc +++ b/tensorflow/contrib/lite/toco/tooling_util.cc @@ -350,7 +350,7 @@ const char* OperatorTypeName(OperatorType type) { HANDLE_OPERATORTYPENAME_CASE(Less) HANDLE_OPERATORTYPENAME_CASE(LessEqual) HANDLE_OPERATORTYPENAME_CASE(MatMul) - HANDLE_OPERATORTYPENAME_CASE(Max) // Reduction Max + HANDLE_OPERATORTYPENAME_CASE(ReduceMax) // Reduction Max HANDLE_OPERATORTYPENAME_CASE(Maximum) // Element-wise Maximum HANDLE_OPERATORTYPENAME_CASE(Merge) HANDLE_OPERATORTYPENAME_CASE(Min) // Reduction Min @@ -385,6 +385,7 @@ const char* OperatorTypeName(OperatorType type) { HANDLE_OPERATORTYPENAME_CASE(SpaceToBatchND) HANDLE_OPERATORTYPENAME_CASE(BatchToSpaceND) HANDLE_OPERATORTYPENAME_CASE(Mean) + HANDLE_OPERATORTYPENAME_CASE(ReduceProd) HANDLE_OPERATORTYPENAME_CASE(Svdf) HANDLE_OPERATORTYPENAME_CASE(ArgMax) HANDLE_OPERATORTYPENAME_CASE(ArgMin) |