diff options
Diffstat (limited to 'libs/support')
-rw-r--r-- | libs/support/CMakeLists.txt | 2 | ||||
-rw-r--r-- | libs/support/nnapi/CMakeLists.txt | 6 | ||||
-rw-r--r-- | libs/support/nnapi/src/feature/Utils.cpp | 43 | ||||
-rw-r--r-- | libs/support/tflite/CMakeLists.txt | 10 | ||||
-rw-r--r-- | libs/support/tflite/src/Diff.cpp | 262 | ||||
-rw-r--r-- | libs/support/tflite/src/FeatureView.cpp | 76 | ||||
-rw-r--r-- | libs/support/tflite/src/TensorView.cpp | 69 | ||||
-rw-r--r-- | libs/support/tflite/src/TensorView.test.cpp | 36 | ||||
-rw-r--r-- | libs/support/tflite/src/interp/FlatBufferBuilder.cpp | 46 | ||||
-rw-r--r-- | libs/support/tflite/src/interp/FunctionBuilder.cpp | 40 |
10 files changed, 590 insertions, 0 deletions
diff --git a/libs/support/CMakeLists.txt b/libs/support/CMakeLists.txt new file mode 100644 index 000000000..c91677266 --- /dev/null +++ b/libs/support/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(tflite) +add_subdirectory(nnapi) diff --git a/libs/support/nnapi/CMakeLists.txt b/libs/support/nnapi/CMakeLists.txt new file mode 100644 index 000000000..cd1f365cf --- /dev/null +++ b/libs/support/nnapi/CMakeLists.txt @@ -0,0 +1,6 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_library(nnfw_support_nnapi ${SOURCES}) +set_property(TARGET nnfw_support_nnapi PROPERTY POSITION_INDEPENDENT_CODE ON) +target_include_directories(nnfw_support_nnapi PUBLIC ${CMAKE_SOURCE_DIR}/include) +target_link_libraries(nnfw_support_nnapi nnfw_util) diff --git a/libs/support/nnapi/src/feature/Utils.cpp b/libs/support/nnapi/src/feature/Utils.cpp new file mode 100644 index 000000000..62939ff4a --- /dev/null +++ b/libs/support/nnapi/src/feature/Utils.cpp @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#include "support/nnapi/feature/Utils.h" + +namespace nnfw +{ +namespace support +{ +namespace nnapi +{ +namespace feature +{ + +uint32_t indexOf(const nnfw::util::feature::Shape &shape, uint32_t ch, uint32_t row, uint32_t col) +{ + uint32_t res = 0; + + // NNAPI assumes that NHWC ordering for feature map + res += row * shape.W * shape.C; + res += col * shape.C; + res += ch; + + return res; +} + +} // namespace feature +} // namespace nnapi +} // namespace support +} // namespace nnfw diff --git a/libs/support/tflite/CMakeLists.txt b/libs/support/tflite/CMakeLists.txt new file mode 100644 index 000000000..cccc7de3d --- /dev/null +++ b/libs/support/tflite/CMakeLists.txt @@ -0,0 +1,10 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(nnfw_support_tflite ${SOURCES}) +target_include_directories(nnfw_support_tflite PUBLIC ${CMAKE_SOURCE_DIR}/include) +target_link_libraries(nnfw_support_tflite nnfw_util tensorflow-lite ${LIB_PTHREAD} dl) + +add_executable(nnfw_support_tflite_test_TensorView src/TensorView.test.cpp) +target_link_libraries(nnfw_support_tflite_test_TensorView nnfw_support_tflite) diff --git a/libs/support/tflite/src/Diff.cpp b/libs/support/tflite/src/Diff.cpp new file mode 100644 index 000000000..f382df2d6 --- /dev/null +++ b/libs/support/tflite/src/Diff.cpp @@ -0,0 +1,262 @@ +/* + * 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. + */ + +#include "support/tflite/Diff.h" + +#include "util/fp32.h" + +#include "util/tensor/IndexIterator.h" +#include "util/tensor/IndexFormatter.h" +#include "util/tensor/Zipper.h" + +#include <iostream> + +class DiffSummary : public TfLiteTensorComparator::Observer +{ +public: + DiffSummary() + : max_abs_diff_index(0), max_abs_diff_value{0.0f}, + max_rel_diff_index(0), max_rel_diff_value{0.0f} + { + // DO NOTHING + } +public: + void notify(const nnfw::util::tensor::Index &index, float expected, float obtained) override; + +public: + nnfw::util::tensor::Index max_abs_diff_index; + float max_abs_diff_expected; + float max_abs_diff_obtained; + float max_abs_diff_value; + + nnfw::util::tensor::Index max_rel_diff_index; + float max_rel_diff_expected; + float max_rel_diff_obtained; + float max_rel_diff_value; +}; + +void DiffSummary::notify(const nnfw::util::tensor::Index &index, float expected, float obtained) +{ + const auto abs_diff_value = std::fabs(expected - obtained); + + if (max_abs_diff_value < abs_diff_value) + { + max_abs_diff_index = index; + max_abs_diff_value = abs_diff_value; + max_abs_diff_expected = expected; + max_abs_diff_obtained = obtained; + } + + const auto rel_diff_value = nnfw::util::fp32::relative_diff(expected, obtained); + + if (max_rel_diff_value < rel_diff_value) + { + max_rel_diff_index = index; + max_rel_diff_value = rel_diff_value; + max_rel_diff_expected = expected; + max_rel_diff_obtained = obtained; + } +} + +std::vector<TfLiteTensorDiff> +TfLiteTensorComparator::compare(const nnfw::support::tflite::TensorView<float> &expected, + const nnfw::support::tflite::TensorView<float> &obtained, + Observer *observer) const +{ + std::vector<TfLiteTensorDiff> res; + + assert(expected.shape() == obtained.shape()); + + nnfw::util::tensor::zip(expected.shape(), expected, obtained) << + [&] (const nnfw::util::tensor::Index &index, float expected_value, float obtained_value) + { + const auto relative_diff = nnfw::util::fp32::relative_diff(expected_value, obtained_value); + + if (!_compare_fn(expected_value, obtained_value)) + { + TfLiteTensorDiff diff(index); + + diff.expected = expected_value; + diff.obtained = obtained_value; + + res.emplace_back(diff); + } + + // Update max_diff_index, if necessary + if (observer != nullptr) + { + observer->notify(index, expected_value, obtained_value); + } + }; + + return res; +} + +bool TfLiteInterpMatchApp::run(::tflite::Interpreter &interp, ::tflite::Interpreter &nnapi) const +{ + assert(interp.outputs() == nnapi.outputs()); + + for (const auto &id : interp.outputs()) + { + const auto expected = nnfw::support::tflite::TensorView<float>::make(interp, id); + const auto obtained = nnfw::support::tflite::TensorView<float>::make(nnapi, id); + + DiffSummary summary; + + auto diffs = _comparator.compare(expected, obtained, &summary); + + if (diffs.size() == 0) + { + std::cout << " Tensor #" << id << ": MATCHED" << std::endl; + } + else + { + std::cout << " Tensor #" << id << ": UNMATCHED" << std::endl; + std::cout << " " << diffs.size() << " diffs are detected" << std::endl; + } + + // Print out max_diff + if (summary.max_abs_diff_value > 0) + { + std::cout << " Max absolute diff at [" << nnfw::util::tensor::IndexFormatter(summary.max_abs_diff_index) << "]" << std::endl; + std::cout << " expected: " << summary.max_abs_diff_expected << std::endl; + std::cout << " obtained: " << summary.max_abs_diff_obtained << std::endl; + std::cout << " absolute diff: " << summary.max_abs_diff_value << std::endl; + } + + if (summary.max_rel_diff_value > 0) + { + const auto tolerance_level = summary.max_rel_diff_value / FLT_EPSILON; + + std::cout << " Max relative diff at [" << nnfw::util::tensor::IndexFormatter(summary.max_rel_diff_index) << "]" << std::endl; + std::cout << " expected: " << summary.max_rel_diff_expected << std::endl; + std::cout << " obtained: " << summary.max_rel_diff_obtained << std::endl; + std::cout << " relative diff: " << summary.max_rel_diff_value << std::endl; + std::cout << " (tolerance level = " << tolerance_level << ")" << std::endl; + } + + if (diffs.size() > 0) + { + if (_verbose != 0) + { + std::cout << " ---- Details ---" << std::endl; + for (const auto &diff : diffs) + { + const auto absolute_diff = std::fabs(diff.expected - diff.obtained); + const auto relative_diff = nnfw::util::fp32::relative_diff(diff.expected, diff.obtained); + const auto tolerance_level = relative_diff / FLT_EPSILON; + + std::cout << " Diff at [" << nnfw::util::tensor::IndexFormatter(diff.index) << "]" << std::endl; + std::cout << " expected: " << diff.expected << std::endl; + std::cout << " obtained: " << diff.obtained << std::endl; + std::cout << " absolute diff: " << absolute_diff << std::endl; + std::cout << " relative diff: " << relative_diff << std::endl; + std::cout << " (tolerance level = " << tolerance_level << ")" << std::endl; + } + } + + return false; + } + } + + return true; +} + +#include "util/tensor/Object.h" + +// +// Random Test Runner +// +int RandomTestRunner::run(const nnfw::support::tflite::interp::Builder &builder) +{ + auto pure = builder.build(); + auto nnapi = builder.build(); + + pure->UseNNAPI(false); + nnapi->UseNNAPI(true); + + // Allocate Tensors + pure->AllocateTensors(); + nnapi->AllocateTensors(); + + assert(pure->inputs() == nnapi->inputs()); + + // Fill IFM with random numbers + auto ifm_gen = [this] (const nnfw::util::tensor::Shape &, const nnfw::util::tensor::Index &) + { + // TODO Allow users to set min/max and distribution + std::normal_distribution<float> dist(0.0f, 2.0f); + return dist(_rand); + }; + + for (const auto id : pure->inputs()) + { + auto pure_view = nnfw::support::tflite::TensorView<float>::make(*pure, id); + auto nnapi_view = nnfw::support::tflite::TensorView<float>::make(*nnapi, id); + + assert(pure_view.shape() == nnapi_view.shape()); + + const nnfw::util::tensor::Object<float> data(pure_view.shape(), ifm_gen); + + assert(pure_view.shape() == data.shape()); + + nnfw::util::tensor::iterate(pure_view.shape()) << [&] (const nnfw::util::tensor::Index &ind) + { + const auto value = data.at(ind); + + pure_view.at(ind) = value; + nnapi_view.at(ind) = value; + }; + } + + std::cout << "[NNAPI TEST] Run T/F Lite Interpreter without NNAPI" << std::endl; + pure->Invoke(); + + std::cout << "[NNAPI TEST] Run T/F Lite Interpreter with NNAPI" << std::endl; + nnapi->Invoke(); + + // Compare OFM + std::cout << "[NNAPI TEST] Compare the result" << std::endl; + + const auto tolerance = _param.tolerance; + + auto equals = [tolerance] (float lhs, float rhs) + { + // NOTE Hybrid approach + // TODO Allow users to set tolerance for absolute_epsilon_equal + if (nnfw::util::fp32::absolute_epsilon_equal(lhs, rhs)) + { + return true; + } + + return nnfw::util::fp32::epsilon_equal(lhs, rhs, tolerance); + }; + + TfLiteTensorComparator comparator(equals); + TfLiteInterpMatchApp app(comparator); + + app.verbose() = _param.verbose; + + bool res = app.run(*pure, *nnapi); + + if (!res) + { + return 255; + } + + std::cout << "[NNAPI TEST] PASSED" << std::endl; + return 0; +} diff --git a/libs/support/tflite/src/FeatureView.cpp b/libs/support/tflite/src/FeatureView.cpp new file mode 100644 index 000000000..50f599d2e --- /dev/null +++ b/libs/support/tflite/src/FeatureView.cpp @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#include "support/tflite/FeatureView.h" +#include "support/tflite/TensorUtils.h" + +#include <cassert> + +namespace nnfw +{ +namespace support +{ +namespace tflite +{ + +nnfw::util::feature::Shape getFeatureShape(const TfLiteTensor *tensor) +{ + nnfw::util::feature::Shape shape; + + shape.C = tensor->dims->data[3]; + shape.H = tensor->dims->data[1]; + shape.W = tensor->dims->data[2]; + + return shape; +} + +FeatureView<float>::FeatureView(::tflite::Interpreter &interp, const InputIndex &index) +{ + const auto tensor_index = interp.inputs().at(index.asInt()); + auto tensor_ptr = interp.tensor(tensor_index); + + assert(isFloatTensor(tensor_ptr)); + assert(isFeatureTensor(tensor_ptr)); + + _shape = getFeatureShape(tensor_ptr); + _base = interp.typed_tensor<float>(tensor_index); +} + +FeatureView<float>::FeatureView(::tflite::Interpreter &interp, const OutputIndex &index) +{ + const auto tensor_index = interp.outputs().at(index.asInt()); + auto tensor_ptr = interp.tensor(tensor_index); + + assert(isFloatTensor(tensor_ptr)); + assert(isFeatureTensor(tensor_ptr)); + + _shape = getFeatureShape(tensor_ptr); + _base = interp.typed_tensor<float>(tensor_index); +} + +float FeatureView<float>::at(uint32_t ch, uint32_t row, uint32_t col) const +{ + return *(_base + getElementOffset(ch, row, col)); +} + +float &FeatureView<float>::at(uint32_t ch, uint32_t row, uint32_t col) +{ + return *(_base + getElementOffset(ch, row, col)); +} + +} // namespace tflite +} // namespace support +} // namespace nnfw diff --git a/libs/support/tflite/src/TensorView.cpp b/libs/support/tflite/src/TensorView.cpp new file mode 100644 index 000000000..9e164acc2 --- /dev/null +++ b/libs/support/tflite/src/TensorView.cpp @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#include "support/tflite/TensorView.h" + +#include <cassert> + +namespace nnfw +{ +namespace support +{ +namespace tflite +{ + +TensorView<float>::TensorView(const nnfw::util::tensor::Shape &shape, float *base) : _shape{shape}, _base{base} +{ + // Set 'stride' + _stride.init(_shape); +} + +float TensorView<float>::at(const nnfw::util::tensor::Index &index) const +{ + const auto offset = _stride.offset(index); + + return *(_base + offset); +} + +float &TensorView<float>::at(const nnfw::util::tensor::Index &index) +{ + const auto offset = _stride.offset(index); + + return *(_base + offset); +} + +TensorView<float> TensorView<float>::make(::tflite::Interpreter &interp, int tensor_index) +{ + auto tensor_ptr = interp.tensor(tensor_index); + + // TODO Enable the following assets + // assert(isFloatTensor(tensor_ptr)); + // assert(isFeatureTensor(tensor_ptr)); + + // Set 'shape' + nnfw::util::tensor::Shape shape(tensor_ptr->dims->size); + + for (uint32_t axis = 0; axis < shape.rank(); ++axis) + { + shape.dim(axis) = tensor_ptr->dims->data[axis]; + } + + return TensorView<float>(shape, interp.typed_tensor<float>(tensor_index)); +} + +} // namespace tflite +} // namespace support +} // namespace nnfw diff --git a/libs/support/tflite/src/TensorView.test.cpp b/libs/support/tflite/src/TensorView.test.cpp new file mode 100644 index 000000000..75993a6da --- /dev/null +++ b/libs/support/tflite/src/TensorView.test.cpp @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#include "support/tflite/TensorView.h" + +#include <cassert> + +int main(int argc, char **argv) +{ + float value[6] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; + + const nnfw::util::tensor::Shape shape{2, 3}; + const nnfw::support::tflite::TensorView<float> view{shape, value}; + + assert(view.at(nnfw::util::tensor::Index{0, 0}) == 1.0f); + assert(view.at(nnfw::util::tensor::Index{0, 1}) == 2.0f); + assert(view.at(nnfw::util::tensor::Index{0, 2}) == 3.0f); + assert(view.at(nnfw::util::tensor::Index{1, 0}) == 4.0f); + assert(view.at(nnfw::util::tensor::Index{1, 1}) == 5.0f); + assert(view.at(nnfw::util::tensor::Index{1, 2}) == 6.0f); + + return 0; +} diff --git a/libs/support/tflite/src/interp/FlatBufferBuilder.cpp b/libs/support/tflite/src/interp/FlatBufferBuilder.cpp new file mode 100644 index 000000000..f46c74652 --- /dev/null +++ b/libs/support/tflite/src/interp/FlatBufferBuilder.cpp @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#include "support/tflite/interp/FlatBufferBuilder.h" + +#include <tensorflow/contrib/lite/kernels/register.h> + +namespace nnfw +{ +namespace support +{ +namespace tflite +{ +namespace interp +{ + +std::unique_ptr<::tflite::Interpreter> FlatBufferBuilder::build(void) const +{ + std::unique_ptr<::tflite::Interpreter> interpreter; + + ::tflite::ops::builtin::BuiltinOpResolver resolver; + + ::tflite::InterpreterBuilder builder(_model, resolver); + + builder(&interpreter); + + return std::move(interpreter); +} + +} // namespace interp +} // namespace tflite +} // namespace support +} // namespace nnfw diff --git a/libs/support/tflite/src/interp/FunctionBuilder.cpp b/libs/support/tflite/src/interp/FunctionBuilder.cpp new file mode 100644 index 000000000..65783bd37 --- /dev/null +++ b/libs/support/tflite/src/interp/FunctionBuilder.cpp @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#include "support/tflite/interp/FunctionBuilder.h" + +namespace nnfw +{ +namespace support +{ +namespace tflite +{ +namespace interp +{ + +std::unique_ptr<::tflite::Interpreter> FunctionBuilder::build(void) const +{ + auto res = std::unique_ptr<::tflite::Interpreter>{new ::tflite::Interpreter}; + + _fn(*res); + + return std::move(res); +} + +} // namespace interp +} // namespace tflite +} // namespace support +} // namespace nnfw |