diff --git a/compiler/mio-tflite2121/CMakeLists.txt b/compiler/mio-tflite2121/CMakeLists.txt new file mode 100644 index 00000000000..1ca8e75816a --- /dev/null +++ b/compiler/mio-tflite2121/CMakeLists.txt @@ -0,0 +1,60 @@ +nnas_find_package(FlatBuffers EXACT 2.0 QUIET) + +if(NOT FlatBuffers_FOUND) + message(STATUS "Build mio-tflite2121: FAILED (missing Flatbuffers 2.0)") + return() +endif(NOT FlatBuffers_FOUND) + +nnas_find_package(TensorFlowSource EXACT 2.12.1 QUIET) + +if(NOT TensorFlowSource_FOUND) + message(STATUS "Build mio-tflite2121: FAILED (missing TensorFlowSource 2.12.1)") + return() +endif(NOT TensorFlowSource_FOUND) + +message(STATUS "Build mio-tflite2121: TRUE") +message(STATUS "Build mio-tflite2121: with ${TensorFlowSource_DIR}") + +set(SCHEMA_FILE "${TensorFlowSource_DIR}/tensorflow/lite/schema/schema.fbs") + +# NOTE Use copy of schema.fbs as to provide unified way for circle also +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" + COMMAND ${CMAKE_COMMAND} -E copy "${SCHEMA_FILE}" schema.fbs + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS "${SCHEMA_FILE}" +) + +FlatBuffers_Target(mio_tflite2121 + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen/mio/tflite" + INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" + SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}" + SCHEMA_FILES "schema.fbs" +) + +add_executable(mio_tflite2121_example example.cpp) +target_link_libraries(mio_tflite2121_example mio_tflite2121) + +# Temporay tflite validation tool to replace nnkit-tflite +# TODO provide full tflite validation with runtime/interpreter +add_executable(mio_tflite2121_validate example.cpp) +target_link_libraries(mio_tflite2121_validate mio_tflite2121) + +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(mio_tflite2121_helper STATIC ${SOURCES}) +target_include_directories(mio_tflite2121_helper PRIVATE src) +target_include_directories(mio_tflite2121_helper PUBLIC include) +target_link_libraries(mio_tflite2121_helper mio_tflite2121) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(mio_tflite2121_helper_test ${TESTS}) +target_include_directories(mio_tflite2121_helper_test PRIVATE src) +target_link_libraries(mio_tflite2121_helper_test mio_tflite2121) +target_link_libraries(mio_tflite2121_helper_test mio_tflite2121_helper) diff --git a/compiler/mio-tflite2121/README.md b/compiler/mio-tflite2121/README.md new file mode 100644 index 00000000000..a922f304b09 --- /dev/null +++ b/compiler/mio-tflite2121/README.md @@ -0,0 +1,3 @@ +# mio-tflite2121 + +_mio-tflite2121_ provides a library to access TensorFlow lite model files with V2.12.1. diff --git a/compiler/mio-tflite2121/example.cpp b/compiler/mio-tflite2121/example.cpp new file mode 100644 index 00000000000..54fe9e79938 --- /dev/null +++ b/compiler/mio-tflite2121/example.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 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. + */ + +// +// This example shows how to include and use "mio-tflite2121" +// +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + std::ifstream ifs(argv[1], std::ios_base::binary); + std::vector buf(std::istreambuf_iterator{ifs}, std::istreambuf_iterator{}); + + flatbuffers::Verifier verifier{reinterpret_cast(buf.data()), buf.size()}; + + if (!tflite::VerifyModelBuffer(verifier)) + { + std::cout << "Fail" << std::endl; + return 255; + } + + std::cout << "Pass" << std::endl; + return 0; +} diff --git a/compiler/mio-tflite2121/include/mio_tflite2121/Helper.h b/compiler/mio-tflite2121/include/mio_tflite2121/Helper.h new file mode 100644 index 00000000000..f2062600af7 --- /dev/null +++ b/compiler/mio-tflite2121/include/mio_tflite2121/Helper.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 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 __MIO_TFLITE2121_HELPER_H__ +#define __MIO_TFLITE2121_HELPER_H__ + +#include + +namespace mio +{ +namespace tflite +{ + +::tflite::BuiltinOperator builtin_code_neutral(const ::tflite::OperatorCode *opcode); +bool is_valid(const ::tflite::OperatorCode *opcode); +bool is_custom(const ::tflite::OperatorCode *opcode); +std::string opcode_name(const ::tflite::OperatorCode *opcode); +const char *tensor_type(const ::tflite::Tensor *tensor); +const char *tensor_name(const ::tflite::Tensor *tensor); + +} // namespace tflite +} // namespace mio + +#endif // __MIO_TFLITE2121_HELPER_H__ diff --git a/compiler/mio-tflite2121/src/Helper.cpp b/compiler/mio-tflite2121/src/Helper.cpp new file mode 100644 index 00000000000..b0d1ba1075c --- /dev/null +++ b/compiler/mio-tflite2121/src/Helper.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. 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 "mio_tflite2121/Helper.h" + +#include + +namespace mio +{ +namespace tflite +{ + +/** + * This will provide v3/v3a format neutral BuiltinOperator + * + * This function referenced + * https://github.com/tensorflow/tensorflow/blob/7d12007d7800d3714a02e05059f3ea602d1aec78/tensorflow/lite/schema/schema_utils.cc + */ +::tflite::BuiltinOperator builtin_code_neutral(const ::tflite::OperatorCode *opcode) +{ + assert(opcode != nullptr); + return std::max(opcode->builtin_code(), + static_cast<::tflite::BuiltinOperator>(opcode->deprecated_builtin_code())); +} + +bool is_valid(const ::tflite::OperatorCode *opcode) +{ + // Valid Range : 0 <= deprecated_builtin_code <= 127 + const int8_t deprecated_builtin_code = opcode->deprecated_builtin_code(); + if (deprecated_builtin_code < 0) + return false; + + const ::tflite::BuiltinOperator builtin_code = opcode->builtin_code(); + if (!(::tflite::BuiltinOperator_MIN <= builtin_code && + builtin_code <= ::tflite::BuiltinOperator_MAX)) + return false; + + return true; +} + +bool is_custom(const ::tflite::OperatorCode *opcode) +{ + ::tflite::BuiltinOperator code = builtin_code_neutral(opcode); + return (code == ::tflite::BuiltinOperator_CUSTOM); +} + +std::string opcode_name(const ::tflite::OperatorCode *opcode) +{ + assert(opcode); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid)"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (!opcode->custom_code()) + return "(invalid custom)"; + + std::string custom_op = "CUSTOM("; + custom_op += opcode->custom_code()->c_str(); + custom_op += ")"; + return custom_op; + } + + ::tflite::BuiltinOperator code = builtin_code_neutral(opcode); + return ::tflite::EnumNameBuiltinOperator(code); +} + +const char *tensor_type(const ::tflite::Tensor *tensor) +{ + return ::tflite::EnumNameTensorType(tensor->type()); +} + +const char *tensor_name(const ::tflite::Tensor *tensor) +{ + static const char *kEmptyTensorName = "(noname)"; + + auto name = tensor->name(); + if (name) + return name->c_str(); + + return kEmptyTensorName; +} + +} // namespace tflite +} // namespace mio diff --git a/compiler/mio-tflite2121/src/Helper.test.cpp b/compiler/mio-tflite2121/src/Helper.test.cpp new file mode 100644 index 00000000000..1527a49560c --- /dev/null +++ b/compiler/mio-tflite2121/src/Helper.test.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2023 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 "mio_tflite2121/Helper.h" + +#include +#include + +#include + +class mio_tflite2121_helper_test : public ::testing::Test +{ +protected: + void initialization_finish(void) + { + _fbb.Finish(tflite::CreateModelDirect(_fbb, 0, &_opcodes_vec)); + } + +protected: + void add_operator_code(int8_t deprecated_builtin_code, const char *custom_code, + tflite::BuiltinOperator builtin_code) + { + _opcodes_vec.push_back(tflite::CreateOperatorCodeDirect( + _fbb, deprecated_builtin_code, custom_code, 1 /* version */, builtin_code)); + } + + const tflite::OperatorCode *get_operator_code(uint8_t idx) + { + return tflite::GetModel(_fbb.GetBufferPointer())->operator_codes()->Get(idx); + } + +private: + flatbuffers::FlatBufferBuilder _fbb; + std::vector> _opcodes_vec; +}; + +/** + * Extended 'builtin_code' is not in TFLite schema v3. + * + * Thus it is filled with 0(BuiltinOperator_ADD) in schame v3. Please refer to + * https://github.com/tensorflow/tensorflow/blob/1ab788fa8d08430be239ab970980b891ad7af494/tensorflow/lite/schema/schema_utils.cc#L28-L31 + */ +TEST_F(mio_tflite2121_helper_test, v3) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CONV_2D = 3 + add_operator_code(3, "", tflite::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CONV_2D); + ASSERT_FALSE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3_custom) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CUSTOM = 32 + add_operator_code(32, "custom", tflite::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CUSTOM); + ASSERT_TRUE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3_NEG) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", tflite::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_FALSE(mio::tflite::is_valid(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_under127) +{ + // BuiltinOperator_CONV_2D = 3 + add_operator_code(3, "", tflite::BuiltinOperator_CONV_2D); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CONV_2D); + ASSERT_FALSE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_under127_NEG) +{ + // BuiltinOperator_CONV_2D = 3 + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", tflite::BuiltinOperator_CONV_2D); + initialization_finish(); + + ASSERT_FALSE(mio::tflite::is_valid(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_custom) +{ + // BuiltinOperator_CUSTOM = 32 + add_operator_code(32, "custom", tflite::BuiltinOperator_CUSTOM); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CUSTOM); + ASSERT_TRUE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_custom_NEG) +{ + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "custom", tflite::BuiltinOperator_CUSTOM); + initialization_finish(); + + ASSERT_FALSE(mio::tflite::is_valid(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_over127) +{ + // BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES = 127 + // BuiltinOperator_CUMSUM = 128 + add_operator_code(127, "", tflite::BuiltinOperator_CUMSUM); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CUMSUM); + ASSERT_FALSE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_over127_NEG) +{ + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", tflite::BuiltinOperator_CUMSUM); + initialization_finish(); + + ASSERT_FALSE(mio::tflite::is_valid(get_operator_code(0))); +}