From 12d88feea8573f8490629cf62fc342b152e57d65 Mon Sep 17 00:00:00 2001 From: Chunseok Lee Date: Mon, 14 Dec 2020 14:43:04 +0900 Subject: Imported Upstream version 1.11.0 --- compiler/tfinfo-v2/CMakeLists.txt | 36 +++ compiler/tfinfo-v2/README.md | 1 + .../tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h | 43 ++++ .../tfinfo-v2/include/tfinfo-v2/TensorSignature.h | 124 +++++++++++ compiler/tfinfo-v2/proto/tfinfo-v2.proto | 46 ++++ compiler/tfinfo-v2/requires.cmake | 2 + compiler/tfinfo-v2/src/TFInfo_v2.test.cpp | 246 +++++++++++++++++++++ compiler/tfinfo-v2/src/TensorInfoLoader.cpp | 179 +++++++++++++++ compiler/tfinfo-v2/src/TensorSignature.cpp | 17 ++ 9 files changed, 694 insertions(+) create mode 100644 compiler/tfinfo-v2/CMakeLists.txt create mode 100644 compiler/tfinfo-v2/README.md create mode 100644 compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h create mode 100644 compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h create mode 100644 compiler/tfinfo-v2/proto/tfinfo-v2.proto create mode 100644 compiler/tfinfo-v2/requires.cmake create mode 100644 compiler/tfinfo-v2/src/TFInfo_v2.test.cpp create mode 100644 compiler/tfinfo-v2/src/TensorInfoLoader.cpp create mode 100644 compiler/tfinfo-v2/src/TensorSignature.cpp (limited to 'compiler/tfinfo-v2') diff --git a/compiler/tfinfo-v2/CMakeLists.txt b/compiler/tfinfo-v2/CMakeLists.txt new file mode 100644 index 000000000..cf438ea29 --- /dev/null +++ b/compiler/tfinfo-v2/CMakeLists.txt @@ -0,0 +1,36 @@ +nnas_find_package(Protobuf QUIET) + +if(NOT Protobuf_FOUND) + return() +endif(NOT Protobuf_FOUND) + +# generating and building schema +Protobuf_Generate(TFINFO_PROTO + "${CMAKE_CURRENT_BINARY_DIR}/generated" + "./proto" + tfinfo-v2.proto) + +add_library(tfinfo_v2_proto STATIC ${TFINFO_PROTO_SOURCES}) +set_target_properties(tfinfo_v2_proto PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(tfinfo_v2_proto PUBLIC ${TFINFO_PROTO_INCLUDE_DIRS}) +target_link_libraries(tfinfo_v2_proto PUBLIC libprotobuf) + +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(tfinfo_v2 STATIC ${SOURCES}) +set_target_properties(tfinfo_v2 PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(tfinfo_v2 PUBLIC include) +target_link_libraries(tfinfo_v2 PRIVATE tfinfo_v2_proto) +target_link_libraries(tfinfo_v2 PRIVATE oops) +target_link_libraries(tfinfo_v2 PRIVATE stdex) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(tfinfo_v2_test ${TESTS}) +target_link_libraries(tfinfo_v2_test tfinfo_v2) diff --git a/compiler/tfinfo-v2/README.md b/compiler/tfinfo-v2/README.md new file mode 100644 index 000000000..72fbdb06a --- /dev/null +++ b/compiler/tfinfo-v2/README.md @@ -0,0 +1 @@ +# tfinfo-v2 diff --git a/compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h b/compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h new file mode 100644 index 000000000..ee3348e85 --- /dev/null +++ b/compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 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 __TFINFO_TENSOR_INFO_LOADER_H__ +#define __TFINFO_TENSOR_INFO_LOADER_H__ + +#include "TensorSignature.h" + +#include +#include + +namespace tfinfo +{ +inline namespace v2 +{ + +/** + * @brief Function to create TensorSignatures defined in info file + */ +TensorSignatures load(const char *info_path); + +/** + * @brief Function to create TensorSignatures from stream + */ +TensorSignatures load(std::istream *stream, const char *path_for_error_msg); + +} // namespace v2 +} // namespace tfinfo + +#endif // __TFINFO_TENSOR_INFO_LOADER_H__ diff --git a/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h b/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h new file mode 100644 index 000000000..f26d0354a --- /dev/null +++ b/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020 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 __TFINFO_V2_TENSORSIGNATURE_H__ +#define __TFINFO_V2_TENSORSIGNATURE_H__ + +#include +#include +#include +#include +#include + +namespace tfinfo +{ +inline namespace v2 +{ + +/** + * @brief Supported Data Types + */ +enum class DataType +{ + UNKNOWN, + + FLOAT32, // IEEE 32-bit floating point + /* To be added */ +}; + +/** + * @brief Class to represent axis and size of dims. + * User should enter axis and size of dim(s) when input tensor(s) contain(s) unknown dim(s). + * Such axis and size of dim(s) will be stored in ShapeHint. + */ +class ShapeHint +{ + using AxisHint = uint32_t; + using SizeHint = uint64_t; + +public: + ShapeHint() = default; + + void add(AxisHint axis, SizeHint size) + { + if (_dims.find(axis) != _dims.end()) + throw std::runtime_error("dim value already exists"); + + _dims[axis] = size; + } + + std::map::const_iterator cbegin() const { return _dims.cbegin(); } + + std::map::const_iterator cend() const { return _dims.cend(); } + + bool empty() { return _dims.size() == 0; } + + size_t size() { return _dims.size(); } + +private: + std::map _dims; +}; + +using TensorName = std::string; + +/** + * @brief Class to store input and output tensor information + */ +class TensorSignature final +{ +public: + enum class Kind + { + Input, + Output + }; + + TensorSignature(const Kind kind, const std::string &name) : _kind(kind), _tensor_name() + { + // tensor name can be a form of "placeholder:0" or "placeholder". + // If tensor index is omitted, ":0" is appended + auto pos = name.find(":"); + if (pos == std::string::npos) + _tensor_name.assign(name + ":0"); + else + _tensor_name.assign(name); + } + + TensorSignature(const Kind kind, const std::string &name, const ShapeHint &shape_hint) + : TensorSignature(kind, name) + { + _shape_hint = shape_hint; + } + +public: + Kind kind() const { return _kind; } + + const TensorName &name() { return _tensor_name; } + + ShapeHint &shapeHint() { return _shape_hint; } + +private: + Kind _kind; + std::string _tensor_name; + ShapeHint _shape_hint; +}; + +using TensorSignatures = std::vector>; + +} // namespace v2 +} // namespace tfinfo + +#endif // __TFINFO_V2_TENSORSIGNATURE_H__ diff --git a/compiler/tfinfo-v2/proto/tfinfo-v2.proto b/compiler/tfinfo-v2/proto/tfinfo-v2.proto new file mode 100644 index 000000000..4f7c47b4a --- /dev/null +++ b/compiler/tfinfo-v2/proto/tfinfo-v2.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +package tfinfo_v2_proto; +option cc_enable_arenas = true; + +/* +Example of prototxt file is as follows: + +input { + name : "placeholder:0" +} +output { + name : "relu:0" +} + +When a model has unknown dim in its input, +value of all unknowm dims must be provided like the following: + +input { + name : "placeholder:0" + dim { axis: 0 size: 8 } + dim { axis: 3 size: 4 } +} +output { + name : "relu:0" +} +*/ + +message Dim { + int32 axis = 1; + int64 size = 2; // tensorflow uses int64 +} + +message OutputDef { + string name = 1; +} + +message InputDef { + string name = 1; + repeated Dim dim = 2; +} + +message InfoDef { + repeated InputDef input = 1; + repeated OutputDef output = 2; +} diff --git a/compiler/tfinfo-v2/requires.cmake b/compiler/tfinfo-v2/requires.cmake new file mode 100644 index 000000000..e7efab4fb --- /dev/null +++ b/compiler/tfinfo-v2/requires.cmake @@ -0,0 +1,2 @@ +require("oops") +require("stdex") diff --git a/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp b/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp new file mode 100644 index 000000000..02a2d9199 --- /dev/null +++ b/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020 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 "tfinfo-v2/TensorInfoLoader.h" + +#include "tfinfo-v2/TensorSignature.h" + +#include + +#include +#include + +#define TC_CASE(content) #content + +using namespace tfinfo::v2; + +namespace +{ + +// clang-format off +const std::vector success_cases = +{ + TC_CASE( + output { + name : "relu:0" + } + ), + + TC_CASE( + input { + name : "placeholder:0" + } + + input { + name : "placeholder:1" + dim { axis:0 size: 1 } + dim { axis:2 size: 4 } + } + + output { + name : "relu:0" + } + ), + // clang-format on +}; + +} // namespace + +TEST(TFINFO_V2, success_0) +{ + std::stringstream ss{success_cases[0]}; + + auto tensors = load(&ss, "tfinfo_v2_test"); + + std::map m; + + for (auto &tensor : tensors) + { + m[tensor->name()] = tensor.get(); + } + + ASSERT_EQ(m.size(), 1); + + auto t1 = m["relu:0"]; + { + ASSERT_EQ(t1->kind(), tfinfo::v2::TensorSignature::Kind::Output); + ASSERT_TRUE(t1->shapeHint().empty()); + } +} + +TEST(TFINFO_V2, success_1) +{ + std::stringstream ss{success_cases[1]}; + + auto tensors = load(&ss, "tfinfo_v2_test"); + + std::map m; + + for (auto &tensor : tensors) + { + m[tensor->name()] = tensor.get(); + } + + ASSERT_EQ(m.size(), 3); + + auto t1 = m["placeholder:0"]; + { + ASSERT_EQ(t1->kind(), tfinfo::v2::TensorSignature::Kind::Input); + ASSERT_TRUE(t1->shapeHint().empty()); + } + + auto t2 = m["placeholder:1"]; + { + ASSERT_EQ(t2->kind(), tfinfo::v2::TensorSignature::Kind::Input); + ASSERT_FALSE(t2->shapeHint().empty()); + + auto iter = t2->shapeHint().cbegin(); + + ASSERT_TRUE(iter != t2->shapeHint().cend()); + ASSERT_EQ(iter->first, 0); // axis + ASSERT_EQ(iter->second, 1); // size + + iter++; + + ASSERT_TRUE(iter != t2->shapeHint().cend()); + ASSERT_EQ(iter->first, 2); // axis + ASSERT_EQ(iter->second, 4); // size + + iter++; + + ASSERT_TRUE(iter == t2->shapeHint().cend()); + } + + auto t3 = m["relu:0"]; + { + ASSERT_EQ(t3->kind(), tfinfo::v2::TensorSignature::Kind::Output); + ASSERT_TRUE(t3->shapeHint().empty()); + } +} + +namespace +{ + +// clang-format off +const std::vector fail_cases = + { + // no output + TC_CASE( + input { + name : "relu:0" + } + ), + + // no name in input + TC_CASE( + input { + shape { + dim { size: 1 } + dim { size: 2 } + } + } + output { + name : "relu:0" + } + ), + + // wrong name format - no tensor index + TC_CASE( + output { + name : "name_with_no_index" + } + ), + + // wrong name format - no name but numbers + TC_CASE( + output { + name : "1" + } + ), + + // duplicated node def - input, input + TC_CASE( + input { + name : "duplicated_name:0" + } + + input { + name : "duplicated_name:0" + } + ), + + // duplicated node def - input, output + TC_CASE( + input { + name : "duplicated_name:0" + } + + output { + name : "duplicated_name:0" + } + ), + + // wrong keyword ('in', 'out' instead of 'input', 'output') + TC_CASE( + in { + name : "a:0" + } + + out { + name : "b:0" + } + ), + + // wrong keyword ('input_name' instead of 'name') + TC_CASE( + input { + input_name : "a:0" + } + + output { + name : "b:0" + } + ), + + // using deprecated format + // (note that because of ",", macro TC_CASE cannot be used.) + R"( + input, a:0, TF_FLOAT, [2, 3 ,4] + output, b:0, TF_FLOAT, [2, 3 ,4] + )", + // clang-format on +}; + +} // namespace + +TEST(TFINFO_V2, failure) +{ + for (int i = 0; i < fail_cases.size(); i++) + { + std::stringstream ss{fail_cases[i]}; + + try + { + load(&ss, "tfinfo_v2_test"); + + FAIL(); + } + catch (const std::exception &e) + { + std::cerr << ss.str() << std::endl << e.what() << '\n'; + } + } +} diff --git a/compiler/tfinfo-v2/src/TensorInfoLoader.cpp b/compiler/tfinfo-v2/src/TensorInfoLoader.cpp new file mode 100644 index 000000000..0bf828773 --- /dev/null +++ b/compiler/tfinfo-v2/src/TensorInfoLoader.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2020 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 "tfinfo-v2/TensorInfoLoader.h" + +#include "tfinfo-v2/TensorSignature.h" + +#include +#include + +#include + +#include +#include + +#include +#include + +namespace +{ + +// for testing purpose +bool load_text(std::istream *stream, tfinfo_v2_proto::InfoDef &info_def) +{ + google::protobuf::io::IstreamInputStream iis(stream); + + return google::protobuf::TextFormat::Parse(&iis, &info_def); +} + +bool is_num(const std::string &num) +{ + for (int i = 0; i < num.length(); i++) + if (!isdigit(num[i])) + return false; + + return true; +} + +void validate_tensor_name(const std::string &tensor_name, const char *path) +{ + // Note that Tensorflow tensor name format is + // operation name ":" index, e.g., "in/placeholder:0" + int pos = tensor_name.find(":"); + if (pos == std::string::npos) + throw oops::UserExn("Missing index separator, ':'", "name", tensor_name, "file", path); + + if (tensor_name.length() == pos + 1) // ':' is the last char + throw oops::UserExn("Missing tensor index after ':'", "name", tensor_name, "file", path); + + // 1. Validating operation name. + // for naming format, refer to https://www.tensorflow.org/api_docs/python/tf/Operation#__init__ + // First char is in the form of "[A-Za-z0-9.]" + // and the rest chars are in the form of "[A-Za-z0-9_.\\-/]*" + std::string op_name = tensor_name.substr(0, pos); + + // first character + if (!(isalnum(op_name[0]) || op_name[0] == '.')) + throw oops::UserExn("Wrong tensor name format", "name", tensor_name, "file", path); + + // and the rest chars + for (int i = 1; i < op_name.length(); i++) + if (!(isalnum(op_name[i]) || std::string("_.\\-/").find(op_name[i]) != std::string::npos)) + throw oops::UserExn("Wrong tensor name format", "name", tensor_name, "file", path); + + // 2. validating index after ":" + std::string index = tensor_name.substr(pos + 1, op_name.length() - pos - 1); + + if (!is_num(index)) + throw oops::UserExn("Wrong tensor name format", "name", tensor_name, "file", path); +} + +void check_duplicate(tfinfo::v2::TensorSignatures &tensors, const char *path) +{ + std::map tool; + for (auto &tensor : tensors) + { + if (tool.find(tensor->name()) != tool.end()) + throw oops::UserExn("Duplicate tensor definition", "name", tensor->name(), "file", path); + else + tool[tensor->name()] = true; + } +} + +void convert(tfinfo_v2_proto::InfoDef &info_def, tfinfo::v2::TensorSignatures &tensors, + const char *path) +{ + // processing input. Note that there could be no input. + if (auto input_size = info_def.input_size()) + { + for (int i = 0; i < input_size; i++) + { + auto input_def = info_def.input().Get(i); + + auto name = input_def.name(); + validate_tensor_name(name, path); + + auto tensor = stdex::make_unique( + tfinfo::v2::TensorSignature::Kind::Input, name); + + // when there is dim attribute for unknown shape + if (input_def.dim_size() > 0) + { + for (int d = 0; d < input_def.dim().size(); d++) + { + auto dim = input_def.dim(d); + tensor->shapeHint().add(dim.axis(), dim.size()); + } + } + + tensors.emplace_back(std::move(tensor)); + } + } + + // processing output + auto output_size = info_def.output_size(); + if (output_size == 0) + throw oops::UserExn("Missing output node. At least 1 output node must exist", "file", path); + + if (auto output_node_size = info_def.output_size()) + { + for (int i = 0; i < output_node_size; i++) + { + auto name = info_def.output().Get(i).name(); + validate_tensor_name(name, path); + + auto tensor = stdex::make_unique( + tfinfo::v2::TensorSignature::Kind::Output, name); + tensors.emplace_back(std::move(tensor)); + } + } + + check_duplicate(tensors, path); +} + +} // namespace + +namespace tfinfo +{ +inline namespace v2 +{ + +TensorSignatures load(const char *path) +{ + std::ifstream stream(path, std::ios::in | std::ios::binary); + + return load(&stream, path); +} + +TensorSignatures load(std::istream *stream, const char *path_for_error_msg) +{ + tfinfo_v2_proto::InfoDef info_def; + + if (!load_text(stream, info_def)) + { + throw oops::UserExn("Cannot parse the info file", "path", path_for_error_msg); + } + + TensorSignatures tensors; + + convert(info_def, tensors, path_for_error_msg); + + return tensors; +} + +} // namespace v2 +} // namespace tfinfo diff --git a/compiler/tfinfo-v2/src/TensorSignature.cpp b/compiler/tfinfo-v2/src/TensorSignature.cpp new file mode 100644 index 000000000..3107f33db --- /dev/null +++ b/compiler/tfinfo-v2/src/TensorSignature.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2020 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 "tfinfo-v2/TensorSignature.h" -- cgit v1.2.3