diff --git a/ngraph/frontend/onnx_import/include/onnx_import/core/tensor.hpp b/ngraph/frontend/onnx_import/include/onnx_import/core/tensor.hpp index 0f0600477b3..94ae06e0563 100644 --- a/ngraph/frontend/onnx_import/include/onnx_import/core/tensor.hpp +++ b/ngraph/frontend/onnx_import/include/onnx_import/core/tensor.hpp @@ -23,6 +23,7 @@ #include "ngraph/op/constant.hpp" #include "ngraph/shape.hpp" #include "ngraph/type/element_type.hpp" +#include "tensor_external_data.hpp" namespace ngraph { @@ -158,6 +159,30 @@ namespace ngraph return std::vector( it, it + (raw_data.size() / __get_onnx_data_size(onnx_data_type))); } + + template + inline std::vector + get_external_data(const ONNX_NAMESPACE::TensorProto& tensor) + { + const auto tensor_external_data = TensorExternalData(tensor); + const auto raw_data = tensor_external_data.load_external_data(); + + return detail::__get_raw_data(raw_data, tensor.data_type()); + } + + bool has_tensor_external_data(const ONNX_NAMESPACE::TensorProto& tensor) + { + if (tensor.has_data_location() && + tensor.data_location() == ONNX_NAMESPACE::TensorProto_DataLocation:: + TensorProto_DataLocation_EXTERNAL) + { + return true; + } + else + { + return false; + } + } } } @@ -171,6 +196,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -202,6 +231,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), tensor.data_type()); @@ -229,6 +262,10 @@ namespace ngraph inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -244,6 +281,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -259,6 +300,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -274,6 +319,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -289,6 +338,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -304,6 +357,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -319,6 +376,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -334,6 +395,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -349,6 +414,10 @@ namespace ngraph template <> inline std::vector get_data(const ONNX_NAMESPACE::TensorProto& tensor) { + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), @@ -366,6 +435,10 @@ namespace ngraph { // Boolean values are stored as char because std::vector // can behave differently from other vector containers. + if (detail::has_tensor_external_data(tensor)) + { + return detail::get_external_data(tensor); + } if (tensor.has_raw_data()) { return detail::__get_raw_data(tensor.raw_data(), tensor.data_type()); diff --git a/ngraph/frontend/onnx_import/include/onnx_import/exceptions.hpp b/ngraph/frontend/onnx_import/include/onnx_import/exceptions.hpp index f61a83469da..b74112e7f9d 100644 --- a/ngraph/frontend/onnx_import/include/onnx_import/exceptions.hpp +++ b/ngraph/frontend/onnx_import/include/onnx_import/exceptions.hpp @@ -21,6 +21,7 @@ #include "ngraph/check.hpp" #include "ngraph/except.hpp" #include "onnx_import/core/node.hpp" +#include "onnx_import/utils/tensor_external_data.hpp" namespace ngraph { @@ -44,6 +45,15 @@ namespace ngraph } }; + struct invalid_external_data : ngraph_error + { + invalid_external_data(const onnx_import::detail::TensorExternalData& external_data) + : ngraph_error{std::string{"invalid external data: "} + + external_data.to_string()} + { + } + }; + } // namespace error } // namespace onnx_import diff --git a/ngraph/frontend/onnx_import/include/onnx_import/onnx.hpp b/ngraph/frontend/onnx_import/include/onnx_import/onnx.hpp index 44d494da3bd..af3c8280415 100644 --- a/ngraph/frontend/onnx_import/include/onnx_import/onnx.hpp +++ b/ngraph/frontend/onnx_import/include/onnx_import/onnx.hpp @@ -62,11 +62,15 @@ namespace ngraph /// \note If stream parsing fails or the ONNX model contains unsupported ops, /// the function throws an ngraph_error exception. /// - /// \param[in] stream The input stream (e.g. file stream, memory stream, etc). + /// \param[in] stream The input stream (e.g. file stream, memory stream, etc). + /// \param[in] model_path The path to the imported onnx model. + /// It is required if the imported model uses data saved in external + /// files. /// /// \return An nGraph function that represents a single output from the created graph. ONNX_IMPORTER_API - std::shared_ptr import_onnx_model(std::istream& stream); + std::shared_ptr import_onnx_model(std::istream& stream, + const std::string& model_path = ""); /// \brief Imports and converts an ONNX model from the input file /// to an nGraph Function representation. diff --git a/ngraph/frontend/onnx_import/include/onnx_import/utils/tensor_external_data.hpp b/ngraph/frontend/onnx_import/include/onnx_import/utils/tensor_external_data.hpp new file mode 100644 index 00000000000..0baaef583cd --- /dev/null +++ b/ngraph/frontend/onnx_import/include/onnx_import/utils/tensor_external_data.hpp @@ -0,0 +1,54 @@ +//***************************************************************************** +// Copyright 2017-2019 Intel Corporation +// +// 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. +//***************************************************************************** + +#pragma once + +#include + +namespace ngraph +{ + namespace onnx_import + { + namespace detail + { + /// \brief Helper class used to load tensor data from external files + class TensorExternalData + { + public: + TensorExternalData(const ONNX_NAMESPACE::TensorProto& tensor); + + /// \brief Load external data from tensor passed to constructor + /// + /// \note If read data from external file fails, + /// the invalid_external_data is thrown + /// + /// \return External binary data loaded into a std::string + std::string load_external_data() const; + + /// \brief Represets parameter of external data as string + /// + /// \return State of TensorExternalData as string representation + std::string to_string() const; + + private: + std::string m_data_location; + int m_offset = 0; + int m_data_lenght = 0; + int m_sha1_digest = 0; + }; + } + } +} diff --git a/ngraph/frontend/onnx_import/src/core/graph.cpp b/ngraph/frontend/onnx_import/src/core/graph.cpp index 4897d52f2d5..0287bbb0fc8 100644 --- a/ngraph/frontend/onnx_import/src/core/graph.cpp +++ b/ngraph/frontend/onnx_import/src/core/graph.cpp @@ -88,6 +88,11 @@ namespace ngraph { ng_constant = tensor.get_ng_constant(); } + catch (const error::invalid_external_data&) + { + // invalid external data makes initializers creation impossible + throw; + } catch (const ngraph::ngraph_error& exc) { NGRAPH_WARN << "Could not create an nGraph Constant for initializer '" diff --git a/ngraph/frontend/onnx_import/src/onnx.cpp b/ngraph/frontend/onnx_import/src/onnx.cpp index c398e49a729..b3e8b7e99b6 100644 --- a/ngraph/frontend/onnx_import/src/onnx.cpp +++ b/ngraph/frontend/onnx_import/src/onnx.cpp @@ -20,6 +20,7 @@ #include #include "ngraph/except.hpp" +#include "ngraph/file_util.hpp" #include "onnx_import/core/graph.hpp" #include "onnx_import/core/model.hpp" #include "onnx_import/onnx.hpp" @@ -96,6 +97,38 @@ namespace ngraph } } + // The paths to external data files are stored as relative to model path. + // The helper function below combines them and replaces the original relative path. + // As a result in futher processing data from external files can be read directly. + void update_external_data_paths(ONNX_NAMESPACE::ModelProto& model_proto, + const std::string& model_path) + { + if (model_path.empty()) + { + return; + } + const auto model_dir_path = file_util::get_directory(model_path); + auto graph_proto = model_proto.mutable_graph(); + for (auto& initializer_tensor : *graph_proto->mutable_initializer()) + { + const auto location_key_value_index = 0; + if (initializer_tensor.has_data_location() && + initializer_tensor.data_location() == + ONNX_NAMESPACE::TensorProto_DataLocation:: + TensorProto_DataLocation_EXTERNAL) + { + const auto external_data_relative_path = + initializer_tensor.external_data(location_key_value_index).value(); + const auto external_data_full_path = + file_util::path_join(model_dir_path, external_data_relative_path); + + // Set full paths to the external file + initializer_tensor.mutable_external_data(location_key_value_index) + ->set_value(external_data_full_path); + } + } + } + std::shared_ptr convert_to_ng_function(const ONNX_NAMESPACE::ModelProto& model_proto) { @@ -112,7 +145,8 @@ namespace ngraph } } // namespace detail - std::shared_ptr import_onnx_model(std::istream& stream) + std::shared_ptr import_onnx_model(std::istream& stream, + const std::string& model_path) { if (!stream.good()) { @@ -144,6 +178,7 @@ namespace ngraph } detail::fixup_legacy_operators(model_proto.mutable_graph()); + detail::update_external_data_paths(model_proto, model_path); return detail::convert_to_ng_function(model_proto); } @@ -155,7 +190,7 @@ namespace ngraph { throw detail::error::file_open{file_path}; } - return import_onnx_model(ifs); + return import_onnx_model(ifs, file_path); } std::set get_supported_operators(std::int64_t version, diff --git a/ngraph/frontend/onnx_import/src/utils/tensor_external_data.cpp b/ngraph/frontend/onnx_import/src/utils/tensor_external_data.cpp new file mode 100644 index 00000000000..c7cfd5e3d83 --- /dev/null +++ b/ngraph/frontend/onnx_import/src/utils/tensor_external_data.cpp @@ -0,0 +1,93 @@ +//***************************************************************************** +// Copyright 2017-2019 Intel Corporation +// +// 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 +#include + +#include "ngraph/log.hpp" +#include "onnx_import/exceptions.hpp" +#include "tensor_external_data.hpp" + +namespace ngraph +{ + namespace onnx_import + { + namespace detail + { + TensorExternalData::TensorExternalData(const ONNX_NAMESPACE::TensorProto& tensor) + { + for (const auto& entry : tensor.external_data()) + { + if (entry.key() == "location") + m_data_location = entry.value(); + if (entry.key() == "offset") + m_offset = std::stoi(entry.value()); + if (entry.key() == "length") + m_data_lenght = std::stoi(entry.value()); + if (entry.key() == "checksum") + m_sha1_digest = std::stoi(entry.value()); + } + } + + std::string TensorExternalData::load_external_data() const + { + std::ifstream external_data_stream(m_data_location, + std::ios::binary | std::ios::in | std::ios::ate); + if (external_data_stream.fail()) + throw error::invalid_external_data{*this}; + + std::streamsize read_data_lenght; + if (m_data_lenght == 0) // read entire file + read_data_lenght = external_data_stream.tellg(); + else + read_data_lenght = m_data_lenght; + + const auto page_size = 4096; + if (m_offset != 0 && m_offset % page_size != 0) + { + NGRAPH_WARN << "offset should be multiples 4096 (page size) to enable mmap " + "support, current value is " + << m_offset; + } + // default value of m_offset is 0 + external_data_stream.seekg(m_offset, std::ios::beg); + + if (m_sha1_digest != 0) + { + NGRAPH_WARN << "SHA1 checksum is not supported"; + } + + std::string read_data; + read_data.resize(read_data_lenght); + external_data_stream.read(&read_data[0], read_data_lenght); + external_data_stream.close(); + + return read_data; + } + + std::string TensorExternalData::to_string() const + { + std::stringstream s; + s << "ExternalDataInfo("; + s << "data_full_path: " << m_data_location; + s << ", offset: " << m_offset; + s << ", data_lenght: " << m_data_lenght; + s << ", sha1_digest: " << m_sha1_digest << ")"; + return s.str(); + } + } + } +} diff --git a/ngraph/test/CMakeLists.txt b/ngraph/test/CMakeLists.txt index 31224ab75a3..8052d0de4df 100644 --- a/ngraph/test/CMakeLists.txt +++ b/ngraph/test/CMakeLists.txt @@ -353,6 +353,7 @@ if (NGRAPH_ONNX_IMPORT_ENABLE AND NOT NGRAPH_USE_PROTOBUF_LITE) onnx/onnx_import_const_folding.in.cpp onnx/onnx_import_convpool.in.cpp onnx/onnx_import_dyn_shapes.in.cpp + onnx/onnx_import_external_data.in.cpp onnx/onnx_import_library.in.cpp onnx/onnx_import_provenance.in.cpp onnx/onnx_import_reshape.in.cpp diff --git a/ngraph/test/files/onnx/external_data/a/tensor_a.data b/ngraph/test/files/onnx/external_data/a/tensor_a.data new file mode 100644 index 00000000000..489b4be7407 Binary files /dev/null and b/ngraph/test/files/onnx/external_data/a/tensor_a.data differ diff --git a/ngraph/test/files/onnx/external_data/b/tensor_b.data b/ngraph/test/files/onnx/external_data/b/tensor_b.data new file mode 100644 index 00000000000..672b9cba6f0 Binary files /dev/null and b/ngraph/test/files/onnx/external_data/b/tensor_b.data differ diff --git a/ngraph/test/files/onnx/external_data/multiple_tensors.data b/ngraph/test/files/onnx/external_data/multiple_tensors.data new file mode 100644 index 00000000000..eebacc956fa Binary files /dev/null and b/ngraph/test/files/onnx/external_data/multiple_tensors.data differ diff --git a/ngraph/test/files/onnx/external_data/tensor.data b/ngraph/test/files/onnx/external_data/tensor.data new file mode 100644 index 00000000000..8db91db9674 Binary files /dev/null and b/ngraph/test/files/onnx/external_data/tensor.data differ diff --git a/ngraph/test/files/onnx/external_data/tensor_optional_fields.data b/ngraph/test/files/onnx/external_data/tensor_optional_fields.data new file mode 100644 index 00000000000..d7f6b6967e0 Binary files /dev/null and b/ngraph/test/files/onnx/external_data/tensor_optional_fields.data differ diff --git a/ngraph/test/models/onnx/external_data.prototxt b/ngraph/test/models/onnx/external_data.prototxt new file mode 100644 index 00000000000..62465aa0fe5 --- /dev/null +++ b/ngraph/test/models/onnx/external_data.prototxt @@ -0,0 +1,99 @@ +ir_version: 3 +producer_name: "nGraph ONNX Importer" +graph { + node { + output: "B" + op_type: "Constant" + attribute { + name: "value" + t { + dims: 2 + dims: 2 + data_type: 1 + float_data: 1 + float_data: 2 + float_data: 3 + float_data: 4 + name: "const_tensor" + } + type: TENSOR + } + } + node { + input: "A" + input: "B" + output: "X" + name: "add_node1" + op_type: "Add" + } + node { + input: "X" + input: "C" + output: "Y" + name: "add_node2" + op_type: "Add" + } + name: "test_graph" + initializer { + dims: 2 + dims: 2 + data_type: 1 + name: "A" + external_data { + key: "location", + value: "../../files/onnx/external_data/tensor.data" + } + data_location: 1 + } + input { + name: "A" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2 + } + dim { + dim_value: 2 + } + } + } + } + } + input { + name: "C" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2 + } + dim { + dim_value: 2 + } + } + } + } + } + output { + name: "Y" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2 + } + dim { + dim_value: 2 + } + } + } + } + } +} +opset_import { + version: 4 +} diff --git a/ngraph/test/models/onnx/external_data_different_paths.prototxt b/ngraph/test/models/onnx/external_data_different_paths.prototxt new file mode 100644 index 00000000000..22133570f3a --- /dev/null +++ b/ngraph/test/models/onnx/external_data_different_paths.prototxt @@ -0,0 +1,87 @@ +ir_version: 3 +producer_name: "nGraph ONNX Importer" +graph { + node { + input: "data_a" + input: "data_b" + input: "data_c" + output: "result" + op_type: "Mean" + } + name: "test_mean_example" + initializer { + dims: 1 + data_type: 1 + name: "data_a" + external_data { + key: "location", + value: "../../files/onnx/external_data/a/tensor_a.data" + } + data_location: 1 + } + initializer { + dims: 3 + data_type: 1 + name: "data_b" + external_data { + key: "location", + value: "../../files/onnx/external_data/b/tensor_b.data" + } + data_location: 1 + } + input { + name: "data_a" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + } + } + } + } + input { + name: "data_b" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 3 + } + } + } + } + } + input { + name: "data_c" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 3 + } + } + } + } + } + output { + name: "result" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 3 + } + } + } + } + } +} +opset_import { + version: 8 +} diff --git a/ngraph/test/models/onnx/external_data_file_not_found.prototxt b/ngraph/test/models/onnx/external_data_file_not_found.prototxt new file mode 100644 index 00000000000..f65b8186aa8 --- /dev/null +++ b/ngraph/test/models/onnx/external_data_file_not_found.prototxt @@ -0,0 +1,82 @@ +ir_version: 3 +producer_name: "nGraph ONNX Importer" +graph { + node { + input: "A" + input: "B" + output: "Y" + name: "add" + op_type: "Add" + } + name: "test_graph" + initializer { + dims: 2 + dims: 2 + data_type: 1 + name: "A" + external_data { + key: "location", + value: "not_existed_file.data" + } + external_data { + key: "offset", + value: "4096" + } + external_data { + key: "length", + value: "16" + } + data_location: 1 + } + input { + name: "A" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2 + } + dim { + dim_value: 2 + } + } + } + } + } + input { + name: "B" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2 + } + dim { + dim_value: 2 + } + } + } + } + } + output { + name: "Y" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2 + } + dim { + dim_value: 2 + } + } + } + } + } +} +opset_import { + version: 4 +} diff --git a/ngraph/test/models/onnx/external_data_optional_fields.prototxt b/ngraph/test/models/onnx/external_data_optional_fields.prototxt new file mode 100644 index 00000000000..8438d377b41 --- /dev/null +++ b/ngraph/test/models/onnx/external_data_optional_fields.prototxt @@ -0,0 +1,107 @@ +ir_version: 3 +producer_name: "nGraph ONNX Importer" +graph { + node { + output: "B" + op_type: "Constant" + attribute { + name: "value" + t { + dims: 2 + dims: 2 + data_type: 1 + float_data: 1 + float_data: 2 + float_data: 3 + float_data: 4 + name: "const_tensor" + } + type: TENSOR + } + } + node { + input: "A" + input: "B" + output: "X" + name: "add_node1" + op_type: "Add" + } + node { + input: "X" + input: "C" + output: "Y" + name: "add_node2" + op_type: "Add" + } + name: "test_graph" + initializer { + dims: 2 + dims: 2 + data_type: 1 + name: "A" + external_data { + key: "location", + value: "../../files/onnx/external_data/tensor_optional_fields.data" + } + external_data { + key: "offset", + value: "4096" + } + external_data { + key: "length", + value: "16" + } + data_location: 1 + } + input { + name: "A" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2 + } + dim { + dim_value: 2 + } + } + } + } + } + input { + name: "C" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2 + } + dim { + dim_value: 2 + } + } + } + } + } + output { + name: "Y" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2 + } + dim { + dim_value: 2 + } + } + } + } + } +} +opset_import { + version: 4 +} diff --git a/ngraph/test/models/onnx/external_data_two_tensors_data_in_the_same_file.prototxt b/ngraph/test/models/onnx/external_data_two_tensors_data_in_the_same_file.prototxt new file mode 100644 index 00000000000..3f24f37749b --- /dev/null +++ b/ngraph/test/models/onnx/external_data_two_tensors_data_in_the_same_file.prototxt @@ -0,0 +1,103 @@ +ir_version: 3 +producer_name: "nGraph ONNX Importer" +graph { + node { + input: "data_a" + input: "data_b" + input: "data_c" + output: "result" + op_type: "Max" + } + name: "test_mean_example" + initializer { + dims: 3 + data_type: 6 + name: "data_a" + external_data { + key: "location", + value: "../../files/onnx/external_data/multiple_tensors.data" + } + external_data { + key: "offset", + value: "0" + } + external_data { + key: "length", + value: "12" + } + data_location: 1 + } + initializer { + dims: 3 + data_type: 6 + name: "data_b" + external_data { + key: "location", + value: "../../files/onnx/external_data/multiple_tensors.data" + } + external_data { + key: "offset", + value: "4096" + } + external_data { + key: "length", + value: "12" + } + data_location: 1 + } + input { + name: "data_a" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 3 + } + } + } + } + } + input { + name: "data_b" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 3 + } + } + } + } + } + input { + name: "data_c" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 3 + } + } + } + } + } + output { + name: "result" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 3 + } + } + } + } + } +} +opset_import { + version: 8 +} diff --git a/ngraph/test/onnx/onnx_import_external_data.in.cpp b/ngraph/test/onnx/onnx_import_external_data.in.cpp new file mode 100644 index 00000000000..e9217bcc20b --- /dev/null +++ b/ngraph/test/onnx/onnx_import_external_data.in.cpp @@ -0,0 +1,121 @@ +//***************************************************************************** +// Copyright 2017-2020 Intel Corporation +// +// 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 "gtest/gtest.h" +#include "ngraph/file_util.hpp" +#include "ngraph/type/element_type.hpp" +#include "onnx_import/default_opset.hpp" +#include "onnx_import/onnx.hpp" +#include "util/engine/test_engines.hpp" +#include "util/test_case.hpp" +#include "util/test_control.hpp" +#include "util/test_tools.hpp" +#include "util/type_prop.hpp" + +using namespace ngraph; +using namespace ngraph::onnx_import; +using namespace ngraph::test; + +static std::string s_manifest = "${MANIFEST}"; + +using TestEngine = test::ENGINE_CLASS_NAME(${BACKEND_NAME}); + +NGRAPH_TEST(${BACKEND_NAME}, onnx_external_data) +{ + const auto function = onnx_import::import_onnx_model( + file_util::path_join(SERIALIZED_ZOO, "onnx/external_data.prototxt")); + + auto test_case = test::TestCase(function); + test_case.add_input({1.f, 2.f, 3.f, 4.f}); + test_case.add_expected_output(Shape{2, 2}, {3.f, 6.f, 9.f, 12.f}); + + test_case.run(); +} + +NGRAPH_TEST(${BACKEND_NAME}, onnx_external_data_from_stream) +{ + std::string path = file_util::path_join(SERIALIZED_ZOO, "onnx/external_data.prototxt"); + std::ifstream stream{path, std::ios::in | std::ios::binary}; + ASSERT_TRUE(stream.is_open()); + const auto function = onnx_import::import_onnx_model(stream, path); + + auto test_case = test::TestCase(function); + test_case.add_input({1.f, 2.f, 3.f, 4.f}); + test_case.add_expected_output(Shape{2, 2}, {3.f, 6.f, 9.f, 12.f}); + + test_case.run(); + + stream.close(); +} + +NGRAPH_TEST(${BACKEND_NAME}, onnx_external_data_optinal_fields) +{ + const auto function = onnx_import::import_onnx_model( + file_util::path_join(SERIALIZED_ZOO, "onnx/external_data_optional_fields.prototxt")); + + auto test_case = test::TestCase(function); + test_case.add_input({1.f, 2.f, 3.f, 4.f}); + test_case.add_expected_output(Shape{2, 2}, {3.f, 6.f, 9.f, 12.f}); + + test_case.run(); +} + +NGRAPH_TEST(${BACKEND_NAME}, onnx_external_data_in_different_paths) +{ + auto function = onnx_import::import_onnx_model( + file_util::path_join(SERIALIZED_ZOO, "onnx/external_data_different_paths.prototxt")); + + auto test_case = test::TestCase(function); + // first input: {3.f}, second: {1.f, 2.f, 5.f} read from external files + test_case.add_input({2.f, 7.f, 7.f}); + + test_case.add_expected_output({2.f, 4.f, 5.f}); + test_case.run(); +} + +NGRAPH_TEST(${BACKEND_NAME}, onnx_external_two_tensors_data_in_the_same_file) +{ + auto function = onnx_import::import_onnx_model(file_util::path_join( + SERIALIZED_ZOO, "onnx/external_data_two_tensors_data_in_the_same_file.prototxt")); + + auto test_case = test::TestCase(function); + // first input: {3, 2, 1}, second: {1, 2, 3} read from external file + test_case.add_input({2, 3, 1}); + + test_case.add_expected_output({3, 3, 3}); + test_case.run(); +} + +NGRAPH_TEST(${BACKEND_NAME}, onnx_external_invalid_external_data_exception) +{ + try + { + auto function = onnx_import::import_onnx_model( + file_util::path_join(SERIALIZED_ZOO, "onnx/external_data_file_not_found.prototxt")); + FAIL() << "Incorrect path to external data not detected"; + } + catch (const ngraph_error& error) + { + EXPECT_PRED_FORMAT2( + testing::IsSubstring, + std::string("not_existed_file.data, offset: 4096, data_lenght: 16, sha1_digest: 0)"), + error.what()); + } + catch (...) + { + FAIL() << "Importing onnx model failed for unexpected reason"; + } +}