diff --git a/tensorflow/BUILD b/tensorflow/BUILD index 1701ed2065c..c6d5f741d4f 100644 --- a/tensorflow/BUILD +++ b/tensorflow/BUILD @@ -356,7 +356,6 @@ filegroup( "//tensorflow/contrib/data:all_files", "//tensorflow/contrib/data/python/kernel_tests:all_files", "//tensorflow/contrib/data/python/ops:all_files", - "//tensorflow/contrib/data/python/util:all_files", "//tensorflow/contrib/decision_trees/proto:all_files", "//tensorflow/contrib/distributions:all_files", "//tensorflow/contrib/eager/python:all_files", @@ -475,6 +474,9 @@ filegroup( "//tensorflow/java/src/main/java/org/tensorflow/examples:all_files", "//tensorflow/java/src/main/native:all_files", "//tensorflow/python:all_files", + "//tensorflow/python/data:all_files", + "//tensorflow/python/data/ops:all_files", + "//tensorflow/python/data/util:all_files", "//tensorflow/python/debug:all_files", "//tensorflow/python/eager:all_files", "//tensorflow/python/estimator:all_files", diff --git a/tensorflow/cc/framework/scope.h b/tensorflow/cc/framework/scope.h index 0335f6357d0..0225ac04729 100644 --- a/tensorflow/cc/framework/scope.h +++ b/tensorflow/cc/framework/scope.h @@ -107,13 +107,13 @@ class Scope { static Scope NewRootScope(); /// Return a new scope. Ops created with this scope will have - /// / as the prefix. The actual name will be unique + /// `name/child_scope_name` as the prefix. The actual name will be unique /// in the current scope. All other properties are inherited from the current - /// scope. If child_scope_name is empty, the '/' is elided. + /// scope. If `child_scope_name` is empty, the `/` is elided. Scope NewSubScope(const string& child_scope_name) const; /// Return a new scope. All ops created within the returned scope will have - /// names of the form /[_& grad_inputs, std::vector* grad_outputs) { - auto softmax = Exp(scope, op.output(0)); auto sum = Sum(scope, grad_inputs[0], {1}, Sum::KeepDims(true)); auto mul = Mul(scope, sum, softmax); @@ -130,8 +129,7 @@ Status Conv2DGrad(const Scope& scope, const Operation& op, TF_RETURN_IF_ERROR(GetNodeAttr(attrs, "data_format", &data_format)); TF_RETURN_IF_ERROR(GetNodeAttr(attrs, "padding", &padding)); TF_RETURN_IF_ERROR(GetNodeAttr(attrs, "strides", &strides)); - TF_RETURN_IF_ERROR(GetNodeAttr(attrs, "use_cudnn_on_gpu", - &use_cudnn_on_gpu)); + TF_RETURN_IF_ERROR(GetNodeAttr(attrs, "use_cudnn_on_gpu", &use_cudnn_on_gpu)); Conv2DBackpropInput::Attrs input_attrs; input_attrs.DataFormat(data_format); input_attrs.UseCudnnOnGpu(use_cudnn_on_gpu); @@ -198,8 +196,6 @@ Status MaxPoolGradV2Helper(const Scope& scope, const Operation& op, } REGISTER_GRADIENT_OP("MaxPoolV2", MaxPoolGradV2Helper); - - } // anonymous namespace } // namespace ops } // namespace tensorflow diff --git a/tensorflow/cc/gradients/nn_grad_test.cc b/tensorflow/cc/gradients/nn_grad_test.cc index 156a070acfb..ac66f51cf01 100644 --- a/tensorflow/cc/gradients/nn_grad_test.cc +++ b/tensorflow/cc/gradients/nn_grad_test.cc @@ -36,7 +36,7 @@ class NNGradTest : public ::testing::Test { float max_error; TF_ASSERT_OK((ComputeGradientError( scope_, {x}, {x_shape}, {y}, {y_shape}, &max_error))); - EXPECT_LT(max_error, 2.2e-4); + EXPECT_LT(max_error, 1e-3); } void RunTest(const Output& x, const Tensor& x_init_value, const Output& y, @@ -44,7 +44,7 @@ class NNGradTest : public ::testing::Test { float max_error; TF_ASSERT_OK((ComputeGradientError( scope_, x, x_init_value, y, y_shape, &max_error))); - EXPECT_LT(max_error, 2.2e-4); + EXPECT_LT(max_error, 1e-3); } void RunTest(const OutputList& xs, const std::vector& x_shapes, @@ -53,7 +53,25 @@ class NNGradTest : public ::testing::Test { float max_error; TF_ASSERT_OK((ComputeGradientError( scope_, xs, x_shapes, ys, y_shapes, &max_error))); - EXPECT_LT(max_error, 2.2e-4); + EXPECT_LT(max_error, 1e-3); + } + + // Sets tensor with random values, ensuring that the max value is largest by + // a reasonable amount. + // This is an issue for MaxPool and MaxPoolV2, in which perturbations by the + // numeric gradient computation in the gradient checker can change the max + // value if values are too close together. + template + void SetRandomValuesWithBumpedMax(Tensor* tensor) { + auto tensor_flat = tensor->flat(); + tensor_flat.setRandom(); + int32 max_index = 0; + for (size_t i = 1; i < tensor->NumElements(); i++) { + if (tensor_flat(i) > tensor_flat(max_index)) { + max_index = i; + } + } + tensor_flat(max_index) += 1e-2; } Scope scope_; @@ -148,22 +166,30 @@ TEST_F(NNGradTest, Conv2DGrad) { } TEST_F(NNGradTest, MaxPoolGradHelper) { - TensorShape shape({1, 2, 2, 1}); - auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape)); + TensorShape x_shape({1, 2, 2, 1}); + TensorShape y_shape({1, 1, 1, 1}); + auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); + // Setup window and strides so that we only do one MaxPool. const std::vector ksize{1, 2, 2, 1}; - const std::vector strides{1, 1, 1, 1}; - auto y = MaxPool(scope_, x, ksize, strides, "SAME"); - RunTest(x, shape, y, shape); + const std::vector strides{1, 2, 2, 1}; + auto y = MaxPool(scope_, x, ksize, strides, "VALID"); + Tensor x_init_value = Tensor(DT_FLOAT, x_shape); + SetRandomValuesWithBumpedMax(&x_init_value); + RunTest(x, x_init_value, y, y_shape); } TEST_F(NNGradTest, MaxPoolGradV2Helper) { - TensorShape shape({1, 2, 2, 1}); - auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape)); + TensorShape x_shape({1, 2, 2, 1}); + TensorShape y_shape({1, 1, 1, 1}); + auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); + // Setup window and strides so that we only do one MaxPool. Tensor ksize = test::AsTensor({1, 2, 2, 1}, {4}); - Tensor strides = test::AsTensor({1, 1, 1, 1}, {4}); - auto y = MaxPoolV2(scope_, x, ksize, strides, "SAME"); - RunTest(x, shape, y, shape); + Tensor strides = test::AsTensor({1, 2, 2, 1}, {4}); + auto y = MaxPoolV2(scope_, x, ksize, strides, "VALID"); + Tensor x_init_value = Tensor(DT_FLOAT, x_shape); + SetRandomValuesWithBumpedMax(&x_init_value); + RunTest(x, x_init_value, y, y_shape); } - + } // namespace } // namespace tensorflow diff --git a/tensorflow/compiler/jit/BUILD b/tensorflow/compiler/jit/BUILD index 4bc48d68864..e366db248a5 100644 --- a/tensorflow/compiler/jit/BUILD +++ b/tensorflow/compiler/jit/BUILD @@ -45,7 +45,7 @@ cc_library( visibility = ["//visibility:public"], deps = [ ":jit_compilation_passes", - "//tensorflow/compiler/jit/kernels:xla_local_launch_op", + "//tensorflow/compiler/jit/kernels:xla_launch_op", "//tensorflow/compiler/tf2xla/kernels:xla_ops", "//tensorflow/compiler/xla/service:cpu_plugin", ], @@ -57,7 +57,7 @@ cc_library( visibility = ["//visibility:public"], deps = if_cuda([ ":jit_compilation_passes", - "//tensorflow/compiler/jit/kernels:xla_local_launch_op", + "//tensorflow/compiler/jit/kernels:xla_launch_op", "//tensorflow/compiler/tf2xla/kernels:xla_ops", "//tensorflow/compiler/xla/service:gpu_plugin", ]), @@ -71,7 +71,7 @@ cc_library( deps = [ ":jit_compilation_passes", ":xla_device", - "//tensorflow/compiler/jit/kernels:xla_local_launch_op", + "//tensorflow/compiler/jit/kernels:xla_launch_op", "//tensorflow/compiler/tf2xla:xla_compiler", "//tensorflow/compiler/tf2xla/kernels:xla_ops", "//tensorflow/compiler/xla/service:cpu_plugin", # buildcleaner: keep @@ -88,7 +88,7 @@ cc_library( deps = [ ":jit_compilation_passes", ":xla_device", - "//tensorflow/compiler/jit/kernels:xla_local_launch_op", + "//tensorflow/compiler/jit/kernels:xla_launch_op", "//tensorflow/compiler/tf2xla:xla_compiler", "//tensorflow/compiler/tf2xla/kernels:xla_ops", "//tensorflow/compiler/xla/service:gpu_plugin", # buildcleaner: keep @@ -103,7 +103,7 @@ cc_library( srcs = ["xla_interpreter_device.cc"], deps = [ ":xla_device", - "//tensorflow/compiler/jit/kernels:xla_local_launch_op", + "//tensorflow/compiler/jit/kernels:xla_launch_op", "//tensorflow/compiler/tf2xla:xla_compiler", ], alwayslink = True, @@ -213,7 +213,7 @@ cc_library( deps = [ ":common", ":compilation_passes", - "//tensorflow/compiler/jit/kernels:xla_local_launch_op", + "//tensorflow/compiler/jit/kernels:xla_launch_op", "//tensorflow/compiler/tf2xla:const_analysis", "//tensorflow/compiler/tf2xla:xla_compiler", "//tensorflow/core:core_cpu_internal", @@ -297,7 +297,7 @@ tf_cc_test( "//tensorflow/cc:cc_ops_internal", "//tensorflow/cc:function_ops", "//tensorflow/cc:ops", - "//tensorflow/compiler/jit/kernels:xla_local_launch_op", + "//tensorflow/compiler/jit/kernels:xla_launch_op", "//tensorflow/compiler/tf2xla:xla_compiler", "//tensorflow/compiler/tf2xla/kernels:xla_ops", "//tensorflow/core:core_cpu", diff --git a/tensorflow/compiler/jit/create_xla_launch_op.cc b/tensorflow/compiler/jit/create_xla_launch_op.cc index 8bdd8b8dff1..18d901323f1 100644 --- a/tensorflow/compiler/jit/create_xla_launch_op.cc +++ b/tensorflow/compiler/jit/create_xla_launch_op.cc @@ -14,7 +14,7 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/compiler/jit/defs.h" -#include "tensorflow/compiler/jit/kernels/xla_local_launch_op.h" +#include "tensorflow/compiler/jit/kernels/xla_launch_op.h" #include "tensorflow/compiler/jit/mark_for_compilation_pass.h" #include "tensorflow/compiler/tf2xla/const_analysis.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" diff --git a/tensorflow/compiler/jit/kernels/BUILD b/tensorflow/compiler/jit/kernels/BUILD index e3780476249..b61b3b9845e 100644 --- a/tensorflow/compiler/jit/kernels/BUILD +++ b/tensorflow/compiler/jit/kernels/BUILD @@ -7,9 +7,9 @@ package( ) cc_library( - name = "xla_local_launch_op", - srcs = ["xla_local_launch_op.cc"], - hdrs = ["xla_local_launch_op.h"], + name = "xla_launch_op", + srcs = ["xla_launch_op.cc"], + hdrs = ["xla_launch_op.h"], deps = [ "//tensorflow/compiler/jit:common", "//tensorflow/compiler/jit:xla_compilation_cache", diff --git a/tensorflow/compiler/jit/kernels/xla_local_launch_op.cc b/tensorflow/compiler/jit/kernels/xla_launch_op.cc similarity index 99% rename from tensorflow/compiler/jit/kernels/xla_local_launch_op.cc rename to tensorflow/compiler/jit/kernels/xla_launch_op.cc index d9b5b2dd697..4460436b2e3 100644 --- a/tensorflow/compiler/jit/kernels/xla_local_launch_op.cc +++ b/tensorflow/compiler/jit/kernels/xla_launch_op.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "tensorflow/compiler/jit/kernels/xla_local_launch_op.h" +#include "tensorflow/compiler/jit/kernels/xla_launch_op.h" #include "tensorflow/compiler/jit/defs.h" #include "tensorflow/compiler/jit/xla_device.h" @@ -194,7 +194,7 @@ std::vector SnapshotResourceVariables(OpKernelContext* ctx, Var* variable = nullptr; ResourceHandle handle = HandleFromInput(ctx, first_variable + i); if (LookupResource(ctx, handle, &variable).ok()) { - mutex_lock lock(*variable->mu()); + tf_shared_lock lock(*variable->mu()); snapshot[i].name = handle.name(); snapshot[i].present = true; snapshot[i].value = *variable->tensor(); diff --git a/tensorflow/compiler/jit/kernels/xla_local_launch_op.h b/tensorflow/compiler/jit/kernels/xla_launch_op.h similarity index 100% rename from tensorflow/compiler/jit/kernels/xla_local_launch_op.h rename to tensorflow/compiler/jit/kernels/xla_launch_op.h diff --git a/tensorflow/compiler/jit/xla_cpu_device.cc b/tensorflow/compiler/jit/xla_cpu_device.cc index d2bbaba3ffe..57b9d6b56bc 100644 --- a/tensorflow/compiler/jit/xla_cpu_device.cc +++ b/tensorflow/compiler/jit/xla_cpu_device.cc @@ -16,7 +16,7 @@ limitations under the License. // Registers the XLA_CPU device, which is an XlaDevice instantiation that runs // operators using XLA via the XLA "Host" (CPU) backend. -#include "tensorflow/compiler/jit/kernels/xla_local_launch_op.h" +#include "tensorflow/compiler/jit/kernels/xla_launch_op.h" #include "tensorflow/compiler/jit/xla_device.h" #include "tensorflow/compiler/jit/xla_device_ops.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" diff --git a/tensorflow/compiler/jit/xla_gpu_device.cc b/tensorflow/compiler/jit/xla_gpu_device.cc index 0f0b5158b35..4474d8f4eb0 100644 --- a/tensorflow/compiler/jit/xla_gpu_device.cc +++ b/tensorflow/compiler/jit/xla_gpu_device.cc @@ -16,7 +16,7 @@ limitations under the License. // Registers the XLA_GPU device, which is an XlaDevice instantiation that runs // operators using XLA via the XLA "CUDA" (GPU) backend. -#include "tensorflow/compiler/jit/kernels/xla_local_launch_op.h" +#include "tensorflow/compiler/jit/kernels/xla_launch_op.h" #include "tensorflow/compiler/jit/xla_device.h" #include "tensorflow/compiler/jit/xla_device_ops.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" diff --git a/tensorflow/compiler/jit/xla_interpreter_device.cc b/tensorflow/compiler/jit/xla_interpreter_device.cc index ea6a9e8af08..4e4cbe200a2 100644 --- a/tensorflow/compiler/jit/xla_interpreter_device.cc +++ b/tensorflow/compiler/jit/xla_interpreter_device.cc @@ -15,7 +15,7 @@ limitations under the License. // Registers the XLA_INTERPRETER device which exposes the XLA Interpreter. -#include "tensorflow/compiler/jit/kernels/xla_local_launch_op.h" +#include "tensorflow/compiler/jit/kernels/xla_launch_op.h" #include "tensorflow/compiler/jit/xla_device.h" #include "tensorflow/compiler/jit/xla_device_ops.h" #include "tensorflow/compiler/tf2xla/xla_op_registry.h" diff --git a/tensorflow/compiler/xla/array2d.h b/tensorflow/compiler/xla/array2d.h index 593084a0c11..2737764cbda 100644 --- a/tensorflow/compiler/xla/array2d.h +++ b/tensorflow/compiler/xla/array2d.h @@ -165,6 +165,22 @@ class Array2D { return tensorflow::str_util::Join(pieces, ""); } + bool operator==(const Array2D& other) const { + if (n1() != other.n1() || n2() != other.n2()) { + return false; + } + for (int64 i0 = 0; i0 < n1(); ++i0) { + for (int64 i1 = 0; i1 < n2(); ++i1) { + if ((*this)(i0, i1) != other(i0, i1)) { + return false; + } + } + } + return true; + } + + bool operator!=(const Array2D& other) const { return !(*this == other); } + private: int64 n1_; int64 n2_; diff --git a/tensorflow/compiler/xla/array2d_test.cc b/tensorflow/compiler/xla/array2d_test.cc index 795d50ca5b5..c08e42c20ee 100644 --- a/tensorflow/compiler/xla/array2d_test.cc +++ b/tensorflow/compiler/xla/array2d_test.cc @@ -139,5 +139,46 @@ TEST(Array2dTest, Stringification) { EXPECT_EQ(expected, arr->ToString()); } +TEST(Array2dTest, Equals) { + Array2D arr0 = {{1, 2}, {3, 4}, {5, 6}}; + Array2D arr1 = {{1, 2}, {3, 4}, {5, 6}}; + EXPECT_TRUE(arr0 == arr1); + EXPECT_FALSE(arr0 != arr1); + EXPECT_TRUE(arr1 == arr0); + EXPECT_FALSE(arr1 != arr0); + + Array2D arr2 = {{1, 2}, {3, 4}, {5, 6}, {7, 8}}; + EXPECT_TRUE(arr0 != arr2); + EXPECT_FALSE(arr0 == arr2); + EXPECT_TRUE(arr2 != arr0); + EXPECT_FALSE(arr2 == arr0); + + Array2D arr3 = {{1, 2, 3}, {4, 5, 6}}; + EXPECT_TRUE(arr0 != arr3); + EXPECT_FALSE(arr0 == arr3); + EXPECT_TRUE(arr3 != arr0); + EXPECT_FALSE(arr3 == arr0); + + Array2D arr4 = {{1, 2}, {3, 4}}; + EXPECT_TRUE(arr0 != arr4); + EXPECT_FALSE(arr0 == arr4); + EXPECT_TRUE(arr4 != arr0); + EXPECT_FALSE(arr4 == arr0); + + Array2D arr5 = {{1, 2}, {13, 4}, {5, 6}}; + EXPECT_TRUE(arr0 != arr5); + EXPECT_FALSE(arr0 == arr5); + EXPECT_TRUE(arr5 != arr0); + EXPECT_FALSE(arr5 == arr0); + + Array2D bool_arr0 = {{false}, {true}}; + Array2D bool_arr1 = {{false}, {true}}; + EXPECT_TRUE(bool_arr0 == bool_arr1); + EXPECT_FALSE(bool_arr0 != bool_arr1); + Array2D bool_arr2 = {{false}, {false}}; + EXPECT_FALSE(bool_arr0 == bool_arr2); + EXPECT_TRUE(bool_arr0 != bool_arr2); +} + } // namespace } // namespace xla diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index 7285fb00d60..0f30eb44889 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -503,6 +503,7 @@ cc_library( "//tensorflow/compiler/xla:xla_data_proto", "//tensorflow/compiler/xla/legacy_flags:debug_options_flags", "//tensorflow/core:lib", + "//tensorflow/core:lib_internal", "//tensorflow/core:stream_executor_no_cuda", ], ) @@ -1966,6 +1967,20 @@ cc_library( alwayslink = 1, ) +tf_cc_test( + name = "hlo_graph_dumper_test", + srcs = ["hlo_graph_dumper_test.cc"], + deps = [ + ":hlo", + ":hlo_graph_dumper", + "//tensorflow/compiler/xla:test", + "//tensorflow/compiler/xla:xla_proto", + "//tensorflow/compiler/xla/tests:test_utils", + "//tensorflow/compiler/xla/tests:xla_internal_test_main", # fixdeps: keep + "//tensorflow/core:lib", + ], +) + cc_library( name = "transpose_folding", srcs = ["transpose_folding.cc"], diff --git a/tensorflow/compiler/xla/service/compile_only_service.cc b/tensorflow/compiler/xla/service/compile_only_service.cc index 62dab56a71c..c95670b1954 100644 --- a/tensorflow/compiler/xla/service/compile_only_service.cc +++ b/tensorflow/compiler/xla/service/compile_only_service.cc @@ -28,7 +28,9 @@ limitations under the License. #include "tensorflow/compiler/xla/types.h" #include "tensorflow/compiler/xla/util.h" #include "tensorflow/core/lib/gtl/cleanup.h" +#include "tensorflow/core/lib/io/path.h" #include "tensorflow/core/lib/strings/strcat.h" +#include "tensorflow/core/platform/host_info.h" #include "tensorflow/core/platform/logging.h" #include "tensorflow/core/platform/stream_executor_no_cuda.h" @@ -83,7 +85,10 @@ CompileOnlyService::CompileAheadOfTime( "computation_", versioned_handle.handle.handle(), "__", session_module->entry().name(), "__version_", versioned_handle.version); - TF_RETURN_IF_ERROR(Executable::DumpToDirectory(directory_path, filename, + const string& per_host_path = tensorflow::io::JoinPath( + directory_path, tensorflow::port::Hostname()); + + TF_RETURN_IF_ERROR(Executable::DumpToDirectory(per_host_path, filename, *session_module)); } diff --git a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc index 61e6f4f8e57..5b90b6b7f0d 100644 --- a/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc +++ b/tensorflow/compiler/xla/service/cpu/cpu_compiler.cc @@ -811,7 +811,7 @@ CpuCompiler::CompileAheadOfTime(std::vector> modules, /*is_entry_computation=*/true, &module_sequence.at(computation))); - entry_function->setName(llvm_ir::AsStringRef(entry_point_name)); + CHECK(entry_function->getName() == llvm_ir::AsStringRef(entry_point_name)); ModuleHook pre_optimization_ir_dump_hook; ModuleHook post_optimization_ir_dump_hook; diff --git a/tensorflow/compiler/xla/service/hlo_graph_dumper.cc b/tensorflow/compiler/xla/service/hlo_graph_dumper.cc index bba6fbfae04..39edfffcee5 100644 --- a/tensorflow/compiler/xla/service/hlo_graph_dumper.cc +++ b/tensorflow/compiler/xla/service/hlo_graph_dumper.cc @@ -340,11 +340,8 @@ class HloDotDumper { string Header(); string Footer(); - // Maps HloComputations we should dump to their parent instruction in the - // outer computation. - std::unordered_map - SubcomputationsToDump(); - + bool ShouldShowSubcomputation(const HloComputation* subcomp); + bool ShouldShowFusionSubcomputation(const HloInstruction* instr); string DumpSubcomputation(const HloComputation* subcomp, const HloInstruction* parent_instr); string DumpComputation(const HloComputation* comp); @@ -401,11 +398,6 @@ class HloDotDumper { string HloDotDumper::Dump() { string body; - for (const auto& kv : SubcomputationsToDump()) { - const HloComputation* subcomp = kv.first; - const HloInstruction* parent = kv.second; - StrAppend(&body, DumpSubcomputation(subcomp, parent)); - } StrAppend(&body, DumpComputation(computation_)); StrAppend(&body, DumpRootTag()); @@ -525,33 +517,36 @@ stylesheet=" string HloDotDumper::Footer() { return StrCat(Join(edges_, "\n"), "\n}"); } -std::unordered_map -HloDotDumper::SubcomputationsToDump() { - // Dump the subcomputations of each instruction that's shown and doesn't have - // its operands omitted. If an instruction has just one subcomputation and - // it's trivial, omit it: We'll display that subcomputation inlined into the - // instruction's node when we draw it. - std::unordered_map to_dump; - for (const auto& instr : computation_->instructions()) { - if (!filter_.Show(instr.get()) || - filter_.SomeOrAllOperandsOmitted(instr.get())) { - continue; - } - if (instr->opcode() == HloOpcode::kFusion) { - to_dump[instr->fused_instructions_computation()] = instr.get(); - } +bool HloDotDumper::ShouldShowFusionSubcomputation(const HloInstruction* instr) { + CHECK_EQ(instr->opcode(), HloOpcode::kFusion); + return ShouldShowSubcomputation(instr->fused_instructions_computation()); +} - for (const HloComputation* comp : instr->called_computations()) { - if (!MatchTrivialComputation(comp)) { - to_dump[comp] = instr.get(); - } +bool HloDotDumper::ShouldShowSubcomputation(const HloComputation* subcomp) { + if (subcomp->IsFusionComputation()) { + const HloInstruction* fusion = subcomp->FusionInstruction(); + if (!filter_.Show(fusion) || filter_.SomeOrAllOperandsOmitted(fusion)) { + return false; } } - return to_dump; + + // Don't show trivial subcomputations on non-fusion nodes -- these are inlined + // into the graph. + if (!subcomp->IsFusionComputation() && MatchTrivialComputation(subcomp)) { + return false; + } + + // Show the subcomputation if we're showing any of its members. + return std::any_of(computation_->instructions().begin(), + computation_->instructions().end(), + [&](const std::unique_ptr& instr) { + return filter_.Show(instr.get()); + }); } string HloDotDumper::DumpSubcomputation(const HloComputation* subcomp, const HloInstruction* parent_instr) { + VLOG(2) << "Dumping subcomputation " << subcomp->name(); const char* computation_fmt = R"(subgraph %s { %s label = <%s>; @@ -593,20 +588,10 @@ tooltip = " "; string comp_body = DumpComputation(subcomp); - if (parent_instr->opcode() == HloOpcode::kFusion) { - // Dump any nested fusion nodes. - for (const auto& subcomp_instr : subcomp->instructions()) { - if (subcomp_instr->opcode() == HloOpcode::kFusion) { - StrAppend( - &comp_body, - DumpSubcomputation(subcomp_instr->fused_instructions_computation(), - subcomp_instr.get())); - } - } - } else { - // Add an edge from the subcomputation to its parent node. If subcomp - // belongs to a fusion node, it's drawn in place of the fusion instruction, - // so there's no need to link those. + // Add an edge from the subcomputation to its parent node. If subcomp + // belongs to a fusion node, it's drawn in place of the fusion instruction, + // so there's no need to link those. + if (parent_instr->opcode() != HloOpcode::kFusion) { VLOG(2) << "Edge: from " << subcomp->root_instruction()->name() << " to " << parent_instr->name() << " as " << next_edge_id_; edge_ids_.insert( @@ -631,6 +616,14 @@ string HloDotDumper::DumpComputation(const HloComputation* comp) { if (!filter_.Show(instr.get())) { continue; } + + // Dump subcomputations within instr. + for (const HloComputation* subcomp : instr->called_computations()) { + if (ShouldShowSubcomputation(subcomp)) { + StrAppend(&g, DumpSubcomputation(subcomp, instr.get())); + } + } + StrAppend(&g, DumpInstruction(instr.get())); } return g; @@ -638,6 +631,14 @@ string HloDotDumper::DumpComputation(const HloComputation* comp) { string HloDotDumper::DumpRootTag() { HloInstruction* from = computation_->root_instruction(); + + // Fusion nodes are expanded inline, so if root is an expanded fusion node, + // walk up the graph until we find a node that isn't. + while (from->opcode() == HloOpcode::kFusion && + ShouldShowFusionSubcomputation(from)) { + from = from->fused_expression_root(); + } + auto from_id = InstructionId(from); if (!filter_.Show(from)) { @@ -678,7 +679,7 @@ string HloDotDumper::DumpInstruction(const HloInstruction* instr) { // Omit the fusion node if its subcomputation is drawn, since the // subcomputation will be drawn inline. if (instr->opcode() == HloOpcode::kFusion && - filter_.ShowFusionSubcomputation(instr)) { + ShouldShowFusionSubcomputation(instr)) { return ""; } @@ -937,7 +938,7 @@ string HloDotDumper::GetInstructionNodeExtraInfo(const HloInstruction* instr) { // Show the shape and layout of the instruction, unless it's an inlined fusion // node -- there the shape and layout is present in the output node. if (instr->opcode() != HloOpcode::kFusion || - !filter_.ShowFusionSubcomputation(instr)) { + !ShouldShowFusionSubcomputation(instr)) { string instr_shape = ShapeUtil::HumanString(instr->shape()); // Show layout of non-tuple shapes with more than one dimension. @@ -982,7 +983,7 @@ void HloDotDumper::AddInstructionIncomingEdges(const HloInstruction* instr) { // fusion node and the node's subcomputation is shown, we draw our edge // starting at the fusion node's root instead of at the fusion node itself. if (from->opcode() == HloOpcode::kFusion && - filter_.ShowFusionSubcomputation(from)) { + ShouldShowFusionSubcomputation(from)) { from = from->fused_expression_root(); } if (!filter_.Show(from) || from->opcode() == HloOpcode::kConstant) { @@ -1147,6 +1148,11 @@ NodeFilter MakeNodeFilter(const HloInstruction* root, int64 radius) { } } + // Traverse into instr's nested computations. + for (const HloComputation* computation : instr->called_computations()) { + worklist.push_back({computation->root_instruction(), depth + 1}); + } + // Traverse into instr's users, unless: // // - there are a ton of them, in which case they're probably not diff --git a/tensorflow/compiler/xla/service/hlo_graph_dumper_test.cc b/tensorflow/compiler/xla/service/hlo_graph_dumper_test.cc new file mode 100644 index 00000000000..4015ee6cace --- /dev/null +++ b/tensorflow/compiler/xla/service/hlo_graph_dumper_test.cc @@ -0,0 +1,122 @@ +/* Copyright 2017 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/compiler/xla/service/hlo_graph_dumper.h" + +#include "tensorflow/compiler/xla/service/hlo_computation.h" +#include "tensorflow/compiler/xla/service/hlo_instruction.h" +#include "tensorflow/compiler/xla/service/hlo_module.h" +#include "tensorflow/compiler/xla/service/hlo_opcode.h" +#include "tensorflow/compiler/xla/test.h" +#include "tensorflow/compiler/xla/tests/test_utils.h" +#include "tensorflow/compiler/xla/xla.pb.h" +#include "tensorflow/core/lib/strings/strcat.h" + +namespace xla { +namespace { + +using ::tensorflow::strings::StrCat; +using ::testing::HasSubstr; + +string TestName() { + return ::testing::UnitTest::GetInstance()->current_test_info()->name(); +} + +class DotRenderer : public hlo_graph_dumper::GraphRendererInterface { + public: + string RenderGraph(const string& graph, GraphKind graph_kind, + const DebugOptions& debug_options) override { + return graph; + } + + private: + string last_graph_; +}; + +XLA_REGISTER_GRAPH_RENDERER(DotRenderer, std::numeric_limits::max()); + +TEST(HloGraphDumperTest, NestedFusion) { + HloComputation::Builder b("b"); + + // Build param0 + param1 + param2 + param3 + param4. + auto shape = ShapeUtil::MakeShape(F32, {10, 100}); + std::vector params; + for (int i = 0; i <= 4; ++i) { + params.push_back(b.AddInstruction( + HloInstruction::CreateParameter(i, shape, StrCat("param", i)))); + } + std::vector sums; + sums.push_back(b.AddInstruction(HloInstruction::CreateBinary( + shape, HloOpcode::kAdd, params[0], params[1]))); + for (int i = 0; i <= 2; ++i) { + sums.push_back(b.AddInstruction(HloInstruction::CreateBinary( + shape, HloOpcode::kAdd, sums[i], params[i + 2]))); + } + + HloModule m(TestName()); + m.AddEntryComputation(b.Build()); + HloComputation* root_computation = m.entry_computation(); + + // Fuse into fusion(param0 + param1 + param2 + param3 + param4). + auto* outer_fusion = root_computation->CreateFusionInstruction( + {sums[3], sums[2], sums[1], sums[0]}, HloInstruction::FusionKind::kLoop); + + // Fusing invalidates the pointers in sums -- the instructions are cloned when + // they're moved to the new computation. Get the updated pointers to sums. + std::vector fused_sums; + for (auto* instr : outer_fusion->fused_instructions_computation() + ->MakeInstructionPostOrder()) { + if (instr->opcode() == HloOpcode::kAdd) { + fused_sums.push_back(instr); + } + } + + // Fuse into fusion(fusion(param0 + param1 + param2) + param3 + param4). + auto* inner_fusion = + outer_fusion->fused_instructions_computation()->CreateFusionInstruction( + {fused_sums[1], fused_sums[0]}, HloInstruction::FusionKind::kLoop); + + // Generate the graph; all nodes should be present. + string graph = hlo_graph_dumper::DumpGraph(*root_computation, /*label=*/"", + DebugOptions()); + for (const HloComputation* computation : + {root_computation, // + inner_fusion->fused_instructions_computation(), + outer_fusion->fused_instructions_computation()}) { + for (const std::unique_ptr& instruction : + computation->instructions()) { + EXPECT_THAT(graph, HasSubstr(instruction->name())); + } + } + + // Dump a neighborhood around one of the inner sum nodes. We don't really + // care that the outer nodes are omitted -- whether they are or not is based + // fiddly heuristics -- but we do care that the node we asked for is printed. + const HloInstruction* inner_sum = nullptr; + for (const std::unique_ptr& instruction : + inner_fusion->fused_instructions_computation()->instructions()) { + if (instruction->opcode() == HloOpcode::kAdd) { + inner_sum = instruction.get(); + break; + } + } + ASSERT_NE(inner_sum, nullptr); + EXPECT_THAT( + hlo_graph_dumper::DumpNeighborhoodAround(*inner_sum, /*radius=*/1), + HasSubstr(inner_sum->name())); +} + +} // anonymous namespace +} // namespace xla diff --git a/tensorflow/compiler/xla/service/service.cc b/tensorflow/compiler/xla/service/service.cc index 00fe55419d6..def440888e9 100644 --- a/tensorflow/compiler/xla/service/service.cc +++ b/tensorflow/compiler/xla/service/service.cc @@ -930,9 +930,10 @@ tensorflow::Status Service::TransferToClient(const TransferToClientRequest* arg, } Literal literal; - auto status = LiteralFromAllocation(allocation, *literal_shape, &literal); + TF_RETURN_IF_ERROR( + LiteralFromAllocation(allocation, *literal_shape, &literal)); *result->mutable_literal() = literal.ToProto(); - return status; + return tensorflow::Status::OK(); } tensorflow::Status Service::TransferToServer(const TransferToServerRequest* arg, diff --git a/tensorflow/compiler/xla/tests/reduce_window_test.cc b/tensorflow/compiler/xla/tests/reduce_window_test.cc index 6ef5c4a8c8b..7b7f2687286 100644 --- a/tensorflow/compiler/xla/tests/reduce_window_test.cc +++ b/tensorflow/compiler/xla/tests/reduce_window_test.cc @@ -32,6 +32,8 @@ limitations under the License. #include "tensorflow/compiler/xla/tests/literal_test_util.h" #include "tensorflow/compiler/xla/tests/test_macros.h" #include "tensorflow/compiler/xla/xla_data.pb.h" +#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/lib/core/status_test_util.h" #include "tensorflow/core/lib/gtl/array_slice.h" #include "tensorflow/core/platform/test.h" #include "tensorflow/core/platform/types.h" @@ -320,6 +322,81 @@ TEST_F(ReduceWindowTest, R4UnitWindow) { ErrorSpec(1e-3, 1e-3)); } +XLA_TEST_F(HloTestBase, R6AddMultipleStrides) { + auto b = HloComputation::Builder(TestName()); + + std::vector input_dims(6, 8); + auto shape = ShapeUtil::MakeShape(F32, input_dims); + + std::unique_ptr arg_literal = Literal::CreateFromShape(shape); + auto generator = [&](tensorflow::gtl::ArraySlice indexes) -> float { + return 1.0f; + }; + TF_EXPECT_OK(arg_literal->Populate(generator)); + + auto input = + b.AddInstruction(HloInstruction::CreateConstant(std::move(arg_literal))); + + auto init_value = b.AddInstruction( + HloInstruction::CreateConstant(Literal::CreateR0(0.f))); + + HloComputation::Builder add_computation("add"); + Shape scalar_shape = ShapeUtil::MakeShape(F32, {}); + auto param_lhs = add_computation.AddInstruction( + HloInstruction::CreateParameter(0, scalar_shape, "lhs")); + auto param_rhs = add_computation.AddInstruction( + HloInstruction::CreateParameter(1, scalar_shape, "rhs")); + add_computation.AddInstruction(HloInstruction::CreateBinary( + scalar_shape, HloOpcode::kAdd, param_lhs, param_rhs)); + + auto module = CreateNewModule(); + auto add_func = module->AddEmbeddedComputation(add_computation.Build()); + + WindowDimension trivial_dim; + trivial_dim.set_size(1); + trivial_dim.set_stride(1); + trivial_dim.set_padding_low(0); + trivial_dim.set_padding_high(0); + trivial_dim.set_window_dilation(1); + trivial_dim.set_base_dilation(1); + + WindowDimension active_dim; + active_dim.set_size(3); + active_dim.set_stride(1); + active_dim.set_padding_low(0); + active_dim.set_padding_high(0); + active_dim.set_window_dilation(1); + active_dim.set_base_dilation(1); + + Window window; + *window.add_dimensions() = active_dim; + *window.add_dimensions() = trivial_dim; + *window.add_dimensions() = active_dim; + *window.add_dimensions() = active_dim; + *window.add_dimensions() = trivial_dim; + *window.add_dimensions() = trivial_dim; + + // Non-monotonic output layout with minor dims trivial. + std::vector output_layout = {1, 5, 3, 2, 0, 4}; + std::vector output_dims = {6, 8, 6, 6, 8, 8}; + Shape result_shape = + ShapeUtil::MakeShapeWithLayout(F32, output_dims, output_layout); + b.AddInstruction(HloInstruction::CreateReduceWindow( + result_shape, input, init_value, window, add_func)); + + std::unique_ptr expected = Literal::CreateFromShape(result_shape); + auto out_generator = + [&](tensorflow::gtl::ArraySlice indexes) -> float { + return 27.0f; + }; + TF_EXPECT_OK(expected->Populate(out_generator)); + + module->AddEntryComputation(b.Build()); + auto actual = ExecuteAndTransfer(std::move(module), {}); + + LiteralTestUtil::ExpectNear(*actual, *expected, ErrorSpec(1e-3, 1e-3)); +} + XLA_TEST_F(HloTestBase, R6Add) { auto b = HloComputation::Builder(TestName()); diff --git a/tensorflow/contrib/boosted_trees/BUILD b/tensorflow/contrib/boosted_trees/BUILD index 7b20d31e27c..30f12d02f2a 100644 --- a/tensorflow/contrib/boosted_trees/BUILD +++ b/tensorflow/contrib/boosted_trees/BUILD @@ -222,9 +222,6 @@ py_test( size = "small", srcs = ["python/kernel_tests/split_handler_ops_test.py"], srcs_version = "PY2AND3", - tags = [ - "nomac", # b/63258195 - ], deps = [ ":split_handler_ops_py", "//tensorflow/contrib/boosted_trees/proto:learner_proto_py", diff --git a/tensorflow/contrib/boosted_trees/estimator_batch/BUILD b/tensorflow/contrib/boosted_trees/estimator_batch/BUILD index fc7b48904c8..f9e186788f6 100644 --- a/tensorflow/contrib/boosted_trees/estimator_batch/BUILD +++ b/tensorflow/contrib/boosted_trees/estimator_batch/BUILD @@ -90,9 +90,6 @@ py_test( size = "small", srcs = ["custom_export_strategy_test.py"], srcs_version = "PY2AND3", - tags = [ - "nomac", # b/63258195 - ], deps = [ ":custom_export_strategy", "//tensorflow/contrib/decision_trees/proto:generic_tree_model_extensions_py", diff --git a/tensorflow/contrib/boosted_trees/lib/BUILD b/tensorflow/contrib/boosted_trees/lib/BUILD index 314fe104a8b..d4d405c3a9a 100644 --- a/tensorflow/contrib/boosted_trees/lib/BUILD +++ b/tensorflow/contrib/boosted_trees/lib/BUILD @@ -281,9 +281,6 @@ py_test( name = "categorical_split_handler_test", srcs = ["learner/batch/categorical_split_handler_test.py"], srcs_version = "PY2AND3", - tags = [ - "nomac", # b/63258195 - ], deps = [ ":categorical_split_handler", "//tensorflow/contrib/boosted_trees/proto:learner_proto_py", @@ -309,9 +306,6 @@ py_test( name = "ordinal_split_handler_test", srcs = ["learner/batch/ordinal_split_handler_test.py"], srcs_version = "PY2AND3", - tags = [ - "nomac", # b/63258195 - ], deps = [ ":ordinal_split_handler", "//tensorflow/contrib/boosted_trees/proto:learner_proto_py", diff --git a/tensorflow/contrib/cmake/tf_python.cmake b/tensorflow/contrib/cmake/tf_python.cmake index e033289e0b1..400f007ee75 100755 --- a/tensorflow/contrib/cmake/tf_python.cmake +++ b/tensorflow/contrib/cmake/tf_python.cmake @@ -204,6 +204,9 @@ add_python_module("tensorflow/examples/tutorials") add_python_module("tensorflow/examples/tutorials/mnist") add_python_module("tensorflow/python") add_python_module("tensorflow/python/client") +add_python_module("tensorflow/python/data") +add_python_module("tensorflow/python/data/ops") +add_python_module("tensorflow/python/data/util") add_python_module("tensorflow/python/debug") add_python_module("tensorflow/python/debug/cli") add_python_module("tensorflow/python/debug/examples") @@ -237,6 +240,7 @@ add_python_module("tensorflow/python/keras/datasets/cifar100") add_python_module("tensorflow/python/keras/datasets/imdb") add_python_module("tensorflow/python/keras/datasets/mnist") add_python_module("tensorflow/python/keras/datasets/reuters") +add_python_module("tensorflow/python/keras/estimator") add_python_module("tensorflow/python/keras/initializers") add_python_module("tensorflow/python/keras/layers") add_python_module("tensorflow/python/keras/losses") @@ -333,7 +337,6 @@ add_python_module("tensorflow/contrib/data") add_python_module("tensorflow/contrib/data/python") add_python_module("tensorflow/contrib/data/python/kernel_tests") add_python_module("tensorflow/contrib/data/python/ops") -add_python_module("tensorflow/contrib/data/python/util") add_python_module("tensorflow/contrib/decision_trees") add_python_module("tensorflow/contrib/decision_trees/proto") add_python_module("tensorflow/contrib/deprecated") diff --git a/tensorflow/contrib/data/BUILD b/tensorflow/contrib/data/BUILD index c417650a96f..1c3a798c5fa 100644 --- a/tensorflow/contrib/data/BUILD +++ b/tensorflow/contrib/data/BUILD @@ -12,6 +12,7 @@ py_library( "//tensorflow/contrib/data/python/ops:dataset_ops", "//tensorflow/contrib/data/python/ops:sloppy_ops", "//tensorflow/python:util", + "//tensorflow/python/data/ops:dataset_ops", ], ) diff --git a/tensorflow/contrib/data/__init__.py b/tensorflow/contrib/data/__init__.py index aaf080f7d8d..67dff0a4ab0 100644 --- a/tensorflow/contrib/data/__init__.py +++ b/tensorflow/contrib/data/__init__.py @@ -21,10 +21,15 @@ @@TextLineDataset @@batch_and_drop_remainder -@@read_batch_features -@@rejection_resample +@@dense_to_sparse_batch +@@enumerate_dataset @@group_by_window +@@ignore_errors +@@read_batch_features +@@unbatch +@@rejection_resample @@sloppy_interleave + """ from __future__ import absolute_import @@ -34,15 +39,19 @@ from __future__ import print_function # pylint: disable=unused-import from tensorflow.contrib.data.python.ops.dataset_ops import batch_and_drop_remainder from tensorflow.contrib.data.python.ops.dataset_ops import Dataset +from tensorflow.contrib.data.python.ops.dataset_ops import dense_to_sparse_batch +from tensorflow.contrib.data.python.ops.dataset_ops import enumerate_dataset from tensorflow.contrib.data.python.ops.dataset_ops import FixedLengthRecordDataset from tensorflow.contrib.data.python.ops.dataset_ops import group_by_window -from tensorflow.contrib.data.python.ops.dataset_ops import Iterator +from tensorflow.contrib.data.python.ops.dataset_ops import ignore_errors from tensorflow.contrib.data.python.ops.dataset_ops import read_batch_features from tensorflow.contrib.data.python.ops.dataset_ops import rejection_resample from tensorflow.contrib.data.python.ops.dataset_ops import SqlDataset from tensorflow.contrib.data.python.ops.dataset_ops import TextLineDataset from tensorflow.contrib.data.python.ops.dataset_ops import TFRecordDataset +from tensorflow.contrib.data.python.ops.dataset_ops import unbatch from tensorflow.contrib.data.python.ops.sloppy_ops import sloppy_interleave +from tensorflow.python.data.ops.dataset_ops import Iterator # pylint: enable=unused-import from tensorflow.python.util.all_util import remove_undocumented diff --git a/tensorflow/contrib/data/python/kernel_tests/BUILD b/tensorflow/contrib/data/python/kernel_tests/BUILD index 4e5bb9086cf..aa047803e9e 100644 --- a/tensorflow/contrib/data/python/kernel_tests/BUILD +++ b/tensorflow/contrib/data/python/kernel_tests/BUILD @@ -89,6 +89,7 @@ py_test( "//tensorflow/python:math_ops", "//tensorflow/python:string_ops", "//tensorflow/python:tensor_shape", + "//tensorflow/python/data/ops:dataset_ops", "//third_party/py/numpy", ], ) @@ -104,13 +105,17 @@ py_test( ], deps = [ "//tensorflow/contrib/data/python/ops:dataset_ops", + "//tensorflow/core:protos_all_py", "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:dtypes", "//tensorflow/python:errors", + "//tensorflow/python:framework_ops", "//tensorflow/python:math_ops", + "//tensorflow/python:resource_variable_ops", + "//tensorflow/python:session", "//tensorflow/python:sparse_tensor", - "//tensorflow/python:util", + "//tensorflow/python/data/util:nest", "//third_party/py/numpy", ], ) @@ -195,6 +200,7 @@ py_test( "//tensorflow/python:data_flow_ops", "//tensorflow/python:dtypes", "//tensorflow/python:errors", + "//tensorflow/python:functional_ops", "//tensorflow/python:io_ops", "//tensorflow/python:lookup_ops", "//tensorflow/python:math_ops", @@ -217,9 +223,13 @@ py_test( "//tensorflow/python:array_ops", "//tensorflow/python:client_testlib", "//tensorflow/python:constant_op", + "//tensorflow/python:dataset_ops_gen", "//tensorflow/python:dtypes", "//tensorflow/python:errors", + "//tensorflow/python:framework_ops", + "//tensorflow/python:platform", "//tensorflow/python:tensor_shape", + "//tensorflow/python:variables", ], ) @@ -358,10 +368,10 @@ py_test( srcs_version = "PY2AND3", deps = [ "//tensorflow/contrib/data/python/ops:dataset_ops", - "//tensorflow/contrib/data/python/util:nest", "//tensorflow/python:client_testlib", "//tensorflow/python:errors", "//tensorflow/python:tensor_shape", + "//tensorflow/python/data/util:nest", "//third_party/py/numpy", ], ) diff --git a/tensorflow/contrib/data/python/kernel_tests/batch_dataset_op_test.py b/tensorflow/contrib/data/python/kernel_tests/batch_dataset_op_test.py index c6afbd23ab4..4a7fb1b8b06 100644 --- a/tensorflow/contrib/data/python/kernel_tests/batch_dataset_op_test.py +++ b/tensorflow/contrib/data/python/kernel_tests/batch_dataset_op_test.py @@ -229,8 +229,9 @@ class BatchDatasetTest(test.TestCase): def testDenseToSparseBatchDataset(self): components = np.random.randint(12, size=(100,)).astype(np.int32) iterator = (dataset_ops.Dataset.from_tensor_slices(components) - .map(lambda x: array_ops.fill([x], x)).dense_to_sparse_batch( - 4, [12]).make_initializable_iterator()) + .map(lambda x: array_ops.fill([x], x)).apply( + dataset_ops.dense_to_sparse_batch(4, [12])) + .make_initializable_iterator()) init_op = iterator.initializer get_next = sparse_tensor.SparseTensor(*iterator.get_next()) @@ -253,8 +254,9 @@ class BatchDatasetTest(test.TestCase): def testDenseToSparseBatchDatasetShapeErrors(self): input_tensor = array_ops.placeholder(dtypes.int32) - iterator = (dataset_ops.Dataset.from_tensors(input_tensor) - .dense_to_sparse_batch(4, [12]).make_initializable_iterator()) + iterator = (dataset_ops.Dataset.from_tensors(input_tensor).apply( + dataset_ops.dense_to_sparse_batch(4, [12])) + .make_initializable_iterator()) init_op = iterator.initializer get_next = sparse_tensor.SparseTensor(*iterator.get_next()) @@ -277,7 +279,7 @@ class BatchDatasetTest(test.TestCase): expected_types = (dtypes.int32,) * 3 data = data.batch(2) self.assertEqual(expected_types, data.output_types) - data = data.unbatch() + data = data.apply(dataset_ops.unbatch()) self.assertEqual(expected_types, data.output_types) iterator = data.make_one_shot_iterator() @@ -296,7 +298,7 @@ class BatchDatasetTest(test.TestCase): expected_types = ((dtypes.int32,),) * 3 data = data.batch(2) self.assertEqual(expected_types, data.output_types) - data = data.unbatch() + data = data.apply(dataset_ops.unbatch()) self.assertEqual(expected_types, data.output_types) iterator = data.make_one_shot_iterator() @@ -317,7 +319,7 @@ class BatchDatasetTest(test.TestCase): expected_types = ((dtypes.int32, dtypes.string),) * 3 data = data.batch(2) self.assertAllEqual(expected_types, data.output_types) - data = data.unbatch() + data = data.apply(dataset_ops.unbatch()) self.assertAllEqual(expected_types, data.output_types) iterator = data.make_one_shot_iterator() diff --git a/tensorflow/contrib/data/python/kernel_tests/concatenate_dataset_op_test.py b/tensorflow/contrib/data/python/kernel_tests/concatenate_dataset_op_test.py index 3407a06a984..a77f3232ceb 100644 --- a/tensorflow/contrib/data/python/kernel_tests/concatenate_dataset_op_test.py +++ b/tensorflow/contrib/data/python/kernel_tests/concatenate_dataset_op_test.py @@ -20,7 +20,7 @@ from __future__ import print_function import numpy as np from tensorflow.contrib.data.python.ops import dataset_ops -from tensorflow.contrib.data.python.util import nest +from tensorflow.python.data.util import nest from tensorflow.python.framework import errors from tensorflow.python.framework import tensor_shape from tensorflow.python.platform import test diff --git a/tensorflow/contrib/data/python/kernel_tests/dataset_constructor_op_test.py b/tensorflow/contrib/data/python/kernel_tests/dataset_constructor_op_test.py index fb1305f735e..acbd117a331 100644 --- a/tensorflow/contrib/data/python/kernel_tests/dataset_constructor_op_test.py +++ b/tensorflow/contrib/data/python/kernel_tests/dataset_constructor_op_test.py @@ -22,9 +22,9 @@ import threading import numpy as np from tensorflow.contrib.data.python.ops import dataset_ops -from tensorflow.contrib.data.python.util import nest from tensorflow.core.protobuf import config_pb2 from tensorflow.python.client import session +from tensorflow.python.data.util import nest from tensorflow.python.framework import dtypes from tensorflow.python.framework import errors from tensorflow.python.framework import ops diff --git a/tensorflow/contrib/data/python/kernel_tests/map_dataset_op_test.py b/tensorflow/contrib/data/python/kernel_tests/map_dataset_op_test.py index 4c1496ccf98..49d3d4c260b 100644 --- a/tensorflow/contrib/data/python/kernel_tests/map_dataset_op_test.py +++ b/tensorflow/contrib/data/python/kernel_tests/map_dataset_op_test.py @@ -271,8 +271,8 @@ class MapDatasetTest(test.TestCase): components = np.array([1., 2., 3., np.nan, 5.]).astype(np.float32) dataset = (dataset_ops.Dataset.from_tensor_slices(components) - .map(lambda x: array_ops.check_numerics(x, "message")) - .ignore_errors()) + .map(lambda x: array_ops.check_numerics(x, "message")).apply( + dataset_ops.ignore_errors())) iterator = dataset.make_initializable_iterator() init_op = iterator.initializer get_next = iterator.get_next() @@ -287,10 +287,10 @@ class MapDatasetTest(test.TestCase): def testParallelMapIgnoreError(self): components = np.array([1., 2., 3., np.nan, 5.]).astype(np.float32) - dataset = (dataset_ops.Dataset.from_tensor_slices(components) - .map(lambda x: array_ops.check_numerics(x, "message"), - num_threads=2, output_buffer_size=2) - .ignore_errors()) + dataset = (dataset_ops.Dataset.from_tensor_slices(components).map( + lambda x: array_ops.check_numerics(x, "message"), + num_threads=2, + output_buffer_size=2).apply(dataset_ops.ignore_errors())) iterator = dataset.make_initializable_iterator() init_op = iterator.initializer get_next = iterator.get_next() @@ -311,9 +311,9 @@ class MapDatasetTest(test.TestCase): for filename in filenames: write_string_to_file(filename, filename) - dataset = (dataset_ops.Dataset.from_tensor_slices(filenames) - .map(io_ops.read_file, num_threads=2, output_buffer_size=2) - .ignore_errors()) + dataset = (dataset_ops.Dataset.from_tensor_slices(filenames).map( + io_ops.read_file, num_threads=2, output_buffer_size=2).apply( + dataset_ops.ignore_errors())) iterator = dataset.make_initializable_iterator() init_op = iterator.initializer get_next = iterator.get_next() diff --git a/tensorflow/contrib/data/python/kernel_tests/range_dataset_op_test.py b/tensorflow/contrib/data/python/kernel_tests/range_dataset_op_test.py index 87bab43ccf5..faa4d187aca 100644 --- a/tensorflow/contrib/data/python/kernel_tests/range_dataset_op_test.py +++ b/tensorflow/contrib/data/python/kernel_tests/range_dataset_op_test.py @@ -169,8 +169,8 @@ class RangeDatasetTest(test.TestCase): components = (["a", "b"], [1, 2], [37.0, 38]) start = constant_op.constant(20, dtype=dtypes.int64) - iterator = (dataset_ops.Dataset.from_tensor_slices(components).enumerate( - start=start).make_initializable_iterator()) + iterator = (dataset_ops.Dataset.from_tensor_slices(components).apply( + dataset_ops.enumerate_dataset(start)).make_initializable_iterator()) init_op = iterator.initializer get_next = iterator.get_next() diff --git a/tensorflow/contrib/data/python/ops/BUILD b/tensorflow/contrib/data/python/ops/BUILD index 1abbf38d1a6..f429cc49de0 100644 --- a/tensorflow/contrib/data/python/ops/BUILD +++ b/tensorflow/contrib/data/python/ops/BUILD @@ -9,9 +9,7 @@ py_library( srcs = ["dataset_ops.py"], srcs_version = "PY2AND3", deps = [ - "//tensorflow/contrib/data/python/util:nest", "//tensorflow/python:array_ops", - "//tensorflow/python:constant_op", "//tensorflow/python:control_flow_ops", "//tensorflow/python:dataset_ops_gen", "//tensorflow/python:dtypes", @@ -22,12 +20,13 @@ py_library( "//tensorflow/python:parsing_ops", "//tensorflow/python:platform", "//tensorflow/python:random_ops", - "//tensorflow/python:random_seed", "//tensorflow/python:resource_variable_ops", "//tensorflow/python:script_ops", "//tensorflow/python:sparse_tensor", "//tensorflow/python:tensor_shape", "//tensorflow/python:tensor_util", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/util:nest", "//third_party/py/numpy", ], ) @@ -37,13 +36,12 @@ py_library( srcs = ["sloppy_ops.py"], srcs_version = "PY2AND3", deps = [ - ":dataset_ops", - "//tensorflow/contrib/data/python/util:nest", "//tensorflow/python:dataset_ops_gen", "//tensorflow/python:dtypes", "//tensorflow/python:framework_ops", "//tensorflow/python:function", - "//tensorflow/python:platform", + "//tensorflow/python/data/ops:dataset_ops", + "//tensorflow/python/data/util:nest", ], ) diff --git a/tensorflow/contrib/data/python/ops/dataset_ops.py b/tensorflow/contrib/data/python/ops/dataset_ops.py index 5ed5949acaf..945b673c9ef 100644 --- a/tensorflow/contrib/data/python/ops/dataset_ops.py +++ b/tensorflow/contrib/data/python/ops/dataset_ops.py @@ -17,18 +17,16 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import abc -import collections -import threading - import numpy as np -from tensorflow.contrib.data.python.util import nest -from tensorflow.python.framework import constant_op +from tensorflow.python.data.ops import dataset_ops +# pylint: disable=unused-import +from tensorflow.python.data.ops.dataset_ops import Iterator +# pylint: enable=unused-import +from tensorflow.python.data.util import nest from tensorflow.python.framework import dtypes from tensorflow.python.framework import function from tensorflow.python.framework import ops -from tensorflow.python.framework import random_seed from tensorflow.python.framework import sparse_tensor as sparse_tensor_lib from tensorflow.python.framework import tensor_shape from tensorflow.python.framework import tensor_util @@ -45,487 +43,28 @@ from tensorflow.python.ops import script_ops from tensorflow.python.platform import gfile -class Iterator(object): - """Represents the state of iterating through a `Dataset`.""" - - def __init__(self, iterator_resource, initializer, output_types, - output_shapes): - """Creates a new iterator from the given iterator resource. - - NOTE(mrry): Most users will not call this initializer directly, and will - instead use `Iterator.from_dataset()` or `Dataset.make_one_shot_iterator()`. - - Args: - iterator_resource: A `tf.resource` scalar `tf.Tensor` representing the - iterator. - initializer: A `tf.Operation` that should be run to initialize this - iterator. - output_types: A nested structure of `tf.DType` objects corresponding to - each component of an element of this iterator. - output_shapes: A nested structure of `tf.TensorShape` objects - corresponding to each component of an element of this dataset. - """ - self._iterator_resource = iterator_resource - self._initializer = initializer - self._output_types = output_types - self._output_shapes = output_shapes - - @staticmethod - def from_dataset(dataset, shared_name=None): - """Creates a new, uninitialized `Iterator` from the given `Dataset`. - - To initialize this iterator, you must run its `initializer`: - - ```python - dataset = ... - iterator = Iterator.from_dataset(dataset) - # ... - sess.run(iterator.initializer) - ``` - - Args: - dataset: A `Dataset` object. - shared_name: (Optional.) If non-empty, this iterator will be shared under - the given name across multiple sessions that share the same devices - (e.g. when using a remote server). - - Returns: - An `Iterator`. - """ - if shared_name is None: - shared_name = "" - iterator_resource = gen_dataset_ops.iterator( - container="", - shared_name=shared_name, - output_types=nest.flatten(dataset.output_types), - output_shapes=nest.flatten(dataset.output_shapes)) - with ops.colocate_with(iterator_resource): - initializer = gen_dataset_ops.make_iterator( - dataset.make_dataset_resource(), iterator_resource) - return Iterator(iterator_resource, initializer, dataset.output_types, - dataset.output_shapes) - - @staticmethod - def from_structure(output_types, output_shapes=None, shared_name=None): - """Creates a new, uninitialized `Iterator` with the given structure. - - This iterator-constructing method can be used to create an iterator that - is reusable with many different datasets. - - The returned iterator is not bound to a particular dataset, and it has - no `initializer`. To initialize the iterator, run the operation returned by - `Iterator.make_initializer(dataset)`. - - The following is an example - - ```python - iterator = Iterator.from_structure(tf.int64, tf.TensorShape([])) - - dataset_range = Dataset.range(10) - range_initializer = iterator.make_initializer(dataset_range) - - dataset_evens = dataset_range.filter(lambda x: x % 2 == 0) - evens_initializer = iterator.make_initializer(dataset_evens) - - # Define a model based on the iterator; in this example, the model_fn - # is expected to take scalar tf.int64 Tensors as input (see - # the definition of 'iterator' above). - prediction, loss = model_fn(iterator.get_next()) - - # Train for `num_epochs`, where for each epoch, we first iterate over - # dataset_range, and then iterate over dataset_evens. - for _ in range(num_epochs): - # Initialize the iterator to `dataset_range` - sess.run(range_initializer) - while True: - try: - pred, loss_val = sess.run([prediction, loss]) - except tf.errors.OutOfRangeError: - break - - # Initialize the iterator to `dataset_evens` - sess.run(evens_initializer) - while True: - try: - pred, loss_val = sess.run([prediction, loss]) - except tf.errors.OutOfRangeError: - break - ``` - - Args: - output_types: A nested structure of `tf.DType` objects corresponding to - each component of an element of this iterator. - output_shapes: (Optional.) A nested structure of `tf.TensorShape` objects - corresponding to each component of an element of this dataset. If - omitted, each component will have an unconstrainted shape. - shared_name: (Optional.) If non-empty, this iterator will be shared under - the given name across multiple sessions that share the same devices - (e.g. when using a remote server). - - Returns: - An `Iterator`. - - Raises: - TypeError: If the structures of `output_shapes` and `output_types` are - not the same. - """ - output_types = nest.map_structure(dtypes.as_dtype, output_types) - if output_shapes is None: - output_shapes = nest.map_structure( - lambda _: tensor_shape.TensorShape(None), output_types) - else: - output_shapes = nest.map_structure_up_to( - output_types, tensor_shape.as_shape, output_shapes) - nest.assert_same_structure(output_types, output_shapes) - if shared_name is None: - shared_name = "" - iterator_resource = gen_dataset_ops.iterator( - container="", - shared_name=shared_name, - output_types=nest.flatten(output_types), - output_shapes=nest.flatten(output_shapes)) - return Iterator(iterator_resource, None, output_types, output_shapes) - - @staticmethod - def from_string_handle(string_handle, output_types, output_shapes=None): - """Creates a new, uninitialized `Iterator` based on the given handle. - - This method allows you to define a "feedable" iterator where you can choose - between concrete iterators by feeding a value in a @{tf.Session.run} call. - In that case, `string_handle` would a @{tf.placeholder}, and you would feed - it with the value of @{tf.contrib.data.Iterator.string_handle} in each step. - - For example, if you had two iterators that marked the current position in - a training dataset and a test dataset, you could choose which to use in - each step as follows: - - ```python - train_iterator = tf.contrib.data.Dataset(...).make_one_shot_iterator() - train_iterator_handle = sess.run(train_iterator.string_handle()) - - test_iterator = tf.contrib.data.Dataset(...).make_one_shot_iterator() - test_iterator_handle = sess.run(test_iterator.string_handle()) - - handle = tf.placeholder(tf.string, shape=[]) - iterator = tf.contrib.data.Iterator.from_string_handle( - handle, train_iterator.output_types) - - next_element = iterator.get_next() - loss = f(next_element) - - train_loss = sess.run(loss, feed_dict={handle: train_iterator_handle}) - test_loss = sess.run(loss, feed_dict={handle: test_iterator_handle}) - ``` - - Args: - string_handle: A scalar `tf.Tensor` of type `tf.string` that evaluates - to a handle produced by the `Iterator.string_handle()` method. - output_types: A nested structure of `tf.DType` objects corresponding to - each component of an element of this iterator. - output_shapes: (Optional.) A nested structure of `tf.TensorShape` objects - corresponding to each component of an element of this dataset. If - omitted, each component will have an unconstrainted shape. - - Returns: - An `Iterator`. - """ - output_types = nest.map_structure(dtypes.as_dtype, output_types) - if output_shapes is None: - output_shapes = nest.map_structure( - lambda _: tensor_shape.TensorShape(None), output_types) - else: - output_shapes = nest.map_structure_up_to( - output_types, tensor_shape.as_shape, output_shapes) - nest.assert_same_structure(output_types, output_shapes) - string_handle = ops.convert_to_tensor(string_handle, dtype=dtypes.string) - iterator_resource = gen_dataset_ops.iterator_from_string_handle( - string_handle, - output_types=nest.flatten(output_types), - output_shapes=nest.flatten(output_shapes)) - return Iterator(iterator_resource, None, output_types, output_shapes) - - @property - def initializer(self): - """A `tf.Operation` that should be run to initialize this iterator. - - Returns: - A `tf.Operation` that should be run to initialize this iterator - - Raises: - ValueError: If this iterator initializes itself automatically. - """ - if self._initializer is not None: - return self._initializer - else: - # TODO(mrry): Consider whether one-shot iterators should have - # initializers that simply reset their state to the beginning. - raise ValueError("Iterator does not have an initializer.") - - def make_initializer(self, dataset, name=None): - """Returns a `tf.Operation` that initializes this iterator on `dataset`. - - Args: - dataset: A `Dataset` with compatible structure to this iterator. - name: (Optional.) A name for the created operation. - - Returns: - A `tf.Operation` that can be run to initialize this iterator on the given - `dataset`. - - Raises: - TypeError: If `dataset` and this iterator do not have a compatible - element structure. - """ - with ops.name_scope(name, "make_initializer") as name: - nest.assert_same_structure(self._output_types, dataset.output_types) - nest.assert_same_structure(self._output_shapes, dataset.output_shapes) - for iterator_dtype, dataset_dtype in zip( - nest.flatten(self._output_types), nest.flatten(dataset.output_types)): - if iterator_dtype != dataset_dtype: - raise TypeError( - "Expected output types %r but got dataset with output types %r." % - (self._output_types, dataset.output_types)) - for iterator_shape, dataset_shape in zip( - nest.flatten(self._output_shapes), - nest.flatten(dataset.output_shapes)): - if not iterator_shape.is_compatible_with(dataset_shape): - raise TypeError("Expected output shapes compatible with %r but got " - "dataset with output shapes %r." % - (self._output_shapes, dataset.output_shapes)) - with ops.colocate_with(self._iterator_resource): - return gen_dataset_ops.make_iterator( - dataset.make_dataset_resource(), self._iterator_resource, name=name) - - def get_next(self, name=None): - """Returns a nested structure of `tf.Tensor`s containing the next element. - - Args: - name: (Optional.) A name for the created operation. - - Returns: - A nested structure of `tf.Tensor` objects. - """ - return nest.pack_sequence_as( - self._output_types, - gen_dataset_ops.iterator_get_next( - self._iterator_resource, - output_types=nest.flatten(self._output_types), - output_shapes=nest.flatten(self._output_shapes), - name=name)) - - def dispose_op(self, name=None): - """Returns a `tf.Operation` that destroys this iterator. - - The returned operation may be used to release any resources consumed by - this iterator without closing the session. - - Args: - name: (Optional.) A name for the created operation. - - Returns: - A `tf.Operation`. - """ - return gen_dataset_ops.iterator_dispose(self._iterator_resource, name=name) - - def string_handle(self, name=None): - """Returns a string-valued `tf.Tensor` that represents this iterator. - - Args: - name: (Optional.) A name for the created operation. - - Returns: - A scalar `tf.Tensor` of type `tf.string`. - """ - return gen_dataset_ops.iterator_to_string_handle( - self._iterator_resource, name=name) - - @property - def output_shapes(self): - """Returns the shape of each component of an element of this iterator. - - Returns: - A nested structure of `tf.TensorShape` objects corresponding to each - component of an element of this iterator. - """ - return self._output_shapes - - @property - def output_types(self): - """Returns the type of each component of an element of this iterator. - - Returns: - A nested structure of `tf.DType` objects corresponding to each component - of an element of this iterator. - """ - return self._output_types - - -def _calculate_acceptance_probs(initial_probs, target_probs): - """Calculate the per-class acceptance rates. - - Args: - initial_probs: The class probabilities of the data. - target_probs: The desired class proportion in minibatches. - Returns: - A list of the per-class acceptance probabilities. - - This method is based on solving the following analysis: - - Let F be the probability of a rejection (on any example). - Let p_i be the proportion of examples in the data in class i (init_probs) - Let a_i is the rate the rejection sampler should *accept* class i - Let t_i is the target proportion in the minibatches for class i (target_probs) - - ``` - F = sum_i(p_i * (1-a_i)) - = 1 - sum_i(p_i * a_i) using sum_i(p_i) = 1 - ``` - - An example with class `i` will be accepted if `k` rejections occur, then an - example with class `i` is seen by the rejector, and it is accepted. This can - be written as follows: - - ``` - t_i = sum_k=0^inf(F^k * p_i * a_i) - = p_i * a_j / (1 - F) using geometric series identity, since 0 <= F < 1 - = p_i * a_i / sum_j(p_j * a_j) using F from above - ``` - - Note that the following constraints hold: - ``` - 0 <= p_i <= 1, sum_i(p_i) = 1 - 0 <= a_i <= 1 - 0 <= t_i <= 1, sum_i(t_i) = 1 - ``` - - - A solution for a_i in terms of the other variabes is the following: - ```a_i = (t_i / p_i) / max_i[t_i / p_i]``` - """ - # Add tiny to initial_probs to avoid divide by zero. - denom = (initial_probs + np.finfo(initial_probs.dtype.as_numpy_dtype).tiny) - ratio_l = target_probs / denom - - # Calculate list of acceptance probabilities. - max_ratio = math_ops.reduce_max(ratio_l) - return ratio_l / max_ratio - - -def _estimate_data_distribution(c, num_examples_per_class_seen): - """Estimate data distribution as labels are seen. - - Args: - c: The class labels. Type `int32`, shape `[batch_size]`. - num_examples_per_class_seen: A `ResourceVariable` containing counts. - Type `int64`, shape `[num_classes]`. - - Returns: - dist: The updated distribution. Type `float32`, shape `[num_classes]`. - """ - num_classes = num_examples_per_class_seen.get_shape()[0].value - # Update the class-count based on what labels are seen in - # batch. But do this asynchronously to avoid performing a - # cross-device round-trip. Just use the cached value. - num_examples_per_class_seen = num_examples_per_class_seen.assign_add( - math_ops.reduce_sum( - array_ops.one_hot(c, num_classes, dtype=dtypes.int64), 0)) - init_prob_estimate = math_ops.truediv( - num_examples_per_class_seen, - math_ops.reduce_sum(num_examples_per_class_seen)) - return math_ops.cast(init_prob_estimate, dtypes.float32) - - -class Dataset(object): +class Dataset(dataset_ops.Dataset): """Represents a potentially large set of elements. A `Dataset` can be used to represent an input pipeline as a collection of elements (nested structures of tensors) and a "logical plan" of transformations that act on those elements. """ - __metaclass__ = abc.ABCMeta - def __init__(self): - pass + def __init__(self, dataset): + super(Dataset, self).__init__() + self._dataset = dataset - # TODO(mrry): Rename this to `make_dataset_variant()`, - # `make_dataset_tensor()`, or something else more accurate. - @abc.abstractmethod def make_dataset_resource(self): - """Creates a scalar `tf.Tensor` of `tf.variant` representing this dataset. + return self._dataset.make_dataset_resource() - Returns: - A scalar `tf.Tensor` of `tf.variant` type, which represents this dataset. - """ - raise NotImplementedError("Dataset.make_dataset_resource") - - def make_initializable_iterator(self, shared_name=None): - """Creates an `Iterator` for enumerating the elements of this dataset. - - **N.B.** The returned iterator will be in an uninitialized state, - and you must run the `iterator.initializer` operation before using it. - - Args: - shared_name: (Optional.) If non-empty, this iterator will be shared under - the given name across multiple sessions that share the same devices - (e.g. when using a remote server). - - - Returns: - An `Iterator` over the elements of this dataset. - """ - return Iterator.from_dataset(self, shared_name) - - def make_one_shot_iterator(self): - """Creates an `Iterator` for enumerating the elements of this dataset. - - **N.B.** The returned iterator will be initialized automatically. - A "one-shot" iterator does not currently support re-initialization. - - Returns: - An `Iterator` over the elements of this dataset. - """ - # NOTE(mrry): We capture by value here to ensure that `_make_dataset()` is - # a 0-argument function. - @function.Defun(capture_by_value=True) - def _make_dataset(): - return self.make_dataset_resource() - - _make_dataset.add_to_graph(ops.get_default_graph()) - - return Iterator( - gen_dataset_ops.one_shot_iterator( - dataset_factory=_make_dataset, - output_types=nest.flatten(self.output_types), - output_shapes=nest.flatten(self.output_shapes)), None, - self.output_types, self.output_shapes) - - @abc.abstractproperty + @property def output_shapes(self): - """Returns the shape of each component of an element of this dataset. + return self._dataset.output_shapes - Returns: - A nested structure of `tf.TensorShape` objects corresponding to each - component of an element of this dataset. - """ - raise NotImplementedError("Dataset.output_shapes") - - @abc.abstractproperty + @property def output_types(self): - """Returns the type of each component of an element of this dataset. - - Returns: - A nested structure of `tf.DType` objects corresponding to each component - of an element of this dataset. - """ - raise NotImplementedError("Dataset.output_types") - - def __repr__(self): - output_shapes = nest.map_structure(str, self.output_shapes) - output_shapes = str(output_shapes).replace("'", "") - output_types = nest.map_structure(repr, self.output_types) - output_types = str(output_types).replace("'", "") - return ("<%s shapes: %s, types: %s>" % (type(self).__name__, output_shapes, - output_types)) + return self._dataset.output_types @staticmethod def from_tensors(tensors): @@ -537,7 +76,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return TensorDataset(tensors) + return Dataset(dataset_ops.TensorDataset(tensors)) @staticmethod def from_tensor_slices(tensors): @@ -550,7 +89,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return TensorSliceDataset(tensors) + return Dataset(dataset_ops.TensorSliceDataset(tensors)) @staticmethod def from_sparse_tensor_slices(sparse_tensor): @@ -562,33 +101,7 @@ class Dataset(object): Returns: A `Dataset` of rank-(N-1) sparse tensors. """ - return SparseTensorSliceDataset(sparse_tensor) - - class _GeneratorState(object): - """Stores outstanding iterators created from a Python generator. - - This class keeps track of potentially multiple iterators that may have - been created from a generator, e.g. in the case that the dataset is - repeated, or nested within a parallel computation. - """ - - def __init__(self, generator): - self._generator = generator - self._lock = threading.Lock() - self._next_id = 0 # GUARDED_BY(self._lock) - self._iterators = collections.defaultdict(lambda: iter(generator())) - - def get_next_id(self): - with self._lock: - ret = self._next_id - self._next_id += 1 - return ret - - def get_iterator(self, iterator_id): - return self._iterators[iterator_id] - - def iterator_completed(self, iterator_id): - del self._iterators[iterator_id] + return Dataset(dataset_ops.SparseTensorSliceDataset(sparse_tensor)) @staticmethod def from_generator(generator, output_types, output_shapes=None): @@ -640,7 +153,7 @@ class Dataset(object): flattened_types = nest.flatten(output_types) flattened_shapes = nest.flatten(output_shapes) - generator_state = Dataset._GeneratorState(generator) + generator_state = dataset_ops.Dataset._GeneratorState(generator) def get_iterator_id_map_fn(unused_dummy): """Creates a unique `iterator_id` for each pass over the dataset. @@ -773,7 +286,7 @@ class Dataset(object): Raises: ValueError: if len(args) == 0. """ - return RangeDataset(*args) + return Dataset(dataset_ops.RangeDataset(*args)) @staticmethod def zip(datasets): @@ -814,7 +327,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return ZipDataset(datasets) + return Dataset(dataset_ops.ZipDataset(datasets)) def concatenate(self, dataset): """Creates a `Dataset` by concatenating given dataset with this dataset. @@ -840,7 +353,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return ConcatenateDataset(self, dataset) + return Dataset(dataset_ops.ConcatenateDataset(self._dataset, dataset)) def prefetch(self, buffer_size): """Creates a `Dataset` that prefetches elements from this dataset. @@ -852,7 +365,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return PrefetchDataset(self, buffer_size) + return Dataset(dataset_ops.PrefetchDataset(self._dataset, buffer_size)) @staticmethod def list_files(file_pattern): @@ -889,34 +402,12 @@ class Dataset(object): Returns: A `Dataset`. """ - return RepeatDataset(self, count) + return Dataset(dataset_ops.RepeatDataset(self._dataset, count)) def enumerate(self, start=0): - """Enumerate the elements of this dataset. Similar to python's `enumerate`. + """Deprecated: Use `Dataset.apply(tf.contrib.data.enumerate_dataset(..)`.""" - For example: - - ```python - # NOTE: The following examples use `{ ... }` to represent the - # contents of a dataset. - a = { 1, 2, 3 } - b = { (7, 8), (9, 10), (11, 12) } - - # The nested structure of the `datasets` argument determines the - # structure of elements in the resulting dataset. - a.enumerate(start=5) == { (5, 1), (6, 2), (7, 3) } - b.enumerate() == { (0, (7, 8)), (1, (9, 10)), (2, (11, 12)) } - ``` - - Args: - start: A `tf.int64` scalar `tf.Tensor`, representing the start - value for enumeration. - - Returns: - A `Dataset`. - """ - max_value = np.iinfo(dtypes.int64.as_numpy_dtype).max - return Dataset.zip((Dataset.range(start, max_value), self)) + return self.apply(enumerate_dataset(start)) def shuffle(self, buffer_size, seed=None): """Randomly shuffles the elements of this dataset. @@ -932,7 +423,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return ShuffleDataset(self, buffer_size, seed) + return Dataset(dataset_ops.ShuffleDataset(self._dataset, buffer_size, seed)) def cache(self, filename=""): """Caches the elements in this dataset. @@ -945,7 +436,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return CacheDataset(self, filename) + return Dataset(dataset_ops.CacheDataset(self._dataset, filename)) def take(self, count): """Creates a `Dataset` with at most `count` elements from this dataset. @@ -959,7 +450,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return TakeDataset(self, count) + return Dataset(dataset_ops.TakeDataset(self._dataset, count)) def skip(self, count): """Creates a `Dataset` that skips `count` elements from this dataset. @@ -974,7 +465,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return SkipDataset(self, count) + return Dataset(dataset_ops.SkipDataset(self._dataset, count)) def shard(self, num_shards, index): """Creates a `Dataset` that includes only 1/`num_shards` of this dataset. @@ -1028,48 +519,12 @@ class Dataset(object): bypasses the early checking, and will instead result in an error during a session.run call.) """ - num_shards = ops.convert_to_tensor( - num_shards, name="num_shards", dtype=dtypes.int64) - num_shards_static = tensor_util.constant_value(num_shards) - index = ops.convert_to_tensor(index, name="index", dtype=dtypes.int64) - index_static = tensor_util.constant_value(index) - - if num_shards_static is not None and num_shards_static < 1: - raise ValueError("num_shards must be >= 1; got: %s" % num_shards_static) - if index_static is not None and index_static < 0: - raise ValueError("index must be >= 0; got: %s" % index_static) - if (index_static is not None and num_shards_static is not None and - index_static >= num_shards_static): - raise ValueError("index must be <= num_shards; %s is not < %s" % - (index_static, num_shards_static)) - - def filter_fn(elem_index, _): - mod_result = math_ops.mod(elem_index, num_shards) - return math_ops.equal(mod_result, index) - - return self.enumerate().filter(filter_fn).map(lambda _, elem: elem) + return Dataset(self._dataset.shard(num_shards, index)) def ignore_errors(self): - """Creates a `Dataset` from this one and silently ignores any errors. + """Deprecated: Use `Dataset.apply(tf.contrib.data.ignore_errors()`.""" - Use this transformation to produce a dataset that contains the same elements - as the input, but silently drops any elements that caused an error. For - example: - - ```python - dataset = tf.contrib.data.Dataset.from_tensor_slices([1., 2., 0., 4.]) - - # Computing `tf.check_numerics(1. / 0.)` will raise an InvalidArgumentError. - dataset = dataset.map(lambda x: tf.check_numerics(1. / x, "error")) - - # Using `ignore_errors()` will drop the element that causes an error. - dataset = dataset.ignore_errors() # ==> { 1., 0.5, 0.2 } - ``` - - Returns: - A `Dataset`. - """ - return IgnoreErrorsDataset(self) + return self.apply(ignore_errors()) def batch(self, batch_size): """Combines consecutive elements of this dataset into batches. @@ -1081,7 +536,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return BatchDataset(self, batch_size) + return Dataset(dataset_ops.BatchDataset(self._dataset, batch_size)) def padded_batch(self, batch_size, padded_shapes, padding_values=None): """Combines consecutive elements of this dataset into padded batches. @@ -1110,52 +565,18 @@ class Dataset(object): Returns: A `Dataset`. """ - return PaddedBatchDataset(self, batch_size, padded_shapes, padding_values) + return Dataset( + dataset_ops.PaddedBatchDataset(self._dataset, batch_size, padded_shapes, + padding_values)) def dense_to_sparse_batch(self, batch_size, row_shape): - """Batches ragged elements of this dataset into `tf.SparseTensor`s. + """Use: `Dataset.apply(tf.contrib.data.dense_to_sparse_batch(...))`.""" - Like `Dataset.padded_batch()`, this method combines multiple - consecutive elements of this dataset, which might have different - shapes, into a single element. The resulting element has three - components (`indices`, `values`, and `dense_shape`), which - comprise a `tf.SparseTensor` that represents the same data. The - `row_shape` represents the dense shape of each row in the - resulting `tf.SparseTensor`, to which the effective batch size is - prepended. For example: - - ```python - # NOTE: The following examples use `{ ... }` to represent the - # contents of a dataset. - a = { ['a', 'b', 'c'], ['a', 'b'], ['a', 'b', 'c', 'd'] } - - a.dense_to_sparse_batch(batch_size=2, row_shape=[6]) == { - ([[0, 0], [0, 1], [0, 2], [1, 0], [1, 1]], # indices - ['a', 'b', 'c', 'a', 'b'], # values - [2, 6]), # dense_shape - ([[2, 0], [2, 1], [2, 2], [2, 3]], - ['a', 'b', 'c', 'd'], - [1, 6]) - } - ``` - - Args: - batch_size: A `tf.int64` scalar `tf.Tensor`, representing the - number of consecutive elements of this dataset to combine in a - single batch. - row_shape: A `tf.TensorShape` or `tf.int64` vector tensor-like - object representing the equivalent dense shape of a row in the - resulting `tf.SparseTensor`. Each element of this dataset must - have the same rank as `row_shape`, and must have size less - than or equal to `row_shape` in each dimension. - - Returns: - A `Dataset`. - """ - return DenseToSparseBatchDataset(self, batch_size, row_shape) + return self.apply(dense_to_sparse_batch(batch_size, row_shape)) def group_by_window(self, key_func, reduce_func, window_size): - """Deprecated: Use `Dataset.apply(tf.contrib.data.group_by_window(...)`.""" + """Deprecated: Use `Dataset.apply(tf.contrib.data.group_by_window(...))`.""" + return self.apply(group_by_window(key_func, reduce_func, window_size)) def map(self, @@ -1181,12 +602,16 @@ class Dataset(object): A `Dataset`. """ if num_threads is None and num_parallel_calls is None: - ret = MapDataset(self, map_func) + ret = Dataset(dataset_ops.MapDataset(self._dataset, map_func)) else: if num_threads is None: - ret = ParallelMapDataset(self, map_func, num_parallel_calls) + ret = Dataset( + dataset_ops.ParallelMapDataset(self._dataset, map_func, + num_parallel_calls)) else: - ret = ParallelMapDataset(self, map_func, num_threads) + ret = Dataset( + dataset_ops.ParallelMapDataset(self._dataset, map_func, + num_threads)) if output_buffer_size is not None: ret = ret.prefetch(output_buffer_size) return ret @@ -1202,7 +627,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return FlatMapDataset(self, map_func) + return Dataset(dataset_ops.FlatMapDataset(self._dataset, map_func)) def interleave(self, map_func, cycle_length, block_length=1): """Maps `map_func` across this dataset, and interleaves the results. @@ -1271,27 +696,14 @@ class Dataset(object): Returns: A `Dataset`. """ - return InterleaveDataset(self, map_func, cycle_length, block_length) + return Dataset( + dataset_ops.InterleaveDataset(self._dataset, map_func, cycle_length, + block_length)) def unbatch(self): - """Splits elements of this dataset into sequences of consecutive elements. + """Deprecated: Use `Dataset.apply(tf.contrib.data.unbatch()`.""" - For example, if elements of this dataset are shaped `[B, a0, a1, ...]`, - where `B` may vary from element to element, then for each element in - this dataset, the unbatched dataset will contain `B` consecutive elements - of shape `[a0, a1, ...]`. - - Returns: - A `Dataset`. - """ - - def unbatch_map(arg, *rest): - if rest: - return Dataset.from_tensor_slices((arg,) + rest) - else: - return Dataset.from_tensor_slices(arg) - - return self.flat_map(map_func=unbatch_map) + return self.apply(unbatch()) def filter(self, predicate): """Filters this dataset according to `predicate`. @@ -1304,7 +716,7 @@ class Dataset(object): Returns: A `Dataset`. """ - return FilterDataset(self, predicate) + return Dataset(dataset_ops.FilterDataset(self._dataset, predicate)) def apply(self, transformation_func): """Apply a transformation function to this dataset. @@ -1317,7 +729,7 @@ class Dataset(object): ``` dataset = (dataset.map(lambda x: x ** 2) - .apply(group_by_window(key_func, reduce_func, window_size)) + .(group_by_window(key_func, reduce_func, window_size)) .map(lambda x: x ** 3)) ``` @@ -1329,852 +741,9 @@ class Dataset(object): The `Dataset` returned by applying `transformation_func` to this dataset. """ dataset = transformation_func(self) - if not isinstance(dataset, Dataset): + if not isinstance(dataset, dataset_ops.Dataset): raise TypeError("`transformation_func` must return a Dataset.") - return dataset - - -class TensorDataset(Dataset): - """A `Dataset` with a single element, viz. a nested structure of tensors.""" - - def __init__(self, tensors): - """See `Dataset.from_tensors()` for details.""" - super(TensorDataset, self).__init__() - with ops.name_scope("tensors"): - self._tensors = nest.pack_sequence_as(tensors, [ - ops.convert_to_tensor(t, name="component_%d" % i) - for i, t in enumerate(nest.flatten(tensors)) - ]) - - def make_dataset_resource(self): - return gen_dataset_ops.tensor_dataset( - nest.flatten(self._tensors), - output_shapes=nest.flatten(self.output_shapes)) - - @property - def output_shapes(self): - return nest.pack_sequence_as(self._tensors, - [t.shape for t in nest.flatten(self._tensors)]) - - @property - def output_types(self): - return nest.pack_sequence_as(self._tensors, - [t.dtype for t in nest.flatten(self._tensors)]) - - -class TensorSliceDataset(Dataset): - """A `Dataset` of slices from a nested structure of tensors.""" - - def __init__(self, tensors): - """See `Dataset.from_tensor_slices()` for details.""" - super(TensorSliceDataset, self).__init__() - with ops.name_scope("tensors"): - flat_tensors = [ - ops.convert_to_tensor(t, name="component_%d" % i) - for i, t in enumerate(nest.flatten(tensors)) - ] - - self._tensors = nest.pack_sequence_as(tensors, flat_tensors) - batch_dim = flat_tensors[0].get_shape()[0] - for t in flat_tensors[1:]: - batch_dim.assert_is_compatible_with(t.get_shape()[0]) - - def make_dataset_resource(self): - return gen_dataset_ops.tensor_slice_dataset( - nest.flatten(self._tensors), - output_shapes=nest.flatten(self.output_shapes)) - - @property - def output_shapes(self): - return nest.pack_sequence_as(self._tensors, [ - tensor_shape.TensorShape(t.shape[1:]) - for t in nest.flatten(self._tensors) - ]) - - @property - def output_types(self): - return nest.pack_sequence_as(self._tensors, - [t.dtype for t in nest.flatten(self._tensors)]) - - -class SparseTensorSliceDataset(Dataset): - """A `Dataset` that splits a rank-N `tf.SparseTensor` into its rows.""" - - def __init__(self, sparse_tensor): - """See `Dataset.from_sparse_tensor_slices()` for details.""" - super(SparseTensorSliceDataset, self).__init__() - if not isinstance(sparse_tensor, sparse_tensor_lib.SparseTensor): - raise TypeError("`sparse_tensor` must be a `tf.SparseTensor` object.") - self._sparse_tensor = sparse_tensor - - def make_dataset_resource(self): - return gen_dataset_ops.sparse_tensor_slice_dataset( - self._sparse_tensor.indices, self._sparse_tensor.values, - self._sparse_tensor.dense_shape) - - @property - def output_shapes(self): - indices_shape = self._sparse_tensor.indices.get_shape() - shape_shape = self._sparse_tensor.dense_shape.get_shape() - rank = (indices_shape[1] - 1).merge_with(shape_shape[0] - 1) - num_values = tensor_shape.Dimension(None) - return (tensor_shape.TensorShape([num_values, rank]), - tensor_shape.TensorShape([num_values]), tensor_shape.TensorShape( - [rank])) - - @property - def output_types(self): - return (dtypes.int64, self._sparse_tensor.dtype, dtypes.int64) - - -class ZipDataset(Dataset): - """A `Dataset` that zips its inputs together.""" - - def __init__(self, datasets): - """See `Dataset.zip()` for details.""" - super(ZipDataset, self).__init__() - self._datasets = datasets - - def make_dataset_resource(self): - return gen_dataset_ops.zip_dataset( - [ds.make_dataset_resource() for ds in nest.flatten(self._datasets)], - output_shapes=[ - s - for ds in nest.flatten(self._datasets) - for s in nest.flatten(ds.output_shapes) - ], - output_types=[ - t - for ds in nest.flatten(self._datasets) - for t in nest.flatten(ds.output_types) - ]) - - @property - def output_shapes(self): - return nest.pack_sequence_as(self._datasets, [ - ds.output_shapes for ds in nest.flatten(self._datasets) - ]) - - @property - def output_types(self): - return nest.pack_sequence_as(self._datasets, [ - ds.output_types for ds in nest.flatten(self._datasets) - ]) - - -class ConcatenateDataset(Dataset): - """A `Dataset` that concatenates its input with given dataset.""" - - def __init__(self, input_dataset, dataset_to_concatenate): - """See `Dataset.concatenate()` for details.""" - super(ConcatenateDataset, self).__init__() - self._input_dataset = input_dataset - self._dataset_to_concatenate = dataset_to_concatenate - nest.assert_same_structure(input_dataset.output_types, - dataset_to_concatenate.output_types) - for a, b in zip( - nest.flatten(input_dataset.output_types), - nest.flatten(dataset_to_concatenate.output_types)): - if a != b: - raise TypeError( - "Two datasets to concatenate have different types %s and %s" % - (input_dataset.output_types, dataset_to_concatenate.output_types)) - - def make_dataset_resource(self): - return gen_dataset_ops.concatenate_dataset( - self._input_dataset.make_dataset_resource(), - self._dataset_to_concatenate.make_dataset_resource(), - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - return nest.pack_sequence_as(self._input_dataset.output_shapes, [ - ts1.most_specific_compatible_shape(ts2) - for (ts1, ts2) in zip( - nest.flatten(self._input_dataset.output_shapes), - nest.flatten(self._dataset_to_concatenate.output_shapes)) - ]) - - @property - def output_types(self): - return self._input_dataset.output_types - - -class RepeatDataset(Dataset): - """A `Dataset` that repeats its input several times.""" - - def __init__(self, input_dataset, count): - """See `Dataset.repeat()` for details.""" - super(RepeatDataset, self).__init__() - self._input_dataset = input_dataset - if count is None: - self._count = constant_op.constant(-1, dtype=dtypes.int64, name="count") - else: - self._count = ops.convert_to_tensor( - count, dtype=dtypes.int64, name="count") - - def make_dataset_resource(self): - return gen_dataset_ops.repeat_dataset( - self._input_dataset.make_dataset_resource(), - count=self._count, - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -class RangeDataset(Dataset): - """A `Dataset` of a step separated range of values.""" - - def __init__(self, *args): - """See `Dataset.range()` for details.""" - super(RangeDataset, self).__init__() - self._parse_args(*args) - - def _parse_args(self, *args): - if len(args) == 1: - self._start = self._build_tensor(0, "start") - self._stop = args[0] - self._step = self._build_tensor(1, "step") - elif len(args) == 2: - self._start = args[0] - self._stop = args[1] - self._step = self._build_tensor(1, "step") - elif len(args) == 3: - self._start = args[0] - self._stop = args[1] - self._step = args[2] - else: - raise ValueError("Invalid arguments to RangeDataset: %s" % str(args)) - - def _build_tensor(self, int64_value, name): - return constant_op.constant(int64_value, dtype=dtypes.int64, name=name) - - def make_dataset_resource(self): - return gen_dataset_ops.range_dataset( - start=self._start, - stop=self._stop, - step=self._step, - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - return tensor_shape.scalar() - - @property - def output_types(self): - return dtypes.int64 - - -class CacheDataset(Dataset): - """A `Dataset` that caches elements of its input.""" - - def __init__(self, input_dataset, filename): - """See `Dataset.cache()` for details.""" - super(CacheDataset, self).__init__() - self._input_dataset = input_dataset - self._filename = ops.convert_to_tensor( - filename, dtype=dtypes.string, name="filename") - - def make_dataset_resource(self): - return gen_dataset_ops.cache_dataset( - self._input_dataset.make_dataset_resource(), - filename=self._filename, - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -class ShuffleDataset(Dataset): - """A `Dataset` that randomly shuffles the elements of its input.""" - - def __init__(self, input_dataset, buffer_size, seed=None): - """See `Dataset.shuffle()` for details.""" - super(ShuffleDataset, self).__init__() - self._input_dataset = input_dataset - self._buffer_size = ops.convert_to_tensor( - buffer_size, dtype=dtypes.int64, name="buffer_size") - seed, seed2 = random_seed.get_seed(seed) - if seed is None: - self._seed = constant_op.constant(0, dtype=dtypes.int64, name="seed") - else: - self._seed = ops.convert_to_tensor(seed, dtype=dtypes.int64, name="seed") - if seed2 is None: - self._seed2 = constant_op.constant(0, dtype=dtypes.int64, name="seed2") - else: - self._seed2 = ops.convert_to_tensor( - seed2, dtype=dtypes.int64, name="seed2") - - def make_dataset_resource(self): - return gen_dataset_ops.shuffle_dataset( - self._input_dataset.make_dataset_resource(), - buffer_size=self._buffer_size, - seed=self._seed, - seed2=self._seed2, - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -class TakeDataset(Dataset): - """A `Dataset` containing the first `count` elements from its input.""" - - def __init__(self, input_dataset, count): - """See `Dataset.take()` for details.""" - super(TakeDataset, self).__init__() - self._input_dataset = input_dataset - self._count = ops.convert_to_tensor(count, dtype=dtypes.int64, name="count") - - def make_dataset_resource(self): - return gen_dataset_ops.take_dataset( - self._input_dataset.make_dataset_resource(), - count=self._count, - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -class SkipDataset(Dataset): - """A `Dataset` skipping the first `count` elements from its input.""" - - def __init__(self, input_dataset, count): - """See `Dataset.skip()` for details.""" - super(SkipDataset, self).__init__() - self._input_dataset = input_dataset - self._count = ops.convert_to_tensor(count, dtype=dtypes.int64, name="count") - - def make_dataset_resource(self): - return gen_dataset_ops.skip_dataset( - self._input_dataset.make_dataset_resource(), - count=self._count, - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -class IgnoreErrorsDataset(Dataset): - """A `Dataset` that silently ignores errors when computing its input.""" - - def __init__(self, input_dataset): - """See `Dataset.ignore_errors()` for details.""" - super(IgnoreErrorsDataset, self).__init__() - self._input_dataset = input_dataset - - def make_dataset_resource(self): - return gen_dataset_ops.ignore_errors_dataset( - self._input_dataset.make_dataset_resource(), - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -class BatchDataset(Dataset): - """A `Dataset` that batches contiguous elements from its input.""" - - def __init__(self, input_dataset, batch_size): - """See `Dataset.batch()` for details.""" - super(BatchDataset, self).__init__() - self._input_dataset = input_dataset - self._batch_size = batch_size - - def make_dataset_resource(self): - return gen_dataset_ops.batch_dataset( - self._input_dataset.make_dataset_resource(), - batch_size=self._batch_size, - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - input_shapes = self._input_dataset.output_shapes - return nest.pack_sequence_as(input_shapes, [ - tensor_shape.vector(None).concatenate(s) - for s in nest.flatten(self._input_dataset.output_shapes) - ]) - - @property - def output_types(self): - return self._input_dataset.output_types - - -def _partial_shape_to_tensor(shape_like): - try: - # First attempt to convert the input to a shape, and return the - # "canonical" tensor representation, which uses `-1` in place of - # `None`. - shape_like = tensor_shape.as_shape(shape_like) - return ops.convert_to_tensor( - [dim if dim is not None else -1 for dim in shape_like.as_list()], - dtype=dtypes.int64) - except (TypeError, ValueError): - # The argument was not trivially convertible to a - # `tf.TensorShape`, so fall back on the conversion to tensor - # machinery. - return ops.convert_to_tensor(shape_like, dtype=dtypes.int64) - - -def _padding_value_to_tensor(value, output_type): - """Converts the padding value to a tensor. - - Args: - value: The padding value. - output_type: Its expected dtype. - - Returns: - A scalar `Tensor`. - - Raises: - ValueError: if the padding value is not a scalar. - TypeError: if the padding value's type does not match `output_type`. - """ - value = ops.convert_to_tensor(value, name="padding_value") - if not value.shape.is_compatible_with(tensor_shape.scalar()): - raise ValueError("Padding value should be a scalar, but is not: %s" % value) - if value.dtype != output_type: - raise TypeError("Padding value tensor (%s) does not match output type: %s" % - (value, output_type)) - return value - - -class PaddedBatchDataset(Dataset): - """A `Dataset` that batches and pads contiguous elements from its input.""" - - def __init__(self, input_dataset, batch_size, padded_shapes, padding_values): - """See `Dataset.batch()` for details.""" - super(PaddedBatchDataset, self).__init__() - self._input_dataset = input_dataset - self._batch_size = batch_size - padding_values = (padding_values if padding_values is not None else - self._default_padding(input_dataset)) - self._padded_shapes = nest.map_structure_up_to( - input_dataset.output_shapes, _partial_shape_to_tensor, padded_shapes) - self._padding_values = nest.map_structure_up_to( - input_dataset.output_shapes, _padding_value_to_tensor, padding_values, - input_dataset.output_types) - - def _default_padding(self, input_dataset): - - def make_zero(t): - if t.base_dtype == dtypes.string: - return "" - else: - return np.zeros_like(t.as_numpy_dtype()) - - return nest.map_structure(make_zero, input_dataset.output_types) - - def make_dataset_resource(self): - return gen_dataset_ops.padded_batch_dataset( - self._input_dataset.make_dataset_resource(), - batch_size=self._batch_size, - padded_shapes=[ - ops.convert_to_tensor(s, dtype=dtypes.int64) - for s in nest.flatten(self._padded_shapes) - ], - padding_values=nest.flatten(self._padding_values), - output_shapes=nest.flatten(self.output_shapes)) - - @property - def output_shapes(self): - - def _padded_shape_to_batch_shape(s): - return tensor_shape.vector(None).concatenate( - tensor_util.constant_value_as_shape(s)) - - return nest.map_structure(_padded_shape_to_batch_shape, self._padded_shapes) - - @property - def output_types(self): - return self._input_dataset.output_types - - -class DenseToSparseBatchDataset(Dataset): - """A `Dataset` that batches ragged dense elements into `tf.SparseTensor`s.""" - - def __init__(self, input_dataset, batch_size, row_shape): - """See `Dataset.dense_to_sparse_batch()` for more details.""" - super(DenseToSparseBatchDataset, self).__init__() - if not isinstance(input_dataset.output_types, dtypes.DType): - raise TypeError("DenseToSparseDataset requires an input whose elements " - "have a single component, whereas the input has %r." % - input_dataset.output_types) - self._input_dataset = input_dataset - self._batch_size = batch_size - self._row_shape = _partial_shape_to_tensor(row_shape) - - def make_dataset_resource(self): - return gen_dataset_ops.dense_to_sparse_batch_dataset( - self._input_dataset.make_dataset_resource(), - self._batch_size, - self._row_shape, - output_shapes=self.output_shapes, - output_types=self.output_types) - - @property - def output_shapes(self): - num_elements = tensor_shape.Dimension(None) - return (tensor_shape.matrix(num_elements, self._row_shape.shape[0] + 1), - tensor_shape.vector(num_elements), - tensor_shape.vector(self._row_shape.shape[0] + 1)) - - @property - def output_types(self): - return (dtypes.int64, self._input_dataset.output_types, dtypes.int64) - - -def _should_unpack_args(args): - """Returns `True` if `args` should be `*args` when passed to a callable.""" - return type(args) is tuple # pylint: disable=unidiomatic-typecheck - - -class _VariantDataset(Dataset): - """A Dataset wrapper for a tf.variant-typed function argument.""" - - def __init__(self, dataset_variant, output_types, output_shapes): - super(_VariantDataset, self).__init__() - self._dataset_variant = dataset_variant - self._output_types = output_types - self._output_shapes = output_shapes - - def make_dataset_resource(self): - return self._dataset_variant - - @property - def output_shapes(self): - return self._output_shapes - - @property - def output_types(self): - return self._output_types - - -class MapDataset(Dataset): - """A `Dataset` that maps a function over elements in its input.""" - - def __init__(self, input_dataset, map_func): - """See `Dataset.map()` for details.""" - super(MapDataset, self).__init__() - self._input_dataset = input_dataset - - self._output_shapes = None - self._output_types = None - - @function.Defun(*nest.flatten(input_dataset.output_types)) - def tf_map_func(*args): - """A wrapper for Defun that facilitates shape inference.""" - # Pass in shape information from the input_dataset. - for arg, shape in zip(args, nest.flatten(input_dataset.output_shapes)): - arg.set_shape(shape) - - nested_args = nest.pack_sequence_as(input_dataset.output_types, args) - - if _should_unpack_args(nested_args): - ret = map_func(*nested_args) - else: - ret = map_func(nested_args) - - # If `map_func` returns a list of tensors, `nest.flatten()` and - # `ops.convert_to_tensor()` would conspire to attempt to stack - # those tensors into a single tensor, because the customized - # version of `nest.flatten()` does not recurse into lists. Since - # it is more likely that the list arose from returning the - # result of an operation (such as `tf.py_func()`) that returns a - # list of not-necessarily-stackable tensors, we treat the - # returned value is a `tuple` instead. A user wishing to pack - # the return value into a single tensor can use an explicit - # `tf.stack()` before returning. - if isinstance(ret, list): - ret = tuple(ret) - - # Extract shape information from the returned values. - flattened_ret = [ops.convert_to_tensor(t) for t in nest.flatten(ret)] - self._output_shapes = nest.pack_sequence_as( - ret, [t.get_shape() for t in flattened_ret]) - self._output_types = nest.pack_sequence_as( - ret, [t.dtype for t in flattened_ret]) - - return flattened_ret - - self._map_func = tf_map_func - self._map_func.add_to_graph(ops.get_default_graph()) - - def make_dataset_resource(self): - input_resource = self._input_dataset.make_dataset_resource() - return gen_dataset_ops.map_dataset( - input_resource, - self._map_func.captured_inputs, - f=self._map_func, - output_types=nest.flatten(self.output_types), - output_shapes=nest.flatten(self.output_shapes)) - - @property - def output_shapes(self): - return self._output_shapes - - @property - def output_types(self): - return self._output_types - - -class ParallelMapDataset(MapDataset): - """A `Dataset` that maps a function over elements in its input in parallel.""" - - def __init__(self, input_dataset, map_func, num_parallel_calls): - """See `Dataset.map()` for details.""" - super(ParallelMapDataset, self).__init__(input_dataset, map_func) - - self._num_parallel_calls = ops.convert_to_tensor( - num_parallel_calls, dtype=dtypes.int32, name="num_parallel_calls") - - def make_dataset_resource(self): - input_resource = self._input_dataset.make_dataset_resource() - # pylint: disable=protected-access - return gen_dataset_ops.parallel_map_dataset( - input_resource, - self._map_func.captured_inputs, - f=self._map_func, - num_parallel_calls=self._num_parallel_calls, - output_types=nest.flatten(self.output_types), - output_shapes=nest.flatten(self.output_shapes)) - # pylint: enable=protected-access - - -class FlatMapDataset(Dataset): - """A `Dataset` that maps a function over its input and flattens the result.""" - - def __init__(self, input_dataset, map_func): - """See `Dataset.flat_map()` for details.""" - super(FlatMapDataset, self).__init__() - self._input_dataset = input_dataset - - @function.Defun(*nest.flatten(input_dataset.output_types)) - def tf_map_func(*args): - """A wrapper for Defun that facilitates shape inference.""" - # Pass in shape information from the input_dataset. - for arg, shape in zip(args, nest.flatten(input_dataset.output_shapes)): - arg.set_shape(shape) - - nested_args = nest.pack_sequence_as(input_dataset.output_types, args) - - if _should_unpack_args(nested_args): - dataset = map_func(*nested_args) - else: - dataset = map_func(nested_args) - - if not isinstance(dataset, Dataset): - raise TypeError("`map_func` must return a `Dataset` object.") - - self._output_types = dataset.output_types - self._output_shapes = dataset.output_shapes - - return dataset.make_dataset_resource() - - self._map_func = tf_map_func - self._map_func.add_to_graph(ops.get_default_graph()) - - def make_dataset_resource(self): - return gen_dataset_ops.flat_map_dataset( - self._input_dataset.make_dataset_resource(), - self._map_func.captured_inputs, - f=self._map_func, - output_types=nest.flatten(self.output_types), - output_shapes=nest.flatten(self.output_shapes)) - - @property - def output_shapes(self): - return self._output_shapes - - @property - def output_types(self): - return self._output_types - - -class InterleaveDataset(Dataset): - """A `Dataset` that maps a function over its input and interleaves the result. - """ - - def __init__(self, input_dataset, map_func, cycle_length, block_length): - """See `Dataset.interleave()` for details.""" - super(InterleaveDataset, self).__init__() - self._input_dataset = input_dataset - - @function.Defun(*nest.flatten(input_dataset.output_types)) - def tf_map_func(*args): - """A wrapper for Defun that facilitates shape inference.""" - # Pass in shape information from the input_dataset. - for arg, shape in zip(args, nest.flatten(input_dataset.output_shapes)): - arg.set_shape(shape) - - nested_args = nest.pack_sequence_as(input_dataset.output_types, args) - - if _should_unpack_args(nested_args): - dataset = map_func(*nested_args) - else: - dataset = map_func(nested_args) - - if not isinstance(dataset, Dataset): - raise TypeError("`map_func` must return a `Dataset` object.") - - self._output_types = dataset.output_types - self._output_shapes = dataset.output_shapes - - return dataset.make_dataset_resource() - - self._map_func = tf_map_func - self._map_func.add_to_graph(ops.get_default_graph()) - - self._cycle_length = ops.convert_to_tensor(cycle_length, dtype=dtypes.int64) - self._block_length = ops.convert_to_tensor(block_length, dtype=dtypes.int64) - - def make_dataset_resource(self): - return gen_dataset_ops.interleave_dataset( - self._input_dataset.make_dataset_resource(), - self._map_func.captured_inputs, - self._cycle_length, - self._block_length, - f=self._map_func, - output_types=nest.flatten(self.output_types), - output_shapes=nest.flatten(self.output_shapes)) - - @property - def output_shapes(self): - return self._output_shapes - - @property - def output_types(self): - return self._output_types - - -class FilterDataset(Dataset): - """A `Dataset` that filters its input according to a predicate function.""" - - def __init__(self, input_dataset, predicate): - """See `Dataset.filter()` for details.""" - super(FilterDataset, self).__init__() - self._input_dataset = input_dataset - - @function.Defun(*nest.flatten(input_dataset.output_types)) - def tf_predicate(*args): - """A wrapper for Defun that facilitates shape inference.""" - # Pass in shape information from the input_dataset. - for arg, shape in zip(args, nest.flatten(input_dataset.output_shapes)): - arg.set_shape(shape) - - nested_args = nest.pack_sequence_as(input_dataset.output_types, args) - - if _should_unpack_args(nested_args): - ret = predicate(*nested_args) - else: - ret = predicate(nested_args) - - ret = ops.convert_to_tensor(ret, dtype=dtypes.bool) - if not (ret.dtype == dtypes.bool and - ret.shape.is_compatible_with(tensor_shape.scalar())): - raise ValueError("`predicate` must return a scalar boolean tensor.") - - return ret - - self._predicate = tf_predicate - self._predicate.add_to_graph(ops.get_default_graph()) - - def make_dataset_resource(self): - return gen_dataset_ops.filter_dataset( - self._input_dataset.make_dataset_resource(), - other_arguments=self._predicate.captured_inputs, - predicate=self._predicate, - output_types=nest.flatten(self.output_types), - output_shapes=nest.flatten(self.output_shapes)) - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -class PrefetchDataset(Dataset): - """A `Dataset` that asynchronously prefetches its input.""" - - def __init__(self, input_dataset, buffer_size): - """See `Dataset.prefetch()` for details.""" - super(PrefetchDataset, self).__init__() - self._input_dataset = input_dataset - self._buffer_size = ops.convert_to_tensor(buffer_size, dtype=dtypes.int64) - - def make_dataset_resource(self): - return gen_dataset_ops.prefetch_dataset( - self._input_dataset.make_dataset_resource(), - buffer_size=self._buffer_size, - output_shapes=nest.flatten(self.output_shapes), - output_types=nest.flatten(self.output_types)) - - @property - def output_shapes(self): - return self._input_dataset.output_shapes - - @property - def output_types(self): - return self._input_dataset.output_types - - -# TODO(b/64974358): Increase default buffer size to 256 MB. -_DEFAULT_READER_BUFFER_SIZE_BYTES = 256 * 1024 # 256 KB - - -def _convert_optional_param_to_tensor(argument_name, - argument_value, - argument_default=0, - argument_dtype=dtypes.int64): - if argument_value is not None: - return ops.convert_to_tensor( - argument_value, dtype=argument_dtype, name=argument_name) - else: - return constant_op.constant( - argument_default, dtype=argument_dtype, name=argument_name) + return Dataset(dataset) class TextLineDataset(Dataset): @@ -2191,85 +760,9 @@ class TextLineDataset(Dataset): to buffer. A value of 0 results in the default buffering values chosen based on the compression type. """ - super(TextLineDataset, self).__init__() - self._filenames = ops.convert_to_tensor( - filenames, dtype=dtypes.string, name="filenames") - self._compression_type = _convert_optional_param_to_tensor( - "compression_type", - compression_type, - argument_default="", - argument_dtype=dtypes.string) - self._buffer_size = _convert_optional_param_to_tensor( - "buffer_size", buffer_size, _DEFAULT_READER_BUFFER_SIZE_BYTES) - - def make_dataset_resource(self): - return gen_dataset_ops.text_line_dataset( - self._filenames, self._compression_type, self._buffer_size) - - @property - def output_shapes(self): - return tensor_shape.scalar() - - @property - def output_types(self): - return dtypes.string - - -class SqlDataset(Dataset): - """A `Dataset` consisting of the results from a SQL query.""" - - def __init__(self, driver_name, data_source_name, query, output_types): - """Creates a `SqlDataset`. - - `SqlDataset` allows a user to read data from the result set of a SQL query. - For example: - - ```python - dataset = tf.contrib.data.SqlDataset("sqlite", "/foo/bar.sqlite3", - "SELECT name, age FROM people", - (tf.string, tf.int32)) - iterator = dataset.make_one_shot_iterator() - next_element = iterator.get_next() - # Prints the rows of the result set of the above query. - while True: - try: - print(sess.run(next_element)) - except tf.errors.OutOfRangeError: - break - ``` - - Args: - driver_name: A 0-D `tf.string` tensor containing the database type. - Currently, the only supported value is 'sqlite'. - data_source_name: A 0-D `tf.string` tensor containing a connection string - to connect to the database. - query: A 0-D `tf.string` tensor containing the SQL query to execute. - output_types: A tuple of `tf.DType` objects representing the types of the - columns returned by `query`. - """ - super(SqlDataset, self).__init__() - self._driver_name = ops.convert_to_tensor( - driver_name, dtype=dtypes.string, name="driver_name") - self._data_source_name = ops.convert_to_tensor( - data_source_name, dtype=dtypes.string, name="data_source_name") - self._query = ops.convert_to_tensor( - query, dtype=dtypes.string, name="query") - self._output_types = output_types - - def make_dataset_resource(self): - return gen_dataset_ops.sql_dataset(self._driver_name, - self._data_source_name, self._query, - nest.flatten(self.output_types), - nest.flatten(self.output_shapes)) - - @property - def output_shapes(self): - return nest.map_structure(lambda _: tensor_shape.TensorShape([]), - self._output_types) - - @property - def output_types(self): - return self._output_types + dataset = dataset_ops.TextLineDataset(filenames, compression_type, + buffer_size) + super(TextLineDataset, self).__init__(dataset) class TFRecordDataset(Dataset): @@ -2285,30 +778,9 @@ class TFRecordDataset(Dataset): buffer_size: (Optional.) A `tf.int64` scalar representing the number of bytes in the read buffer. 0 means no buffering. """ - super(TFRecordDataset, self).__init__() - self._filenames = ops.convert_to_tensor( - filenames, dtype=dtypes.string, name="filenames") - self._compression_type = _convert_optional_param_to_tensor( - "compression_type", - compression_type, - argument_default="", - argument_dtype=dtypes.string) - self._buffer_size = _convert_optional_param_to_tensor( - "buffer_size", - buffer_size, - argument_default=_DEFAULT_READER_BUFFER_SIZE_BYTES) - - def make_dataset_resource(self): - return gen_dataset_ops.tf_record_dataset( - self._filenames, self._compression_type, self._buffer_size) - - @property - def output_shapes(self): - return tensor_shape.TensorShape([]) - - @property - def output_types(self): - return dtypes.string + dataset = dataset_ops.TFRecordDataset(filenames, compression_type, + buffer_size) + super(TFRecordDataset, self).__init__(dataset) class FixedLengthRecordDataset(Dataset): @@ -2333,31 +805,147 @@ class FixedLengthRecordDataset(Dataset): buffer_size: (Optional.) A `tf.int64` scalar representing the number of bytes to buffer when reading. """ - super(FixedLengthRecordDataset, self).__init__() - self._filenames = ops.convert_to_tensor( - filenames, dtype=dtypes.string, name="filenames") - self._record_bytes = ops.convert_to_tensor( - record_bytes, dtype=dtypes.int64, name="record_bytes") + dataset = dataset_ops.FixedLengthRecordDataset( + filenames, record_bytes, header_bytes, footer_bytes, buffer_size) + super(FixedLengthRecordDataset, self).__init__(dataset) - self._header_bytes = _convert_optional_param_to_tensor( - "header_bytes", header_bytes) - self._footer_bytes = _convert_optional_param_to_tensor( - "footer_bytes", footer_bytes) - self._buffer_size = _convert_optional_param_to_tensor( - "buffer_size", buffer_size, _DEFAULT_READER_BUFFER_SIZE_BYTES) - def make_dataset_resource(self): - return gen_dataset_ops.fixed_length_record_dataset( - self._filenames, self._header_bytes, self._record_bytes, - self._footer_bytes, self._buffer_size) +def enumerate_dataset(start=0): + """A transformation that enumerate the elements of a dataset. - @property - def output_shapes(self): - return tensor_shape.scalar() + It is Similar to python's `enumerate`. + For example: - @property - def output_types(self): - return dtypes.string + ```python + # NOTE: The following examples use `{ ... }` to represent the + # contents of a dataset. + a = { 1, 2, 3 } + b = { (7, 8), (9, 10) } + + # The nested structure of the `datasets` argument determines the + # structure of elements in the resulting dataset. + a.apply(tf.contrib.data.enumerate(start=5)) == { (5, 1), (6, 2), (7, 3) } + b.apply(tf.contrib.data.enumerate()) == { (0, (7, 8)), (1, (9, 10)) } + ``` + + Args: + start: A `tf.int64` scalar `tf.Tensor`, representing the start + value for enumeration. + + Returns: + A `Dataset` transformation function, which can be passed to + @{tf.contrib.data.Dataset.apply}. + """ + + def _apply_fn(dataset): + max_value = np.iinfo(dtypes.int64.as_numpy_dtype).max + return Dataset.zip((Dataset.range(start, max_value), dataset)) + + return _apply_fn + + +def ignore_errors(): + """Creates a `Dataset` from another `Dataset` and silently ignores any errors. + + Use this transformation to produce a dataset that contains the same elements + as the input, but silently drops any elements that caused an error. For + example: + + ```python + dataset = tf.contrib.data.Dataset.from_tensor_slices([1., 2., 0., 4.]) + + # Computing `tf.check_numerics(1. / 0.)` will raise an InvalidArgumentError. + dataset = dataset.map(lambda x: tf.check_numerics(1. / x, "error")) + + # Using `ignore_errors()` will drop the element that causes an error. + dataset = + dataset.apply(tf.contrib.data.ignore_errors()) # ==> { 1., 0.5, 0.2 } + ``` + + Returns: + A `Dataset` transformation function, which can be passed to + @{tf.contrib.data.Dataset.apply}. + """ + + def _apply_fn(dataset): + return IgnoreErrorsDataset(dataset) + + return _apply_fn + + +def dense_to_sparse_batch(batch_size, row_shape): + """A transformation that batches ragged elements into `tf.SparseTensor`s. + + Like `Dataset.padded_batch()`, this transformation combines multiple + consecutive elements of the dataset, which might have different + shapes, into a single element. The resulting element has three + components (`indices`, `values`, and `dense_shape`), which + comprise a `tf.SparseTensor` that represents the same data. The + `row_shape` represents the dense shape of each row in the + resulting `tf.SparseTensor`, to which the effective batch size is + prepended. For example: + + ```python + # NOTE: The following examples use `{ ... }` to represent the + # contents of a dataset. + a = { ['a', 'b', 'c'], ['a', 'b'], ['a', 'b', 'c', 'd'] } + + a.apply(tf.contrib.data.dense_to_sparse_batch(batch_size=2, row_shape=[6])) == + { + ([[0, 0], [0, 1], [0, 2], [1, 0], [1, 1]], # indices + ['a', 'b', 'c', 'a', 'b'], # values + [2, 6]), # dense_shape + ([[2, 0], [2, 1], [2, 2], [2, 3]], + ['a', 'b', 'c', 'd'], + [1, 6]) + } + ``` + + Args: + batch_size: A `tf.int64` scalar `tf.Tensor`, representing the + number of consecutive elements of this dataset to combine in a + single batch. + row_shape: A `tf.TensorShape` or `tf.int64` vector tensor-like + object representing the equivalent dense shape of a row in the + resulting `tf.SparseTensor`. Each element of this dataset must + have the same rank as `row_shape`, and must have size less + than or equal to `row_shape` in each dimension. + + Returns: + A `Dataset` transformation function, which can be passed to + @{tf.contrib.data.Dataset.apply}. + """ + + def _apply_fn(dataset): + return DenseToSparseBatchDataset(dataset, batch_size, row_shape) + + return _apply_fn + + +def unbatch(): + """A Transformation which splits the elements of a dataset. + + For example, if elements of the dataset are shaped `[B, a0, a1, ...]`, + where `B` may vary from element to element, then for each element in + the dataset, the unbatched dataset will contain `B` consecutive elements + of shape `[a0, a1, ...]`. + + Returns: + A `Dataset` transformation function, which can be passed to + @{tf.contrib.data.Dataset.apply}. + """ + + def _apply_fn(dataset): + + def unbatch_map(arg, *rest): + if rest: + return Dataset.from_tensor_slices((arg,) + rest) + else: + return Dataset.from_tensor_slices(arg) + + return dataset.flat_map(map_func=unbatch_map) + + return _apply_fn def rejection_resample(class_func, @@ -2415,7 +1003,7 @@ def rejection_resample(class_func, [dist_estimation_batch_size, 1]) initial_dist_ds = (class_values_ds.batch(dist_estimation_batch_size) - .map(update_estimate_and_tile).unbatch()) + .map(update_estimate_and_tile).apply(unbatch())) acceptance_dist_ds = initial_dist_ds.map( lambda initial: _calculate_acceptance_probs(initial, target_dist_t)) @@ -2444,6 +1032,161 @@ def rejection_resample(class_func, return _apply_fn +def _calculate_acceptance_probs(initial_probs, target_probs): + """Calculate the per-class acceptance rates. + + Args: + initial_probs: The class probabilities of the data. + target_probs: The desired class proportion in minibatches. + Returns: + A list of the per-class acceptance probabilities. + + This method is based on solving the following analysis: + + Let F be the probability of a rejection (on any example). + Let p_i be the proportion of examples in the data in class i (init_probs) + Let a_i is the rate the rejection sampler should *accept* class i + Let t_i is the target proportion in the minibatches for class i (target_probs) + + ``` + F = sum_i(p_i * (1-a_i)) + = 1 - sum_i(p_i * a_i) using sum_i(p_i) = 1 + ``` + + An example with class `i` will be accepted if `k` rejections occur, then an + example with class `i` is seen by the rejector, and it is accepted. This can + be written as follows: + + ``` + t_i = sum_k=0^inf(F^k * p_i * a_i) + = p_i * a_j / (1 - F) using geometric series identity, since 0 <= F < 1 + = p_i * a_i / sum_j(p_j * a_j) using F from above + ``` + + Note that the following constraints hold: + ``` + 0 <= p_i <= 1, sum_i(p_i) = 1 + 0 <= a_i <= 1 + 0 <= t_i <= 1, sum_i(t_i) = 1 + ``` + + + A solution for a_i in terms of the other variabes is the following: + ```a_i = (t_i / p_i) / max_i[t_i / p_i]``` + """ + # Add tiny to initial_probs to avoid divide by zero. + denom = (initial_probs + np.finfo(initial_probs.dtype.as_numpy_dtype).tiny) + ratio_l = target_probs / denom + + # Calculate list of acceptance probabilities. + max_ratio = math_ops.reduce_max(ratio_l) + return ratio_l / max_ratio + + +def _estimate_data_distribution(c, num_examples_per_class_seen): + """Estimate data distribution as labels are seen. + + Args: + c: The class labels. Type `int32`, shape `[batch_size]`. + num_examples_per_class_seen: A `ResourceVariable` containing counts. + Type `int64`, shape `[num_classes]`. + + Returns: + dist: The updated distribution. Type `float32`, shape `[num_classes]`. + """ + num_classes = num_examples_per_class_seen.get_shape()[0].value + # Update the class-count based on what labels are seen in + # batch. But do this asynchronously to avoid performing a + # cross-device round-trip. Just use the cached value. + num_examples_per_class_seen = num_examples_per_class_seen.assign_add( + math_ops.reduce_sum( + array_ops.one_hot(c, num_classes, dtype=dtypes.int64), 0)) + init_prob_estimate = math_ops.truediv( + num_examples_per_class_seen, + math_ops.reduce_sum(num_examples_per_class_seen)) + return math_ops.cast(init_prob_estimate, dtypes.float32) + + +class _VariantDataset(dataset_ops.Dataset): + """A Dataset wrapper for a tf.variant-typed function argument.""" + + def __init__(self, dataset_variant, output_types, output_shapes): + super(_VariantDataset, self).__init__() + self._dataset_variant = dataset_variant + self._output_types = output_types + self._output_shapes = output_shapes + + def make_dataset_resource(self): + return self._dataset_variant + + @property + def output_shapes(self): + return self._output_shapes + + @property + def output_types(self): + return self._output_types + + +class DenseToSparseBatchDataset(dataset_ops.Dataset): + """A `Dataset` that batches ragged dense elements into `tf.SparseTensor`s.""" + + def __init__(self, input_dataset, batch_size, row_shape): + """See `Dataset.dense_to_sparse_batch()` for more details.""" + super(DenseToSparseBatchDataset, self).__init__() + if not isinstance(input_dataset.output_types, dtypes.DType): + raise TypeError("DenseToSparseDataset requires an input whose elements " + "have a single component, whereas the input has %r." % + input_dataset.output_types) + self._input_dataset = input_dataset + self._batch_size = batch_size + # pylint: disable=protected-access + self._row_shape = dataset_ops._partial_shape_to_tensor(row_shape) + # pylint: enable=protected-access + + def make_dataset_resource(self): + return gen_dataset_ops.dense_to_sparse_batch_dataset( + self._input_dataset.make_dataset_resource(), + self._batch_size, + self._row_shape, + output_shapes=self.output_shapes, + output_types=self.output_types) + + @property + def output_shapes(self): + num_elements = tensor_shape.Dimension(None) + return (tensor_shape.matrix(num_elements, self._row_shape.shape[0] + 1), + tensor_shape.vector(num_elements), + tensor_shape.vector(self._row_shape.shape[0] + 1)) + + @property + def output_types(self): + return (dtypes.int64, self._input_dataset.output_types, dtypes.int64) + + +class IgnoreErrorsDataset(dataset_ops.Dataset): + """A `Dataset` that silently ignores errors when computing its input.""" + + def __init__(self, input_dataset): + """See `Dataset.ignore_errors()` for details.""" + super(IgnoreErrorsDataset, self).__init__() + self._input_dataset = input_dataset + + def make_dataset_resource(self): + return gen_dataset_ops.ignore_errors_dataset( + self._input_dataset.make_dataset_resource(), + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + return self._input_dataset.output_shapes + + @property + def output_types(self): + return self._input_dataset.output_types + + def read_batch_features(file_pattern, batch_size, features, @@ -2591,7 +1334,7 @@ def _get_file_names(file_pattern, randomize_input): return file_names -class GroupByWindowDataset(Dataset): +class GroupByWindowDataset(dataset_ops.Dataset): """A `Dataset` that groups its input and performs a windowed reduction.""" def __init__(self, input_dataset, key_func, reduce_func, window_size_func): @@ -2630,8 +1373,10 @@ class GroupByWindowDataset(Dataset): for arg, shape in zip(args, nest.flatten(input_dataset.output_shapes)): arg.set_shape(shape) nested_args = nest.pack_sequence_as(input_dataset.output_types, args) - if _should_unpack_args(nested_args): + # pylint: disable=protected-access + if dataset_ops._should_unpack_args(nested_args): ret = key_func(*nested_args) + # pylint: enable=protected-access else: ret = key_func(nested_args) ret = ops.convert_to_tensor(ret, dtype=dtypes.int64) @@ -2652,8 +1397,10 @@ class GroupByWindowDataset(Dataset): window_dataset = _VariantDataset(window_dataset_variant, input_dataset.output_types, input_dataset.output_shapes) + if not isinstance(window_dataset, dataset_ops.Dataset): + raise TypeError("`window_dataset` must return a `Dataset` object.") output_dataset = reduce_func(key, window_dataset) - if not isinstance(output_dataset, Dataset): + if not isinstance(output_dataset, dataset_ops.Dataset): raise TypeError("`reduce_func` must return a `Dataset` object.") self._output_types = output_dataset.output_types self._output_shapes = output_dataset.output_shapes @@ -2742,7 +1489,64 @@ def group_by_window(key_func, return _apply_fn -class _RestructuredDataset(Dataset): +class SqlDataset(dataset_ops.Dataset): + """A `Dataset` consisting of the results from a SQL query.""" + + def __init__(self, driver_name, data_source_name, query, output_types): + """Creates a `SqlDataset`. + + `SqlDataset` allows a user to read data from the result set of a SQL query. + For example: + + ```python + dataset = tf.contrib.data.SqlDataset("sqlite", "/foo/bar.sqlite3", + "SELECT name, age FROM people", + (tf.string, tf.int32)) + iterator = dataset.make_one_shot_iterator() + next_element = iterator.get_next() + # Prints the rows of the result set of the above query. + while True: + try: + print(sess.run(next_element)) + except tf.errors.OutOfRangeError: + break + ``` + + Args: + driver_name: A 0-D `tf.string` tensor containing the database type. + Currently, the only supported value is 'sqlite'. + data_source_name: A 0-D `tf.string` tensor containing a connection string + to connect to the database. + query: A 0-D `tf.string` tensor containing the SQL query to execute. + output_types: A tuple of `tf.DType` objects representing the types of the + columns returned by `query`. + """ + super(SqlDataset, self).__init__() + self._driver_name = ops.convert_to_tensor( + driver_name, dtype=dtypes.string, name="driver_name") + self._data_source_name = ops.convert_to_tensor( + data_source_name, dtype=dtypes.string, name="data_source_name") + self._query = ops.convert_to_tensor( + query, dtype=dtypes.string, name="query") + self._output_types = output_types + + def make_dataset_resource(self): + return gen_dataset_ops.sql_dataset(self._driver_name, + self._data_source_name, self._query, + nest.flatten(self.output_types), + nest.flatten(self.output_shapes)) + + @property + def output_shapes(self): + return nest.map_structure(lambda _: tensor_shape.TensorShape([]), + self._output_types) + + @property + def output_types(self): + return self._output_types + + +class _RestructuredDataset(dataset_ops.Dataset): """An internal helper for changing the structure and shape of a dataset.""" def __init__(self, dataset, output_types, output_shapes=None): diff --git a/tensorflow/contrib/data/python/ops/sloppy_ops.py b/tensorflow/contrib/data/python/ops/sloppy_ops.py index f8053c43d65..375f54193c6 100644 --- a/tensorflow/contrib/data/python/ops/sloppy_ops.py +++ b/tensorflow/contrib/data/python/ops/sloppy_ops.py @@ -17,8 +17,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -from tensorflow.contrib.data.python.ops import dataset_ops -from tensorflow.contrib.data.python.util import nest +from tensorflow.python.data.ops import dataset_ops +from tensorflow.python.data.util import nest from tensorflow.python.framework import dtypes from tensorflow.python.framework import function from tensorflow.python.framework import ops diff --git a/tensorflow/contrib/eager/python/BUILD b/tensorflow/contrib/eager/python/BUILD index d112bc3e9fd..10c276826df 100644 --- a/tensorflow/contrib/eager/python/BUILD +++ b/tensorflow/contrib/eager/python/BUILD @@ -41,11 +41,10 @@ py_library( srcs_version = "PY2AND3", visibility = ["//tensorflow:internal"], deps = [ - "//tensorflow/contrib/data/python/ops:dataset_ops", - "//tensorflow/contrib/data/python/util:nest", "//tensorflow/python:dataset_ops_gen", "//tensorflow/python:errors", "//tensorflow/python:resource_variable_ops", + "//tensorflow/python/data/util:nest", "//tensorflow/python/eager:context", ], ) diff --git a/tensorflow/contrib/eager/python/datasets.py b/tensorflow/contrib/eager/python/datasets.py index f7f9ddd7b08..7e353eb3f44 100644 --- a/tensorflow/contrib/eager/python/datasets.py +++ b/tensorflow/contrib/eager/python/datasets.py @@ -20,7 +20,7 @@ from __future__ import print_function import threading -from tensorflow.contrib.data.python.util import nest +from tensorflow.python.data.util import nest from tensorflow.python.eager import context from tensorflow.python.framework import errors from tensorflow.python.ops import gen_dataset_ops diff --git a/tensorflow/contrib/fused_conv/BUILD b/tensorflow/contrib/fused_conv/BUILD index 9b34cf1bdb0..31917b40eb9 100644 --- a/tensorflow/contrib/fused_conv/BUILD +++ b/tensorflow/contrib/fused_conv/BUILD @@ -63,6 +63,7 @@ tf_kernel_library( "kernels/fused_conv_ops_gpu.h", ], prefix = "fused_conv2d_bias_activation_op", + visibility = ["//visibility:public"], deps = [ "//tensorflow/core:framework", "//tensorflow/core:lib", diff --git a/tensorflow/contrib/gan/BUILD b/tensorflow/contrib/gan/BUILD index 39acfcc1877..54dbb11b6eb 100644 --- a/tensorflow/contrib/gan/BUILD +++ b/tensorflow/contrib/gan/BUILD @@ -41,7 +41,6 @@ py_library( "//tensorflow/contrib/training:training_py", "//tensorflow/python:array_ops", "//tensorflow/python:check_ops", - "//tensorflow/python:dtypes", "//tensorflow/python:framework_ops", "//tensorflow/python:init_ops", "//tensorflow/python:training", diff --git a/tensorflow/contrib/gan/python/train.py b/tensorflow/contrib/gan/python/train.py index af7dbcf249a..cdc4d78e5b2 100644 --- a/tensorflow/contrib/gan/python/train.py +++ b/tensorflow/contrib/gan/python/train.py @@ -26,7 +26,6 @@ from tensorflow.contrib.gan.python import losses as tfgan_losses from tensorflow.contrib.gan.python import namedtuples from tensorflow.contrib.slim.python.slim import learning as slim_learning from tensorflow.contrib.training.python.training import training -from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.ops import array_ops from tensorflow.python.ops import check_ops @@ -549,7 +548,7 @@ def gan_train_ops( generator_global_step = variable_scope.get_variable( 'dummy_global_step_generator', shape=[], - dtype=dtypes.int64, + dtype=global_step.dtype.base_dtype, initializer=init_ops.zeros_initializer(), trainable=False, collections=[ops.GraphKeys.GLOBAL_VARIABLES]) @@ -570,7 +569,7 @@ def gan_train_ops( discriminator_global_step = variable_scope.get_variable( 'dummy_global_step_discriminator', shape=[], - dtype=dtypes.int64, + dtype=global_step.dtype.base_dtype, initializer=init_ops.zeros_initializer(), trainable=False, collections=[ops.GraphKeys.GLOBAL_VARIABLES]) diff --git a/tensorflow/contrib/gan/python/train_test.py b/tensorflow/contrib/gan/python/train_test.py index 83b763806cd..6b27b692610 100644 --- a/tensorflow/contrib/gan/python/train_test.py +++ b/tensorflow/contrib/gan/python/train_test.py @@ -542,11 +542,17 @@ class GANTrainOpsTest(test.TestCase): def test_unused_update_ops_callable_acgan_provideupdates(self): self._test_unused_update_ops(create_callable_acgan_model, True) - def _test_sync_replicas_helper(self, create_gan_model_fn): + def _test_sync_replicas_helper( + self, create_gan_model_fn, create_global_step=False): model = create_gan_model_fn() loss = train.gan_loss(model) num_trainable_vars = len(variables_lib.get_trainable_variables()) + if create_global_step: + gstep = variable_scope.get_variable( + 'custom_gstep', dtype=dtypes.int32, initializer=0, trainable=False) + ops.add_to_collection(ops.GraphKeys.GLOBAL_STEP, gstep) + g_opt = get_sync_optimizer() d_opt = get_sync_optimizer() train_ops = train.gan_train_ops( @@ -610,6 +616,9 @@ class GANTrainOpsTest(test.TestCase): def test_sync_replicas_callable_acgan(self): self._test_sync_replicas_helper(create_callable_acgan_model) + def test_global_step_can_be_int32(self): + self._test_sync_replicas_helper(create_gan_model, create_global_step=True) + class GANTrainTest(test.TestCase): """Tests for `gan_train`.""" diff --git a/tensorflow/contrib/receptive_field/README.md b/tensorflow/contrib/receptive_field/README.md index b150b903b23..dfe53cdf142 100644 --- a/tensorflow/contrib/receptive_field/README.md +++ b/tensorflow/contrib/receptive_field/README.md @@ -38,7 +38,7 @@ models are available to you. This can be done in three simple commands: ```sh git clone https://github.com/tensorflow/models -cd models/slim +cd models/research/slim sudo python setup.py install_lib ``` diff --git a/tensorflow/contrib/seq2seq/python/kernel_tests/beam_search_decoder_test.py b/tensorflow/contrib/seq2seq/python/kernel_tests/beam_search_decoder_test.py index 3d0627467aa..2caeb9eb614 100644 --- a/tensorflow/contrib/seq2seq/python/kernel_tests/beam_search_decoder_test.py +++ b/tensorflow/contrib/seq2seq/python/kernel_tests/beam_search_decoder_test.py @@ -121,7 +121,7 @@ class TestBeamStep(test.TestCase): log_probs=nn_ops.log_softmax( array_ops.ones([self.batch_size, self.beam_width])), lengths=constant_op.constant( - 2, shape=[self.batch_size, self.beam_width], dtype=dtypes.int32), + 2, shape=[self.batch_size, self.beam_width], dtype=dtypes.int64), finished=array_ops.zeros( [self.batch_size, self.beam_width], dtype=dtypes.bool)) @@ -176,7 +176,7 @@ class TestBeamStep(test.TestCase): log_probs=nn_ops.log_softmax( array_ops.ones([self.batch_size, self.beam_width])), lengths=ops.convert_to_tensor( - [[2, 1, 2], [2, 2, 1]], dtype=dtypes.int32), + [[2, 1, 2], [2, 2, 1]], dtype=dtypes.int64), finished=ops.convert_to_tensor( [[False, True, False], [False, False, True]], dtype=dtypes.bool)) diff --git a/tensorflow/contrib/seq2seq/python/ops/beam_search_decoder.py b/tensorflow/contrib/seq2seq/python/ops/beam_search_decoder.py index ef1735a6c98..1cfd5f32a70 100644 --- a/tensorflow/contrib/seq2seq/python/ops/beam_search_decoder.py +++ b/tensorflow/contrib/seq2seq/python/ops/beam_search_decoder.py @@ -22,6 +22,7 @@ import collections from tensorflow.contrib.seq2seq.python.ops import beam_search_ops from tensorflow.contrib.seq2seq.python.ops import decoder +from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes from tensorflow.python.framework import ops from tensorflow.python.framework import tensor_shape @@ -256,7 +257,7 @@ class BeamSearchDecoder(decoder.Decoder): dtype=nest.flatten(self._initial_cell_state)[0].dtype), finished=finished, lengths=array_ops.zeros( - [self._batch_size, self._beam_width], dtype=dtypes.int32)) + [self._batch_size, self._beam_width], dtype=dtypes.int64)) return (finished, start_inputs, initial_state) @@ -267,7 +268,7 @@ class BeamSearchDecoder(decoder.Decoder): outputs: An instance of BeamSearchDecoderOutput. final_state: An instance of BeamSearchDecoderState. Passed through to the output. - sequence_lengths: An `int32` tensor shaped `[batch_size, beam_width]`. + sequence_lengths: An `int64` tensor shaped `[batch_size, beam_width]`. The sequence lengths determined for each beam during decode. Returns: @@ -491,9 +492,10 @@ def _beam_search_step(time, logits, next_cell_state, beam_state, batch_size, indices=array_ops.tile( array_ops.reshape(end_token, [1, 1]), [batch_size, beam_width]), depth=vocab_size, - on_value=0, - off_value=1) - add_mask = (1 - math_ops.to_int32(previously_finished)) + on_value=constant_op.constant(0, dtype=dtypes.int64), + off_value=constant_op.constant(1, dtype=dtypes.int64), + dtype=dtypes.int64) + add_mask = (1 - math_ops.to_int64(previously_finished)) lengths_to_add = array_ops.expand_dims(add_mask, 2) * lengths_to_add new_prediction_lengths = ( lengths_to_add + array_ops.expand_dims(prediction_lengths, 2)) @@ -547,9 +549,9 @@ def _beam_search_step(time, logits, next_cell_state, beam_state, batch_size, # 1. Finished beams remain unchanged # 2. Beams that are now finished (EOS predicted) remain unchanged # 3. Beams that are not yet finished have their length increased by 1 - lengths_to_add = math_ops.to_int32( + lengths_to_add = math_ops.to_int64( math_ops.not_equal(next_word_ids, end_token)) - lengths_to_add = (1 - math_ops.to_int32(next_finished)) * lengths_to_add + lengths_to_add = (1 - math_ops.to_int64(next_finished)) * lengths_to_add next_prediction_len = _tensor_gather_helper( gather_indices=next_beam_ids, gather_from=beam_state.lengths, diff --git a/tensorflow/contrib/signal/python/kernel_tests/mel_ops_test.py b/tensorflow/contrib/signal/python/kernel_tests/mel_ops_test.py index 0448ff5bb0f..f107b53f01c 100644 --- a/tensorflow/contrib/signal/python/kernel_tests/mel_ops_test.py +++ b/tensorflow/contrib/signal/python/kernel_tests/mel_ops_test.py @@ -33,7 +33,7 @@ def hertz_to_mel(frequencies_hertz): """Convert frequencies to mel scale using HTK formula. Copied from - https://github.com/tensorflow/models/blob/master/audioset/mel_features.py. + https://github.com/tensorflow/models/blob/master/research/audioset/mel_features.py. Args: frequencies_hertz: Scalar or np.array of frequencies in hertz. @@ -54,7 +54,7 @@ def spectrogram_to_mel_matrix(num_mel_bins=20, """Return a matrix that can post-multiply spectrogram rows to make mel. Copied from - https://github.com/tensorflow/models/blob/master/audioset/mel_features.py. + https://github.com/tensorflow/models/blob/master/research/audioset/mel_features.py. Returns a np.array matrix A that can be used to post-multiply a matrix S of spectrogram values (STFT magnitudes) arranged as frames x bins to generate a diff --git a/tensorflow/contrib/tpu/BUILD b/tensorflow/contrib/tpu/BUILD index c952288704a..e753fe7a514 100644 --- a/tensorflow/contrib/tpu/BUILD +++ b/tensorflow/contrib/tpu/BUILD @@ -13,6 +13,7 @@ load("//tensorflow:tensorflow.bzl", "tf_py_test") package( default_visibility = [ + "//cloud/vmm/testing/tests/tpu:__subpackages__", "//learning/brain:__subpackages__", "//tensorflow:__subpackages__", ], diff --git a/tensorflow/contrib/verbs/BUILD b/tensorflow/contrib/verbs/BUILD index 173a65a7eb6..746ff38b37f 100644 --- a/tensorflow/contrib/verbs/BUILD +++ b/tensorflow/contrib/verbs/BUILD @@ -50,11 +50,8 @@ cc_library( srcs = ["verbs_util.cc"], hdrs = ["verbs_util.h"], deps = [ - "//tensorflow/core:core_cpu_internal", "//tensorflow/core:framework", - "//tensorflow/core:gpu_runtime", "//tensorflow/core:lib", - "//tensorflow/core:lib_internal", ], ) diff --git a/tensorflow/contrib/verbs/verbs_util.cc b/tensorflow/contrib/verbs/verbs_util.cc index 4f5c731a188..a6333d9f362 100644 --- a/tensorflow/contrib/verbs/verbs_util.cc +++ b/tensorflow/contrib/verbs/verbs_util.cc @@ -15,9 +15,13 @@ limitations under the License. #include "tensorflow/contrib/verbs/verbs_util.h" -#include "tensorflow/core/common_runtime/gpu/gpu_util.h" -#include "tensorflow/core/lib/core/notification.h" +#include + +#include "tensorflow/core/lib/core/stringpiece.h" +#include "tensorflow/core/lib/strings/numbers.h" #include "tensorflow/core/lib/strings/str_util.h" +#include "tensorflow/core/lib/strings/strcat.h" + namespace tensorflow { // static diff --git a/tensorflow/contrib/verbs/verbs_util.h b/tensorflow/contrib/verbs/verbs_util.h index 8b44adaedcb..5cd0a3533af 100644 --- a/tensorflow/contrib/verbs/verbs_util.h +++ b/tensorflow/contrib/verbs/verbs_util.h @@ -18,14 +18,10 @@ limitations under the License. #include -#include "tensorflow/core/common_runtime/device.h" -#include "tensorflow/core/framework/tensor.h" -#include "tensorflow/core/lib/core/status.h" +#include "tensorflow/core/framework/types.h" namespace tensorflow { -class TensorProto; - class VerbsUtil { public: static string AppendStepidToKey(const string& key, int64 step_id); diff --git a/tensorflow/core/grappler/BUILD b/tensorflow/core/grappler/BUILD index 7fa22ef47d9..3f2cd2ddbff 100644 --- a/tensorflow/core/grappler/BUILD +++ b/tensorflow/core/grappler/BUILD @@ -55,12 +55,14 @@ tf_cuda_library( name = "devices", srcs = ["devices.cc"], hdrs = ["devices.h"], + cuda_deps = [ + "//tensorflow/core:gpu_init", + "//tensorflow/core:stream_executor", + ], visibility = ["//visibility:public"], deps = [ - "//tensorflow/core:gpu_init", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", - "//tensorflow/core:stream_executor", ], ) diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index d8e4941dca7..a08e2f5ee3d 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -2300,7 +2300,12 @@ tf_kernel_library( tf_kernel_library( name = "self_adjoint_eig_v2_op", prefix = "self_adjoint_eig_v2_op", - deps = LINALG_DEPS, + deps = LINALG_DEPS + if_cuda([ + ":cast_op", + ":cwise_op", + ":cuda_solvers", + ":transpose_functor", + ]), ) tf_kernel_library( @@ -4011,7 +4016,6 @@ tf_kernel_library( name = "word2vec_kernels", prefix = "word2vec_kernels", deps = [ - "//tensorflow/core", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", @@ -4803,8 +4807,8 @@ tf_kernel_library( ":image_resizer_state", ":ops_util", ":pooling_ops", - "//tensorflow/core", "//tensorflow/core:array_ops_op_lib", + "//tensorflow/core:core_cpu", "//tensorflow/core:framework", "//tensorflow/core:lib", "//tensorflow/core:math_ops_op_lib", diff --git a/tensorflow/core/kernels/cuda_solvers.cc b/tensorflow/core/kernels/cuda_solvers.cc index 29ced2434d2..2d8a4cc6286 100644 --- a/tensorflow/core/kernels/cuda_solvers.cc +++ b/tensorflow/core/kernels/cuda_solvers.cc @@ -194,15 +194,14 @@ Status CudaSolver::CopyLapackInfoToHostAsync( #define TF_CALL_LAPACK_TYPES_NO_COMPLEX(m) m(float, S) m(double, D) // Macros to construct cusolverDn method names. -#define DN_SOLVER_FN(method, lapack_prefix) cusolverDn##lapack_prefix##method -#define DN_SOLVER_NAME(method, lapack_prefix) \ - "cusolverDn" #lapack_prefix #method -#define DN_BUFSIZE_FN(method, lapack_prefix) \ - cusolverDn##lapack_prefix##method##_bufferSize +#define DN_SOLVER_FN(method, type_prefix) cusolverDn##type_prefix##method +#define DN_SOLVER_NAME(method, type_prefix) "cusolverDn" #type_prefix #method +#define DN_BUFSIZE_FN(method, type_prefix) \ + cusolverDn##type_prefix##method##_bufferSize // Macros to construct cublas method names. -#define BLAS_SOLVER_FN(method, lapack_prefix) cublas##lapack_prefix##method -#define BLAS_SOLVER_NAME(method, lapack_prefix) "cublas" #lapack_prefix #method +#define BLAS_SOLVER_FN(method, type_prefix) cublas##type_prefix##method +#define BLAS_SOLVER_NAME(method, type_prefix) "cublas" #type_prefix #method //============================================================================= // Wrappers of cuSolverDN computational methods begin here. @@ -229,17 +228,16 @@ static inline Status GeamImpl(SolverFnT solver, cublasHandle_t cublas_handle, return Status::OK(); } -#define GEAM_INSTANCE(Scalar, lapack_prefix) \ - template <> \ - Status CudaSolver::Geam( \ - cublasOperation_t transa, cublasOperation_t transb, int m, int n, \ - const Scalar* alpha, /* host or device pointer */ \ - const Scalar* A, int lda, \ - const Scalar* beta, /* host or device pointer */ \ - const Scalar* B, int ldb, Scalar* C, int ldc) const { \ - return GeamImpl(BLAS_SOLVER_FN(geam, lapack_prefix), cublas_handle_, \ - transa, transb, m, n, alpha, A, lda, beta, B, ldb, C, \ - ldc); \ +#define GEAM_INSTANCE(Scalar, type_prefix) \ + template <> \ + Status CudaSolver::Geam( \ + cublasOperation_t transa, cublasOperation_t transb, int m, int n, \ + const Scalar* alpha, /* host or device pointer */ \ + const Scalar* A, int lda, \ + const Scalar* beta, /* host or device pointer */ \ + const Scalar* B, int ldb, Scalar* C, int ldc) const { \ + return GeamImpl(BLAS_SOLVER_FN(geam, type_prefix), cublas_handle_, transa, \ + transb, m, n, alpha, A, lda, beta, B, ldb, C, ldc); \ } TF_CALL_LAPACK_TYPES(GEAM_INSTANCE); @@ -263,12 +261,12 @@ static inline Status PotrfImpl(BufSizeFnT bufsize, SolverFnT solver, return Status::OK(); } -#define POTRF_INSTANCE(Scalar, lapack_prefix) \ +#define POTRF_INSTANCE(Scalar, type_prefix) \ template <> \ Status CudaSolver::Potrf(cublasFillMode_t uplo, int n, Scalar* A, \ int lda, int* dev_lapack_info) const { \ - return PotrfImpl(DN_BUFSIZE_FN(potrf, lapack_prefix), \ - DN_SOLVER_FN(potrf, lapack_prefix), context_, \ + return PotrfImpl(DN_BUFSIZE_FN(potrf, type_prefix), \ + DN_SOLVER_FN(potrf, type_prefix), context_, \ cusolver_dn_handle_, uplo, n, A, lda, dev_lapack_info); \ } @@ -293,13 +291,13 @@ static inline Status GetrfImpl(BufSizeFnT bufsize, SolverFnT solver, return Status::OK(); } -#define GETRF_INSTANCE(Scalar, lapack_prefix) \ +#define GETRF_INSTANCE(Scalar, type_prefix) \ template <> \ Status CudaSolver::Getrf(int m, int n, Scalar* A, int lda, \ int* dev_pivots, int* dev_lapack_info) \ const { \ - return GetrfImpl(DN_BUFSIZE_FN(getrf, lapack_prefix), \ - DN_SOLVER_FN(getrf, lapack_prefix), context_, \ + return GetrfImpl(DN_BUFSIZE_FN(getrf, type_prefix), \ + DN_SOLVER_FN(getrf, type_prefix), context_, \ cusolver_dn_handle_, m, n, A, lda, dev_pivots, \ dev_lapack_info); \ } @@ -319,53 +317,18 @@ static inline Status GetrsImpl(SolverFnT solver, OpKernelContext* context, return Status::OK(); } -#define GETRS_INSTANCE(Scalar, lapack_prefix) \ +#define GETRS_INSTANCE(Scalar, type_prefix) \ template <> \ Status CudaSolver::Getrs( \ cublasOperation_t trans, int n, int nrhs, const Scalar* A, int lda, \ const int* pivots, Scalar* B, int ldb, int* dev_lapack_info) const { \ - return GetrsImpl(DN_SOLVER_FN(getrs, lapack_prefix), context_, \ + return GetrsImpl(DN_SOLVER_FN(getrs, type_prefix), context_, \ cusolver_dn_handle_, trans, n, nrhs, A, lda, pivots, B, \ ldb, dev_lapack_info); \ } TF_CALL_LAPACK_TYPES(GETRS_INSTANCE); -template -static inline Status GesvdImpl(BufSizeFnT bufsize, SolverFnT solver, - OpKernelContext* context, - cusolverDnHandle_t cusolver_dn_handle, - signed char jobu, signed char jobvt, int m, - int n, Scalar* A, int lda, Scalar* S, Scalar* U, - int ldu, Scalar* VT, int ldvt, - int* dev_lapack_info) { - /* Get amount of workspace memory required. */ - int lwork; - TF_RETURN_IF_CUSOLVER_ERROR(bufsize(cusolver_dn_handle, m, n, &lwork)); - /* Allocate device memory for workspace. */ - ScratchSpace dev_workspace(context, lwork, /* on_host */ false); - /* Launch the solver kernel. */ - TF_RETURN_IF_CUSOLVER_ERROR(solver( - cusolver_dn_handle, jobu, jobvt, m, n, CUDAComplex(A), lda, S, - CUDAComplex(U), ldu, CUDAComplex(VT), ldvt, - CUDAComplex(dev_workspace.mutable_data()), lwork, NULL, dev_lapack_info)); - return Status::OK(); -} - -#define GESVD_INSTANCE(Scalar, lapack_prefix) \ - template <> \ - Status CudaSolver::Gesvd( \ - signed char jobu, signed char jobvt, int m, int n, Scalar* dev_A, \ - int lda, Scalar* dev_S, Scalar* dev_U, int ldu, Scalar* dev_VT, \ - int ldvt, int* dev_lapack_info) const { \ - return GesvdImpl(DN_BUFSIZE_FN(gesvd, lapack_prefix), \ - DN_SOLVER_FN(gesvd, lapack_prefix), context_, \ - cusolver_dn_handle_, jobu, jobvt, m, n, dev_A, lda, \ - dev_S, dev_U, ldu, dev_VT, ldvt, dev_lapack_info); \ - } - -TF_CALL_LAPACK_TYPES_NO_COMPLEX(GESVD_INSTANCE); - template static inline Status GeqrfImpl(BufSizeFnT bufsize, SolverFnT solver, OpKernelContext* context, @@ -385,19 +348,19 @@ static inline Status GeqrfImpl(BufSizeFnT bufsize, SolverFnT solver, return Status::OK(); } -#define GEQRF_INSTANCE(Scalar, lapack_prefix) \ +#define GEQRF_INSTANCE(Scalar, type_prefix) \ template <> \ Status CudaSolver::Geqrf(int m, int n, Scalar* A, int lda, \ Scalar* tau, int* dev_lapack_info) const { \ - return GeqrfImpl(DN_BUFSIZE_FN(geqrf, lapack_prefix), \ - DN_SOLVER_FN(geqrf, lapack_prefix), context_, \ + return GeqrfImpl(DN_BUFSIZE_FN(geqrf, type_prefix), \ + DN_SOLVER_FN(geqrf, type_prefix), context_, \ cusolver_dn_handle_, m, n, A, lda, tau, dev_lapack_info); \ } TF_CALL_LAPACK_TYPES(GEQRF_INSTANCE); template -static inline Status OrmqrImpl(BufSizeFnT bufsize, SolverFnT solver, +static inline Status UnmqrImpl(BufSizeFnT bufsize, SolverFnT solver, OpKernelContext* context, cusolverDnHandle_t cusolver_dn_handle, cublasSideMode_t side, cublasOperation_t trans, @@ -422,47 +385,25 @@ static inline Status OrmqrImpl(BufSizeFnT bufsize, SolverFnT solver, // Unfortunately the LAPACK function name differs for the real and complex case // (complex ones are prefixed with "UN" for "unitary"), so we instantiate each // one separately. -template <> -Status CudaSolver::Ormqr(cublasSideMode_t side, cublasOperation_t trans, int m, - int n, int k, const float* dev_a, int lda, - const float* dev_tau, float* dev_c, int ldc, - int* dev_lapack_info) const { - return OrmqrImpl(DN_BUFSIZE_FN(ormqr, S), DN_SOLVER_FN(ormqr, S), context_, - cusolver_dn_handle_, side, trans, m, n, k, dev_a, lda, - dev_tau, dev_c, ldc, dev_lapack_info); -} -template <> -Status CudaSolver::Ormqr(cublasSideMode_t side, cublasOperation_t trans, int m, - int n, int k, const double* dev_a, int lda, - const double* dev_tau, double* dev_c, int ldc, - int* dev_lapack_info) const { - return OrmqrImpl(DN_BUFSIZE_FN(ormqr, D), DN_SOLVER_FN(ormqr, D), context_, - cusolver_dn_handle_, side, trans, m, n, k, dev_a, lda, - dev_tau, dev_c, ldc, dev_lapack_info); -} -template <> -Status CudaSolver::Ormqr(cublasSideMode_t side, cublasOperation_t trans, int m, - int n, int k, const std::complex* dev_a, - int lda, const std::complex* dev_tau, - std::complex* dev_c, int ldc, - int* dev_lapack_info) const { - return OrmqrImpl(DN_BUFSIZE_FN(unmqr, C), DN_SOLVER_FN(unmqr, C), context_, - cusolver_dn_handle_, side, trans, m, n, k, dev_a, lda, - dev_tau, dev_c, ldc, dev_lapack_info); -} -template <> -Status CudaSolver::Ormqr(cublasSideMode_t side, cublasOperation_t trans, int m, - int n, int k, const std::complex* dev_a, - int lda, const std::complex* dev_tau, - std::complex* dev_c, int ldc, - int* dev_lapack_info) const { - return OrmqrImpl(DN_BUFSIZE_FN(unmqr, Z), DN_SOLVER_FN(unmqr, Z), context_, - cusolver_dn_handle_, side, trans, m, n, k, dev_a, lda, - dev_tau, dev_c, ldc, dev_lapack_info); -} +#define UNMQR_INSTANCE(Scalar, function_prefix, type_prefix) \ + template <> \ + Status CudaSolver::Unmqr(cublasSideMode_t side, cublasOperation_t trans, \ + int m, int n, int k, const Scalar* dev_a, int lda, \ + const Scalar* dev_tau, Scalar* dev_c, int ldc, \ + int* dev_lapack_info) const { \ + return UnmqrImpl(DN_BUFSIZE_FN(function_prefix##mqr, type_prefix), \ + DN_SOLVER_FN(function_prefix##mqr, type_prefix), \ + context_, cusolver_dn_handle_, side, trans, m, n, k, \ + dev_a, lda, dev_tau, dev_c, ldc, dev_lapack_info); \ + } + +UNMQR_INSTANCE(float, or, S); +UNMQR_INSTANCE(double, or, D); +UNMQR_INSTANCE(complex64, un, C); +UNMQR_INSTANCE(complex128, un, Z); template -static inline Status OrgqrImpl(BufSizeFnT bufsize, SolverFnT solver, +static inline Status UngqrImpl(BufSizeFnT bufsize, SolverFnT solver, OpKernelContext* context, cusolverDnHandle_t cusolver_dn_handle, int m, int n, int k, Scalar* dev_a, int lda, @@ -482,40 +423,97 @@ static inline Status OrgqrImpl(BufSizeFnT bufsize, SolverFnT solver, return Status::OK(); } -// Unfortunately the LAPACK function name differs for the real and complex case -// (complex ones are prefixed with "UN" for "unitary"), so we instantiate each -// one separately. -template <> -Status CudaSolver::Orgqr(int m, int n, int k, float* dev_a, int lda, - const float* dev_tau, int* dev_lapack_info) const { - return OrgqrImpl(DN_BUFSIZE_FN(orgqr, S), DN_SOLVER_FN(orgqr, S), context_, - cusolver_dn_handle_, m, n, k, dev_a, lda, dev_tau, - dev_lapack_info); +#define UNGQR_INSTANCE(Scalar, function_prefix, type_prefix) \ + template <> \ + Status CudaSolver::Ungqr(int m, int n, int k, Scalar* dev_a, int lda, \ + const Scalar* dev_tau, int* dev_lapack_info) \ + const { \ + return UngqrImpl(DN_BUFSIZE_FN(function_prefix##gqr, type_prefix), \ + DN_SOLVER_FN(function_prefix##gqr, type_prefix), \ + context_, cusolver_dn_handle_, m, n, k, dev_a, lda, \ + dev_tau, dev_lapack_info); \ + } + +UNGQR_INSTANCE(float, or, S); +UNGQR_INSTANCE(double, or, D); +UNGQR_INSTANCE(complex64, un, C); +UNGQR_INSTANCE(complex128, un, Z); + +template +static inline Status HeevdImpl(BufSizeFnT bufsize, SolverFnT solver, + OpKernelContext* context, + cusolverDnHandle_t cusolver_dn_handle, + cusolverEigMode_t jobz, cublasFillMode_t uplo, + int n, Scalar* dev_A, int lda, + typename Eigen::NumTraits::Real* dev_W, + int* dev_lapack_info) { + /* Get amount of workspace memory required. */ + int lwork; + TF_RETURN_IF_CUSOLVER_ERROR(bufsize(cusolver_dn_handle, jobz, uplo, n, + CUDAComplex(dev_A), lda, + CUDAComplex(dev_W), &lwork)); + /* Allocate device memory for workspace. */ + ScratchSpace dev_workspace(context, lwork, /* on_host */ false); + /* Launch the solver kernel. */ + TF_RETURN_IF_CUSOLVER_ERROR( + solver(cusolver_dn_handle, jobz, uplo, n, CUDAComplex(dev_A), lda, + CUDAComplex(dev_W), CUDAComplex(dev_workspace.mutable_data()), + lwork, dev_lapack_info)); + return Status::OK(); } -template <> -Status CudaSolver::Orgqr(int m, int n, int k, double* dev_a, int lda, - const double* dev_tau, int* dev_lapack_info) const { - return OrgqrImpl(DN_BUFSIZE_FN(orgqr, D), DN_SOLVER_FN(orgqr, D), context_, - cusolver_dn_handle_, m, n, k, dev_a, lda, dev_tau, - dev_lapack_info); -} -template <> -Status CudaSolver::Orgqr(int m, int n, int k, std::complex* dev_a, - int lda, const std::complex* dev_tau, - int* dev_lapack_info) const { - return OrgqrImpl(DN_BUFSIZE_FN(ungqr, C), DN_SOLVER_FN(ungqr, C), context_, - cusolver_dn_handle_, m, n, k, dev_a, lda, dev_tau, - dev_lapack_info); -} -template <> -Status CudaSolver::Orgqr(int m, int n, int k, std::complex* dev_a, - int lda, const std::complex* dev_tau, - int* dev_lapack_info) const { - return OrgqrImpl(DN_BUFSIZE_FN(ungqr, Z), DN_SOLVER_FN(ungqr, Z), context_, - cusolver_dn_handle_, m, n, k, dev_a, lda, dev_tau, - dev_lapack_info); + +#define HEEVD_INSTANCE(Scalar, function_prefix, type_prefix) \ + template <> \ + Status CudaSolver::Heevd(cusolverEigMode_t jobz, cublasFillMode_t uplo, \ + int n, Scalar* dev_A, int lda, \ + typename Eigen::NumTraits::Real* dev_W, \ + int* dev_lapack_info) const { \ + return HeevdImpl(DN_BUFSIZE_FN(function_prefix##evd, type_prefix), \ + DN_SOLVER_FN(function_prefix##evd, type_prefix), \ + context_, cusolver_dn_handle_, jobz, uplo, n, dev_A, lda, \ + dev_W, dev_lapack_info); \ + } + +HEEVD_INSTANCE(float, sy, S); +HEEVD_INSTANCE(double, sy, D); +HEEVD_INSTANCE(complex64, he, C); +HEEVD_INSTANCE(complex128, he, Z); + +template +static inline Status GesvdImpl(BufSizeFnT bufsize, SolverFnT solver, + OpKernelContext* context, + cusolverDnHandle_t cusolver_dn_handle, + signed char jobu, signed char jobvt, int m, + int n, Scalar* A, int lda, Scalar* S, Scalar* U, + int ldu, Scalar* VT, int ldvt, + int* dev_lapack_info) { + /* Get amount of workspace memory required. */ + int lwork; + TF_RETURN_IF_CUSOLVER_ERROR(bufsize(cusolver_dn_handle, m, n, &lwork)); + /* Allocate device memory for workspace. */ + ScratchSpace dev_workspace(context, lwork, /* on_host */ false); + /* Launch the solver kernel. */ + TF_RETURN_IF_CUSOLVER_ERROR(solver( + cusolver_dn_handle, jobu, jobvt, m, n, CUDAComplex(A), lda, S, + CUDAComplex(U), ldu, CUDAComplex(VT), ldvt, + CUDAComplex(dev_workspace.mutable_data()), lwork, NULL, dev_lapack_info)); + return Status::OK(); } +#define GESVD_INSTANCE(Scalar, type_prefix) \ + template <> \ + Status CudaSolver::Gesvd( \ + signed char jobu, signed char jobvt, int m, int n, Scalar* dev_A, \ + int lda, Scalar* dev_S, Scalar* dev_U, int ldu, Scalar* dev_VT, \ + int ldvt, int* dev_lapack_info) const { \ + return GesvdImpl(DN_BUFSIZE_FN(gesvd, type_prefix), \ + DN_SOLVER_FN(gesvd, type_prefix), context_, \ + cusolver_dn_handle_, jobu, jobvt, m, n, dev_A, lda, \ + dev_S, dev_U, ldu, dev_VT, ldvt, dev_lapack_info); \ + } + +TF_CALL_LAPACK_TYPES_NO_COMPLEX(GESVD_INSTANCE); + //============================================================================= // Wrappers of cuBlas computational methods begin here. // @@ -542,12 +540,12 @@ static inline Status GetrfBatchedImpl( return Status::OK(); } -#define GETRF_BATCHED_INSTANCE(Scalar, lapack_prefix) \ +#define GETRF_BATCHED_INSTANCE(Scalar, type_prefix) \ template <> \ Status CudaSolver::GetrfBatched( \ int n, const Scalar* host_a_dev_ptrs[], int lda, int* dev_pivots, \ DeviceLapackInfo* dev_lapack_info, int batch_size) const { \ - return GetrfBatchedImpl(BLAS_SOLVER_FN(getrfBatched, lapack_prefix), \ + return GetrfBatchedImpl(BLAS_SOLVER_FN(getrfBatched, type_prefix), \ context_, cublas_handle_, n, host_a_dev_ptrs, lda, \ dev_pivots, dev_lapack_info, batch_size); \ } @@ -580,14 +578,14 @@ static inline Status GetrsBatchedImpl( return Status::OK(); } -#define GETRS_BATCHED_INSTANCE(Scalar, lapack_prefix) \ +#define GETRS_BATCHED_INSTANCE(Scalar, type_prefix) \ template <> \ Status CudaSolver::GetrsBatched( \ cublasOperation_t trans, int n, int nrhs, \ const Scalar* host_a_dev_ptrs[], int lda, const int* dev_pivots, \ const Scalar* host_b_dev_ptrs[], int ldb, \ DeviceLapackInfo* dev_lapack_info, int batch_size) const { \ - return GetrsBatchedImpl(BLAS_SOLVER_FN(getrsBatched, lapack_prefix), \ + return GetrsBatchedImpl(BLAS_SOLVER_FN(getrsBatched, type_prefix), \ context_, cublas_handle_, trans, n, nrhs, \ host_a_dev_ptrs, lda, dev_pivots, host_b_dev_ptrs, \ ldb, dev_lapack_info, batch_size); \ @@ -619,13 +617,13 @@ static inline Status GetriBatchedImpl( return Status::OK(); } -#define GETRI_BATCHED_INSTANCE(Scalar, lapack_prefix) \ +#define GETRI_BATCHED_INSTANCE(Scalar, type_prefix) \ template <> \ Status CudaSolver::GetriBatched( \ int n, const Scalar* host_a_dev_ptrs[], int lda, const int* dev_pivots, \ const Scalar* host_a_inv_dev_ptrs[], int ldainv, \ DeviceLapackInfo* dev_lapack_info, int batch_size) const { \ - return GetriBatchedImpl(BLAS_SOLVER_FN(getriBatched, lapack_prefix), \ + return GetriBatchedImpl(BLAS_SOLVER_FN(getriBatched, type_prefix), \ context_, cublas_handle_, n, host_a_dev_ptrs, lda, \ dev_pivots, host_a_inv_dev_ptrs, ldainv, \ dev_lapack_info, batch_size); \ @@ -657,13 +655,13 @@ static inline Status MatInvBatchedImpl( return Status::OK(); } -#define MATINV_BATCHED_INSTANCE(Scalar, lapack_prefix) \ +#define MATINV_BATCHED_INSTANCE(Scalar, type_prefix) \ template <> \ Status CudaSolver::MatInvBatched( \ int n, const Scalar* host_a_dev_ptrs[], int lda, \ const Scalar* host_a_inv_dev_ptrs[], int ldainv, \ DeviceLapackInfo* dev_lapack_info, int batch_size) const { \ - return MatInvBatchedImpl(BLAS_SOLVER_FN(matinvBatched, lapack_prefix), \ + return MatInvBatchedImpl(BLAS_SOLVER_FN(matinvBatched, type_prefix), \ context_, cublas_handle_, n, host_a_dev_ptrs, \ lda, host_a_inv_dev_ptrs, ldainv, \ dev_lapack_info, batch_size); \ diff --git a/tensorflow/core/kernels/cuda_solvers.h b/tensorflow/core/kernels/cuda_solvers.h index f041923edb7..adb8de45027 100644 --- a/tensorflow/core/kernels/cuda_solvers.h +++ b/tensorflow/core/kernels/cuda_solvers.h @@ -242,32 +242,39 @@ class CudaSolver { Status Geqrf(int m, int n, Scalar* dev_A, int lda, Scalar* dev_tau, int* dev_lapack_info) const TF_MUST_USE_RESULT; - // Overwrite matrix C by product of C and Householder matrix Q. The - // Householder matrix Q is represented by the output from Geqrf in dev_a and - // dev_tau. + // Overwrite matrix C by product of C and the unitary Householder matrix Q. + // The Householder matrix Q is represented by the output from Geqrf in dev_a + // and dev_tau. // Notice: If Scalar is real, only trans=CUBLAS_OP_N or trans=CUBLAS_OP_T is // supported. If Scalar is complex, trans=CUBLAS_OP_N or trans=CUBLAS_OP_C is // supported. // Returns Status::OK() if the kernel was launched successfully. // See: http://docs.nvidia.com/cuda/cusolver/#cuds-lt-t-gt-ormqr template - Status Ormqr(cublasSideMode_t side, cublasOperation_t trans, int m, int n, + Status Unmqr(cublasSideMode_t side, cublasOperation_t trans, int m, int n, int k, const Scalar* dev_a, int lda, const Scalar* dev_tau, Scalar* dev_c, int ldc, int* dev_lapack_info) const TF_MUST_USE_RESULT; - // Overwrites QR factorization produced by Geqrf by Householder matrix Q. - // On input, the Householder matrix Q is represented by the output from Geqrf - // in dev_a and dev_tau. On output, dev_a is overwritten with the first n - // columns of Q. - // Requires m >= n >= 0. + // Overwrites QR factorization produced by Geqrf by the unitary Householder + // matrix Q. On input, the Householder matrix Q is represented by the output + // from Geqrf in dev_a and dev_tau. On output, dev_a is overwritten with the + // first n columns of Q. Requires m >= n >= 0. // Returns Status::OK() if the kernel was launched successfully. // See: http://docs.nvidia.com/cuda/cusolver/#cuds-lt-t-gt-orgqr template - Status Orgqr(int m, int n, int k, Scalar* dev_a, int lda, + Status Ungqr(int m, int n, int k, Scalar* dev_a, int lda, const Scalar* dev_tau, int* dev_lapack_info) const TF_MUST_USE_RESULT; + // Hermitian (Symmetric) Eigen decomposition. + // See: http://docs.nvidia.com/cuda/cusolver/#cuds-lt-t-gt-syevd + template + Status Heevd(cusolverEigMode_t jobz, cublasFillMode_t uplo, int n, + Scalar* dev_A, int lda, + typename Eigen::NumTraits::Real* dev_W, + int* dev_lapack_info) const TF_MUST_USE_RESULT; + // Singular value decomposition. // Returns Status::OK() if the kernel was launched successfully. // TODO(rmlarsen, volunteers): Add support for complex types. @@ -277,16 +284,6 @@ class CudaSolver { int lda, Scalar* dev_S, Scalar* dev_U, int ldu, Scalar* dev_VT, int ldvt, int* dev_lapack_info) const TF_MUST_USE_RESULT; - /* - TODO(rmlarsen, volunteers): Implement the kernels below. - - // Symmetric/Hermitian Eigen decomposition. - // See: http://docs.nvidia.com/cuda/cusolver/#cuds-lt-t-gt-syevd - template - Status Syevd(cusolverEigMode_t jobz, cublasFillMode_t uplo, int n, Scalar* - dev_A, int lda, Scalar* dev_W, int* dev_lapack_info) const TF_MUST_USE_RESULT; - */ - private: OpKernelContext* context_; // not owned. cudaStream_t cuda_stream_; diff --git a/tensorflow/core/kernels/one_hot_op.cc b/tensorflow/core/kernels/one_hot_op.cc index 79824b8fa0b..c66a812cd95 100644 --- a/tensorflow/core/kernels/one_hot_op.cc +++ b/tensorflow/core/kernels/one_hot_op.cc @@ -159,6 +159,8 @@ namespace functor { DECLARE_GPU_SPEC_INDEX(T, int64); TF_CALL_GPU_NUMBER_TYPES(DECLARE_GPU_SPEC); +TF_CALL_int32(DECLARE_GPU_SPEC); +TF_CALL_int64(DECLARE_GPU_SPEC); #undef DECLARE_GPU_SPEC_INDEX #undef DECLARE_GPU_SPEC @@ -180,6 +182,8 @@ TF_CALL_GPU_NUMBER_TYPES(DECLARE_GPU_SPEC); REGISTER_ONE_HOT_GPU_INDEX(type, int64); TF_CALL_GPU_NUMBER_TYPES(REGISTER_ONE_HOT_GPU); +TF_CALL_int32(REGISTER_ONE_HOT_GPU); +TF_CALL_int64(REGISTER_ONE_HOT_GPU); #undef REGISTER_ONE_HOT_GPU_INDEX #undef REGISTER_ONE_HOT_GPU diff --git a/tensorflow/core/kernels/one_hot_op_gpu.cu.cc b/tensorflow/core/kernels/one_hot_op_gpu.cu.cc index f5d9ca2de14..49fd4bdebad 100644 --- a/tensorflow/core/kernels/one_hot_op_gpu.cu.cc +++ b/tensorflow/core/kernels/one_hot_op_gpu.cu.cc @@ -37,6 +37,8 @@ typedef Eigen::GpuDevice GPUDevice; DEFINE_GPU_SPEC_INDEX(T, int64) TF_CALL_GPU_NUMBER_TYPES(DEFINE_GPU_SPEC); +TF_CALL_int32(DEFINE_GPU_SPEC); +TF_CALL_int64(DEFINE_GPU_SPEC); #undef DEFINE_GPU_SPEC_INDEX #undef DEFINE_GPU_SPEC diff --git a/tensorflow/core/kernels/qr_op_impl.h b/tensorflow/core/kernels/qr_op_impl.h index 431b083eefd..b9843428a51 100644 --- a/tensorflow/core/kernels/qr_op_impl.h +++ b/tensorflow/core/kernels/qr_op_impl.h @@ -248,12 +248,12 @@ class QrOpGpu : public AsyncOpKernel { auto q_reshaped = q->flat_inner_dims(); eye(device, q_reshaped); for (int batch = 0; batch < batch_size; ++batch) { - // Notice: It appears that Ormqr does not write a zero into *info upon + // Notice: It appears that Unmqr does not write a zero into *info upon // success (probably a bug), so we simply re-use the info array already // zeroed by Geqrf above. OP_REQUIRES_OK_ASYNC( context, - solver.Ormqr(CUBLAS_SIDE_LEFT, CublasAdjointOp(), m, m, + solver.Unmqr(CUBLAS_SIDE_LEFT, CublasAdjointOp(), m, m, min_size, &input_transposed_reshaped(batch, 0, 0), m, &tau_matrix(batch, 0), &q_reshaped(batch, 0, 0), m, dev_info.back().mutable_data() + batch), @@ -266,12 +266,12 @@ class QrOpGpu : public AsyncOpKernel { } } else { // Generate m x n matrix Q. In this case we can use the more efficient - // algorithm in Orgqr to generate Q in place. + // algorithm in Ungqr to generate Q in place. dev_info.emplace_back(context, batch_size, "orgqr"); for (int batch = 0; batch < batch_size; ++batch) { OP_REQUIRES_OK_ASYNC( context, - solver.Orgqr( + solver.Ungqr( m, n, min_size, &input_transposed_reshaped(batch, 0, 0), m, &tau_matrix(batch, 0), dev_info.back().mutable_data() + batch), done); diff --git a/tensorflow/core/kernels/self_adjoint_eig_v2_op_gpu.cc b/tensorflow/core/kernels/self_adjoint_eig_v2_op_gpu.cc new file mode 100644 index 00000000000..2b5f93069a9 --- /dev/null +++ b/tensorflow/core/kernels/self_adjoint_eig_v2_op_gpu.cc @@ -0,0 +1,194 @@ +/* Copyright 2017 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. +==============================================================================*/ + +// See docs in ../ops/linalg_ops.cc. + +#if GOOGLE_CUDA + +#include +#include + +#define EIGEN_USE_GPU +#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" +#include "tensorflow/core/framework/kernel_def_builder.h" +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/tensor_shape.h" +#include "tensorflow/core/kernels/cast_op.h" +#include "tensorflow/core/kernels/cuda_solvers.h" +#include "tensorflow/core/kernels/cwise_ops.h" +#include "tensorflow/core/kernels/transpose_functor.h" +#include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/platform/logging.h" +#include "tensorflow/core/platform/types.h" + +namespace tensorflow { + +typedef Eigen::GpuDevice GPUDevice; + +template +class SelfAdjointEigV2OpGpu : public AsyncOpKernel { + public: + explicit SelfAdjointEigV2OpGpu(OpKernelConstruction* context) + : AsyncOpKernel(context) { + OP_REQUIRES_OK(context, context->GetAttr("compute_v", &compute_v_)); + } + + void ComputeAsync(OpKernelContext* context, DoneCallback done) final { + const Tensor& input = context->input(0); + const int ndims = input.dims(); + OP_REQUIRES_ASYNC( + context, ndims >= 2, + errors::InvalidArgument("Input must have rank >= 2, got ", ndims), + done); + const int64 n = input.dim_size(ndims - 1); + OP_REQUIRES_ASYNC( + context, input.dim_size(ndims - 2) == n, + errors::InvalidArgument("Input matrices must be squares, got", + input.dim_size(ndims - 2), " != ", n), + done); + const int64 batch_size = + input.template flat_inner_dims().dimension(0); + + // Allocate outputs. + Tensor* eigenvalues; + TensorShape eigenvalues_shape = input.shape(); + eigenvalues_shape.RemoveLastDims(1); + OP_REQUIRES_OK_ASYNC( + context, context->allocate_output(0, eigenvalues_shape, &eigenvalues), + done); + Tensor* eigenvectors; + TensorShape eigenvectors_shape = + compute_v_ ? input.shape() : TensorShape({}); + OP_REQUIRES_OK_ASYNC( + context, context->allocate_output(1, eigenvectors_shape, &eigenvectors), + done); + + if (input.NumElements() == 0) { + done(); + return; + } + + // Allocate workspace. + Tensor eigenvalues_real; + using RealScalar = typename Eigen::NumTraits::Real; + if (std::is_same::value) { + eigenvalues_real = *eigenvalues; + } else { + OP_REQUIRES_OK_ASYNC( + context, + context->allocate_temp(DataTypeToEnum::value, + eigenvalues_shape, &eigenvalues_real), + done); + } + + Tensor input_copy; + OP_REQUIRES_OK_ASYNC( + context, + context->forward_input_or_allocate_temp( + {0}, DataTypeToEnum::value, input.shape(), &input_copy), + done); + // For real symmetric matrices, row-major and column-major are the same. For + // complex Hermitian, row-major and column-major differ by a conjugation, + // which is still cheaper than a transpose. + const GPUDevice& device = context->eigen_device(); + if (!input.SharesBufferWith(input_copy)) { + if (Eigen::NumTraits::IsComplex) { + functor::UnaryFunctor> conj; + conj(device, input_copy.flat() /*out*/, + input.flat() /*in*/); + } else { + device.memcpy(input_copy.flat().data(), + input.flat().data(), + input.NumElements() * sizeof(Scalar)); + } + } else if (Eigen::NumTraits::IsComplex) { + functor::UnaryFunctor> conj; + conj(device, const_cast(&input)->flat() /*out*/, + input.flat() /*in*/); + } + + // Compute eigen decomposition in-place in input_copy. + CudaSolver solver(context); + std::vector dev_info; + dev_info.emplace_back(context, batch_size, "heevd"); + auto input_copy_reshaped = input_copy.flat_inner_dims(); + auto eigenvalues_real_reshaped = + eigenvalues_real.flat_inner_dims(); + for (int batch = 0; batch < batch_size; ++batch) { + OP_REQUIRES_OK_ASYNC(context, + solver.Heevd(compute_v_ ? CUSOLVER_EIG_MODE_VECTOR + : CUSOLVER_EIG_MODE_NOVECTOR, + CUBLAS_FILL_MODE_UPPER, n, + &input_copy_reshaped(batch, 0, 0), n, + &eigenvalues_real_reshaped(batch, 0), + dev_info.back().mutable_data() + batch), + done); + } + + if (!std::is_same::value) { + functor::CastFunctor cast; + cast(device, eigenvalues->flat(), + const_cast(&eigenvalues_real)->flat()); + } + + if (compute_v_) { + // Transpose eigenvectors now stored in input_copy in column-major form to + // output in row-major form. + std::vector perm(ndims); + std::iota(perm.begin(), perm.end(), 0); + std::swap(perm[ndims - 2], perm[ndims - 1]); + OP_REQUIRES_OK_ASYNC( + context, DoTranspose(device, input_copy, perm, eigenvectors), done); + } + + // Asynchronously check return status from cuSolver kernels. + TensorReference input_copy_ref(input_copy); + TensorReference eigenvalues_real_ref(eigenvalues_real); + auto info_checker = [context, dev_info, input_copy_ref, + eigenvalues_real_ref, + done](const Status& status, + const std::vector& host_infos) { + input_copy_ref.Unref(); + eigenvalues_real_ref.Unref(); + OP_REQUIRES_OK_ASYNC(context, status, done); + done(); + }; + OP_REQUIRES_OK_ASYNC( + context, + solver.CopyLapackInfoToHostAsync(dev_info, std::move(info_checker)), + done); + } + + private: + bool compute_v_; + + TF_DISALLOW_COPY_AND_ASSIGN(SelfAdjointEigV2OpGpu); +}; + +#define REGISTER(Scalar) \ + REGISTER_KERNEL_BUILDER( \ + Name("SelfAdjointEigV2").Device(DEVICE_GPU).TypeConstraint("T"), \ + (SelfAdjointEigV2OpGpu)) + +REGISTER(float); +REGISTER(double); +REGISTER(complex64); +REGISTER(complex128); + +#undef REGISTER + +} // namespace tensorflow + +#endif // GOOGLE_CUDA diff --git a/tensorflow/core/lib/gtl/flatmap.h b/tensorflow/core/lib/gtl/flatmap.h index e92083fecfc..6dd67ad2ea5 100644 --- a/tensorflow/core/lib/gtl/flatmap.h +++ b/tensorflow/core/lib/gtl/flatmap.h @@ -146,8 +146,8 @@ class FlatMap { friend class FlatMap; Bucket* b_; Bucket* end_; + char space_ alignas(value_type)[sizeof(value_type)]; uint32 i_; - char space_[sizeof(value_type)]; pointer val() { return reinterpret_cast(space_); } void FillValue() { new (space_) value_type(b_->key(i_), b_->val(i_)); } diff --git a/tensorflow/core/platform/file_system.cc b/tensorflow/core/platform/file_system.cc index 2abda457145..938f5af487a 100644 --- a/tensorflow/core/platform/file_system.cc +++ b/tensorflow/core/platform/file_system.cc @@ -56,6 +56,9 @@ void ForEach(int first, int last, const std::function& f) { FileSystem::~FileSystem() {} string FileSystem::TranslateName(const string& name) const { + // If the name is empty, CleanPath returns "." which is incorrect and + // we should return the empty path instead. + if (name.empty()) return name; return io::CleanPath(name); } diff --git a/tensorflow/core/util/ctc/BUILD b/tensorflow/core/util/ctc/BUILD index 45107d95cd3..1521349e4dd 100644 --- a/tensorflow/core/util/ctc/BUILD +++ b/tensorflow/core/util/ctc/BUILD @@ -62,7 +62,6 @@ cc_library( ], deps = [ ":ctc_loss_util_lib", - "//tensorflow/core:gpu_runtime", "//tensorflow/core:lib", "//tensorflow/core:lib_internal", "//third_party/eigen3", diff --git a/tensorflow/docs_src/get_started/summaries_and_tensorboard.md b/tensorflow/docs_src/get_started/summaries_and_tensorboard.md index ece8fbf43c3..ce5db079ba3 100644 --- a/tensorflow/docs_src/get_started/summaries_and_tensorboard.md +++ b/tensorflow/docs_src/get_started/summaries_and_tensorboard.md @@ -198,7 +198,7 @@ You're now all set to visualize this data using TensorBoard. ## Launching TensorBoard To run TensorBoard, use the following command (alternatively `python -m -tensorflow.tensorboard`) +tensorboard.main`) ```bash tensorboard --logdir=path/to/log-directory diff --git a/tensorflow/python/BUILD b/tensorflow/python/BUILD index af5cf68bfec..497588f2ed3 100644 --- a/tensorflow/python/BUILD +++ b/tensorflow/python/BUILD @@ -50,7 +50,6 @@ py_library( "//tensorflow/tools/quantization:__pkg__", # TODO(b/34059704): remove when fixed ], deps = [ - ":tf_optimizer", ":array_ops", ":bitwise_ops", ":check_ops", @@ -63,15 +62,20 @@ py_library( ":framework_for_generated_wrappers", ":functional_ops", ":gradient_checker", + ":graph_util", ":histogram_ops", ":image_ops", ":initializers_ns", ":io_ops", + ":layers", ":lib", ":linalg_ns", ":math_ops", + ":metrics", ":nn", + ":ops", ":platform", + ":pywrap_tensorflow", ":script_ops", ":session_ops", ":sets", @@ -81,24 +85,24 @@ py_library( ":state_ops", ":string_ops", ":summary", - ":metrics", - ":layers", ":tensor_array_ops", ":training", - ":ops", ":saver_test_utils", ":subscribe", ":test_ops", # TODO: Break testing code out into separate rule. + ":tf_optimizer", ":util", ":weights_broadcast_ops", "//third_party/py/numpy", + "//tensorflow/core:protos_all_py", + "//tensorflow/python/data", "//tensorflow/python/estimator:estimator_py", "//tensorflow/python/feature_column:feature_column_py", + "//tensorflow/python/keras", "//tensorflow/python/ops/losses", "//tensorflow/python/ops/distributions", "//tensorflow/python/profiler", "//tensorflow/python/saved_model", - "//tensorflow/python/keras", ] + if_not_windows([ "//tensorflow/contrib:contrib_py", ]), diff --git a/tensorflow/python/data/BUILD b/tensorflow/python/data/BUILD new file mode 100644 index 00000000000..6465593207a --- /dev/null +++ b/tensorflow/python/data/BUILD @@ -0,0 +1,27 @@ +package(default_visibility = ["//tensorflow:internal"]) + +licenses(["notice"]) # Apache 2.0 + +exports_files(["LICENSE"]) + +py_library( + name = "data", + srcs = ["__init__.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:util", + "//tensorflow/python/data/ops:dataset_ops", + ], +) + +filegroup( + name = "all_files", + srcs = glob( + ["**/*"], + exclude = [ + "**/METADATA", + "**/OWNERS", + ], + ), + visibility = ["//tensorflow:__subpackages__"], +) diff --git a/tensorflow/python/data/__init__.py b/tensorflow/python/data/__init__.py new file mode 100644 index 00000000000..a741b73ad38 --- /dev/null +++ b/tensorflow/python/data/__init__.py @@ -0,0 +1,37 @@ +# Copyright 2017 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. +# ============================================================================== +"""`tf.data.Dataset` API for input pipelines. + +@@Dataset +@@Iterator +@@TFRecordDataset +@@FixedLengthRecordDataset +@@TextLineDataset +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +# pylint: disable=unused-import +from tensorflow.python.data.ops.dataset_ops import Dataset +from tensorflow.python.data.ops.dataset_ops import FixedLengthRecordDataset +from tensorflow.python.data.ops.dataset_ops import Iterator +from tensorflow.python.data.ops.dataset_ops import TextLineDataset +from tensorflow.python.data.ops.dataset_ops import TFRecordDataset +# pylint: enable=unused-import + +from tensorflow.python.util.all_util import remove_undocumented +remove_undocumented(__name__) diff --git a/tensorflow/python/data/ops/BUILD b/tensorflow/python/data/ops/BUILD new file mode 100644 index 00000000000..81c800db961 --- /dev/null +++ b/tensorflow/python/data/ops/BUILD @@ -0,0 +1,38 @@ +package(default_visibility = ["//tensorflow:internal"]) + +licenses(["notice"]) # Apache 2.0 + +exports_files(["LICENSE"]) + +py_library( + name = "dataset_ops", + srcs = ["dataset_ops.py"], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:constant_op", + "//tensorflow/python:dataset_ops_gen", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:function", + "//tensorflow/python:math_ops", + "//tensorflow/python:random_seed", + "//tensorflow/python:script_ops", + "//tensorflow/python:sparse_tensor", + "//tensorflow/python:tensor_shape", + "//tensorflow/python:tensor_util", + "//tensorflow/python/data/util:nest", + "//third_party/py/numpy", + ], +) + +filegroup( + name = "all_files", + srcs = glob( + ["**/*"], + exclude = [ + "**/METADATA", + "**/OWNERS", + ], + ), + visibility = ["//tensorflow:__subpackages__"], +) diff --git a/tensorflow/python/data/ops/dataset_ops.py b/tensorflow/python/data/ops/dataset_ops.py new file mode 100644 index 00000000000..818a0036e05 --- /dev/null +++ b/tensorflow/python/data/ops/dataset_ops.py @@ -0,0 +1,2032 @@ +# Copyright 2017 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. +# ============================================================================== +"""Python wrappers for Datasets and Iterators.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import abc +import collections +import threading + +import numpy as np + +from tensorflow.python.data.util import nest +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import function +from tensorflow.python.framework import ops +from tensorflow.python.framework import random_seed +from tensorflow.python.framework import sparse_tensor as sparse_tensor_lib +from tensorflow.python.framework import tensor_shape +from tensorflow.python.framework import tensor_util +from tensorflow.python.ops import gen_dataset_ops +from tensorflow.python.ops import gen_io_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import script_ops + + +class Iterator(object): + """Represents the state of iterating through a `Dataset`.""" + + def __init__(self, iterator_resource, initializer, output_types, + output_shapes): + """Creates a new iterator from the given iterator resource. + + NOTE(mrry): Most users will not call this initializer directly, and will + instead use `Iterator.from_dataset()` or `Dataset.make_one_shot_iterator()`. + + Args: + iterator_resource: A `tf.resource` scalar `tf.Tensor` representing the + iterator. + initializer: A `tf.Operation` that should be run to initialize this + iterator. + output_types: A nested structure of `tf.DType` objects corresponding to + each component of an element of this iterator. + output_shapes: A nested structure of `tf.TensorShape` objects + corresponding to each component of an element of this dataset. + """ + self._iterator_resource = iterator_resource + self._initializer = initializer + self._output_types = output_types + self._output_shapes = output_shapes + + @staticmethod + def from_dataset(dataset, shared_name=None): + """Creates a new, uninitialized `Iterator` from the given `Dataset`. + + To initialize this iterator, you must run its `initializer`: + + ```python + dataset = ... + iterator = Iterator.from_dataset(dataset) + # ... + sess.run(iterator.initializer) + ``` + + Args: + dataset: A `Dataset` object. + shared_name: (Optional.) If non-empty, this iterator will be shared under + the given name across multiple sessions that share the same devices + (e.g. when using a remote server). + + Returns: + An `Iterator`. + """ + if shared_name is None: + shared_name = "" + iterator_resource = gen_dataset_ops.iterator( + container="", + shared_name=shared_name, + output_types=nest.flatten(dataset.output_types), + output_shapes=nest.flatten(dataset.output_shapes)) + with ops.colocate_with(iterator_resource): + initializer = gen_dataset_ops.make_iterator( + dataset.make_dataset_resource(), iterator_resource) + return Iterator(iterator_resource, initializer, dataset.output_types, + dataset.output_shapes) + + @staticmethod + def from_structure(output_types, output_shapes=None, shared_name=None): + """Creates a new, uninitialized `Iterator` with the given structure. + + This iterator-constructing method can be used to create an iterator that + is reusable with many different datasets. + + The returned iterator is not bound to a particular dataset, and it has + no `initializer`. To initialize the iterator, run the operation returned by + `Iterator.make_initializer(dataset)`. + + The following is an example + + ```python + iterator = Iterator.from_structure(tf.int64, tf.TensorShape([])) + + dataset_range = Dataset.range(10) + range_initializer = iterator.make_initializer(dataset_range) + + dataset_evens = dataset_range.filter(lambda x: x % 2 == 0) + evens_initializer = iterator.make_initializer(dataset_evens) + + # Define a model based on the iterator; in this example, the model_fn + # is expected to take scalar tf.int64 Tensors as input (see + # the definition of 'iterator' above). + prediction, loss = model_fn(iterator.get_next()) + + # Train for `num_epochs`, where for each epoch, we first iterate over + # dataset_range, and then iterate over dataset_evens. + for _ in range(num_epochs): + # Initialize the iterator to `dataset_range` + sess.run(range_initializer) + while True: + try: + pred, loss_val = sess.run([prediction, loss]) + except tf.errors.OutOfRangeError: + break + + # Initialize the iterator to `dataset_evens` + sess.run(evens_initializer) + while True: + try: + pred, loss_val = sess.run([prediction, loss]) + except tf.errors.OutOfRangeError: + break + ``` + + Args: + output_types: A nested structure of `tf.DType` objects corresponding to + each component of an element of this iterator. + output_shapes: (Optional.) A nested structure of `tf.TensorShape` objects + corresponding to each component of an element of this dataset. If + omitted, each component will have an unconstrainted shape. + shared_name: (Optional.) If non-empty, this iterator will be shared under + the given name across multiple sessions that share the same devices + (e.g. when using a remote server). + + Returns: + An `Iterator`. + + Raises: + TypeError: If the structures of `output_shapes` and `output_types` are + not the same. + """ + output_types = nest.map_structure(dtypes.as_dtype, output_types) + if output_shapes is None: + output_shapes = nest.map_structure( + lambda _: tensor_shape.TensorShape(None), output_types) + else: + output_shapes = nest.map_structure_up_to( + output_types, tensor_shape.as_shape, output_shapes) + nest.assert_same_structure(output_types, output_shapes) + if shared_name is None: + shared_name = "" + iterator_resource = gen_dataset_ops.iterator( + container="", + shared_name=shared_name, + output_types=nest.flatten(output_types), + output_shapes=nest.flatten(output_shapes)) + return Iterator(iterator_resource, None, output_types, output_shapes) + + @staticmethod + def from_string_handle(string_handle, output_types, output_shapes=None): + """Creates a new, uninitialized `Iterator` based on the given handle. + + This method allows you to define a "feedable" iterator where you can choose + between concrete iterators by feeding a value in a @{tf.Session.run} call. + In that case, `string_handle` would a @{tf.placeholder}, and you would feed + it with the value of @{tf.contrib.data.Iterator.string_handle} in each step. + + For example, if you had two iterators that marked the current position in + a training dataset and a test dataset, you could choose which to use in + each step as follows: + + ```python + train_iterator = tf.contrib.data.Dataset(...).make_one_shot_iterator() + train_iterator_handle = sess.run(train_iterator.string_handle()) + + test_iterator = tf.contrib.data.Dataset(...).make_one_shot_iterator() + test_iterator_handle = sess.run(test_iterator.string_handle()) + + handle = tf.placeholder(tf.string, shape=[]) + iterator = tf.contrib.data.Iterator.from_string_handle( + handle, train_iterator.output_types) + + next_element = iterator.get_next() + loss = f(next_element) + + train_loss = sess.run(loss, feed_dict={handle: train_iterator_handle}) + test_loss = sess.run(loss, feed_dict={handle: test_iterator_handle}) + ``` + + Args: + string_handle: A scalar `tf.Tensor` of type `tf.string` that evaluates + to a handle produced by the `Iterator.string_handle()` method. + output_types: A nested structure of `tf.DType` objects corresponding to + each component of an element of this iterator. + output_shapes: (Optional.) A nested structure of `tf.TensorShape` objects + corresponding to each component of an element of this dataset. If + omitted, each component will have an unconstrainted shape. + + Returns: + An `Iterator`. + """ + output_types = nest.map_structure(dtypes.as_dtype, output_types) + if output_shapes is None: + output_shapes = nest.map_structure( + lambda _: tensor_shape.TensorShape(None), output_types) + else: + output_shapes = nest.map_structure_up_to( + output_types, tensor_shape.as_shape, output_shapes) + nest.assert_same_structure(output_types, output_shapes) + string_handle = ops.convert_to_tensor(string_handle, dtype=dtypes.string) + iterator_resource = gen_dataset_ops.iterator_from_string_handle( + string_handle, + output_types=nest.flatten(output_types), + output_shapes=nest.flatten(output_shapes)) + return Iterator(iterator_resource, None, output_types, output_shapes) + + @property + def initializer(self): + """A `tf.Operation` that should be run to initialize this iterator. + + Returns: + A `tf.Operation` that should be run to initialize this iterator + + Raises: + ValueError: If this iterator initializes itself automatically. + """ + if self._initializer is not None: + return self._initializer + else: + # TODO(mrry): Consider whether one-shot iterators should have + # initializers that simply reset their state to the beginning. + raise ValueError("Iterator does not have an initializer.") + + def make_initializer(self, dataset, name=None): + """Returns a `tf.Operation` that initializes this iterator on `dataset`. + + Args: + dataset: A `Dataset` with compatible structure to this iterator. + name: (Optional.) A name for the created operation. + + Returns: + A `tf.Operation` that can be run to initialize this iterator on the given + `dataset`. + + Raises: + TypeError: If `dataset` and this iterator do not have a compatible + element structure. + """ + with ops.name_scope(name, "make_initializer") as name: + nest.assert_same_structure(self._output_types, dataset.output_types) + nest.assert_same_structure(self._output_shapes, dataset.output_shapes) + for iterator_dtype, dataset_dtype in zip( + nest.flatten(self._output_types), nest.flatten(dataset.output_types)): + if iterator_dtype != dataset_dtype: + raise TypeError( + "Expected output types %r but got dataset with output types %r." % + (self._output_types, dataset.output_types)) + for iterator_shape, dataset_shape in zip( + nest.flatten(self._output_shapes), + nest.flatten(dataset.output_shapes)): + if not iterator_shape.is_compatible_with(dataset_shape): + raise TypeError("Expected output shapes compatible with %r but got " + "dataset with output shapes %r." % + (self._output_shapes, dataset.output_shapes)) + with ops.colocate_with(self._iterator_resource): + return gen_dataset_ops.make_iterator( + dataset.make_dataset_resource(), self._iterator_resource, name=name) + + def get_next(self, name=None): + """Returns a nested structure of `tf.Tensor`s containing the next element. + + Args: + name: (Optional.) A name for the created operation. + + Returns: + A nested structure of `tf.Tensor` objects. + """ + return nest.pack_sequence_as( + self._output_types, + gen_dataset_ops.iterator_get_next( + self._iterator_resource, + output_types=nest.flatten(self._output_types), + output_shapes=nest.flatten(self._output_shapes), + name=name)) + + def dispose_op(self, name=None): + """Returns a `tf.Operation` that destroys this iterator. + + The returned operation may be used to release any resources consumed by + this iterator without closing the session. + + Args: + name: (Optional.) A name for the created operation. + + Returns: + A `tf.Operation`. + """ + return gen_dataset_ops.iterator_dispose(self._iterator_resource, name=name) + + def string_handle(self, name=None): + """Returns a string-valued `tf.Tensor` that represents this iterator. + + Args: + name: (Optional.) A name for the created operation. + + Returns: + A scalar `tf.Tensor` of type `tf.string`. + """ + return gen_dataset_ops.iterator_to_string_handle( + self._iterator_resource, name=name) + + @property + def output_shapes(self): + """Returns the shape of each component of an element of this iterator. + + Returns: + A nested structure of `tf.TensorShape` objects corresponding to each + component of an element of this iterator. + """ + return self._output_shapes + + @property + def output_types(self): + """Returns the type of each component of an element of this iterator. + + Returns: + A nested structure of `tf.DType` objects corresponding to each component + of an element of this iterator. + """ + return self._output_types + + +class Dataset(object): + """Represents a potentially large set of elements. + + A `Dataset` can be used to represent an input pipeline as a + collection of elements (nested structures of tensors) and a "logical + plan" of transformations that act on those elements. + """ + __metaclass__ = abc.ABCMeta + + def __init__(self): + pass + + # TODO(mrry): Rename this to `make_dataset_variant()`, + # `make_dataset_tensor()`, or something else more accurate. + @abc.abstractmethod + def make_dataset_resource(self): + """Creates a scalar `tf.Tensor` of `tf.variant` representing this dataset. + + Returns: + A scalar `tf.Tensor` of `tf.variant` type, which represents this dataset. + """ + raise NotImplementedError("Dataset.make_dataset_resource") + + def make_initializable_iterator(self, shared_name=None): + """Creates an `Iterator` for enumerating the elements of this dataset. + + **N.B.** The returned iterator will be in an uninitialized state, + and you must run the `iterator.initializer` operation before using it. + + Args: + shared_name: (Optional.) If non-empty, this iterator will be shared under + the given name across multiple sessions that share the same devices + (e.g. when using a remote server). + + + Returns: + An `Iterator` over the elements of this dataset. + """ + return Iterator.from_dataset(self, shared_name) + + def make_one_shot_iterator(self): + """Creates an `Iterator` for enumerating the elements of this dataset. + + **N.B.** The returned iterator will be initialized automatically. + A "one-shot" iterator does not currently support re-initialization. + + Returns: + An `Iterator` over the elements of this dataset. + """ + # NOTE(mrry): We capture by value here to ensure that `_make_dataset()` is + # a 0-argument function. + @function.Defun(capture_by_value=True) + def _make_dataset(): + return self.make_dataset_resource() + + _make_dataset.add_to_graph(ops.get_default_graph()) + + return Iterator( + gen_dataset_ops.one_shot_iterator( + dataset_factory=_make_dataset, + output_types=nest.flatten(self.output_types), + output_shapes=nest.flatten(self.output_shapes)), None, + self.output_types, self.output_shapes) + + @abc.abstractproperty + def output_shapes(self): + """Returns the shape of each component of an element of this dataset. + + Returns: + A nested structure of `tf.TensorShape` objects corresponding to each + component of an element of this dataset. + """ + raise NotImplementedError("Dataset.output_shapes") + + @abc.abstractproperty + def output_types(self): + """Returns the type of each component of an element of this dataset. + + Returns: + A nested structure of `tf.DType` objects corresponding to each component + of an element of this dataset. + """ + raise NotImplementedError("Dataset.output_types") + + def __repr__(self): + output_shapes = nest.map_structure(str, self.output_shapes) + output_shapes = str(output_shapes).replace("'", "") + output_types = nest.map_structure(repr, self.output_types) + output_types = str(output_types).replace("'", "") + return ("<%s shapes: %s, types: %s>" % (type(self).__name__, output_shapes, + output_types)) + + @staticmethod + def from_tensors(tensors): + """Creates a `Dataset` with a single element, comprising the given tensors. + + Args: + tensors: A nested structure of tensors. + + Returns: + A `Dataset`. + """ + return TensorDataset(tensors) + + @staticmethod + def from_tensor_slices(tensors): + """Creates a `Dataset` whose elements are slices of the given tensors. + + Args: + tensors: A nested structure of tensors, each having the same size in the + 0th dimension. + + Returns: + A `Dataset`. + """ + return TensorSliceDataset(tensors) + + @staticmethod + def from_sparse_tensor_slices(sparse_tensor): + """Splits each rank-N `tf.SparseTensor` in this dataset row-wise. + + Args: + sparse_tensor: A `tf.SparseTensor`. + + Returns: + A `Dataset` of rank-(N-1) sparse tensors. + """ + return SparseTensorSliceDataset(sparse_tensor) + + class _GeneratorState(object): + """Stores outstanding iterators created from a Python generator. + + This class keeps track of potentially multiple iterators that may have + been created from a generator, e.g. in the case that the dataset is + repeated, or nested within a parallel computation. + """ + + def __init__(self, generator): + self._generator = generator + self._lock = threading.Lock() + self._next_id = 0 # GUARDED_BY(self._lock) + self._iterators = collections.defaultdict(lambda: iter(generator())) + + def get_next_id(self): + with self._lock: + ret = self._next_id + self._next_id += 1 + return ret + + def get_iterator(self, iterator_id): + return self._iterators[iterator_id] + + def iterator_completed(self, iterator_id): + del self._iterators[iterator_id] + + @staticmethod + def from_generator(generator, output_types, output_shapes=None): + """Creates a `Dataset` whose elements are generated by `generator`. + + The `generator` argument must be a callable object that returns + an object that support the `iter()` protocol (e.g. a generator function). + The elements generated by `generator` must be compatible with the given + `output_types` and (optional) `output_shapes` arguments. + + For example: + + ```python + import itertools + + def gen(): + for i in itertools.count(1): + yield (i, [1] * i) + + ds = Dataset.from_generator( + gen, (tf.int64, tf.int64), (tf.TensorShape([]), tf.TensorShape([None]))) + value = ds.make_one_shot_iterator().get_next() + + sess.run(value) # (1, array([1])) + sess.run(value) # (2, array([1, 1])) + ``` + + Args: + generator: A callable object that takes no arguments and returns an + object that supports the `iter()` protocol. + output_types: A nested structure of `tf.DType` objects corresponding to + each component of an element yielded by `generator`. + output_shapes: (Optional.) A nested structure of `tf.TensorShape` + objects corresponding to each component of an element yielded by + `generator`. + + Returns: + A `Dataset`. + """ + if not callable(generator): + raise TypeError("`generator` must be callable.") + if output_shapes is None: + output_shapes = nest.map_structure( + lambda _: tensor_shape.TensorShape(None), output_types) + else: + output_shapes = nest.map_structure_up_to( + output_types, tensor_shape.as_shape, output_shapes) + + flattened_types = nest.flatten(output_types) + flattened_shapes = nest.flatten(output_shapes) + + generator_state = Dataset._GeneratorState(generator) + + def get_iterator_id_map_fn(unused_dummy): + """Creates a unique `iterator_id` for each pass over the dataset. + + The "iterator_id" disambiguates between multiple concurrently + existing iterators. + + Args: + unused_dummy: Ignored value. + + Returns: + A `tf.int64` tensor whose value uniquely identifies an iterator in + `generator_state`. + """ + return script_ops.py_func( + generator_state.get_next_id, [], dtypes.int64, stateful=True) + + def generator_map_fn(iterator_id_t): + """Generates the next element from iterator with ID `iterator_id_t`. + + We map this function across an infinite repetition of the + `iterator_id_t`, and raise `StopIteration` to terminate the iteration. + + Args: + iterator_id_t: A `tf.int64` tensor whose value uniquely identifies + the iterator in `generator_state` from which to generate an element. + + Returns: + A nested structure of tensors representing an element from the iterator. + """ + + def generator_py_func(iterator_id): + """A `py_func` that will be called to invoke the iterator.""" + try: + values = next(generator_state.get_iterator(iterator_id)) + except StopIteration: + generator_state.iterator_completed(iterator_id) + raise StopIteration("Iteration finished.") + + # Use the same _convert function from the py_func() implementation to + # convert the returned values to arrays early, so that we can inspect + # their values. + # pylint: disable=protected-access + ret_arrays = [ + script_ops.FuncRegistry._convert(ret) + for ret in nest.flatten_up_to(output_types, values) + ] + # pylint: enable=protected-access + + # Additional type and shape checking to ensure that the components + # of the generated element match the `output_types` and `output_shapes` + # arguments. + for (ret_array, expected_dtype, expected_shape) in zip( + ret_arrays, flattened_types, flattened_shapes): + if ret_array.dtype != expected_dtype.as_numpy_dtype: + raise TypeError( + "`generator` yielded an element of type %s where an element " + "of type %s was expected." % (ret_array.dtype, + expected_dtype.as_numpy_dtype)) + if not expected_shape.is_compatible_with(ret_array.shape): + raise ValueError( + "`generator` yielded an element of shape %s where an element " + "of shape %s was expected." % (ret_array.shape, expected_shape)) + + return ret_arrays + + flat_values = script_ops.py_func( + generator_py_func, [iterator_id_t], flattened_types, stateful=True) + + # The `py_func()` op drops the inferred shapes, so we add them back in + # here. + if output_shapes is not None: + for ret_t, shape in zip(flat_values, flattened_shapes): + ret_t.set_shape(shape) + + return nest.pack_sequence_as(output_types, flat_values) + + # This function associates each traversal of `generator` with a unique + # iterator ID. + def flat_map_fn(iterator_id_t): + # First, generate an infinite dataset containing the iterator ID repeated + # forever. + repeated_id = Dataset.from_tensors(iterator_id_t).repeat(None) + + # The `generator_map_fn` gets the next element from the iterator with the + # relevant ID, and raises StopIteration when that iterator contains no + # more elements. + return repeated_id.map(generator_map_fn) + + # A single-element dataset that, each time it is evaluated, contains a + # freshly-generated and unique (for the returned dataset) int64 + # ID that will be used to identify the appropriate Python state, which + # is encapsulated in `generator_state`, and captured in + # `get_iterator_id_map_fn`. + dummy = 0 + id_dataset = Dataset.from_tensors(dummy).map(get_iterator_id_map_fn) + + # A dataset that contains all of the elements generated by a + # single iterator created from `generator`, identified by the + # iterator ID contained in `id_dataset`. Lifting the iteration + # into a flat_map here enables multiple repetitions and/or nested + # versions of the returned dataset to be created, because it forces + # the generation of a new ID for each version. + return id_dataset.flat_map(flat_map_fn) + + @staticmethod + def range(*args): + """Creates a `Dataset` of a step-separated range of values. + + For example: + + ```python + Dataset.range(5) == [0, 1, 2, 3, 4] + Dataset.range(2, 5) == [2, 3, 4] + Dataset.range(1, 5, 2) == [1, 3] + Dataset.range(1, 5, -2) == [] + Dataset.range(5, 1) == [] + Dataset.range(5, 1, -2) == [5, 3] + ``` + + Args: + *args: follow same semantics as python's xrange. + len(args) == 1 -> start = 0, stop = args[0], step = 1 + len(args) == 2 -> start = args[0], stop = args[1], step = 1 + len(args) == 3 -> start = args[0], stop = args[1, stop = args[2] + + Returns: + A `RangeDataset`. + + Raises: + ValueError: if len(args) == 0. + """ + return RangeDataset(*args) + + @staticmethod + def zip(datasets): + """Creates a `Dataset` by zipping together the given datasets. + + This method has similar semantics to the built-in `zip()` function + in Python, with the main difference being that the `datasets` + argument can be an arbitrary nested structure of `Dataset` objects. + For example: + + ```python + # NOTE: The following examples use `{ ... }` to represent the + # contents of a dataset. + a = { 1, 2, 3 } + b = { 4, 5, 6 } + c = { (7, 8), (9, 10), (11, 12) } + d = { 13, 14 } + + # The nested structure of the `datasets` argument determines the + # structure of elements in the resulting dataset. + Dataset.zip((a, b)) == { (1, 4), (2, 5), (3, 6) } + Dataset.zip((b, a)) == { (4, 1), (5, 2), (6, 3) } + + # The `datasets` argument may contain an arbitrary number of + # datasets. + Dataset.zip((a, b, c)) == { (1, 4, (7, 8)), + (2, 5, (9, 10)), + (3, 6, (11, 12)) } + + # The number of elements in the resulting dataset is the same as + # the size of the smallest dataset in `datasets`. + Dataset.zip((a, d)) == { (1, 13), (2, 14) } + ``` + + Args: + datasets: A nested structure of datasets. + + Returns: + A `Dataset`. + """ + return ZipDataset(datasets) + + def concatenate(self, dataset): + """Creates a `Dataset` by concatenating given dataset with this dataset. + + ```python + # NOTE: The following examples use `{ ... }` to represent the + # contents of a dataset. + a = { 1, 2, 3 } + b = { 4, 5, 6, 7 } + + # Input dataset and dataset to be concatenated should have same + # nested structures and output types. + # c = { (8, 9), (10, 11), (12, 13) } + # d = { 14.0, 15.0, 16.0 } + # a.concatenate(c) and a.concatenate(d) would result in error. + + a.concatenate(b) == { 1, 2, 3, 4, 5, 6, 7 } + ``` + + Args: + dataset: `Dataset` to be concatenated. + + Returns: + A `Dataset`. + """ + return ConcatenateDataset(self, dataset) + + def prefetch(self, buffer_size): + """Creates a `Dataset` that prefetches elements from this dataset. + + Args: + buffer_size: A `tf.int64` scalar `tf.Tensor`, representing the + maximum number elements that will be buffered when prefetching. + + Returns: + A `Dataset`. + """ + return PrefetchDataset(self, buffer_size) + + @staticmethod + def list_files(file_pattern): + """A dataset of all files matching a pattern. + + Example: + If we had the following files on our filesystem: + - /path/to/dir/a.txt + - /path/to/dir/b.py + - /path/to/dir/c.py + If we pass "/path/to/dir/*.py" as the directory, the dataset would + produce: + - /path/to/dir/b.py + - /path/to/dir/c.py + + Args: + file_pattern: A string or scalar string `tf.Tensor`, representing + the filename pattern that will be matched. + + Returns: + A `Dataset` of strings corresponding to file names. + """ + return Dataset.from_tensor_slices(gen_io_ops.matching_files(file_pattern)) + + def repeat(self, count=None): + """Repeats this dataset `count` times. + + Args: + count: (Optional.) A `tf.int64` scalar `tf.Tensor`, representing the + number of times the elements of this dataset should be repeated. The + default behavior (if `count` is `None` or `-1`) is for the elements to + be repeated indefinitely. + + Returns: + A `Dataset`. + """ + return RepeatDataset(self, count) + + def _enumerate(self, start=0): + + max_value = np.iinfo(dtypes.int64.as_numpy_dtype).max + return Dataset.zip((Dataset.range(start, max_value), self)) + + def shuffle(self, buffer_size, seed=None): + """Randomly shuffles the elements of this dataset. + + Args: + buffer_size: A `tf.int64` scalar `tf.Tensor`, representing the + number of elements from this dataset from which the new + dataset will sample. + seed: (Optional.) A `tf.int64` scalar `tf.Tensor`, representing the + random seed that will be used to create the distribution. See + @{tf.set_random_seed} for behavior. + + Returns: + A `Dataset`. + """ + return ShuffleDataset(self, buffer_size, seed) + + def cache(self, filename=""): + """Caches the elements in this dataset. + + Args: + filename: A `tf.string` scalar `tf.Tensor`, representing the name of a + directory on the filesystem to use for caching tensors in this Dataset. + If a filename is not provided, the dataset will be cached in memory. + + Returns: + A `Dataset`. + """ + return CacheDataset(self, filename) + + def take(self, count): + """Creates a `Dataset` with at most `count` elements from this dataset. + + Args: + count: A `tf.int64` scalar `tf.Tensor`, representing the number of + elements of this dataset that should be taken to form the new dataset. + If `count` is -1, or if `count` is greater than the size of this + dataset, the new dataset will contain all elements of this dataset. + + Returns: + A `Dataset`. + """ + return TakeDataset(self, count) + + def skip(self, count): + """Creates a `Dataset` that skips `count` elements from this dataset. + + Args: + count: A `tf.int64` scalar `tf.Tensor`, representing the number + of elements of this dataset that should be skipped to form the + new dataset. If `count` is greater than the size of this + dataset, the new dataset will contain no elements. If `count` + is -1, skips the entire dataset. + + Returns: + A `Dataset`. + """ + return SkipDataset(self, count) + + def shard(self, num_shards, index): + """Creates a `Dataset` that includes only 1/`num_shards` of this dataset. + + This dataset operator is very useful when running distributed training, as + it allows each worker to read a unique subset. + + When reading a single input file, you can skip elements as follows: + + ```python + d = tf.data.TFRecordDataset(FLAGS.input_file) + d = d.shard(FLAGS.num_workers, FLAGS.worker_index) + d = d.repeat(FLAGS.num_epochs) + d = d.shuffle(FLAGS.shuffle_buffer_size) + d = d.map(parser_fn, num_parallel_calls=FLAGS.num_map_threads) + ``` + + Important caveats: + + - Be sure to shard before you use any randomizing operator (such as + shuffle). + - Generally it is best if the shard operator is used early in the dataset + pipeline. For example, when reading from a set of TFRecord files, shard + before converting the dataset to input samples. This avoids reading every + file on every worker. The following is an example of an efficient + sharding strategy within a complete pipeline: + + ```python + d = Dataset.list_files(FLAGS.pattern) + d = d.shard(FLAGS.num_workers, FLAGS.worker_index) + d = d.repeat(FLAGS.num_epochs) + d = d.shuffle(FLAGS.shuffle_buffer_size) + d = d.repeat() + d = d.interleave(tf.data.TFRecordDataset, + cycle_length=FLAGS.num_readers, block_length=1) + d = d.map(parser_fn, num_parallel_calls=FLAGS.num_map_threads) + ``` + + Args: + num_shards: A `tf.int64` scalar `tf.Tensor`, representing the number of + shards operating in parallel. + index: A `tf.int64` scalar `tf.Tensor`, representing the worker index. + + Returns: + A `Dataset`. + + Raises: + ValueError: if `num_shards` or `index` are illegal values. Note: error + checking is done on a best-effort basis, and aren't guaranteed to be + caught upon dataset creation. (e.g. providing in a placeholder tensor + bypasses the early checking, and will instead result in an error during + a session.run call.) + """ + num_shards = ops.convert_to_tensor( + num_shards, name="num_shards", dtype=dtypes.int64) + num_shards_static = tensor_util.constant_value(num_shards) + index = ops.convert_to_tensor(index, name="index", dtype=dtypes.int64) + index_static = tensor_util.constant_value(index) + + if num_shards_static is not None and num_shards_static < 1: + raise ValueError("num_shards must be >= 1; got: %s" % num_shards_static) + if index_static is not None and index_static < 0: + raise ValueError("index must be >= 0; got: %s" % index_static) + if (index_static is not None and num_shards_static is not None and + index_static >= num_shards_static): + raise ValueError("index must be <= num_shards; %s is not < %s" % + (index_static, num_shards_static)) + + def filter_fn(elem_index, _): + mod_result = math_ops.mod(elem_index, num_shards) + return math_ops.equal(mod_result, index) + + return self._enumerate().filter(filter_fn).map(lambda _, elem: elem) + + def batch(self, batch_size): + """Combines consecutive elements of this dataset into batches. + + Args: + batch_size: A `tf.int64` scalar `tf.Tensor`, representing the number of + consecutive elements of this dataset to combine in a single batch. + + Returns: + A `Dataset`. + """ + return BatchDataset(self, batch_size) + + def padded_batch(self, batch_size, padded_shapes, padding_values=None): + """Combines consecutive elements of this dataset into padded batches. + + Like `Dataset.dense_to_sparse_batch()`, this method combines + multiple consecutive elements of this dataset, which might have + different shapes, into a single element. The tensors in the + resulting element have an additional outer dimension, and are + padded to the respective shape in `padded_shapes`. + + Args: + batch_size: A `tf.int64` scalar `tf.Tensor`, representing the number of + consecutive elements of this dataset to combine in a single batch. + padded_shapes: A nested structure of `tf.TensorShape` or + `tf.int64` vector tensor-like objects representing the shape + to which the respective component of each input element should + be padded prior to batching. Any unknown dimensions + (e.g. `tf.Dimension(None)` in a `tf.TensorShape` or `-1` in a + tensor-like object) will be padded to the maximum size of that + dimension in each batch. + padding_values: (Optional.) A nested structure of scalar-shaped + `tf.Tensor`, representing the padding values to use for the + respective components. Defaults are `0` for numeric types and + the empty string for string types. + + Returns: + A `Dataset`. + """ + return PaddedBatchDataset(self, batch_size, padded_shapes, padding_values) + + def map(self, + map_func, + num_threads=None, + output_buffer_size=None, + num_parallel_calls=None): + """Maps `map_func` across this datset. + + Args: + map_func: A function mapping a nested structure of tensors (having + shapes and types defined by `self.output_shapes` and + `self.output_types`) to another nested structure of tensors. + num_threads: (Optional.) Deprecated, use `num_parallel_calls` instead. + output_buffer_size: (Optional.) A `tf.int64` scalar `tf.Tensor`, + representing the maximum number of processed elements that will be + buffered. + num_parallel_calls: (Optional.) A `tf.int32` scalar `tf.Tensor`, + representing the number elements to process in parallel. If not + specified, elements will be processed sequentially. + + Returns: + A `Dataset`. + """ + if num_threads is None and num_parallel_calls is None: + ret = MapDataset(self, map_func) + else: + if num_threads is None: + ret = ParallelMapDataset(self, map_func, num_parallel_calls) + else: + ret = ParallelMapDataset(self, map_func, num_threads) + if output_buffer_size is not None: + ret = ret.prefetch(output_buffer_size) + return ret + + def flat_map(self, map_func): + """Maps `map_func` across this dataset and flattens the result. + + Args: + map_func: A function mapping a nested structure of tensors (having shapes + and types defined by `self.output_shapes` and `self.output_types`) to a + `Dataset`. + + Returns: + A `Dataset`. + """ + return FlatMapDataset(self, map_func) + + def interleave(self, map_func, cycle_length, block_length=1): + """Maps `map_func` across this dataset, and interleaves the results. + + For example, you can use `Dataset.interleave()` to process many input files + concurrently: + + ```python + # Preprocess 4 files concurrently, and interleave blocks of 16 records from + # each file. + filenames = ["/var/data/file1.txt", "/var/data/file2.txt", ..."] + dataset = (Dataset.from_tensor_slices(filenames) + .interleave(lambda x: + TextLineDataset(x).map(parse_fn, num_parallel_calls=1), + cycle_length=4, block_length=16)) + ``` + + The `cycle_length` and `block_length` arguments control the order in which + elements are produced. `cycle_length` controls the number of input elements + that are processed concurrently. If you set `cycle_length` to 1, this + transformation will handle one input element at a time, and will produce + identical results = to @{tf.data.Dataset.flat_map}. In general, + this transformation will apply `map_func` to `cycle_length` input elements, + open iterators on the returned `Dataset` objects, and cycle through them + producing `block_length` consecutive elements from each iterator, and + consuming the next input element each time it reaches the end of an + iterator. + + For example: + + ```python + # NOTE: The following examples use `{ ... }` to represent the + # contents of a dataset. + a = { 1, 2, 3, 4, 5 } + + # NOTE: New lines indicate "block" boundaries. + a.interleave(lambda x: Dataset.from_tensors(x).repeat(6), + cycle_length=2, block_length=4) == { + 1, 1, 1, 1, + 2, 2, 2, 2, + 1, 1, + 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, + 3, 3, + 4, 4, + 5, 5, 5, 5, + 5, 5, + } + ``` + + NOTE: The order of elements yielded by this transformation is + deterministic, as long as `map_func` is a pure function. If + `map_func` contains any stateful operations, the order in which + that state is accessed is undefined. + + Args: + map_func: A function mapping a nested structure of tensors (having shapes + and types defined by `self.output_shapes` and `self.output_types`) to a + `Dataset`. + cycle_length: The number of elements from this dataset that will be + processed concurrently. + block_length: The number of consecutive elements to produce from each + input element before cycling to another input element. + + Returns: + A `Dataset`. + """ + return InterleaveDataset(self, map_func, cycle_length, block_length) + + def filter(self, predicate): + """Filters this dataset according to `predicate`. + + Args: + predicate: A function mapping a nested structure of tensors (having shapes + and types defined by `self.output_shapes` and `self.output_types`) to a + scalar `tf.bool` tensor. + + Returns: + A `Dataset`. + """ + return FilterDataset(self, predicate) + + def apply(self, transformation_func): + """Apply a transformation function to this dataset. + + `apply` enables chaining of custom `Dataset` transformations, which are + represented as functions that take one `Dataset` argument and return a + transformed `Dataset`. + + For example: + + ``` + dataset = (dataset.map(lambda x: x ** 2) + .apply(group_by_window(key_func, reduce_func, window_size)) + .map(lambda x: x ** 3)) + ``` + + Args: + transformation_func: A function that takes one `Dataset` argument and + returns a `Dataset`. + + Returns: + The `Dataset` returned by applying `transformation_func` to this dataset. + """ + dataset = transformation_func(self) + if not isinstance(dataset, Dataset): + raise TypeError("`transformation_func` must return a Dataset.") + return dataset + + +class TensorDataset(Dataset): + """A `Dataset` with a single element, viz. a nested structure of tensors.""" + + def __init__(self, tensors): + """See `Dataset.from_tensors()` for details.""" + super(TensorDataset, self).__init__() + with ops.name_scope("tensors"): + self._tensors = nest.pack_sequence_as(tensors, [ + ops.convert_to_tensor(t, name="component_%d" % i) + for i, t in enumerate(nest.flatten(tensors)) + ]) + + def make_dataset_resource(self): + return gen_dataset_ops.tensor_dataset( + nest.flatten(self._tensors), + output_shapes=nest.flatten(self.output_shapes)) + + @property + def output_shapes(self): + return nest.pack_sequence_as(self._tensors, + [t.shape for t in nest.flatten(self._tensors)]) + + @property + def output_types(self): + return nest.pack_sequence_as(self._tensors, + [t.dtype for t in nest.flatten(self._tensors)]) + + +class TensorSliceDataset(Dataset): + """A `Dataset` of slices from a nested structure of tensors.""" + + def __init__(self, tensors): + """See `Dataset.from_tensor_slices()` for details.""" + super(TensorSliceDataset, self).__init__() + with ops.name_scope("tensors"): + flat_tensors = [ + ops.convert_to_tensor(t, name="component_%d" % i) + for i, t in enumerate(nest.flatten(tensors)) + ] + + self._tensors = nest.pack_sequence_as(tensors, flat_tensors) + batch_dim = flat_tensors[0].get_shape()[0] + for t in flat_tensors[1:]: + batch_dim.assert_is_compatible_with(t.get_shape()[0]) + + def make_dataset_resource(self): + return gen_dataset_ops.tensor_slice_dataset( + nest.flatten(self._tensors), + output_shapes=nest.flatten(self.output_shapes)) + + @property + def output_shapes(self): + return nest.pack_sequence_as(self._tensors, [ + tensor_shape.TensorShape(t.shape[1:]) + for t in nest.flatten(self._tensors) + ]) + + @property + def output_types(self): + return nest.pack_sequence_as(self._tensors, + [t.dtype for t in nest.flatten(self._tensors)]) + + +class SparseTensorSliceDataset(Dataset): + """A `Dataset` that splits a rank-N `tf.SparseTensor` into its rows.""" + + def __init__(self, sparse_tensor): + """See `Dataset.from_sparse_tensor_slices()` for details.""" + super(SparseTensorSliceDataset, self).__init__() + if not isinstance(sparse_tensor, sparse_tensor_lib.SparseTensor): + raise TypeError("`sparse_tensor` must be a `tf.SparseTensor` object.") + self._sparse_tensor = sparse_tensor + + def make_dataset_resource(self): + return gen_dataset_ops.sparse_tensor_slice_dataset( + self._sparse_tensor.indices, self._sparse_tensor.values, + self._sparse_tensor.dense_shape) + + @property + def output_shapes(self): + indices_shape = self._sparse_tensor.indices.get_shape() + shape_shape = self._sparse_tensor.dense_shape.get_shape() + rank = (indices_shape[1] - 1).merge_with(shape_shape[0] - 1) + num_values = tensor_shape.Dimension(None) + return (tensor_shape.TensorShape([num_values, rank]), + tensor_shape.TensorShape([num_values]), tensor_shape.TensorShape( + [rank])) + + @property + def output_types(self): + return (dtypes.int64, self._sparse_tensor.dtype, dtypes.int64) + + +class ZipDataset(Dataset): + """A `Dataset` that zips its inputs together.""" + + def __init__(self, datasets): + """See `Dataset.zip()` for details.""" + super(ZipDataset, self).__init__() + self._datasets = datasets + + def make_dataset_resource(self): + return gen_dataset_ops.zip_dataset( + [ds.make_dataset_resource() for ds in nest.flatten(self._datasets)], + output_shapes=[ + s + for ds in nest.flatten(self._datasets) + for s in nest.flatten(ds.output_shapes) + ], + output_types=[ + t + for ds in nest.flatten(self._datasets) + for t in nest.flatten(ds.output_types) + ]) + + @property + def output_shapes(self): + return nest.pack_sequence_as(self._datasets, [ + ds.output_shapes for ds in nest.flatten(self._datasets) + ]) + + @property + def output_types(self): + return nest.pack_sequence_as(self._datasets, [ + ds.output_types for ds in nest.flatten(self._datasets) + ]) + + +class ConcatenateDataset(Dataset): + """A `Dataset` that concatenates its input with given dataset.""" + + def __init__(self, input_dataset, dataset_to_concatenate): + """See `Dataset.concatenate()` for details.""" + super(ConcatenateDataset, self).__init__() + self._input_dataset = input_dataset + self._dataset_to_concatenate = dataset_to_concatenate + nest.assert_same_structure(input_dataset.output_types, + dataset_to_concatenate.output_types) + for a, b in zip( + nest.flatten(input_dataset.output_types), + nest.flatten(dataset_to_concatenate.output_types)): + if a != b: + raise TypeError( + "Two datasets to concatenate have different types %s and %s" % + (input_dataset.output_types, dataset_to_concatenate.output_types)) + + def make_dataset_resource(self): + return gen_dataset_ops.concatenate_dataset( + self._input_dataset.make_dataset_resource(), + self._dataset_to_concatenate.make_dataset_resource(), + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + return nest.pack_sequence_as(self._input_dataset.output_shapes, [ + ts1.most_specific_compatible_shape(ts2) + for (ts1, ts2) in zip( + nest.flatten(self._input_dataset.output_shapes), + nest.flatten(self._dataset_to_concatenate.output_shapes)) + ]) + + @property + def output_types(self): + return self._input_dataset.output_types + + +class RepeatDataset(Dataset): + """A `Dataset` that repeats its input several times.""" + + def __init__(self, input_dataset, count): + """See `Dataset.repeat()` for details.""" + super(RepeatDataset, self).__init__() + self._input_dataset = input_dataset + if count is None: + self._count = constant_op.constant(-1, dtype=dtypes.int64, name="count") + else: + self._count = ops.convert_to_tensor( + count, dtype=dtypes.int64, name="count") + + def make_dataset_resource(self): + return gen_dataset_ops.repeat_dataset( + self._input_dataset.make_dataset_resource(), + count=self._count, + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + return self._input_dataset.output_shapes + + @property + def output_types(self): + return self._input_dataset.output_types + + +class RangeDataset(Dataset): + """A `Dataset` of a step separated range of values.""" + + def __init__(self, *args): + """See `Dataset.range()` for details.""" + super(RangeDataset, self).__init__() + self._parse_args(*args) + + def _parse_args(self, *args): + if len(args) == 1: + self._start = self._build_tensor(0, "start") + self._stop = args[0] + self._step = self._build_tensor(1, "step") + elif len(args) == 2: + self._start = args[0] + self._stop = args[1] + self._step = self._build_tensor(1, "step") + elif len(args) == 3: + self._start = args[0] + self._stop = args[1] + self._step = args[2] + else: + raise ValueError("Invalid arguments to RangeDataset: %s" % str(args)) + + def _build_tensor(self, int64_value, name): + return constant_op.constant(int64_value, dtype=dtypes.int64, name=name) + + def make_dataset_resource(self): + return gen_dataset_ops.range_dataset( + start=self._start, + stop=self._stop, + step=self._step, + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + return tensor_shape.scalar() + + @property + def output_types(self): + return dtypes.int64 + + +class CacheDataset(Dataset): + """A `Dataset` that caches elements of its input.""" + + def __init__(self, input_dataset, filename): + """See `Dataset.cache()` for details.""" + super(CacheDataset, self).__init__() + self._input_dataset = input_dataset + self._filename = ops.convert_to_tensor( + filename, dtype=dtypes.string, name="filename") + + def make_dataset_resource(self): + return gen_dataset_ops.cache_dataset( + self._input_dataset.make_dataset_resource(), + filename=self._filename, + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + return self._input_dataset.output_shapes + + @property + def output_types(self): + return self._input_dataset.output_types + + +class ShuffleDataset(Dataset): + """A `Dataset` that randomly shuffles the elements of its input.""" + + def __init__(self, input_dataset, buffer_size, seed=None): + """See `Dataset.shuffle()` for details.""" + super(ShuffleDataset, self).__init__() + self._input_dataset = input_dataset + self._buffer_size = ops.convert_to_tensor( + buffer_size, dtype=dtypes.int64, name="buffer_size") + seed, seed2 = random_seed.get_seed(seed) + if seed is None: + self._seed = constant_op.constant(0, dtype=dtypes.int64, name="seed") + else: + self._seed = ops.convert_to_tensor(seed, dtype=dtypes.int64, name="seed") + if seed2 is None: + self._seed2 = constant_op.constant(0, dtype=dtypes.int64, name="seed2") + else: + self._seed2 = ops.convert_to_tensor( + seed2, dtype=dtypes.int64, name="seed2") + + def make_dataset_resource(self): + return gen_dataset_ops.shuffle_dataset( + self._input_dataset.make_dataset_resource(), + buffer_size=self._buffer_size, + seed=self._seed, + seed2=self._seed2, + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + return self._input_dataset.output_shapes + + @property + def output_types(self): + return self._input_dataset.output_types + + +class TakeDataset(Dataset): + """A `Dataset` containing the first `count` elements from its input.""" + + def __init__(self, input_dataset, count): + """See `Dataset.take()` for details.""" + super(TakeDataset, self).__init__() + self._input_dataset = input_dataset + self._count = ops.convert_to_tensor(count, dtype=dtypes.int64, name="count") + + def make_dataset_resource(self): + return gen_dataset_ops.take_dataset( + self._input_dataset.make_dataset_resource(), + count=self._count, + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + return self._input_dataset.output_shapes + + @property + def output_types(self): + return self._input_dataset.output_types + + +class SkipDataset(Dataset): + """A `Dataset` skipping the first `count` elements from its input.""" + + def __init__(self, input_dataset, count): + """See `Dataset.skip()` for details.""" + super(SkipDataset, self).__init__() + self._input_dataset = input_dataset + self._count = ops.convert_to_tensor(count, dtype=dtypes.int64, name="count") + + def make_dataset_resource(self): + return gen_dataset_ops.skip_dataset( + self._input_dataset.make_dataset_resource(), + count=self._count, + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + return self._input_dataset.output_shapes + + @property + def output_types(self): + return self._input_dataset.output_types + + +class BatchDataset(Dataset): + """A `Dataset` that batches contiguous elements from its input.""" + + def __init__(self, input_dataset, batch_size): + """See `Dataset.batch()` for details.""" + super(BatchDataset, self).__init__() + self._input_dataset = input_dataset + self._batch_size = batch_size + + def make_dataset_resource(self): + return gen_dataset_ops.batch_dataset( + self._input_dataset.make_dataset_resource(), + batch_size=self._batch_size, + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + input_shapes = self._input_dataset.output_shapes + return nest.pack_sequence_as(input_shapes, [ + tensor_shape.vector(None).concatenate(s) + for s in nest.flatten(self._input_dataset.output_shapes) + ]) + + @property + def output_types(self): + return self._input_dataset.output_types + + +def _partial_shape_to_tensor(shape_like): + try: + # First attempt to convert the input to a shape, and return the + # "canonical" tensor representation, which uses `-1` in place of + # `None`. + shape_like = tensor_shape.as_shape(shape_like) + return ops.convert_to_tensor( + [dim if dim is not None else -1 for dim in shape_like.as_list()], + dtype=dtypes.int64) + except (TypeError, ValueError): + # The argument was not trivially convertible to a + # `tf.TensorShape`, so fall back on the conversion to tensor + # machinery. + return ops.convert_to_tensor(shape_like, dtype=dtypes.int64) + + +def _padding_value_to_tensor(value, output_type): + """Converts the padding value to a tensor. + + Args: + value: The padding value. + output_type: Its expected dtype. + + Returns: + A scalar `Tensor`. + + Raises: + ValueError: if the padding value is not a scalar. + TypeError: if the padding value's type does not match `output_type`. + """ + value = ops.convert_to_tensor(value, name="padding_value") + if not value.shape.is_compatible_with(tensor_shape.scalar()): + raise ValueError("Padding value should be a scalar, but is not: %s" % value) + if value.dtype != output_type: + raise TypeError("Padding value tensor (%s) does not match output type: %s" % + (value, output_type)) + return value + + +class PaddedBatchDataset(Dataset): + """A `Dataset` that batches and pads contiguous elements from its input.""" + + def __init__(self, input_dataset, batch_size, padded_shapes, padding_values): + """See `Dataset.batch()` for details.""" + super(PaddedBatchDataset, self).__init__() + self._input_dataset = input_dataset + self._batch_size = batch_size + padding_values = (padding_values if padding_values is not None else + self._default_padding(input_dataset)) + self._padded_shapes = nest.map_structure_up_to( + input_dataset.output_shapes, _partial_shape_to_tensor, padded_shapes) + self._padding_values = nest.map_structure_up_to( + input_dataset.output_shapes, _padding_value_to_tensor, padding_values, + input_dataset.output_types) + + def _default_padding(self, input_dataset): + + def make_zero(t): + if t.base_dtype == dtypes.string: + return "" + else: + return np.zeros_like(t.as_numpy_dtype()) + + return nest.map_structure(make_zero, input_dataset.output_types) + + def make_dataset_resource(self): + return gen_dataset_ops.padded_batch_dataset( + self._input_dataset.make_dataset_resource(), + batch_size=self._batch_size, + padded_shapes=[ + ops.convert_to_tensor(s, dtype=dtypes.int64) + for s in nest.flatten(self._padded_shapes) + ], + padding_values=nest.flatten(self._padding_values), + output_shapes=nest.flatten(self.output_shapes)) + + @property + def output_shapes(self): + + def _padded_shape_to_batch_shape(s): + return tensor_shape.vector(None).concatenate( + tensor_util.constant_value_as_shape(s)) + + return nest.map_structure(_padded_shape_to_batch_shape, self._padded_shapes) + + @property + def output_types(self): + return self._input_dataset.output_types + + +def _should_unpack_args(args): + """Returns `True` if `args` should be `*args` when passed to a callable.""" + return type(args) is tuple # pylint: disable=unidiomatic-typecheck + + +class MapDataset(Dataset): + """A `Dataset` that maps a function over elements in its input.""" + + def __init__(self, input_dataset, map_func): + """See `Dataset.map()` for details.""" + super(MapDataset, self).__init__() + self._input_dataset = input_dataset + + self._output_shapes = None + self._output_types = None + + @function.Defun(*nest.flatten(input_dataset.output_types)) + def tf_map_func(*args): + """A wrapper for Defun that facilitates shape inference.""" + # Pass in shape information from the input_dataset. + for arg, shape in zip(args, nest.flatten(input_dataset.output_shapes)): + arg.set_shape(shape) + + nested_args = nest.pack_sequence_as(input_dataset.output_types, args) + + if _should_unpack_args(nested_args): + ret = map_func(*nested_args) + else: + ret = map_func(nested_args) + + # If `map_func` returns a list of tensors, `nest.flatten()` and + # `ops.convert_to_tensor()` would conspire to attempt to stack + # those tensors into a single tensor, because the customized + # version of `nest.flatten()` does not recurse into lists. Since + # it is more likely that the list arose from returning the + # result of an operation (such as `tf.py_func()`) that returns a + # list of not-necessarily-stackable tensors, we treat the + # returned value is a `tuple` instead. A user wishing to pack + # the return value into a single tensor can use an explicit + # `tf.stack()` before returning. + if isinstance(ret, list): + ret = tuple(ret) + + # Extract shape information from the returned values. + flattened_ret = [ops.convert_to_tensor(t) for t in nest.flatten(ret)] + self._output_shapes = nest.pack_sequence_as( + ret, [t.get_shape() for t in flattened_ret]) + self._output_types = nest.pack_sequence_as( + ret, [t.dtype for t in flattened_ret]) + + return flattened_ret + + self._map_func = tf_map_func + self._map_func.add_to_graph(ops.get_default_graph()) + + def make_dataset_resource(self): + input_resource = self._input_dataset.make_dataset_resource() + return gen_dataset_ops.map_dataset( + input_resource, + self._map_func.captured_inputs, + f=self._map_func, + output_types=nest.flatten(self.output_types), + output_shapes=nest.flatten(self.output_shapes)) + + @property + def output_shapes(self): + return self._output_shapes + + @property + def output_types(self): + return self._output_types + + +class ParallelMapDataset(MapDataset): + """A `Dataset` that maps a function over elements in its input in parallel.""" + + def __init__(self, input_dataset, map_func, num_parallel_calls): + """See `Dataset.map()` for details.""" + super(ParallelMapDataset, self).__init__(input_dataset, map_func) + + self._num_parallel_calls = ops.convert_to_tensor( + num_parallel_calls, dtype=dtypes.int32, name="num_parallel_calls") + + def make_dataset_resource(self): + input_resource = self._input_dataset.make_dataset_resource() + # pylint: disable=protected-access + return gen_dataset_ops.parallel_map_dataset( + input_resource, + self._map_func.captured_inputs, + f=self._map_func, + num_parallel_calls=self._num_parallel_calls, + output_types=nest.flatten(self.output_types), + output_shapes=nest.flatten(self.output_shapes)) + # pylint: enable=protected-access + + +class FlatMapDataset(Dataset): + """A `Dataset` that maps a function over its input and flattens the result.""" + + def __init__(self, input_dataset, map_func): + """See `Dataset.flat_map()` for details.""" + super(FlatMapDataset, self).__init__() + self._input_dataset = input_dataset + + @function.Defun(*nest.flatten(input_dataset.output_types)) + def tf_map_func(*args): + """A wrapper for Defun that facilitates shape inference.""" + # Pass in shape information from the input_dataset. + for arg, shape in zip(args, nest.flatten(input_dataset.output_shapes)): + arg.set_shape(shape) + + nested_args = nest.pack_sequence_as(input_dataset.output_types, args) + + if _should_unpack_args(nested_args): + dataset = map_func(*nested_args) + else: + dataset = map_func(nested_args) + + if not isinstance(dataset, Dataset): + raise TypeError("`map_func` must return a `Dataset` object.") + + self._output_types = dataset.output_types + self._output_shapes = dataset.output_shapes + + return dataset.make_dataset_resource() + + self._map_func = tf_map_func + self._map_func.add_to_graph(ops.get_default_graph()) + + def make_dataset_resource(self): + return gen_dataset_ops.flat_map_dataset( + self._input_dataset.make_dataset_resource(), + self._map_func.captured_inputs, + f=self._map_func, + output_types=nest.flatten(self.output_types), + output_shapes=nest.flatten(self.output_shapes)) + + @property + def output_shapes(self): + return self._output_shapes + + @property + def output_types(self): + return self._output_types + + +class InterleaveDataset(Dataset): + """A `Dataset` that maps a function over its input and interleaves the result. + """ + + def __init__(self, input_dataset, map_func, cycle_length, block_length): + """See `Dataset.interleave()` for details.""" + super(InterleaveDataset, self).__init__() + self._input_dataset = input_dataset + + @function.Defun(*nest.flatten(input_dataset.output_types)) + def tf_map_func(*args): + """A wrapper for Defun that facilitates shape inference.""" + # Pass in shape information from the input_dataset. + for arg, shape in zip(args, nest.flatten(input_dataset.output_shapes)): + arg.set_shape(shape) + + nested_args = nest.pack_sequence_as(input_dataset.output_types, args) + + if _should_unpack_args(nested_args): + dataset = map_func(*nested_args) + else: + dataset = map_func(nested_args) + + if not isinstance(dataset, Dataset): + raise TypeError("`map_func` must return a `Dataset` object.") + + self._output_types = dataset.output_types + self._output_shapes = dataset.output_shapes + + return dataset.make_dataset_resource() + + self._map_func = tf_map_func + self._map_func.add_to_graph(ops.get_default_graph()) + + self._cycle_length = ops.convert_to_tensor(cycle_length, dtype=dtypes.int64) + self._block_length = ops.convert_to_tensor(block_length, dtype=dtypes.int64) + + def make_dataset_resource(self): + return gen_dataset_ops.interleave_dataset( + self._input_dataset.make_dataset_resource(), + self._map_func.captured_inputs, + self._cycle_length, + self._block_length, + f=self._map_func, + output_types=nest.flatten(self.output_types), + output_shapes=nest.flatten(self.output_shapes)) + + @property + def output_shapes(self): + return self._output_shapes + + @property + def output_types(self): + return self._output_types + + +class FilterDataset(Dataset): + """A `Dataset` that filters its input according to a predicate function.""" + + def __init__(self, input_dataset, predicate): + """See `Dataset.filter()` for details.""" + super(FilterDataset, self).__init__() + self._input_dataset = input_dataset + + @function.Defun(*nest.flatten(input_dataset.output_types)) + def tf_predicate(*args): + """A wrapper for Defun that facilitates shape inference.""" + # Pass in shape information from the input_dataset. + for arg, shape in zip(args, nest.flatten(input_dataset.output_shapes)): + arg.set_shape(shape) + + nested_args = nest.pack_sequence_as(input_dataset.output_types, args) + + if _should_unpack_args(nested_args): + ret = predicate(*nested_args) + else: + ret = predicate(nested_args) + + ret = ops.convert_to_tensor(ret, dtype=dtypes.bool) + if not (ret.dtype == dtypes.bool and + ret.shape.is_compatible_with(tensor_shape.scalar())): + raise ValueError("`predicate` must return a scalar boolean tensor.") + + return ret + + self._predicate = tf_predicate + self._predicate.add_to_graph(ops.get_default_graph()) + + def make_dataset_resource(self): + return gen_dataset_ops.filter_dataset( + self._input_dataset.make_dataset_resource(), + other_arguments=self._predicate.captured_inputs, + predicate=self._predicate, + output_types=nest.flatten(self.output_types), + output_shapes=nest.flatten(self.output_shapes)) + + @property + def output_shapes(self): + return self._input_dataset.output_shapes + + @property + def output_types(self): + return self._input_dataset.output_types + + +class PrefetchDataset(Dataset): + """A `Dataset` that asynchronously prefetches its input.""" + + def __init__(self, input_dataset, buffer_size): + """See `Dataset.prefetch()` for details.""" + super(PrefetchDataset, self).__init__() + self._input_dataset = input_dataset + self._buffer_size = ops.convert_to_tensor(buffer_size, dtype=dtypes.int64) + + def make_dataset_resource(self): + return gen_dataset_ops.prefetch_dataset( + self._input_dataset.make_dataset_resource(), + buffer_size=self._buffer_size, + output_shapes=nest.flatten(self.output_shapes), + output_types=nest.flatten(self.output_types)) + + @property + def output_shapes(self): + return self._input_dataset.output_shapes + + @property + def output_types(self): + return self._input_dataset.output_types + + +# TODO(b/64974358): Increase default buffer size to 256 MB. +_DEFAULT_READER_BUFFER_SIZE_BYTES = 256 * 1024 # 256 KB + + +def _convert_optional_param_to_tensor(argument_name, + argument_value, + argument_default=0, + argument_dtype=dtypes.int64): + if argument_value is not None: + return ops.convert_to_tensor( + argument_value, dtype=argument_dtype, name=argument_name) + else: + return constant_op.constant( + argument_default, dtype=argument_dtype, name=argument_name) + + +class TextLineDataset(Dataset): + """A `Dataset` comprising lines from one or more text files.""" + + def __init__(self, filenames, compression_type=None, buffer_size=None): + """Creates a `TextLineDataset`. + + Args: + filenames: A `tf.string` tensor containing one or more filenames. + compression_type: (Optional.) A `tf.string` scalar evaluating to one of + `""` (no compression), `"ZLIB"`, or `"GZIP"`. + buffer_size: (Optional.) A `tf.int64` scalar denoting the number of bytes + to buffer. A value of 0 results in the default buffering values chosen + based on the compression type. + """ + super(TextLineDataset, self).__init__() + self._filenames = ops.convert_to_tensor( + filenames, dtype=dtypes.string, name="filenames") + self._compression_type = _convert_optional_param_to_tensor( + "compression_type", + compression_type, + argument_default="", + argument_dtype=dtypes.string) + self._buffer_size = _convert_optional_param_to_tensor( + "buffer_size", buffer_size, _DEFAULT_READER_BUFFER_SIZE_BYTES) + + def make_dataset_resource(self): + return gen_dataset_ops.text_line_dataset( + self._filenames, self._compression_type, self._buffer_size) + + @property + def output_shapes(self): + return tensor_shape.scalar() + + @property + def output_types(self): + return dtypes.string + + +class TFRecordDataset(Dataset): + """A `Dataset` comprising records from one or more TFRecord files.""" + + def __init__(self, filenames, compression_type=None, buffer_size=None): + """Creates a `TFRecordDataset`. + + Args: + filenames: A `tf.string` tensor containing one or more filenames. + compression_type: (Optional.) A `tf.string` scalar evaluating to one of + `""` (no compression), `"ZLIB"`, or `"GZIP"`. + buffer_size: (Optional.) A `tf.int64` scalar representing the number of + bytes in the read buffer. 0 means no buffering. + """ + super(TFRecordDataset, self).__init__() + self._filenames = ops.convert_to_tensor(filenames, name="filenames") + self._compression_type = _convert_optional_param_to_tensor( + "compression_type", + compression_type, + argument_default="", + argument_dtype=dtypes.string) + self._buffer_size = _convert_optional_param_to_tensor( + "buffer_size", + buffer_size, + argument_default=_DEFAULT_READER_BUFFER_SIZE_BYTES) + + def make_dataset_resource(self): + return gen_dataset_ops.tf_record_dataset( + self._filenames, self._compression_type, self._buffer_size) + + @property + def output_shapes(self): + return tensor_shape.TensorShape([]) + + @property + def output_types(self): + return dtypes.string + + +class FixedLengthRecordDataset(Dataset): + """A `Dataset` of fixed-length records from one or more binary files.""" + + def __init__(self, + filenames, + record_bytes, + header_bytes=None, + footer_bytes=None, + buffer_size=None): + """Creates a `FixedLengthRecordDataset`. + + Args: + filenames: A `tf.string` tensor containing one or more filenames. + record_bytes: A `tf.int64` scalar representing the number of bytes in + each record. + header_bytes: (Optional.) A `tf.int64` scalar representing the number of + bytes to skip at the start of a file. + footer_bytes: (Optional.) A `tf.int64` scalar representing the number of + bytes to ignore at the end of a file. + buffer_size: (Optional.) A `tf.int64` scalar representing the number of + bytes to buffer when reading. + """ + super(FixedLengthRecordDataset, self).__init__() + self._filenames = ops.convert_to_tensor( + filenames, dtype=dtypes.string, name="filenames") + self._record_bytes = ops.convert_to_tensor( + record_bytes, dtype=dtypes.int64, name="record_bytes") + + self._header_bytes = _convert_optional_param_to_tensor( + "header_bytes", header_bytes) + self._footer_bytes = _convert_optional_param_to_tensor( + "footer_bytes", footer_bytes) + self._buffer_size = _convert_optional_param_to_tensor( + "buffer_size", buffer_size, _DEFAULT_READER_BUFFER_SIZE_BYTES) + + def make_dataset_resource(self): + return gen_dataset_ops.fixed_length_record_dataset( + self._filenames, self._header_bytes, self._record_bytes, + self._footer_bytes, self._buffer_size) + + @property + def output_shapes(self): + return tensor_shape.scalar() + + @property + def output_types(self): + return dtypes.string diff --git a/tensorflow/contrib/data/python/util/BUILD b/tensorflow/python/data/util/BUILD similarity index 100% rename from tensorflow/contrib/data/python/util/BUILD rename to tensorflow/python/data/util/BUILD diff --git a/tensorflow/contrib/data/python/util/nest.py b/tensorflow/python/data/util/nest.py similarity index 99% rename from tensorflow/contrib/data/python/util/nest.py rename to tensorflow/python/data/util/nest.py index 5c4b64c8739..83908d8a0e9 100644 --- a/tensorflow/contrib/data/python/util/nest.py +++ b/tensorflow/python/data/util/nest.py @@ -13,6 +13,7 @@ # limitations under the License. # ============================================================================== +# TODO(shivaniagrawal): Merge with core nest """## Functions for working with arbitrarily nested sequences of elements. NOTE(mrry): This fork of the `tensorflow.python.util.nest` module diff --git a/tensorflow/contrib/data/python/util/nest_test.py b/tensorflow/python/data/util/nest_test.py similarity index 99% rename from tensorflow/contrib/data/python/util/nest_test.py rename to tensorflow/python/data/util/nest_test.py index 58a10445fc8..6416e2850d5 100644 --- a/tensorflow/contrib/data/python/util/nest_test.py +++ b/tensorflow/python/data/util/nest_test.py @@ -22,7 +22,7 @@ import collections import numpy as np -from tensorflow.contrib.data.python.util import nest +from tensorflow.python.data.util import nest from tensorflow.python.framework import constant_op from tensorflow.python.ops import array_ops from tensorflow.python.ops import math_ops diff --git a/tensorflow/python/estimator/training.py b/tensorflow/python/estimator/training.py index 0f389f0017c..55cfc051291 100644 --- a/tensorflow/python/estimator/training.py +++ b/tensorflow/python/estimator/training.py @@ -203,6 +203,22 @@ class EvalSpec( throttle_secs=throttle_secs) +class _StopAtSecsHook(session_run_hook.SessionRunHook): + """Stops given secs after begin is called.""" + + def __init__(self, stop_after_secs): + self._stop_after_secs = stop_after_secs + self._start_time = None + + def begin(self): + self._start_time = time.time() + + def after_run(self, run_context, run_values): + del run_values + if time.time() - self._start_time >= self._stop_after_secs: + run_context.request_stop() + + class UnimplementedError(Exception): pass @@ -254,7 +270,38 @@ class _TrainingExecutor(object): def run_local(self): """Runs training and evaluation locally (non-distributed).""" - raise UnimplementedError('Method run_local has not been implemented.') + + def _should_stop_local_train(global_step): + if self._train_spec.max_steps is None: + return False + if global_step >= self._train_spec.max_steps: + return True + return False + + if self._eval_spec.throttle_secs <= 0: + raise ValueError('eval_spec.throttle_secs should be positive, given: {}.' + 'It is used do determine how long each training ' + 'iteration should go when train and evaluate ' + 'locally.'.format( + self._eval_spec.throttle_secs)) + + stop_hook = _StopAtSecsHook(self._eval_spec.throttle_secs) + train_hooks = list(self._train_spec.hooks) + [stop_hook] + logging.info('Start train and evaluate loop. The evaluate will happen ' + 'after {} secs (eval_spec.throttle_secs) or training is ' + 'finished.'.format(self._eval_spec.throttle_secs)) + while True: + self._estimator.train( + input_fn=self._train_spec.input_fn, + max_steps=self._train_spec.max_steps, + hooks=train_hooks) + metrics = self._estimator.evaluate( + input_fn=self._eval_spec.input_fn, + steps=self._eval_spec.steps, + hooks=self._eval_spec.hooks, + name=self._eval_spec.name) + if _should_stop_local_train(metrics[ops.GraphKeys.GLOBAL_STEP]): + break def _start_std_server(self, config): """Creates, starts, and returns a server_lib.Server.""" diff --git a/tensorflow/python/estimator/training_test.py b/tensorflow/python/estimator/training_test.py index 35398a929b4..cd58bfffd46 100644 --- a/tensorflow/python/estimator/training_test.py +++ b/tensorflow/python/estimator/training_test.py @@ -27,8 +27,10 @@ from tensorflow.python.estimator import estimator as estimator_lib from tensorflow.python.estimator import run_config as run_config_lib from tensorflow.python.estimator import training from tensorflow.python.framework import ops +from tensorflow.python.ops import control_flow_ops from tensorflow.python.platform import test from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.training import monitored_session from tensorflow.python.training import saver from tensorflow.python.training import server_lib from tensorflow.python.training import session_run_hook @@ -614,5 +616,102 @@ class TrainingExecutorRunPsTest(test.TestCase): mock_eval_spec).run_ps() +class StopAtSecsHookTest(test.TestCase): + """Tests StopAtSecsHook.""" + + @test.mock.patch.object(time, 'time') + def test_stops_after_time(self, mock_time): + mock_time.return_value = 1484695987.209386 + hook = training._StopAtSecsHook(1000) + with ops.Graph().as_default(): + no_op = control_flow_ops.no_op() + # some time passed before training starts + mock_time.return_value += 250 + with monitored_session.MonitoredSession(hooks=[hook]) as sess: + self.assertFalse(sess.should_stop()) + sess.run(no_op) + self.assertFalse(sess.should_stop()) + mock_time.return_value += 500 + sess.run(no_op) + self.assertFalse(sess.should_stop()) + mock_time.return_value += 400 + sess.run(no_op) + self.assertFalse(sess.should_stop()) + mock_time.return_value += 200 + sess.run(no_op) + self.assertTrue(sess.should_stop()) + + +class TrainingExecutorRunLocalTest(test.TestCase): + """Tests run_local of _TrainingExecutor.""" + + def test_send_stop_at_secs_to_train(self): + mock_est = test.mock.Mock(spec=estimator_lib.Estimator) + train_spec = training.TrainSpec( + input_fn=lambda: 1, max_steps=2, hooks=[_FakeHook()]) + eval_spec = training.EvalSpec( + input_fn=lambda: 1, hooks=[_FakeHook()], throttle_secs=100) + mock_est.evaluate.return_value = {_GLOBAL_STEP_KEY: train_spec.max_steps} + + executor = training._TrainingExecutor(mock_est, train_spec, eval_spec) + executor.run_local() + + stop_hook = mock_est.train.call_args[1]['hooks'][-1] + self.assertIsInstance(stop_hook, training._StopAtSecsHook) + self.assertEqual(eval_spec.throttle_secs, stop_hook._stop_after_secs) + + def test_runs_in_a_loop_until_max_steps(self): + mock_est = test.mock.Mock(spec=estimator_lib.Estimator) + train_spec = training.TrainSpec( + input_fn=lambda: 1, max_steps=300, hooks=[_FakeHook()]) + eval_spec = training.EvalSpec( + input_fn=lambda: 1, hooks=[_FakeHook()], throttle_secs=100) + # should be called 3 times. + mock_est.evaluate.side_effect = [{ + _GLOBAL_STEP_KEY: train_spec.max_steps - 100 + }, { + _GLOBAL_STEP_KEY: train_spec.max_steps - 50 + }, { + _GLOBAL_STEP_KEY: train_spec.max_steps + }] + + executor = training._TrainingExecutor(mock_est, train_spec, eval_spec) + executor.run_local() + + self.assertEqual(3, mock_est.train.call_count) + self.assertEqual(3, mock_est.evaluate.call_count) + + def test_train_and_evaluate_args(self): + mock_est = test.mock.Mock(spec=estimator_lib.Estimator) + train_spec = training.TrainSpec( + input_fn=lambda: 1, max_steps=300, hooks=[_FakeHook()]) + eval_spec = training.EvalSpec( + input_fn=lambda: 1, steps=2, hooks=[_FakeHook()], name='local_eval') + mock_est.evaluate.return_value = {_GLOBAL_STEP_KEY: train_spec.max_steps} + + executor = training._TrainingExecutor(mock_est, train_spec, eval_spec) + executor.run_local() + + mock_est.evaluate.assert_called_with( + name=eval_spec.name, + input_fn=eval_spec.input_fn, + steps=eval_spec.steps, + hooks=eval_spec.hooks) + + train_args = mock_est.train.call_args[1] + self.assertEqual(list(train_spec.hooks), list(train_args['hooks'][:-1])) + self.assertEqual(train_spec.input_fn, train_args['input_fn']) + self.assertEqual(train_spec.max_steps, train_args['max_steps']) + + def test_errors_out_if_throttle_secs_is_zero(self): + mock_est = test.mock.Mock(spec=estimator_lib.Estimator) + train_spec = training.TrainSpec(input_fn=lambda: 1) + eval_spec = training.EvalSpec(input_fn=lambda: 1, throttle_secs=0) + + executor = training._TrainingExecutor(mock_est, train_spec, eval_spec) + with self.assertRaisesRegexp(ValueError, 'throttle_secs'): + executor.run_local() + + if __name__ == '__main__': test.main() diff --git a/tensorflow/python/keras/BUILD b/tensorflow/python/keras/BUILD index c3468647b87..62720fe67f6 100644 --- a/tensorflow/python/keras/BUILD +++ b/tensorflow/python/keras/BUILD @@ -35,6 +35,7 @@ py_library( "_impl/keras/engine/__init__.py", "_impl/keras/engine/topology.py", "_impl/keras/engine/training.py", + "_impl/keras/estimator.py", "_impl/keras/initializers.py", "_impl/keras/layers/__init__.py", "_impl/keras/layers/advanced_activations.py", @@ -88,6 +89,7 @@ py_library( "datasets/imdb/__init__.py", "datasets/mnist/__init__.py", "datasets/reuters/__init__.py", + "estimator/__init__.py", "initializers/__init__.py", "layers/__init__.py", "losses/__init__.py", @@ -125,9 +127,11 @@ py_library( "//tensorflow/python:layers_base", "//tensorflow/python:logging_ops", "//tensorflow/python:math_ops", + "//tensorflow/python:metrics", "//tensorflow/python:nn", "//tensorflow/python:platform", "//tensorflow/python:random_ops", + "//tensorflow/python:session", "//tensorflow/python:sparse_ops", "//tensorflow/python:sparse_tensor", "//tensorflow/python:state_ops", @@ -139,6 +143,8 @@ py_library( "//tensorflow/python:util", "//tensorflow/python:variable_scope", "//tensorflow/python:variables", + "//tensorflow/python/estimator", + "//tensorflow/python/estimator:model_fn", "@six_archive//:six", ], ) @@ -656,6 +662,22 @@ py_test( ], ) +py_test( + name = "estimator_test", + size = "medium", + srcs = ["_impl/keras/estimator_test.py"], + srcs_version = "PY2AND3", + deps = [ + ":keras", + "//tensorflow/python:client_testlib", + "//tensorflow/python:dtypes", + "//tensorflow/python:framework_ops", + "//tensorflow/python:platform", + "//tensorflow/python/estimator:numpy_io", + "//third_party/py/numpy", + ], +) + py_test( name = "backend_test", size = "small", diff --git a/tensorflow/python/keras/__init__.py b/tensorflow/python/keras/__init__.py index 962c7678dd2..fa798899665 100644 --- a/tensorflow/python/keras/__init__.py +++ b/tensorflow/python/keras/__init__.py @@ -29,6 +29,7 @@ from tensorflow.python.keras import backend from tensorflow.python.keras import callbacks from tensorflow.python.keras import constraints from tensorflow.python.keras import datasets +from tensorflow.python.keras import estimator from tensorflow.python.keras import initializers from tensorflow.python.keras import layers from tensorflow.python.keras import losses diff --git a/tensorflow/python/keras/_impl/keras/__init__.py b/tensorflow/python/keras/_impl/keras/__init__.py index d1aa4415a19..a341065100d 100644 --- a/tensorflow/python/keras/_impl/keras/__init__.py +++ b/tensorflow/python/keras/_impl/keras/__init__.py @@ -25,6 +25,7 @@ from tensorflow.python.keras._impl.keras import callbacks from tensorflow.python.keras._impl.keras import constraints from tensorflow.python.keras._impl.keras import datasets from tensorflow.python.keras._impl.keras import engine +from tensorflow.python.keras._impl.keras import estimator from tensorflow.python.keras._impl.keras import initializers from tensorflow.python.keras._impl.keras import layers from tensorflow.python.keras._impl.keras import losses diff --git a/tensorflow/python/keras/_impl/keras/applications/mobilenet.py b/tensorflow/python/keras/_impl/keras/applications/mobilenet.py index 9375e436f2d..f6482d25496 100644 --- a/tensorflow/python/keras/_impl/keras/applications/mobilenet.py +++ b/tensorflow/python/keras/_impl/keras/applications/mobilenet.py @@ -57,7 +57,7 @@ the 100 % MobileNet on various input sizes: The weights for all 16 models are obtained and translated from Tensorflow checkpoints found at -https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.md +https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md # Reference - [MobileNets: Efficient Convolutional Neural Networks for diff --git a/tensorflow/python/keras/_impl/keras/backend.py b/tensorflow/python/keras/_impl/keras/backend.py index 758a866e188..f02f6d10df9 100644 --- a/tensorflow/python/keras/_impl/keras/backend.py +++ b/tensorflow/python/keras/_impl/keras/backend.py @@ -373,22 +373,7 @@ def get_session(): session = _SESSION if not _MANUAL_VAR_INIT: with session.graph.as_default(): - variables = variables_module.global_variables() - candidate_vars = [] - for v in variables: - if not getattr(v, '_keras_initialized', False): - candidate_vars.append(v) - # This step is expensive, so we only run it on variables not already - # marked as initialized. - is_initialized = session.run( - [variables_module.is_variable_initialized(v) for v in candidate_vars]) - uninitialized_vars = [] - for flag, v in zip(is_initialized, candidate_vars): - if not flag: - uninitialized_vars.append(v) - v._keras_initialized = True - if uninitialized_vars: - session.run(variables_module.variables_initializer(uninitialized_vars)) + _initialize_variables(session) return session @@ -556,6 +541,26 @@ def variable(value, dtype=None, name=None, constraint=None): return v +def _initialize_variables(session): + """Utility to initialize uninitialized variables on the fly.""" + variables = variables_module.global_variables() + candidate_vars = [] + for v in variables: + if not getattr(v, '_keras_initialized', False): + candidate_vars.append(v) + # This step is expensive, so we only run it on variables not already + # marked as initialized. + is_initialized = session.run( + [variables_module.is_variable_initialized(v) for v in candidate_vars]) + uninitialized_vars = [] + for flag, v in zip(is_initialized, candidate_vars): + if not flag: + uninitialized_vars.append(v) + v._keras_initialized = True + if uninitialized_vars: + session.run(variables_module.variables_initializer(uninitialized_vars)) + + def constant(value, dtype=None, shape=None, name=None): """Creates a constant tensor. diff --git a/tensorflow/python/keras/_impl/keras/estimator.py b/tensorflow/python/keras/_impl/keras/estimator.py new file mode 100644 index 00000000000..125e63e1b84 --- /dev/null +++ b/tensorflow/python/keras/_impl/keras/estimator.py @@ -0,0 +1,281 @@ +# Copyright 2015 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. +# ============================================================================== +# pylint: disable=protected-access +"""Home of estimator related functions. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +from tensorflow.python.client import session +from tensorflow.python.estimator import estimator as estimator_lib +from tensorflow.python.estimator import model_fn as model_fn_lib +from tensorflow.python.framework import ops +from tensorflow.python.framework import random_seed +from tensorflow.python.framework import sparse_tensor as sparse_tensor_lib +from tensorflow.python.keras._impl.keras import backend as K +from tensorflow.python.keras._impl.keras import models +from tensorflow.python.keras._impl.keras.utils.generic_utils import CustomObjectScope +from tensorflow.python.ops import metrics as metrics_module +from tensorflow.python.platform import tf_logging as logging +from tensorflow.python.training import saver as saver_lib +from tensorflow.python.training import training_util + + +def _create_ordered_io(keras_model, estimator_io_dict, is_input=True): + """Create a list of tensors from IO dictionary based on Keras IO order. + + Args: + keras_model: an instance of compiled keras model. + estimator_io_dict: features or labels dictionary from model_fn. + is_input: True if dictionary is for inputs. + + Returns: + a list of tensors based on Keras IO order. + + Raises: + ValueError: if dictionary keys cannot be found in Keras model input_names + or output_names. + """ + if is_input: + keras_io_names = keras_model.input_names + else: + keras_io_names = keras_model.output_names + + for key in estimator_io_dict: + if key not in keras_io_names: + raise ValueError( + 'Cannot find %s with name "%s" in Keras Model. It needs to match ' + 'one of the following: %s' % ('input' if is_input else 'output', key, + ', '.join(keras_io_names))) + tensors = [] + for io_name in keras_io_names: + tensors.append(estimator_io_dict[io_name]) + return tensors + + +def _clone_and_build_model(mode, + keras_model, + custom_objects, + features=None, + labels=None): + """Clone and build the given keras_model. + + Args: + mode: training mode. + keras_model: an instance of compiled keras model. + custom_objects: Dictionary for custom objects. + features: + labels: + + Returns: + The newly built model. + """ + # Set to True during training, False for inference. + K.set_learning_phase(mode == model_fn_lib.ModeKeys.TRAIN) + + # Clone keras model. + input_tensors = None if features is None else _create_ordered_io( + keras_model, features) + if custom_objects: + with CustomObjectScope(custom_objects): + model = models.clone_model(keras_model, input_tensors=input_tensors) + else: + model = models.clone_model(keras_model, input_tensors=input_tensors) + + # Compile/Build model + if mode is model_fn_lib.ModeKeys.PREDICT and not model.built: + model.build() + else: + optimizer_config = keras_model.optimizer.get_config() + optimizer = keras_model.optimizer.__class__.from_config(optimizer_config) + optimizer.iterations = training_util.get_or_create_global_step() + + # Get list of outputs. + if labels is None: + target_tensors = None + elif isinstance(labels, dict): + target_tensors = _create_ordered_io(keras_model, labels, is_input=False) + else: + target_tensors = [ + sparse_tensor_lib.convert_to_tensor_or_sparse_tensor(labels) + ] + + model.compile( + optimizer, + keras_model.loss, + metrics=keras_model.metrics, + loss_weights=keras_model.loss_weights, + sample_weight_mode=keras_model.sample_weight_mode, + weighted_metrics=keras_model.weighted_metrics, + target_tensors=target_tensors) + + if isinstance(model, models.Sequential): + model = model.model + return model + + +def _create_keras_model_fn(keras_model, custom_objects=None): + """Creates model_fn for keras Estimator. + + Args: + keras_model: an instance of compiled keras model. + custom_objects: Dictionary for custom objects. + + Returns: + The model_fn for a keras Estimator. + """ + + def model_fn(features, labels, mode): + """model_fn for keras Estimator.""" + model = _clone_and_build_model(mode, keras_model, custom_objects, features, + labels) + # Get inputs to EstimatorSpec + predictions = dict(zip(model.output_names, model.outputs)) + + loss = None + train_op = None + eval_metric_ops = None + + # Set loss and metric only during train and evaluate. + if mode is not model_fn_lib.ModeKeys.PREDICT: + model._make_train_function() # pylint: disable=protected-access + loss = model.total_loss + + if model.metrics: + eval_metric_ops = {} + # When each metric maps to an output + if isinstance(model.metrics, dict): + for i, output_name in enumerate(model.metrics.keys()): + metric_name = model.metrics[output_name] + if callable(metric_name): + metric_name = metric_name.__name__ + # When some outputs use the same metric + if list(model.metrics.values()).count(metric_name) > 1: + metric_name += '_' + output_name + eval_metric_ops[metric_name] = metrics_module.mean( + model.metrics_tensors[i - len(model.metrics)]) + else: + for i, metric_name in enumerate(model.metrics): + if callable(metric_name): + metric_name = metric_name.__name__ + eval_metric_ops[metric_name] = metrics_module.mean( + model.metrics_tensors[i]) + + # Set train_op only during train. + if mode is model_fn_lib.ModeKeys.TRAIN: + train_op = model.train_function.updates_op + + return model_fn_lib.EstimatorSpec( + mode=mode, + predictions=predictions, + loss=loss, + train_op=train_op, + eval_metric_ops=eval_metric_ops) + + return model_fn + + +def _save_first_checkpoint(keras_model, estimator, custom_objects, + keras_weights): + """Save first checkpoint for the keras Estimator. + + Args: + keras_model: an instance of compiled keras model. + estimator: keras estimator. + custom_objects: Dictionary for custom objects. + keras_weights: A flat list of Numpy arrays for weights of given keras_model. + + Returns: + The model_fn for a keras Estimator. + """ + with ops.Graph().as_default() as g, g.device(estimator._device_fn): + random_seed.set_random_seed(estimator.config.tf_random_seed) + training_util.create_global_step() + model = _clone_and_build_model(model_fn_lib.ModeKeys.TRAIN, keras_model, + custom_objects) + + if isinstance(model, models.Sequential): + model = model.model + # Load weights and save to checkpoint if there is no checkpoint + latest_path = saver_lib.latest_checkpoint(estimator.model_dir) + if not latest_path: + with session.Session() as sess: + model.set_weights(keras_weights) + # Make update ops and initialize all variables. + if not model.train_function: + # pylint: disable=protected-access + model._make_train_function() + K._initialize_variables(sess) + # pylint: enable=protected-access + saver = saver_lib.Saver() + saver.save(sess, estimator.model_dir + '/') + + +def model_to_estimator(keras_model=None, + keras_model_path=None, + custom_objects=None, + model_dir=None, + config=None): + """Constructs an `Estimator` instance from given keras model. + + Args: + keras_model: Keras model in memory. + keras_model_path: Directory to a keras model on disk. + custom_objects: Dictionary for custom objects. + model_dir: Directory to save Estimator model parameters, graph and etc. + config: Configuration object. + + Returns: + An Estimator from given keras model. + + Raises: + ValueError: if neither keras_model nor keras_model_path was given. + ValueError: if both keras_model and keras_model_path was given. + ValueError: if the keras_model_path is a GCS URI. + ValueError: if keras_model has not been compiled. + """ + if (not keras_model) and (not keras_model_path): + raise ValueError( + 'Either keras_model or keras_model_path needs to be provided.') + if keras_model and keras_model_path: + raise ValueError( + 'Please specity either keras_model or keras_model_path but not both.') + + if not keras_model: + if keras_model_path.startswith( + 'gs://') or 'storage.googleapis.com' in keras_model_path: + raise ValueError( + '%s is not a local path. Please copy the model locally first.' % + keras_model_path) + logging.info('Loading models from %s', keras_model_path) + keras_model = models.load_model(keras_model_path) + else: + logging.info('Using the Keras model from memory.') + keras_model = keras_model + + if not hasattr(keras_model, 'optimizer'): + raise ValueError( + 'Given keras model has not been compiled yet. Please compile first ' + 'before creating the estimator.') + + keras_weights = keras_model.get_weights() + keras_model_fn = _create_keras_model_fn(keras_model, custom_objects) + est = estimator_lib.Estimator( + keras_model_fn, model_dir=model_dir, config=config) + # TODO(yifeif): move checkpoint initialization to scaffold.init_fn + _save_first_checkpoint(keras_model, est, custom_objects, keras_weights) + return est diff --git a/tensorflow/python/keras/_impl/keras/estimator_test.py b/tensorflow/python/keras/_impl/keras/estimator_test.py new file mode 100644 index 00000000000..7967038e76c --- /dev/null +++ b/tensorflow/python/keras/_impl/keras/estimator_test.py @@ -0,0 +1,392 @@ +# Copyright 2016 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. +# ============================================================================== +"""Tests for training routines.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from math import log10 +import os +import tempfile + +import numpy as np + +from tensorflow.python.estimator.inputs import numpy_io +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.keras._impl import keras +from tensorflow.python.keras._impl.keras import testing_utils +from tensorflow.python.platform import gfile +from tensorflow.python.platform import test + +try: + import h5py # pylint:disable=g-import-not-at-top +except ImportError: + h5py = None + + +def simple_sequential_model(): + model = keras.models.Sequential() + model.add( + keras.layers.Conv2D( + 32, kernel_size=(3, 3), activation='relu', input_shape=(14, 14, 3))) + model.add(keras.layers.MaxPooling2D(pool_size=(2, 2))) + model.add(keras.layers.Dropout(0.25)) + model.add(keras.layers.Flatten()) + model.add(keras.layers.Dense(16, activation='relu')) + model.add(keras.layers.Dropout(0.25)) + model.add(keras.layers.Dense(3, activation='softmax')) + return model + + +def simple_functional_model(): + a = keras.layers.Input(shape=(14, 14, 3)) + b = keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu')(a) + b = keras.layers.MaxPooling2D(pool_size=(2, 2))(b) + b = keras.layers.Dropout(0.25)(b) + b = keras.layers.Flatten()(b) + b = keras.layers.Dense(16, activation='relu')(b) + b = keras.layers.Dropout(0.25)(b) + b = keras.layers.Dense(3, activation='softmax')(b) + model = keras.models.Model(inputs=[a], outputs=[b]) + return model + + +def get_resource_for_simple_model(is_sequential, is_evaluate): + model = simple_sequential_model( + ) if is_sequential else simple_functional_model() + if is_sequential: + model.build() + input_name = model.input_names[0] + + np.random.seed(1337) + (x_train, y_train), (x_test, y_test) = testing_utils.get_test_data( + train_samples=200, + test_samples=100, + input_shape=(14, 14, 3), + num_classes=3) + y_train = keras.utils.to_categorical(y_train) + y_test = keras.utils.to_categorical(y_test) + + train_input_fn = numpy_io.numpy_input_fn( + x={input_name: np.array(x_train, dtype=np.float32)}, + y=np.array(y_train, dtype=np.float32), + shuffle=False, + num_epochs=None, + batch_size=16) + + evaluate_input_fn = numpy_io.numpy_input_fn( + x={input_name: np.array(x_test, dtype=np.float32)}, + y=np.array(y_test, dtype=np.float32), + num_epochs=1, + shuffle=False) + + predict_input_fn = numpy_io.numpy_input_fn( + x={input_name: np.array(x_test, dtype=np.float32)}, + num_epochs=1, + shuffle=False) + + inference_input_fn = evaluate_input_fn if is_evaluate else predict_input_fn + + return model, (x_train, y_train), (x_test, + y_test), train_input_fn, inference_input_fn + + +def multi_inputs_multi_outputs_model(): + # test multi-input layer + a = keras.layers.Input(shape=(32,), name='input_a') + b = keras.layers.Input(shape=(32,), name='input_b') + dense = keras.layers.Dense(16, name='dense_1') + a_2 = dense(a) + b_2 = dense(b) + merged = keras.layers.concatenate([a_2, b_2], name='merge') + c = keras.layers.Dense(3, activation='softmax', name='dense_2')(merged) + d = keras.layers.Dense(2, activation='softmax', name='dense_3')(merged) + model = keras.models.Model(inputs=[a, b], outputs=[c, d]) + model.compile( + loss='categorical_crossentropy', + optimizer='rmsprop', + metrics={'dense_2': 'accuracy', + 'dense_3': 'accuracy'}) + return model + + +class TestKerasEstimator(test.TestCase): + + def setUp(self): + self._base_dir = os.path.join(self.get_temp_dir(), 'keras_estimator_test') + gfile.MakeDirs(self._base_dir) + + def tearDown(self): + gfile.DeleteRecursively(self._base_dir) + + def test_train(self): + for is_sequential in [True, False]: + keras_model, (_, _), ( + _, _), train_input_fn, eval_input_fn = get_resource_for_simple_model( + is_sequential=is_sequential, is_evaluate=True) + keras_model.compile( + loss='categorical_crossentropy', + optimizer='rmsprop', + metrics=['accuracy', 'mse', keras.metrics.categorical_accuracy]) + + with self.test_session(): + est_keras = keras.estimator.model_to_estimator( + keras_model=keras_model, + model_dir=tempfile.mkdtemp(dir=self._base_dir)) + est_keras.train(input_fn=train_input_fn, steps=200 * 10 / 16) + eval_results = est_keras.evaluate(input_fn=eval_input_fn) + self.assertGreater(eval_results['accuracy'], 0.9) + self.assertGreater(eval_results['categorical_accuracy'], 0.9) + self.assertLess(eval_results['mse'], 0.1) + + def test_evaluate(self): + keras_model, (x_train, y_train), ( + x_test, y_test), _, eval_input_fn = get_resource_for_simple_model( + is_sequential=False, is_evaluate=True) + + with self.test_session(): + metrics = [ + 'binary_accuracy', 'binary_crossentropy', 'categorical_accuracy', + 'categorical_crossentropy', 'cosine_proximity', 'hinge', + 'kullback_leibler_divergence', 'mean_absolute_error', + 'mean_absolute_percentage_error', 'mean_squared_error', + 'mean_squared_logarithmic_error', 'poisson', 'squared_hinge', + 'top_k_categorical_accuracy' + ] + keras_model.compile( + loss='categorical_crossentropy', optimizer='adam', metrics=metrics) + keras_model.fit(x_train, y_train, epochs=1) + keras_eval = keras_model.evaluate(x_test, y_test, batch_size=32) + + with self.test_session(): + keras_est = keras.estimator.model_to_estimator( + keras_model=keras_model, + model_dir=tempfile.mkdtemp(dir=self._base_dir)) + est_eval = keras_est.evaluate(input_fn=eval_input_fn) + + metrics = ['loss'] + metrics + + # Check loss and all metrics match between keras and estimator. + def shift(val): + return val / 10**int(log10(abs(val))) + + for i, metric_name in enumerate(metrics): + self.assertAlmostEqual( + shift(est_eval[metric_name]), + shift(keras_eval[i]), + places=4, + msg='%s mismatch, keras model: %s, estimator: %s' % + (metric_name, est_eval[metric_name], keras_eval[i])) + + def test_predict(self): + # Check that predict on a pretrained model yield the same result. + keras_model, (x_train, y_train), ( + x_test, _), _, pred_input_fn = get_resource_for_simple_model( + is_sequential=True, is_evaluate=False) + + with self.test_session(): + keras_model.compile( + loss='categorical_crossentropy', + optimizer='adam', + metrics=['accuracy']) + keras_model.fit(x_train, y_train, epochs=1) + keras_pred = [np.argmax(y) for y in keras_model.predict(x_test)] + + with self.test_session(): + keras_est = keras.estimator.model_to_estimator( + keras_model=keras_model, + model_dir=tempfile.mkdtemp(dir=self._base_dir)) + est_pred = [ + np.argmax(y[keras_model.output_names[0]]) + for y in keras_est.predict(input_fn=pred_input_fn) + ] + self.assertAllEqual(est_pred, keras_pred) + + def test_multi_inputs_multi_outputs(self): + np.random.seed(1337) + (a_train, c_train), (a_test, c_test) = testing_utils.get_test_data( + train_samples=200, test_samples=100, input_shape=(32,), num_classes=3) + (b_train, d_train), (b_test, d_test) = testing_utils.get_test_data( + train_samples=200, test_samples=100, input_shape=(32,), num_classes=2) + c_train = keras.utils.to_categorical(c_train) + c_test = keras.utils.to_categorical(c_test) + d_train = keras.utils.to_categorical(d_train) + d_test = keras.utils.to_categorical(d_test) + + def train_input_fn(): + input_dict = { + 'input_a': + ops.convert_to_tensor( + np.array(a_train, dtype=np.float32), dtype=dtypes.float32), + 'input_b': + ops.convert_to_tensor( + np.array(b_train, dtype=np.float32), dtype=dtypes.float32) + } + output_dict = { + 'dense_2': + ops.convert_to_tensor( + np.array(c_train, dtype=np.float32), dtype=dtypes.float32), + 'dense_3': + ops.convert_to_tensor( + np.array(d_train, dtype=np.float32), dtype=dtypes.float32) + } + return input_dict, output_dict + + def evaluate_input_fn(): + input_dict = { + 'input_a': + ops.convert_to_tensor( + np.array(a_test, dtype=np.float32), dtype=dtypes.float32), + 'input_b': + ops.convert_to_tensor( + np.array(b_test, dtype=np.float32), dtype=dtypes.float32) + } + output_dict = { + 'dense_2': + ops.convert_to_tensor( + np.array(c_test, dtype=np.float32), dtype=dtypes.float32), + 'dense_3': + ops.convert_to_tensor( + np.array(d_test, dtype=np.float32), dtype=dtypes.float32) + } + return input_dict, output_dict + + with self.test_session(): + model = multi_inputs_multi_outputs_model() + est_keras = keras.estimator.model_to_estimator( + keras_model=model, model_dir=tempfile.mkdtemp(dir=self._base_dir)) + est_keras.train(input_fn=train_input_fn, steps=200 * 10 / 16) + eval_results = est_keras.evaluate(input_fn=evaluate_input_fn, steps=1) + self.assertGreater(eval_results['accuracy_dense_2'], 0.5) + self.assertGreater(eval_results['accuracy_dense_3'], 0.5) + + def test_init_from_file(self): + if h5py is None: + return # Skip test if models cannot be saved. + + keras_model, (x_train, y_train), ( + x_test, _), _, pred_input_fn = get_resource_for_simple_model( + is_sequential=False, is_evaluate=False) + + with self.test_session(): + keras_model.compile( + loss='categorical_crossentropy', + optimizer='rmsprop', + metrics=['accuracy']) + keras_model.fit(x_train, y_train, epochs=1) + keras_pred = [np.argmax(y) for y in keras_model.predict(x_test)] + fname = os.path.join(self._base_dir, 'keras_model.h5') + keras.models.save_model(keras_model, fname) + + with self.test_session(): + keras_est = keras.estimator.model_to_estimator( + keras_model_path=fname, + model_dir=tempfile.mkdtemp(dir=self._base_dir)) + est_pred = [ + np.argmax(y[keras_model.output_names[0]]) + for y in keras_est.predict(input_fn=pred_input_fn) + ] + self.assertAllEqual(est_pred, keras_pred) + + def test_keras_model_init_error(self): + with self.assertRaisesRegexp(ValueError, 'Either'): + keras.estimator.model_to_estimator() + + with self.test_session(): + keras_model = simple_sequential_model() + with self.assertRaisesRegexp(ValueError, 'not both'): + keras.estimator.model_to_estimator( + keras_model=keras_model, + keras_model_path=tempfile.mkdtemp(dir=self._base_dir)) + + with self.test_session(): + keras_model = simple_sequential_model() + with self.assertRaisesRegexp(ValueError, 'compiled'): + keras.estimator.model_to_estimator(keras_model=keras_model) + + with self.test_session(): + keras_model = simple_sequential_model() + with self.assertRaisesRegexp(ValueError, 'not a local path'): + keras.estimator.model_to_estimator( + keras_model_path='gs://bucket/object') + + def test_invalid_ionames_error(self): + np.random.seed(1337) + (x_train, y_train), (_, _) = testing_utils.get_test_data( + train_samples=200, test_samples=100, input_shape=(10,), num_classes=2) + y_train = keras.utils.to_categorical(y_train) + + def invald_input_name_input_fn(): + input_dict = { + 'invalid_input_name': + ops.convert_to_tensor( + np.array(x_train, dtype=np.float32), dtype=dtypes.float32), + } + output = ops.convert_to_tensor( + np.array(y_train, dtype=np.float32), dtype=dtypes.float32) + return input_dict, output + + def invald_output_name_input_fn(): + input_dict = { + 'input_1': + ops.convert_to_tensor( + np.array(x_train, dtype=np.float32), dtype=dtypes.float32), + } + output_dict = { + 'invalid_output_name': + ops.convert_to_tensor( + np.array(y_train, dtype=np.float32), dtype=dtypes.float32), + } + return input_dict, output_dict + + model = simple_functional_model() + model.compile( + loss='categorical_crossentropy', optimizer='adam', metrics=['acc']) + est_keras = keras.estimator.model_to_estimator( + keras_model=model, model_dir=tempfile.mkdtemp(dir=self._base_dir)) + + with self.test_session(): + with self.assertRaises(ValueError): + est_keras.train(input_fn=invald_input_name_input_fn, steps=100) + + with self.assertRaises(ValueError): + est_keras.train(input_fn=invald_output_name_input_fn, steps=100) + + def test_custom_objects(self): + keras_model, (_, _), ( + _, _), train_input_fn, eval_input_fn = get_resource_for_simple_model( + is_sequential=True, is_evaluate=True) + + class CustomOp(keras.optimizers.RMSprop): + pass + + def custom_loss(y_true, y_pred): + return keras.losses.categorical_crossentropy(y_true, y_pred) + + keras_model.compile( + loss=custom_loss, optimizer=CustomOp(), metrics=['accuracy']) + + with self.test_session(): + est_keras = keras.estimator.model_to_estimator( + keras_model=keras_model, + model_dir=tempfile.mkdtemp(dir=self._base_dir)) + est_keras.train(input_fn=train_input_fn, steps=200 * 10 / 16) + eval_results = est_keras.evaluate(input_fn=eval_input_fn) + self.assertGreater(eval_results['accuracy'], 0.9) + + +if __name__ == '__main__': + test.main() diff --git a/tensorflow/python/keras/_impl/keras/models.py b/tensorflow/python/keras/_impl/keras/models.py index 9a4578b89b3..fce86dd565b 100644 --- a/tensorflow/python/keras/_impl/keras/models.py +++ b/tensorflow/python/keras/_impl/keras/models.py @@ -417,6 +417,9 @@ class Sequential(Model): name = prefix + str(K.get_uid(prefix)) self.name = name + # Used by Layer base class. + self._dtype = None + # The following properties are not actually used by Keras; # they exist for compatibility with TF's variable scoping mechanism. self._updates = [] diff --git a/tensorflow/python/keras/estimator/__init__.py b/tensorflow/python/keras/estimator/__init__.py new file mode 100644 index 00000000000..6f931f41581 --- /dev/null +++ b/tensorflow/python/keras/estimator/__init__.py @@ -0,0 +1,25 @@ +# Copyright 2016 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. +# ============================================================================== +"""Keras estimator API.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.python.keras._impl.keras.estimator import model_to_estimator + +del absolute_import +del division +del print_function diff --git a/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py b/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py index 0ee972f4923..ad47545c939 100644 --- a/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py +++ b/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py @@ -29,6 +29,13 @@ from tensorflow.python.ops import math_ops from tensorflow.python.platform import test +def _AddTest(test_class, op_name, testcase_name, fn): + test_name = "_".join(["test", op_name, testcase_name]) + if hasattr(test_class, test_name): + raise RuntimeError("Test %s defined more than once" % test_name) + setattr(test_class, test_name, fn) + + class SelfAdjointEigTest(test.TestCase): def testWrongDimensions(self): @@ -50,28 +57,30 @@ def SortEigenDecomposition(e, v): return np.take(e, perm, -1), np.take(v, perm, -1) -def NormalizeEigenvectorsPhase(v): - """Normalizes the phase of the Eigenvectors stored in the columns of `v`. +def EquilibrateEigenVectorPhases(x, y): + """Equilibrate the phase of the Eigenvectors in the columns of `x` and `y`. - (complex) Eigenvectors are only unique up to an arbitrary phase. - We normalize the vectors such that the first component has phase 0. + Eigenvectors are only unique up to an arbitrary phase. This function rotates x + such that it matches y. Precondition: The coluns of x and y differ by a + multiplicative complex phase factor only. Args: - v: `np.ndarray` with Eigenvectors as returned from `np.linalg.eigh`. + x: `np.ndarray` with Eigenvectors + y: `np.ndarray` with Eigenvectors Returns: - `np.ndarray` normalized Eigenvectors. + `np.ndarray` containing an equilibrated version of x. """ - reference = v / np.linalg.norm(v[..., 0:1, :], axis=-1, keepdims=True) - return v * reference.conj() + phases = np.sum(np.conj(x) * y, -2, keepdims=True) + phases /= np.abs(phases) + return phases * x def _GetSelfAdjointEigTest(dtype_, shape_, compute_v_): def CompareEigenVectors(self, x, y, tol): - x = NormalizeEigenvectorsPhase(x) - y = NormalizeEigenvectorsPhase(y) - self.assertAllClose(x, y, atol=tol, rtol=tol) + x = EquilibrateEigenVectorPhases(x, y) + self.assertAllClose(x, y, atol=tol) def CompareEigenDecompositions(self, x_e, x_v, y_e, y_v, tol): num_batches = int(np.prod(x_e.shape[:-1])) @@ -103,7 +112,7 @@ def _GetSelfAdjointEigTest(dtype_, shape_, compute_v_): else: atol = 1e-12 np_e, np_v = np.linalg.eigh(a) - with self.test_session(): + with self.test_session(use_gpu=True): if compute_v_: tf_e, tf_v = linalg_ops.self_adjoint_eig(constant_op.constant(a)) @@ -152,7 +161,7 @@ def _GetSelfAdjointEigGradTest(dtype_, shape_, compute_v_): tol = 1e-2 else: tol = 1e-7 - with self.test_session(): + with self.test_session(use_gpu=True): tf_a = constant_op.constant(a) if compute_v_: tf_e, tf_v = linalg_ops.self_adjoint_eig(tf_a) @@ -185,17 +194,16 @@ def _GetSelfAdjointEigGradTest(dtype_, shape_, compute_v_): return Test -if __name__ == '__main__': - for compute_v in [True, False]: - for dtype in ( - dtypes_lib.float32, dtypes_lib.float64, - dtypes_lib.complex64, dtypes_lib.complex128): +if __name__ == "__main__": + for compute_v in True, False: + for dtype in (dtypes_lib.float32, dtypes_lib.float64, dtypes_lib.complex64, + dtypes_lib.complex128): for size in 1, 2, 5, 10: for batch_dims in [(), (3,)] + [(3, 2)] * (max(size, size) < 10): shape = batch_dims + (size, size) - name = '%s_%s_%s' % (dtype, '_'.join(map(str, shape)), compute_v) - setattr(SelfAdjointEigTest, 'testSelfAdjointEig_' + name, - _GetSelfAdjointEigTest(dtype, shape, compute_v)) - setattr(SelfAdjointEigGradTest, 'testSelfAdjointEigGrad_' + name, - _GetSelfAdjointEigGradTest(dtype, shape, compute_v)) + name = "%s_%s_%s" % (dtype, "_".join(map(str, shape)), compute_v) + _AddTest(SelfAdjointEigTest, "SelfAdjointEig", name, + _GetSelfAdjointEigTest(dtype, shape, compute_v)) + _AddTest(SelfAdjointEigGradTest, "SelfAdjointEigGrad", name, + _GetSelfAdjointEigGradTest(dtype, shape, compute_v)) test.main() diff --git a/tensorflow/python/layers/base.py b/tensorflow/python/layers/base.py index 3db5e4754a0..6dceaecf0f4 100644 --- a/tensorflow/python/layers/base.py +++ b/tensorflow/python/layers/base.py @@ -57,7 +57,8 @@ class Layer(object): Properties: trainable: Whether the layer should be trained (boolean). name: The name of the layer (string). - dtype: Default dtype of the layer (dtypes.float32). + dtype: Default dtype of the layer (default of None means use the + type of the first input). trainable_variables: List of trainable variables. non_trainable_variables: List of non-trainable variables. variables: List of all variables of this layer, trainable and non-trainable. @@ -68,7 +69,7 @@ class Layer(object): """ def __init__(self, trainable=True, name=None, - dtype=dtypes.float32, **kwargs): + dtype=None, **kwargs): # We use a kwargs dict here because these kwargs only exist # for compatibility reasons. # The list of kwargs is subject to changes in the future. @@ -97,7 +98,7 @@ class Layer(object): self._graph = ops.get_default_graph() self._per_input_losses = {} self._per_input_updates = {} - self.dtype = dtypes.as_dtype(dtype).name + self._dtype = None if dtype is None else dtypes.as_dtype(dtype).name self.input_spec = None self._compute_previous_mask = ('mask' in estimator_util.fn_args(self.call) or hasattr(self, 'compute_mask')) @@ -131,6 +132,10 @@ class Layer(object): batch_size = kwargs.get('batch_size') self.batch_input_shape = (batch_size,) + tuple(kwargs['input_shape']) + @property + def dtype(self): + return self._dtype + @property def scope_name(self): if not self._scope: @@ -389,7 +394,7 @@ class Layer(object): Arguments: name: variable name. shape: variable shape. - dtype: The type of the variable. Defaults to `self.dtype`. + dtype: The type of the variable. Defaults to `self.dtype` or `float32`. initializer: initializer instance (callable). regularizer: regularizer instance (callable). trainable: whether the variable should be part of the layer's @@ -414,7 +419,7 @@ class Layer(object): raise RuntimeError('Variable regularization not supported in Eager ' 'mode.') if dtype is None: - dtype = self.dtype + dtype = self.dtype or dtypes.float32 self._set_scope(None) vs_reuse = ((self.built or self._reuse) @@ -526,6 +531,11 @@ class Layer(object): # Check input assumptions set before layer building, e.g. input rank. self._assert_input_compatibility(inputs) input_list = nest.flatten(inputs) + if input_list and self._dtype is None: + try: + self._dtype = input_list[0].dtype.name + except AttributeError: + pass input_shapes = [x.get_shape() for x in input_list] if len(input_shapes) == 1: self.build(input_shapes[0]) @@ -1406,8 +1416,8 @@ class Network(Layer): self.trainable = True # A Network does not create weights of its own, thus it is already built. self.built = True - # A Network does not create weights of its own, thus dtype is not settable. - self.dtype = None + # A Network does not create weights of its own, thus has no dtype. + self._dtype = None # The following are implemented as property functions: # self.trainable_weights # self.non_trainable_weights diff --git a/tensorflow/python/lib/io/file_io_test.py b/tensorflow/python/lib/io/file_io_test.py index 427b2dd1783..a751607aaa1 100644 --- a/tensorflow/python/lib/io/file_io_test.py +++ b/tensorflow/python/lib/io/file_io_test.py @@ -35,6 +35,11 @@ class FileIoTest(test.TestCase): def tearDown(self): file_io.delete_recursively(self._base_dir) + def testEmptyFilename(self): + f = file_io.FileIO("", mode="r") + with self.assertRaises(errors.NotFoundError): + _ = f.read() + def testFileDoesntExist(self): file_path = os.path.join(self._base_dir, "temp_file") self.assertFalse(file_io.file_exists(file_path)) diff --git a/tensorflow/tools/api/golden/tensorflow.keras.estimator.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.estimator.pbtxt new file mode 100644 index 00000000000..7a3fb39f774 --- /dev/null +++ b/tensorflow/tools/api/golden/tensorflow.keras.estimator.pbtxt @@ -0,0 +1,7 @@ +path: "tensorflow.keras.estimator" +tf_module { + member_method { + name: "model_to_estimator" + argspec: "args=[\'keras_model\', \'keras_model_path\', \'custom_objects\', \'model_dir\', \'config\'], varargs=None, keywords=None, defaults=[\'None\', \'None\', \'None\', \'None\', \'None\'], " + } +} diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-activation.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-activation.pbtxt index 52b65bb9163..ed421acda21 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-activation.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-activation.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-activity-regularization.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-activity-regularization.pbtxt index 5ef00eada9f..316c32ee464 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-activity-regularization.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-activity-regularization.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-add.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-add.pbtxt index a75a51a4114..0a0e6ca589b 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-add.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-add.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-alpha-dropout.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-alpha-dropout.pbtxt index 560295eb3ea..2800e265ab8 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-alpha-dropout.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-alpha-dropout.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling1-d.pbtxt index f05a216e952..1ae126eda4d 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling1-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling2-d.pbtxt index 2a71a5a2e64..522841c0689 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling2-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling3-d.pbtxt index 8756b96297a..fe26a18fcfe 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-average-pooling3-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-average.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-average.pbtxt index 9a2940d2982..605bcb37930 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-average.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-average.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool1-d.pbtxt index 62a53b8ab64..1b1b96f45e5 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool1-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool2-d.pbtxt index d4423110877..2378dbfb778 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool2-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool3-d.pbtxt index 812118f3401..34f54c2f2d2 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-avg-pool3-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-batch-normalization.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-batch-normalization.pbtxt index 3aa6a990b66..8ce4f29a7c5 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-batch-normalization.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-batch-normalization.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-bidirectional.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-bidirectional.pbtxt index a0f64a8245d..644ac91842d 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-bidirectional.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-bidirectional.pbtxt @@ -13,6 +13,10 @@ tf_class { name: "constraints" mtype: "" } + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-concatenate.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-concatenate.pbtxt index fe8fc4fd6df..8852492b425 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-concatenate.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-concatenate.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt index a482dec23f8..3004d152dcb 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv-l-s-t-m2-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv1-d.pbtxt index 977a0035bfe..2e502e7cff1 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv1-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv2-d-transpose.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv2-d-transpose.pbtxt index d63c5a23b4a..ecb1d714ba0 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv2-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv2-d-transpose.pbtxt @@ -7,6 +7,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv2-d.pbtxt index 3cc9a2267f8..6d08774d993 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv2-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv3-d-transpose.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv3-d-transpose.pbtxt index 3653eb5b3b1..fc3554d8139 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv3-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv3-d-transpose.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv3-d.pbtxt index e5494449865..60760cb3d74 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-conv3-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution1-d.pbtxt index a8984deb2ba..b9ba19ae983 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution1-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt index bd611432350..815de3bfec6 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution2-d-transpose.pbtxt @@ -7,6 +7,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution2-d.pbtxt index 0a87c40e27f..fa9ff3ff071 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution2-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt index 005cec9748f..c24fe60f819 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution3-d-transpose.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution3-d.pbtxt index caf06b130d7..05ee570f106 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-convolution3-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping1-d.pbtxt index e3287554a6d..3c91a819cdb 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping1-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping2-d.pbtxt index 7aecf7fe339..fdbbbb2ef6e 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping2-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping3-d.pbtxt index a7bd30675b4..38d7d7beec8 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-cropping3-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-dense.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-dense.pbtxt index c502083af82..b9d87481fa4 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-dense.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-dense.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-dot.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-dot.pbtxt index ebc21b01685..a9a5910f624 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-dot.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-dot.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-dropout.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-dropout.pbtxt index 19a8a3cc038..22ad9015549 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-dropout.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-dropout.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-e-l-u.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-e-l-u.pbtxt index 2c8f19068b0..d651a5f5f02 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-e-l-u.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-e-l-u.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-embedding.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-embedding.pbtxt index e5a9273009e..a18149ea95b 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-embedding.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-embedding.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-flatten.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-flatten.pbtxt index 470d41d1afa..2900f607c7f 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-flatten.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-flatten.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-g-r-u.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-g-r-u.pbtxt index c8cd8faaac2..d67288dc810 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-g-r-u.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-g-r-u.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-gaussian-dropout.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-gaussian-dropout.pbtxt index 98c8b96719e..b6c9cb9f7d3 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-gaussian-dropout.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-gaussian-dropout.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-gaussian-noise.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-gaussian-noise.pbtxt index f961291110d..7e2105a8677 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-gaussian-noise.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-gaussian-noise.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt index e120da36495..09a7b48a766 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling1-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt index 89eb90efd91..1a85a6f0db6 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling2-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt index d6d35c45dff..b12d71ab07e 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-average-pooling3-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt index 3d28cb068ed..30aecf67ce7 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool1-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt index 2bc4297b831..a8ed2d004fc 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool2-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt index 83de1acdcf2..3254e1d86d9 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-avg-pool3-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool1-d.pbtxt index 58dee9406c0..d34790f3c1e 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool1-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool2-d.pbtxt index 6490cd4b59c..d2b1a898582 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool2-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool3-d.pbtxt index 15e1a609f3d..be15d56e1a4 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pool3-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt index 4a795aa6633..efd6f18dcdb 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling1-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt index dab26b5627b..15c20c6845c 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling2-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt index cbe05ed7a47..a000b0cdbfa 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-global-max-pooling3-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-input-layer.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-input-layer.pbtxt index b3f81cc4595..f457f7bcc2c 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-input-layer.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-input-layer.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-l-s-t-m.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-l-s-t-m.pbtxt index 36a7e4a176c..9e92d1cf396 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-l-s-t-m.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-l-s-t-m.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-lambda.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-lambda.pbtxt index 1d62867eb4b..c63fe1b391b 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-lambda.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-lambda.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-layer.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-layer.pbtxt index 7326d87cda9..3e12d41bf19 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-layer.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-layer.pbtxt @@ -3,6 +3,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-leaky-re-l-u.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-leaky-re-l-u.pbtxt index 6a0c72ecdf0..8435fdeadac 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-leaky-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-leaky-re-l-u.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-locally-connected1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-locally-connected1-d.pbtxt index a8338314b82..64611425233 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-locally-connected1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-locally-connected1-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-locally-connected2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-locally-connected2-d.pbtxt index a74f1a7c2ae..bb0d9cd46ea 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-locally-connected2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-locally-connected2-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-masking.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-masking.pbtxt index 8c5d9b0fc99..e4e94db6a51 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-masking.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-masking.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool1-d.pbtxt index 0d1998dff60..9aa3f21924c 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool1-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool2-d.pbtxt index 4858920ea72..101977680e3 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool2-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool3-d.pbtxt index 57df6727cf3..e9df31906ce 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pool3-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling1-d.pbtxt index 5ddc879399a..37f3a69a3bc 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling1-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling2-d.pbtxt index b8186c15f39..f98215fee4a 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling2-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling3-d.pbtxt index 16fe3372f77..7457c643d6f 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-max-pooling3-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-maximum.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-maximum.pbtxt index baeb3d83538..28d753091d5 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-maximum.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-maximum.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-multiply.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-multiply.pbtxt index 5c1d511cf72..4791e14a4cf 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-multiply.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-multiply.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-p-re-l-u.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-p-re-l-u.pbtxt index a8f938cc6e5..69be0788265 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-p-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-p-re-l-u.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-permute.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-permute.pbtxt index eac826b965f..ba2ce08f024 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-permute.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-permute.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-repeat-vector.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-repeat-vector.pbtxt index dfae244356f..96a67a77845 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-repeat-vector.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-repeat-vector.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-reshape.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-reshape.pbtxt index 5c8192b2260..936aeb0b054 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-reshape.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-reshape.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-separable-conv2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-separable-conv2-d.pbtxt index 3da1d840600..26199d8f8eb 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-separable-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-separable-conv2-d.pbtxt @@ -7,6 +7,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-separable-convolution2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-separable-convolution2-d.pbtxt index 4b593c19c7a..b9ab38420c3 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-separable-convolution2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-separable-convolution2-d.pbtxt @@ -7,6 +7,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-simple-r-n-n.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-simple-r-n-n.pbtxt index 86203222309..4ec3a67da17 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-simple-r-n-n.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-simple-r-n-n.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt index 156943a201a..2e979b26cca 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout1-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt index 5368b5468ab..1b18015a8d2 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout2-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt index 568b5ad66ec..40cc8622689 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-spatial-dropout3-d.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt index 445f2df59d7..b9eb99a092c 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-thresholded-re-l-u.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-time-distributed.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-time-distributed.pbtxt index b6ebf02b2a4..8290d222e59 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-time-distributed.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-time-distributed.pbtxt @@ -13,6 +13,10 @@ tf_class { name: "constraints" mtype: "" } + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling1-d.pbtxt index 868805a563b..eb15f3e3604 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling1-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling2-d.pbtxt index caa85afa151..143b01ba895 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling2-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling3-d.pbtxt index d3362faefa3..98085515eae 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-up-sampling3-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-wrapper.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-wrapper.pbtxt index ede827f4ecb..91f540524e0 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-wrapper.pbtxt @@ -12,6 +12,10 @@ tf_class { name: "constraints" mtype: "" } + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding1-d.pbtxt index 3472bb45140..db1bdd8dc4e 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding1-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding2-d.pbtxt index 5af56bd135c..a3428f0d178 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding2-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding3-d.pbtxt index 1caf07fedcb..17af1f07501 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.layers.-zero-padding3-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.models.-model.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.models.-model.pbtxt index ade551d02a9..5114bb0d1f4 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.models.-model.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.models.-model.pbtxt @@ -6,6 +6,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.models.-sequential.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.models.-sequential.pbtxt index cadd74eb5ff..df1eeb8bbd7 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.models.-sequential.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.models.-sequential.pbtxt @@ -7,6 +7,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.keras.pbtxt b/tensorflow/tools/api/golden/tensorflow.keras.pbtxt index b198bde7afe..77cfe33ac47 100644 --- a/tensorflow/tools/api/golden/tensorflow.keras.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.keras.pbtxt @@ -24,6 +24,10 @@ tf_module { name: "datasets" mtype: "" } + member { + name: "estimator" + mtype: "" + } member { name: "initializers" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling1-d.pbtxt index d6e4987cb97..5af92daef3d 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling1-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling2-d.pbtxt index 4cc4d15bc1d..cd5fa9650c0 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling2-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling3-d.pbtxt index 2849c25a1e0..f846eca16e7 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-average-pooling3-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-batch-normalization.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-batch-normalization.pbtxt index 90e553d24c1..67d945a6ed6 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-batch-normalization.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-batch-normalization.pbtxt @@ -3,6 +3,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-conv1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-conv1-d.pbtxt index c6aa0ee8e29..800b034d813 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-conv1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-conv1-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-conv2-d-transpose.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-conv2-d-transpose.pbtxt index 474415dd079..e3069daa030 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-conv2-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-conv2-d-transpose.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-conv2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-conv2-d.pbtxt index 4697649d120..587d3666546 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-conv2-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-conv3-d-transpose.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-conv3-d-transpose.pbtxt index c743177a163..e7d99b4ec09 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-conv3-d-transpose.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-conv3-d-transpose.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-conv3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-conv3-d.pbtxt index 81ce14139a6..557cf795760 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-conv3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-conv3-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-dense.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-dense.pbtxt index 43fcc931063..f6fead6c1b6 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-dense.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-dense.pbtxt @@ -3,6 +3,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-dropout.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-dropout.pbtxt index d57e69b97c9..5974365539a 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-dropout.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-dropout.pbtxt @@ -3,6 +3,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-flatten.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-flatten.pbtxt index 9995d20024b..cdb80e5acb9 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-flatten.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-flatten.pbtxt @@ -3,6 +3,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-layer.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-layer.pbtxt index de22c7cf47b..23067f63149 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-layer.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-layer.pbtxt @@ -2,6 +2,10 @@ path: "tensorflow.layers.Layer" tf_class { is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" @@ -60,7 +64,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'trainable\', \'name\', \'dtype\'], varargs=None, keywords=kwargs, defaults=[\'True\', \'None\', \"\"], " + argspec: "args=[\'self\', \'trainable\', \'name\', \'dtype\'], varargs=None, keywords=kwargs, defaults=[\'True\', \'None\', \'None\'], " } member_method { name: "add_loss" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling1-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling1-d.pbtxt index ad4739fbd63..82a68b4eb60 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling1-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling1-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling2-d.pbtxt index c34b3035147..6cde8f2f507 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling2-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling3-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling3-d.pbtxt index 21c99fbf1ab..10bb34ad060 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling3-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-max-pooling3-d.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-network.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-network.pbtxt index 2fb39dda324..8fd8aae231c 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-network.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-network.pbtxt @@ -3,6 +3,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.layers.-separable-conv2-d.pbtxt b/tensorflow/tools/api/golden/tensorflow.layers.-separable-conv2-d.pbtxt index 198eb0296f4..d44b19407b8 100644 --- a/tensorflow/tools/api/golden/tensorflow.layers.-separable-conv2-d.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.layers.-separable-conv2-d.pbtxt @@ -5,6 +5,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt index 49319154057..ed455937fc2 100644 --- a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt index 42a8e3887bc..fce1230c2a5 100644 --- a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt index 0880f932a0d..8b157db33ff 100644 --- a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-device-wrapper.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt index f284c6715f6..dbea51cce37 100644 --- a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-dropout-wrapper.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt index dbca7503a4a..e4d2ca6db4b 100644 --- a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt index 61126c0db9a..8b1b44337b1 100644 --- a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt index dd0cd8445ce..c4634570e70 100644 --- a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" diff --git a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt index 85d6f0ec609..a1409249f83 100644 --- a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt @@ -3,6 +3,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: "" @@ -69,7 +73,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'trainable\', \'name\', \'dtype\'], varargs=None, keywords=kwargs, defaults=[\'True\', \'None\', \"\"], " + argspec: "args=[\'self\', \'trainable\', \'name\', \'dtype\'], varargs=None, keywords=kwargs, defaults=[\'True\', \'None\', \'None\'], " } member_method { name: "add_loss" diff --git a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt index 308ce841654..0e3a26b8c64 100644 --- a/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt +++ b/tensorflow/tools/api/golden/tensorflow.nn.rnn_cell.-residual-wrapper.pbtxt @@ -4,6 +4,10 @@ tf_class { is_instance: "" is_instance: "" is_instance: "" + member { + name: "dtype" + mtype: "" + } member { name: "graph" mtype: ""