From 00a5ef689b0080ae2a73fe81da4673985078ec5c Mon Sep 17 00:00:00 2001 From: Brian Zhao <bmzhao@google.com> Date: Fri, 18 Sep 2020 14:09:38 -0700 Subject: [PATCH] Support savedmodels that reference assets. PiperOrigin-RevId: 332523998 Change-Id: Ia0f7cd75f79020c1b385ee6c02323c9d431ff86d --- tensorflow/c/eager/BUILD | 2 + tensorflow/c/eager/c_api_test_util.cc | 17 ++++++ tensorflow/c/eager/c_api_test_util.h | 5 ++ .../c/experimental/saved_model/core/BUILD | 5 +- .../saved_model/core/revived_types/BUILD | 19 ++++++ .../saved_model/core/revived_types/asset.cc | 49 +++++++++++++++ .../saved_model/core/revived_types/asset.h | 50 +++++++++++++++ .../saved_model/core/saved_model_utils.cc | 14 +++++ .../saved_model/core/saved_model_utils.h | 8 +++ .../saved_model/core/tf_saved_model_api.cc | 20 +++--- .../internal/saved_model_api_test.cc | 57 ++++++++++++++++++ tensorflow/cc/saved_model/BUILD | 13 ++++ .../AssetModule/assets/test_asset.txt | 1 + .../testdata/AssetModule/saved_model.pb | Bin 0 -> 8248 bytes .../variables/variables.data-00000-of-00001 | Bin 0 -> 38 bytes .../AssetModule/variables/variables.index | Bin 0 -> 144 bytes .../testdata/generate_saved_models.py | 15 +++++ .../cc/saved_model/testdata/test_asset.txt | 1 + 18 files changed, 266 insertions(+), 10 deletions(-) create mode 100644 tensorflow/c/experimental/saved_model/core/revived_types/asset.cc create mode 100644 tensorflow/c/experimental/saved_model/core/revived_types/asset.h create mode 100644 tensorflow/cc/saved_model/testdata/AssetModule/assets/test_asset.txt create mode 100644 tensorflow/cc/saved_model/testdata/AssetModule/saved_model.pb create mode 100644 tensorflow/cc/saved_model/testdata/AssetModule/variables/variables.data-00000-of-00001 create mode 100644 tensorflow/cc/saved_model/testdata/AssetModule/variables/variables.index create mode 100644 tensorflow/cc/saved_model/testdata/test_asset.txt diff --git a/tensorflow/c/eager/BUILD b/tensorflow/c/eager/BUILD index d259b32f339..da18424f936 100644 --- a/tensorflow/c/eager/BUILD +++ b/tensorflow/c/eager/BUILD @@ -619,6 +619,8 @@ tf_cuda_library( ":c_api", ":c_api_experimental", "//tensorflow/c:c_test_util", + "//tensorflow/c:tf_datatype", + "//tensorflow/c:tf_tensor", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", diff --git a/tensorflow/c/eager/c_api_test_util.cc b/tensorflow/c/eager/c_api_test_util.cc index fd68866f502..6eb5b521c50 100644 --- a/tensorflow/c/eager/c_api_test_util.cc +++ b/tensorflow/c/eager/c_api_test_util.cc @@ -17,12 +17,16 @@ limitations under the License. #include "tensorflow/c/eager/c_api.h" #include "tensorflow/c/eager/c_api_experimental.h" +#include "tensorflow/c/tf_datatype.h" +#include "tensorflow/c/tf_tensor.h" #include "tensorflow/core/platform/logging.h" #include "tensorflow/core/platform/strcat.h" #include "tensorflow/core/platform/test.h" +#include "tensorflow/core/platform/tstring.h" #include "tensorflow/core/protobuf/cluster.pb.h" using tensorflow::string; +using tensorflow::tstring; TFE_TensorHandle* TestScalarTensorHandle(TFE_Context* ctx, float value) { float data[] = {value}; @@ -36,6 +40,19 @@ TFE_TensorHandle* TestScalarTensorHandle(TFE_Context* ctx, float value) { return th; } +TFE_TensorHandle* TestScalarTensorHandle(TFE_Context* ctx, + const tensorflow::tstring& value) { + TF_Status* status = TF_NewStatus(); + TF_Tensor* t = TFE_AllocateHostTensor(ctx, TF_STRING, nullptr, 0, status); + tstring* data = static_cast<tstring*>(TF_TensorData(t)); + *data = value; + TFE_TensorHandle* th = TFE_NewTensorHandleFromTensor(ctx, t, status); + CHECK_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TF_DeleteTensor(t); + TF_DeleteStatus(status); + return th; +} + TFE_TensorHandle* TestScalarTensorHandle(TFE_Context* ctx, int value) { int data[] = {value}; TF_Status* status = TF_NewStatus(); diff --git a/tensorflow/c/eager/c_api_test_util.h b/tensorflow/c/eager/c_api_test_util.h index 2f77ae5cf44..ad0c7c6340f 100644 --- a/tensorflow/c/eager/c_api_test_util.h +++ b/tensorflow/c/eager/c_api_test_util.h @@ -16,6 +16,7 @@ limitations under the License. #define TENSORFLOW_C_EAGER_C_API_TEST_UTIL_H_ #include "tensorflow/c/eager/c_api.h" +#include "tensorflow/core/platform/tstring.h" #include "tensorflow/core/platform/types.h" #include "tensorflow/core/protobuf/tensorflow_server.pb.h" @@ -28,6 +29,10 @@ TFE_TensorHandle* TestScalarTensorHandle(TFE_Context* ctx, int value); // Return a tensor handle containing a bool scalar TFE_TensorHandle* TestScalarTensorHandle(TFE_Context* ctx, bool value); +// Return a tensor handle containing a tstring scalar +TFE_TensorHandle* TestScalarTensorHandle(TFE_Context* ctx, + const tensorflow::tstring& value); + // Return a tensor handle containing a 2x2 matrix of doubles TFE_TensorHandle* DoubleTestMatrixTensorHandle(TFE_Context* ctx); diff --git a/tensorflow/c/experimental/saved_model/core/BUILD b/tensorflow/c/experimental/saved_model/core/BUILD index 2feb7c1b33e..4127fdfe0ee 100644 --- a/tensorflow/c/experimental/saved_model/core/BUILD +++ b/tensorflow/c/experimental/saved_model/core/BUILD @@ -62,6 +62,7 @@ cc_library( ":function_metadata", "//tensorflow/c:tf_tensor_internal", "//tensorflow/c/eager:immediate_execution_context", + "//tensorflow/c/experimental/saved_model/core/revived_types:asset", "//tensorflow/c/experimental/saved_model/core/revived_types:constant", "//tensorflow/c/experimental/saved_model/core/revived_types:tf_concrete_function", "//tensorflow/c/experimental/saved_model/core/revived_types:variable", @@ -69,6 +70,7 @@ cc_library( "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", ], ) @@ -138,7 +140,6 @@ cc_library( ":saved_model_api", ":saved_model_utils", ":signature_def_function", - "//tensorflow/c:tensor_interface", "//tensorflow/c/eager:immediate_execution_context", "//tensorflow/c/eager:immediate_execution_tensor_handle", "//tensorflow/c/experimental/saved_model/core/ops:restore_ops", @@ -148,10 +149,10 @@ cc_library( "//tensorflow/c/experimental/saved_model/core/revived_types:variable", "//tensorflow/cc/saved_model:bundle_v2", "//tensorflow/cc/saved_model:constants", + "//tensorflow/cc/saved_model:loader_util", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:protos_all_cc", - "//tensorflow/core/common_runtime/eager:tensor_handle", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:optional", diff --git a/tensorflow/c/experimental/saved_model/core/revived_types/BUILD b/tensorflow/c/experimental/saved_model/core/revived_types/BUILD index 25cac39daa0..93a485e717f 100644 --- a/tensorflow/c/experimental/saved_model/core/revived_types/BUILD +++ b/tensorflow/c/experimental/saved_model/core/revived_types/BUILD @@ -8,6 +8,25 @@ package( licenses = ["notice"], # Apache 2.0 ) +cc_library( + name = "asset", + srcs = [ + "asset.cc", + ], + hdrs = [ + "asset.h", + ], + deps = [ + ":tensorhandle_convertible", + "//tensorflow/c:tensor_interface", + "//tensorflow/c/eager:immediate_execution_context", + "//tensorflow/c/eager:immediate_execution_tensor_handle", + "//tensorflow/cc/saved_model:constants", + "//tensorflow/core:lib", + "//tensorflow/core:protos_all_cc", + ], +) + cc_library( name = "constant", srcs = [ diff --git a/tensorflow/c/experimental/saved_model/core/revived_types/asset.cc b/tensorflow/c/experimental/saved_model/core/revived_types/asset.cc new file mode 100644 index 00000000000..5cc14d615f5 --- /dev/null +++ b/tensorflow/c/experimental/saved_model/core/revived_types/asset.cc @@ -0,0 +1,49 @@ +/* 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 "tensorflow/c/experimental/saved_model/core/revived_types/asset.h" + +#include <string> + +#include "tensorflow/c/eager/immediate_execution_context.h" +#include "tensorflow/c/eager/immediate_execution_tensor_handle.h" +#include "tensorflow/c/tensor_interface.h" +#include "tensorflow/cc/saved_model/constants.h" +#include "tensorflow/core/platform/errors.h" +#include "tensorflow/core/platform/path.h" + +namespace tensorflow { + +Asset::Asset(ImmediateTensorHandlePtr handle) + : TensorHandleConvertible(std::move(handle)) {} + +Status Asset::Create(ImmediateExecutionContext* ctx, + const std::string& saved_model_dir, + const std::string& asset_filename, + std::unique_ptr<Asset>* output) { + std::string abs_path = + io::JoinPath(saved_model_dir, kSavedModelAssetsDirectory, asset_filename); + AbstractTensorPtr tensor(ctx->CreateStringScalar(abs_path)); + if (tensor.get() == nullptr) { + return errors::Internal( + "Failed to create scalar string tensor for Asset at path ", abs_path); + } + + ImmediateTensorHandlePtr handle(ctx->CreateLocalHandle(tensor.get())); + output->reset(new Asset(std::move(handle))); + return Status(); +} + +} // namespace tensorflow diff --git a/tensorflow/c/experimental/saved_model/core/revived_types/asset.h b/tensorflow/c/experimental/saved_model/core/revived_types/asset.h new file mode 100644 index 00000000000..c98bd9b5628 --- /dev/null +++ b/tensorflow/c/experimental/saved_model/core/revived_types/asset.h @@ -0,0 +1,50 @@ +/* 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. +==============================================================================*/ + +#ifndef TENSORFLOW_C_EXPERIMENTAL_SAVED_MODEL_CORE_REVIVED_TYPES_ASSET_H_ +#define TENSORFLOW_C_EXPERIMENTAL_SAVED_MODEL_CORE_REVIVED_TYPES_ASSET_H_ + +#include <string> + +#include "tensorflow/c/eager/immediate_execution_context.h" +#include "tensorflow/c/eager/immediate_execution_tensor_handle.h" +#include "tensorflow/c/experimental/saved_model/core/revived_types/tensorhandle_convertible.h" +#include "tensorflow/c/tensor_interface.h" +#include "tensorflow/core/framework/tensor.pb.h" + +namespace tensorflow { + +class Asset : public TensorHandleConvertible { + public: + static Status Create(ImmediateExecutionContext* ctx, + const std::string& saved_model_dir, + const std::string& asset_filename, + std::unique_ptr<Asset>* output); + + // Asset is movable, but not copyable. + Asset(Asset&& other) = default; + Asset& operator=(Asset&& other) = default; + + ~Asset() override = default; + + private: + explicit Asset(ImmediateTensorHandlePtr handle); + Asset(const Asset&) = delete; + Asset& operator=(const Asset&) = delete; +}; + +} // namespace tensorflow + +#endif // TENSORFLOW_C_EXPERIMENTAL_SAVED_MODEL_CORE_REVIVED_TYPES_ASSET_H_ diff --git a/tensorflow/c/experimental/saved_model/core/saved_model_utils.cc b/tensorflow/c/experimental/saved_model/core/saved_model_utils.cc index e79fd8d7001..8d1e0966ff7 100644 --- a/tensorflow/c/experimental/saved_model/core/saved_model_utils.cc +++ b/tensorflow/c/experimental/saved_model/core/saved_model_utils.cc @@ -100,6 +100,20 @@ Status ValidateSavedFunctionCompatibleWithFunctionDef( } // namespace +Status LoadSavedAsset(ImmediateExecutionContext* ctx, const SavedAsset& asset, + const std::string& saved_model_dir, + absl::Span<const AssetFileDef> assets, + std::unique_ptr<Asset>* output) { + int asset_index = asset.asset_file_def_index(); + if (asset_index >= assets.size()) { + return errors::FailedPrecondition( + "SavedAsset contained asset index ", asset_index, + " but AssetFileDef only contains ", assets.size(), " # of assets"); + } + const std::string& asset_filename = assets[asset_index].filename(); + return Asset::Create(ctx, saved_model_dir, asset_filename, output); +} + Status TensorProtoToConstant(ImmediateExecutionContext* ctx, const TensorProto& proto, std::unique_ptr<Constant>* output) { diff --git a/tensorflow/c/experimental/saved_model/core/saved_model_utils.h b/tensorflow/c/experimental/saved_model/core/saved_model_utils.h index 68bfbe32222..e82ec1bd104 100644 --- a/tensorflow/c/experimental/saved_model/core/saved_model_utils.h +++ b/tensorflow/c/experimental/saved_model/core/saved_model_utils.h @@ -22,7 +22,9 @@ limitations under the License. #include <memory> #include <unordered_map> +#include "absl/types/span.h" #include "tensorflow/c/eager/immediate_execution_context.h" +#include "tensorflow/c/experimental/saved_model/core/revived_types/asset.h" #include "tensorflow/c/experimental/saved_model/core/revived_types/constant.h" #include "tensorflow/c/experimental/saved_model/core/revived_types/tf_concrete_function.h" #include "tensorflow/c/experimental/saved_model/core/revived_types/variable.h" @@ -31,6 +33,7 @@ limitations under the License. #include "tensorflow/core/lib/hash/hash.h" #include "tensorflow/core/platform/status.h" #include "tensorflow/core/platform/stringpiece.h" +#include "tensorflow/core/protobuf/meta_graph.pb.h" #include "tensorflow/core/protobuf/saved_object_graph.pb.h" #include "tensorflow/core/protobuf/struct.pb.h" @@ -52,6 +55,11 @@ Status LoadSavedVariable(ImmediateExecutionContext* ctx, const SavedVariable& variable, std::unique_ptr<Variable>* output); +Status LoadSavedAsset(ImmediateExecutionContext* ctx, const SavedAsset& asset, + const std::string& saved_model_dir, + absl::Span<const AssetFileDef> assets, + std::unique_ptr<Asset>* output); + // Creates a TFConcreteFunction from a SavedConcreteFunction. Status LoadTFConcreteFunction( const SavedConcreteFunction& saved_concrete_function, diff --git a/tensorflow/c/experimental/saved_model/core/tf_saved_model_api.cc b/tensorflow/c/experimental/saved_model/core/tf_saved_model_api.cc index ab7052b52ed..143257b01d5 100644 --- a/tensorflow/c/experimental/saved_model/core/tf_saved_model_api.cc +++ b/tensorflow/c/experimental/saved_model/core/tf_saved_model_api.cc @@ -37,6 +37,7 @@ limitations under the License. #include "tensorflow/c/experimental/saved_model/core/signature_def_function.h" #include "tensorflow/cc/saved_model/bundle_v2.h" #include "tensorflow/cc/saved_model/constants.h" +#include "tensorflow/cc/saved_model/loader_util.h" #include "tensorflow/core/framework/attr_value.pb.h" #include "tensorflow/core/framework/function.pb.h" #include "tensorflow/core/framework/graph.pb.h" @@ -108,12 +109,17 @@ Status ConstantFromSavedConstant( // SavedResources. These are returned via the `out` parameter. Status ReviveObjects( const MetaGraphDef& metagraph, ImmediateExecutionContext* context, + const std::string& directory, std::unordered_map<int, std::unique_ptr<TensorHandleConvertible>>* revived_objects) { // This is needed to restore "Constant" nodes by looking up their // "Value" attribute. NodeAttrMap node_attr_map = internal::NodeToAttrMap(metagraph.graph_def()); + // These are needed for creating "Assets", by looking up their filenames. + std::vector<AssetFileDef> assets; + TF_RETURN_IF_ERROR(internal::GetAssetFileDefs(metagraph, &assets)); + // Iterate through all the saved objects, restoring objects as we go. // We don't recreate functions until all other objects have been created. for (int i = 0; i < metagraph.object_graph_def().nodes_size(); ++i) { @@ -129,12 +135,10 @@ Status ReviveObjects( node_attr_map, &constant)); (*revived_objects)[i] = std::move(constant); } else if (node.kind_case() == SavedObject::kAsset) { - // TODO(bmzhao): Implement Asset C++ class. This should be just recreating - // the full path to the asset file: - // https://github.com/tensorflow/tensorflow/blob/6a0bdbdb7c48a3491ae1277083ae3dafb4ab4d7a/tensorflow/python/saved_model/load.py#L395-L396 - // and storing it as a string tensor: - // https://github.com/tensorflow/tensorflow/blob/6a0bdbdb7c48a3491ae1277083ae3dafb4ab4d7a/tensorflow/python/training/tracking/tracking.py#L324-L325 - return errors::Unimplemented("SavedAsset loading is not implemented yet"); + std::unique_ptr<Asset> asset; + TF_RETURN_IF_ERROR(internal::LoadSavedAsset(context, node.asset(), + directory, assets, &asset)); + (*revived_objects)[i] = std::move(asset); } else if (node.kind_case() == SavedObject::kResource) { // TODO(bmzhao): Figure out how resource loading works and implement it return errors::Unimplemented( @@ -352,8 +356,8 @@ Status TFSavedModelAPI::Load( // https://github.com/tensorflow/tensorflow/blob/285b5fa15405c5e2c084080f52a1818be8648079/tensorflow/python/saved_model/function_deserialization.py#L438-L454 RevivedObjectMap revived_objects; - TF_RETURN_IF_ERROR( - ReviveObjects(bundle.meta_graph_def(), context, &revived_objects)); + TF_RETURN_IF_ERROR(ReviveObjects(bundle.meta_graph_def(), context, directory, + &revived_objects)); // TODO(bmzhao): When we later add support for loading resources, we need to // handle the case where materializing a function's captures requires invoking diff --git a/tensorflow/c/experimental/saved_model/internal/saved_model_api_test.cc b/tensorflow/c/experimental/saved_model/internal/saved_model_api_test.cc index df998fcf6cd..86754b32c0c 100644 --- a/tensorflow/c/experimental/saved_model/internal/saved_model_api_test.cc +++ b/tensorflow/c/experimental/saved_model/internal/saved_model_api_test.cc @@ -27,9 +27,12 @@ limitations under the License. #include "tensorflow/core/lib/io/path.h" #include "tensorflow/core/platform/stringpiece.h" #include "tensorflow/core/platform/test.h" +#include "tensorflow/core/platform/tstring.h" namespace { +using tensorflow::tstring; + constexpr char kTestData[] = "cc/saved_model/testdata"; const char* kServeTag[] = {"serve"}; @@ -137,6 +140,60 @@ TEST_P(CSavedModelAPITest, LoadsSavedModel) { TFE_DeleteContext(ctx); } +TEST_P(CSavedModelAPITest, LoadsAssetSavedModel) { + TF_Status* status = TF_NewStatus(); + TFE_ContextOptions* opts = TFE_NewContextOptions(); + bool use_tfrt = GetParam(); + if (use_tfrt) { + TFE_DeleteContextOptions(opts); + TF_DeleteStatus(status); + GTEST_SKIP(); // TODO(chky) : Enable this once TFRT is open sourced. + } + + TFE_ContextOptionsSetTfrt(opts, use_tfrt); + + TFE_Context* ctx = TFE_NewContext(opts, status); + ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status); + TFE_DeleteContextOptions(opts); + + std::string model_dir = SavedModelPath("AssetModule"); + + TF_SavedModel* saved_model = + TF_LoadSavedModel(model_dir.c_str(), ctx, status); + + EXPECT_EQ(TF_GetCode(status), TF_OK) << TF_Message(status); + TF_ConcreteFunction* read_file_fn = + TF_GetSavedModelConcreteFunction(saved_model, "read_file", status); + EXPECT_EQ(TF_GetCode(status), TF_OK) << TF_Message(status); + + TFE_Op* read_file_op = + TF_ConcreteFunctionMakeCallOp(read_file_fn, nullptr, 0, status); + EXPECT_EQ(TF_GetCode(status), TF_OK) << TF_Message(status); + + // TODO(bmzhao): Finish API on FunctionMetadata args, so we know how many + // inputs + outputs a function has. + TFE_TensorHandle* read_file_fn_outputs[1] = {nullptr}; + int num_retvals = 1; + + TFE_Execute(read_file_op, &read_file_fn_outputs[0], &num_retvals, status); + EXPECT_EQ(TF_GetCode(status), TF_OK) << TF_Message(status); + + TF_Tensor* result = TFE_TensorHandleResolve(read_file_fn_outputs[0], status); + EXPECT_EQ(TF_GetCode(status), TF_OK) << TF_Message(status); + + EXPECT_EQ(TF_NumDims(result), 0); + tensorflow::tstring* output_value = + static_cast<tensorflow::tstring*>(TF_TensorData(result)); + EXPECT_EQ(std::string(*output_value), "TEST ASSET FILE CONTENTS\n"); + + TF_DeleteTensor(result); + TFE_DeleteTensorHandle(read_file_fn_outputs[0]); + TFE_DeleteOp(read_file_op); + TF_DeleteSavedModel(saved_model); + TF_DeleteStatus(status); + TFE_DeleteContext(ctx); +} + INSTANTIATE_TEST_SUITE_P(RuntimeAgnosticSavedModelTests, CSavedModelAPITest, ::testing::Bool()); diff --git a/tensorflow/cc/saved_model/BUILD b/tensorflow/cc/saved_model/BUILD index fddbcfec6e6..d25b580dace 100644 --- a/tensorflow/cc/saved_model/BUILD +++ b/tensorflow/cc/saved_model/BUILD @@ -209,9 +209,13 @@ tf_cc_test( py_binary( name = "testdata/generate_saved_models", srcs = ["testdata/generate_saved_models.py"], + data = [ + ":saved_model_asset_data", + ], python_version = "PY3", srcs_version = "PY3", deps = [ + "//tensorflow/python:client_testlib", "//tensorflow/python:dtypes", "//tensorflow/python:framework_ops", "//tensorflow/python:tensor_spec", @@ -221,6 +225,7 @@ py_binary( "//tensorflow/python/module", "//tensorflow/python/saved_model", "//tensorflow/python/saved_model:save_options", + "//tensorflow/python/training/tracking", "@absl_py//absl:app", ], ) @@ -229,6 +234,7 @@ py_binary( filegroup( name = "saved_model_test_files", srcs = glob([ + "testdata/AssetModule/**", "testdata/half_plus_two_pbtxt/**", "testdata/half_plus_two_main_op/**", "testdata/half_plus_two/**", @@ -245,6 +251,13 @@ alias( actual = ":saved_model_test_files", ) +filegroup( + name = "saved_model_asset_data", + srcs = [ + "testdata/test_asset.txt", + ], +) + exports_files( glob([ "testdata/half_plus_two_pbtxt/**", diff --git a/tensorflow/cc/saved_model/testdata/AssetModule/assets/test_asset.txt b/tensorflow/cc/saved_model/testdata/AssetModule/assets/test_asset.txt new file mode 100644 index 00000000000..40d69b1aac4 --- /dev/null +++ b/tensorflow/cc/saved_model/testdata/AssetModule/assets/test_asset.txt @@ -0,0 +1 @@ +TEST ASSET FILE CONTENTS diff --git a/tensorflow/cc/saved_model/testdata/AssetModule/saved_model.pb b/tensorflow/cc/saved_model/testdata/AssetModule/saved_model.pb new file mode 100644 index 0000000000000000000000000000000000000000..4bf99e03c22cd4c541b98d49e89ec15fccac46da GIT binary patch literal 8248 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 literal 0 HcmV?d00001 diff --git a/tensorflow/cc/saved_model/testdata/AssetModule/variables/variables.data-00000-of-00001 b/tensorflow/cc/saved_model/testdata/AssetModule/variables/variables.data-00000-of-00001 new file mode 100644 index 0000000000000000000000000000000000000000..4105bb4c15e473b2f00dd3ae3236c7ef8c2328bf GIT binary patch literal 38 tcmY#<o|oOnCC$aj!6?L<SX`W1!o|nIB*aymnVy$eQd*Q+%*DXP006&g34Z_p literal 0 HcmV?d00001 diff --git a/tensorflow/cc/saved_model/testdata/AssetModule/variables/variables.index b/tensorflow/cc/saved_model/testdata/AssetModule/variables/variables.index new file mode 100644 index 0000000000000000000000000000000000000000..3d903ca79a2904080c70501148466371b2e96f59 GIT binary patch literal 144 zcmZQzVB=tvV&Y(Akl~JZ_HcFf4)FK%3vqPvagFzP@^W<!iFXfj4DjG!7h=#*GyNKU yL4g4X7(vA3)xo)N&Vf%0H!v_VB`{dSg<QTJ^mO3x<pT*20^z?Kx>ZWuZvy~a=NKaZ literal 0 HcmV?d00001 diff --git a/tensorflow/cc/saved_model/testdata/generate_saved_models.py b/tensorflow/cc/saved_model/testdata/generate_saved_models.py index 5f39ae0651d..91c09d33bb0 100644 --- a/tensorflow/cc/saved_model/testdata/generate_saved_models.py +++ b/tensorflow/cc/saved_model/testdata/generate_saved_models.py @@ -29,9 +29,12 @@ from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_spec from tensorflow.python.module import module +from tensorflow.python.ops import io_ops from tensorflow.python.ops import variables +from tensorflow.python.platform import test from tensorflow.python.saved_model import save_options from tensorflow.python.saved_model import saved_model +from tensorflow.python.training.tracking import tracking class VarsAndArithmeticObjectGraph(module.Module): @@ -68,9 +71,21 @@ class CyclicModule(module.Module): self.child = ReferencesParent(self) +class AssetModule(module.Module): + + def __init__(self): + self.asset = tracking.Asset( + test.test_src_dir_path("cc/saved_model/testdata/test_asset.txt")) + + @def_function.function(input_signature=[]) + def read_file(self): + return io_ops.read_file(self.asset) + + MODULE_CTORS = { "VarsAndArithmeticObjectGraph": VarsAndArithmeticObjectGraph, "CyclicModule": CyclicModule, + "AssetModule": AssetModule, } diff --git a/tensorflow/cc/saved_model/testdata/test_asset.txt b/tensorflow/cc/saved_model/testdata/test_asset.txt new file mode 100644 index 00000000000..40d69b1aac4 --- /dev/null +++ b/tensorflow/cc/saved_model/testdata/test_asset.txt @@ -0,0 +1 @@ +TEST ASSET FILE CONTENTS