From e3413de529c3f762885efd62932f76445ed22653 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 11:34:01 -0700 Subject: [PATCH 01/26] Add GPU support for self_adjoint_eig a.k.a. tf.linalg.eigh. Clean up macros and template specializations in cuda_solvers.cc a bit. PiperOrigin-RevId: 169715681 --- tensorflow/core/kernels/BUILD | 7 +- tensorflow/core/kernels/cuda_solvers.cc | 284 +++++++++--------- tensorflow/core/kernels/cuda_solvers.h | 37 ++- tensorflow/core/kernels/qr_op_impl.h | 8 +- .../kernels/self_adjoint_eig_v2_op_gpu.cc | 194 ++++++++++++ .../kernel_tests/self_adjoint_eig_op_test.py | 54 ++-- 6 files changed, 393 insertions(+), 191 deletions(-) create mode 100644 tensorflow/core/kernels/self_adjoint_eig_v2_op_gpu.cc diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index 2623d072586..3f14fc9d1d8 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( 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<Scalar>( \ - 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<Scalar>( \ + 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<Scalar>(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<Scalar>(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<Scalar>( \ 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 <typename Scalar, typename BufSizeFnT, typename SolverFnT> -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<Scalar> 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<Scalar>( \ - 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 <typename Scalar, typename BufSizeFnT, typename SolverFnT> 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<Scalar>(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 <typename Scalar, typename BufSizeFnT, typename SolverFnT> -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<float>* dev_a, - int lda, const std::complex<float>* dev_tau, - std::complex<float>* 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<double>* dev_a, - int lda, const std::complex<double>* dev_tau, - std::complex<double>* 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 <typename Scalar, typename BufSizeFnT, typename SolverFnT> -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 <typename Scalar, typename BufSizeFnT, typename SolverFnT> +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<Scalar>::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<Scalar> 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<float>* dev_a, - int lda, const std::complex<float>* 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<double>* dev_a, - int lda, const std::complex<double>* 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<Scalar>::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 <typename Scalar, typename BufSizeFnT, typename SolverFnT> +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<Scalar> 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<Scalar>( \ + 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 <typename Scalar> - 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 <typename Scalar> - 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 <typename Scalar> + Status Heevd(cusolverEigMode_t jobz, cublasFillMode_t uplo, int n, + Scalar* dev_A, int lda, + typename Eigen::NumTraits<Scalar>::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 <typename Scalar> - 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/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<Scalar, 3>(); 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<Scalar>(), m, m, + solver.Unmqr(CUBLAS_SIDE_LEFT, CublasAdjointOp<Scalar>(), 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 <numeric> +#include <type_traits> + +#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 Scalar> +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<Scalar, 3>().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<Scalar>::Real; + if (std::is_same<Scalar, RealScalar>::value) { + eigenvalues_real = *eigenvalues; + } else { + OP_REQUIRES_OK_ASYNC( + context, + context->allocate_temp(DataTypeToEnum<RealScalar>::value, + eigenvalues_shape, &eigenvalues_real), + done); + } + + Tensor input_copy; + OP_REQUIRES_OK_ASYNC( + context, + context->forward_input_or_allocate_temp( + {0}, DataTypeToEnum<Scalar>::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<GPUDevice>(); + if (!input.SharesBufferWith(input_copy)) { + if (Eigen::NumTraits<Scalar>::IsComplex) { + functor::UnaryFunctor<GPUDevice, functor::conj<Scalar>> conj; + conj(device, input_copy.flat<Scalar>() /*out*/, + input.flat<Scalar>() /*in*/); + } else { + device.memcpy(input_copy.flat<Scalar>().data(), + input.flat<Scalar>().data(), + input.NumElements() * sizeof(Scalar)); + } + } else if (Eigen::NumTraits<Scalar>::IsComplex) { + functor::UnaryFunctor<GPUDevice, functor::conj<Scalar>> conj; + conj(device, const_cast<Tensor*>(&input)->flat<Scalar>() /*out*/, + input.flat<Scalar>() /*in*/); + } + + // Compute eigen decomposition in-place in input_copy. + CudaSolver solver(context); + std::vector<DeviceLapackInfo> dev_info; + dev_info.emplace_back(context, batch_size, "heevd"); + auto input_copy_reshaped = input_copy.flat_inner_dims<Scalar, 3>(); + auto eigenvalues_real_reshaped = + eigenvalues_real.flat_inner_dims<RealScalar, 2>(); + 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<Scalar, RealScalar>::value) { + functor::CastFunctor<GPUDevice, Scalar, RealScalar> cast; + cast(device, eigenvalues->flat<Scalar>(), + const_cast<const Tensor*>(&eigenvalues_real)->flat<RealScalar>()); + } + + if (compute_v_) { + // Transpose eigenvectors now stored in input_copy in column-major form to + // output in row-major form. + std::vector<int> 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<HostLapackInfo>& 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<Scalar>("T"), \ + (SelfAdjointEigV2OpGpu<Scalar>)) + +REGISTER(float); +REGISTER(double); +REGISTER(complex64); +REGISTER(complex128); + +#undef REGISTER + +} // namespace tensorflow + +#endif // GOOGLE_CUDA 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() From 3b9d327f0613d99757d6cc98e8370788b3c7dbc9 Mon Sep 17 00:00:00 2001 From: Tayo Oguntebi <tayo@google.com> Date: Fri, 22 Sep 2017 11:38:53 -0700 Subject: [PATCH 02/26] Adds test for six-dimensional pooling case. PiperOrigin-RevId: 169716336 --- .../compiler/xla/tests/reduce_window_test.cc | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) 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<int64> input_dims(6, 8); + auto shape = ShapeUtil::MakeShape(F32, input_dims); + + std::unique_ptr<Literal> arg_literal = Literal::CreateFromShape(shape); + auto generator = [&](tensorflow::gtl::ArraySlice<int64> indexes) -> float { + return 1.0f; + }; + TF_EXPECT_OK(arg_literal->Populate<float>(generator)); + + auto input = + b.AddInstruction(HloInstruction::CreateConstant(std::move(arg_literal))); + + auto init_value = b.AddInstruction( + HloInstruction::CreateConstant(Literal::CreateR0<float>(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<int64> output_layout = {1, 5, 3, 2, 0, 4}; + std::vector<int64> 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<Literal> expected = Literal::CreateFromShape(result_shape); + auto out_generator = + [&](tensorflow::gtl::ArraySlice<int64> indexes) -> float { + return 27.0f; + }; + TF_EXPECT_OK(expected->Populate<float>(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()); From 90d284fbf7ffb0a329744006d244fcd964092a57 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 11:55:47 -0700 Subject: [PATCH 03/26] Changes for TPU ops. PiperOrigin-RevId: 169718641 --- tensorflow/compiler/xla/array2d.h | 16 ++++++++++ tensorflow/compiler/xla/array2d_test.cc | 41 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+) 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<T>& 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<T>& 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<int> arr0 = {{1, 2}, {3, 4}, {5, 6}}; + Array2D<int> arr1 = {{1, 2}, {3, 4}, {5, 6}}; + EXPECT_TRUE(arr0 == arr1); + EXPECT_FALSE(arr0 != arr1); + EXPECT_TRUE(arr1 == arr0); + EXPECT_FALSE(arr1 != arr0); + + Array2D<int> 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<int> arr3 = {{1, 2, 3}, {4, 5, 6}}; + EXPECT_TRUE(arr0 != arr3); + EXPECT_FALSE(arr0 == arr3); + EXPECT_TRUE(arr3 != arr0); + EXPECT_FALSE(arr3 == arr0); + + Array2D<int> arr4 = {{1, 2}, {3, 4}}; + EXPECT_TRUE(arr0 != arr4); + EXPECT_FALSE(arr0 == arr4); + EXPECT_TRUE(arr4 != arr0); + EXPECT_FALSE(arr4 == arr0); + + Array2D<int> 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> bool_arr0 = {{false}, {true}}; + Array2D<bool> bool_arr1 = {{false}, {true}}; + EXPECT_TRUE(bool_arr0 == bool_arr1); + EXPECT_FALSE(bool_arr0 != bool_arr1); + Array2D<bool> bool_arr2 = {{false}, {false}}; + EXPECT_FALSE(bool_arr0 == bool_arr2); + EXPECT_TRUE(bool_arr0 != bool_arr2); +} + } // namespace } // namespace xla From d9bd87b17c87959938273ef5167e161882be2be4 Mon Sep 17 00:00:00 2001 From: Max Galkin <maxgalkin@google.com> Date: Fri, 22 Sep 2017 11:55:51 -0700 Subject: [PATCH 04/26] Fix tensorflow::Scope documentation: angle brackets in comments don't play well with HTML docs. See: https://www.tensorflow.org/api_docs/cc/class/tensorflow/scope#classtensorflow_1_1_scope_1a726021aa3104fec02353e8713f1e5b63 PiperOrigin-RevId: 169718650 --- tensorflow/cc/framework/scope.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 - /// <name>/<child_scope_name> 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 <name>/<op_name>[_<suffix]. + /// names of the form `name/op_name[_suffix]`. Scope WithOpName(const string& op_name) const; /// Return a new scope. All ops created within the returned scope will have as From cef42c64b3dccc45fadfca651ef895a966129d44 Mon Sep 17 00:00:00 2001 From: Rui Zhao <rzhao@google.com> Date: Fri, 22 Sep 2017 12:12:04 -0700 Subject: [PATCH 05/26] Register int types for one_hot GPU ops. Update beam_search_decoder internal int32 types to int64 in order to compute length penalty on GPU. This speedup the NMT beam search decoding (beam_width=10) with length penalty by ~6.8 times. PiperOrigin-RevId: 169720804 --- .../kernel_tests/beam_search_decoder_test.py | 4 ++-- .../seq2seq/python/ops/beam_search_decoder.py | 16 +++++++++------- tensorflow/core/kernels/one_hot_op.cc | 4 ++++ tensorflow/core/kernels/one_hot_op_gpu.cu.cc | 2 ++ 4 files changed, 17 insertions(+), 9 deletions(-) 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/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 From 522db88624f39946f66c3f4dff07c33cc4499311 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 12:12:37 -0700 Subject: [PATCH 06/26] Internal Change PiperOrigin-RevId: 169720878 --- tensorflow/contrib/tpu/BUILD | 1 + 1 file changed, 1 insertion(+) 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__", ], From 0f0ea131ada593b6b8698eba621a11ddca4473ad Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 12:29:21 -0700 Subject: [PATCH 07/26] Set FusedConv2DBiasActivation kernel to visibility=public. PiperOrigin-RevId: 169722662 --- tensorflow/contrib/fused_conv/BUILD | 1 + 1 file changed, 1 insertion(+) 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", From 7a3345d4d5251f160ca1ea094ddcc7e0c3aca428 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 12:55:49 -0700 Subject: [PATCH 08/26] Bugfix: don't force dummy global step to be int64. PiperOrigin-RevId: 169725538 --- tensorflow/contrib/gan/BUILD | 1 - tensorflow/contrib/gan/python/train.py | 5 ++--- tensorflow/contrib/gan/python/train_test.py | 11 ++++++++++- 3 files changed, 12 insertions(+), 5 deletions(-) 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`.""" From 580624a1100560c62cd2629ed331656fcfb1d295 Mon Sep 17 00:00:00 2001 From: Yifei Feng <yifeif@google.com> Date: Fri, 22 Sep 2017 13:21:02 -0700 Subject: [PATCH 09/26] Add keras.estimator.model_to_estimator() that creates an estimator from an existing keras model or model file. PiperOrigin-RevId: 169728595 --- tensorflow/contrib/cmake/tf_python.cmake | 1 + tensorflow/python/keras/BUILD | 22 + tensorflow/python/keras/__init__.py | 1 + .../python/keras/_impl/keras/__init__.py | 1 + .../python/keras/_impl/keras/backend.py | 37 +- .../python/keras/_impl/keras/estimator.py | 281 +++++++++++++ .../keras/_impl/keras/estimator_test.py | 392 ++++++++++++++++++ tensorflow/python/keras/estimator/__init__.py | 25 ++ .../golden/tensorflow.keras.estimator.pbtxt | 7 + .../tools/api/golden/tensorflow.keras.pbtxt | 4 + 10 files changed, 755 insertions(+), 16 deletions(-) create mode 100644 tensorflow/python/keras/_impl/keras/estimator.py create mode 100644 tensorflow/python/keras/_impl/keras/estimator_test.py create mode 100644 tensorflow/python/keras/estimator/__init__.py create mode 100644 tensorflow/tools/api/golden/tensorflow.keras.estimator.pbtxt diff --git a/tensorflow/contrib/cmake/tf_python.cmake b/tensorflow/contrib/cmake/tf_python.cmake index e033289e0b1..9c6a9a10006 100755 --- a/tensorflow/contrib/cmake/tf_python.cmake +++ b/tensorflow/contrib/cmake/tf_python.cmake @@ -237,6 +237,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") 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/backend.py b/tensorflow/python/keras/_impl/keras/backend.py index 83dd90aba84..05756024ca2 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/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/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.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: "<type \'module\'>" } + member { + name: "estimator" + mtype: "<type \'module\'>" + } member { name: "initializers" mtype: "<type \'module\'>" From b650bf5a32432770a4c2b580464b0b736993a210 Mon Sep 17 00:00:00 2001 From: Rohan Jain <rohanj@google.com> Date: Fri, 22 Sep 2017 13:29:10 -0700 Subject: [PATCH 10/26] Handling empty path names by fixing how TranslateName() works. Currently that calls out to io::CleanPath which converts "" to "." which isn't correct. PiperOrigin-RevId: 169729593 --- tensorflow/core/platform/file_system.cc | 3 +++ tensorflow/python/lib/io/file_io_test.py | 5 +++++ 2 files changed, 8 insertions(+) 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<void(int)>& 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/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)) From 5893f926e8b75a4ef06fe85bb0d8d431df2b56cf Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 13:41:09 -0700 Subject: [PATCH 11/26] Rename xla_local_launch to xla_launch. Use a shared lock to snapshot resource variables. PiperOrigin-RevId: 169731148 --- tensorflow/compiler/jit/BUILD | 14 +++++++------- tensorflow/compiler/jit/create_xla_launch_op.cc | 2 +- tensorflow/compiler/jit/kernels/BUILD | 6 +++--- .../{xla_local_launch_op.cc => xla_launch_op.cc} | 4 ++-- .../{xla_local_launch_op.h => xla_launch_op.h} | 0 tensorflow/compiler/jit/xla_cpu_device.cc | 2 +- tensorflow/compiler/jit/xla_gpu_device.cc | 2 +- tensorflow/compiler/jit/xla_interpreter_device.cc | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) rename tensorflow/compiler/jit/kernels/{xla_local_launch_op.cc => xla_launch_op.cc} (99%) rename tensorflow/compiler/jit/kernels/{xla_local_launch_op.h => xla_launch_op.h} (100%) 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<OptionalTensor> 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" From 83066d45ee670d77349e124eb1258cff7045d4e7 Mon Sep 17 00:00:00 2001 From: Justin Lebar <jlebar@google.com> Date: Fri, 22 Sep 2017 13:46:29 -0700 Subject: [PATCH 12/26] Revamp handling of subcomputations in HLO graph dumper. Before, we relied on a hacky heuristic -- "recurse into nested fusion nodes" -- that didn't work for the case when e.g. a fusion node was nested inside a while loop. This change also adds a (very basic) testcase for the HLO graph dumper. PiperOrigin-RevId: 169731958 --- tensorflow/compiler/xla/service/BUILD | 14 ++ .../compiler/xla/service/hlo_graph_dumper.cc | 100 +++++++------- .../xla/service/hlo_graph_dumper_test.cc | 122 ++++++++++++++++++ 3 files changed, 189 insertions(+), 47 deletions(-) create mode 100644 tensorflow/compiler/xla/service/hlo_graph_dumper_test.cc diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index 7418f2d8b11..b9650175f04 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -1968,6 +1968,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/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<const HloComputation*, const HloInstruction*> - 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<const HloComputation*, const HloInstruction*> -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<const HloComputation*, const HloInstruction*> 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<HloInstruction>& 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<int>::max()); + +TEST(HloGraphDumperTest, NestedFusion) { + HloComputation::Builder b("b"); + + // Build param0 + param1 + param2 + param3 + param4. + auto shape = ShapeUtil::MakeShape(F32, {10, 100}); + std::vector<HloInstruction*> params; + for (int i = 0; i <= 4; ++i) { + params.push_back(b.AddInstruction( + HloInstruction::CreateParameter(i, shape, StrCat("param", i)))); + } + std::vector<HloInstruction*> 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<HloInstruction*> 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<HloInstruction>& 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<HloInstruction>& 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 From 7766611a1939842316997214bc911a80fcee4697 Mon Sep 17 00:00:00 2001 From: Suharsh Sivakumar <suharshs@google.com> Date: Fri, 22 Sep 2017 13:47:41 -0700 Subject: [PATCH 13/26] Fix flakiness issues in c++ nn gradient tests. Changes: - Tolerance was too strict for L2LossGrad. Reducing tolerance to 1e-3 resolved the issue. - Numeric gradient computation perturbation changed the value of the max value in MaxPool and MaxPoolV2 if two values were too close together (thanks Chad!). Resolved this by ensuring that the tests only compute one window on the entire input. Then I ensure that the max value of the random value is max by 1e-2, so that a perturbation can't replace it. - Also some minor formatting fixes in nn_grad.cc PiperOrigin-RevId: 169732110 --- tensorflow/cc/gradients/nn_grad.cc | 6 +-- tensorflow/cc/gradients/nn_grad_test.cc | 54 ++++++++++++++++++------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/tensorflow/cc/gradients/nn_grad.cc b/tensorflow/cc/gradients/nn_grad.cc index fcc3fc9dae9..09fadfcab51 100644 --- a/tensorflow/cc/gradients/nn_grad.cc +++ b/tensorflow/cc/gradients/nn_grad.cc @@ -50,7 +50,6 @@ REGISTER_GRADIENT_OP("Softmax", SoftmaxGrad); Status LogSoftmaxGrad(const Scope& scope, const Operation& op, const std::vector<Output>& grad_inputs, std::vector<Output>* 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<float, float, float>( 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<float, float, float>( 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<TensorShape>& x_shapes, @@ -53,7 +53,25 @@ class NNGradTest : public ::testing::Test { float max_error; TF_ASSERT_OK((ComputeGradientError<float, float, float>( 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 <typename T> + void SetRandomValuesWithBumpedMax(Tensor* tensor) { + auto tensor_flat = tensor->flat<T>(); + 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<int> ksize{1, 2, 2, 1}; - const std::vector<int> strides{1, 1, 1, 1}; - auto y = MaxPool(scope_, x, ksize, strides, "SAME"); - RunTest(x, shape, y, shape); + const std::vector<int> strides{1, 2, 2, 1}; + auto y = MaxPool(scope_, x, ksize, strides, "VALID"); + Tensor x_init_value = Tensor(DT_FLOAT, x_shape); + SetRandomValuesWithBumpedMax<float>(&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<int>({1, 2, 2, 1}, {4}); - Tensor strides = test::AsTensor<int>({1, 1, 1, 1}, {4}); - auto y = MaxPoolV2(scope_, x, ksize, strides, "SAME"); - RunTest(x, shape, y, shape); + Tensor strides = test::AsTensor<int>({1, 2, 2, 1}, {4}); + auto y = MaxPoolV2(scope_, x, ksize, strides, "VALID"); + Tensor x_init_value = Tensor(DT_FLOAT, x_shape); + SetRandomValuesWithBumpedMax<float>(&x_init_value); + RunTest(x, x_init_value, y, y_shape); } - + } // namespace } // namespace tensorflow From 0d864630161d9f3b9eaef0b7c6ce7443654df97a Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 13:48:33 -0700 Subject: [PATCH 14/26] Move GPU-specific dependencies of core/grappler:devices into cuda_deps. Fix #includes and deps of contrib/verbs:verbs_util, in particular removing an unnecessary #include of gpu_util.h that relied on a transitive dependency through :devices. PiperOrigin-RevId: 169732234 --- tensorflow/contrib/verbs/BUILD | 3 --- tensorflow/contrib/verbs/verbs_util.cc | 8 ++++++-- tensorflow/contrib/verbs/verbs_util.h | 6 +----- tensorflow/core/grappler/BUILD | 6 ++++-- 4 files changed, 11 insertions(+), 12 deletions(-) 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 <vector> + +#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 <string> -#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", ], ) From 9d526b136b37e1b690d857aa69fe8824b0fce1a8 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 13:55:51 -0700 Subject: [PATCH 15/26] Remove an unnecessary dependency from core/util/ctc:ctc_beam_search_lib to core:gpu_runtime. PiperOrigin-RevId: 169733352 --- tensorflow/core/util/ctc/BUILD | 1 - 1 file changed, 1 deletion(-) 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", From c5223bee239db1ec8a2837d845aab67680619653 Mon Sep 17 00:00:00 2001 From: Vijay Vasudevan <vrv@google.com> Date: Fri, 22 Sep 2017 14:49:52 -0700 Subject: [PATCH 16/26] Create XLA computations inside hostname-specific directory. In a distributed environment, one would like to be able to separate XLA computations based on the binary that produced them, rather than having all computations serialized to a single directory. PiperOrigin-RevId: 169740827 --- tensorflow/compiler/xla/service/BUILD | 1 + tensorflow/compiler/xla/service/compile_only_service.cc | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tensorflow/compiler/xla/service/BUILD b/tensorflow/compiler/xla/service/BUILD index b9650175f04..223916adb24 100644 --- a/tensorflow/compiler/xla/service/BUILD +++ b/tensorflow/compiler/xla/service/BUILD @@ -505,6 +505,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", ], ) 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)); } From 0fbd0dc540e244bbaff1c163468ce6527cd652be Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 15:06:32 -0700 Subject: [PATCH 17/26] Change kernel ops to depend on core:core_cpu rather than :core to avoid unnecessarily pulling in the GPU runtime when it is not used. PiperOrigin-RevId: 169743241 --- tensorflow/core/kernels/BUILD | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index 3f14fc9d1d8..93e28bfb38a 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -4016,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", @@ -4807,8 +4806,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", From dcb3722e61533d4667502b89b27c953846c20ca5 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 15:26:16 -0700 Subject: [PATCH 18/26] Move of Dataset API to tf.data. This also modifies semantics of `dataset.enumerate()`, `dataset.dense_to_sparse_batch()`, `dataset.ignore_errors()` and `dataset.unbatch()`; which now return a transformation function from `Dataset` to `Dataset`. Further API `tf.contrib.data.batch_and_drop_remainder()`, `tf.contrib.data.dense_to_sparse_batch()`, `tf.contrib.data.enumerate_dataset()`, `tf.contrib.data.group_by_window()`, `tf.contrib.data.ignore_errors()`, `tf.contrib.data.read_batch_features()`, `tf.contrib.data.sloppy_interleave()` and `tf.contrib.data.unbatch()` did not move to tf.data. PiperOrigin-RevId: 169746213 --- tensorflow/BUILD | 4 +- tensorflow/contrib/cmake/tf_python.cmake | 4 +- tensorflow/contrib/data/BUILD | 1 + tensorflow/contrib/data/__init__.py | 15 +- .../contrib/data/python/kernel_tests/BUILD | 14 +- .../kernel_tests/batch_dataset_op_test.py | 16 +- .../concatenate_dataset_op_test.py | 2 +- .../dataset_constructor_op_test.py | 2 +- .../kernel_tests/map_dataset_op_test.py | 18 +- .../kernel_tests/range_dataset_op_test.py | 4 +- tensorflow/contrib/data/python/ops/BUILD | 10 +- .../contrib/data/python/ops/dataset_ops.py | 2038 ++++------------- .../contrib/data/python/ops/sloppy_ops.py | 4 +- tensorflow/contrib/eager/python/BUILD | 3 +- tensorflow/contrib/eager/python/datasets.py | 2 +- tensorflow/python/BUILD | 14 +- tensorflow/python/data/BUILD | 27 + tensorflow/python/data/__init__.py | 37 + tensorflow/python/data/ops/BUILD | 38 + tensorflow/python/data/ops/dataset_ops.py | 2032 ++++++++++++++++ .../data/python => python/data}/util/BUILD | 0 .../data/python => python/data}/util/nest.py | 1 + .../python => python/data}/util/nest_test.py | 2 +- 23 files changed, 2627 insertions(+), 1661 deletions(-) create mode 100644 tensorflow/python/data/BUILD create mode 100644 tensorflow/python/data/__init__.py create mode 100644 tensorflow/python/data/ops/BUILD create mode 100644 tensorflow/python/data/ops/dataset_ops.py rename tensorflow/{contrib/data/python => python/data}/util/BUILD (100%) rename tensorflow/{contrib/data/python => python/data}/util/nest.py (99%) rename tensorflow/{contrib/data/python => python/data}/util/nest_test.py (99%) diff --git a/tensorflow/BUILD b/tensorflow/BUILD index 19ca404ad52..4054318c4c7 100644 --- a/tensorflow/BUILD +++ b/tensorflow/BUILD @@ -348,7 +348,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", @@ -467,6 +466,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/contrib/cmake/tf_python.cmake b/tensorflow/contrib/cmake/tf_python.cmake index 9c6a9a10006..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") @@ -334,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/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 From 4e766010263c051a7d32817f08be14323715fe9c Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 15:39:31 -0700 Subject: [PATCH 19/26] Align the cached value in FlatMap's iterator. This may be misaligned for types requiring >=8-byte alignment (like pointer types). PiperOrigin-RevId: 169747894 --- tensorflow/core/lib/gtl/flatmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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<pointer>(space_); } void FillValue() { new (space_) value_type(b_->key(i_), b_->val(i_)); } From 592697b4d2dd161893a126b6237261f156c92708 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" <gardener@tensorflow.org> Date: Fri, 22 Sep 2017 16:18:06 -0700 Subject: [PATCH 20/26] Automatically infer Layer.dtype from the first input if it is not set. PiperOrigin-RevId: 169752318 --- tensorflow/python/keras/_impl/keras/models.py | 3 +++ tensorflow/python/layers/base.py | 24 +++++++++++++------ .../tensorflow.keras.layers.-activation.pbtxt | 4 ++++ ...eras.layers.-activity-regularization.pbtxt | 4 ++++ .../golden/tensorflow.keras.layers.-add.pbtxt | 4 ++++ ...nsorflow.keras.layers.-alpha-dropout.pbtxt | 4 ++++ ...low.keras.layers.-average-pooling1-d.pbtxt | 4 ++++ ...low.keras.layers.-average-pooling2-d.pbtxt | 4 ++++ ...low.keras.layers.-average-pooling3-d.pbtxt | 4 ++++ .../tensorflow.keras.layers.-average.pbtxt | 4 ++++ ...tensorflow.keras.layers.-avg-pool1-d.pbtxt | 4 ++++ ...tensorflow.keras.layers.-avg-pool2-d.pbtxt | 4 ++++ ...tensorflow.keras.layers.-avg-pool3-d.pbtxt | 4 ++++ ...ow.keras.layers.-batch-normalization.pbtxt | 4 ++++ ...nsorflow.keras.layers.-bidirectional.pbtxt | 4 ++++ ...tensorflow.keras.layers.-concatenate.pbtxt | 4 ++++ ...orflow.keras.layers.-conv-l-s-t-m2-d.pbtxt | 4 ++++ .../tensorflow.keras.layers.-conv1-d.pbtxt | 4 ++++ ...flow.keras.layers.-conv2-d-transpose.pbtxt | 4 ++++ .../tensorflow.keras.layers.-conv2-d.pbtxt | 4 ++++ ...flow.keras.layers.-conv3-d-transpose.pbtxt | 4 ++++ .../tensorflow.keras.layers.-conv3-d.pbtxt | 4 ++++ ...sorflow.keras.layers.-convolution1-d.pbtxt | 4 ++++ ...ras.layers.-convolution2-d-transpose.pbtxt | 4 ++++ ...sorflow.keras.layers.-convolution2-d.pbtxt | 4 ++++ ...ras.layers.-convolution3-d-transpose.pbtxt | 4 ++++ ...sorflow.keras.layers.-convolution3-d.pbtxt | 4 ++++ ...tensorflow.keras.layers.-cropping1-d.pbtxt | 4 ++++ ...tensorflow.keras.layers.-cropping2-d.pbtxt | 4 ++++ ...tensorflow.keras.layers.-cropping3-d.pbtxt | 4 ++++ .../tensorflow.keras.layers.-dense.pbtxt | 4 ++++ .../golden/tensorflow.keras.layers.-dot.pbtxt | 4 ++++ .../tensorflow.keras.layers.-dropout.pbtxt | 4 ++++ .../tensorflow.keras.layers.-e-l-u.pbtxt | 4 ++++ .../tensorflow.keras.layers.-embedding.pbtxt | 4 ++++ .../tensorflow.keras.layers.-flatten.pbtxt | 4 ++++ .../tensorflow.keras.layers.-g-r-u.pbtxt | 4 ++++ ...rflow.keras.layers.-gaussian-dropout.pbtxt | 4 ++++ ...sorflow.keras.layers.-gaussian-noise.pbtxt | 4 ++++ ...as.layers.-global-average-pooling1-d.pbtxt | 4 ++++ ...as.layers.-global-average-pooling2-d.pbtxt | 4 ++++ ...as.layers.-global-average-pooling3-d.pbtxt | 4 ++++ ...low.keras.layers.-global-avg-pool1-d.pbtxt | 4 ++++ ...low.keras.layers.-global-avg-pool2-d.pbtxt | 4 ++++ ...low.keras.layers.-global-avg-pool3-d.pbtxt | 4 ++++ ...low.keras.layers.-global-max-pool1-d.pbtxt | 4 ++++ ...low.keras.layers.-global-max-pool2-d.pbtxt | 4 ++++ ...low.keras.layers.-global-max-pool3-d.pbtxt | 4 ++++ ....keras.layers.-global-max-pooling1-d.pbtxt | 4 ++++ ....keras.layers.-global-max-pooling2-d.pbtxt | 4 ++++ ....keras.layers.-global-max-pooling3-d.pbtxt | 4 ++++ ...tensorflow.keras.layers.-input-layer.pbtxt | 4 ++++ .../tensorflow.keras.layers.-l-s-t-m.pbtxt | 4 ++++ .../tensorflow.keras.layers.-lambda.pbtxt | 4 ++++ .../tensorflow.keras.layers.-layer.pbtxt | 4 ++++ ...ensorflow.keras.layers.-leaky-re-l-u.pbtxt | 4 ++++ ...w.keras.layers.-locally-connected1-d.pbtxt | 4 ++++ ...w.keras.layers.-locally-connected2-d.pbtxt | 4 ++++ .../tensorflow.keras.layers.-masking.pbtxt | 4 ++++ ...tensorflow.keras.layers.-max-pool1-d.pbtxt | 4 ++++ ...tensorflow.keras.layers.-max-pool2-d.pbtxt | 4 ++++ ...tensorflow.keras.layers.-max-pool3-d.pbtxt | 4 ++++ ...sorflow.keras.layers.-max-pooling1-d.pbtxt | 4 ++++ ...sorflow.keras.layers.-max-pooling2-d.pbtxt | 4 ++++ ...sorflow.keras.layers.-max-pooling3-d.pbtxt | 4 ++++ .../tensorflow.keras.layers.-maximum.pbtxt | 4 ++++ .../tensorflow.keras.layers.-multiply.pbtxt | 4 ++++ .../tensorflow.keras.layers.-p-re-l-u.pbtxt | 4 ++++ .../tensorflow.keras.layers.-permute.pbtxt | 4 ++++ ...nsorflow.keras.layers.-repeat-vector.pbtxt | 4 ++++ .../tensorflow.keras.layers.-reshape.pbtxt | 4 ++++ ...flow.keras.layers.-separable-conv2-d.pbtxt | 4 ++++ ...ras.layers.-separable-convolution2-d.pbtxt | 4 ++++ ...ensorflow.keras.layers.-simple-r-n-n.pbtxt | 4 ++++ ...low.keras.layers.-spatial-dropout1-d.pbtxt | 4 ++++ ...low.keras.layers.-spatial-dropout2-d.pbtxt | 4 ++++ ...low.keras.layers.-spatial-dropout3-d.pbtxt | 4 ++++ ...low.keras.layers.-thresholded-re-l-u.pbtxt | 4 ++++ ...rflow.keras.layers.-time-distributed.pbtxt | 4 ++++ ...sorflow.keras.layers.-up-sampling1-d.pbtxt | 4 ++++ ...sorflow.keras.layers.-up-sampling2-d.pbtxt | 4 ++++ ...sorflow.keras.layers.-up-sampling3-d.pbtxt | 4 ++++ .../tensorflow.keras.layers.-wrapper.pbtxt | 4 ++++ ...orflow.keras.layers.-zero-padding1-d.pbtxt | 4 ++++ ...orflow.keras.layers.-zero-padding2-d.pbtxt | 4 ++++ ...orflow.keras.layers.-zero-padding3-d.pbtxt | 4 ++++ .../tensorflow.keras.models.-model.pbtxt | 4 ++++ .../tensorflow.keras.models.-sequential.pbtxt | 4 ++++ ...ensorflow.layers.-average-pooling1-d.pbtxt | 4 ++++ ...ensorflow.layers.-average-pooling2-d.pbtxt | 4 ++++ ...ensorflow.layers.-average-pooling3-d.pbtxt | 4 ++++ ...nsorflow.layers.-batch-normalization.pbtxt | 4 ++++ .../golden/tensorflow.layers.-conv1-d.pbtxt | 4 ++++ ...tensorflow.layers.-conv2-d-transpose.pbtxt | 4 ++++ .../golden/tensorflow.layers.-conv2-d.pbtxt | 4 ++++ ...tensorflow.layers.-conv3-d-transpose.pbtxt | 4 ++++ .../golden/tensorflow.layers.-conv3-d.pbtxt | 4 ++++ .../api/golden/tensorflow.layers.-dense.pbtxt | 4 ++++ .../golden/tensorflow.layers.-dropout.pbtxt | 4 ++++ .../golden/tensorflow.layers.-flatten.pbtxt | 4 ++++ .../api/golden/tensorflow.layers.-layer.pbtxt | 6 ++++- .../tensorflow.layers.-max-pooling1-d.pbtxt | 4 ++++ .../tensorflow.layers.-max-pooling2-d.pbtxt | 4 ++++ .../tensorflow.layers.-max-pooling3-d.pbtxt | 4 ++++ .../golden/tensorflow.layers.-network.pbtxt | 4 ++++ ...tensorflow.layers.-separable-conv2-d.pbtxt | 4 ++++ ...flow.nn.rnn_cell.-basic-l-s-t-m-cell.pbtxt | 4 ++++ ...orflow.nn.rnn_cell.-basic-r-n-n-cell.pbtxt | 4 ++++ ...nsorflow.nn.rnn_cell.-device-wrapper.pbtxt | 4 ++++ ...sorflow.nn.rnn_cell.-dropout-wrapper.pbtxt | 4 ++++ .../tensorflow.nn.rnn_cell.-g-r-u-cell.pbtxt | 4 ++++ ...tensorflow.nn.rnn_cell.-l-s-t-m-cell.pbtxt | 4 ++++ ...orflow.nn.rnn_cell.-multi-r-n-n-cell.pbtxt | 4 ++++ .../tensorflow.nn.rnn_cell.-r-n-n-cell.pbtxt | 6 ++++- ...orflow.nn.rnn_cell.-residual-wrapper.pbtxt | 4 ++++ 115 files changed, 474 insertions(+), 9 deletions(-) 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/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/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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<type \'property\'>" } + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<type \'property\'>" } + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<type \'property\'>" } + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.pooling._Pooling1D\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.pooling._Pooling2D\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.pooling._Pooling3D\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.normalization.BatchNormalization\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.convolutional._Conv\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.convolutional._Conv\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.convolutional._Conv\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.convolutional._Conv\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.convolutional._Conv\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.core.Dense\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.core.Dropout\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.core.Flatten\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" @@ -60,7 +64,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'trainable\', \'name\', \'dtype\'], varargs=None, keywords=kwargs, defaults=[\'True\', \'None\', \"<dtype: \'float32\'>\"], " + 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: "<class \'tensorflow.python.layers.pooling._Pooling1D\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.pooling._Pooling2D\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.pooling._Pooling3D\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.base.Network\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.layers.convolutional._Conv\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.ops.rnn_cell_impl.RNNCell\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.ops.rnn_cell_impl.RNNCell\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.ops.rnn_cell_impl.RNNCell\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.ops.rnn_cell_impl.RNNCell\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.ops.rnn_cell_impl.RNNCell\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.ops.rnn_cell_impl.RNNCell\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.ops.rnn_cell_impl.RNNCell\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" 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: "<class \'tensorflow.python.ops.rnn_cell_impl.RNNCell\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" @@ -69,7 +73,7 @@ tf_class { } member_method { name: "__init__" - argspec: "args=[\'self\', \'trainable\', \'name\', \'dtype\'], varargs=None, keywords=kwargs, defaults=[\'True\', \'None\', \"<dtype: \'float32\'>\"], " + 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: "<class \'tensorflow.python.ops.rnn_cell_impl.RNNCell\'>" is_instance: "<class \'tensorflow.python.layers.base.Layer\'>" is_instance: "<type \'object\'>" + member { + name: "dtype" + mtype: "<type \'property\'>" + } member { name: "graph" mtype: "<type \'property\'>" From 1dc2fe7acdb68e3243544dfb09999fff4237f54a Mon Sep 17 00:00:00 2001 From: Gunhan Gulsoy <gunan@google.com> Date: Fri, 22 Sep 2017 16:25:55 -0700 Subject: [PATCH 21/26] Automated g4 rollback of changelist 166264198 PiperOrigin-RevId: 169753258 --- tensorflow/contrib/boosted_trees/BUILD | 3 --- tensorflow/contrib/boosted_trees/estimator_batch/BUILD | 3 --- tensorflow/contrib/boosted_trees/lib/BUILD | 6 ------ 3 files changed, 12 deletions(-) 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", From 2957cd89481c0a6b07872335857af8baffd492e8 Mon Sep 17 00:00:00 2001 From: Mustafa Ispir <ispir@google.com> Date: Fri, 22 Sep 2017 16:55:58 -0700 Subject: [PATCH 22/26] Local run option of estimator training. PiperOrigin-RevId: 169756384 --- tensorflow/python/estimator/training.py | 49 +++++++++- tensorflow/python/estimator/training_test.py | 99 ++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) 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() From b1ada5f0c7047ac0f20ee8666d07c5853f8fd974 Mon Sep 17 00:00:00 2001 From: Justine Tunney <jart@google.com> Date: Fri, 22 Sep 2017 17:18:01 -0700 Subject: [PATCH 23/26] Fix TensorBoard python -m invoke in docs PiperOrigin-RevId: 169758752 --- tensorflow/docs_src/get_started/summaries_and_tensorboard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 1b94147dc9793e63389d2099672351954fa2c326 Mon Sep 17 00:00:00 2001 From: Neal Wu <wun@google.com> Date: Fri, 22 Sep 2017 18:18:29 -0700 Subject: [PATCH 24/26] Fix broken GitHub links in tensorflow and tensorflow_models resulting from The Great Models Move (a.k.a. the research subfolder) PiperOrigin-RevId: 169763373 --- tensorflow/contrib/receptive_field/README.md | 2 +- tensorflow/contrib/signal/python/kernel_tests/mel_ops_test.py | 4 ++-- .../tensorflow/demo/TensorFlowObjectDetectionAPIModel.java | 2 +- tensorflow/python/keras/_impl/keras/applications/mobilenet.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) 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/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/examples/android/src/org/tensorflow/demo/TensorFlowObjectDetectionAPIModel.java b/tensorflow/examples/android/src/org/tensorflow/demo/TensorFlowObjectDetectionAPIModel.java index 687318c7ce0..614d3c7dd77 100644 --- a/tensorflow/examples/android/src/org/tensorflow/demo/TensorFlowObjectDetectionAPIModel.java +++ b/tensorflow/examples/android/src/org/tensorflow/demo/TensorFlowObjectDetectionAPIModel.java @@ -35,7 +35,7 @@ import org.tensorflow.demo.env.Logger; /** * Wrapper for frozen detection models trained using the Tensorflow Object Detection API: - * github.com/tensorflow/models/tree/master/object_detection + * github.com/tensorflow/models/tree/master/research/object_detection */ public class TensorFlowObjectDetectionAPIModel implements Classifier { private static final Logger LOGGER = new Logger(); 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 From 8ef722253c470390b83b6c8f903f3017cd4003ca Mon Sep 17 00:00:00 2001 From: Sanjoy Das <sanjoy@google.com> Date: Fri, 22 Sep 2017 18:46:21 -0700 Subject: [PATCH 25/26] Remove a redundant setName. The EmitComputation should have emitted a function with the right name, so use a CHECK instead. PiperOrigin-RevId: 169764856 --- tensorflow/compiler/xla/service/cpu/cpu_compiler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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<std::unique_ptr<HloModule>> 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; From 286f57061cb8a21d3cc87b5ee06a87e3f51467c8 Mon Sep 17 00:00:00 2001 From: Bjarke Hammersholt Roune <broune@google.com> Date: Fri, 22 Sep 2017 20:38:54 -0700 Subject: [PATCH 26/26] Make Service::TransferToClient not attempt to manipulate the literal when the transfer failed, preventing a crash and allowing the caller to see the reason for the failed transfer. PiperOrigin-RevId: 169770126 --- tensorflow/compiler/xla/service/service.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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,