merge internal changes
This commit is contained in:
commit
f6a1d34447
78
WORKSPACE
78
WORKSPACE
@ -46,7 +46,7 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "font_roboto",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/font-roboto.git",
|
||||
remote = "https://github.com/polymerelements/font-roboto.git",
|
||||
tag = "v1.0.1",
|
||||
)
|
||||
|
||||
@ -60,49 +60,49 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "iron_a11y_announcer",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-a11y-announcer.git",
|
||||
remote = "https://github.com/polymerelements/iron-a11y-announcer.git",
|
||||
tag = "v1.0.4",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_a11y_keys_behavior",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-a11y-keys-behavior.git",
|
||||
remote = "https://github.com/polymerelements/iron-a11y-keys-behavior.git",
|
||||
tag = "v1.1.2",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_ajax",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-ajax.git",
|
||||
remote = "https://github.com/polymerelements/iron-ajax.git",
|
||||
tag = "v1.1.1",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_autogrow_textarea",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-autogrow-textarea.git",
|
||||
remote = "https://github.com/polymerelements/iron-autogrow-textarea.git",
|
||||
tag = "v1.0.12",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_behaviors",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-behaviors.git",
|
||||
remote = "https://github.com/polymerelements/iron-behaviors.git",
|
||||
tag = "v1.0.13",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_checked_element_behavior",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-checked-element-behavior.git",
|
||||
remote = "https://github.com/polymerelements/iron-checked-element-behavior.git",
|
||||
tag = "v1.0.4",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_collapse",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-collapse.git",
|
||||
remote = "https://github.com/polymerelements/iron-collapse.git",
|
||||
tag = "v1.0.6",
|
||||
)
|
||||
|
||||
@ -116,7 +116,7 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "iron_fit_behavior",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-fit-behavior.git",
|
||||
remote = "https://github.com/polymerelements/iron-fit-behavior.git",
|
||||
tag = "v1.0.6",
|
||||
)
|
||||
|
||||
@ -130,7 +130,7 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "iron_form_element_behavior",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-form-element-behavior.git",
|
||||
remote = "https://github.com/polymerelements/iron-form-element-behavior.git",
|
||||
tag = "v1.0.6",
|
||||
)
|
||||
|
||||
@ -151,28 +151,28 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "iron_iconset_svg",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-iconset-svg.git",
|
||||
remote = "https://github.com/polymerelements/iron-iconset-svg.git",
|
||||
tag = "v1.0.9",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_input",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-input.git",
|
||||
remote = "https://github.com/polymerelements/iron-input.git",
|
||||
tag = "v1.0.9",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_list",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-list.git",
|
||||
remote = "https://github.com/polymerelements/iron-list.git",
|
||||
tag = "v1.1.7",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_menu_behavior",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-menu-behavior.git",
|
||||
remote = "https://github.com/polymerelements/iron-menu-behavior.git",
|
||||
tag = "v1.1.5",
|
||||
)
|
||||
|
||||
@ -187,13 +187,13 @@ new_git_repository(
|
||||
name = "iron_overlay_behavior",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/polymerelements/iron-overlay-behavior.git",
|
||||
tag = "v1.6.1",
|
||||
tag = "v1.6.2",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_range_behavior",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-range-behavior.git",
|
||||
remote = "https://github.com/polymerelements/iron-range-behavior.git",
|
||||
tag = "v1.0.4",
|
||||
)
|
||||
|
||||
@ -207,14 +207,14 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "iron_selector",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-selector.git",
|
||||
remote = "https://github.com/polymerelements/iron-selector.git",
|
||||
tag = "v1.2.4",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "iron_validatable_behavior",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/iron-validatable-behavior.git",
|
||||
remote = "https://github.com/polymerelements/iron-validatable-behavior.git",
|
||||
tag = "v1.0.5",
|
||||
)
|
||||
|
||||
@ -235,56 +235,56 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "paper_behaviors",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-behaviors.git",
|
||||
remote = "https://github.com/polymerelements/paper-behaviors.git",
|
||||
tag = "v1.0.11",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_button",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-button.git",
|
||||
remote = "https://github.com/polymerelements/paper-button.git",
|
||||
tag = "v1.0.11",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_checkbox",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-checkbox.git",
|
||||
remote = "https://github.com/polymerelements/paper-checkbox.git",
|
||||
tag = "v1.1.3",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_dropdown_menu",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-dropdown-menu.git",
|
||||
remote = "https://github.com/polymerelements/paper-dropdown-menu.git",
|
||||
tag = "v1.1.3",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_header_panel",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-header-panel.git",
|
||||
remote = "https://github.com/polymerelements/paper-header-panel.git",
|
||||
tag = "v1.1.4",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_icon_button",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-icon-button.git",
|
||||
remote = "https://github.com/polymerelements/paper-icon-button.git",
|
||||
tag = "v1.0.6",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_input",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-input.git",
|
||||
remote = "https://github.com/polymerelements/paper-input.git",
|
||||
tag = "v1.1.5",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_item",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-item.git",
|
||||
remote = "https://github.com/polymerelements/paper-item.git",
|
||||
tag = "v1.1.4",
|
||||
)
|
||||
|
||||
@ -298,7 +298,7 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "paper_menu",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-menu.git",
|
||||
remote = "https://github.com/polymerelements/paper-menu.git",
|
||||
tag = "v1.2.2",
|
||||
)
|
||||
|
||||
@ -306,27 +306,27 @@ new_git_repository(
|
||||
name = "paper_menu_button",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/polymerelements/paper-menu-button.git",
|
||||
tag = "v1.0.4",
|
||||
tag = "v1.1.0",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_progress",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-progress.git",
|
||||
tag = "v1.0.8",
|
||||
remote = "https://github.com/polymerelements/paper-progress.git",
|
||||
tag = "v1.0.9",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_radio_button",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-radio-button.git",
|
||||
remote = "https://github.com/polymerelements/paper-radio-button.git",
|
||||
tag = "v1.1.1",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_radio_group",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-radio-group.git",
|
||||
remote = "https://github.com/polymerelements/paper-radio-group.git",
|
||||
tag = "v1.0.9",
|
||||
)
|
||||
|
||||
@ -340,35 +340,35 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "paper_slider",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-slider.git",
|
||||
remote = "https://github.com/polymerelements/paper-slider.git",
|
||||
tag = "v1.0.8",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_styles",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-styles.git",
|
||||
remote = "https://github.com/polymerelements/paper-styles.git",
|
||||
tag = "v1.1.1",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_tabs",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-tabs.git",
|
||||
remote = "https://github.com/polymerelements/paper-tabs.git",
|
||||
tag = "v1.2.4",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_toggle_button",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-toggle-button.git",
|
||||
remote = "https://github.com/polymerelements/paper-toggle-button.git",
|
||||
tag = "v1.0.12",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "paper_toolbar",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/PolymerElements/paper-toolbar.git",
|
||||
remote = "https://github.com/polymerelements/paper-toolbar.git",
|
||||
tag = "v1.1.2",
|
||||
)
|
||||
|
||||
@ -382,7 +382,7 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "polymer",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/Polymer/polymer.git",
|
||||
remote = "https://github.com/polymer/polymer.git",
|
||||
tag = "v1.4.0",
|
||||
)
|
||||
|
||||
@ -403,6 +403,6 @@ new_git_repository(
|
||||
new_git_repository(
|
||||
name = "webcomponentsjs",
|
||||
build_file = "bower.BUILD",
|
||||
remote = "https://github.com/Polymer/webcomponentsjs.git",
|
||||
remote = "https://github.com/polymer/webcomponentsjs.git",
|
||||
tag = "v0.7.21",
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
archive_dir = "eigen-eigen-6e521c802bf5"
|
||||
archive_dir = "eigen-eigen-3f653ace7d28"
|
||||
|
||||
cc_library(
|
||||
name = "eigen",
|
||||
|
@ -20,6 +20,15 @@ config_setting(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "android_arm",
|
||||
values = {
|
||||
"crosstool_top": "//external:android/crosstool",
|
||||
"android_cpu": "armeabi-v7a",
|
||||
},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "darwin",
|
||||
values = {"cpu": "darwin"},
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
include (ExternalProject)
|
||||
|
||||
set(eigen_archive_hash "6e521c802bf5")
|
||||
set(eigen_archive_hash "3f653ace7d28")
|
||||
|
||||
set(eigen_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
@ -16,7 +16,7 @@ set(eigen_INCLUDE_DIRS
|
||||
${tensorflow_source_dir}/third_party/eigen3
|
||||
)
|
||||
set(eigen_URL https://bitbucket.org/eigen/eigen/get/${eigen_archive_hash}.tar.gz)
|
||||
set(eigen_HASH SHA256=f1b4b4401d08d0d44128ab80ebe76633363dab20c29b1bf2370aed8b4893cc5e)
|
||||
set(eigen_HASH SHA256=b49502f423deda55cea33bc503f84409cca92157f3b536d17113b81138f86715)
|
||||
set(eigen_BUILD ${CMAKE_CURRENT_BINARY_DIR}/eigen/src/eigen)
|
||||
set(eigen_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/eigen/install)
|
||||
|
||||
|
@ -752,7 +752,7 @@ class WithShapeTest(tf.test.TestCase):
|
||||
|
||||
for incompatible_shape in [[0], [1]]:
|
||||
self.assertRaisesRegexp(
|
||||
ValueError, "must have the same rank",
|
||||
ValueError, r"Shapes \(\?, 2\) and \([01],\) are not compatible",
|
||||
tf.contrib.framework.with_shape,
|
||||
incompatible_shape, tensor_partial_shape)
|
||||
for incompatible_shape in [[1, 2, 1]]:
|
||||
@ -761,7 +761,7 @@ class WithShapeTest(tf.test.TestCase):
|
||||
incompatible_shape, tensor_partial_shape)
|
||||
for incompatible_shape in [[2, 1]]:
|
||||
self.assertRaisesRegexp(
|
||||
ValueError, "Dimensions.*are not compatible",
|
||||
ValueError, r"Shapes \(\?, 2\) and \(2, 1\) are not compatible",
|
||||
tf.contrib.framework.with_shape,
|
||||
incompatible_shape, tensor_partial_shape)
|
||||
|
||||
|
@ -164,7 +164,6 @@ TEST_F(DataByExampleTest, VisitUnavailable) {
|
||||
signal(&updated_data);
|
||||
});
|
||||
wait(&completed_visit);
|
||||
EXPECT_FALSE(thread_pool.HasPendingClosures());
|
||||
EXPECT_TRUE(errors::IsUnavailable(status));
|
||||
}
|
||||
|
||||
|
@ -245,6 +245,7 @@ tf_cuda_library(
|
||||
"framework/register_types.h",
|
||||
"framework/resource_mgr.h",
|
||||
"framework/selective_registration.h",
|
||||
"framework/session_state.h",
|
||||
"framework/tensor.h",
|
||||
"framework/tensor_shape.h",
|
||||
"framework/tensor_slice.h",
|
||||
@ -267,6 +268,7 @@ tf_cuda_library(
|
||||
"util/saved_tensor_slice_util.h",
|
||||
"util/sparse/group_iterator.h",
|
||||
"util/sparse/sparse_tensor.h",
|
||||
"util/stat_summarizer.h",
|
||||
"util/tensor_format.h",
|
||||
"util/tensor_slice_reader.h",
|
||||
"util/tensor_slice_reader_cache.h",
|
||||
@ -856,6 +858,7 @@ filegroup(
|
||||
"framework/partial_tensor_shape.h",
|
||||
"framework/rendezvous.h",
|
||||
"framework/selective_registration.h",
|
||||
"framework/session_state.h",
|
||||
"framework/tensor.h",
|
||||
"framework/tensor_reference.h",
|
||||
"framework/tensor_shape.h",
|
||||
@ -1268,6 +1271,7 @@ tf_cc_test(
|
||||
"//tensorflow/core/kernels:matmul_op",
|
||||
"//tensorflow/core/kernels:ops_util",
|
||||
"//tensorflow/core/kernels:queue_ops",
|
||||
"//tensorflow/core/kernels:session_ops",
|
||||
"//tensorflow/core/kernels:variable_ops",
|
||||
"//third_party/eigen3",
|
||||
],
|
||||
|
@ -50,6 +50,11 @@ bool IsConstantFoldable(const Node* n,
|
||||
if (n->IsControlFlow() || n->IsSend() || n->IsRecv()) {
|
||||
return false;
|
||||
}
|
||||
// TODO(yuanbyu): For now disable these session handle operations.
|
||||
if (n->IsGetSessionHandle() || n->IsGetSessionTensor() ||
|
||||
n->IsDeleteSessionTensor()) {
|
||||
return false;
|
||||
}
|
||||
if (n->IsSource()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -313,6 +313,8 @@ Status DirectSession::Run(const RunOptions& run_options,
|
||||
args.rendezvous = run_state.rendez;
|
||||
args.cancellation_manager = cancellation_manager_;
|
||||
args.runner = [this](Executor::Args::Closure c) { SchedClosure(c); };
|
||||
args.session_state = &session_state_;
|
||||
args.tensor_store = &run_state.tensor_store;
|
||||
if (LogMemory::IsEnabled()) {
|
||||
LogMemory::RecordStep(args.step_id, run_state_args.handle);
|
||||
}
|
||||
@ -340,6 +342,11 @@ Status DirectSession::Run(const RunOptions& run_options,
|
||||
// Receive outputs.
|
||||
TF_RETURN_IF_ERROR(
|
||||
RecvOutputs(output_names, executors_and_keys, &run_state, outputs));
|
||||
|
||||
// Save the output tensors of this run we choose to keep.
|
||||
TF_RETURN_IF_ERROR(
|
||||
run_state.tensor_store.SaveTensors(output_names, &session_state_));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@ -369,9 +376,8 @@ Status DirectSession::PRunSetup(const std::vector<string>& input_names,
|
||||
{
|
||||
mutex_lock l(executor_lock_);
|
||||
if (!partial_runs_.insert({run_state_args.handle, run_state}).second) {
|
||||
return errors::Internal("The handle ", run_state_args.handle,
|
||||
" created for this partial"
|
||||
" run is not unique.");
|
||||
return errors::Internal("The handle '", run_state_args.handle,
|
||||
"' created for this partial run is not unique.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,13 +396,12 @@ Status DirectSession::PRunSetup(const std::vector<string>& input_names,
|
||||
});
|
||||
|
||||
Executor::Args args;
|
||||
{
|
||||
mutex_lock l(mu_);
|
||||
args.step_id = name_counter_++;
|
||||
}
|
||||
args.step_id = step_id_counter_.fetch_add(1);
|
||||
args.rendezvous = run_state->rendez;
|
||||
args.cancellation_manager = cancellation_manager_;
|
||||
args.runner = [this](Executor::Args::Closure c) { SchedClosure(c); };
|
||||
args.session_state = &session_state_;
|
||||
args.tensor_store = &run_state->tensor_store;
|
||||
if (LogMemory::IsEnabled()) {
|
||||
LogMemory::RecordStep(args.step_id, run_state_args.handle);
|
||||
}
|
||||
@ -470,9 +475,14 @@ Status DirectSession::PRun(const string& handle, const NamedTensorList& inputs,
|
||||
s = RecvOutputs(output_names, executors_and_keys, run_state, outputs);
|
||||
}
|
||||
|
||||
// Delete the run state if there is an error or all fetches are done.
|
||||
// Save the output tensors of this run we choose to keep.
|
||||
if (s.ok()) {
|
||||
s = run_state->tensor_store.SaveTensors(output_names, &session_state_);
|
||||
}
|
||||
|
||||
{
|
||||
mutex_lock l(executor_lock_);
|
||||
// Delete the run state if there is an error or all fetches are done.
|
||||
bool done = true;
|
||||
if (s.ok()) {
|
||||
{
|
||||
@ -911,7 +921,7 @@ Status DirectSession::CreateGraphs(gtl::ArraySlice<string> feeds,
|
||||
// allow.
|
||||
device_opts.allow_internal_ops = true;
|
||||
device_opts.expect_device_spec = true;
|
||||
Status s = ConvertGraphDefToGraph(device_opts, *graph_def, device_graph);
|
||||
s = ConvertGraphDefToGraph(device_opts, *graph_def, device_graph);
|
||||
if (!s.ok()) {
|
||||
delete device_graph;
|
||||
break;
|
||||
|
@ -29,6 +29,7 @@ limitations under the License.
|
||||
#include "tensorflow/core/common_runtime/rendezvous_mgr.h"
|
||||
#include "tensorflow/core/framework/cancellation.h"
|
||||
#include "tensorflow/core/framework/graph.pb.h"
|
||||
#include "tensorflow/core/framework/session_state.h"
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/lib/core/errors.h"
|
||||
#include "tensorflow/core/lib/core/status.h"
|
||||
@ -78,6 +79,7 @@ class DirectSession : public Session {
|
||||
::tensorflow::Status PRun(const string& handle, const NamedTensorList& inputs,
|
||||
const std::vector<string>& output_names,
|
||||
std::vector<Tensor>* outputs) override;
|
||||
|
||||
::tensorflow::Status Close() override;
|
||||
|
||||
// NOTE: This is a temporary api that is only meant to enable testing.
|
||||
@ -135,6 +137,7 @@ class DirectSession : public Session {
|
||||
Notification executors_done;
|
||||
std::unordered_set<string> pending_inputs;
|
||||
std::unordered_set<string> pending_outputs;
|
||||
TensorStore tensor_store;
|
||||
|
||||
RunState(const std::vector<string>& input_names,
|
||||
const std::vector<string>& output_names) {
|
||||
@ -146,6 +149,7 @@ class DirectSession : public Session {
|
||||
pending_outputs.emplace(name);
|
||||
}
|
||||
}
|
||||
|
||||
~RunState();
|
||||
};
|
||||
|
||||
@ -228,6 +232,9 @@ class DirectSession : public Session {
|
||||
std::unordered_map<string, RunState*> partial_runs_
|
||||
GUARDED_BY(executor_lock_);
|
||||
|
||||
// This holds all the tensors that are currently alive in the session.
|
||||
SessionState session_state_;
|
||||
|
||||
CancellationManager* cancellation_manager_;
|
||||
|
||||
// Saves and restores device placements for stateful nodes.
|
||||
|
@ -564,6 +564,77 @@ TEST(DirectSessionTest, PartialRunMultiOutputFeed) {
|
||||
ASSERT_EQ(true, outputs[0].flat<bool>()(0));
|
||||
}
|
||||
|
||||
TEST(DirectSessionTest, RunHandleTest) {
|
||||
GraphDef def;
|
||||
Graph g(OpRegistry::Global());
|
||||
|
||||
Tensor value0(DT_FLOAT, TensorShape({}));
|
||||
value0.scalar<float>()() = 1.0;
|
||||
Node* const0 = test::graph::Constant(&g, value0);
|
||||
Node* identity0 = test::graph::Identity(&g, const0);
|
||||
|
||||
Tensor value1(DT_FLOAT, TensorShape({}));
|
||||
value1.scalar<float>()() = 2.0;
|
||||
Node* const1 = test::graph::Constant(&g, value1);
|
||||
Node* node3 = test::graph::Add(&g, identity0, const1);
|
||||
Node* node4 = test::graph::Unary(&g, "GetSessionHandle", node3);
|
||||
|
||||
Tensor value2(DT_STRING, TensorShape({}));
|
||||
Node* const2 = test::graph::Constant(&g, value2);
|
||||
Node* node5 = test::graph::GetSessionTensor(&g, const2);
|
||||
Node* node6 = test::graph::Add(&g, node5, const1);
|
||||
|
||||
Node* node7 = test::graph::Unary(&g, "DeleteSessionTensor", const2);
|
||||
|
||||
test::graph::ToGraphDef(&g, &def);
|
||||
|
||||
std::unique_ptr<Session> session(CreateSession());
|
||||
ASSERT_TRUE(session != nullptr);
|
||||
TF_ASSERT_OK(session->Create(def));
|
||||
|
||||
// First run call: Create a handle.
|
||||
std::vector<Tensor> outputs;
|
||||
Status s = session->Run({}, {node4->name() + ":0"}, {}, &outputs);
|
||||
ASSERT_TRUE(s.ok());
|
||||
ASSERT_EQ(1, outputs.size());
|
||||
|
||||
// Second run call: Use a handle.
|
||||
std::vector<Tensor> outputs1;
|
||||
s = session->Run({{const2->name(), outputs[0]}}, {node6->name() + ":0"}, {},
|
||||
&outputs1);
|
||||
ASSERT_TRUE(s.ok());
|
||||
ASSERT_EQ(1, outputs1.size());
|
||||
ASSERT_EQ(5.0, outputs1[0].flat<float>()(0));
|
||||
|
||||
// Third run call: Delete a handle.
|
||||
std::vector<Tensor> outputs2;
|
||||
s = session->Run({{const2->name(), outputs[0]}}, {}, {node7->name()},
|
||||
&outputs2);
|
||||
ASSERT_TRUE(s.ok());
|
||||
}
|
||||
|
||||
TEST(DirectSessionTest, CreateGraphFailsWhenAssigningAFedVar) {
|
||||
Graph graph(OpRegistry::Global());
|
||||
|
||||
Node* a = test::graph::Var(&graph, DT_FLOAT, {});
|
||||
Node* b = test::graph::Constant(&graph, {});
|
||||
|
||||
Tensor zero(DT_FLOAT, {});
|
||||
test::FillValues<float>(&zero, {0});
|
||||
|
||||
// a = b
|
||||
Node* assign = test::graph::Assign(&graph, a, b);
|
||||
|
||||
std::unique_ptr<Session> session(CreateSession());
|
||||
ASSERT_TRUE(session != nullptr);
|
||||
|
||||
// The graph is invalid since a constant cannot be assigned to a constant.
|
||||
// The return Status of session->Run should flag this as an invalid argument.
|
||||
std::vector<Tensor> outputs;
|
||||
Status s = session->Run({{a->name(), zero}}, {assign->name()}, {}, &outputs);
|
||||
ASSERT_TRUE(errors::IsInvalidArgument(s));
|
||||
}
|
||||
|
||||
TEST(DirectSessionTest, TimeoutSession) {
|
||||
GraphDef graph;
|
||||
// Creates a graph with one FIFOQueue and one dequeue op.
|
||||
|
@ -645,6 +645,8 @@ class ExecutorState {
|
||||
int64 step_id_;
|
||||
// Not owned.
|
||||
Rendezvous* rendezvous_;
|
||||
SessionState* session_state_;
|
||||
TensorStore* tensor_store_;
|
||||
StepStatsCollector* stats_collector_;
|
||||
// QUESTION: Make it a checkpoint::TensorSliceReaderCacheWrapper
|
||||
// instead of a pointer? (avoids having to delete).
|
||||
@ -793,6 +795,8 @@ class ExecutorState {
|
||||
ExecutorState::ExecutorState(const Executor::Args& args, ExecutorImpl* impl)
|
||||
: step_id_(args.step_id),
|
||||
rendezvous_(args.rendezvous),
|
||||
session_state_(args.session_state),
|
||||
tensor_store_(args.tensor_store),
|
||||
stats_collector_(args.stats_collector),
|
||||
slice_reader_cache_(new checkpoint::TensorSliceReaderCacheWrapper),
|
||||
call_frame_(args.call_frame),
|
||||
@ -938,6 +942,8 @@ void ExecutorState::Process(TaggedNode tagged_node, int64 scheduled_usec) {
|
||||
// track allocations if and only if we are collecting statistics
|
||||
params.track_allocations = (stats_collector_ != nullptr);
|
||||
params.rendezvous = rendezvous_;
|
||||
params.session_state = session_state_;
|
||||
params.tensor_store = tensor_store_;
|
||||
params.cancellation_manager = cancellation_manager_;
|
||||
params.call_frame = call_frame_;
|
||||
params.function_library = impl_->params_.function_library;
|
||||
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||
|
||||
#include "tensorflow/core/common_runtime/device.h"
|
||||
#include "tensorflow/core/framework/rendezvous.h"
|
||||
#include "tensorflow/core/framework/session_state.h"
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/graph/graph.h"
|
||||
#include "tensorflow/core/lib/core/notification.h"
|
||||
@ -85,6 +86,8 @@ class Executor {
|
||||
StepStatsCollector* stats_collector = nullptr;
|
||||
FunctionCallFrame* call_frame = nullptr;
|
||||
CancellationManager* cancellation_manager = nullptr;
|
||||
SessionState* session_state = nullptr;
|
||||
TensorStore* tensor_store = nullptr;
|
||||
|
||||
typedef std::function<void()> Closure;
|
||||
typedef std::function<void(Closure)> Runner;
|
||||
|
@ -21,6 +21,7 @@ limitations under the License.
|
||||
#include <vector>
|
||||
|
||||
#include "tensorflow/core/common_runtime/gpu/gpu_init.h"
|
||||
#include "tensorflow/core/lib/core/threadpool.h"
|
||||
#include "tensorflow/core/lib/gtl/inlined_vector.h"
|
||||
#include "tensorflow/core/lib/random/simple_philox.h"
|
||||
#include "tensorflow/core/platform/logging.h"
|
||||
|
83
tensorflow/core/common_runtime/session_state.cc
Normal file
83
tensorflow/core/common_runtime/session_state.cc
Normal file
@ -0,0 +1,83 @@
|
||||
/* Copyright 2015 Google Inc. 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/core/framework/session_state.h"
|
||||
#include "tensorflow/core/graph/tensor_id.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
Status SessionState::GetTensor(const string& handle, Tensor* tensor) {
|
||||
mutex_lock l(state_lock_);
|
||||
auto it = tensors_.find(handle);
|
||||
if (it == tensors_.end()) {
|
||||
return errors::InvalidArgument("The tensor with handle '", handle,
|
||||
"' is not in the session store.");
|
||||
}
|
||||
*tensor = it->second;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status SessionState::AddTensor(const string& handle, const Tensor& tensor) {
|
||||
mutex_lock l(state_lock_);
|
||||
if (!tensors_.insert({handle, tensor}).second) {
|
||||
return errors::InvalidArgument("Failed to add a tensor with handle '",
|
||||
handle, "' to the session store.");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status SessionState::DeleteTensor(const string& handle) {
|
||||
mutex_lock l(state_lock_);
|
||||
if (tensors_.erase(handle) == 0) {
|
||||
return errors::InvalidArgument("Failed to delete a tensor with handle '",
|
||||
handle, "' in the session store.");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
int64 SessionState::GetNewId() {
|
||||
mutex_lock l(state_lock_);
|
||||
return tensor_id_++;
|
||||
}
|
||||
|
||||
Status TensorStore::AddTensor(const string& name, const TensorAndKey& tk) {
|
||||
mutex_lock l(lock_);
|
||||
if (!tensors_.insert({name, tk}).second) {
|
||||
return errors::InvalidArgument("Failed to add a tensor with name '", name,
|
||||
"' to the tensor store.");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status TensorStore::SaveTensors(const std::vector<string>& output_names,
|
||||
SessionState* session_state) {
|
||||
mutex_lock l(lock_);
|
||||
if (tensors_.size() != 0) {
|
||||
// Save only the tensors in output_names in the session.
|
||||
for (const string& name : output_names) {
|
||||
TensorId id(ParseTensorName(name));
|
||||
const string& op_name = id.first.ToString();
|
||||
auto it = tensors_.find(op_name);
|
||||
if (it != tensors_.end()) {
|
||||
// Save the tensor to the session state.
|
||||
string key = it->second.GetHandle(op_name);
|
||||
TF_RETURN_IF_ERROR(session_state->AddTensor(key, it->second.tensor));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace tensorflow
|
@ -103,10 +103,14 @@ Status GrpcServer::Init() {
|
||||
return errors::InvalidArgument("Task ", server_def_.task_index(),
|
||||
" was not defined in job \"",
|
||||
server_def_.job_name(), "\"");
|
||||
} else if (!strings::safe_strto32(str_util::Split(iter->second, ':')[1],
|
||||
&requested_port_)) {
|
||||
return errors::Internal("Could not parse port for local server from \"",
|
||||
iter->second, "\"");
|
||||
}
|
||||
const std::vector<string> hostname_port =
|
||||
str_util::Split(iter->second, ':');
|
||||
if (hostname_port.size() != 2 ||
|
||||
!strings::safe_strto32(hostname_port[1], &requested_port_)) {
|
||||
return errors::InvalidArgument(
|
||||
"Could not parse port for local server from \"", iter->second,
|
||||
"\"");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -89,12 +89,12 @@ class GrpcServer : public ServerInterface {
|
||||
|
||||
// Implementation of a TensorFlow master, and RPC polling thread.
|
||||
MasterEnv master_env_;
|
||||
AsyncServiceInterface* master_service_;
|
||||
AsyncServiceInterface* master_service_ = nullptr;
|
||||
std::unique_ptr<Thread> master_thread_ GUARDED_BY(mu_);
|
||||
|
||||
// Implementation of a TensorFlow worker, and RPC polling thread.
|
||||
WorkerEnv worker_env_;
|
||||
AsyncServiceInterface* worker_service_;
|
||||
AsyncServiceInterface* worker_service_ = nullptr;
|
||||
std::unique_ptr<Thread> worker_thread_ GUARDED_BY(mu_);
|
||||
|
||||
std::unique_ptr<::grpc::Server> server_ GUARDED_BY(mu_);
|
||||
|
@ -678,8 +678,8 @@ Status FunctionCallFrame::GetRetvals(std::vector<Tensor>* rets) const {
|
||||
|
||||
Status FunctionCallFrame::GetArg(int index, Tensor* val) const {
|
||||
if (index < 0 || static_cast<size_t>(index) >= args_.size()) {
|
||||
return errors::OutOfRange("GetArg ", index, " is not within [0, ",
|
||||
args_.size(), ")");
|
||||
return errors::InvalidArgument("GetArg ", index, " is not within [0, ",
|
||||
args_.size(), ")");
|
||||
}
|
||||
*val = args_[index];
|
||||
return Status::OK();
|
||||
@ -687,8 +687,8 @@ Status FunctionCallFrame::GetArg(int index, Tensor* val) const {
|
||||
|
||||
Status FunctionCallFrame::SetRetval(int index, const Tensor& val) {
|
||||
if (index < 0 || static_cast<size_t>(index) >= rets_.size()) {
|
||||
return errors::OutOfRange("SetRetval ", index, " is not within [0, ",
|
||||
rets_.size(), ")");
|
||||
return errors::InvalidArgument("SetRetval ", index, " is not within [0, ",
|
||||
rets_.size(), ")");
|
||||
}
|
||||
if (val.dtype() != ret_types_[index]) {
|
||||
return errors::InvalidArgument(
|
||||
|
@ -563,8 +563,8 @@ TEST(FunctionCallFrame, Void_Void) {
|
||||
auto a = test::AsTensor<float>({100});
|
||||
HasError(frame.SetArgs({a}), "Invalid argument");
|
||||
Tensor v;
|
||||
HasError(frame.GetArg(0, &v), "Out of range");
|
||||
HasError(frame.SetRetval(0, v), "Out of range");
|
||||
HasError(frame.GetArg(0, &v), "Invalid argument");
|
||||
HasError(frame.SetRetval(0, v), "Invalid argument");
|
||||
std::vector<Tensor> rets;
|
||||
TF_EXPECT_OK(frame.GetRetvals(&rets));
|
||||
EXPECT_EQ(rets.size(), 0);
|
||||
@ -581,16 +581,16 @@ TEST(FunctionCallFrame, Float_Float_Float) {
|
||||
TF_EXPECT_OK(frame.SetArgs({a, b}));
|
||||
|
||||
Tensor v;
|
||||
HasError(frame.GetArg(-1, &v), "Out of range");
|
||||
HasError(frame.GetArg(2, &v), "Out of range");
|
||||
HasError(frame.GetArg(-1, &v), "Invalid argument");
|
||||
HasError(frame.GetArg(2, &v), "Invalid argument");
|
||||
TF_EXPECT_OK(frame.GetArg(0, &v));
|
||||
test::ExpectTensorEqual<float>(a, v);
|
||||
TF_EXPECT_OK(frame.GetArg(1, &v));
|
||||
test::ExpectTensorEqual<float>(b, v);
|
||||
|
||||
v = test::AsTensor<float>({-100});
|
||||
HasError(frame.SetRetval(-1, v), "Out of range");
|
||||
HasError(frame.SetRetval(1, v), "Out of range");
|
||||
HasError(frame.SetRetval(-1, v), "Invalid argument");
|
||||
HasError(frame.SetRetval(1, v), "Invalid argument");
|
||||
HasError(frame.SetRetval(0, test::AsTensor<int64>({-100})),
|
||||
"Invalid argument: Expects ret[0] to be float");
|
||||
|
||||
|
@ -99,7 +99,7 @@ class BinaryElementWiseOp : public BinaryOp<T> {
|
||||
#undef NDIM_CASE
|
||||
|
||||
default:
|
||||
context->SetStatus(errors::OutOfRange(
|
||||
context->SetStatus(errors::InvalidArgument(
|
||||
"We only handle up to Tensor::dims() up to 8, not ", a.dims()));
|
||||
break;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ limitations under the License.
|
||||
#include "tensorflow/core/framework/op.h"
|
||||
#include "tensorflow/core/framework/rendezvous.h"
|
||||
#include "tensorflow/core/framework/selective_registration.h"
|
||||
#include "tensorflow/core/framework/session_state.h"
|
||||
#include "tensorflow/core/framework/step_stats.pb.h"
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/framework/tensor_shape.h"
|
||||
@ -502,6 +503,12 @@ class OpKernelContext {
|
||||
// computations running on other devices.
|
||||
Rendezvous* rendezvous = nullptr;
|
||||
|
||||
// The session state for this op.
|
||||
SessionState* session_state = nullptr;
|
||||
|
||||
// The tensor store for this op.
|
||||
TensorStore* tensor_store = nullptr;
|
||||
|
||||
// Mechanism used by this op kernel invocation to register a callback
|
||||
// for its cancellation.
|
||||
CancellationManager* cancellation_manager = nullptr;
|
||||
@ -841,6 +848,12 @@ class OpKernelContext {
|
||||
// Rendezvous Send() and Recv().
|
||||
Rendezvous* rendezvous() const { return params_->rendezvous; }
|
||||
|
||||
// An op kernel can access the session state it belongs to.
|
||||
SessionState* session_state() const { return params_->session_state; }
|
||||
|
||||
// An op kernel can access the tensor store of the run it belongs to.
|
||||
TensorStore* tensor_store() const { return params_->tensor_store; }
|
||||
|
||||
// Function call support.
|
||||
//
|
||||
// If this kernel invocation is within a function execution,
|
||||
@ -1031,15 +1044,16 @@ typedef ::tensorflow::KernelDefBuilder Name;
|
||||
#define REGISTER_KERNEL_BUILDER_UNIQ_HELPER(ctr, kernel_builder, ...) \
|
||||
REGISTER_KERNEL_BUILDER_UNIQ(ctr, kernel_builder, __VA_ARGS__)
|
||||
|
||||
#define REGISTER_KERNEL_BUILDER_UNIQ(ctr, kernel_builder, ...) \
|
||||
static ::tensorflow::kernel_factory::OpKernelRegistrar \
|
||||
registrar__body__##ctr##__object( \
|
||||
SHOULD_REGISTER_OP_KERNEL(#__VA_ARGS__) \
|
||||
? ::tensorflow::register_kernel::kernel_builder.Build() \
|
||||
: nullptr, \
|
||||
#__VA_ARGS__, \
|
||||
[](::tensorflow::OpKernelConstruction* context) \
|
||||
-> ::tensorflow::OpKernel* { return new __VA_ARGS__(context); })
|
||||
#define REGISTER_KERNEL_BUILDER_UNIQ(ctr, kernel_builder, ...) \
|
||||
static ::tensorflow::kernel_factory::OpKernelRegistrar \
|
||||
registrar__body__##ctr##__object( \
|
||||
SHOULD_REGISTER_OP_KERNEL(#__VA_ARGS__) \
|
||||
? ::tensorflow::register_kernel::kernel_builder.Build() \
|
||||
: nullptr, \
|
||||
#__VA_ARGS__, [](::tensorflow::OpKernelConstruction* context) \
|
||||
-> ::tensorflow::OpKernel* { \
|
||||
return new __VA_ARGS__(context); \
|
||||
});
|
||||
|
||||
void* GlobalKernelRegistry();
|
||||
|
||||
|
@ -120,8 +120,8 @@ class OpKernelTest : public ::testing::Test {
|
||||
void ExpectEqual(const string& what, const DataTypeVector& expected,
|
||||
const DataTypeVector& observed) {
|
||||
EXPECT_EQ(expected.size(), observed.size()) << what;
|
||||
const int size = std::min(expected.size(), observed.size());
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const size_t size = std::min(expected.size(), observed.size());
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
bool match = TypesCompatible(expected[i], observed[i]);
|
||||
EXPECT_TRUE(match) << what << " i:" << i << ", expected: " << expected[i]
|
||||
<< ", observed: " << observed[i];
|
||||
|
@ -47,65 +47,42 @@ limitations under the License.
|
||||
// Call "m" for all number types that support the comparison operations "<" and
|
||||
// ">".
|
||||
#define TF_CALL_REAL_NUMBER_TYPES(m) \
|
||||
m(float); \
|
||||
m(double); \
|
||||
m(int64); \
|
||||
m(int32); \
|
||||
m(uint8); \
|
||||
m(int16); \
|
||||
m(int8)
|
||||
m(float) m(double) m(int64) m(int32) m(uint8) m(int16) m(int8)
|
||||
|
||||
#define TF_CALL_REAL_NUMBER_TYPES_NO_INT32(m) \
|
||||
m(float); \
|
||||
m(double); \
|
||||
m(int64); \
|
||||
m(uint8); \
|
||||
m(int16); \
|
||||
m(int8)
|
||||
m(float) m(double) m(int64) m(uint8) m(int16) m(int8)
|
||||
|
||||
// Call "m" for all number types, including complex64 and complex128.
|
||||
#define TF_CALL_NUMBER_TYPES(m) \
|
||||
TF_CALL_REAL_NUMBER_TYPES(m); \
|
||||
m(complex64); \
|
||||
m(complex128)
|
||||
TF_CALL_REAL_NUMBER_TYPES(m) \
|
||||
m(complex64) m(complex128)
|
||||
|
||||
#define TF_CALL_NUMBER_TYPES_NO_INT32(m) \
|
||||
TF_CALL_REAL_NUMBER_TYPES_NO_INT32(m); \
|
||||
m(complex64); \
|
||||
m(complex128)
|
||||
TF_CALL_REAL_NUMBER_TYPES_NO_INT32(m) \
|
||||
m(complex64) m(complex128)
|
||||
|
||||
#define TF_CALL_POD_TYPES(m) \
|
||||
TF_CALL_NUMBER_TYPES(m); \
|
||||
TF_CALL_NUMBER_TYPES(m) \
|
||||
m(bool)
|
||||
|
||||
// Call "m" on all types.
|
||||
#define TF_CALL_ALL_TYPES(m) \
|
||||
TF_CALL_POD_TYPES(m); \
|
||||
TF_CALL_POD_TYPES(m) \
|
||||
m(string)
|
||||
|
||||
// Call "m" on all types supported on GPU.
|
||||
#define TF_CALL_GPU_NUMBER_TYPES(m) \
|
||||
m(float); \
|
||||
m(double)
|
||||
#define TF_CALL_GPU_NUMBER_TYPES(m) m(float) m(double)
|
||||
|
||||
// Call "m" on all quantized types.
|
||||
#define TF_CALL_QUANTIZED_TYPES(m) \
|
||||
m(qint8); \
|
||||
m(quint8); \
|
||||
m(qint32)
|
||||
#define TF_CALL_QUANTIZED_TYPES(m) m(qint8) m(quint8) m(qint32)
|
||||
|
||||
#elif defined(__ANDROID_TYPES_FULL__)
|
||||
|
||||
#define TF_CALL_REAL_NUMBER_TYPES(m) \
|
||||
m(float); \
|
||||
m(int32); \
|
||||
m(int64)
|
||||
#define TF_CALL_REAL_NUMBER_TYPES(m) m(float) m(int32) m(int64)
|
||||
|
||||
#define TF_CALL_NUMBER_TYPES(m) TF_CALL_REAL_NUMBER_TYPES(m)
|
||||
|
||||
#define TF_CALL_REAL_NUMBER_TYPES_NO_INT32(m) \
|
||||
m(float); \
|
||||
m(int64)
|
||||
#define TF_CALL_REAL_NUMBER_TYPES_NO_INT32(m) m(float) m(int64)
|
||||
|
||||
#define TF_CALL_NUMBER_TYPES_NO_INT32(m) TF_CALL_REAL_NUMBER_TYPES_NO_INT32(m)
|
||||
|
||||
@ -117,16 +94,11 @@ limitations under the License.
|
||||
#define TF_CALL_GPU_NUMBER_TYPES(m) m(float)
|
||||
|
||||
// Call "m" on all quantized types.
|
||||
#define TF_CALL_QUANTIZED_TYPES(m) \
|
||||
m(qint8); \
|
||||
m(quint8); \
|
||||
m(qint32)
|
||||
#define TF_CALL_QUANTIZED_TYPES(m) m(qint8) m(quint8) m(qint32)
|
||||
|
||||
#else // defined(__ANDROID__) && !defined(__ANDROID_TYPES_FULL__)
|
||||
|
||||
#define TF_CALL_REAL_NUMBER_TYPES(m) \
|
||||
m(float); \
|
||||
m(int32)
|
||||
#define TF_CALL_REAL_NUMBER_TYPES(m) m(float) m(int32)
|
||||
|
||||
#define TF_CALL_NUMBER_TYPES(m) TF_CALL_REAL_NUMBER_TYPES(m)
|
||||
|
||||
|
85
tensorflow/core/framework/session_state.h
Normal file
85
tensorflow/core/framework/session_state.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_FRAMEWORK_SESSION_STATE_H_
|
||||
#define TENSORFLOW_FRAMEWORK_SESSION_STATE_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/lib/core/errors.h"
|
||||
#include "tensorflow/core/platform/mutex.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
// The session state remembers the tensors we choose to keep across
|
||||
// multiple run calls.
|
||||
class SessionState {
|
||||
public:
|
||||
// Get a tensor from the session state.
|
||||
Status GetTensor(const string& handle, Tensor* tensor);
|
||||
|
||||
// Store a tensor in the session state.
|
||||
Status AddTensor(const string& handle, const Tensor& tensor);
|
||||
|
||||
// Delete a tensdor from the session state.
|
||||
Status DeleteTensor(const string& handle);
|
||||
|
||||
int64 GetNewId();
|
||||
|
||||
private:
|
||||
mutex state_lock_;
|
||||
|
||||
// For generating unique ids for tensors stored in the session.
|
||||
int64 tensor_id_ = 0;
|
||||
|
||||
// The live tensors in the session. A map from tensor handle to tensor.
|
||||
std::unordered_map<string, Tensor> tensors_;
|
||||
};
|
||||
|
||||
// The tensor store remembers the tensors we choose to keep for the
|
||||
// current run call. It is available to every op kernel.
|
||||
class TensorStore {
|
||||
public:
|
||||
struct TensorAndKey {
|
||||
Tensor tensor;
|
||||
int64 id;
|
||||
string device_name;
|
||||
|
||||
string GetHandle(const string& tensor_name) {
|
||||
return strings::StrCat(tensor_name, ";", id, ";", device_name);
|
||||
}
|
||||
};
|
||||
|
||||
// Add the named tensor to the tensor store for this run.
|
||||
Status AddTensor(const string& name, const TensorAndKey& tk);
|
||||
|
||||
// Save the tensors in the tensor store of this run to the session.
|
||||
Status SaveTensors(const std::vector<string>& output_names,
|
||||
SessionState* session_state);
|
||||
|
||||
private:
|
||||
mutex lock_;
|
||||
|
||||
// The tensors that will be saved to session state when this run completes.
|
||||
// A map from tensor string name to tensor.
|
||||
std::unordered_map<string, TensorAndKey> tensors_ GUARDED_BY(lock_);
|
||||
};
|
||||
|
||||
} // namespace tensorflow
|
||||
|
||||
#endif // TENSORFLOW_FRAMEWORK_SESSION_STATE_H_
|
@ -44,6 +44,7 @@ void TensorShape::CheckDimsAtLeast(int NDIMS) const {
|
||||
|
||||
bool TensorShape::IsValid(const TensorShapeProto& proto) {
|
||||
int64 num_elements = 1;
|
||||
if (proto.dim().size() > MaxDimensions()) return false;
|
||||
for (const auto& d : proto.dim()) {
|
||||
if (d.size() < 0) return false;
|
||||
num_elements *= d.size();
|
||||
@ -54,6 +55,10 @@ bool TensorShape::IsValid(const TensorShapeProto& proto) {
|
||||
|
||||
Status TensorShape::IsValidShape(const TensorShapeProto& proto) {
|
||||
int64 num_elements = 1;
|
||||
if (proto.dim().size() > MaxDimensions()) {
|
||||
return errors::InvalidArgument("Shape ", DebugString(proto),
|
||||
" has too many dimensions");
|
||||
}
|
||||
for (const auto& d : proto.dim()) {
|
||||
if (d.size() < 0) {
|
||||
return errors::InvalidArgument("Shape ", DebugString(proto),
|
||||
@ -165,7 +170,7 @@ void TensorShape::RecomputeNumElements() {
|
||||
void TensorShape::AddDim(int64 size) {
|
||||
CHECK_GE(size, 0);
|
||||
const int nd = ndims_byte();
|
||||
CHECK_LT(nd, 255) << "Too many dimensions in tensor";
|
||||
CHECK_LT(nd, MaxDimensions()) << "Too many dimensions in tensor";
|
||||
if (tag() == REP16 && nd < 6 && size < kMaxRep16) {
|
||||
as16()->dims_[nd] = static_cast<int16>(size);
|
||||
} else if (tag() == REP32 && nd < 3 && size < kMaxRep32) {
|
||||
@ -214,6 +219,7 @@ void TensorShape::InsertDim(int d, int64 size) {
|
||||
CHECK_GE(d, 0);
|
||||
CHECK_LE(d, dims());
|
||||
CHECK_GE(size, 0);
|
||||
CHECK_LT(dims(), MaxDimensions());
|
||||
gtl::InlinedVector<int64, 8> vals;
|
||||
AppendTo(*this, &vals);
|
||||
vals.insert(vals.begin() + d, size);
|
||||
@ -341,6 +347,9 @@ bool TensorShapeUtils::StartsWith(const TensorShape& shape,
|
||||
template <typename T>
|
||||
static inline Status MakeShapeHelper(const T* dims, int n, TensorShape* out) {
|
||||
*out = TensorShape();
|
||||
if (n > TensorShape::MaxDimensions()) {
|
||||
return errors::InvalidArgument("Too many dimensions");
|
||||
}
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const T dim = internal::SubtleMustCopy(dims[i]);
|
||||
if (dim >= 0) {
|
||||
|
@ -71,6 +71,9 @@ class TensorShape {
|
||||
/// Appends all the dimensions from `shape`.
|
||||
void AppendShape(const TensorShape& shape);
|
||||
|
||||
// Maximum number of dimensions in a tensor.
|
||||
static constexpr int MaxDimensions() { return 255; }
|
||||
|
||||
/// \brief Insert a dimension somewhere in the `TensorShape`.
|
||||
/// REQUIRES: `0 <= d <= dims()`
|
||||
/// REQUIRES: `size >= 0`
|
||||
@ -277,6 +280,7 @@ template <int NDIMS>
|
||||
Eigen::DSizes<Eigen::DenseIndex, NDIMS> TensorShape::AsEigenDSizesWithPadding()
|
||||
const {
|
||||
CheckDimsAtLeast(NDIMS);
|
||||
static_assert(NDIMS <= TensorShape::MaxDimensions(), "Too many dimensions");
|
||||
Eigen::DSizes<Eigen::DenseIndex, NDIMS> dsizes;
|
||||
for (int d = 0; d < dims(); d++) {
|
||||
dsizes[d] = dim_size(d);
|
||||
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||
|
||||
#include "tensorflow/core/framework/tensor_shape.h"
|
||||
|
||||
#include "tensorflow/core/lib/core/status_test_util.h"
|
||||
#include "tensorflow/core/lib/random/simple_philox.h"
|
||||
#include "tensorflow/core/lib/strings/str_util.h"
|
||||
#include "tensorflow/core/lib/strings/strcat.h"
|
||||
@ -87,6 +88,21 @@ TEST(TensorShapeTest, InvalidShapeProto) {
|
||||
EXPECT_FALSE(TensorShape::IsValid(proto));
|
||||
}
|
||||
|
||||
TEST(TensorShapeTest, TooManyDimsProto) {
|
||||
TensorShapeProto proto;
|
||||
// Deliberate redundancy to ensure that both paths work.
|
||||
EXPECT_TRUE(TensorShape::IsValid(proto));
|
||||
TF_EXPECT_OK(TensorShape::IsValidShape(proto));
|
||||
for (int i = 0; i < TensorShape::MaxDimensions(); i++) {
|
||||
proto.add_dim()->set_size(1);
|
||||
}
|
||||
EXPECT_TRUE(TensorShape::IsValid(proto));
|
||||
TF_EXPECT_OK(TensorShape::IsValidShape(proto));
|
||||
proto.add_dim()->set_size(1);
|
||||
EXPECT_FALSE(TensorShape::IsValid(proto));
|
||||
EXPECT_FALSE(TensorShape::IsValidShape(proto).ok());
|
||||
}
|
||||
|
||||
TEST(TensorShapeTest, SetDimForEmptyTensor) {
|
||||
TensorShape s({10, 5, 20});
|
||||
EXPECT_EQ(1000, s.num_elements());
|
||||
|
@ -95,6 +95,9 @@ void Node::Initialize(int id, int cost_id, Properties* props) {
|
||||
SET_CLASS(NC_CONSTANT, ts, "Const", "HostConst");
|
||||
SET_CLASS(NC_VARIABLE, ts, "Variable", "");
|
||||
SET_CLASS(NC_IDENTITY, ts, "Identity", "RefIdentity");
|
||||
SET_CLASS(NC_GET_SESSION_HANDLE, ts, "GetSessionHandle", "");
|
||||
SET_CLASS(NC_GET_SESSION_TENSOR, ts, "GetSessionTensor", "");
|
||||
SET_CLASS(NC_DELETE_SESSION_TENSOR, ts, "DeleteSessionTensor", "");
|
||||
if (class_ == NC_UNINITIALIZED) {
|
||||
class_ = NC_OTHER; // Catch all
|
||||
}
|
||||
|
@ -118,6 +118,11 @@ class Node {
|
||||
bool IsConstant() const { return (class_ == NC_CONSTANT); }
|
||||
bool IsVariable() const { return (class_ == NC_VARIABLE); }
|
||||
bool IsIdentity() const { return (class_ == NC_IDENTITY); }
|
||||
bool IsGetSessionHandle() const { return (class_ == NC_GET_SESSION_HANDLE); }
|
||||
bool IsGetSessionTensor() const { return (class_ == NC_GET_SESSION_TENSOR); }
|
||||
bool IsDeleteSessionTensor() const {
|
||||
return (class_ == NC_DELETE_SESSION_TENSOR);
|
||||
}
|
||||
bool IsControlFlow() const {
|
||||
return (class_ != NC_OTHER) && // Fast path
|
||||
(IsSwitch() || IsMerge() || IsEnter() || IsExit() ||
|
||||
@ -172,6 +177,9 @@ class Node {
|
||||
NC_CONSTANT,
|
||||
NC_VARIABLE,
|
||||
NC_IDENTITY,
|
||||
NC_GET_SESSION_HANDLE,
|
||||
NC_GET_SESSION_TENSOR,
|
||||
NC_DELETE_SESSION_TENSOR,
|
||||
NC_OTHER // Not a special kind of node
|
||||
};
|
||||
|
||||
|
@ -360,6 +360,15 @@ Node* Gather(Graph* g, Node* in0, Node* in1) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
Node* GetSessionTensor(Graph* g, Node* in) {
|
||||
Node* ret;
|
||||
TF_CHECK_OK(NodeBuilder(g->NewName("n"), "GetSessionTensor")
|
||||
.Input(in, 0)
|
||||
.Attr("dtype", DT_FLOAT)
|
||||
.Finalize(g, &ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ToGraphDef(Graph* g, GraphDef* gdef) { g->ToGraphDef(gdef); }
|
||||
|
||||
} // end namespace graph
|
||||
|
@ -161,6 +161,9 @@ Node* Gather(Graph* g, Node* in0, Node* in1);
|
||||
// Computes the args needed broadcast gradient function.
|
||||
Node* BroadcastGradientArgs(Graph* g, Node* s0, Node* s1);
|
||||
|
||||
// Gets a tensor stored in the session state.
|
||||
Node* GetSessionTensor(Graph* g, Node* in);
|
||||
|
||||
} // end namespace graph
|
||||
} // end namespace test
|
||||
} // end namespace tensorflow
|
||||
|
@ -173,6 +173,7 @@ cc_library(
|
||||
srcs = ["save_restore_tensor.cc"],
|
||||
hdrs = ["save_restore_tensor.h"],
|
||||
deps = [
|
||||
":bounds_check",
|
||||
"//tensorflow/core:framework",
|
||||
"//tensorflow/core:lib",
|
||||
],
|
||||
@ -261,6 +262,7 @@ tf_kernel_libraries(
|
||||
"concat_op",
|
||||
"constant_op",
|
||||
"diag_op",
|
||||
"batch_matrix_diag_op",
|
||||
"edit_distance_op",
|
||||
"gather_nd_op",
|
||||
"gather_op",
|
||||
@ -337,6 +339,23 @@ tf_cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
tf_cc_test(
|
||||
name = "example_parsing_ops_test",
|
||||
size = "small",
|
||||
deps = [
|
||||
":example_parsing_ops",
|
||||
":ops_testutil",
|
||||
":ops_util",
|
||||
"//tensorflow/core:core_cpu",
|
||||
"//tensorflow/core:framework",
|
||||
"//tensorflow/core:lib",
|
||||
"//tensorflow/core:protos_all_cc",
|
||||
"//tensorflow/core:test",
|
||||
"//tensorflow/core:test_main",
|
||||
"//tensorflow/core:testlib",
|
||||
],
|
||||
)
|
||||
|
||||
tf_cuda_cc_test(
|
||||
name = "gather_op_test",
|
||||
size = "small",
|
||||
@ -523,6 +542,7 @@ tf_kernel_libraries(
|
||||
"padding_fifo_queue_op",
|
||||
"queue_ops",
|
||||
"random_shuffle_queue_op",
|
||||
"session_ops",
|
||||
"stack_ops",
|
||||
"tensor_array_ops",
|
||||
],
|
||||
@ -593,14 +613,16 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
tf_kernel_library(
|
||||
name = "tensor_array",
|
||||
srcs = ["tensor_array.cc"],
|
||||
hdrs = ["tensor_array.h"],
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
":aggregate_ops",
|
||||
"//tensorflow/core:framework",
|
||||
"//tensorflow/core:lib",
|
||||
"//third_party/eigen3",
|
||||
],
|
||||
)
|
||||
|
||||
@ -1282,6 +1304,7 @@ tf_kernel_libraries(
|
||||
name = "string",
|
||||
prefixes = [
|
||||
"string_to_hash_bucket_op",
|
||||
"reduce_join_op",
|
||||
],
|
||||
deps = [
|
||||
"//tensorflow/core:framework",
|
||||
@ -1497,6 +1520,7 @@ filegroup(
|
||||
"restore_op.cc",
|
||||
"save_op.cc",
|
||||
"save_restore_tensor.cc",
|
||||
"session_ops.cc",
|
||||
"softplus_op.cc",
|
||||
"softsign_op.cc",
|
||||
"sparse_to_dense_op.cc",
|
||||
|
232
tensorflow/core/kernels/batch_matrix_diag_op.cc
Normal file
232
tensorflow/core/kernels/batch_matrix_diag_op.cc
Normal file
@ -0,0 +1,232 @@
|
||||
/* Copyright 2015 Google Inc. 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/array_ops.cc.
|
||||
|
||||
#define EIGEN_USE_THREADS
|
||||
|
||||
#if GOOGLE_CUDA
|
||||
#define EIGEN_USE_GPU
|
||||
#endif // GOOGLE_CUDA
|
||||
|
||||
#include "tensorflow/core/kernels/batch_matrix_diag_op.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
|
||||
#include "tensorflow/core/framework/op_kernel.h"
|
||||
#include "tensorflow/core/framework/register_types.h"
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/framework/tensor_shape.h"
|
||||
#include "tensorflow/core/framework/tensor_types.h"
|
||||
#include "tensorflow/core/framework/types.h"
|
||||
#include "tensorflow/core/platform/logging.h"
|
||||
#include "tensorflow/core/platform/macros.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
typedef Eigen::ThreadPoolDevice CPUDevice;
|
||||
typedef Eigen::GpuDevice GPUDevice;
|
||||
|
||||
template <typename T>
|
||||
inline typename TTypes<T, 3>::ConstTensor flat_inner_dims_matrix(
|
||||
const Tensor& t) {
|
||||
int64 last_size = t.dims() > 1 ? t.dim_size(t.dims() - 1) : 1;
|
||||
int64 but_last_size = t.dims() > 1 ? t.dim_size(t.dims() - 2) : 1;
|
||||
if (last_size * but_last_size == 0) {
|
||||
DCHECK_EQ(t.NumElements(), 0);
|
||||
// Return something empty, avoiding divide by 0
|
||||
return t.shaped<T, 3>({0, 0, 0});
|
||||
} else {
|
||||
return t.shaped<T, 3>({t.NumElements() / (but_last_size * last_size),
|
||||
but_last_size, last_size});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline typename TTypes<T, 3>::Tensor flat_inner_dims_matrix(Tensor* t) {
|
||||
int64 last_size = t->dims() > 1 ? t->dim_size(t->dims() - 1) : 1;
|
||||
int64 but_last_size = t->dims() > 1 ? t->dim_size(t->dims() - 2) : 1;
|
||||
if (last_size * but_last_size == 0) {
|
||||
DCHECK_EQ(t->NumElements(), 0);
|
||||
// Return something empty, avoiding divide by 0
|
||||
return t->shaped<T, 3>({0, 0, 0});
|
||||
} else {
|
||||
return t->shaped<T, 3>({t->NumElements() / (but_last_size * last_size),
|
||||
but_last_size, last_size});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Device, typename T>
|
||||
class BatchMatrixDiagPartOp : public OpKernel {
|
||||
public:
|
||||
explicit BatchMatrixDiagPartOp(OpKernelConstruction* context)
|
||||
: OpKernel(context) {}
|
||||
|
||||
void Compute(OpKernelContext* context) override {
|
||||
const Tensor& input = context->input(0);
|
||||
|
||||
const TensorShape& input_shape = input.shape();
|
||||
const int rank = input_shape.dims();
|
||||
|
||||
// Preliminary validation of sizes.
|
||||
OP_REQUIRES(context, TensorShapeUtils::IsMatrixOrHigher(input_shape),
|
||||
errors::InvalidArgument(
|
||||
"input must be at least 2-dim, received shape: ",
|
||||
input.shape().DebugString()));
|
||||
|
||||
// Check to make sure the last two dimensions have the same value
|
||||
const int64 k = input_shape.dim_size(rank - 1);
|
||||
OP_REQUIRES(
|
||||
context, k == input_shape.dim_size(rank - 2),
|
||||
errors::InvalidArgument(
|
||||
"input's last two dimensions must be equal, received shape: ",
|
||||
input.shape().DebugString()));
|
||||
|
||||
auto input_reshaped = flat_inner_dims_matrix<T>(input);
|
||||
|
||||
TensorShape output_shape = input_shape;
|
||||
output_shape.RemoveDim(rank - 1);
|
||||
|
||||
Tensor* output = nullptr;
|
||||
OP_REQUIRES_OK(context, context->allocate_output(0, output_shape, &output));
|
||||
|
||||
auto output_reshaped = output->flat_inner_dims<T>();
|
||||
|
||||
functor::BatchMatrixDiagPart<Device, T>::Compute(
|
||||
context->eigen_device<Device>(), input_reshaped, output_reshaped);
|
||||
}
|
||||
|
||||
private:
|
||||
TF_DISALLOW_COPY_AND_ASSIGN(BatchMatrixDiagPartOp);
|
||||
};
|
||||
|
||||
template <typename Device, typename T>
|
||||
class BatchMatrixDiagOp : public OpKernel {
|
||||
public:
|
||||
explicit BatchMatrixDiagOp(OpKernelConstruction* context)
|
||||
: OpKernel(context) {}
|
||||
|
||||
void Compute(OpKernelContext* context) override {
|
||||
const Tensor& input = context->input(0);
|
||||
|
||||
const TensorShape& input_shape = input.shape();
|
||||
const int rank = input_shape.dims();
|
||||
|
||||
// Preliminary validation of sizes.
|
||||
OP_REQUIRES(context, TensorShapeUtils::IsVectorOrHigher(input_shape),
|
||||
errors::InvalidArgument(
|
||||
"input must be at least 1-dim, received shape: ",
|
||||
input.shape().DebugString()));
|
||||
|
||||
// Check to make sure the last two dimensions have the same value
|
||||
const int64 k = input_shape.dim_size(rank - 1);
|
||||
auto input_reshaped = input.flat_inner_dims<T>();
|
||||
|
||||
TensorShape output_shape = input_shape;
|
||||
output_shape.AddDim(k);
|
||||
|
||||
Tensor* output = nullptr;
|
||||
OP_REQUIRES_OK(context, context->allocate_output(0, output_shape, &output));
|
||||
|
||||
auto output_reshaped = flat_inner_dims_matrix<T>(output);
|
||||
|
||||
functor::BatchMatrixDiag<Device, T>::Compute(
|
||||
context->eigen_device<Device>(), input_reshaped, output_reshaped);
|
||||
}
|
||||
|
||||
private:
|
||||
TF_DISALLOW_COPY_AND_ASSIGN(BatchMatrixDiagOp);
|
||||
};
|
||||
|
||||
#define REGISTER_BATCH_MATRIX_DIAG(type) \
|
||||
REGISTER_KERNEL_BUILDER( \
|
||||
Name("BatchMatrixDiag").Device(DEVICE_CPU).TypeConstraint<type>("T"), \
|
||||
BatchMatrixDiagOp<CPUDevice, type>); \
|
||||
REGISTER_KERNEL_BUILDER(Name("BatchMatrixDiagPart") \
|
||||
.Device(DEVICE_CPU) \
|
||||
.TypeConstraint<type>("T"), \
|
||||
BatchMatrixDiagPartOp<CPUDevice, type>);
|
||||
|
||||
TF_CALL_NUMBER_TYPES(REGISTER_BATCH_MATRIX_DIAG);
|
||||
|
||||
// Implementation of the functor specialization for CPU.
|
||||
namespace functor {
|
||||
template <typename T>
|
||||
struct BatchMatrixDiag<CPUDevice, T> {
|
||||
static void Compute(const CPUDevice& d,
|
||||
typename TTypes<T, 2>::ConstTensor input,
|
||||
typename TTypes<T, 3>::Tensor output) {
|
||||
output.device(d) = output.constant(T());
|
||||
for (int64 r = 0; r < output.dimension(0); ++r) {
|
||||
for (int64 d = 0; d < output.dimension(1); ++d) {
|
||||
output(r, d, d) = input(r, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BatchMatrixDiagPart<CPUDevice, T> {
|
||||
static void Compute(const CPUDevice& d,
|
||||
typename TTypes<T, 3>::ConstTensor input,
|
||||
typename TTypes<T, 2>::Tensor output) {
|
||||
for (int64 r = 0; r < output.dimension(0); ++r) {
|
||||
for (int64 d = 0; d < output.dimension(1); ++d) {
|
||||
output(r, d) = input(r, d, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace functor
|
||||
|
||||
#if GOOGLE_CUDA
|
||||
|
||||
// Forward declarations of the functor specializations for GPU.
|
||||
namespace functor {
|
||||
#define DECLARE_GPU_SPEC(T) \
|
||||
template <> \
|
||||
void BatchMatrixDiag<GPUDevice, T>::Compute( \
|
||||
const GPUDevice& d, typename TTypes<T, 2>::ConstTensor input, \
|
||||
typename TTypes<T, 3>::Tensor output); \
|
||||
extern template struct BatchMatrixDiag<GPUDevice, T>; \
|
||||
template <> \
|
||||
void BatchMatrixDiagPart<GPUDevice, T>::Compute( \
|
||||
const GPUDevice& d, typename TTypes<T, 3>::ConstTensor input, \
|
||||
typename TTypes<T, 2>::Tensor output); \
|
||||
extern template struct BatchMatrixDiagPart<GPUDevice, T>;
|
||||
|
||||
TF_CALL_GPU_NUMBER_TYPES(DECLARE_GPU_SPEC);
|
||||
|
||||
} // namespace functor
|
||||
|
||||
// Registration of the GPU implementations.
|
||||
#define REGISTER_BATCH_MATRIX_DIAG_GPU(type) \
|
||||
REGISTER_KERNEL_BUILDER( \
|
||||
Name("BatchMatrixDiag").Device(DEVICE_GPU).TypeConstraint<type>("T"), \
|
||||
BatchMatrixDiagOp<GPUDevice, type>); \
|
||||
REGISTER_KERNEL_BUILDER(Name("BatchMatrixDiagPart") \
|
||||
.Device(DEVICE_GPU) \
|
||||
.TypeConstraint<type>("T"), \
|
||||
BatchMatrixDiagPartOp<GPUDevice, type>);
|
||||
|
||||
TF_CALL_GPU_NUMBER_TYPES(REGISTER_BATCH_MATRIX_DIAG_GPU);
|
||||
|
||||
#undef REGISTER_BATCH_MATRIX_DIAG_GPU
|
||||
|
||||
#endif // GOOGLE_CUDA
|
||||
|
||||
} // namespace tensorflow
|
94
tensorflow/core/kernels/batch_matrix_diag_op.h
Normal file
94
tensorflow/core/kernels/batch_matrix_diag_op.h
Normal file
@ -0,0 +1,94 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_KERNELS_BATCH_MATRIX_DIAG_OP_H_
|
||||
#define TENSORFLOW_KERNELS_BATCH_MATRIX_DIAG_OP_H_
|
||||
|
||||
// Generator definition for BatchMatrixDiagOp, must be compilable by nvcc.
|
||||
|
||||
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
|
||||
#include "tensorflow/core/framework/tensor_types.h"
|
||||
#include "tensorflow/core/platform/types.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
namespace generator {
|
||||
|
||||
template <typename T>
|
||||
class BatchMatrixDiagPartGenerator {
|
||||
public:
|
||||
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE
|
||||
BatchMatrixDiagPartGenerator(typename TTypes<T, 3>::ConstTensor input)
|
||||
: input_(input) {}
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE T
|
||||
operator()(const Eigen::array<Eigen::DenseIndex, 2>& coords) const {
|
||||
Eigen::array<Eigen::DenseIndex, 3> diag_from_coords(
|
||||
{coords[0], coords[1], coords[1]});
|
||||
return input_(diag_from_coords);
|
||||
}
|
||||
|
||||
private:
|
||||
typename TTypes<T, 3>::ConstTensor input_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class BatchMatrixDiagGenerator {
|
||||
public:
|
||||
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE
|
||||
BatchMatrixDiagGenerator(typename TTypes<T, 2>::ConstTensor input)
|
||||
: input_(input) {}
|
||||
|
||||
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE T
|
||||
operator()(const Eigen::array<Eigen::DenseIndex, 3>& coords) const {
|
||||
if (coords[2] != coords[1]) return T();
|
||||
|
||||
Eigen::array<Eigen::DenseIndex, 2> diag_coords({coords[0], coords[1]});
|
||||
return input_(diag_coords);
|
||||
}
|
||||
|
||||
private:
|
||||
typename TTypes<T, 2>::ConstTensor input_;
|
||||
};
|
||||
|
||||
} // namespace generator
|
||||
|
||||
namespace functor {
|
||||
|
||||
template <typename Device, typename T>
|
||||
struct BatchMatrixDiagPart {
|
||||
EIGEN_ALWAYS_INLINE static void Compute(
|
||||
const Device& d, typename TTypes<T, 3>::ConstTensor input,
|
||||
typename TTypes<T, 2>::Tensor output) {
|
||||
generator::BatchMatrixDiagPartGenerator<T> generator(input);
|
||||
output.device(d) = output.generate(generator);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Device, typename T>
|
||||
struct BatchMatrixDiag {
|
||||
EIGEN_ALWAYS_INLINE static void Compute(
|
||||
const Device& d, typename TTypes<T, 2>::ConstTensor input,
|
||||
typename TTypes<T, 3>::Tensor output) {
|
||||
generator::BatchMatrixDiagGenerator<T> generator(input);
|
||||
output.device(d) = output.generate(generator);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace functor
|
||||
|
||||
} // namespace tensorflow
|
||||
|
||||
#endif // TENSORFLOW_KERNELS_BATCH_MATRIX_DIAG_OP_H_
|
37
tensorflow/core/kernels/batch_matrix_diag_op_gpu.cu.cc
Normal file
37
tensorflow/core/kernels/batch_matrix_diag_op_gpu.cu.cc
Normal file
@ -0,0 +1,37 @@
|
||||
/* Copyright 2015 Google Inc. 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.
|
||||
==============================================================================*/
|
||||
|
||||
#if GOOGLE_CUDA
|
||||
|
||||
#define EIGEN_USE_GPU
|
||||
|
||||
#include "tensorflow/core/framework/register_types.h"
|
||||
#include "tensorflow/core/kernels/batch_matrix_diag_op.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
typedef Eigen::GpuDevice GPUDevice;
|
||||
|
||||
#define DEFINE_GPU_SPEC(T) \
|
||||
template class generator::BatchMatrixDiagGenerator<T>; \
|
||||
template struct functor::BatchMatrixDiag<GPUDevice, T>; \
|
||||
template class generator::BatchMatrixDiagPartGenerator<T>; \
|
||||
template struct functor::BatchMatrixDiagPart<GPUDevice, T>;
|
||||
|
||||
TF_CALL_GPU_NUMBER_TYPES(DEFINE_GPU_SPEC);
|
||||
|
||||
} // end namespace tensorflow
|
||||
|
||||
#endif // GOOGLE_CUDA
|
@ -60,7 +60,7 @@ class CheckNumericsOp<CPUDevice, T> : public OpKernel {
|
||||
|
||||
auto in = context->input(0).flat<T>();
|
||||
const T* data = in.data();
|
||||
const int size = in.size();
|
||||
const int64 size = in.size();
|
||||
// Check to see if any element of the tensor is NaN or Inf.
|
||||
int fp_props =
|
||||
std::accumulate(data, data + size, 0, [](const int& x, const T& y) {
|
||||
|
@ -23,8 +23,10 @@ limitations under the License.
|
||||
#include "tensorflow/core/framework/register_types.h"
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/framework/tensor.pb.h"
|
||||
#include "tensorflow/core/framework/tensor_shape.h"
|
||||
#include "tensorflow/core/framework/tensor_types.h"
|
||||
#include "tensorflow/core/framework/types.h"
|
||||
#include "tensorflow/core/kernels/bounds_check.h"
|
||||
#include "tensorflow/core/kernels/fill_functor.h"
|
||||
#include "tensorflow/core/platform/macros.h"
|
||||
|
||||
@ -145,6 +147,10 @@ class FillOp : public OpKernel {
|
||||
errors::InvalidArgument("value must be a scalar, got shape ",
|
||||
Tvalue.shape().DebugString()));
|
||||
auto dims = Tdims.flat<int32>();
|
||||
OP_REQUIRES(context,
|
||||
FastBoundsCheck(dims.size(), TensorShape::MaxDimensions()),
|
||||
errors::InvalidArgument("dims must have size < ",
|
||||
TensorShape::MaxDimensions()));
|
||||
for (int i = 0; i < dims.size(); i++) {
|
||||
OP_REQUIRES(context, dims(i) >= 0,
|
||||
errors::InvalidArgument("dims[", i, "] = ", dims(i),
|
||||
@ -153,7 +159,7 @@ class FillOp : public OpKernel {
|
||||
TensorShape shape;
|
||||
OP_REQUIRES_OK(context, TensorShapeUtils::MakeShape(
|
||||
reinterpret_cast<const int32*>(dims.data()),
|
||||
dims.size(), &shape));
|
||||
static_cast<int>(dims.size()), &shape));
|
||||
Tensor* out = nullptr;
|
||||
OP_REQUIRES_OK(context, context->allocate_output(0, shape, &out));
|
||||
functor::FillFunctor<Device, T> functor;
|
||||
|
@ -78,7 +78,7 @@ struct FillFunctor<GPUDevice, T> {
|
||||
}
|
||||
};
|
||||
|
||||
#define DEFINE_FILL_GPU(T) template struct FillFunctor<GPUDevice, T>
|
||||
#define DEFINE_FILL_GPU(T) template struct FillFunctor<GPUDevice, T>;
|
||||
TF_CALL_REAL_NUMBER_TYPES(DEFINE_FILL_GPU);
|
||||
DEFINE_FILL_GPU(bool);
|
||||
DEFINE_FILL_GPU(Eigen::half);
|
||||
|
@ -341,62 +341,6 @@ struct cos : base<T, Eigen::internal::scalar_cos_op<T> > {};
|
||||
struct logical_not : base<bool, Eigen::internal::scalar_boolean_not_op<bool> > {
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
#ifndef __CUDACC__
|
||||
// Uses STL std cmath functions.
|
||||
template <typename T>
|
||||
bool isinf(T v) {
|
||||
return std::isinf(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool isnan(T v) {
|
||||
return std::isnan(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool isfinite(T v) {
|
||||
return std::isfinite(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T floor(T v) {
|
||||
return std::floor(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ceil(T v) {
|
||||
return std::ceil(v);
|
||||
}
|
||||
#else
|
||||
// Uses CUDA's functions for float and double.
|
||||
template <typename T>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool isinf(T v) {
|
||||
return ::isinf(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool isnan(T v) {
|
||||
return ::isnan(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool isfinite(T v) {
|
||||
return ::isfinite(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T floor(T v) {
|
||||
return ::floor(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T ceil(T v) {
|
||||
return ::ceil(v);
|
||||
}
|
||||
#endif
|
||||
} // end namespace impl
|
||||
|
||||
// NOTE: std::isinf, std::isnan, std::isfinite are plain function.
|
||||
// Therefore we need to wrap them in functors to be used with Eigen's
|
||||
@ -406,7 +350,7 @@ template <typename T>
|
||||
struct isinf_func {
|
||||
typedef bool result_type;
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool operator()(T x) const {
|
||||
return impl::isinf(x);
|
||||
return Eigen::numext::isinf(x);
|
||||
}
|
||||
};
|
||||
|
||||
@ -417,7 +361,7 @@ template <typename T>
|
||||
struct isnan_func {
|
||||
typedef bool result_type;
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool operator()(T x) const {
|
||||
return impl::isnan(x);
|
||||
return Eigen::numext::isnan(x);
|
||||
}
|
||||
};
|
||||
|
||||
@ -428,7 +372,7 @@ template <typename T>
|
||||
struct isfinite_func {
|
||||
typedef bool result_type;
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool operator()(T x) const {
|
||||
return impl::isfinite(x);
|
||||
return Eigen::numext::isfinite(x);
|
||||
}
|
||||
};
|
||||
|
||||
@ -439,7 +383,7 @@ template <typename T>
|
||||
struct floor_func {
|
||||
typedef T result_type;
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T operator()(T x) const {
|
||||
return impl::floor(x);
|
||||
return Eigen::numext::floor(x);
|
||||
}
|
||||
};
|
||||
|
||||
@ -450,7 +394,7 @@ template <typename T>
|
||||
struct ceil_func {
|
||||
typedef T result_type;
|
||||
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T operator()(T x) const {
|
||||
return impl::ceil(x);
|
||||
return Eigen::numext::ceil(x);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -130,7 +130,7 @@ namespace functor {
|
||||
void DenseUpdate<GPUDevice, T, OP>::operator()( \
|
||||
const GPUDevice& d, typename TTypes<T>::Flat params, \
|
||||
typename TTypes<T>::ConstFlat update); \
|
||||
extern template struct DenseUpdate<GPUDevice, T, OP>
|
||||
extern template struct DenseUpdate<GPUDevice, T, OP>;
|
||||
#define DECLARE_GPU_SPEC(T) \
|
||||
DECLARE_GPU_SPEC_FOR_OP(T, DenseUpdateType::ADD); \
|
||||
DECLARE_GPU_SPEC_FOR_OP(T, DenseUpdateType::SUB)
|
||||
|
121
tensorflow/core/kernels/example_parsing_ops_test.cc
Normal file
121
tensorflow/core/kernels/example_parsing_ops_test.cc
Normal file
@ -0,0 +1,121 @@
|
||||
/* Copyright 2015 Google Inc. 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 <unordered_map>
|
||||
|
||||
#include "tensorflow/core/common_runtime/kernel_benchmark_testlib.h"
|
||||
#include "tensorflow/core/example/example.pb.h"
|
||||
#include "tensorflow/core/example/feature.pb.h"
|
||||
#include "tensorflow/core/framework/op.h"
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/framework/tensor_shape.h"
|
||||
#include "tensorflow/core/framework/tensor_types.h"
|
||||
#include "tensorflow/core/framework/types.pb.h"
|
||||
#include "tensorflow/core/graph/graph.h"
|
||||
#include "tensorflow/core/graph/node_builder.h"
|
||||
#include "tensorflow/core/lib/core/status_test_util.h"
|
||||
#include "tensorflow/core/lib/strings/stringprintf.h"
|
||||
#include "tensorflow/core/platform/test.h"
|
||||
#include "tensorflow/core/platform/test_benchmark.h"
|
||||
#include "tensorflow/core/platform/types.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
typedef std::map<std::pair<int, int>, Tensor> ExampleTensorMap;
|
||||
|
||||
struct DenseStringExampleStore {
|
||||
static ExampleTensorMap GetSerializedExamples() {
|
||||
ExampleTensorMap examples;
|
||||
int keys[] = {10, 100, 1000, 10000};
|
||||
int batch_sizes[] = {128};
|
||||
Example example;
|
||||
for (int num_keys : keys) {
|
||||
for (int batch_size : batch_sizes) {
|
||||
Tensor record_string(DT_STRING, TensorShape({batch_size}));
|
||||
auto string_t = record_string.vec<string>();
|
||||
example.Clear();
|
||||
for (int b = 0; b < batch_size; ++b) {
|
||||
for (int k = 0; k < num_keys; ++k) {
|
||||
string k_str = strings::Printf("%d", k);
|
||||
Feature f;
|
||||
f.mutable_bytes_list()->add_value("abc");
|
||||
Features* features = example.mutable_features();
|
||||
(*features->mutable_feature())[k_str] = f;
|
||||
}
|
||||
CHECK(example.SerializeToString(&string_t(b)));
|
||||
}
|
||||
examples[std::make_pair(batch_size, num_keys)] = record_string;
|
||||
}
|
||||
}
|
||||
return examples;
|
||||
}
|
||||
static ExampleTensorMap serialized_example;
|
||||
};
|
||||
|
||||
ExampleTensorMap DenseStringExampleStore::serialized_example =
|
||||
DenseStringExampleStore::GetSerializedExamples();
|
||||
|
||||
static Graph* ParseDenseStringExample(int batch_size, int num_keys) {
|
||||
Graph* g = new Graph(OpRegistry::Global());
|
||||
Tensor& serialized =
|
||||
DenseStringExampleStore::serialized_example[std::make_pair(batch_size,
|
||||
num_keys)];
|
||||
|
||||
Tensor names(DT_STRING, TensorShape({batch_size}));
|
||||
|
||||
std::vector<NodeBuilder::NodeOut> sparse_keys;
|
||||
std::vector<NodeBuilder::NodeOut> dense_keys;
|
||||
std::vector<NodeBuilder::NodeOut> dense_defaults;
|
||||
for (int i = 0; i < num_keys; ++i) {
|
||||
Tensor dense_key(DT_STRING, TensorShape());
|
||||
dense_key.scalar<string>()() = strings::Printf("%d", i);
|
||||
dense_keys.emplace_back(test::graph::Constant(g, dense_key));
|
||||
|
||||
Tensor dense_default(DT_STRING, TensorShape());
|
||||
dense_defaults.emplace_back(test::graph::Constant(g, dense_default));
|
||||
}
|
||||
|
||||
std::vector<DataType> sparse_types;
|
||||
std::vector<TensorShape> dense_shapes(num_keys, TensorShape());
|
||||
|
||||
Node* ret;
|
||||
TF_EXPECT_OK(NodeBuilder(g->NewName("n"), "ParseExample")
|
||||
.Input(test::graph::Constant(g, serialized))
|
||||
.Input(test::graph::Constant(g, names))
|
||||
.Input(sparse_keys)
|
||||
.Input(dense_keys)
|
||||
.Input(dense_defaults)
|
||||
.Attr("sparse_types", sparse_types)
|
||||
.Attr("dense_shapes", dense_shapes)
|
||||
.Finalize(g, &ret));
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
// B == batch_size, K == num_keys. K must be one of 10, 100, 1000, 10000
|
||||
#define BM_ParseDenseStringExample(B, K) \
|
||||
static void BM_ParseDenseStringExample##_##B##_##K(int iters) { \
|
||||
int64 items_per_iter = static_cast<int64>(B) * K; \
|
||||
testing::ItemsProcessed(static_cast<int64>(iters) * items_per_iter); \
|
||||
test::Benchmark("cpu", ParseDenseStringExample(B, K)).Run(iters); \
|
||||
} \
|
||||
BENCHMARK(BM_ParseDenseStringExample##_##B##_##K);
|
||||
|
||||
BM_ParseDenseStringExample(128, 10);
|
||||
BM_ParseDenseStringExample(128, 100);
|
||||
BM_ParseDenseStringExample(128, 1000);
|
||||
BM_ParseDenseStringExample(128, 10000);
|
||||
|
||||
} // end namespace tensorflow
|
@ -200,7 +200,7 @@ namespace functor {
|
||||
const GPUDevice& d, typename TTypes<T, NDIM>::ConstTensor Tparams, \
|
||||
typename TTypes<Index>::ConstMatrix Tindices, \
|
||||
typename TTypes<T>::Flat Tout); \
|
||||
extern template struct GatherNd<GPUDevice, T, Index, NDIM>
|
||||
extern template struct GatherNd<GPUDevice, T, Index, NDIM>;
|
||||
|
||||
#define DECLARE_GPU_SPECS_INDEX(T, Index) \
|
||||
DECLARE_GPU_SPECS_INDEX_NDIM(T, Index, 1); \
|
||||
|
@ -187,7 +187,7 @@ namespace functor {
|
||||
const GPUDevice& d, typename TTypes<T>::ConstMatrix Tparams, \
|
||||
typename TTypes<Index>::ConstFlat Tindices, \
|
||||
typename TTypes<T>::Matrix Tout); \
|
||||
extern template struct Gather<GPUDevice, T, Index>
|
||||
extern template struct Gather<GPUDevice, T, Index>;
|
||||
|
||||
#define DECLARE_GPU_SPECS(T) \
|
||||
DECLARE_GPU_SPECS_INDEX(T, int32); \
|
||||
|
@ -73,7 +73,7 @@ class ListDiffOp : public OpKernel {
|
||||
for (int i = 0, p = 0; i < x_size; ++i) {
|
||||
if (y_set.count(Tx(i)) == 0) {
|
||||
OP_REQUIRES(context, p < out_size,
|
||||
errors::OutOfRange(
|
||||
errors::InvalidArgument(
|
||||
"Tried to set output index ", p,
|
||||
" when output Tensor only had ", out_size,
|
||||
" elements. Check that your "
|
||||
|
202
tensorflow/core/kernels/reduce_join_op.cc
Normal file
202
tensorflow/core/kernels/reduce_join_op.cc
Normal file
@ -0,0 +1,202 @@
|
||||
/* Copyright 2016 Google Inc. 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/string_ops.cc.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "tensorflow/core/framework/kernel_def_builder.h"
|
||||
#include "tensorflow/core/framework/op_kernel.h"
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/framework/tensor_shape.h"
|
||||
#include "tensorflow/core/lib/core/errors.h"
|
||||
#include "tensorflow/core/lib/core/status.h"
|
||||
#include "tensorflow/core/lib/core/stringpiece.h"
|
||||
#include "tensorflow/core/lib/gtl/inlined_vector.h"
|
||||
#include "tensorflow/core/lib/strings/str_util.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
namespace {
|
||||
|
||||
const gtl::InlinedVector<int64, 8> GetStrides(const TensorShape& shape) {
|
||||
gtl::InlinedVector<int64, 8> result(shape.dims());
|
||||
int64 product = 1;
|
||||
for (int32 i = shape.dims() - 1; i >= 0; --i) {
|
||||
result[i] = product;
|
||||
product *= shape.dim_size(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Given a linear index to a subset of dimensions, full shape,
|
||||
// precomputed list of running products of the full shape, and list of
|
||||
// dimensions in the subset, outputs the linear index to the full shape with
|
||||
// nonspecified dimensions set to 0. Dimensions must be ordered from outer-most
|
||||
// to inner-most with respect to the subset linear index.
|
||||
inline int64 LinearSubIndexToFullIndex(
|
||||
int64 output_index, const gtl::InlinedVector<int32, 8>& dim_list,
|
||||
const TensorShape& input_shape,
|
||||
const gtl::InlinedVector<int64, 8>& strides) {
|
||||
int64 result = 0;
|
||||
int64 quotient = output_index;
|
||||
for (int32 i = dim_list.size() - 1; i >= 0; --i) {
|
||||
int32 dim = dim_list[i];
|
||||
int64 dim_value = quotient % input_shape.dim_size(dim);
|
||||
quotient = quotient / input_shape.dim_size(dim);
|
||||
result += strides[dim] * dim_value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Computes the number of input elements reduced per output element.
|
||||
int64 GetReductionIterSize(const gtl::InlinedVector<int32, 8>& reduced_indices,
|
||||
const TensorShape& input_shape) {
|
||||
int64 result = 1;
|
||||
for (int32 reduce_dim : reduced_indices) {
|
||||
result *= input_shape.dim_size(reduce_dim);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Computes a list of all true reduced indices, accounting for negative
|
||||
// indices and empty inputs.
|
||||
gtl::InlinedVector<int32, 8> GetReducedIndices(const Tensor& reduction_indices,
|
||||
int32 input_dims) {
|
||||
const auto reduction_indices_flat = reduction_indices.flat<int32>();
|
||||
const int32 reduction_dims = reduction_indices_flat.size();
|
||||
|
||||
gtl::InlinedVector<int32, 8> reduced_indices(reduction_dims);
|
||||
if (reduction_dims > 0) {
|
||||
for (int32 i = 0; i < reduction_dims; ++i) {
|
||||
reduced_indices[i] = reduction_indices_flat(reduction_dims - i - 1);
|
||||
reduced_indices[i] += reduced_indices[i] < 0 ? input_dims : 0;
|
||||
}
|
||||
} else {
|
||||
for (int32 i = 0; i < input_dims; ++i) {
|
||||
reduced_indices.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
return reduced_indices;
|
||||
}
|
||||
|
||||
// Appends all unreduced dimensions to the given vector.
|
||||
void MakeUnreducedIndices(gtl::InlinedVector<bool, 8> index_is_reduced,
|
||||
int32 input_dims,
|
||||
gtl::InlinedVector<int32, 8>* unreduced_indices) {
|
||||
for (int32 index = 0; index < input_dims; ++index) {
|
||||
if (!index_is_reduced[index]) unreduced_indices->push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
TensorShape GetOutputShape(gtl::InlinedVector<bool, 8> index_is_reduced,
|
||||
const TensorShape& input_shape, bool keep_dims) {
|
||||
TensorShape output_shape;
|
||||
for (int32 index = 0; index < index_is_reduced.size(); ++index) {
|
||||
if (index_is_reduced[index]) {
|
||||
if (keep_dims) output_shape.AddDim(1);
|
||||
} else {
|
||||
output_shape.AddDim(input_shape.dim_size(index));
|
||||
}
|
||||
}
|
||||
return output_shape;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class ReduceJoinOp : public OpKernel {
|
||||
public:
|
||||
using OpKernel::OpKernel;
|
||||
|
||||
explicit ReduceJoinOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
|
||||
OP_REQUIRES_OK(ctx, ctx->GetAttr("keep_dims", &keep_dims_));
|
||||
OP_REQUIRES_OK(ctx, ctx->GetAttr("separator", &separator_));
|
||||
}
|
||||
|
||||
void Compute(OpKernelContext* context) override {
|
||||
const Tensor& input = context->input(0);
|
||||
const auto input_flat = input.flat<string>();
|
||||
const TensorShape& input_shape = input.shape();
|
||||
const int32 input_dims = input_shape.dims();
|
||||
OP_REQUIRES(context, TensorShapeUtils::IsVectorOrHigher(input_shape),
|
||||
errors::InvalidArgument("Input cannot be a scalar."));
|
||||
|
||||
const Tensor& reduction_indices = context->input(1);
|
||||
const auto reduction_indices_flat = reduction_indices.flat<int32>();
|
||||
const int32 reduction_dims = reduction_indices_flat.size();
|
||||
|
||||
// Empty reduction_indices indicates that all indices are reduced.
|
||||
gtl::InlinedVector<bool, 8> index_is_reduced(input_dims,
|
||||
reduction_dims == 0);
|
||||
for (int32 i = 0; i < reduction_dims; i++) {
|
||||
int32 reduce_index = reduction_indices_flat(i);
|
||||
const int32 true_reduce_index =
|
||||
reduce_index < 0 ? reduce_index + input_dims : reduce_index;
|
||||
OP_REQUIRES(
|
||||
context, reduce_index >= -input_dims && reduce_index < input_dims,
|
||||
errors::OutOfRange("Invalid reduction dimension ", reduce_index,
|
||||
" for input with ", input_dims, " dimension(s)"));
|
||||
OP_REQUIRES(context, input_shape.dim_size(true_reduce_index) > 0,
|
||||
errors::InvalidArgument("Reduction dimension ", reduce_index,
|
||||
" has size 0"));
|
||||
OP_REQUIRES(context, !index_is_reduced[true_reduce_index],
|
||||
errors::InvalidArgument("Duplicate reduction dimension ",
|
||||
reduce_index));
|
||||
index_is_reduced[true_reduce_index] = true;
|
||||
}
|
||||
|
||||
gtl::InlinedVector<int32, 8> reduced_indices =
|
||||
GetReducedIndices(reduction_indices, input_dims);
|
||||
gtl::InlinedVector<int32, 8> unreduced_indices;
|
||||
if (reduction_indices.shape().num_elements() > 0) {
|
||||
MakeUnreducedIndices(index_is_reduced, input_dims, &unreduced_indices);
|
||||
}
|
||||
const auto strides = GetStrides(input_shape);
|
||||
|
||||
Tensor* output_tensor = nullptr;
|
||||
TensorShape output_shape =
|
||||
GetOutputShape(index_is_reduced, input_shape, keep_dims_);
|
||||
OP_REQUIRES_OK(context, context->allocate_output("output", output_shape,
|
||||
&output_tensor));
|
||||
auto output_flat = output_tensor->flat<string>();
|
||||
|
||||
const int64 reduction_iter_size =
|
||||
GetReductionIterSize(reduced_indices, input_shape);
|
||||
gtl::InlinedVector<StringPiece, 8> curr_strings(reduction_iter_size);
|
||||
for (int64 output_index = 0; output_index < output_shape.num_elements();
|
||||
++output_index) {
|
||||
int64 output_full_index = LinearSubIndexToFullIndex(
|
||||
output_index, unreduced_indices, input_shape, strides);
|
||||
for (int64 reduction_index = 0; reduction_index < reduction_iter_size;
|
||||
++reduction_index) {
|
||||
int64 reduction_full_index = LinearSubIndexToFullIndex(
|
||||
reduction_index, reduced_indices, input_shape, strides);
|
||||
curr_strings[reduction_index] =
|
||||
input_flat(output_full_index + reduction_full_index);
|
||||
}
|
||||
output_flat(output_index) =
|
||||
str_util::Join(curr_strings, separator_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool keep_dims_;
|
||||
string separator_;
|
||||
};
|
||||
|
||||
REGISTER_KERNEL_BUILDER(Name("ReduceJoin").Device(DEVICE_CPU), ReduceJoinOp);
|
||||
|
||||
} // namespace tensorflow
|
@ -63,9 +63,9 @@ Status ReductionHelper::Simplify(const Tensor& data, const Tensor& axis,
|
||||
for (int64 i = 0; i < axis.NumElements(); ++i) {
|
||||
const int32 index = axis_vec(i);
|
||||
if (index < 0 || index >= data.dims()) {
|
||||
return errors::OutOfRange("Invalid reduction dimension (", index,
|
||||
" for input with ", data.dims(),
|
||||
" dimension(s)");
|
||||
return errors::InvalidArgument("Invalid reduction dimension (", index,
|
||||
" for input with ", data.dims(),
|
||||
" dimension(s)");
|
||||
}
|
||||
bitmap[index] = true;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ typedef Eigen::GpuDevice GPUDevice;
|
||||
template struct functor::Relu6<GPUDevice, T>; \
|
||||
template struct functor::Relu6Grad<GPUDevice, T>; \
|
||||
template struct functor::Elu<GPUDevice, T>; \
|
||||
template struct functor::EluGrad<GPUDevice, T>
|
||||
template struct functor::EluGrad<GPUDevice, T>;
|
||||
|
||||
TF_CALL_GPU_NUMBER_TYPES(DEFINE_GPU_KERNELS);
|
||||
|
||||
|
@ -21,6 +21,7 @@ limitations under the License.
|
||||
#include "tensorflow/core/framework/op_kernel.h"
|
||||
#include "tensorflow/core/framework/register_types.h"
|
||||
#include "tensorflow/core/framework/types.h"
|
||||
#include "tensorflow/core/kernels/bounds_check.h"
|
||||
#include "tensorflow/core/lib/gtl/array_slice.h"
|
||||
#include "tensorflow/core/lib/strings/str_util.h"
|
||||
#include "tensorflow/core/lib/strings/strcat.h"
|
||||
@ -49,17 +50,7 @@ bool ParseShapeAndSlice(const string& shape_and_slice, TensorShape* shape,
|
||||
shape_and_slice);
|
||||
return false;
|
||||
}
|
||||
int num_dims = splits.size() - 1;
|
||||
shape->Clear();
|
||||
for (int i = 0; i < num_dims; ++i) {
|
||||
int dim;
|
||||
if (!strings::safe_strto32(splits[i], &dim)) {
|
||||
*error = strings::StrCat("Non numerical dimension in shape_and_slice: ",
|
||||
shape_and_slice);
|
||||
return false;
|
||||
}
|
||||
shape->AddDim(dim);
|
||||
}
|
||||
|
||||
// The last split is the slice specification.
|
||||
slice->Clear();
|
||||
auto status = slice->Parse(splits.back(), slice);
|
||||
@ -67,6 +58,20 @@ bool ParseShapeAndSlice(const string& shape_and_slice, TensorShape* shape,
|
||||
*error = status.error_message();
|
||||
return false;
|
||||
}
|
||||
|
||||
// The first n-1 are the shape specification.
|
||||
splits.pop_back();
|
||||
shape->Clear();
|
||||
for (const auto& s : splits) {
|
||||
int dim;
|
||||
if (!strings::safe_strto32(s, &dim)) {
|
||||
*error = strings::StrCat("Non numerical dimension in shape_and_slice: ",
|
||||
shape_and_slice);
|
||||
return false;
|
||||
}
|
||||
shape->AddDim(dim);
|
||||
}
|
||||
|
||||
// The specified slice must be compatible with the specified shape.
|
||||
status = slice->SliceTensorShape(*shape, shape_slice);
|
||||
if (!status.ok()) {
|
||||
@ -91,13 +96,20 @@ void SaveTensors(
|
||||
size, "elements"));
|
||||
}
|
||||
|
||||
// Path, names, and slices if save_slices is true.
|
||||
const int kFixedInputs = save_slices ? 3 : 2;
|
||||
const Tensor& tensor_names_t = context->input(1);
|
||||
const int64 N = tensor_names_t.NumElements();
|
||||
OP_REQUIRES(context,
|
||||
FastBoundsCheck(tensor_names_t.NumElements() + kFixedInputs,
|
||||
std::numeric_limits<int>::max()),
|
||||
errors::InvalidArgument("Too many inputs to SaveTensors"));
|
||||
const int N = static_cast<int>(tensor_names_t.NumElements());
|
||||
const string* tensor_shapes_and_slices_ptr = nullptr;
|
||||
if (save_slices) {
|
||||
const Tensor& tensor_shapes_and_slices_t = context->input(2);
|
||||
OP_REQUIRES(
|
||||
context, tensor_shapes_and_slices_t.NumElements() == N,
|
||||
context,
|
||||
tensor_shapes_and_slices_t.NumElements() == static_cast<int64>(N),
|
||||
errors::InvalidArgument("Expected ", N,
|
||||
" elements for the tensor "
|
||||
"shapes and slices but got ",
|
||||
@ -105,8 +117,6 @@ void SaveTensors(
|
||||
tensor_shapes_and_slices_ptr =
|
||||
tensor_shapes_and_slices_t.flat<string>().data();
|
||||
}
|
||||
// Path, names, and slices if save_slices is true.
|
||||
const int kFixedInputs = save_slices ? 3 : 2;
|
||||
OP_REQUIRES(context, context->num_inputs() == N + kFixedInputs,
|
||||
errors::InvalidArgument("Expected totally ", N + kFixedInputs,
|
||||
" inputs as input #1 (which is a string "
|
||||
@ -123,7 +133,7 @@ void SaveTensors(
|
||||
auto tensor_names_flat = tensor_names_t.flat<string>();
|
||||
|
||||
string error;
|
||||
for (int64 i = 0; i < N; ++i) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
const string& name = tensor_names_flat(i);
|
||||
const Tensor& input = context->input(i + kFixedInputs);
|
||||
TensorShape shape(input.shape());
|
||||
|
@ -130,7 +130,7 @@ class ScatterUpdateOp : public OpKernel {
|
||||
"indices has too many elements for ",
|
||||
DataTypeString(DataTypeToEnum<Index>::v()), " indexing: ",
|
||||
N_big, " > ", std::numeric_limits<Index>::max()));
|
||||
const Index N = indices.NumElements();
|
||||
const Index N = static_cast<Index>(indices.NumElements());
|
||||
OP_REQUIRES(
|
||||
c, params.dim_size(0) <= std::numeric_limits<Index>::max(),
|
||||
errors::InvalidArgument("params.shape[0] too large for ",
|
||||
@ -166,8 +166,9 @@ struct ScatterFunctor<CPUDevice, T, Index, op> {
|
||||
typename TTypes<T>::Matrix params,
|
||||
typename TTypes<T>::ConstMatrix updates,
|
||||
typename TTypes<Index>::ConstFlat indices) {
|
||||
const Index N = indices.size();
|
||||
const Index limit = params.dimension(0);
|
||||
// indices and params sizes were validated in DoCompute().
|
||||
const Index N = static_cast<Index>(indices.size());
|
||||
const Index limit = static_cast<Index>(params.dimension(0));
|
||||
for (Index i = 0; i < N; i++) {
|
||||
// Grab the index and check its validity. An earlier version of the
|
||||
// code checked it and then grabbed it from memory a second time, which
|
||||
|
120
tensorflow/core/kernels/session_ops.cc
Normal file
120
tensorflow/core/kernels/session_ops.cc
Normal file
@ -0,0 +1,120 @@
|
||||
/* Copyright 2015 Google Inc. 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/data_flow_ops.cc.
|
||||
|
||||
#include <limits.h>
|
||||
#include <vector>
|
||||
|
||||
#include "tensorflow/core/common_runtime/device.h"
|
||||
#include "tensorflow/core/framework/device_base.h"
|
||||
#include "tensorflow/core/framework/op_kernel.h"
|
||||
#include "tensorflow/core/framework/register_types.h"
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/framework/tensor_shape.h"
|
||||
#include "tensorflow/core/framework/types.h"
|
||||
#include "tensorflow/core/lib/core/errors.h"
|
||||
#include "tensorflow/core/lib/gtl/map_util.h"
|
||||
#include "tensorflow/core/platform/logging.h"
|
||||
#include "tensorflow/core/platform/macros.h"
|
||||
#include "tensorflow/core/platform/mutex.h"
|
||||
#include "tensorflow/core/platform/thread_annotations.h"
|
||||
#include "tensorflow/core/platform/types.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
class GetSessionHandleOp : public OpKernel {
|
||||
public:
|
||||
explicit GetSessionHandleOp(OpKernelConstruction* context)
|
||||
: OpKernel(context) {}
|
||||
|
||||
void Compute(OpKernelContext* ctx) override {
|
||||
Tensor val = ctx->input(0);
|
||||
int64 id = ctx->session_state()->GetNewId();
|
||||
TensorStore::TensorAndKey tk{val, id, def().device()};
|
||||
OP_REQUIRES_OK(ctx, ctx->tensor_store()->AddTensor(def().name(), tk));
|
||||
Tensor* handle = nullptr;
|
||||
OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape({}), &handle));
|
||||
handle->flat<string>().setConstant(tk.GetHandle(def().name()));
|
||||
}
|
||||
|
||||
TF_DISALLOW_COPY_AND_ASSIGN(GetSessionHandleOp);
|
||||
};
|
||||
|
||||
REGISTER_KERNEL_BUILDER(Name("GetSessionHandle").Device(DEVICE_CPU),
|
||||
GetSessionHandleOp);
|
||||
|
||||
#define REGISTER_GPU_KERNEL(type) \
|
||||
REGISTER_KERNEL_BUILDER(Name("GetSessionHandle") \
|
||||
.Device(DEVICE_GPU) \
|
||||
.HostMemory("handle") \
|
||||
.TypeConstraint<type>("T"), \
|
||||
GetSessionHandleOp)
|
||||
|
||||
TF_CALL_NUMBER_TYPES(REGISTER_GPU_KERNEL);
|
||||
REGISTER_GPU_KERNEL(bool);
|
||||
#undef REGISTER_GPU_KERNEL
|
||||
|
||||
class GetSessionTensorOp : public OpKernel {
|
||||
public:
|
||||
explicit GetSessionTensorOp(OpKernelConstruction* context)
|
||||
: OpKernel(context) {}
|
||||
|
||||
void Compute(OpKernelContext* ctx) override {
|
||||
const Tensor& handle = ctx->input(0);
|
||||
const string& name = handle.scalar<string>()();
|
||||
Tensor val;
|
||||
OP_REQUIRES_OK(ctx, ctx->session_state()->GetTensor(name, &val));
|
||||
ctx->set_output(0, val);
|
||||
}
|
||||
|
||||
TF_DISALLOW_COPY_AND_ASSIGN(GetSessionTensorOp);
|
||||
};
|
||||
|
||||
REGISTER_KERNEL_BUILDER(Name("GetSessionTensor").Device(DEVICE_CPU),
|
||||
GetSessionTensorOp);
|
||||
|
||||
#define REGISTER_GPU_KERNEL(type) \
|
||||
REGISTER_KERNEL_BUILDER(Name("GetSessionTensor") \
|
||||
.Device(DEVICE_GPU) \
|
||||
.HostMemory("handle") \
|
||||
.TypeConstraint<type>("dtype"), \
|
||||
GetSessionTensorOp)
|
||||
|
||||
TF_CALL_NUMBER_TYPES(REGISTER_GPU_KERNEL);
|
||||
REGISTER_GPU_KERNEL(bool);
|
||||
#undef REGISTER_GPU_KERNEL
|
||||
|
||||
class DeleteSessionTensorOp : public OpKernel {
|
||||
public:
|
||||
explicit DeleteSessionTensorOp(OpKernelConstruction* context)
|
||||
: OpKernel(context) {}
|
||||
|
||||
void Compute(OpKernelContext* ctx) override {
|
||||
const Tensor& handle = ctx->input(0);
|
||||
const string& name = handle.scalar<string>()();
|
||||
OP_REQUIRES_OK(ctx, ctx->session_state()->DeleteTensor(name));
|
||||
}
|
||||
|
||||
TF_DISALLOW_COPY_AND_ASSIGN(DeleteSessionTensorOp);
|
||||
};
|
||||
|
||||
REGISTER_KERNEL_BUILDER(Name("DeleteSessionTensor").Device(DEVICE_CPU),
|
||||
DeleteSessionTensorOp);
|
||||
REGISTER_KERNEL_BUILDER(
|
||||
Name("DeleteSessionTensor").Device(DEVICE_GPU).HostMemory("handle"),
|
||||
DeleteSessionTensorOp);
|
||||
|
||||
} // namespace tensorflow
|
@ -155,7 +155,7 @@ class SliceOp : public OpKernel {
|
||||
// TODO(agarwal): Consider multi-threading this loop for cases where
|
||||
// size[0] is very large.
|
||||
for (int i = 0; i < size[0]; ++i) {
|
||||
const int row = begin[0] + i;
|
||||
const int64 row = begin[0] + i;
|
||||
if (i + 1 < size[0]) {
|
||||
port::prefetch<port::PREFETCH_HINT_T0>(&output(i + 1, 0));
|
||||
port::prefetch<port::PREFETCH_HINT_T0>(&input(row + 1, begin[1]));
|
||||
|
@ -89,7 +89,7 @@ class SummaryHistoOp : public OpKernel {
|
||||
T v = flat(i);
|
||||
if (!std::isfinite(v)) {
|
||||
c->SetStatus(
|
||||
errors::OutOfRange("Nan in summary histogram for: ", name()));
|
||||
errors::InvalidArgument("Nan in summary histogram for: ", name()));
|
||||
break;
|
||||
}
|
||||
histo.Add(static_cast<double>(v));
|
||||
|
@ -13,49 +13,45 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#define EIGEN_USE_THREADS
|
||||
#include "tensorflow/core/kernels/tensor_array.h"
|
||||
|
||||
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
|
||||
#include "tensorflow/core/framework/register_types.h"
|
||||
#include "tensorflow/core/kernels/aggregate_ops_cpu.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
Status TensorArray::LockedWrite(OpKernelContext* ctx, const int32 index,
|
||||
PersistentTensor* value) {
|
||||
TF_RETURN_IF_ERROR(LockedReturnIfClosed());
|
||||
size_t index_size = static_cast<size_t>(index);
|
||||
if (index < 0 ||
|
||||
(!dynamic_size_ && index_size >= tensors_.size())) {
|
||||
return errors::InvalidArgument(
|
||||
"TensorArray ", handle_.vec<string>()(1), ": Tried to write to index ",
|
||||
index, " but array is not resizeable and size is: ", tensors_.size());
|
||||
typedef Eigen::ThreadPoolDevice CPUDevice;
|
||||
typedef Eigen::GpuDevice GPUDevice;
|
||||
|
||||
namespace tensor_array {
|
||||
|
||||
#define TENSOR_ARRAY_WRITE_OR_ADD(Device, T) \
|
||||
template <> \
|
||||
Status AddToTensor<Device, T>(OpKernelContext * ctx, Tensor * sum, \
|
||||
const Tensor* current, const Tensor* add) { \
|
||||
functor::Add2Functor<Device, T> add_functor; \
|
||||
add_functor(ctx->template eigen_device<Device>(), sum->flat<T>(), \
|
||||
current->flat<T>(), add->flat<T>()); \
|
||||
return Status::OK(); \
|
||||
}
|
||||
if (dynamic_size_) {
|
||||
// We must grow the internal TensorArray
|
||||
if (index_size >= tensors_.capacity()) {
|
||||
tensors_.reserve(2 * (index_size + 1));
|
||||
}
|
||||
if (index_size >= tensors_.size()) {
|
||||
tensors_.resize(index_size + 1);
|
||||
}
|
||||
}
|
||||
TensorAndState& t = tensors_[index];
|
||||
if (t.written) {
|
||||
return errors::InvalidArgument("TensorArray ", handle_.vec<string>()(1),
|
||||
": Could not write to TensorArray index ",
|
||||
index,
|
||||
" because it has already been written to.");
|
||||
}
|
||||
Tensor* value_t = value->AccessTensor(ctx);
|
||||
if (value_t->dtype() != dtype_) {
|
||||
return errors::InvalidArgument(
|
||||
"TensorArray ", handle_.vec<string>()(1),
|
||||
": Could not write to TensorArray index ", index,
|
||||
" because the value dtype is ", DataTypeString(value_t->dtype()),
|
||||
" but TensorArray dtype is ", DataTypeString(dtype_), ".");
|
||||
}
|
||||
t.tensor = *value;
|
||||
t.shape = value_t->shape();
|
||||
t.written = true;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#define TENSOR_ARRAY_WRITE_OR_ADD_CPU(T) TENSOR_ARRAY_WRITE_OR_ADD(CPUDevice, T)
|
||||
TF_CALL_NUMBER_TYPES(TENSOR_ARRAY_WRITE_OR_ADD_CPU)
|
||||
#undef TENSOR_ARRAY_WRITE_OR_ADD_CPU
|
||||
|
||||
#if GOOGLE_CUDA
|
||||
|
||||
#define TENSOR_ARRAY_WRITE_OR_ADD_GPU(T) TENSOR_ARRAY_WRITE_OR_ADD(GPUDevice, T)
|
||||
TF_CALL_GPU_NUMBER_TYPES(TENSOR_ARRAY_WRITE_OR_ADD_GPU);
|
||||
#undef TENSOR_ARRAY_WRITE_OR_ADD_GPU
|
||||
|
||||
#endif // GOOGLE_CUDA
|
||||
|
||||
#undef TENSOR_ARRAY_WRITE_OR_ADD
|
||||
|
||||
} // namespace tensor_array
|
||||
|
||||
Status TensorArray::LockedRead(const int32 index, PersistentTensor* value) {
|
||||
TF_RETURN_IF_ERROR(LockedReturnIfClosed());
|
||||
@ -64,20 +60,25 @@ Status TensorArray::LockedRead(const int32 index, PersistentTensor* value) {
|
||||
" but array size is: ", tensors_.size());
|
||||
}
|
||||
TensorAndState& t = tensors_[index];
|
||||
if (t.read) {
|
||||
return errors::InvalidArgument(
|
||||
"TensorArray ", handle_.vec<string>()(1), ": Could not read index ",
|
||||
index, " twice because TensorArray a read-once object.");
|
||||
}
|
||||
if (!t.written) {
|
||||
return errors::InvalidArgument("TensorArray ", handle_.vec<string>()(1),
|
||||
": Could not read from TensorArray index ",
|
||||
index,
|
||||
" because it has not yet been written to.");
|
||||
}
|
||||
if (t.cleared) {
|
||||
return errors::InvalidArgument("TensorArray ", handle_.vec<string>()(1),
|
||||
": Could not read index ", index,
|
||||
" twice because it was cleared after a "
|
||||
"previous read (perhaps try setting "
|
||||
"clear_after_read = false?).");
|
||||
}
|
||||
*value = t.tensor;
|
||||
if (clear_after_read_) {
|
||||
t.tensor = PersistentTensor();
|
||||
t.cleared = true;
|
||||
}
|
||||
t.read = true;
|
||||
t.tensor = PersistentTensor();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
@ -24,22 +24,60 @@ limitations under the License.
|
||||
#include "tensorflow/core/framework/tensor.h"
|
||||
#include "tensorflow/core/framework/tensor_shape.h"
|
||||
#include "tensorflow/core/framework/types.h"
|
||||
#include "tensorflow/core/kernels/aggregate_ops.h"
|
||||
#include "tensorflow/core/lib/core/errors.h"
|
||||
#include "tensorflow/core/platform/logging.h"
|
||||
#include "tensorflow/core/platform/types.h"
|
||||
|
||||
namespace tensorflow {
|
||||
|
||||
typedef Eigen::ThreadPoolDevice CPUDevice;
|
||||
typedef Eigen::GpuDevice GPUDevice;
|
||||
|
||||
namespace tensor_array {
|
||||
|
||||
// Full implementations are in tensor_array.cc
|
||||
template <typename Device, typename T>
|
||||
Status AddToTensor(OpKernelContext* ctx, Tensor* sum, const Tensor* current,
|
||||
const Tensor* add) {
|
||||
return errors::InvalidArgument(
|
||||
"tensor_array::AddToTensor type not supported: ",
|
||||
DataTypeString(DataTypeToEnum<T>::value));
|
||||
};
|
||||
|
||||
#define TENSOR_ARRAY_WRITE_OR_ADD(Device, T) \
|
||||
template <> \
|
||||
Status AddToTensor<Device, T>(OpKernelContext * ctx, Tensor * sum, \
|
||||
const Tensor* current, const Tensor* add);
|
||||
|
||||
#define TENSOR_ARRAY_WRITE_OR_ADD_CPU(T) TENSOR_ARRAY_WRITE_OR_ADD(CPUDevice, T)
|
||||
TF_CALL_NUMBER_TYPES(TENSOR_ARRAY_WRITE_OR_ADD_CPU)
|
||||
#undef TENSOR_ARRAY_WRITE_OR_ADD_CPU
|
||||
|
||||
#if GOOGLE_CUDA
|
||||
|
||||
#define TENSOR_ARRAY_WRITE_OR_ADD_GPU(T) TENSOR_ARRAY_WRITE_OR_ADD(GPUDevice, T)
|
||||
TF_CALL_GPU_NUMBER_TYPES(TENSOR_ARRAY_WRITE_OR_ADD_GPU);
|
||||
#undef TENSOR_ARRAY_WRITE_OR_ADD_GPU
|
||||
|
||||
#endif // GOOGLE_CUDA
|
||||
|
||||
#undef TENSOR_ARRAY_WRITE_OR_ADD
|
||||
|
||||
} // namespace tensor_array
|
||||
|
||||
// The TensorArray object keeps an array of PersistentTensors. It
|
||||
// allows reading from the array and writing to the array.
|
||||
//
|
||||
// Important properties:
|
||||
// * Reading and writing to a particular index in the TensorArray
|
||||
// is allowed at most once per index.
|
||||
// * Upon reading an entry, that entry is cleared from the array and
|
||||
// marked as read. This allows removal of Tensor from memory
|
||||
// as soon as it is not needed. Its shape is saved.
|
||||
// * No deep copies of any PersistentTensor are ever made.
|
||||
// * Usually, writing to a particular index in the TensorArray is allowed at
|
||||
// most once per index. In a special case, writes with the flag
|
||||
// multiple_writes_aggregate allow multiple writes to the same
|
||||
// index. In this case, the writes are summed.
|
||||
// * Multiple reads are supported.
|
||||
// * Deep copies of PersistentTensors are rarely made. The only
|
||||
// time they are made is when WriteOrAggregate is called at least twice
|
||||
// on the same index with the flag multiple_writes_aggregate = True.
|
||||
// * Reading and Writing to the array is protected by a mutex.
|
||||
// All operations on a TensorArray are thread-safe.
|
||||
// * A TensorArray may be preemptively closed, which releases all
|
||||
@ -51,8 +89,12 @@ namespace tensorflow {
|
||||
// * Write-Once semantics mean the gradient of a TensorArray Read never has to
|
||||
// worry which of multiple writes to that index the gradient value
|
||||
// is meant for.
|
||||
// * Read-Once semantics mean the TensorArray never sees
|
||||
// multiple writes to the same index as part of gradient aggregation.
|
||||
// * Read-Many semantics (when using clear_after_read=false) allow the
|
||||
// TensorArray to be read, packed, or concatenated multiple times;
|
||||
// and the gradient operations use the multiple_writes_aggregate
|
||||
// flag to aggregate the backprop writes. Multiple backprop writes to
|
||||
// the same index are partial gradients corresponding to the
|
||||
// multiple reads of that index in the forward phase.
|
||||
//
|
||||
class TensorArray : public ResourceBase {
|
||||
public:
|
||||
@ -61,11 +103,15 @@ class TensorArray : public ResourceBase {
|
||||
// can hold more than MAX_INT entries, in practice we do not expect
|
||||
// users to construct this many Tensors for storage in a TensorArray.
|
||||
TensorArray(const DataType& dtype, const Tensor& handle, int32 N,
|
||||
bool dynamic_size)
|
||||
bool dynamic_size, bool multiple_writes_aggregate,
|
||||
bool clear_after_read)
|
||||
: dtype_(dtype),
|
||||
handle_(handle),
|
||||
closed_(false),
|
||||
dynamic_size_(dynamic_size),
|
||||
multiple_writes_aggregate_(multiple_writes_aggregate),
|
||||
gradients_disallowed_(false),
|
||||
clear_after_read_(clear_after_read),
|
||||
tensors_(N) {}
|
||||
|
||||
// Write PersistentTensor 'value' to index 'index'.
|
||||
@ -77,25 +123,40 @@ class TensorArray : public ResourceBase {
|
||||
// Otherwise:
|
||||
// The index is in [0, N) where N == Size()
|
||||
// * The dtype of the Tensor in 'value' matches the TensorArray's dtype.
|
||||
// * The Tensor at 'index' has not yet been written to.
|
||||
// * If multiple_writes_aggregate is false:
|
||||
// The Tensor at 'index' has not yet been written to.
|
||||
// * If multiple_writes_aggregate is true:
|
||||
// The Tensor at 'index' has the same shape as value.
|
||||
//
|
||||
// Side effects:
|
||||
// * The underlying Tensor in 'value' has a new reference to it.
|
||||
// * Index 'index' is marked as written.
|
||||
// * On the first write to 'index':
|
||||
// - The underlying Tensor in 'value' has a new reference to it.
|
||||
// - The index 'index' is marked as written.
|
||||
// * If multiple_writes_aggregate is false, subsequent writes to 'index'
|
||||
// raise an InvalidArgument error.
|
||||
// * If multiple_writes_aggregate is true, subsequent writes to 'index':
|
||||
// - The underlying Tensors in 'value' and from the first write
|
||||
// are released and a local PersistentTensor is created.
|
||||
// - Index 'index' is also marked as local_copy.
|
||||
// - The gradient_disallowed flag is set true (GradientAllowed()
|
||||
// will now return false).
|
||||
//
|
||||
// Note, value is passed as a pointer because we its underlying
|
||||
// Tensor's shape is accessed. Otherwise it is not modified.
|
||||
Status Write(OpKernelContext* ctx, const int32 index,
|
||||
PersistentTensor* value) {
|
||||
template <typename Device, typename T>
|
||||
Status WriteOrAggregate(OpKernelContext* ctx, const int32 index,
|
||||
PersistentTensor* value) {
|
||||
mutex_lock l(mu_);
|
||||
return LockedWrite(ctx, index, value);
|
||||
return LockedWriteOrAggregate<Device, T>(ctx, index, value);
|
||||
}
|
||||
|
||||
Status WriteMany(OpKernelContext* ctx,
|
||||
std::vector<PersistentTensor>* values) {
|
||||
template <typename Device, typename T>
|
||||
Status WriteOrAggregateMany(OpKernelContext* ctx,
|
||||
std::vector<PersistentTensor>* values) {
|
||||
mutex_lock l(mu_);
|
||||
for (int32 i = values->size() - 1; i >= 0; --i) {
|
||||
TF_RETURN_IF_ERROR(LockedWrite(ctx, i, &(*values)[i]));
|
||||
Status s = LockedWriteOrAggregate<Device, T>(ctx, i, &(*values)[i]);
|
||||
TF_RETURN_IF_ERROR(s);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
@ -106,13 +167,15 @@ class TensorArray : public ResourceBase {
|
||||
// * The TensorArray is not closed
|
||||
// * The index is in [0, N)
|
||||
// * The Tensor at 'index' has been written to.
|
||||
// * The Tensor at 'index' has not already been read.
|
||||
// * The Tensor at 'index' has not been read from with flag
|
||||
// clear_after_read = true.
|
||||
//
|
||||
// Side effects:
|
||||
// * The PersistentTensor at 'index' is cleared from the given index.
|
||||
// * The reference to the underlying Tensor at 'index' is shifted to
|
||||
// * If clear_after_read is true, the reference to the underlying
|
||||
// Tensor is deleted.
|
||||
// * The reference to the underlying Tensor at 'index' is copied to
|
||||
// the returned '*value'.
|
||||
// * Index 'index' is marked as read.
|
||||
// * The index is marked as read (it cannot be rewritten to).
|
||||
Status Read(const int32 index, PersistentTensor* value) {
|
||||
mutex_lock l(mu_);
|
||||
return LockedRead(index, value);
|
||||
@ -161,6 +224,11 @@ class TensorArray : public ResourceBase {
|
||||
return dynamic_size_;
|
||||
}
|
||||
|
||||
bool GradientsAllowed() {
|
||||
mutex_lock l(mu_);
|
||||
return !gradients_disallowed_;
|
||||
}
|
||||
|
||||
// Clear the TensorArray, including any Tensor references, and mark as closed.
|
||||
void ClearAndMarkClosed() {
|
||||
mutex_lock l(mu_);
|
||||
@ -175,6 +243,11 @@ class TensorArray : public ResourceBase {
|
||||
Status LockedWrite(OpKernelContext* ctx, const int32 index,
|
||||
PersistentTensor* value) EXCLUSIVE_LOCKS_REQUIRED(mu_);
|
||||
|
||||
template <typename Device, typename T>
|
||||
Status LockedWriteOrAggregate(OpKernelContext* ctx, const int32 index,
|
||||
PersistentTensor* value)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mu_);
|
||||
|
||||
Status LockedRead(const int32 index, PersistentTensor* value)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mu_);
|
||||
|
||||
@ -191,25 +264,134 @@ class TensorArray : public ResourceBase {
|
||||
|
||||
mutex mu_;
|
||||
|
||||
bool closed_
|
||||
GUARDED_BY(mu_); // Marks that the tensor_array_ has been cleared.
|
||||
// Marks that the tensor_array_ has been cleared.
|
||||
bool closed_ GUARDED_BY(mu_);
|
||||
|
||||
bool dynamic_size_; // Determines if Writes are allowed to grow the array.
|
||||
// Writes are allowed to grow the array.
|
||||
bool dynamic_size_;
|
||||
|
||||
// Multiple writes to the same index will result in summation of the
|
||||
// values (used by backprop)
|
||||
bool multiple_writes_aggregate_;
|
||||
|
||||
// If multiple Writes were attempted (e.g. via attribute
|
||||
// multiple_writes_aggregate), then gradients are disallowed.
|
||||
bool gradients_disallowed_ GUARDED_BY(mu_);
|
||||
|
||||
// After a read at an index, clear away its PersistentTensor to
|
||||
// release memory.
|
||||
bool clear_after_read_;
|
||||
|
||||
// TensorAndState is used to keep track of the PersistentTensors
|
||||
// stored in the TensorArray, along with their shapes, and a boolean
|
||||
// that determines whether they have already been read or not.
|
||||
struct TensorAndState {
|
||||
TensorAndState() : written(false), read(false) {}
|
||||
TensorAndState()
|
||||
: written(false), read(false), cleared(false), local_copy(false) {}
|
||||
PersistentTensor tensor;
|
||||
TensorShape shape;
|
||||
bool written; // True if a Tensor has been written to the index.
|
||||
bool read; // True if a Tensor has been written to and read from the index.
|
||||
bool cleared; // True if a tensor has been read with
|
||||
// clear_after_read = true;
|
||||
|
||||
// Used by writes when multiple_writes_aggregate is true. In this
|
||||
// case, the first time a value is written, it is a shallow copy.
|
||||
// The second time a value is written, it is aggregated. However,
|
||||
// in this case a new Tensor must be constructed to hold the
|
||||
// aggregated value. This flag marks that such a Tensor is being
|
||||
// used. All future writes will aggregate to the existing local Tensor.
|
||||
bool local_copy;
|
||||
};
|
||||
// The list of underlying PersistentTensors and states.
|
||||
std::vector<TensorAndState> tensors_ GUARDED_BY(mu_);
|
||||
};
|
||||
|
||||
template <typename Device, typename T>
|
||||
Status TensorArray::LockedWriteOrAggregate(OpKernelContext* ctx,
|
||||
const int32 index,
|
||||
PersistentTensor* value) {
|
||||
TF_RETURN_IF_ERROR(LockedReturnIfClosed());
|
||||
size_t index_size = static_cast<size_t>(index);
|
||||
if (index < 0 || (!dynamic_size_ && index_size >= tensors_.size())) {
|
||||
return errors::InvalidArgument(
|
||||
"TensorArray ", handle_.vec<string>()(1), ": Tried to write to index ",
|
||||
index, " but array is not resizeable and size is: ", tensors_.size());
|
||||
}
|
||||
if (dynamic_size_) {
|
||||
// We must grow the internal TensorArray
|
||||
if (index_size >= tensors_.capacity()) {
|
||||
tensors_.reserve(2 * (index_size + 1));
|
||||
}
|
||||
if (index_size >= tensors_.size()) {
|
||||
tensors_.resize(index_size + 1);
|
||||
}
|
||||
}
|
||||
TensorAndState& t = tensors_[index];
|
||||
|
||||
Tensor* value_t = value->AccessTensor(ctx);
|
||||
if (value_t->dtype() != dtype_) {
|
||||
return errors::InvalidArgument(
|
||||
"TensorArray ", handle_.vec<string>()(1),
|
||||
": Could not write to TensorArray index ", index,
|
||||
" because the value dtype is ", DataTypeString(value_t->dtype()),
|
||||
" but TensorArray dtype is ", DataTypeString(dtype_), ".");
|
||||
}
|
||||
|
||||
if (t.read) {
|
||||
return errors::InvalidArgument("TensorArray ", handle_.vec<string>()(1),
|
||||
": Could not write to TensorArray index ",
|
||||
index, " because it has already been read.");
|
||||
}
|
||||
|
||||
if (!multiple_writes_aggregate_ && t.written) {
|
||||
return errors::InvalidArgument("TensorArray ", handle_.vec<string>()(1),
|
||||
": Could not write to TensorArray index ",
|
||||
index,
|
||||
" because it has already been written to.");
|
||||
}
|
||||
|
||||
if (t.written) {
|
||||
DCHECK(multiple_writes_aggregate_);
|
||||
|
||||
// Check that value_t shape matches t.shape
|
||||
if (value_t->shape() != t.shape) {
|
||||
return errors::InvalidArgument(
|
||||
"TensorArray ", handle_.vec<string>()(1),
|
||||
": Could not aggregate to TensorArray index ", index,
|
||||
" because the existing shape is ", t.shape.DebugString(),
|
||||
" but the new input shape is ", value_t->shape().DebugString(), ".");
|
||||
}
|
||||
|
||||
Tensor* existing_t = t.tensor.AccessTensor(ctx);
|
||||
|
||||
if (t.local_copy) {
|
||||
Status s = tensor_array::AddToTensor<Device, T>(ctx, existing_t,
|
||||
existing_t, value_t);
|
||||
TF_RETURN_IF_ERROR(s);
|
||||
} else {
|
||||
PersistentTensor local_tensor;
|
||||
Tensor* local_tensor_t;
|
||||
TF_RETURN_IF_ERROR(ctx->allocate_persistent(
|
||||
dtype_, existing_t->shape(), &local_tensor, &local_tensor_t));
|
||||
Status s = tensor_array::AddToTensor<Device, T>(ctx, local_tensor_t,
|
||||
existing_t, value_t);
|
||||
TF_RETURN_IF_ERROR(s);
|
||||
t.tensor = local_tensor;
|
||||
t.local_copy = true;
|
||||
}
|
||||
|
||||
// We've aggregated the values, so disallow backprop on this
|
||||
// TensorArray.
|
||||
gradients_disallowed_ = true;
|
||||
} else {
|
||||
t.tensor = *value;
|
||||
t.shape = value_t->shape();
|
||||
t.written = true;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace tensorflow
|
||||
|
||||
#endif // TENSORFLOW_KERNELS_TENSOR_ARRAY_H_
|
||||
|
@ -125,6 +125,8 @@ class TensorArrayOp : public TensorArrayCreationOp {
|
||||
: TensorArrayCreationOp(context) {
|
||||
OP_REQUIRES_OK(context, context->GetAttr("dtype", &dtype_));
|
||||
OP_REQUIRES_OK(context, context->GetAttr("dynamic_size", &dynamic_size_));
|
||||
OP_REQUIRES_OK(context,
|
||||
context->GetAttr("clear_after_read", &clear_after_read_));
|
||||
OP_REQUIRES_OK(context,
|
||||
context->GetAttr("tensor_array_name", &tensor_array_name_));
|
||||
if (tensor_array_name_ == "") tensor_array_name_ = name();
|
||||
@ -148,7 +150,8 @@ class TensorArrayOp : public TensorArrayCreationOp {
|
||||
handle(1) = tensor_array_name_;
|
||||
|
||||
TensorArray* tensor_array = new TensorArray(
|
||||
dtype_, *tensor_array_output_handle, size, dynamic_size_);
|
||||
dtype_, *tensor_array_output_handle, size, dynamic_size_,
|
||||
false /* multiple_writes_aggregate */, clear_after_read_);
|
||||
|
||||
TF_RETURN_IF_ERROR(rm->Create(handle(0), tensor_array_name_, tensor_array));
|
||||
|
||||
@ -160,6 +163,7 @@ class TensorArrayOp : public TensorArrayCreationOp {
|
||||
private:
|
||||
DataType dtype_;
|
||||
bool dynamic_size_;
|
||||
bool clear_after_read_;
|
||||
string tensor_array_name_; // The name used to create the TensorArray.
|
||||
|
||||
TF_DISALLOW_COPY_AND_ASSIGN(TensorArrayOp);
|
||||
@ -220,11 +224,20 @@ class TensorArrayGradOp : public TensorArrayCreationOp {
|
||||
tensor_array->DisableDynamicSize();
|
||||
TF_RETURN_IF_ERROR(tensor_array->Size(&array_size));
|
||||
|
||||
if (!tensor_array->GradientsAllowed()) {
|
||||
return errors::InvalidArgument(
|
||||
"Unable to create a gradients TensorArray for ", tensor_array_name,
|
||||
". Perhaps you used the multiple_writes_aggregate flag on a "
|
||||
"previous write? Gradient calculation is impossible when multiple "
|
||||
"writes are performed to the same index.");
|
||||
}
|
||||
|
||||
auto creator = [this, tensor_array, array_size,
|
||||
tensor_array_output_handle](TensorArray** ret) {
|
||||
*ret =
|
||||
new TensorArray(tensor_array->ElemType(), *tensor_array_output_handle,
|
||||
array_size, false /* dynamic_size */);
|
||||
*ret = new TensorArray(
|
||||
tensor_array->ElemType(), *tensor_array_output_handle, array_size,
|
||||
false /* dynamic_size */, true /* multiple_writes_aggregate */,
|
||||
true /* close_after_read */);
|
||||
return Status::OK();
|
||||
};
|
||||
|
||||
@ -285,10 +298,10 @@ class TensorArrayWriteOp : public OpKernel {
|
||||
" but Op is trying to write dtype ",
|
||||
DataTypeString(tensor_value->dtype()), "."));
|
||||
PersistentTensor persistent_tensor(*tensor_value);
|
||||
OP_REQUIRES_OK(ctx, tensor_array->Write(ctx, index, &persistent_tensor));
|
||||
Status s = tensor_array->WriteOrAggregate<Device, T>(ctx, index,
|
||||
&persistent_tensor);
|
||||
OP_REQUIRES_OK(ctx, s);
|
||||
}
|
||||
|
||||
bool IsExpensive() override { return false; }
|
||||
};
|
||||
|
||||
#define REGISTER_WRITE(type) \
|
||||
@ -737,7 +750,9 @@ class TensorArrayUnpackOp : public OpKernel {
|
||||
write_values.push_back(persistent_tensor);
|
||||
}
|
||||
|
||||
OP_REQUIRES_OK(ctx, tensor_array->WriteMany(ctx, &write_values));
|
||||
Status s =
|
||||
tensor_array->WriteOrAggregateMany<Device, T>(ctx, &write_values);
|
||||
OP_REQUIRES_OK(ctx, s);
|
||||
}
|
||||
};
|
||||
|
||||
@ -871,7 +886,9 @@ class TensorArraySplitOp : public OpKernel {
|
||||
write_values.push_back(persistent_tensor);
|
||||
}
|
||||
|
||||
OP_REQUIRES_OK(ctx, tensor_array->WriteMany(ctx, &write_values));
|
||||
Status s =
|
||||
tensor_array->WriteOrAggregateMany<Device, T>(ctx, &write_values);
|
||||
OP_REQUIRES_OK(ctx, s);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -49,7 +49,12 @@ class InvertPermutationOp : public OpKernel {
|
||||
context, TensorShapeUtils::IsVector(input.shape()),
|
||||
errors::InvalidArgument("invert_permutation expects a 1D vector."));
|
||||
auto Tin = input.vec<int32>();
|
||||
const int N = Tin.size();
|
||||
OP_REQUIRES(context,
|
||||
FastBoundsCheck(Tin.size(), std::numeric_limits<int32>::max()),
|
||||
errors::InvalidArgument("permutation of nonnegative int32s "
|
||||
"must have <= int32 max elements"));
|
||||
const int32 N =
|
||||
static_cast<int32>(Tin.size()); // Safe: bounds-checked above.
|
||||
Tensor* output = nullptr;
|
||||
OP_REQUIRES_OK(context,
|
||||
context->allocate_output(0, input.shape(), &output));
|
||||
|
@ -99,7 +99,7 @@ enum Code {
|
||||
// ABORTED, and UNAVAILABLE.
|
||||
ABORTED = 10;
|
||||
|
||||
// Operation was attempted past the valid range. E.g., seeking or
|
||||
// Operation tried to iterate past the valid input range. E.g., seeking or
|
||||
// reading past end of file.
|
||||
//
|
||||
// Unlike INVALID_ARGUMENT, this error indicates a problem that may
|
||||
|
@ -15,6 +15,16 @@ limitations under the License.
|
||||
|
||||
#include "tensorflow/core/lib/core/threadpool.h"
|
||||
|
||||
#ifdef TENSORFLOW_USE_EIGEN_THREADPOOL
|
||||
#define EIGEN_USE_THREADS
|
||||
#define EIGEN_USE_CUSTOM_THREAD_POOL
|
||||
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
|
||||
#else
|
||||
#include <deque>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#include "tensorflow/core/platform/denormal.h"
|
||||
#include "tensorflow/core/platform/logging.h"
|
||||
#include "tensorflow/core/platform/mutex.h"
|
||||
@ -24,26 +34,97 @@ limitations under the License.
|
||||
namespace tensorflow {
|
||||
namespace thread {
|
||||
|
||||
struct ThreadPool::Waiter {
|
||||
condition_variable cv;
|
||||
bool ready;
|
||||
#ifdef TENSORFLOW_USE_EIGEN_THREADPOOL
|
||||
|
||||
struct EigenEnvironment {
|
||||
typedef Thread EnvThread;
|
||||
struct Task {
|
||||
std::function<void()> f;
|
||||
uint64 trace_id;
|
||||
};
|
||||
|
||||
Env* const env_;
|
||||
const ThreadOptions thread_options_;
|
||||
const string name_;
|
||||
|
||||
EigenEnvironment(Env* env, const ThreadOptions& thread_options,
|
||||
const string& name)
|
||||
: env_(env), thread_options_(thread_options), name_(name) {}
|
||||
|
||||
EnvThread* CreateThread(std::function<void()> f) {
|
||||
return env_->StartThread(thread_options_, name_, [=]() {
|
||||
// Set the processor flag to flush denormals to zero
|
||||
port::ScopedFlushDenormal flush;
|
||||
f();
|
||||
});
|
||||
}
|
||||
|
||||
Task CreateTask(std::function<void()> f) {
|
||||
uint64 id = 0;
|
||||
if (port::Tracing::IsActive()) {
|
||||
id = port::Tracing::UniqueId();
|
||||
port::Tracing::RecordEvent(port::Tracing::EventCategory::kScheduleClosure,
|
||||
id);
|
||||
}
|
||||
return Task{std::move(f), id};
|
||||
}
|
||||
|
||||
void ExecuteTask(const Task& t) {
|
||||
if (t.trace_id != 0) {
|
||||
port::Tracing::ScopedActivity region(
|
||||
port::Tracing::EventCategory::kRunClosure, t.trace_id);
|
||||
t.f();
|
||||
} else {
|
||||
t.f();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ThreadPool::ThreadPool(Env* env, const string& name, int num_threads)
|
||||
: ThreadPool(env, ThreadOptions(), name, num_threads) {}
|
||||
struct ThreadPool::Impl : Eigen::ThreadPoolTempl<EigenEnvironment> {
|
||||
Impl(Env* env, const ThreadOptions& thread_options, const string& name,
|
||||
int num_threads)
|
||||
: Eigen::ThreadPoolTempl<EigenEnvironment>(
|
||||
num_threads, EigenEnvironment(env, thread_options, name)) {}
|
||||
};
|
||||
|
||||
ThreadPool::ThreadPool(Env* env, const ThreadOptions& thread_options,
|
||||
#else
|
||||
|
||||
struct ThreadPool::Impl {
|
||||
Impl(Env* env, const ThreadOptions& thread_options, const string& name,
|
||||
int num_threads);
|
||||
~Impl();
|
||||
void Schedule(std::function<void()> fn);
|
||||
|
||||
private:
|
||||
struct Waiter {
|
||||
condition_variable cv;
|
||||
bool ready;
|
||||
};
|
||||
|
||||
struct Task {
|
||||
std::function<void()> fn;
|
||||
uint64 id;
|
||||
};
|
||||
|
||||
void WorkerLoop();
|
||||
|
||||
const string name_;
|
||||
mutex mu_;
|
||||
std::vector<Thread*> threads_; // All threads
|
||||
std::vector<Waiter*> waiters_; // Stack of waiting threads.
|
||||
std::deque<Task> pending_; // Queue of pending work
|
||||
};
|
||||
|
||||
ThreadPool::Impl::Impl(Env* env, const ThreadOptions& thread_options,
|
||||
const string& name, int num_threads)
|
||||
: name_(name) {
|
||||
CHECK_GE(num_threads, 1);
|
||||
string name_prefix = "tf_" + name_;
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
threads_.push_back(env->StartThread(thread_options, name_prefix,
|
||||
[this]() { WorkerLoop(); }));
|
||||
threads_.push_back(
|
||||
env->StartThread(thread_options, name, [this]() { WorkerLoop(); }));
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool() {
|
||||
ThreadPool::Impl::~Impl() {
|
||||
{
|
||||
// Wait for all work to get done.
|
||||
mutex_lock l(mu_);
|
||||
@ -66,13 +147,7 @@ ThreadPool::~ThreadPool() {
|
||||
}
|
||||
}
|
||||
|
||||
bool ThreadPool::HasPendingClosures() const {
|
||||
mutex_lock l(mu_);
|
||||
return pending_.size() != 0;
|
||||
}
|
||||
|
||||
void ThreadPool::Schedule(std::function<void()> fn) {
|
||||
CHECK(fn != nullptr);
|
||||
void ThreadPool::Impl::Schedule(std::function<void()> fn) {
|
||||
uint64 id = 0;
|
||||
if (port::Tracing::IsActive()) {
|
||||
id = port::Tracing::UniqueId();
|
||||
@ -90,7 +165,7 @@ void ThreadPool::Schedule(std::function<void()> fn) {
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::WorkerLoop() {
|
||||
void ThreadPool::Impl::WorkerLoop() {
|
||||
// Set the processor flag to flush denormals to zero
|
||||
port::ScopedFlushDenormal flush;
|
||||
|
||||
@ -107,22 +182,40 @@ void ThreadPool::WorkerLoop() {
|
||||
}
|
||||
}
|
||||
// Pick up pending work
|
||||
Item item = pending_.front();
|
||||
Task t = pending_.front();
|
||||
pending_.pop_front();
|
||||
if (item.fn == nullptr) {
|
||||
if (t.fn == nullptr) {
|
||||
break;
|
||||
}
|
||||
mu_.unlock();
|
||||
if (item.id != 0) {
|
||||
if (t.id != 0) {
|
||||
port::Tracing::ScopedActivity region(
|
||||
port::Tracing::EventCategory::kRunClosure, item.id);
|
||||
item.fn();
|
||||
port::Tracing::EventCategory::kRunClosure, t.id);
|
||||
t.fn();
|
||||
} else {
|
||||
item.fn();
|
||||
t.fn();
|
||||
}
|
||||
mu_.lock();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ThreadPool::ThreadPool(Env* env, const string& name, int num_threads)
|
||||
: ThreadPool(env, ThreadOptions(), name, num_threads) {}
|
||||
|
||||
ThreadPool::ThreadPool(Env* env, const ThreadOptions& thread_options,
|
||||
const string& name, int num_threads) {
|
||||
CHECK_GE(num_threads, 1);
|
||||
impl_.reset(
|
||||
new ThreadPool::Impl(env, thread_options, "tf_" + name, num_threads));
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool() {}
|
||||
|
||||
void ThreadPool::Schedule(std::function<void()> fn) {
|
||||
CHECK(fn != nullptr);
|
||||
impl_->Schedule(std::move(fn));
|
||||
}
|
||||
|
||||
} // namespace thread
|
||||
} // namespace tensorflow
|
||||
|
@ -16,13 +16,10 @@ limitations under the License.
|
||||
#ifndef TENSORFLOW_LIB_CORE_THREADPOOL_H_
|
||||
#define TENSORFLOW_LIB_CORE_THREADPOOL_H_
|
||||
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "tensorflow/core/platform/env.h"
|
||||
#include "tensorflow/core/platform/macros.h"
|
||||
#include "tensorflow/core/platform/mutex.h"
|
||||
#include "tensorflow/core/platform/types.h"
|
||||
|
||||
namespace tensorflow {
|
||||
@ -45,28 +42,15 @@ class ThreadPool {
|
||||
|
||||
// Wait until all scheduled work has finished and then destroy the
|
||||
// set of threads.
|
||||
virtual ~ThreadPool();
|
||||
~ThreadPool();
|
||||
|
||||
// Schedule fn() for execution in the pool of threads.
|
||||
virtual void Schedule(std::function<void()> fn);
|
||||
void Schedule(std::function<void()> fn);
|
||||
|
||||
virtual bool HasPendingClosures() const;
|
||||
struct Impl;
|
||||
|
||||
private:
|
||||
struct Waiter;
|
||||
struct Item {
|
||||
std::function<void()> fn;
|
||||
uint64 id;
|
||||
};
|
||||
|
||||
void WorkerLoop();
|
||||
|
||||
const string name_;
|
||||
mutable mutex mu_;
|
||||
std::vector<Thread*> threads_; // All threads
|
||||
std::vector<Waiter*> waiters_; // Stack of waiting threads.
|
||||
std::deque<Item> pending_; // Queue of pending work
|
||||
|
||||
std::unique_ptr<Impl> impl_;
|
||||
TF_DISALLOW_COPY_AND_ASSIGN(ThreadPool);
|
||||
};
|
||||
|
||||
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||
#include <atomic>
|
||||
|
||||
#include "tensorflow/core/platform/env.h"
|
||||
#include "tensorflow/core/platform/mutex.h"
|
||||
#include "tensorflow/core/platform/test.h"
|
||||
#include "tensorflow/core/platform/test_benchmark.h"
|
||||
|
||||
|
@ -581,9 +581,9 @@ TEST(TableTest, ApproximateOffsetOfCompressed) {
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 10, 100));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 4000));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 4000));
|
||||
ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 7000));
|
||||
}
|
||||
|
||||
TEST(TableTest, SeekToFirstKeyDoesNotReadTooMuch) {
|
||||
|
@ -224,6 +224,87 @@ diagonal: The extracted diagonal.
|
||||
|
||||
)doc");
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
REGISTER_OP("BatchMatrixDiag")
|
||||
.Input("diagonal: T")
|
||||
.Output("output: T")
|
||||
.Attr("T: type")
|
||||
.Doc(R"doc(
|
||||
Returns a batched diagonal tensor with a given batched diagonal values.
|
||||
|
||||
Given a `diagonal`, this operation returns a tensor with the `diagonal` and
|
||||
everything else padded with zeros. The diagonal is computed as follows:
|
||||
|
||||
Assume `diagonal` has `k` dimensions `[I, J, K, ..., N]`, then the output is a
|
||||
tensor of rank `k+1` with dimensions [I, J, K, ..., N, N]` where:
|
||||
|
||||
`output[i, j, k, ..., m, n] = 1{m=n} * diagonal[i, j, k, ..., n]`.
|
||||
|
||||
For example:
|
||||
|
||||
```prettyprint
|
||||
# 'diagonal' is [[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
|
||||
and diagonal.shape = (2, 4)
|
||||
|
||||
tf.batch_matrix_diag(diagonal) ==> [[[1, 0, 0, 0]
|
||||
[0, 2, 0, 0]
|
||||
[0, 0, 3, 0]
|
||||
[0, 0, 0, 4]],
|
||||
[[5, 0, 0, 0]
|
||||
[0, 6, 0, 0]
|
||||
[0, 0, 7, 0]
|
||||
[0, 0, 0, 8]]]
|
||||
|
||||
which has shape (2, 4, 4)
|
||||
```
|
||||
|
||||
diagonal: Rank `k`, where `k >= 1`.
|
||||
output: Rank `k+1`, with `output.shape = diagonal.shape + [diagonal.shape[-1]]`.
|
||||
)doc");
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
REGISTER_OP("BatchMatrixDiagPart")
|
||||
.Input("input: T")
|
||||
.Output("diagonal: T")
|
||||
.Attr("T: type")
|
||||
.Doc(R"doc(
|
||||
Returns the batched diagonal part of a batched tensor.
|
||||
|
||||
This operation returns a tensor with the `diagonal` part
|
||||
of the batched `input`. The `diagonal` part is computed as follows:
|
||||
|
||||
Assume `input` has `k` dimensions `[I, J, K, ..., N, N]`, then the output is a
|
||||
tensor of rank `k - 1` with dimensions `[I, J, K, ..., N]` where:
|
||||
|
||||
`diagonal[i, j, k, ..., n] = input[i, j, k, ..., n, n]`.
|
||||
|
||||
The input must be at least a matrix.
|
||||
|
||||
For example:
|
||||
|
||||
```prettyprint
|
||||
# 'input' is [[[1, 0, 0, 0]
|
||||
[0, 2, 0, 0]
|
||||
[0, 0, 3, 0]
|
||||
[0, 0, 0, 4]],
|
||||
[[5, 0, 0, 0]
|
||||
[0, 6, 0, 0]
|
||||
[0, 0, 7, 0]
|
||||
[0, 0, 0, 8]]]
|
||||
|
||||
and input.shape = (2, 4, 4)
|
||||
|
||||
tf.batch_matrix_diag_part(input) ==> [[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
|
||||
which has shape (2, 4)
|
||||
```
|
||||
|
||||
input: Rank `k` tensor where `k >= 2` and the last two dimensions are equal.
|
||||
diagonal: The extracted diagonal(s) having shape
|
||||
`diagonal.shape = input.shape[:-1]`.
|
||||
)doc");
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
REGISTER_OP("Reverse")
|
||||
.Input("tensor: T")
|
||||
|
@ -3004,6 +3004,36 @@ op {
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixDiag"
|
||||
input_arg {
|
||||
name: "diagonal"
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixDiagPart"
|
||||
input_arg {
|
||||
name: "input"
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "diagonal"
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixInverse"
|
||||
input_arg {
|
||||
@ -3050,6 +3080,38 @@ op {
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixSolve"
|
||||
input_arg {
|
||||
name: "matrix"
|
||||
type_attr: "T"
|
||||
}
|
||||
input_arg {
|
||||
name: "rhs"
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "adjoint"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
allowed_values {
|
||||
list {
|
||||
type: DT_FLOAT
|
||||
type: DT_DOUBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixSolveLs"
|
||||
input_arg {
|
||||
@ -3118,6 +3180,45 @@ op {
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixTriangularSolve"
|
||||
input_arg {
|
||||
name: "matrix"
|
||||
type_attr: "T"
|
||||
}
|
||||
input_arg {
|
||||
name: "rhs"
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "lower"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: true
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "adjoint"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
allowed_values {
|
||||
list {
|
||||
type: DT_FLOAT
|
||||
type: DT_DOUBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "BatchNormWithGlobalNormalization"
|
||||
input_arg {
|
||||
@ -5258,6 +5359,13 @@ op {
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "DeleteSessionTensor"
|
||||
input_arg {
|
||||
name: "handle"
|
||||
type: DT_STRING
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "DepthToSpace"
|
||||
input_arg {
|
||||
@ -6509,6 +6617,36 @@ op {
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "GetSessionHandle"
|
||||
input_arg {
|
||||
name: "value"
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "handle"
|
||||
type: DT_STRING
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "GetSessionTensor"
|
||||
input_arg {
|
||||
name: "handle"
|
||||
type: DT_STRING
|
||||
}
|
||||
output_arg {
|
||||
name: "value"
|
||||
type_attr: "dtype"
|
||||
}
|
||||
attr {
|
||||
name: "dtype"
|
||||
type: "type"
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "Greater"
|
||||
input_arg {
|
||||
@ -8323,6 +8461,38 @@ op {
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "MatrixSolve"
|
||||
input_arg {
|
||||
name: "matrix"
|
||||
type_attr: "T"
|
||||
}
|
||||
input_arg {
|
||||
name: "rhs"
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "adjoint"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
allowed_values {
|
||||
list {
|
||||
type: DT_FLOAT
|
||||
type: DT_DOUBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "MatrixSolveLs"
|
||||
input_arg {
|
||||
@ -8391,6 +8561,45 @@ op {
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "MatrixTriangularSolve"
|
||||
input_arg {
|
||||
name: "matrix"
|
||||
type_attr: "T"
|
||||
}
|
||||
input_arg {
|
||||
name: "rhs"
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "lower"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: true
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "adjoint"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
allowed_values {
|
||||
list {
|
||||
type: DT_FLOAT
|
||||
type: DT_DOUBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "Max"
|
||||
input_arg {
|
||||
@ -11056,6 +11265,35 @@ op {
|
||||
type: DT_FLOAT
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "ReduceJoin"
|
||||
input_arg {
|
||||
name: "inputs"
|
||||
type: DT_STRING
|
||||
}
|
||||
input_arg {
|
||||
name: "reduction_indices"
|
||||
type: DT_INT32
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
type: DT_STRING
|
||||
}
|
||||
attr {
|
||||
name: "keep_dims"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "separator"
|
||||
type: "string"
|
||||
default_value {
|
||||
s: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
op {
|
||||
name: "RefEnter"
|
||||
input_arg {
|
||||
@ -16924,6 +17162,44 @@ op {
|
||||
}
|
||||
is_stateful: true
|
||||
}
|
||||
op {
|
||||
name: "TensorArray"
|
||||
input_arg {
|
||||
name: "size"
|
||||
type: DT_INT32
|
||||
}
|
||||
output_arg {
|
||||
name: "handle"
|
||||
type: DT_STRING
|
||||
is_ref: true
|
||||
}
|
||||
attr {
|
||||
name: "dtype"
|
||||
type: "type"
|
||||
}
|
||||
attr {
|
||||
name: "dynamic_size"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "clear_after_read"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: true
|
||||
}
|
||||
}
|
||||
attr {
|
||||
name: "tensor_array_name"
|
||||
type: "string"
|
||||
default_value {
|
||||
s: ""
|
||||
}
|
||||
}
|
||||
is_stateful: true
|
||||
}
|
||||
op {
|
||||
name: "TensorArrayClose"
|
||||
input_arg {
|
||||
|
@ -389,6 +389,7 @@ REGISTER_OP("TensorArray")
|
||||
.Input("size: int32")
|
||||
.Attr("dtype: type")
|
||||
.Attr("dynamic_size: bool = false")
|
||||
.Attr("clear_after_read: bool = true")
|
||||
.Attr("tensor_array_name: string = ''")
|
||||
.Output("handle: Ref(string)")
|
||||
.SetIsStateful()
|
||||
@ -401,6 +402,9 @@ size: The size of the array.
|
||||
dtype: The type of the elements on the tensor_array.
|
||||
dynamic_size: A boolean that determines whether writes to the TensorArray
|
||||
are allowed to grow the size. By default, this is not allowed.
|
||||
clear_after_read: If true (default), Tensors in the TensorArray are cleared
|
||||
after being read. This disables multiple read semantics but allows early
|
||||
release of memory.
|
||||
tensor_array_name: Overrides the name used for the temporary tensor_array
|
||||
resource. Default value is the name of the 'TensorArray' op (which
|
||||
is guaranteed unique).
|
||||
@ -483,7 +487,7 @@ REGISTER_OP("TensorArrayRead")
|
||||
.Output("value: dtype")
|
||||
.Attr("dtype: type")
|
||||
.Doc(R"doc(
|
||||
Read an element from the TensorArray.
|
||||
Read an element from the TensorArray into output `value`.
|
||||
|
||||
handle: The handle to a TensorArray.
|
||||
dtype: The type of the elem that is returned.
|
||||
@ -497,7 +501,7 @@ REGISTER_OP("TensorArrayPack")
|
||||
.Output("value: dtype")
|
||||
.Attr("dtype: type")
|
||||
.Doc(R"doc(
|
||||
Pack the elements from the TensorArray.
|
||||
Pack the elements from the TensorArray into output `value`.
|
||||
|
||||
All elements must have the same shape.
|
||||
|
||||
@ -530,12 +534,17 @@ REGISTER_OP("TensorArrayConcat")
|
||||
.Output("lengths: int64")
|
||||
.Attr("dtype: type")
|
||||
.Doc(R"doc(
|
||||
Concat the elements from the TensorArray.
|
||||
Concat the elements from the TensorArray into value `value`.
|
||||
|
||||
Takes `T` elements of shapes
|
||||
|
||||
```
|
||||
(n0 x d0 x d1 x ...), (n1 x d0 x d1 x ...), ..., (n(T-1) x d0 x d1 x ...)
|
||||
```
|
||||
|
||||
Takes T elements of shapes (n0 x d0 x d1 x ...), (n1 x d0 x d1 x ...),
|
||||
..., (n(T-1) x d0 x d1 x ...)
|
||||
and concatenates them into a Tensor of shape:
|
||||
(n0 + n1 + ... + n(T-1) x d0 x d1 x ...).
|
||||
|
||||
```(n0 + n1 + ... + n(T-1) x d0 x d1 x ...)```
|
||||
|
||||
All elements must have the same shape (excepting the first dimension).
|
||||
|
||||
@ -546,7 +555,7 @@ value: All of the elements in the TensorArray, concatenated along the first
|
||||
axis.
|
||||
lengths: A vector of the row sizes of the original T elements in the
|
||||
value output. In the example above, this would be the values:
|
||||
(n1, n2, ..., n(T-1))
|
||||
`(n1, n2, ..., n(T-1))`.
|
||||
)doc");
|
||||
|
||||
REGISTER_OP("TensorArraySplit")
|
||||
@ -560,15 +569,22 @@ REGISTER_OP("TensorArraySplit")
|
||||
Split the data from the input value into TensorArray elements.
|
||||
|
||||
Assuming that `lengths` takes on values
|
||||
(n0, n1, ..., n(T-1))
|
||||
|
||||
```(n0, n1, ..., n(T-1))```
|
||||
|
||||
and that `value` has shape
|
||||
(n0 + n1 + ... + n(T-1) x d0 x d1 x ...),
|
||||
|
||||
```(n0 + n1 + ... + n(T-1) x d0 x d1 x ...)```,
|
||||
|
||||
this splits values into a TensorArray with T tensors.
|
||||
|
||||
TensorArray index t will be the subtensor of values with starting position
|
||||
(n0 + n1 + ... + n(t-1), 0, 0, ...)
|
||||
|
||||
```(n0 + n1 + ... + n(t-1), 0, 0, ...)```
|
||||
|
||||
and having size
|
||||
nt x d0 x d1 x ...
|
||||
|
||||
```nt x d0 x d1 x ...```
|
||||
|
||||
handle: The handle to a TensorArray.
|
||||
value: The concatenated tensor to write to the TensorArray.
|
||||
@ -670,4 +686,35 @@ keys: Keys of type Tkey.
|
||||
values: Values of type Tval. Same shape as `keys`.
|
||||
)doc");
|
||||
|
||||
REGISTER_OP("GetSessionHandle")
|
||||
.Input("value: T")
|
||||
.Output("handle: string")
|
||||
.Attr("T: type")
|
||||
.Doc(R"doc(
|
||||
Store the input tensor in the state of the current session.
|
||||
|
||||
value: The tensor to be stored.
|
||||
handle: The handle for the tensor stored in the session state.
|
||||
)doc");
|
||||
|
||||
REGISTER_OP("GetSessionTensor")
|
||||
.Input("handle: string")
|
||||
.Output("value: dtype")
|
||||
.Attr("dtype: type")
|
||||
.Doc(R"doc(
|
||||
Get the value of the tensor specified by its handle.
|
||||
|
||||
handle: The handle for a tensor stored in the session state.
|
||||
value: The tensor for the given handle.
|
||||
dtype: The type of the output value.
|
||||
)doc");
|
||||
|
||||
REGISTER_OP("DeleteSessionTensor")
|
||||
.Input("handle: string")
|
||||
.Doc(R"doc(
|
||||
Delete the tensor specified by its handle in the session.
|
||||
|
||||
handle: The handle for a tensor stored in the session state.
|
||||
)doc");
|
||||
|
||||
} // namespace tensorflow
|
||||
|
@ -89,7 +89,7 @@ The generated
|
||||
[`Summary`](https://www.tensorflow.org/code/tensorflow/core/framework/summary.proto)
|
||||
has one summary value containing a histogram for `values`.
|
||||
|
||||
This op reports an `OutOfRange` error if any value is not finite.
|
||||
This op reports an `InvalidArgument` error if any value is not finite.
|
||||
|
||||
tag: Scalar. Tag to use for the `Summary.Value`.
|
||||
values: Any shape. Values to use to build the histogram.
|
||||
|
@ -1390,6 +1390,44 @@ op {
|
||||
summary: "Calculates the determinants for a batch of square matrices."
|
||||
description: "The input is a tensor of shape `[..., M, M]` whose inner-most 2 dimensions\nform square matrices. The output is a 1-D tensor containing the determinants\nfor all input submatrices `[..., :, :]`."
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixDiag"
|
||||
input_arg {
|
||||
name: "diagonal"
|
||||
description: "Rank `k`, where `k >= 1`."
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
description: "Rank `k+1`, with `output.shape = diagonal.shape + [diagonal.shape[-1]]`."
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
}
|
||||
summary: "Returns a batched diagonal tensor with a given batched diagonal values."
|
||||
description: "Given a `diagonal`, this operation returns a tensor with the `diagonal` and\neverything else padded with zeros. The diagonal is computed as follows:\n\nAssume `diagonal` has `k` dimensions `[I, J, K, ..., N]`, then the output is a\ntensor of rank `k+1` with dimensions [I, J, K, ..., N, N]` where:\n\n`output[i, j, k, ..., m, n] = 1{m=n} * diagonal[i, j, k, ..., n]`.\n\nFor example:\n\n```prettyprint\n# \'diagonal\' is [[1, 2, 3, 4], [5, 6, 7, 8]]\n\nand diagonal.shape = (2, 4)\n\ntf.batch_matrix_diag(diagonal) ==> [[[1, 0, 0, 0]\n [0, 2, 0, 0]\n [0, 0, 3, 0]\n [0, 0, 0, 4]],\n [[5, 0, 0, 0]\n [0, 6, 0, 0]\n [0, 0, 7, 0]\n [0, 0, 0, 8]]]\n\nwhich has shape (2, 4, 4)\n```"
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixDiagPart"
|
||||
input_arg {
|
||||
name: "input"
|
||||
description: "Rank `k` tensor where `k >= 2` and the last two dimensions are equal."
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "diagonal"
|
||||
description: "The extracted diagonal(s) having shape\n`diagonal.shape = input.shape[:-1]`."
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
}
|
||||
summary: "Returns the batched diagonal part of a batched tensor."
|
||||
description: "This operation returns a tensor with the `diagonal` part\nof the batched `input`. The `diagonal` part is computed as follows:\n\nAssume `input` has `k` dimensions `[I, J, K, ..., N, N]`, then the output is a\ntensor of rank `k - 1` with dimensions `[I, J, K, ..., N]` where:\n\n`diagonal[i, j, k, ..., n] = input[i, j, k, ..., n, n]`.\n\nThe input must be at least a matrix.\n\nFor example:\n\n```prettyprint\n# \'input\' is [[[1, 0, 0, 0]\n [0, 2, 0, 0]\n [0, 0, 3, 0]\n [0, 0, 0, 4]],\n [[5, 0, 0, 0]\n [0, 6, 0, 0]\n [0, 0, 7, 0]\n [0, 0, 0, 8]]]\n\nand input.shape = (2, 4, 4)\n\ntf.batch_matrix_diag_part(input) ==> [[1, 2, 3, 4], [5, 6, 7, 8]]\n\nwhich has shape (2, 4)\n```"
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixInverse"
|
||||
input_arg {
|
||||
@ -1432,6 +1470,14 @@ op {
|
||||
description: "Shape is `[..., M, K]`."
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "adjoint"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
description: "Boolean indicating whether to solve with `matrix` or its (block-wise)\nadjoint."
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
@ -1443,7 +1489,7 @@ op {
|
||||
}
|
||||
}
|
||||
summary: "Solves systems of linear equations. Checks for invertibility."
|
||||
description: "Matrix is a tensor of shape `[..., M, M]` whose inner-most 2 dimensions\nform square matrices. Rhs is a tensor of shape\n`[..., M, K]`. The output is a tensor shape `[..., M, K]` where each output\nmatrix satisfies matrix[..., :, :] * output[..., :, :] = rhs[..., :, :]."
|
||||
description: "Matrix is a tensor of shape `[..., M, M]` whose inner-most 2 dimensions\nform square matrices. Rhs is a tensor of shape\n`[..., M, K]`. The output is a tensor shape `[..., M, K]`. If `adjoint` is `False` then each output\nmatrix satisfies `matrix[..., :, :] * output[..., :, :] = rhs[..., :, :]`.\nIf `adjoint` is `True` then each output\nmatrix satisfies `adjoint(matrix[..., :, :]) * output[..., :, :] = rhs[..., :, :]`."
|
||||
}
|
||||
op {
|
||||
name: "BatchMatrixSolveLs"
|
||||
@ -1509,7 +1555,15 @@ op {
|
||||
default_value {
|
||||
b: true
|
||||
}
|
||||
description: "Boolean indicating whether matrix is lower or upper triangular."
|
||||
description: "Boolean indicating whether the innermost matrices in `matrix` are\nlower or upper triangular."
|
||||
}
|
||||
attr {
|
||||
name: "adjoint"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
description: "Boolean indicating whether to solve with `matrix` or its (block-wise)\nadjoint."
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
@ -1522,7 +1576,7 @@ op {
|
||||
}
|
||||
}
|
||||
summary: "Solves systems of linear equations with upper or lower triangular matrices by"
|
||||
description: "backsubstitution.\n\n`matrix` is a tensor of shape `[..., M, M]` whose inner-most 2 dimensions form\nsquare matrices. If `lower` is `True` then the strictly upper triangular part\nof each inner-most matrix is ignored. If `lower` is False then the strictly\nlower triangular part of each inner-most matrix is ignored. `rhs` is a tensor\nof shape [..., M, K]`.\n\nThe output is a tensor of shape `[..., M, K]`. If `lower` is `True` then the\noutput satisfies\n\\\\(\\sum_{k=0}^{i}\\\\) matrix[..., i, k] * output[..., k, j] = rhs[..., i, j].\nIf `lower` is false then the strictly then the output satisfies\n\\\\(sum_{k=i}^{K-1}\\\\) matrix[..., i, k] * output[..., k, j] = rhs[..., i, j]."
|
||||
description: "backsubstitution.\n\n`matrix` is a tensor of shape `[..., M, M]` whose inner-most 2 dimensions form\nsquare matrices. If `lower` is `True` then the strictly upper triangular part\nof each inner-most matrix is assumed to be zero and not accessed.\nIf `lower` is False then the strictly lower triangular part of each inner-most\nmatrix is assumed to be zero and not accessed.\n`rhs` is a tensor of shape [..., M, K]`.\n\nThe output is a tensor of shape `[..., M, K]`. If `adjoint` is `True` then the\ninnermost matrices in output` satisfy matrix equations\n`matrix[..., :, :] * output[..., :, :] = rhs[..., :, :]`.\nIf `adjoint` is `False` then the strictly then the innermost matrices in\n`output` satisfy matrix equations\n`adjoint(matrix[..., i, k]) * output[..., k, j] = rhs[..., i, j]`."
|
||||
}
|
||||
op {
|
||||
name: "BatchNormWithGlobalNormalization"
|
||||
@ -2835,6 +2889,15 @@ op {
|
||||
}
|
||||
summary: "Reinterpret the bytes of a string as a vector of numbers."
|
||||
}
|
||||
op {
|
||||
name: "DeleteSessionTensor"
|
||||
input_arg {
|
||||
name: "handle"
|
||||
description: "The handle for a tensor stored in the session state."
|
||||
type: DT_STRING
|
||||
}
|
||||
summary: "Delete the tensor specified by its handle in the session."
|
||||
}
|
||||
op {
|
||||
name: "DepthToSpace"
|
||||
input_arg {
|
||||
@ -4100,6 +4163,43 @@ op {
|
||||
summary: "Gather values from `params` according to `indices`."
|
||||
description: "`indices` must be integer tensor, containing indices into `params`.\nIt must be shape `[d_0, ..., d_N, R]` where `R` is the rank of `params`.\nThe innermost dimension of `indices` (with length `R`) corresponds to the\nindices of `params`.\n\nProduces an output tensor with shape `[d_0, ..., d_{n-1}]` where:\n\n output[i, j, k, ...] = params[indices[i, j, k, ..., :]]\n\ne.g. for `indices` a matrix:\n\n output[i] = params[indices[i, :]]"
|
||||
}
|
||||
op {
|
||||
name: "GetSessionHandle"
|
||||
input_arg {
|
||||
name: "value"
|
||||
description: "The tensor to be stored."
|
||||
type_attr: "T"
|
||||
}
|
||||
output_arg {
|
||||
name: "handle"
|
||||
description: "The handle for the tensor stored in the session state."
|
||||
type: DT_STRING
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
}
|
||||
summary: "Store the input tensor in the state of the current session."
|
||||
}
|
||||
op {
|
||||
name: "GetSessionTensor"
|
||||
input_arg {
|
||||
name: "handle"
|
||||
description: "The handle for a tensor stored in the session state."
|
||||
type: DT_STRING
|
||||
}
|
||||
output_arg {
|
||||
name: "value"
|
||||
description: "The tensor for the given handle."
|
||||
type_attr: "dtype"
|
||||
}
|
||||
attr {
|
||||
name: "dtype"
|
||||
type: "type"
|
||||
description: "The type of the output value."
|
||||
}
|
||||
summary: "Get the value of the tensor specified by its handle."
|
||||
}
|
||||
op {
|
||||
name: "Greater"
|
||||
input_arg {
|
||||
@ -4257,7 +4357,7 @@ op {
|
||||
}
|
||||
}
|
||||
summary: "Outputs a `Summary` protocol buffer with a histogram."
|
||||
description: "The generated\n[`Summary`](https://www.tensorflow.org/code/tensorflow/core/framework/summary.proto)\nhas one summary value containing a histogram for `values`.\n\nThis op reports an `OutOfRange` error if any value is not finite."
|
||||
description: "The generated\n[`Summary`](https://www.tensorflow.org/code/tensorflow/core/framework/summary.proto)\nhas one summary value containing a histogram for `values`.\n\nThis op reports an `InvalidArgument` error if any value is not finite."
|
||||
}
|
||||
op {
|
||||
name: "IFFT"
|
||||
@ -5403,9 +5503,17 @@ op {
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
description: "Shape is `[M, K]` containing the tensor that solves\nmatrix * output = rhs."
|
||||
description: "Shape is `[M, K]`. If `adjoint` is `False` then `output` that solves\n`matrix` * `output` = `rhs`. If `adjoint` is `True` then `output` that solves\n`adjoint(matrix)` * `output` = `rhs`."
|
||||
type_attr: "T"
|
||||
}
|
||||
attr {
|
||||
name: "adjoint"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
description: "Boolean indicating whether to solve with `matrix` or its adjoint."
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
type: "type"
|
||||
@ -5482,7 +5590,15 @@ op {
|
||||
default_value {
|
||||
b: true
|
||||
}
|
||||
description: "Boolean indicating whether matrix is lower or upper triangular."
|
||||
description: "Boolean indicating whether `matrix` is lower or upper triangular"
|
||||
}
|
||||
attr {
|
||||
name: "adjoint"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
description: "Boolean indicating whether to solve with `matrix` or its adjoint."
|
||||
}
|
||||
attr {
|
||||
name: "T"
|
||||
@ -5495,7 +5611,7 @@ op {
|
||||
}
|
||||
}
|
||||
summary: "Solves a system of linear equations with an upper or lower triangular matrix by"
|
||||
description: "backsubstitution.\n\n`matrix` is a matrix of shape `[M, M]`. If `lower` is `True` then the strictly\nupper triangular part of `matrix` is ignored. If `lower` is False then the\nstrictly lower triangular part of `matrix` is ignored. `rhs` is a matrix of\nshape [M, K]`.\n\nThe output is a matrix of shape `[M, K]`. If `lower` is `True` then the output\nsatisfies \\\\(\\sum_{k=0}^{i}\\\\) matrix[i, k] * output[k, j] = rhs[i, j].\nIf `lower` is false then output satisfies\n\\\\(\\sum_{k=i}^{K-1}\\\\) matrix[i, k] * output[k, j] = rhs[i, j]."
|
||||
description: "backsubstitution.\n\n`matrix` is a matrix of shape `[M, M]`. If `lower` is `True` then the strictly\nupper triangular part of `matrix` is assumed to be zero and not accessed.\nIf `lower` is False then the strictly lower triangular part of `matrix` is\nassumed to be zero and not accessed.\n`rhs` is a matrix of shape [M, K]`.\n\nThe output is a matrix of shape `[M, K]`. If `adjoint` is `False` the output\nsatisfies the matrix equation `matrix` * `output` = `rhs`.\nIf `adjoint` is `False` then `output` satisfies the matrix equation\n`matrix` * `output` = `rhs`.\nIf `adjoint` is `True` then `output` satisfies the matrix equation\n`adjoint(matrix)` * `output` = `rhs`."
|
||||
}
|
||||
op {
|
||||
name: "Max"
|
||||
@ -7568,6 +7684,42 @@ op {
|
||||
summary: "Returns the real part of a complex number."
|
||||
description: "Given a tensor `in` of complex numbers, this operation returns a tensor of type\n`float` that is the real part of each element in `in`. All elements in `in`\nmust be complex numbers of the form \\\\(a + bj\\\\), where *a* is the real part\nreturned by this operation and *b* is the imaginary part.\n\nFor example:\n\n```\n# tensor \'in\' is [-2.25 + 4.75j, 3.25 + 5.75j]\ntf.real(in) ==> [-2.25, 3.25]\n```"
|
||||
}
|
||||
op {
|
||||
name: "ReduceJoin"
|
||||
input_arg {
|
||||
name: "inputs"
|
||||
description: "The input to be joined. All reduced indices must have non-zero size."
|
||||
type: DT_STRING
|
||||
}
|
||||
input_arg {
|
||||
name: "reduction_indices"
|
||||
description: "The dimensions to reduce over. Dimensions are reduced in the\norder specified. If `reduction_indices` has higher rank than `1`, it is\nflattened. Omitting `reduction_indices` is equivalent to passing\n`[n-1, n-2, ..., 0]`. Negative indices from `-n` to `-1` are supported."
|
||||
type: DT_INT32
|
||||
}
|
||||
output_arg {
|
||||
name: "output"
|
||||
description: "Has shape equal to that of the input with reduced dimensions removed or\nset to `1` depending on `keep_dims`."
|
||||
type: DT_STRING
|
||||
}
|
||||
attr {
|
||||
name: "keep_dims"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: false
|
||||
}
|
||||
description: "If `True`, retain reduced dimensions with length `1`."
|
||||
}
|
||||
attr {
|
||||
name: "separator"
|
||||
type: "string"
|
||||
default_value {
|
||||
s: ""
|
||||
}
|
||||
description: "The separator to use when joining."
|
||||
}
|
||||
summary: "Joins a string Tensor across the given dimensions."
|
||||
description: "Computes the string join across dimensions in the given string Tensor of shape\n`[d_0, d_1, ..., d_n-1]`. Returns a new Tensor created by joining the input\nstrings with the given separator (default: empty string). Negative indices are\ncounted backwards from the end, with `-1` being equivalent to `n - 1`. Passing\nan empty `reduction_indices` joins all strings in linear index order and outputs\na scalar string.\n\n\nFor example:\n```\n# tensor `a` is [[\"a\", \"b\"], [\"c\", \"d\"]]\ntf.reduce_join(a, 0) ==> [\"ac\", \"bd\"]\ntf.reduce_join(a, 1) ==> [\"ab\", \"cd\"]\ntf.reduce_join(a, -2) = tf.reduce_join(a, 0) ==> [\"ac\", \"bd\"]\ntf.reduce_join(a, -1) = tf.reduce_join(a, 1) ==> [\"ab\", \"cd\"]\ntf.reduce_join(a, 0, keep_dims=True) ==> [[\"ac\", \"bd\"]]\ntf.reduce_join(a, 1, keep_dims=True) ==> [[\"ab\"], [\"cd\"]]\ntf.reduce_join(a, 0, separator=\".\") ==> [\"a.c\", \"b.d\"]\ntf.reduce_join(a, [0, 1]) ==> [\"acbd\"]\ntf.reduce_join(a, [1, 0]) ==> [\"abcd\"]\ntf.reduce_join(a, []) ==> [\"abcd\"]\n```"
|
||||
}
|
||||
op {
|
||||
name: "RefEnter"
|
||||
input_arg {
|
||||
@ -11107,6 +11259,14 @@ op {
|
||||
}
|
||||
description: "A boolean that determines whether writes to the TensorArray\nare allowed to grow the size. By default, this is not allowed."
|
||||
}
|
||||
attr {
|
||||
name: "clear_after_read"
|
||||
type: "bool"
|
||||
default_value {
|
||||
b: true
|
||||
}
|
||||
description: "If true (default), Tensors in the TensorArray are cleared\nafter being read. This disables multiple read semantics but allows early\nrelease of memory."
|
||||
}
|
||||
attr {
|
||||
name: "tensor_array_name"
|
||||
type: "string"
|
||||
@ -11150,7 +11310,7 @@ op {
|
||||
}
|
||||
output_arg {
|
||||
name: "lengths"
|
||||
description: "A vector of the row sizes of the original T elements in the\nvalue output. In the example above, this would be the values:\n(n1, n2, ..., n(T-1))"
|
||||
description: "A vector of the row sizes of the original T elements in the\nvalue output. In the example above, this would be the values:\n`(n1, n2, ..., n(T-1))`."
|
||||
type: DT_INT64
|
||||
}
|
||||
attr {
|
||||
@ -11158,8 +11318,8 @@ op {
|
||||
type: "type"
|
||||
description: "The type of the elem that is returned."
|
||||
}
|
||||
summary: "Concat the elements from the TensorArray."
|
||||
description: "Takes T elements of shapes (n0 x d0 x d1 x ...), (n1 x d0 x d1 x ...),\n ..., (n(T-1) x d0 x d1 x ...)\nand concatenates them into a Tensor of shape:\n (n0 + n1 + ... + n(T-1) x d0 x d1 x ...).\n\nAll elements must have the same shape (excepting the first dimension)."
|
||||
summary: "Concat the elements from the TensorArray into value `value`."
|
||||
description: "Takes `T` elements of shapes\n\n ```\n (n0 x d0 x d1 x ...), (n1 x d0 x d1 x ...), ..., (n(T-1) x d0 x d1 x ...)\n ```\n\nand concatenates them into a Tensor of shape:\n\n ```(n0 + n1 + ... + n(T-1) x d0 x d1 x ...)```\n\nAll elements must have the same shape (excepting the first dimension)."
|
||||
}
|
||||
op {
|
||||
name: "TensorArrayGrad"
|
||||
@ -11208,7 +11368,7 @@ op {
|
||||
type: "type"
|
||||
description: "The type of the elem that is returned."
|
||||
}
|
||||
summary: "Pack the elements from the TensorArray."
|
||||
summary: "Pack the elements from the TensorArray into output `value`."
|
||||
description: "All elements must have the same shape."
|
||||
}
|
||||
op {
|
||||
@ -11238,7 +11398,7 @@ op {
|
||||
type: "type"
|
||||
description: "The type of the elem that is returned."
|
||||
}
|
||||
summary: "Read an element from the TensorArray."
|
||||
summary: "Read an element from the TensorArray into output `value`."
|
||||
}
|
||||
op {
|
||||
name: "TensorArraySize"
|
||||
@ -11293,7 +11453,7 @@ op {
|
||||
type: "type"
|
||||
}
|
||||
summary: "Split the data from the input value into TensorArray elements."
|
||||
description: "Assuming that `lengths` takes on values\n (n0, n1, ..., n(T-1))\nand that `value` has shape\n (n0 + n1 + ... + n(T-1) x d0 x d1 x ...),\nthis splits values into a TensorArray with T tensors.\n\nTensorArray index t will be the subtensor of values with starting position\n (n0 + n1 + ... + n(t-1), 0, 0, ...)\nand having size\n nt x d0 x d1 x ..."
|
||||
description: "Assuming that `lengths` takes on values\n\n ```(n0, n1, ..., n(T-1))```\n\nand that `value` has shape\n\n ```(n0 + n1 + ... + n(T-1) x d0 x d1 x ...)```,\n\nthis splits values into a TensorArray with T tensors.\n\nTensorArray index t will be the subtensor of values with starting position\n\n ```(n0 + n1 + ... + n(t-1), 0, 0, ...)```\n\nand having size\n\n ```nt x d0 x d1 x ...```"
|
||||
}
|
||||
op {
|
||||
name: "TensorArrayUnpack"
|
||||
|
@ -33,4 +33,48 @@ num_buckets: The number of buckets.
|
||||
output: A Tensor of the same shape as the input `string_tensor`.
|
||||
)doc");
|
||||
|
||||
REGISTER_OP("ReduceJoin")
|
||||
.Input("inputs: string")
|
||||
.Input("reduction_indices: int32")
|
||||
.Attr("keep_dims: bool = false")
|
||||
.Attr("separator: string = ''")
|
||||
.Output("output: string")
|
||||
.Doc(R"doc(
|
||||
Joins a string Tensor across the given dimensions.
|
||||
|
||||
Computes the string join across dimensions in the given string Tensor of shape
|
||||
`[d_0, d_1, ..., d_n-1]`. Returns a new Tensor created by joining the input
|
||||
strings with the given separator (default: empty string). Negative indices are
|
||||
counted backwards from the end, with `-1` being equivalent to `n - 1`. Passing
|
||||
an empty `reduction_indices` joins all strings in linear index order and outputs
|
||||
a scalar string.
|
||||
|
||||
|
||||
For example:
|
||||
```
|
||||
# tensor `a` is [["a", "b"], ["c", "d"]]
|
||||
tf.reduce_join(a, 0) ==> ["ac", "bd"]
|
||||
tf.reduce_join(a, 1) ==> ["ab", "cd"]
|
||||
tf.reduce_join(a, -2) = tf.reduce_join(a, 0) ==> ["ac", "bd"]
|
||||
tf.reduce_join(a, -1) = tf.reduce_join(a, 1) ==> ["ab", "cd"]
|
||||
tf.reduce_join(a, 0, keep_dims=True) ==> [["ac", "bd"]]
|
||||
tf.reduce_join(a, 1, keep_dims=True) ==> [["ab"], ["cd"]]
|
||||
tf.reduce_join(a, 0, separator=".") ==> ["a.c", "b.d"]
|
||||
tf.reduce_join(a, [0, 1]) ==> ["acbd"]
|
||||
tf.reduce_join(a, [1, 0]) ==> ["abcd"]
|
||||
tf.reduce_join(a, []) ==> ["abcd"]
|
||||
```
|
||||
|
||||
inputs: The input to be joined. All reduced indices must have non-zero size.
|
||||
reduction_indices: The dimensions to reduce over. Dimensions are reduced in the
|
||||
order specified. If `reduction_indices` has higher rank than `1`, it is
|
||||
flattened. Omitting `reduction_indices` is equivalent to passing
|
||||
`[n-1, n-2, ..., 0]`. Negative indices from `-n` to `-1` are supported.
|
||||
keep_dims: If `True`, retain reduced dimensions with length `1`.
|
||||
separator: The separator to use when joining.
|
||||
|
||||
output: Has shape equal to that of the input with reduced dimensions removed or
|
||||
set to `1` depending on `keep_dims`.
|
||||
)doc");
|
||||
|
||||
} // namespace tensorflow
|
||||
|
@ -157,11 +157,6 @@ limitations under the License.
|
||||
// annotations will be ignored by the analysis.
|
||||
#define TS_UNCHECKED(x) ""
|
||||
|
||||
// Disables warnings for a single read operation. This can be used to do racy
|
||||
// reads of guarded data members, in cases where the race is benign.
|
||||
#define TS_UNCHECKED_READ(x) \
|
||||
::tensorflow::thread_safety_analysis::ts_unchecked_read(x)
|
||||
|
||||
namespace tensorflow {
|
||||
namespace thread_safety_analysis {
|
||||
|
||||
|
@ -29,30 +29,32 @@ class FileSystemRegistryImpl : public FileSystemRegistry {
|
||||
|
||||
private:
|
||||
mutable mutex mu_;
|
||||
mutable std::unordered_map<string, FileSystem*> registry_ GUARDED_BY(mu_);
|
||||
mutable std::unordered_map<string, std::unique_ptr<FileSystem>> registry_
|
||||
GUARDED_BY(mu_);
|
||||
};
|
||||
|
||||
void FileSystemRegistryImpl::Register(const string& scheme,
|
||||
FileSystemRegistry::Factory factory) {
|
||||
mutex_lock lock(mu_);
|
||||
QCHECK(!gtl::FindOrNull(registry_, scheme)) << "File factory for " << scheme
|
||||
<< " already registered";
|
||||
registry_[scheme] = factory();
|
||||
QCHECK(
|
||||
registry_.emplace(string(scheme), std::unique_ptr<FileSystem>(factory()))
|
||||
.second)
|
||||
<< "File factory for " << scheme << " already registered";
|
||||
}
|
||||
|
||||
FileSystem* FileSystemRegistryImpl::Lookup(const string& scheme) {
|
||||
mutex_lock lock(mu_);
|
||||
auto fs_ptr = gtl::FindOrNull(registry_, scheme);
|
||||
if (!fs_ptr) {
|
||||
const auto found = registry_.find(scheme);
|
||||
if (found == registry_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return *fs_ptr;
|
||||
return found->second.get();
|
||||
}
|
||||
|
||||
Status FileSystemRegistryImpl::GetRegisteredFileSystemSchemes(
|
||||
std::vector<string>* schemes) {
|
||||
mutex_lock lock(mu_);
|
||||
for (auto const e : registry_) {
|
||||
for (const auto& e : registry_) {
|
||||
schemes->push_back(e.first);
|
||||
}
|
||||
return Status::OK();
|
||||
@ -60,8 +62,6 @@ Status FileSystemRegistryImpl::GetRegisteredFileSystemSchemes(
|
||||
|
||||
Env::Env() : file_system_registry_(new FileSystemRegistryImpl) {}
|
||||
|
||||
Env::~Env() { delete file_system_registry_; }
|
||||
|
||||
Status Env::GetFileSystemForFile(const string& fname, FileSystem** result) {
|
||||
string scheme = GetSchemeFromURI(fname);
|
||||
FileSystem* file_system = file_system_registry_->Lookup(scheme);
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||
#define TENSORFLOW_CORE_PLATFORM_ENV_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@ -45,7 +46,7 @@ struct ThreadOptions;
|
||||
class Env {
|
||||
public:
|
||||
Env();
|
||||
virtual ~Env();
|
||||
virtual ~Env() = default;
|
||||
|
||||
/// \brief Returns a default environment suitable for the current operating
|
||||
/// system.
|
||||
@ -59,6 +60,8 @@ class Env {
|
||||
/// \brief Returns the FileSystem object to handle operations on the file
|
||||
/// specified by 'fname'. The FileSystem object is used as the implementation
|
||||
/// for the file system related (non-virtual) functions that follow.
|
||||
/// Returned FileSystem object is still owned by the Env object and will
|
||||
// (might) be destroyed when the environment is destroyed.
|
||||
virtual Status GetFileSystemForFile(const string& fname, FileSystem** result);
|
||||
|
||||
/// \brief Returns the file system schemes registered for this Env.
|
||||
@ -77,6 +80,10 @@ class Env {
|
||||
/// status.
|
||||
///
|
||||
/// The returned file may be concurrently accessed by multiple threads.
|
||||
///
|
||||
/// The ownership of the returned RandomAccessFile is passed to the caller
|
||||
/// and the object should be deleted when is not used. The file object
|
||||
/// shouldn't live longer than the Env object.
|
||||
Status NewRandomAccessFile(const string& fname, RandomAccessFile** result);
|
||||
|
||||
/// \brief Creates an object that writes to a new file with the specified
|
||||
@ -88,6 +95,10 @@ class Env {
|
||||
/// returns non-OK.
|
||||
///
|
||||
/// The returned file will only be accessed by one thread at a time.
|
||||
///
|
||||
/// The ownership of the returned WritableFile is passed to the caller
|
||||
/// and the object should be deleted when is not used. The file object
|
||||
/// shouldn't live longer than the Env object.
|
||||
Status NewWritableFile(const string& fname, WritableFile** result);
|
||||
|
||||
/// \brief Creates an object that either appends to an existing file, or
|
||||
@ -98,6 +109,10 @@ class Env {
|
||||
/// non-OK.
|
||||
///
|
||||
/// The returned file will only be accessed by one thread at a time.
|
||||
///
|
||||
/// The ownership of the returned WritableFile is passed to the caller
|
||||
/// and the object should be deleted when is not used. The file object
|
||||
/// shouldn't live longer than the Env object.
|
||||
Status NewAppendableFile(const string& fname, WritableFile** result);
|
||||
|
||||
/// \brief Creates a readonly region of memory with the file context.
|
||||
@ -107,6 +122,10 @@ class Env {
|
||||
/// the caller. On failure stores nullptr in *result and returns non-OK.
|
||||
///
|
||||
/// The returned memory region can be accessed from many threads in parallel.
|
||||
///
|
||||
/// The ownership of the returned ReadOnlyMemoryRegion is passed to the caller
|
||||
/// and the object should be deleted when is not used. The memory region
|
||||
/// object shouldn't live longer than the Env object.
|
||||
Status NewReadOnlyMemoryRegionFromFile(const string& fname,
|
||||
ReadOnlyMemoryRegion** result);
|
||||
|
||||
@ -192,7 +211,7 @@ class Env {
|
||||
Env(const Env&);
|
||||
void operator=(const Env&);
|
||||
|
||||
FileSystemRegistry* file_system_registry_;
|
||||
std::unique_ptr<FileSystemRegistry> file_system_registry_;
|
||||
};
|
||||
|
||||
/// \brief An implementation of Env that forwards all calls to another Env.
|
||||
|
@ -25,7 +25,6 @@ limitations under the License.
|
||||
#include "tensorflow/core/lib/core/status.h"
|
||||
#include "tensorflow/core/lib/core/stringpiece.h"
|
||||
#include "tensorflow/core/platform/macros.h"
|
||||
#include "tensorflow/core/platform/mutex.h"
|
||||
#include "tensorflow/core/platform/protobuf.h"
|
||||
#include "tensorflow/core/platform/types.h"
|
||||
|
||||
|
@ -1,29 +1,25 @@
|
||||
# Copyright 2015 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# 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,
|
||||
# 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.
|
||||
# ==============================================================================
|
||||
|
||||
"""A very simple MNIST classifier, modified to display data in TensorBoard.
|
||||
"""A simple MNIST classifier which displays summaries in TensorBoard.
|
||||
|
||||
See extensive documentation for the original model at
|
||||
http://tensorflow.org/tutorials/mnist/beginners/index.md
|
||||
|
||||
See documentation on the TensorBoard specific pieces at
|
||||
http://tensorflow.org/how_tos/summaries_and_tensorboard/index.md
|
||||
|
||||
If you modify this file, please update the excerpt in
|
||||
how_tos/summaries_and_tensorboard/index.md.
|
||||
This is an unimpressive MNIST model, but it is a good example of using
|
||||
tf.name_scope to make a graph legible in the TensorBoard graph explorer, and of
|
||||
naming summary tags so that they are grouped meaningfully in TensorBoard.
|
||||
|
||||
It demonstrates the functionality of every TensorBoard dashboard.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
@ -39,72 +35,132 @@ FLAGS = flags.FLAGS
|
||||
flags.DEFINE_boolean('fake_data', False, 'If true, uses fake data '
|
||||
'for unit testing.')
|
||||
flags.DEFINE_integer('max_steps', 1000, 'Number of steps to run trainer.')
|
||||
flags.DEFINE_float('learning_rate', 0.5, 'Initial learning rate.')
|
||||
flags.DEFINE_float('learning_rate', 0.001, 'Initial learning rate.')
|
||||
flags.DEFINE_float('dropout', 0.9, 'Keep probability for training dropout.')
|
||||
flags.DEFINE_string('data_dir', '/tmp/data', 'Directory for storing data')
|
||||
flags.DEFINE_string('summaries_dir', '/tmp/mnist_logs', 'Summaries directory')
|
||||
|
||||
|
||||
def main(_):
|
||||
def train():
|
||||
# Import data
|
||||
mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True,
|
||||
fake_data=FLAGS.fake_data)
|
||||
|
||||
sess = tf.InteractiveSession()
|
||||
|
||||
# Create the model
|
||||
x = tf.placeholder(tf.float32, [None, 784], name='x-input')
|
||||
W = tf.Variable(tf.zeros([784, 10]), name='weights')
|
||||
b = tf.Variable(tf.zeros([10]), name='bias')
|
||||
# Create a multilayer model.
|
||||
|
||||
# Use a name scope to organize nodes in the graph visualizer
|
||||
with tf.name_scope('Wx_b'):
|
||||
y = tf.nn.softmax(tf.matmul(x, W) + b)
|
||||
# Input placehoolders
|
||||
with tf.name_scope('input'):
|
||||
x = tf.placeholder(tf.float32, [None, 784], name='x-input')
|
||||
image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])
|
||||
tf.image_summary('input', image_shaped_input, 10)
|
||||
y_ = tf.placeholder(tf.float32, [None, 10], name='y-input')
|
||||
keep_prob = tf.placeholder(tf.float32)
|
||||
tf.scalar_summary('dropout_keep_probability', keep_prob)
|
||||
|
||||
# Add summary ops to collect data
|
||||
tf.histogram_summary('weights', W)
|
||||
tf.histogram_summary('biases', b)
|
||||
tf.histogram_summary('y', y)
|
||||
# We can't initialize these variables to 0 - the network will get stuck.
|
||||
def weight_variable(shape):
|
||||
"""Create a weight variable with appropriate initialization."""
|
||||
initial = tf.truncated_normal(shape, stddev=0.1)
|
||||
return tf.Variable(initial)
|
||||
|
||||
# Define loss and optimizer
|
||||
y_ = tf.placeholder(tf.float32, [None, 10], name='y-input')
|
||||
# More name scopes will clean up the graph representation
|
||||
with tf.name_scope('xent'):
|
||||
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
|
||||
def bias_variable(shape):
|
||||
"""Create a bias variable with appropriate initialization."""
|
||||
initial = tf.constant(0.1, shape=shape)
|
||||
return tf.Variable(initial)
|
||||
|
||||
def variable_summaries(var, name):
|
||||
"""Attach a lot of summaries to a Tensor."""
|
||||
with tf.name_scope('summaries'):
|
||||
mean = tf.reduce_mean(var)
|
||||
tf.scalar_summary('mean/' + name, mean)
|
||||
with tf.name_scope('stddev'):
|
||||
stddev = tf.sqrt(tf.reduce_sum(tf.square(var - mean)))
|
||||
tf.scalar_summary('sttdev/' + name, stddev)
|
||||
tf.scalar_summary('max/' + name, tf.reduce_max(var))
|
||||
tf.scalar_summary('min/' + name, tf.reduce_min(var))
|
||||
tf.histogram_summary(name, var)
|
||||
|
||||
def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu):
|
||||
"""Reusable code for making a simple neural net layer.
|
||||
|
||||
It does a matrix multiply, bias add, and then uses relu to nonlinearize.
|
||||
It also sets up name scoping so that the resultant graph is easy to read, and
|
||||
adds a number of summary ops.
|
||||
"""
|
||||
# Adding a name scope ensures logical grouping of the layers in the graph.
|
||||
with tf.name_scope(layer_name):
|
||||
# This Variable will hold the state of the weights for the layer
|
||||
with tf.name_scope('weights'):
|
||||
weights = weight_variable([input_dim, output_dim])
|
||||
variable_summaries(weights, layer_name + '/weights')
|
||||
with tf.name_scope('biases'):
|
||||
biases = bias_variable([output_dim])
|
||||
variable_summaries(biases, layer_name + '/biases')
|
||||
with tf.name_scope('Wx_plus_b'):
|
||||
preactivate = tf.matmul(input_tensor, weights) + biases
|
||||
tf.histogram_summary(layer_name + '/pre_activations', preactivate)
|
||||
activations = act(preactivate, 'activation')
|
||||
tf.histogram_summary(layer_name + '/activations', activations)
|
||||
return activations
|
||||
|
||||
hidden1 = nn_layer(x, 784, 500, 'layer1')
|
||||
dropped = tf.nn.dropout(hidden1, keep_prob)
|
||||
y = nn_layer(dropped, 500, 10, 'layer2', act=tf.nn.softmax)
|
||||
|
||||
|
||||
with tf.name_scope('cross_entropy'):
|
||||
diff = y_ * tf.log(y)
|
||||
with tf.name_scope('total'):
|
||||
cross_entropy = -tf.reduce_mean(diff)
|
||||
tf.scalar_summary('cross entropy', cross_entropy)
|
||||
|
||||
with tf.name_scope('train'):
|
||||
train_step = tf.train.GradientDescentOptimizer(
|
||||
train_step = tf.train.AdamOptimizer(
|
||||
FLAGS.learning_rate).minimize(cross_entropy)
|
||||
|
||||
with tf.name_scope('test'):
|
||||
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
|
||||
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
|
||||
with tf.name_scope('accuracy'):
|
||||
with tf.name_scope('correct_prediction'):
|
||||
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
|
||||
with tf.name_scope('accuracy'):
|
||||
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
|
||||
tf.scalar_summary('accuracy', accuracy)
|
||||
|
||||
# Merge all the summaries and write them out to /tmp/mnist_logs (by default)
|
||||
merged = tf.merge_all_summaries()
|
||||
writer = tf.train.SummaryWriter(FLAGS.summaries_dir, sess.graph)
|
||||
train_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/train', sess.graph)
|
||||
test_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/test')
|
||||
tf.initialize_all_variables().run()
|
||||
|
||||
# Train the model, and feed in test data and record summaries every 10 steps
|
||||
# Train the model, and also write summaries.
|
||||
# Every 10th step, measure test-set accuracy, and write test summaries
|
||||
# All other steps, run train_step on training data, & add training summaries
|
||||
|
||||
def feed_dict(train):
|
||||
"""Make a TensorFlow feed_dict: maps data onto Tensor placeholders."""
|
||||
if train or FLAGS.fake_data:
|
||||
xs, ys = mnist.train.next_batch(100, fake_data=FLAGS.fake_data)
|
||||
k = FLAGS.dropout
|
||||
else:
|
||||
xs, ys = mnist.test.images, mnist.test.labels
|
||||
k = 1.0
|
||||
return {x: xs, y_: ys, keep_prob: k}
|
||||
|
||||
for i in range(FLAGS.max_steps):
|
||||
if i % 10 == 0: # Record summary data and the accuracy
|
||||
if FLAGS.fake_data:
|
||||
batch_xs, batch_ys = mnist.train.next_batch(
|
||||
100, fake_data=FLAGS.fake_data)
|
||||
feed = {x: batch_xs, y_: batch_ys}
|
||||
else:
|
||||
feed = {x: mnist.test.images, y_: mnist.test.labels}
|
||||
result = sess.run([merged, accuracy], feed_dict=feed)
|
||||
summary_str = result[0]
|
||||
acc = result[1]
|
||||
writer.add_summary(summary_str, i)
|
||||
if i % 10 == 0: # Record summaries and test-set accuracy
|
||||
summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))
|
||||
test_writer.add_summary(summary, i)
|
||||
print('Accuracy at step %s: %s' % (i, acc))
|
||||
else:
|
||||
batch_xs, batch_ys = mnist.train.next_batch(
|
||||
100, fake_data=FLAGS.fake_data)
|
||||
feed = {x: batch_xs, y_: batch_ys}
|
||||
sess.run(train_step, feed_dict=feed)
|
||||
else: # Record train set summarieis, and train
|
||||
summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True))
|
||||
train_writer.add_summary(summary, i)
|
||||
|
||||
def main(_):
|
||||
if tf.gfile.Exists(FLAGS.summaries_dir):
|
||||
tf.gfile.DeleteRecursively(FLAGS.summaries_dir)
|
||||
tf.gfile.MakeDirs(FLAGS.summaries_dir)
|
||||
train()
|
||||
|
||||
if __name__ == '__main__':
|
||||
tf.app.run()
|
||||
|
@ -117,6 +117,9 @@ method. A graph element can be one of the following types:
|
||||
the *i*th return value will be a
|
||||
[`SparseTensorValue`](../../api_docs/python/sparse_ops.md#SparseTensorValue)
|
||||
containing the value of that sparse tensor.
|
||||
* If the *i*th element of `fetches` is produced by a `get_tensor_handle` op,
|
||||
the *i*th return value will be a numpy ndarray containing the handle of
|
||||
that tensor.
|
||||
|
||||
The optional `feed_dict` argument allows the caller to override
|
||||
the value of tensors in the graph. Each key in `feed_dict` can be
|
||||
@ -620,7 +623,7 @@ Creates an `AbortedError`.
|
||||
|
||||
### `class tf.errors.OutOfRangeError` {#OutOfRangeError}
|
||||
|
||||
Raised when an operation executed past the valid range.
|
||||
Raised when an operation iterates past the valid input range.
|
||||
|
||||
This exception is raised in "end-of-file" conditions, such as when a
|
||||
[`queue.dequeue()`](../../api_docs/python/io_ops.md#QueueBase.dequeue)
|
||||
|
@ -175,7 +175,7 @@ the same non-zero number and type of outputs.
|
||||
y = tf.constant(5)
|
||||
def f1(): return tf.mul(x, 17)
|
||||
def f2(): return tf.add(y, 23)
|
||||
r = cond(math_ops.less(x, y), f1, f2)
|
||||
r = cond(tf.less(x, y), f1, f2)
|
||||
# r is set to f1().
|
||||
# Operations in f2 (e.g., tf.add) are not executed.
|
||||
```
|
||||
@ -259,6 +259,55 @@ Example 2:
|
||||
callable.
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.while_loop(cond, body, loop_vars, parallel_iterations=10, back_prop=True, swap_memory=False, name=None)` {#while_loop}
|
||||
|
||||
Repeat `body` while the condition `cond` is true.
|
||||
|
||||
`cond` is a callable taking a list of tensors and returning a boolean scalar
|
||||
tensor. `body` is a callable taking a list of tensors and returning a list of
|
||||
tensors of the same length and with the same types as the input. `loop_vars`
|
||||
is a list of tensors that is passed to both `cond` and `body`.
|
||||
|
||||
In addition to regular Tensors or IndexedSlices, the body may accept and
|
||||
return TensorArray objects. The flows of the TensorArray objects will
|
||||
be appropriately forwarded between loops and during gradient calculations.
|
||||
|
||||
While `cond` evaluates to true, `body` is executed.
|
||||
|
||||
##### Args:
|
||||
|
||||
|
||||
* <b>`cond`</b>: The termination condition of the loop.
|
||||
* <b>`body`</b>: A callable that represents the loop body.
|
||||
* <b>`loop_vars`</b>: The list of variable input tensors.
|
||||
* <b>`parallel_iterations`</b>: The number of iterations allowed to run in parallel.
|
||||
* <b>`back_prop`</b>: Whether backprop is enabled for this while loop.
|
||||
* <b>`swap_memory`</b>: Whether GPU-CPU memory swap is enabled for this loop.
|
||||
* <b>`name`</b>: Optional name prefix for the returned tensors.
|
||||
|
||||
##### Returns:
|
||||
|
||||
The output tensors for the loop variables after the loop.
|
||||
|
||||
##### Raises:
|
||||
|
||||
|
||||
* <b>`TypeError`</b>: if `cond` or `body` is not callable.
|
||||
* <b>`ValueError`</b>: if `loop_var` is empty.
|
||||
|
||||
|
||||
* <b>`Example`</b>:
|
||||
|
||||
```python
|
||||
i = tf.constant(0)
|
||||
c = lambda i: tf.less(i, 10)
|
||||
b = lambda i: tf.add(i, 1)
|
||||
r = tf.while_loop(c, b, [i])
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Logical Operators
|
||||
|
||||
|
@ -32,6 +32,7 @@ equal width and determined by the arguments `value_range` and `nbins`.
|
||||
|
||||
|
||||
* <b>`Examples`</b>:
|
||||
|
||||
```python
|
||||
# Bins will be: (-inf, 1), [1, 2), [2, 3), [3, 4), [4, inf)
|
||||
nbins = 5
|
||||
|
@ -141,6 +141,8 @@
|
||||
* [`batch_ifft3d`](../../api_docs/python/math_ops.md#batch_ifft3d)
|
||||
* [`batch_matmul`](../../api_docs/python/math_ops.md#batch_matmul)
|
||||
* [`batch_matrix_determinant`](../../api_docs/python/math_ops.md#batch_matrix_determinant)
|
||||
* [`batch_matrix_diag`](../../api_docs/python/math_ops.md#batch_matrix_diag)
|
||||
* [`batch_matrix_diag_part`](../../api_docs/python/math_ops.md#batch_matrix_diag_part)
|
||||
* [`batch_matrix_inverse`](../../api_docs/python/math_ops.md#batch_matrix_inverse)
|
||||
* [`batch_matrix_solve`](../../api_docs/python/math_ops.md#batch_matrix_solve)
|
||||
* [`batch_matrix_solve_ls`](../../api_docs/python/math_ops.md#batch_matrix_solve_ls)
|
||||
@ -224,6 +226,10 @@
|
||||
* [`unsorted_segment_sum`](../../api_docs/python/math_ops.md#unsorted_segment_sum)
|
||||
* [`where`](../../api_docs/python/math_ops.md#where)
|
||||
|
||||
* **[Strings](../../api_docs/python/string_ops.md)**:
|
||||
* [`reduce_join`](../../api_docs/python/string_ops.md#reduce_join)
|
||||
* [`string_to_hash_bucket`](../../api_docs/python/string_ops.md#string_to_hash_bucket)
|
||||
|
||||
* **[Histograms](../../api_docs/python/histogram_ops.md)**:
|
||||
* [`histogram_fixed_width`](../../api_docs/python/histogram_ops.md#histogram_fixed_width)
|
||||
|
||||
@ -255,6 +261,7 @@
|
||||
* [`tuple`](../../api_docs/python/control_flow_ops.md#tuple)
|
||||
* [`verify_tensor_all_finite`](../../api_docs/python/control_flow_ops.md#verify_tensor_all_finite)
|
||||
* [`where`](../../api_docs/python/control_flow_ops.md#where)
|
||||
* [`while_loop`](../../api_docs/python/control_flow_ops.md#while_loop)
|
||||
|
||||
* **[Higher Order Functions](../../api_docs/python/functional_ops.md)**:
|
||||
* [`foldl`](../../api_docs/python/functional_ops.md#foldl)
|
||||
@ -262,6 +269,11 @@
|
||||
* [`map_fn`](../../api_docs/python/functional_ops.md#map_fn)
|
||||
* [`scan`](../../api_docs/python/functional_ops.md#scan)
|
||||
|
||||
* **[Tensor Handle Operations](../../api_docs/python/session_ops.md)**:
|
||||
* [`delete_session_tensor`](../../api_docs/python/session_ops.md#delete_session_tensor)
|
||||
* [`get_session_handle`](../../api_docs/python/session_ops.md#get_session_handle)
|
||||
* [`get_session_tensor`](../../api_docs/python/session_ops.md#get_session_tensor)
|
||||
|
||||
* **[Images](../../api_docs/python/image.md)**:
|
||||
* [`adjust_brightness`](../../api_docs/python/image.md#adjust_brightness)
|
||||
* [`adjust_contrast`](../../api_docs/python/image.md#adjust_contrast)
|
||||
|
@ -741,6 +741,101 @@ Gamma function.
|
||||
TensorFlow provides several operations that you can use to add basic
|
||||
mathematical functions for matrices to your graph.
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.batch_matrix_diag(diagonal, name=None)` {#batch_matrix_diag}
|
||||
|
||||
Returns a batched diagonal tensor with a given batched diagonal values.
|
||||
|
||||
Given a `diagonal`, this operation returns a tensor with the `diagonal` and
|
||||
everything else padded with zeros. The diagonal is computed as follows:
|
||||
|
||||
Assume `diagonal` has `k` dimensions `[I, J, K, ..., N]`, then the output is a
|
||||
tensor of rank `k+1` with dimensions [I, J, K, ..., N, N]` where:
|
||||
|
||||
`output[i, j, k, ..., m, n] = 1{m=n} * diagonal[i, j, k, ..., n]`.
|
||||
|
||||
For example:
|
||||
|
||||
```prettyprint
|
||||
# 'diagonal' is [[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
|
||||
and diagonal.shape = (2, 4)
|
||||
|
||||
tf.batch_matrix_diag(diagonal) ==> [[[1, 0, 0, 0]
|
||||
[0, 2, 0, 0]
|
||||
[0, 0, 3, 0]
|
||||
[0, 0, 0, 4]],
|
||||
[[5, 0, 0, 0]
|
||||
[0, 6, 0, 0]
|
||||
[0, 0, 7, 0]
|
||||
[0, 0, 0, 8]]]
|
||||
|
||||
which has shape (2, 4, 4)
|
||||
```
|
||||
|
||||
##### Args:
|
||||
|
||||
|
||||
* <b>`diagonal`</b>: A `Tensor`. Rank `k`, where `k >= 1`.
|
||||
* <b>`name`</b>: A name for the operation (optional).
|
||||
|
||||
##### Returns:
|
||||
|
||||
A `Tensor`. Has the same type as `diagonal`.
|
||||
Rank `k+1`, with `output.shape = diagonal.shape + [diagonal.shape[-1]]`.
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.batch_matrix_diag_part(input, name=None)` {#batch_matrix_diag_part}
|
||||
|
||||
Returns the batched diagonal part of a batched tensor.
|
||||
|
||||
This operation returns a tensor with the `diagonal` part
|
||||
of the batched `input`. The `diagonal` part is computed as follows:
|
||||
|
||||
Assume `input` has `k` dimensions `[I, J, K, ..., N, N]`, then the output is a
|
||||
tensor of rank `k - 1` with dimensions `[I, J, K, ..., N]` where:
|
||||
|
||||
`diagonal[i, j, k, ..., n] = input[i, j, k, ..., n, n]`.
|
||||
|
||||
The input must be at least a matrix.
|
||||
|
||||
For example:
|
||||
|
||||
```prettyprint
|
||||
# 'input' is [[[1, 0, 0, 0]
|
||||
[0, 2, 0, 0]
|
||||
[0, 0, 3, 0]
|
||||
[0, 0, 0, 4]],
|
||||
[[5, 0, 0, 0]
|
||||
[0, 6, 0, 0]
|
||||
[0, 0, 7, 0]
|
||||
[0, 0, 0, 8]]]
|
||||
|
||||
and input.shape = (2, 4, 4)
|
||||
|
||||
tf.batch_matrix_diag_part(input) ==> [[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
|
||||
which has shape (2, 4)
|
||||
```
|
||||
|
||||
##### Args:
|
||||
|
||||
|
||||
* <b>`input`</b>: A `Tensor`.
|
||||
Rank `k` tensor where `k >= 2` and the last two dimensions are equal.
|
||||
* <b>`name`</b>: A name for the operation (optional).
|
||||
|
||||
##### Returns:
|
||||
|
||||
A `Tensor`. Has the same type as `input`.
|
||||
The extracted diagonal(s) having shape
|
||||
`diagonal.shape = input.shape[:-1]`.
|
||||
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.diag(diagonal, name=None)` {#diag}
|
||||
@ -1192,7 +1287,7 @@ eigenvalues, and subsequent [...,1:, :] containing the eigenvectors.
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.matrix_solve(matrix, rhs, name=None)` {#matrix_solve}
|
||||
### `tf.matrix_solve(matrix, rhs, adjoint=None, name=None)` {#matrix_solve}
|
||||
|
||||
Solves a system of linear equations. Checks for invertibility.
|
||||
|
||||
@ -1202,25 +1297,30 @@ Solves a system of linear equations. Checks for invertibility.
|
||||
* <b>`matrix`</b>: A `Tensor`. Must be one of the following types: `float32`, `float64`.
|
||||
Shape is `[M, M]`.
|
||||
* <b>`rhs`</b>: A `Tensor`. Must have the same type as `matrix`. Shape is `[M, K]`.
|
||||
* <b>`adjoint`</b>: An optional `bool`. Defaults to `False`.
|
||||
Boolean indicating whether to solve with `matrix` or its adjoint.
|
||||
* <b>`name`</b>: A name for the operation (optional).
|
||||
|
||||
##### Returns:
|
||||
|
||||
A `Tensor`. Has the same type as `matrix`.
|
||||
Shape is `[M, K]` containing the tensor that solves
|
||||
matrix * output = rhs.
|
||||
Shape is `[M, K]`. If `adjoint` is `False` then `output` that solves
|
||||
`matrix` * `output` = `rhs`. If `adjoint` is `True` then `output` that solves
|
||||
`adjoint(matrix)` * `output` = `rhs`.
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.batch_matrix_solve(matrix, rhs, name=None)` {#batch_matrix_solve}
|
||||
### `tf.batch_matrix_solve(matrix, rhs, adjoint=None, name=None)` {#batch_matrix_solve}
|
||||
|
||||
Solves systems of linear equations. Checks for invertibility.
|
||||
|
||||
Matrix is a tensor of shape `[..., M, M]` whose inner-most 2 dimensions
|
||||
form square matrices. Rhs is a tensor of shape
|
||||
`[..., M, K]`. The output is a tensor shape `[..., M, K]` where each output
|
||||
matrix satisfies matrix[..., :, :] * output[..., :, :] = rhs[..., :, :].
|
||||
`[..., M, K]`. The output is a tensor shape `[..., M, K]`. If `adjoint` is `False` then each output
|
||||
matrix satisfies `matrix[..., :, :] * output[..., :, :] = rhs[..., :, :]`.
|
||||
If `adjoint` is `True` then each output
|
||||
matrix satisfies `adjoint(matrix[..., :, :]) * output[..., :, :] = rhs[..., :, :]`.
|
||||
|
||||
##### Args:
|
||||
|
||||
@ -1229,6 +1329,9 @@ matrix satisfies matrix[..., :, :] * output[..., :, :] = rhs[..., :, :].
|
||||
Shape is `[..., M, M]`.
|
||||
* <b>`rhs`</b>: A `Tensor`. Must have the same type as `matrix`.
|
||||
Shape is `[..., M, K]`.
|
||||
* <b>`adjoint`</b>: An optional `bool`. Defaults to `False`.
|
||||
Boolean indicating whether to solve with `matrix` or its (block-wise)
|
||||
adjoint.
|
||||
* <b>`name`</b>: A name for the operation (optional).
|
||||
|
||||
##### Returns:
|
||||
@ -1239,21 +1342,24 @@ matrix satisfies matrix[..., :, :] * output[..., :, :] = rhs[..., :, :].
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.matrix_triangular_solve(matrix, rhs, lower=None, name=None)` {#matrix_triangular_solve}
|
||||
### `tf.matrix_triangular_solve(matrix, rhs, lower=None, adjoint=None, name=None)` {#matrix_triangular_solve}
|
||||
|
||||
Solves a system of linear equations with an upper or lower triangular matrix by
|
||||
|
||||
backsubstitution.
|
||||
|
||||
`matrix` is a matrix of shape `[M, M]`. If `lower` is `True` then the strictly
|
||||
upper triangular part of `matrix` is ignored. If `lower` is False then the
|
||||
strictly lower triangular part of `matrix` is ignored. `rhs` is a matrix of
|
||||
shape [M, K]`.
|
||||
upper triangular part of `matrix` is assumed to be zero and not accessed.
|
||||
If `lower` is False then the strictly lower triangular part of `matrix` is
|
||||
assumed to be zero and not accessed.
|
||||
`rhs` is a matrix of shape [M, K]`.
|
||||
|
||||
The output is a matrix of shape `[M, K]`. If `lower` is `True` then the output
|
||||
satisfies \\(\sum_{k=0}^{i}\\) matrix[i, k] * output[k, j] = rhs[i, j].
|
||||
If `lower` is false then output satisfies
|
||||
\\(\sum_{k=i}^{K-1}\\) matrix[i, k] * output[k, j] = rhs[i, j].
|
||||
The output is a matrix of shape `[M, K]`. If `adjoint` is `False` the output
|
||||
satisfies the matrix equation `matrix` * `output` = `rhs`.
|
||||
If `adjoint` is `False` then `output` satisfies the matrix equation
|
||||
`matrix` * `output` = `rhs`.
|
||||
If `adjoint` is `True` then `output` satisfies the matrix equation
|
||||
`adjoint(matrix)` * `output` = `rhs`.
|
||||
|
||||
##### Args:
|
||||
|
||||
@ -1262,7 +1368,9 @@ If `lower` is false then output satisfies
|
||||
Shape is `[M, M]`.
|
||||
* <b>`rhs`</b>: A `Tensor`. Must have the same type as `matrix`. Shape is `[M, K]`.
|
||||
* <b>`lower`</b>: An optional `bool`. Defaults to `True`.
|
||||
Boolean indicating whether matrix is lower or upper triangular.
|
||||
Boolean indicating whether `matrix` is lower or upper triangular
|
||||
* <b>`adjoint`</b>: An optional `bool`. Defaults to `False`.
|
||||
Boolean indicating whether to solve with `matrix` or its adjoint.
|
||||
* <b>`name`</b>: A name for the operation (optional).
|
||||
|
||||
##### Returns:
|
||||
@ -1272,7 +1380,7 @@ If `lower` is false then output satisfies
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.batch_matrix_triangular_solve(matrix, rhs, lower=None, name=None)` {#batch_matrix_triangular_solve}
|
||||
### `tf.batch_matrix_triangular_solve(matrix, rhs, lower=None, adjoint=None, name=None)` {#batch_matrix_triangular_solve}
|
||||
|
||||
Solves systems of linear equations with upper or lower triangular matrices by
|
||||
|
||||
@ -1280,15 +1388,17 @@ backsubstitution.
|
||||
|
||||
`matrix` is a tensor of shape `[..., M, M]` whose inner-most 2 dimensions form
|
||||
square matrices. If `lower` is `True` then the strictly upper triangular part
|
||||
of each inner-most matrix is ignored. If `lower` is False then the strictly
|
||||
lower triangular part of each inner-most matrix is ignored. `rhs` is a tensor
|
||||
of shape [..., M, K]`.
|
||||
of each inner-most matrix is assumed to be zero and not accessed.
|
||||
If `lower` is False then the strictly lower triangular part of each inner-most
|
||||
matrix is assumed to be zero and not accessed.
|
||||
`rhs` is a tensor of shape [..., M, K]`.
|
||||
|
||||
The output is a tensor of shape `[..., M, K]`. If `lower` is `True` then the
|
||||
output satisfies
|
||||
\\(\sum_{k=0}^{i}\\) matrix[..., i, k] * output[..., k, j] = rhs[..., i, j].
|
||||
If `lower` is false then the strictly then the output satisfies
|
||||
\\(sum_{k=i}^{K-1}\\) matrix[..., i, k] * output[..., k, j] = rhs[..., i, j].
|
||||
The output is a tensor of shape `[..., M, K]`. If `adjoint` is `True` then the
|
||||
innermost matrices in output` satisfy matrix equations
|
||||
`matrix[..., :, :] * output[..., :, :] = rhs[..., :, :]`.
|
||||
If `adjoint` is `False` then the strictly then the innermost matrices in
|
||||
`output` satisfy matrix equations
|
||||
`adjoint(matrix[..., i, k]) * output[..., k, j] = rhs[..., i, j]`.
|
||||
|
||||
##### Args:
|
||||
|
||||
@ -1298,7 +1408,11 @@ If `lower` is false then the strictly then the output satisfies
|
||||
* <b>`rhs`</b>: A `Tensor`. Must have the same type as `matrix`.
|
||||
Shape is `[..., M, K]`.
|
||||
* <b>`lower`</b>: An optional `bool`. Defaults to `True`.
|
||||
Boolean indicating whether matrix is lower or upper triangular.
|
||||
Boolean indicating whether the innermost matrices in `matrix` are
|
||||
lower or upper triangular.
|
||||
* <b>`adjoint`</b>: An optional `bool`. Defaults to `False`.
|
||||
Boolean indicating whether to solve with `matrix` or its (block-wise)
|
||||
adjoint.
|
||||
* <b>`name`</b>: A name for the operation (optional).
|
||||
|
||||
##### Returns:
|
||||
|
102
tensorflow/g3doc/api_docs/python/session_ops.md
Normal file
102
tensorflow/g3doc/api_docs/python/session_ops.md
Normal file
@ -0,0 +1,102 @@
|
||||
<!-- This file is machine generated: DO NOT EDIT! -->
|
||||
|
||||
# Tensor Handle Operations
|
||||
|
||||
Note: Functions taking `Tensor` arguments can also take anything accepted by
|
||||
[`tf.convert_to_tensor`](framework.md#convert_to_tensor).
|
||||
|
||||
[TOC]
|
||||
|
||||
## Tensor Handle Operations.
|
||||
|
||||
TensorFlow provides several operators that allows the user to keep tensors
|
||||
"in-place" across run calls.
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.get_session_handle(data, name=None)` {#get_session_handle}
|
||||
|
||||
Return the handle of `data`.
|
||||
|
||||
This is EXPERIMENTAL and subject to change.
|
||||
|
||||
Keep `data` "in-place" in the runtime and create a handle that can be
|
||||
used to retrieve `data` in a subsequent run().
|
||||
|
||||
Combined with `get_session_tensor`, we can keep a tensor produced in
|
||||
one run call in place, and use it as the input in a future run call.
|
||||
Below is a simple example:
|
||||
|
||||
```python
|
||||
c = tf.mul(a, b)
|
||||
h = tf.get_session_handle(c)
|
||||
h = sess.run(h)
|
||||
|
||||
p, a = tf.get_session_tensor(tf.float32)
|
||||
b = tf.mul(a, 10)
|
||||
c = sess.run(b, feed_dict={p: h.handle})
|
||||
```
|
||||
|
||||
##### Args:
|
||||
|
||||
|
||||
* <b>`data`</b>: A tensor to be stored in the session.
|
||||
* <b>`name`</b>: Optional name prefix for the return tensor.
|
||||
|
||||
##### Returns:
|
||||
|
||||
A scalar string tensor representing a unique handle for `data`.
|
||||
|
||||
##### Raises:
|
||||
|
||||
|
||||
* <b>`TypeError`</b>: if `data` is not a Tensor.
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.get_session_tensor(dtype, name=None)` {#get_session_tensor}
|
||||
|
||||
Get the tensor of type `dtype` by feeding a tensor handle.
|
||||
|
||||
This is EXPERIMENTAL and subject to change.
|
||||
|
||||
Get the value of the tensor from a tensor handle. The tensor
|
||||
is produced in a previous run() and stored in the state of the
|
||||
session.
|
||||
|
||||
##### Args:
|
||||
|
||||
|
||||
* <b>`dtype`</b>: The type of the output tensor.
|
||||
* <b>`name`</b>: Optional name prefix for the return tensor.
|
||||
|
||||
##### Returns:
|
||||
|
||||
A pair of tensors. The first is a placeholder for feeding a
|
||||
tensor handle and the second is the tensor in the session state
|
||||
keyed by the tensor handle.
|
||||
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.delete_session_tensor(name=None)` {#delete_session_tensor}
|
||||
|
||||
Delete the tensor by feeding a tensor handle.
|
||||
|
||||
This is EXPERIMENTAL and subject to change.
|
||||
|
||||
Delete the tensor of a given tensor handle. The tensor is produced
|
||||
in a previous run() and stored in the state of the session.
|
||||
|
||||
##### Args:
|
||||
|
||||
|
||||
* <b>`name`</b>: Optional name prefix for the return tensor.
|
||||
|
||||
##### Returns:
|
||||
|
||||
A pair of graph elements. The first is a placeholder for feeding a
|
||||
tensor handle and the second is a deletion operation.
|
||||
|
||||
|
@ -781,7 +781,7 @@ checkpoints per device.
|
||||
|
||||
- - -
|
||||
|
||||
#### `tf.train.Saver.save(sess, save_path, global_step=None, latest_filename=None, meta_graph_suffix='meta')` {#Saver.save}
|
||||
#### `tf.train.Saver.save(sess, save_path, global_step=None, latest_filename=None, meta_graph_suffix='meta', write_meta_graph=True)` {#Saver.save}
|
||||
|
||||
Saves variables.
|
||||
|
||||
@ -807,6 +807,8 @@ path can be passed directly to a call to `restore()`.
|
||||
managed by the saver to keep track of recent checkpoints. Defaults to
|
||||
'checkpoint'.
|
||||
* <b>`meta_graph_suffix`</b>: Suffix for `MetaGraphDef` file. Defaults to 'meta'.
|
||||
* <b>`write_meta_graph`</b>: `Boolean` indicating whether or not to write the meta
|
||||
graph file.
|
||||
|
||||
##### Returns:
|
||||
|
||||
|
96
tensorflow/g3doc/api_docs/python/string_ops.md
Normal file
96
tensorflow/g3doc/api_docs/python/string_ops.md
Normal file
@ -0,0 +1,96 @@
|
||||
<!-- This file is machine generated: DO NOT EDIT! -->
|
||||
|
||||
# Strings
|
||||
|
||||
Note: Functions taking `Tensor` arguments can also take anything accepted by
|
||||
[`tf.convert_to_tensor`](framework.md#convert_to_tensor).
|
||||
|
||||
[TOC]
|
||||
|
||||
## Hashing
|
||||
|
||||
String hashing ops take a string input tensor and map each element to an
|
||||
integer.
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.string_to_hash_bucket(string_tensor, num_buckets, name=None)` {#string_to_hash_bucket}
|
||||
|
||||
Converts each string in the input Tensor to its hash mod by a number of buckets.
|
||||
|
||||
The hash function is deterministic on the content of the string within the
|
||||
process.
|
||||
|
||||
Note that the hash function may change from time to time.
|
||||
|
||||
##### Args:
|
||||
|
||||
|
||||
* <b>`string_tensor`</b>: A `Tensor` of type `string`.
|
||||
* <b>`num_buckets`</b>: An `int` that is `>= 1`. The number of buckets.
|
||||
* <b>`name`</b>: A name for the operation (optional).
|
||||
|
||||
##### Returns:
|
||||
|
||||
A `Tensor` of type `int64`.
|
||||
A Tensor of the same shape as the input `string_tensor`.
|
||||
|
||||
|
||||
|
||||
## Joining
|
||||
|
||||
String joining ops concatenate elements of input string tensors to produce a new
|
||||
string tensor.
|
||||
|
||||
- - -
|
||||
|
||||
### `tf.reduce_join(inputs, reduction_indices, keep_dims=None, separator=None, name=None)` {#reduce_join}
|
||||
|
||||
Joins a string Tensor across the given dimensions.
|
||||
|
||||
Computes the string join across dimensions in the given string Tensor of shape
|
||||
`[d_0, d_1, ..., d_n-1]`. Returns a new Tensor created by joining the input
|
||||
strings with the given separator (default: empty string). Negative indices are
|
||||
counted backwards from the end, with `-1` being equivalent to `n - 1`. Passing
|
||||
an empty `reduction_indices` joins all strings in linear index order and outputs
|
||||
a scalar string.
|
||||
|
||||
|
||||
For example:
|
||||
```
|
||||
# tensor `a` is [["a", "b"], ["c", "d"]]
|
||||
tf.reduce_join(a, 0) ==> ["ac", "bd"]
|
||||
tf.reduce_join(a, 1) ==> ["ab", "cd"]
|
||||
tf.reduce_join(a, -2) = tf.reduce_join(a, 0) ==> ["ac", "bd"]
|
||||
tf.reduce_join(a, -1) = tf.reduce_join(a, 1) ==> ["ab", "cd"]
|
||||
tf.reduce_join(a, 0, keep_dims=True) ==> [["ac", "bd"]]
|
||||
tf.reduce_join(a, 1, keep_dims=True) ==> [["ab"], ["cd"]]
|
||||
tf.reduce_join(a, 0, separator=".") ==> ["a.c", "b.d"]
|
||||
tf.reduce_join(a, [0, 1]) ==> ["acbd"]
|
||||
tf.reduce_join(a, [1, 0]) ==> ["abcd"]
|
||||
tf.reduce_join(a, []) ==> ["abcd"]
|
||||
```
|
||||
|
||||
##### Args:
|
||||
|
||||
|
||||
* <b>`inputs`</b>: A `Tensor` of type `string`.
|
||||
The input to be joined. All reduced indices must have non-zero size.
|
||||
* <b>`reduction_indices`</b>: A `Tensor` of type `int32`.
|
||||
The dimensions to reduce over. Dimensions are reduced in the
|
||||
order specified. If `reduction_indices` has higher rank than `1`, it is
|
||||
flattened. Omitting `reduction_indices` is equivalent to passing
|
||||
`[n-1, n-2, ..., 0]`. Negative indices from `-n` to `-1` are supported.
|
||||
* <b>`keep_dims`</b>: An optional `bool`. Defaults to `False`.
|
||||
If `True`, retain reduced dimensions with length `1`.
|
||||
* <b>`separator`</b>: An optional `string`. Defaults to `""`.
|
||||
The separator to use when joining.
|
||||
* <b>`name`</b>: A name for the operation (optional).
|
||||
|
||||
##### Returns:
|
||||
|
||||
A `Tensor` of type `string`.
|
||||
Has shape equal to that of the input with reduced dimensions removed or
|
||||
set to `1` depending on `keep_dims`.
|
||||
|
||||
|
@ -1558,7 +1558,7 @@ communicate with any other server in the same cluster.
|
||||
Creates a new server with the given definition.
|
||||
|
||||
The `job_name`, `task_index`, and `protocol` arguments are optional, and
|
||||
override any information also provided in `server_or_cluster_def`.
|
||||
override any information provided in `server_or_cluster_def`.
|
||||
|
||||
##### Args:
|
||||
|
||||
@ -1567,13 +1567,15 @@ override any information also provided in `server_or_cluster_def`.
|
||||
`tf.train.ClusterDef` protocol buffer, or a
|
||||
`tf.train.ClusterSpec` object, describing the server to be
|
||||
created and/or the cluster of which it is a member.
|
||||
* <b>`job_name`</b>: (Optional.) If not specified in `server_or_cluster_def`,
|
||||
specifies the name of the job of which this server is a member.
|
||||
* <b>`task_index`</b>: (Optional.) If not specified in `server_or_cluster_def`,
|
||||
specifies the task index of this server in its job.
|
||||
* <b>`protocol`</b>: (Optional.) If not specified in `server_or_cluster_def`,
|
||||
specifies the protocol to be used by this server. Acceptable
|
||||
values include `"grpc"`.
|
||||
* <b>`job_name`</b>: (Optional.) Specifies the name of the job of which the server
|
||||
is a member. Defaults to the value in `server_or_cluster_def`, if
|
||||
specified.
|
||||
* <b>`task_index`</b>: (Optional.) Specifies the task index of the server in its
|
||||
job. Defaults to the value in `server_or_cluster_def`, if specified.
|
||||
Otherwise defaults to 0 if the server's job has only one task.
|
||||
* <b>`protocol`</b>: (Optional.) Specifies the protocol to be used by the server.
|
||||
Acceptable values include `"grpc"`. Defaults to the value in
|
||||
`server_or_cluster_def`, if specified. Otherwise defaults to `"grpc"`.
|
||||
* <b>`start`</b>: (Optional.) Boolean, indicating whether to start the server
|
||||
after creating it. Defaults to `True`.
|
||||
|
||||
@ -2677,7 +2679,7 @@ Returns a list of tasks in the given job.
|
||||
##### Returns:
|
||||
|
||||
A list of strings, corresponding to the network addresses of tasks in
|
||||
the given job.
|
||||
the given job, ordered by task index.
|
||||
|
||||
##### Raises:
|
||||
|
||||
@ -2852,7 +2854,7 @@ The generated
|
||||
[`Summary`](https://www.tensorflow.org/code/tensorflow/core/framework/summary.proto)
|
||||
has one summary value containing a histogram for `values`.
|
||||
|
||||
This op reports an `OutOfRange` error if any value is not finite.
|
||||
This op reports an `InvalidArgument` error if any value is not finite.
|
||||
|
||||
##### Args:
|
||||
|
||||
|
@ -8,7 +8,8 @@ your TensorFlow graph, plot quantitative metrics about the execution of your
|
||||
graph, and show additional data like images that pass through it. When
|
||||
TensorBoard is fully configured, it looks like this:
|
||||
|
||||

|
||||
[](http://tensorflow.org/tensorboard)
|
||||
[*Click try a TensorBoard with data from this tutorial!*](http://tensorflow.org/tensorboard)
|
||||
|
||||
|
||||
## Serializing the data
|
||||
@ -75,56 +76,70 @@ statistics, such as how the weights or accuracy varied during training.
|
||||
The code below is an excerpt; full source is [here](https://www.tensorflow.org/code/tensorflow/examples/tutorials/mnist/mnist_with_summaries.py).
|
||||
|
||||
```python
|
||||
# Create the model
|
||||
x = tf.placeholder(tf.float32, [None, 784], name="x-input")
|
||||
W = tf.Variable(tf.zeros([784,10]), name="weights")
|
||||
b = tf.Variable(tf.zeros([10], name="bias"))
|
||||
def variable_summaries(var, name):
|
||||
with tf.name_scope("summaries"):
|
||||
mean = tf.reduce_mean(var)
|
||||
tf.scalar_summary('mean/' + name, mean)
|
||||
with tf.name_scope('stddev'):
|
||||
stddev = tf.sqrt(tf.reduce_sum(tf.square(var - mean)))
|
||||
tf.scalar_summary('sttdev/' + name, stddev)
|
||||
tf.scalar_summary('max/' + name, tf.reduce_max(var))
|
||||
tf.scalar_summary('min/' + name, tf.reduce_min(var))
|
||||
tf.histogram_summary(name, var)
|
||||
|
||||
# use a name scope to organize nodes in the graph visualizer
|
||||
with tf.name_scope("Wx_b") as scope:
|
||||
y = tf.nn.softmax(tf.matmul(x,W) + b)
|
||||
def nn_layer(input_tensor, input_dim, output_dim, layer_name):
|
||||
"""Reusable code for making a simple neural net layer.
|
||||
|
||||
# Add summary ops to collect data
|
||||
tf.histogram_summary("weights", W)
|
||||
tf.histogram_summary("biases", b)
|
||||
tf.histogram_summary("y", y)
|
||||
It does a matrix multiply, bias add, and then uses relu to nonlinearize.
|
||||
It also sets up name scoping so that the resultant graph is easy to read, and
|
||||
adds a number of summary ops.
|
||||
"""
|
||||
# Adding a name scope ensures logical grouping of the layers in the graph.
|
||||
with tf.name_scope(layer_name):
|
||||
# This Variable will hold the state of the weights for the layer
|
||||
with tf.name_scope("weights"):
|
||||
weights = weight_variable([input_dim, output_dim])
|
||||
variable_summaries(weights, layer_name + '/weights')
|
||||
with tf.name_scope("biases"):
|
||||
biases = bias_variable([output_dim])
|
||||
variable_summaries(biases, layer_name + '/biases')
|
||||
with tf.name_scope('Wx_plus_b'):
|
||||
activations = tf.matmul(input_tensor, weights) + biases
|
||||
tf.histogram_summary(layer_name + '/activations', activations)
|
||||
relu = tf.nn.relu(activations, 'relu')
|
||||
tf.histogram_summary(layer_name + '/activations_relu', relu)
|
||||
return tf.nn.dropout(relu, keep_prob)
|
||||
|
||||
# Define loss and optimizer
|
||||
y_ = tf.placeholder(tf.float32, [None,10], name="y-input")
|
||||
# More name scopes will clean up the graph representation
|
||||
with tf.name_scope("xent") as scope:
|
||||
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
|
||||
tf.scalar_summary("cross entropy", cross_entropy)
|
||||
with tf.name_scope("train") as scope:
|
||||
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
|
||||
layer1 = nn_layer(x, 784, 50, 'layer1')
|
||||
layer2 = nn_layer(layer1, 50, 10, 'layer2')
|
||||
y = tf.nn.softmax(layer2, 'predictions')
|
||||
|
||||
with tf.name_scope("test") as scope:
|
||||
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
|
||||
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
|
||||
tf.scalar_summary("accuracy", accuracy)
|
||||
|
||||
# Merge all the summaries and write them out to /tmp/mnist_logs
|
||||
with tf.name_scope('cross_entropy'):
|
||||
diff = y_ * tf.log(y)
|
||||
with tf.name_scope('total'):
|
||||
cross_entropy = -tf.reduce_sum(diff)
|
||||
with tf.name_scope('normalized'):
|
||||
normalized_cross_entropy = -tf.reduce_mean(diff)
|
||||
tf.scalar_summary('cross entropy', normalized_cross_entropy)
|
||||
|
||||
with tf.name_scope('train'):
|
||||
train_step = tf.train.AdamOptimizer(
|
||||
FLAGS.learning_rate).minimize(cross_entropy)
|
||||
|
||||
with tf.name_scope('accuracy'):
|
||||
with tf.name_scope('correct_prediction'):
|
||||
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
|
||||
with tf.name_scope('accuracy'):
|
||||
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
|
||||
tf.scalar_summary('accuracy', accuracy)
|
||||
|
||||
# Merge all the summaries and write them out to /tmp/mnist_logs (by default)
|
||||
merged = tf.merge_all_summaries()
|
||||
writer = tf.train.SummaryWriter("/tmp/mnist_logs", sess.graph)
|
||||
train_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/train', sess.graph)
|
||||
test_writer = tf.train.SummaryWriter(FLAGS.summaries_dir + '/test')
|
||||
tf.initialize_all_variables().run()
|
||||
|
||||
# Train the model, and feed in test data and record summaries every 10 steps
|
||||
|
||||
for i in range(1000):
|
||||
if i % 10 == 0: # Record summary data, and the accuracy
|
||||
feed = {x: mnist.test.images, y_: mnist.test.labels}
|
||||
result = sess.run([merged, accuracy], feed_dict=feed)
|
||||
summary_str = result[0]
|
||||
acc = result[1]
|
||||
writer.add_summary(summary_str, i)
|
||||
print("Accuracy at step %s: %s" % (i, acc))
|
||||
else:
|
||||
batch_xs, batch_ys = mnist.train.next_batch(100)
|
||||
feed = {x: batch_xs, y_: batch_ys}
|
||||
sess.run(train_step, feed_dict=feed)
|
||||
|
||||
print(accuracy.eval({x: mnist.test.images, y_: mnist.test.labels}))
|
||||
|
||||
```
|
||||
|
||||
You're now all set to visualize this data using TensorBoard.
|
||||
@ -135,7 +150,7 @@ You're now all set to visualize this data using TensorBoard.
|
||||
To run TensorBoard, use the command
|
||||
|
||||
```bash
|
||||
python tensorflow/tensorboard/tensorboard.py --logdir=path/to/log-directory
|
||||
tensorboard --logdir=path/to/log-directory
|
||||
```
|
||||
|
||||
where `logdir` points to the directory where the `SummaryWriter` serialized its
|
||||
@ -144,18 +159,8 @@ serialized data from separate runs, then TensorBoard will visualize the data
|
||||
from all of those runs. Once TensorBoard is running, navigate your web browser
|
||||
to `localhost:6006` to view the TensorBoard.
|
||||
|
||||
If you have pip installed TensorFlow, `tensorboard` is installed into
|
||||
the system path, so you can use the simpler command
|
||||
|
||||
```bash
|
||||
tensorboard --logdir=/path/to/log-directory
|
||||
```
|
||||
|
||||
When looking at TensorBoard, you will see the navigation tabs in the top right
|
||||
corner. Each tab represents a set of serialized data that can be visualized.
|
||||
For any tab you are looking at, if the logs being looked at by TensorBoard do
|
||||
not contain any data relevant to that tab, a message will be displayed
|
||||
indicating how to serialize data that is applicable to that tab.
|
||||
|
||||
For in depth information on how to use the *graph* tab to visualize your graph,
|
||||
see [TensorBoard: Graph Visualization](../../how_tos/graph_viz/index.md).
|
||||
|
@ -575,6 +575,9 @@ tf_gen_op_wrapper_py(
|
||||
"TensorArraySplit",
|
||||
"TensorArrayUnpack",
|
||||
"TensorArrayWrite",
|
||||
"GetSessionHandle",
|
||||
"GetSessionTensor",
|
||||
"DeleteSessionTensor",
|
||||
],
|
||||
require_shape_functions = True,
|
||||
)
|
||||
@ -810,6 +813,7 @@ py_library(
|
||||
"ops/rnn_cell.py",
|
||||
"ops/script_ops.py",
|
||||
"ops/seq2seq.py",
|
||||
"ops/session_ops.py",
|
||||
"ops/sparse_grad.py",
|
||||
"ops/sparse_ops.py",
|
||||
"ops/standard_ops.py",
|
||||
|
@ -106,8 +106,10 @@ from tensorflow.python.ops import histogram_ops
|
||||
from tensorflow.python.ops import io_ops
|
||||
from tensorflow.python.ops import math_ops
|
||||
from tensorflow.python.ops import script_ops
|
||||
from tensorflow.python.ops import session_ops
|
||||
from tensorflow.python.ops import sparse_ops
|
||||
from tensorflow.python.ops import state_ops
|
||||
from tensorflow.python.ops import string_ops
|
||||
|
||||
|
||||
# Don't export modules except for the few we really want
|
||||
@ -120,7 +122,8 @@ _whitelist = set([app, compat, contrib, errors, flags, gfile, image,
|
||||
__all__ = make_all(__name__,
|
||||
[framework_lib, array_ops, client_lib, constant_op,
|
||||
control_flow_ops, functional_ops, histogram_ops, io_ops,
|
||||
math_ops, nn, script_ops, sparse_ops, state_ops, train])
|
||||
math_ops, nn, script_ops, session_ops, sparse_ops,
|
||||
state_ops, string_ops, train])
|
||||
|
||||
# Symbols whitelisted for export without documentation.
|
||||
# TODO(cwhipkey): review these and move to contrib, expose through
|
||||
@ -167,7 +170,6 @@ __all__.extend([
|
||||
'sparse_matmul',
|
||||
'sparse_segment_mean_grad',
|
||||
'sparse_segment_sqrt_n_grad',
|
||||
'string_to_hash_bucket',
|
||||
'unique_with_counts',
|
||||
'user_ops',
|
||||
])
|
||||
|
@ -27,6 +27,7 @@ import numpy as np
|
||||
from tensorflow.python import pywrap_tensorflow as tf_session
|
||||
from tensorflow.python.framework import errors
|
||||
from tensorflow.python.framework import ops
|
||||
from tensorflow.python.ops import session_ops
|
||||
from tensorflow.python.platform import logging
|
||||
from tensorflow.python.util import compat
|
||||
|
||||
@ -99,6 +100,9 @@ class BaseSession(SessionInterface):
|
||||
self._extend_lock = threading.Lock()
|
||||
self._target = target
|
||||
|
||||
self._delete_lock = threading.Lock()
|
||||
self._dead_handles = []
|
||||
|
||||
self._session = None
|
||||
|
||||
opts = tf_session.TF_NewSessionOptions(target=target, config=config)
|
||||
@ -277,6 +281,9 @@ class BaseSession(SessionInterface):
|
||||
the *i*th return value will be a
|
||||
[`SparseTensorValue`](../../api_docs/python/sparse_ops.md#SparseTensorValue)
|
||||
containing the value of that sparse tensor.
|
||||
* If the *i*th element of `fetches` is produced by a `get_tensor_handle` op,
|
||||
the *i*th return value will be a numpy ndarray containing the handle of
|
||||
that tensor.
|
||||
|
||||
The optional `feed_dict` argument allows the caller to override
|
||||
the value of tensors in the graph. Each key in `feed_dict` can be
|
||||
@ -350,17 +357,22 @@ class BaseSession(SessionInterface):
|
||||
list of feeds and fetches that will be used in the subsequent
|
||||
`partial_run` calls.
|
||||
|
||||
The optional `feed_dict` argument allows the caller to override
|
||||
the value of tensors in the graph. See run() for more information.
|
||||
|
||||
Below is a simple example:
|
||||
|
||||
a = array_ops.placeholder(dtypes.float32, shape=[])
|
||||
b = array_ops.placeholder(dtypes.float32, shape=[])
|
||||
c = array_ops.placeholder(dtypes.float32, shape=[])
|
||||
r1 = math_ops.add(a, b)
|
||||
r2 = math_ops.mul(r1, c)
|
||||
```python
|
||||
a = array_ops.placeholder(dtypes.float32, shape=[])
|
||||
b = array_ops.placeholder(dtypes.float32, shape=[])
|
||||
c = array_ops.placeholder(dtypes.float32, shape=[])
|
||||
r1 = math_ops.add(a, b)
|
||||
r2 = math_ops.mul(r1, c)
|
||||
|
||||
h = sess.partial_run_setup([r1, r2], [a, b, c])
|
||||
res = sess.partial_run(h, r1, feed_dict={a: 1, b: 2})
|
||||
res = sess.partial_run(h, r2, feed_dict={c: res})
|
||||
h = sess.partial_run_setup([r1, r2], [a, b, c])
|
||||
res = sess.partial_run(h, r1, feed_dict={a: 1, b: 2})
|
||||
res = sess.partial_run(h, r2, feed_dict={c: res})
|
||||
```
|
||||
|
||||
Args:
|
||||
handle: A handle for a sequence of partial runs.
|
||||
@ -410,7 +422,7 @@ class BaseSession(SessionInterface):
|
||||
'graph before calling run().')
|
||||
|
||||
# Validate and process fetches.
|
||||
unique_fetches, target_list, _ = self._process_fetches(fetches)
|
||||
unique_fetches, target_list, _, _ = self._process_fetches(fetches)
|
||||
|
||||
# Create request.
|
||||
feed_list = []
|
||||
@ -455,6 +467,7 @@ class BaseSession(SessionInterface):
|
||||
fetches = [fetches]
|
||||
|
||||
unique_fetch_targets = set()
|
||||
unique_fetch_handles = {}
|
||||
target_list = []
|
||||
|
||||
fetch_info = []
|
||||
@ -465,10 +478,15 @@ class BaseSession(SessionInterface):
|
||||
try:
|
||||
fetch_t = self.graph.as_graph_element(subfetch, allow_tensor=True,
|
||||
allow_operation=True)
|
||||
fetch_name = compat.as_bytes(fetch_t.name)
|
||||
if isinstance(fetch_t, ops.Operation):
|
||||
target_list.append(compat.as_bytes(fetch_t.name))
|
||||
target_list.append(fetch_name)
|
||||
else:
|
||||
subfetch_names.append(compat.as_bytes(fetch_t.name))
|
||||
subfetch_names.append(fetch_name)
|
||||
# Remember the fetch if it is for a tensor handle.
|
||||
if (isinstance(fetch_t, ops.Tensor) and
|
||||
fetch_t.op.type == 'GetSessionHandle'):
|
||||
unique_fetch_handles[fetch_name] = fetch_t.op.inputs[0].dtype
|
||||
except TypeError as e:
|
||||
raise TypeError('Fetch argument %r of %r has invalid type %r, '
|
||||
'must be a string or Tensor. (%s)'
|
||||
@ -483,7 +501,7 @@ class BaseSession(SessionInterface):
|
||||
fetch_info.append((subfetch_names, fetch_contraction_fn))
|
||||
|
||||
unique_fetch_targets = list(unique_fetch_targets)
|
||||
return unique_fetch_targets, target_list, fetch_info
|
||||
return unique_fetch_targets, target_list, fetch_info, unique_fetch_handles
|
||||
|
||||
def _run(self, handle, fetches, feed_dict, options, run_metadata):
|
||||
"""Perform either run or partial_run, depending the exitence of `handle`."""
|
||||
@ -502,10 +520,15 @@ class BaseSession(SessionInterface):
|
||||
'graph before calling run().')
|
||||
|
||||
# Validate and process fetches.
|
||||
unique_fetches, target_list, fetch_info = self._process_fetches(fetches)
|
||||
processed_fetches = self._process_fetches(fetches)
|
||||
unique_fetches = processed_fetches[0]
|
||||
target_list = processed_fetches[1]
|
||||
fetch_info = processed_fetches[2]
|
||||
unique_handles = processed_fetches[3]
|
||||
|
||||
# Create request.
|
||||
feed_dict_string = {}
|
||||
feed_map = {}
|
||||
|
||||
# Validate and process feed_dict.
|
||||
if feed_dict:
|
||||
@ -522,7 +545,6 @@ class BaseSession(SessionInterface):
|
||||
raise TypeError('The value of a feed cannot be a tf.Tensor object. '
|
||||
'Acceptable feed values include Python scalars, '
|
||||
'strings, lists, or numpy ndarrays.')
|
||||
|
||||
np_val = np.array(subfeed_val, dtype=subfeed_t.dtype.as_numpy_dtype)
|
||||
if not subfeed_t.get_shape().is_compatible_with(np_val.shape):
|
||||
raise ValueError(
|
||||
@ -531,17 +553,31 @@ class BaseSession(SessionInterface):
|
||||
% (np_val.shape, subfeed_t.name, str(subfeed_t.get_shape())))
|
||||
if not self.graph.is_feedable(subfeed_t):
|
||||
raise ValueError('Tensor %s may not be fed.' % subfeed_t)
|
||||
feed_dict_string[compat.as_bytes(subfeed_t.name)] = np_val
|
||||
subfeed_name = compat.as_bytes(subfeed_t.name)
|
||||
feed_dict_string[subfeed_name] = np_val
|
||||
feed_map[subfeed_name] = (subfeed_t, subfeed_val)
|
||||
|
||||
# Run request and get response.
|
||||
results = self._do_run(handle, target_list, unique_fetches,
|
||||
feed_dict_string, options, run_metadata)
|
||||
movers = self._update_with_movers(feed_dict_string, feed_map)
|
||||
try:
|
||||
results = self._do_run(handle, target_list, unique_fetches,
|
||||
feed_dict_string, options, run_metadata)
|
||||
finally:
|
||||
# The movers are no longer used. Delete them.
|
||||
for handle in movers:
|
||||
self._register_dead_handle(handle)
|
||||
|
||||
# User may have fetched the same tensor multiple times, but we
|
||||
# only fetch them from the runtime once. Furthermore, they may
|
||||
# be wrapped as a tuple of tensors. Here we map the results back
|
||||
# to what the client asked for.
|
||||
fetched_results = dict(zip(unique_fetches, results))
|
||||
# TODO(yuanbyu): Use the contraction_fn in _REGISTERED_EXPANSIONS.
|
||||
fetched_results = {}
|
||||
for fetch, result in zip(unique_fetches, results):
|
||||
dtype = unique_handles.get(fetch)
|
||||
if dtype:
|
||||
result = session_ops.TensorHandle(result, dtype, self)
|
||||
fetched_results[fetch] = result
|
||||
ret = []
|
||||
for fetch_names, fetch_contraction_fn in fetch_info:
|
||||
if fetch_names:
|
||||
@ -642,6 +678,55 @@ class BaseSession(SessionInterface):
|
||||
|
||||
self._current_version = self._graph.version
|
||||
|
||||
# The threshold to run garbage collection to delete dead tensors.
|
||||
_DEAD_HANDLES_THRESHOLD = 10
|
||||
|
||||
def _register_dead_handle(self, handle):
|
||||
# Register a dead handle in the session. Delete the dead tensors when
|
||||
# the number of dead tensors exceeds certain threshold.
|
||||
tensors_to_delete = None
|
||||
with self._delete_lock:
|
||||
self._dead_handles.append(handle)
|
||||
if len(self._dead_handles) == BaseSession._DEAD_HANDLES_THRESHOLD:
|
||||
tensors_to_delete = self._dead_handles
|
||||
self._dead_handles = []
|
||||
# Delete the dead tensors.
|
||||
# TODO(yuanbyu): For now we use a sequence of runs to minimize the graph
|
||||
# size and the overhead of graph construction/partitioning.
|
||||
if tensors_to_delete:
|
||||
for tensor_handle in tensors_to_delete:
|
||||
feeds = {}
|
||||
fetches = []
|
||||
holder, deleter = session_ops._get_handle_deleter(self.graph,
|
||||
tensor_handle)
|
||||
feeds[holder] = tensor_handle
|
||||
fetches.append(deleter)
|
||||
self.run(fetches, feed_dict=feeds)
|
||||
|
||||
def _update_with_movers(self, feed_dict, feed_map):
|
||||
# If a tensor handle that is fed to a device incompatible placeholder,
|
||||
# we move the tensor to the right device, generate a new tensor handle,
|
||||
# and update `feed_dict` to use the new handle.
|
||||
handle_movers = []
|
||||
for feed_name, val in feed_map.items():
|
||||
mover = session_ops._get_handle_mover(self.graph, *val)
|
||||
if mover:
|
||||
handle_movers.append((feed_name, val[1], mover))
|
||||
# Transfer a tensor to the right device if needed.
|
||||
if not handle_movers:
|
||||
return []
|
||||
else:
|
||||
feeds = {}
|
||||
fetches = []
|
||||
for _, handle, mover in handle_movers:
|
||||
feeds[mover[0]] = handle
|
||||
fetches.append(mover[1])
|
||||
handles = self.run(fetches, feed_dict=feeds)
|
||||
for handle_mover, handle in zip(handle_movers, handles):
|
||||
np_val = np.array(handle.handle, dtype=np.object)
|
||||
feed_dict[handle_mover[0]] = np_val
|
||||
return handles
|
||||
|
||||
|
||||
class Session(BaseSession):
|
||||
"""A class for running TensorFlow operations.
|
||||
|
@ -99,11 +99,12 @@ class Index(Document):
|
||||
print("", file=f)
|
||||
|
||||
|
||||
def collect_members(module_to_name):
|
||||
def collect_members(module_to_name, exclude=()):
|
||||
"""Collect all symbols from a list of modules.
|
||||
|
||||
Args:
|
||||
module_to_name: Dictionary mapping modules to short names.
|
||||
exclude: Set of fully qualified names to exclude.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping name to (fullname, member) pairs.
|
||||
@ -116,6 +117,8 @@ def collect_members(module_to_name):
|
||||
not _always_drop_symbol_re.match(name) and
|
||||
(all_names is None or name in all_names)):
|
||||
fullname = '%s.%s' % (module_name, name)
|
||||
if fullname in exclude:
|
||||
continue
|
||||
if name in members:
|
||||
other_fullname, other_member = members[name]
|
||||
if member is not other_member:
|
||||
|
@ -328,7 +328,7 @@ class AbortedError(OpError):
|
||||
|
||||
|
||||
class OutOfRangeError(OpError):
|
||||
"""Raised when an operation executed past the valid range.
|
||||
"""Raised when an operation iterates past the valid input range.
|
||||
|
||||
This exception is raised in "end-of-file" conditions, such as when a
|
||||
[`queue.dequeue()`](../../api_docs/python/io_ops.md#QueueBase.dequeue)
|
||||
|
@ -81,9 +81,11 @@ def all_libraries(module_to_name, members, documented):
|
||||
exclude_symbols=["sparse_matmul", "arg_min", "arg_max",
|
||||
"lin_space", "sparse_segment_mean_grad"],
|
||||
prefix=PREFIX_TEXT),
|
||||
library("string_ops", "Strings", prefix=PREFIX_TEXT),
|
||||
library("histogram_ops", "Histograms"),
|
||||
library("control_flow_ops", "Control Flow", prefix=PREFIX_TEXT),
|
||||
library("functional_ops", "Higher Order Functions", prefix=PREFIX_TEXT),
|
||||
library("session_ops", "Tensor Handle Operations", prefix=PREFIX_TEXT),
|
||||
library("image", "Images", tf.image, exclude_symbols=["ResizeMethod"],
|
||||
prefix=PREFIX_TEXT),
|
||||
library("sparse_ops", "Sparse Tensors",
|
||||
|
@ -1871,6 +1871,14 @@ class Graph(object):
|
||||
self._colocation_stack = []
|
||||
# Set of tensors that are dangerous to feed!
|
||||
self._unfeedable_tensors = set()
|
||||
# A map of tensor handle placeholder to tensor dtype.
|
||||
self._handle_feeders = {}
|
||||
# A map from tensor handle to its read op.
|
||||
self._handle_readers = {}
|
||||
# A map from tensor handle to its move op.
|
||||
self._handle_movers = {}
|
||||
# A map from tensor handle to its delete op.
|
||||
self._handle_deleters = {}
|
||||
|
||||
def _check_not_finalized(self):
|
||||
"""Check if the graph is finalized.
|
||||
|
@ -36,6 +36,10 @@ class Dimension(object):
|
||||
def __repr__(self):
|
||||
return "Dimension(%s)" % repr(self._value)
|
||||
|
||||
def __str__(self):
|
||||
value = self._value
|
||||
return "?" if value is None else str(value)
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Returns true if `other` has the same known value as this Dimension."""
|
||||
other = as_dimension(other)
|
||||
@ -429,17 +433,15 @@ class TensorShape(object):
|
||||
self._dims = [as_dimension(d) for d in dims_iter]
|
||||
|
||||
def __repr__(self):
|
||||
return "TensorShape(%s)" % self._dims
|
||||
return "TensorShape(%r)" % self._dims
|
||||
|
||||
def __str__(self):
|
||||
if self.ndims is None:
|
||||
return "<unknown>"
|
||||
elif self.ndims == 1:
|
||||
length = self._dims[0].value
|
||||
return "(%s,)" % (str(length) if length is not None else "?")
|
||||
return "(%s,)" % self._dims[0]
|
||||
else:
|
||||
return "(%s)" % ", ".join(str(d.value) if d.value is not None else "?"
|
||||
for d in self._dims)
|
||||
return "(%s)" % ", ".join(str(d) for d in self._dims)
|
||||
|
||||
@property
|
||||
def dims(self):
|
||||
@ -541,11 +543,15 @@ class TensorShape(object):
|
||||
if self._dims is None:
|
||||
return other
|
||||
else:
|
||||
self.assert_same_rank(other)
|
||||
new_dims = []
|
||||
for i, dim in enumerate(self._dims):
|
||||
new_dims.append(dim.merge_with(other[i]))
|
||||
return TensorShape(new_dims)
|
||||
try:
|
||||
self.assert_same_rank(other)
|
||||
new_dims = []
|
||||
for i, dim in enumerate(self._dims):
|
||||
new_dims.append(dim.merge_with(other[i]))
|
||||
return TensorShape(new_dims)
|
||||
except ValueError:
|
||||
raise ValueError("Shapes %s and %s are not compatible" %
|
||||
(self, other))
|
||||
|
||||
def concatenate(self, other):
|
||||
"""Returns the concatenation of the dimension in `self` and `other`.
|
||||
|
@ -143,6 +143,14 @@ class DimensionTest(test_util.TensorFlowTestCase):
|
||||
self.assertIs(None,
|
||||
tensor_shape.Dimension(None) != tensor_shape.Dimension(None))
|
||||
|
||||
def testRepr(self):
|
||||
self.assertEqual(repr(tensor_shape.Dimension(7)), "Dimension(7)")
|
||||
self.assertEqual(repr(tensor_shape.Dimension(None)), "Dimension(None)")
|
||||
|
||||
def testStr(self):
|
||||
self.assertEqual(str(tensor_shape.Dimension(7)), "7")
|
||||
self.assertEqual(str(tensor_shape.Dimension(None)), "?")
|
||||
|
||||
|
||||
class ShapeTest(test_util.TensorFlowTestCase):
|
||||
|
||||
|
@ -103,6 +103,19 @@ class BenchmarkTest(tf.test.TestCase):
|
||||
self.assertTrue(_ran_somebenchmark_2[0])
|
||||
self.assertFalse(_ran_somebenchmark_but_shouldnt[0])
|
||||
|
||||
_ran_somebenchmark_1[0] = False
|
||||
_ran_somebenchmark_2[0] = False
|
||||
_ran_somebenchmark_but_shouldnt[0] = False
|
||||
|
||||
# Test running a specific method of SomeRandomBenchmark
|
||||
if benchmark.TEST_REPORTER_TEST_ENV in os.environ:
|
||||
del os.environ[benchmark.TEST_REPORTER_TEST_ENV]
|
||||
benchmark._run_benchmarks("SomeRandom.*1$")
|
||||
|
||||
self.assertTrue(_ran_somebenchmark_1[0])
|
||||
self.assertFalse(_ran_somebenchmark_2[0])
|
||||
self.assertFalse(_ran_somebenchmark_but_shouldnt[0])
|
||||
|
||||
def testReportingBenchmark(self):
|
||||
tempdir = tf.test.get_temp_dir()
|
||||
try:
|
||||
|
@ -59,7 +59,7 @@ def isum(s):
|
||||
i = tf.constant(0, name="i")
|
||||
c = lambda i, s: tf.less(i, 10)
|
||||
b = lambda i, s: [tf.add(i, 1), tf.add(i, s)]
|
||||
_, r_s = control_flow_ops.While(c, b, [i, s])
|
||||
_, r_s = tf.while_loop(c, b, [i, s])
|
||||
return r_s
|
||||
|
||||
|
||||
@ -467,7 +467,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
n = tf.constant(0)
|
||||
c = lambda x: tf.less(x, 10000)
|
||||
b = lambda x: tf.add(x, 1)
|
||||
r = control_flow_ops.While(c, b, [n], parallel_iterations=20)
|
||||
r = tf.while_loop(c, b, [n], parallel_iterations=20)
|
||||
self.assertEqual(10000, r.eval())
|
||||
|
||||
def testWhileWithRefs_1(self):
|
||||
@ -482,7 +482,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
self.assertEqual(x.dtype, tf.int32_ref)
|
||||
return (i+1, gen_array_ops._ref_identity(x))
|
||||
|
||||
r = control_flow_ops.While(c, b, [i, x], parallel_iterations=5)
|
||||
r = tf.while_loop(c, b, [i, x], parallel_iterations=5)
|
||||
|
||||
tf.initialize_all_variables().run()
|
||||
|
||||
@ -517,7 +517,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
c = tf.convert_to_tensor(0)
|
||||
o = tf.convert_to_tensor(0)
|
||||
d = tf.convert_to_tensor(100)
|
||||
r = control_flow_ops.While(
|
||||
r = tf.while_loop(
|
||||
lambda i, m, c, o: tf.less(i, d), compute, [i, m, c, o])
|
||||
result = r[3].eval()
|
||||
self.assertTrue(check_op_order(i.graph))
|
||||
@ -539,7 +539,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
o = tf.convert_to_tensor(0)
|
||||
x = tf.convert_to_tensor([1, 2, 3, 4, 5, 6])
|
||||
s = tf.size(x)
|
||||
r = control_flow_ops.While(
|
||||
r = tf.while_loop(
|
||||
lambda i, m, c, o: tf.less(i, s), compute, [i, m, c, o])
|
||||
result = r[3].eval()
|
||||
self.assertTrue(check_op_order(i.graph))
|
||||
@ -559,7 +559,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
o = tf.convert_to_tensor([0])
|
||||
x = tf.convert_to_tensor([1, 2, 3, 4, 5, 6])
|
||||
s = tf.size(x)
|
||||
r = control_flow_ops.While(
|
||||
r = tf.while_loop(
|
||||
lambda i, c, o: tf.less(i, s), compute, [i, c, o])
|
||||
result = r[2].eval()
|
||||
self.assertTrue(check_op_order(i.graph))
|
||||
@ -570,7 +570,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
n = tf.constant(1.0)
|
||||
c = lambda x: tf.less(x, 10.0)
|
||||
b = lambda x: tf.add(x, 1.0)
|
||||
r = control_flow_ops.While(c, b, [n])
|
||||
r = tf.while_loop(c, b, [n])
|
||||
self.assertAllClose(10.0, r.eval())
|
||||
|
||||
def testWhile_Gpu_1(self):
|
||||
@ -584,7 +584,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
def b(x):
|
||||
with tf.device("/cpu:0"):
|
||||
return tf.add(x, 1.0)
|
||||
r = control_flow_ops.While(c, b, [n])
|
||||
r = tf.while_loop(c, b, [n])
|
||||
self.assertAllClose(10.0, r.eval())
|
||||
|
||||
def testWhile_Gpu_2(self):
|
||||
@ -601,11 +601,11 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
with tf.device("/cpu:0"):
|
||||
s1 = tf.add(i, s)
|
||||
return i1, s1
|
||||
_, r_s = control_flow_ops.While(c, b, [n, s])
|
||||
_, r_s = tf.while_loop(c, b, [n, s])
|
||||
return r_s
|
||||
c = lambda x: tf.less(x, 200)
|
||||
b = lambda x: tf.add(x, cpu_sum(n))
|
||||
r = control_flow_ops.While(c, b, [n])
|
||||
r = tf.while_loop(c, b, [n])
|
||||
self.assertEqual(225, r.eval())
|
||||
|
||||
def testNestedWhile_1(self):
|
||||
@ -624,10 +624,8 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
r_ = tf.constant(12)
|
||||
return [n_, r_]
|
||||
|
||||
res = control_flow_ops.While(condition,
|
||||
body,
|
||||
[n, r],
|
||||
parallel_iterations=1)
|
||||
res = tf.while_loop(condition, body, [n, r],
|
||||
parallel_iterations=1)
|
||||
self.assertAllEqual(12, res[1].eval())
|
||||
|
||||
def testWhileWithControl_2(self):
|
||||
@ -640,7 +638,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
r_ = tf.constant(12)
|
||||
return [r_]
|
||||
|
||||
res = control_flow_ops.While(condition, body, [r], parallel_iterations=1)
|
||||
res = tf.while_loop(condition, body, [r], parallel_iterations=1)
|
||||
self.assertAllEqual(12, res.eval())
|
||||
|
||||
def testCondWhile_1(self):
|
||||
@ -649,7 +647,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
c = lambda x: tf.less(x, 10)
|
||||
b = lambda x: tf.add(x, 1)
|
||||
r = tf.cond(tf.less(0, 1),
|
||||
lambda: control_flow_ops.While(c, b, [n]),
|
||||
lambda: tf.while_loop(c, b, [n]),
|
||||
lambda: n)
|
||||
self.assertAllEqual(10, r.eval())
|
||||
|
||||
@ -659,7 +657,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
c = lambda x: tf.less(x, 10)
|
||||
b = lambda x: tf.add(x, 1)
|
||||
r = tf.cond(tf.less(1, 0), lambda: tf.add(n, 1),
|
||||
lambda: control_flow_ops.While(c, b, [n]))
|
||||
lambda: tf.while_loop(c, b, [n]))
|
||||
self.assertAllEqual(10, r.eval())
|
||||
|
||||
def testWhileCond_1(self):
|
||||
@ -673,7 +671,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
b = lambda x: tf.cond(
|
||||
tf.constant(True), lambda: tf.add(x, one), lambda: tf.sub(x, one))
|
||||
# pylint: enable=undefined-variable
|
||||
r = control_flow_ops.While(c, b, [i])
|
||||
r = tf.while_loop(c, b, [i])
|
||||
self.assertAllEqual(10, r.eval())
|
||||
|
||||
def testWhileCond_2(self):
|
||||
@ -681,7 +679,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
n = tf.convert_to_tensor(0, name="n")
|
||||
c = lambda x: tf.less(x, 10)
|
||||
b = lambda x: tf.cond(tf.constant(True), lambda: tf.add(x, 1), lambda: n)
|
||||
r = control_flow_ops.While(c, b, [n])
|
||||
r = tf.while_loop(c, b, [n])
|
||||
self.assertAllEqual(10, r.eval())
|
||||
|
||||
def testWhileCond_3(self):
|
||||
@ -693,7 +691,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
b = lambda x: tf.cond(tf.less(0, 1), lambda: tf.add(x, 1),
|
||||
lambda: tf.sub(x, 1))
|
||||
# pylint: enable=undefined-variable
|
||||
r = control_flow_ops.While(c, b, [n])
|
||||
r = tf.while_loop(c, b, [n])
|
||||
self.assertAllEqual(10, r.eval())
|
||||
|
||||
# NOTE: It is ok to have parallel_iterations > 1
|
||||
@ -712,10 +710,8 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
nj = control_flow_ops.with_dependencies([op], nj)
|
||||
return [nj]
|
||||
|
||||
r = control_flow_ops.While(loop_iterator,
|
||||
loop_body,
|
||||
[n],
|
||||
parallel_iterations=1)
|
||||
r = tf.while_loop(loop_iterator, loop_body, [n],
|
||||
parallel_iterations=1)
|
||||
self.assertTrue(check_op_order(n.graph))
|
||||
tf.initialize_all_variables().run()
|
||||
self.assertEqual(3, r.eval())
|
||||
@ -739,10 +735,8 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
nj = control_flow_ops.with_dependencies([op], nj)
|
||||
return [nj]
|
||||
|
||||
r = control_flow_ops.While(loop_iterator,
|
||||
loop_body,
|
||||
[n],
|
||||
parallel_iterations=1)
|
||||
r = tf.while_loop(loop_iterator, loop_body, [n],
|
||||
parallel_iterations=1)
|
||||
self.assertTrue(check_op_order(n.graph))
|
||||
tf.initialize_all_variables().run()
|
||||
self.assertEqual(3, r.eval())
|
||||
@ -764,10 +758,9 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
nj = tf.add(j, 1)
|
||||
return [nj, ns]
|
||||
|
||||
r = control_flow_ops.While(loop_iterator,
|
||||
loop_body,
|
||||
[n, tf.identity(select)],
|
||||
parallel_iterations=1)
|
||||
r = tf.while_loop(loop_iterator, loop_body,
|
||||
[n, tf.identity(select)],
|
||||
parallel_iterations=1)
|
||||
tf.initialize_all_variables().run()
|
||||
result = r[1].eval()
|
||||
self.assertTrue(check_op_order(n.graph))
|
||||
@ -792,8 +785,8 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
ni = tf.add(i, 1, name="i_add")
|
||||
return ni
|
||||
|
||||
lpa = control_flow_ops.While(pred, loop_body, [c],
|
||||
parallel_iterations=1)
|
||||
lpa = tf.while_loop(pred, loop_body, [c],
|
||||
parallel_iterations=1)
|
||||
|
||||
self.assertEqual(0, var_b.eval())
|
||||
lpa.eval() # Run the loop
|
||||
@ -819,7 +812,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
inc_b = tf.identity(var_b)
|
||||
return inc_b
|
||||
|
||||
lpa = control_flow_ops.While(pred, loop_body, [var_b], 1, name="loop")
|
||||
lpa = tf.while_loop(pred, loop_body, [var_b], 1, name="loop")
|
||||
|
||||
self.assertEqual(0, var_b.eval())
|
||||
lpa.eval() # Run the loop
|
||||
@ -848,7 +841,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
ni = tf.add(i, 1, name="i_add")
|
||||
return ni
|
||||
|
||||
lpa = control_flow_ops.While(pred, loop_body, [c], 1, name="loop")
|
||||
lpa = tf.while_loop(pred, loop_body, [c], 1, name="loop")
|
||||
|
||||
self.assertEqual(0, var_b.eval())
|
||||
lpa.eval() # Run the loop
|
||||
@ -868,7 +861,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
ni = control_flow_ops.with_dependencies([q.enqueue((i,))], ni)
|
||||
return ni
|
||||
|
||||
r = control_flow_ops.While(c, b, [i], parallel_iterations=1)
|
||||
r = tf.while_loop(c, b, [i], parallel_iterations=1)
|
||||
self.assertEqual([10], r.eval())
|
||||
for i in xrange(10):
|
||||
self.assertEqual([i], q.dequeue().eval())
|
||||
@ -885,7 +878,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
ni = control_flow_ops.with_dependencies(
|
||||
[gen_data_flow_ops._stack_push(s, i)], ni)
|
||||
return ni
|
||||
r = control_flow_ops.While(c, b, [i], parallel_iterations=1)
|
||||
r = tf.while_loop(c, b, [i], parallel_iterations=1)
|
||||
|
||||
x = tf.constant(0)
|
||||
def c1(i, _):
|
||||
@ -894,7 +887,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
ni = tf.sub(i, 1)
|
||||
nx = x + gen_data_flow_ops._stack_pop(s, tf.int32)
|
||||
return [ni, nx]
|
||||
_, rx = control_flow_ops.While(c1, b1, [r, x], parallel_iterations=1)
|
||||
_, rx = tf.while_loop(c1, b1, [r, x], parallel_iterations=1)
|
||||
self.assertEqual(45, rx.eval())
|
||||
|
||||
def testWhileGrad_Square(self):
|
||||
@ -902,7 +895,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
v = tf.constant(2.0, name="v")
|
||||
c = lambda v: tf.less(v, 100.0)
|
||||
b = tf.square
|
||||
r = control_flow_ops.While(c, b, [v], parallel_iterations=1)
|
||||
r = tf.while_loop(c, b, [v], parallel_iterations=1)
|
||||
r = control_flow_ops.cond(tf.less(1, 2), lambda: r, lambda: v)
|
||||
|
||||
r = tf.gradients(r, v)[0]
|
||||
@ -915,7 +908,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
n = tf.constant(0, name="n")
|
||||
c = lambda i, v: tf.less(i, 5)
|
||||
b = lambda i, v: [i + 1, tf.mul(x, v)]
|
||||
r = control_flow_ops.While(c, b, [n, v], parallel_iterations=1)
|
||||
r = tf.while_loop(c, b, [n, v], parallel_iterations=1)
|
||||
|
||||
r = tf.gradients(r[1], x)[0]
|
||||
self.assertEqual(r.get_shape(), tensor_shape.unknown_shape())
|
||||
@ -926,7 +919,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
v = tf.constant(2.0, name="v")
|
||||
c = lambda v: tf.less(v, 100.0)
|
||||
b = tf.square
|
||||
r = control_flow_ops.While(c, b, [v], parallel_iterations=1)
|
||||
r = tf.while_loop(c, b, [v], parallel_iterations=1)
|
||||
r = tf.mul(r, r)
|
||||
|
||||
r = tf.gradients(r, v)[0]
|
||||
@ -937,7 +930,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
v = tf.constant(2.0, name="v")
|
||||
c = lambda v: tf.less(v, 100.0)
|
||||
b = tf.square
|
||||
r = control_flow_ops.While(c, b, [v], parallel_iterations=1)
|
||||
r = tf.while_loop(c, b, [v], parallel_iterations=1)
|
||||
r = tf.add(r, r)
|
||||
|
||||
r = tf.gradients(r, v)[0]
|
||||
@ -949,8 +942,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
v = tf.constant(2.0, name="v")
|
||||
c = lambda v: tf.less(v, 100.0)
|
||||
b = lambda v: tf.mul(v, a)
|
||||
r = control_flow_ops.While(c, b, [v],
|
||||
parallel_iterations=p_iters)
|
||||
r = tf.while_loop(c, b, [v], parallel_iterations=p_iters)
|
||||
|
||||
grad_a, grad_v = tf.gradients(r, [a, v])
|
||||
grad_a_val, grad_v_val = sess.run([grad_a, grad_v])
|
||||
@ -969,7 +961,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
v = tf.constant(2.0, name="v")
|
||||
c = lambda v: tf.less(v, 100.0)
|
||||
b = lambda v: tf.mul(v, a)
|
||||
r = control_flow_ops.While(c, b, [v], parallel_iterations=1)
|
||||
r = tf.while_loop(c, b, [v], parallel_iterations=1)
|
||||
|
||||
r = tf.gradients(r, a)
|
||||
tf.initialize_all_variables().run()
|
||||
@ -985,7 +977,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
y1 = tf.add(x, y)
|
||||
x1 = tf.mul(x, y1)
|
||||
return x1, y1
|
||||
rx, ry = control_flow_ops.While(c, b, [x, y], parallel_iterations=1)
|
||||
rx, ry = tf.while_loop(c, b, [x, y], parallel_iterations=1)
|
||||
|
||||
r = tf.gradients([rx, ry], x)
|
||||
self.assertAllClose(304.0, r[0].eval())
|
||||
@ -1006,7 +998,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
x = tf.mul(x, 2.0)
|
||||
i = tf.add(i, 1)
|
||||
return i, x
|
||||
ri, rx = control_flow_ops.While(c, b, [i, x], parallel_iterations=1)
|
||||
ri, rx = tf.while_loop(c, b, [i, x], parallel_iterations=1)
|
||||
|
||||
r = tf.gradients([ri, rx], x)
|
||||
self.assertAllClose(1024.0, r[0].eval())
|
||||
@ -1018,7 +1010,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
v = tf.constant(2.0, name="v")
|
||||
c = lambda v: tf.less(v, 100.0)
|
||||
b = tf.square
|
||||
r = control_flow_ops.While(c, b, [v], back_prop=False)
|
||||
r = tf.while_loop(c, b, [v], back_prop=False)
|
||||
r = tf.add(r, v)
|
||||
r = tf.gradients(r, v)
|
||||
self.assertAllClose(1.0, r[0].eval())
|
||||
@ -1033,8 +1025,8 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
x = tf.mul(x, 2.0)
|
||||
i = tf.add(i, 1)
|
||||
return i, x
|
||||
_, rx = control_flow_ops.While(c, b, [i, x], parallel_iterations=1)
|
||||
_, rx = control_flow_ops.While(c, b, [i, rx], parallel_iterations=1)
|
||||
_, rx = tf.while_loop(c, b, [i, x], parallel_iterations=1)
|
||||
_, rx = tf.while_loop(c, b, [i, rx], parallel_iterations=1)
|
||||
|
||||
r = tf.gradients([rx], x)
|
||||
self.assertAllClose(1024.0, r[0].eval())
|
||||
@ -1049,8 +1041,8 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
x = tf.mul(x, 2.0)
|
||||
i = tf.add(i, 1)
|
||||
return i, x
|
||||
_, r1 = control_flow_ops.While(c, b, [i, x], parallel_iterations=1)
|
||||
_, r2 = control_flow_ops.While(c, b, [i, x], parallel_iterations=1)
|
||||
_, r1 = tf.while_loop(c, b, [i, x], parallel_iterations=1)
|
||||
_, r2 = tf.while_loop(c, b, [i, x], parallel_iterations=1)
|
||||
rx = tf.add(r1, r2)
|
||||
|
||||
r = tf.gradients([rx], x)
|
||||
@ -1062,10 +1054,10 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
def inner_loop(s):
|
||||
c = lambda x: tf.less(x, 4.0)
|
||||
b = lambda x: tf.mul(x, 2.0)
|
||||
return control_flow_ops.While(c, b, [s])
|
||||
return tf.while_loop(c, b, [s])
|
||||
c = lambda x: tf.less(x, 2.0)
|
||||
b = lambda x: tf.mul(inner_loop(x), 2.0)
|
||||
r = control_flow_ops.While(c, b, [v])
|
||||
r = tf.while_loop(c, b, [v])
|
||||
|
||||
r = tf.gradients(r, v)[0]
|
||||
self.assertAllClose(8.0, r.eval())
|
||||
@ -1081,15 +1073,15 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
z = tf.constant(0)
|
||||
c = lambda i, x: tf.less(i, 4)
|
||||
b = lambda i, x: [tf.add(i, 1), tf.mul(x, 2.0)]
|
||||
return control_flow_ops.While(c, b, [z, s])
|
||||
return tf.while_loop(c, b, [z, s])
|
||||
def inner_loop2(s):
|
||||
z = tf.constant(0)
|
||||
c = lambda i, x: tf.less(i, 4)
|
||||
b = lambda i, x: [tf.add(i, 1), tf.mul(x, 2.0)]
|
||||
return control_flow_ops.While(c, b, [z, s])
|
||||
return tf.while_loop(c, b, [z, s])
|
||||
c = lambda x: tf.less(x, 128.0)
|
||||
b = lambda x: inner_loop2(inner_loop1(x)[1])[1]
|
||||
r = control_flow_ops.While(c, b, [v])
|
||||
r = tf.while_loop(c, b, [v])
|
||||
|
||||
r = tf.gradients(r, v)[0]
|
||||
self.assertAllClose(256.0, r.eval())
|
||||
@ -1101,15 +1093,15 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
z = tf.constant(0)
|
||||
c = lambda i, x: tf.less(i, 4)
|
||||
b = lambda i, x: [tf.add(i, 1), tf.mul(x, 2.0)]
|
||||
return control_flow_ops.While(c, b, [z, s])
|
||||
return tf.while_loop(c, b, [z, s])
|
||||
def inner_loop2(s):
|
||||
z = tf.constant(0)
|
||||
c = lambda i, x: tf.less(i, 4)
|
||||
b = lambda i, x: [tf.add(i, 1), tf.mul(x, 2.0)]
|
||||
return control_flow_ops.While(c, b, [z, s])
|
||||
return tf.while_loop(c, b, [z, s])
|
||||
c = lambda x: tf.less(x, 128.0)
|
||||
b = lambda x: tf.mul(inner_loop1(x)[1], inner_loop2(x)[1])
|
||||
r = control_flow_ops.While(c, b, [v])
|
||||
r = tf.while_loop(c, b, [v])
|
||||
|
||||
r = tf.gradients(r, v)[0]
|
||||
self.assertAllClose(512.0, r.eval())
|
||||
@ -1126,7 +1118,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
lambda: tf.square(x),
|
||||
lambda: tf.sub(x, one))
|
||||
# pylint: enable=undefined-variable
|
||||
r = control_flow_ops.While(c, b, [v])
|
||||
r = tf.while_loop(c, b, [v])
|
||||
r = tf.gradients(r, v)[0]
|
||||
self.assertAllClose(1024.0, r.eval())
|
||||
|
||||
@ -1146,7 +1138,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
lambda: tf.square(x),
|
||||
lambda: tf.sub(x, one))
|
||||
# pylint: enable=undefined-variable
|
||||
r = control_flow_ops.While(c, b, [v])
|
||||
r = tf.while_loop(c, b, [v])
|
||||
r = tf.gradients(r, v)[0]
|
||||
r = sess.run(r, feed_dict={v: 2.0})
|
||||
self.assertAllClose(1024.0, r)
|
||||
@ -1165,7 +1157,7 @@ class ControlFlowTest(tf.test.TestCase):
|
||||
return (i+1, gen_array_ops._ref_identity(x))
|
||||
# pylint: enable=protected-access
|
||||
|
||||
r = control_flow_ops.While(c, body, [i, x], parallel_iterations=5)
|
||||
r = tf.while_loop(c, body, [i, x], parallel_iterations=5)
|
||||
|
||||
grad_ys = [tf.Variable(73).ref()]
|
||||
grad = tf.gradients([r[1]], [x], grad_ys=grad_ys)
|
||||
|
@ -17,10 +17,118 @@ from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy
|
||||
import numpy as np
|
||||
import tensorflow as tf
|
||||
|
||||
|
||||
class BatchMatrixDiagTest(tf.test.TestCase):
|
||||
_use_gpu = False
|
||||
|
||||
def testVector(self):
|
||||
with self.test_session(use_gpu=self._use_gpu):
|
||||
v = np.array([1.0, 2.0, 3.0])
|
||||
mat = np.diag(v)
|
||||
v_diag = tf.batch_matrix_diag(v)
|
||||
self.assertEqual((3, 3), v_diag.get_shape())
|
||||
self.assertAllEqual(v_diag.eval(), mat)
|
||||
|
||||
def testBatchVector(self):
|
||||
with self.test_session(use_gpu=self._use_gpu):
|
||||
v_batch = np.array([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
mat_batch = np.array(
|
||||
[[[1.0, 0.0, 0.0],
|
||||
[0.0, 2.0, 0.0],
|
||||
[0.0, 0.0, 3.0]],
|
||||
[[4.0, 0.0, 0.0],
|
||||
[0.0, 5.0, 0.0],
|
||||
[0.0, 0.0, 6.0]]])
|
||||
v_batch_diag = tf.batch_matrix_diag(v_batch)
|
||||
self.assertEqual((2, 3, 3), v_batch_diag.get_shape())
|
||||
self.assertAllEqual(v_batch_diag.eval(), mat_batch)
|
||||
|
||||
def testInvalidShape(self):
|
||||
with self.assertRaisesRegexp(ValueError, "must have rank at least 1"):
|
||||
tf.batch_matrix_diag(0)
|
||||
|
||||
def testInvalidShapeAtEval(self):
|
||||
with self.test_session(use_gpu=self._use_gpu):
|
||||
v = tf.placeholder(dtype=tf.float32)
|
||||
with self.assertRaisesOpError("input must be at least 1-dim"):
|
||||
tf.batch_matrix_diag(v).eval(feed_dict={v: 0.0})
|
||||
|
||||
def testGrad(self):
|
||||
shapes = ((3,), (18, 4), (1, 9, 4, 8,))
|
||||
with self.test_session(use_gpu=self._use_gpu):
|
||||
for shape in shapes:
|
||||
x = tf.constant(np.random.rand(*shape), np.float32)
|
||||
y = tf.batch_matrix_diag(x)
|
||||
error = tf.test.compute_gradient_error(x, x.get_shape().as_list(),
|
||||
y, y.get_shape().as_list())
|
||||
self.assertLess(error, 1e-4)
|
||||
|
||||
|
||||
class BatchMatrixDiagGpuTest(BatchMatrixDiagTest):
|
||||
_use_gpu = True
|
||||
|
||||
|
||||
class BatchMatrixDiagPartTest(tf.test.TestCase):
|
||||
_use_gpu = False
|
||||
|
||||
def testMatrix(self):
|
||||
with self.test_session(use_gpu=self._use_gpu):
|
||||
v = np.array([1.0, 2.0, 3.0])
|
||||
mat = np.diag(v)
|
||||
mat_diag = tf.batch_matrix_diag_part(mat)
|
||||
self.assertEqual((3,), mat_diag.get_shape())
|
||||
self.assertAllEqual(mat_diag.eval(), v)
|
||||
|
||||
def testBatchMatrix(self):
|
||||
with self.test_session(use_gpu=self._use_gpu):
|
||||
v_batch = np.array([[1.0, 2.0, 3.0],
|
||||
[4.0, 5.0, 6.0]])
|
||||
mat_batch = np.array(
|
||||
[[[1.0, 0.0, 0.0],
|
||||
[0.0, 2.0, 0.0],
|
||||
[0.0, 0.0, 3.0]],
|
||||
[[4.0, 0.0, 0.0],
|
||||
[0.0, 5.0, 0.0],
|
||||
[0.0, 0.0, 6.0]]])
|
||||
self.assertEqual(mat_batch.shape, (2, 3, 3))
|
||||
mat_batch_diag = tf.batch_matrix_diag_part(mat_batch)
|
||||
self.assertEqual((2, 3), mat_batch_diag.get_shape())
|
||||
self.assertAllEqual(mat_batch_diag.eval(), v_batch)
|
||||
|
||||
def testInvalidShape(self):
|
||||
with self.assertRaisesRegexp(ValueError, "must have rank at least 2"):
|
||||
tf.batch_matrix_diag_part(0)
|
||||
with self.assertRaisesRegexp(ValueError, r"Dimensions .* not compatible"):
|
||||
tf.batch_matrix_diag_part([[0, 1], [1, 0], [0, 0]])
|
||||
|
||||
def testInvalidShapeAtEval(self):
|
||||
with self.test_session(use_gpu=self._use_gpu):
|
||||
v = tf.placeholder(dtype=tf.float32)
|
||||
with self.assertRaisesOpError("input must be at least 2-dim"):
|
||||
tf.batch_matrix_diag_part(v).eval(feed_dict={v: 0.0})
|
||||
with self.assertRaisesOpError("last two dimensions must be equal"):
|
||||
tf.batch_matrix_diag_part(v).eval(
|
||||
feed_dict={v: [[0, 1], [1, 0], [0, 0]]})
|
||||
|
||||
def testGrad(self):
|
||||
shapes = ((3, 3), (18, 3, 3), (1, 9, 4, 3, 5, 5))
|
||||
with self.test_session(use_gpu=self._use_gpu):
|
||||
for shape in shapes:
|
||||
x = tf.constant(np.random.rand(*shape), dtype=np.float32)
|
||||
y = tf.batch_matrix_diag_part(x)
|
||||
error = tf.test.compute_gradient_error(x, x.get_shape().as_list(),
|
||||
y, y.get_shape().as_list())
|
||||
self.assertLess(error, 1e-4)
|
||||
|
||||
|
||||
class BatchMatrixDiagPartGpuTest(BatchMatrixDiagPartTest):
|
||||
_use_gpu = True
|
||||
|
||||
|
||||
class DiagTest(tf.test.TestCase):
|
||||
|
||||
def diagOp(self, diag, dtype, expected_ans, use_gpu=False):
|
||||
@ -35,56 +143,56 @@ class DiagTest(tf.test.TestCase):
|
||||
self.assertShapeEqual(diag, tf_ans_inv)
|
||||
|
||||
def testEmptyTensor(self):
|
||||
x = numpy.array([])
|
||||
expected_ans = numpy.empty([0, 0])
|
||||
self.diagOp(x, numpy.int32, expected_ans)
|
||||
x = np.array([])
|
||||
expected_ans = np.empty([0, 0])
|
||||
self.diagOp(x, np.int32, expected_ans)
|
||||
|
||||
def testRankOneIntTensor(self):
|
||||
x = numpy.array([1, 2, 3])
|
||||
expected_ans = numpy.array(
|
||||
x = np.array([1, 2, 3])
|
||||
expected_ans = np.array(
|
||||
[[1, 0, 0],
|
||||
[0, 2, 0],
|
||||
[0, 0, 3]])
|
||||
self.diagOp(x, numpy.int32, expected_ans)
|
||||
self.diagOp(x, numpy.int64, expected_ans)
|
||||
self.diagOp(x, np.int32, expected_ans)
|
||||
self.diagOp(x, np.int64, expected_ans)
|
||||
|
||||
def testRankOneFloatTensor(self):
|
||||
x = numpy.array([1.1, 2.2, 3.3])
|
||||
expected_ans = numpy.array(
|
||||
x = np.array([1.1, 2.2, 3.3])
|
||||
expected_ans = np.array(
|
||||
[[1.1, 0, 0],
|
||||
[0, 2.2, 0],
|
||||
[0, 0, 3.3]])
|
||||
self.diagOp(x, numpy.float32, expected_ans)
|
||||
self.diagOp(x, numpy.float64, expected_ans)
|
||||
self.diagOp(x, np.float32, expected_ans)
|
||||
self.diagOp(x, np.float64, expected_ans)
|
||||
|
||||
def testRankTwoIntTensor(self):
|
||||
x = numpy.array([[1, 2, 3], [4, 5, 6]])
|
||||
expected_ans = numpy.array(
|
||||
x = np.array([[1, 2, 3], [4, 5, 6]])
|
||||
expected_ans = np.array(
|
||||
[[[[1, 0, 0], [0, 0, 0]],
|
||||
[[0, 2, 0], [0, 0, 0]],
|
||||
[[0, 0, 3], [0, 0, 0]]],
|
||||
[[[0, 0, 0], [4, 0, 0]],
|
||||
[[0, 0, 0], [0, 5, 0]],
|
||||
[[0, 0, 0], [0, 0, 6]]]])
|
||||
self.diagOp(x, numpy.int32, expected_ans)
|
||||
self.diagOp(x, numpy.int64, expected_ans)
|
||||
self.diagOp(x, np.int32, expected_ans)
|
||||
self.diagOp(x, np.int64, expected_ans)
|
||||
|
||||
def testRankTwoFloatTensor(self):
|
||||
x = numpy.array([[1.1, 2.2, 3.3], [4.4, 5.5, 6.6]])
|
||||
expected_ans = numpy.array(
|
||||
x = np.array([[1.1, 2.2, 3.3], [4.4, 5.5, 6.6]])
|
||||
expected_ans = np.array(
|
||||
[[[[1.1, 0, 0], [0, 0, 0]],
|
||||
[[0, 2.2, 0], [0, 0, 0]],
|
||||
[[0, 0, 3.3], [0, 0, 0]]],
|
||||
[[[0, 0, 0], [4.4, 0, 0]],
|
||||
[[0, 0, 0], [0, 5.5, 0]],
|
||||
[[0, 0, 0], [0, 0, 6.6]]]])
|
||||
self.diagOp(x, numpy.float32, expected_ans)
|
||||
self.diagOp(x, numpy.float64, expected_ans)
|
||||
self.diagOp(x, np.float32, expected_ans)
|
||||
self.diagOp(x, np.float64, expected_ans)
|
||||
|
||||
def testRankThreeFloatTensor(self):
|
||||
x = numpy.array([[[1.1, 2.2], [3.3, 4.4]],
|
||||
[[5.5, 6.6], [7.7, 8.8]]])
|
||||
expected_ans = numpy.array(
|
||||
x = np.array([[[1.1, 2.2], [3.3, 4.4]],
|
||||
[[5.5, 6.6], [7.7, 8.8]]])
|
||||
expected_ans = np.array(
|
||||
[[[[[[1.1, 0], [0, 0]], [[0, 0], [0, 0]]],
|
||||
[[[0, 2.2], [0, 0]], [[0, 0], [0, 0]]]],
|
||||
[[[[0, 0], [3.3, 0]], [[0, 0], [0, 0]]],
|
||||
@ -93,14 +201,14 @@ class DiagTest(tf.test.TestCase):
|
||||
[[[0, 0], [0, 0]], [[0, 6.6], [0, 0]]]],
|
||||
[[[[0, 0], [0, 0]], [[0, 0], [7.7, 0]]],
|
||||
[[[0, 0], [0, 0]], [[0, 0], [0, 8.8]]]]]])
|
||||
self.diagOp(x, numpy.float32, expected_ans)
|
||||
self.diagOp(x, numpy.float64, expected_ans)
|
||||
self.diagOp(x, np.float32, expected_ans)
|
||||
self.diagOp(x, np.float64, expected_ans)
|
||||
|
||||
|
||||
class DiagPartOpTest(tf.test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
numpy.random.seed(0)
|
||||
np.random.seed(0)
|
||||
|
||||
def diagPartOp(self, tensor, dtpe, expected_ans, use_gpu=False):
|
||||
with self.test_session(use_gpu=use_gpu):
|
||||
@ -110,64 +218,64 @@ class DiagPartOpTest(tf.test.TestCase):
|
||||
self.assertShapeEqual(expected_ans, tf_ans_inv)
|
||||
|
||||
def testRankTwoFloatTensor(self):
|
||||
x = numpy.random.rand(3, 3)
|
||||
i = numpy.arange(3)
|
||||
x = np.random.rand(3, 3)
|
||||
i = np.arange(3)
|
||||
expected_ans = x[i, i]
|
||||
self.diagPartOp(x, numpy.float32, expected_ans)
|
||||
self.diagPartOp(x, numpy.float64, expected_ans)
|
||||
self.diagPartOp(x, np.float32, expected_ans)
|
||||
self.diagPartOp(x, np.float64, expected_ans)
|
||||
|
||||
def testRankFourFloatTensor(self):
|
||||
x = numpy.random.rand(2, 3, 2, 3)
|
||||
i = numpy.arange(2)[:, None]
|
||||
j = numpy.arange(3)
|
||||
x = np.random.rand(2, 3, 2, 3)
|
||||
i = np.arange(2)[:, None]
|
||||
j = np.arange(3)
|
||||
expected_ans = x[i, j, i, j]
|
||||
self.diagPartOp(x, numpy.float32, expected_ans)
|
||||
self.diagPartOp(x, numpy.float64, expected_ans)
|
||||
self.diagPartOp(x, np.float32, expected_ans)
|
||||
self.diagPartOp(x, np.float64, expected_ans)
|
||||
|
||||
def testRankSixFloatTensor(self):
|
||||
x = numpy.random.rand(2, 2, 2, 2, 2, 2)
|
||||
i = numpy.arange(2)[:, None, None]
|
||||
j = numpy.arange(2)[:, None]
|
||||
k = numpy.arange(2)
|
||||
x = np.random.rand(2, 2, 2, 2, 2, 2)
|
||||
i = np.arange(2)[:, None, None]
|
||||
j = np.arange(2)[:, None]
|
||||
k = np.arange(2)
|
||||
expected_ans = x[i, j, k, i, j, k]
|
||||
self.diagPartOp(x, numpy.float32, expected_ans)
|
||||
self.diagPartOp(x, numpy.float64, expected_ans)
|
||||
self.diagPartOp(x, np.float32, expected_ans)
|
||||
self.diagPartOp(x, np.float64, expected_ans)
|
||||
|
||||
def testOddRank(self):
|
||||
w = numpy.random.rand(2)
|
||||
x = numpy.random.rand(2, 2, 2)
|
||||
y = numpy.random.rand(2, 2, 2, 2, 2)
|
||||
z = numpy.random.rand(2, 2, 2, 2, 2, 2, 2)
|
||||
self.assertRaises(ValueError, self.diagPartOp, w, numpy.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, x, numpy.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, y, numpy.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, z, numpy.float32, 0)
|
||||
w = np.random.rand(2)
|
||||
x = np.random.rand(2, 2, 2)
|
||||
y = np.random.rand(2, 2, 2, 2, 2)
|
||||
z = np.random.rand(2, 2, 2, 2, 2, 2, 2)
|
||||
self.assertRaises(ValueError, self.diagPartOp, w, np.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, x, np.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, y, np.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, z, np.float32, 0)
|
||||
|
||||
def testUnevenDimensions(self):
|
||||
w = numpy.random.rand(2, 5)
|
||||
x = numpy.random.rand(2, 1, 2, 3)
|
||||
y = numpy.random.rand(2, 1, 2, 1, 2, 5)
|
||||
z = numpy.random.rand(2, 2, 2, 2, 2, 2, 2, 2)
|
||||
self.assertRaises(ValueError, self.diagPartOp, w, numpy.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, x, numpy.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, y, numpy.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, z, numpy.float32, 0)
|
||||
w = np.random.rand(2, 5)
|
||||
x = np.random.rand(2, 1, 2, 3)
|
||||
y = np.random.rand(2, 1, 2, 1, 2, 5)
|
||||
z = np.random.rand(2, 2, 2, 2, 2, 2, 2, 2)
|
||||
self.assertRaises(ValueError, self.diagPartOp, w, np.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, x, np.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, y, np.float32, 0)
|
||||
self.assertRaises(ValueError, self.diagPartOp, z, np.float32, 0)
|
||||
|
||||
|
||||
class DiagGradOpTest(tf.test.TestCase):
|
||||
|
||||
def testDiagGrad(self):
|
||||
numpy.random.seed(0)
|
||||
np.random.seed(0)
|
||||
shapes = ((3,), (3,3), (3,3,3))
|
||||
dtypes = (tf.float32, tf.float64)
|
||||
with self.test_session(use_gpu=False):
|
||||
errors = []
|
||||
for shape in shapes:
|
||||
for dtype in dtypes:
|
||||
x1 = tf.constant(numpy.random.rand(*shape), dtype=dtype)
|
||||
x1 = tf.constant(np.random.rand(*shape), dtype=dtype)
|
||||
y = tf.diag(x1)
|
||||
error = tf.test.compute_gradient_error(x1, x1._shape_as_list(),
|
||||
y, y._shape_as_list())
|
||||
error = tf.test.compute_gradient_error(x1, x1.get_shape().as_list(),
|
||||
y, y.get_shape().as_list())
|
||||
tf.logging.info("error = %f", error)
|
||||
self.assertLess(error, 1e-4)
|
||||
|
||||
@ -175,17 +283,17 @@ class DiagGradOpTest(tf.test.TestCase):
|
||||
class DiagGradPartOpTest(tf.test.TestCase):
|
||||
|
||||
def testDiagPartGrad(self):
|
||||
numpy.random.seed(0)
|
||||
np.random.seed(0)
|
||||
shapes = ((3,3), (3,3,3,3), (3,3,3,3,3,3))
|
||||
dtypes = (tf.float32, tf.float64)
|
||||
with self.test_session(use_gpu=False):
|
||||
errors = []
|
||||
for shape in shapes:
|
||||
for dtype in dtypes:
|
||||
x1 = tf.constant(numpy.random.rand(*shape), dtype=dtype)
|
||||
x1 = tf.constant(np.random.rand(*shape), dtype=dtype)
|
||||
y = tf.diag_part(x1)
|
||||
error = tf.test.compute_gradient_error(x1, x1._shape_as_list(),
|
||||
y, y._shape_as_list())
|
||||
error = tf.test.compute_gradient_error(x1, x1.get_shape().as_list(),
|
||||
y, y.get_shape().as_list())
|
||||
tf.logging.info("error = %f", error)
|
||||
self.assertLess(error, 1e-4)
|
||||
|
||||
|
@ -153,8 +153,7 @@ class MatMulTest(tf.test.TestCase):
|
||||
b = tf.placeholder(tf.float32, [36, 2])
|
||||
c = tf.placeholder(tf.float32, [37])
|
||||
with self.assertRaisesRegexp(
|
||||
ValueError,
|
||||
r"Dimensions Dimension\(37\) and Dimension\(36\) are not compatible"):
|
||||
ValueError, "Dimensions 37 and 36 are not compatible"):
|
||||
tf.matmul(a, b)
|
||||
with self.assertRaisesRegexp(ValueError, "must have rank 2"):
|
||||
tf.matmul(a, c)
|
||||
|
283
tensorflow/python/kernel_tests/reduce_join_op_test.py
Normal file
283
tensorflow/python/kernel_tests/reduce_join_op_test.py
Normal file
@ -0,0 +1,283 @@
|
||||
# Copyright 2016 Google Inc. 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 ReduceJoin op from string_ops."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
import itertools
|
||||
|
||||
import numpy as np
|
||||
from six.moves import xrange # pylint: disable=redefined-builtin
|
||||
import tensorflow as tf
|
||||
|
||||
|
||||
def _input_array(num_dims):
|
||||
"""Creates an ndarray where each element is the binary of its linear index.
|
||||
|
||||
Args:
|
||||
num_dims: The number of dimensions to create.
|
||||
|
||||
Returns:
|
||||
An ndarray of shape [2] * num_dims.
|
||||
"""
|
||||
formatter = "{:0%db}" % num_dims
|
||||
strings = [formatter.format(i) for i in xrange(2 ** num_dims)]
|
||||
return np.array(strings, dtype="S%d" % num_dims).reshape([2] * num_dims)
|
||||
|
||||
|
||||
def _joined_array(num_dims, reduce_dim):
|
||||
"""Creates an ndarray with the result from reduce_join on input_array.
|
||||
|
||||
Args:
|
||||
num_dims: The number of dimensions of the original input array.
|
||||
reduce_dim: The dimension to reduce.
|
||||
|
||||
Returns:
|
||||
An ndarray of shape [2] * (num_dims - 1).
|
||||
"""
|
||||
formatter = "{:0%db}" % (num_dims - 1)
|
||||
result = np.zeros(shape=[2] * (num_dims - 1), dtype="S%d" % (2 * num_dims))
|
||||
flat = result.ravel()
|
||||
for i in xrange(2 ** (num_dims - 1)):
|
||||
dims = formatter.format(i)
|
||||
flat[i] = "".join([(dims[:reduce_dim] + "%d" + dims[reduce_dim:]) % j
|
||||
for j in xrange(2)])
|
||||
return result
|
||||
|
||||
|
||||
class UnicodeTestCase(tf.test.TestCase):
|
||||
"""Test case with Python3-compatible string comparator."""
|
||||
|
||||
def assertAllEqualUnicode(self, truth, actual):
|
||||
self.assertAllEqual(np.array(truth).astype("U"),
|
||||
np.array(actual).astype("U"))
|
||||
|
||||
|
||||
class ReduceJoinTestHelperTest(UnicodeTestCase):
|
||||
"""Tests for helper functions."""
|
||||
|
||||
def testInputArray(self):
|
||||
num_dims = 3
|
||||
truth = ["{:03b}".format(i) for i in xrange(2 ** num_dims)]
|
||||
output_array = _input_array(num_dims).reshape([-1])
|
||||
self.assertAllEqualUnicode(truth, output_array)
|
||||
|
||||
def testJoinedArray(self):
|
||||
num_dims = 3
|
||||
truth_dim_zero = [["000100", "001101"], ["010110", "011111"]]
|
||||
truth_dim_one = [["000010", "001011"], ["100110", "101111"]]
|
||||
truth_dim_two = [["000001", "010011"], ["100101", "110111"]]
|
||||
output_array_dim_zero = _joined_array(num_dims, reduce_dim=0)
|
||||
output_array_dim_one = _joined_array(num_dims, reduce_dim=1)
|
||||
output_array_dim_two = _joined_array(num_dims, reduce_dim=2)
|
||||
self.assertAllEqualUnicode(truth_dim_zero, output_array_dim_zero)
|
||||
self.assertAllEqualUnicode(truth_dim_one, output_array_dim_one)
|
||||
self.assertAllEqualUnicode(truth_dim_two, output_array_dim_two)
|
||||
|
||||
|
||||
class ReduceJoinTest(UnicodeTestCase):
|
||||
|
||||
def _testReduceJoin(self, input_array, truth, reduction_indices,
|
||||
keep_dims=False, separator=""):
|
||||
"""Compares the output of reduce_join to an expected result.
|
||||
|
||||
Args:
|
||||
input_array: The string input to be joined.
|
||||
truth: An array or np.array of the expected result.
|
||||
reduction_indices: The indices to reduce over.
|
||||
keep_dims: Whether or not to retain reduced dimensions.
|
||||
separator: The separator to use for joining.
|
||||
"""
|
||||
with self.test_session():
|
||||
output = tf.reduce_join(inputs=input_array,
|
||||
reduction_indices=reduction_indices,
|
||||
keep_dims=keep_dims,
|
||||
separator=separator)
|
||||
output_array = output.eval()
|
||||
|
||||
self.assertAllEqualUnicode(truth, output_array)
|
||||
|
||||
def _testMultipleReduceJoin(self, input_array, reduction_indices,
|
||||
separator=" "):
|
||||
"""Tests reduce_join for one input and multiple reduction_indices.
|
||||
|
||||
Does so by comparing the output to that from nested reduce_string_joins.
|
||||
The correctness of single-dimension reduce_join is verified by other
|
||||
tests below using _testReduceJoin.
|
||||
|
||||
Args:
|
||||
input_array: The input to test.
|
||||
reduction_indices: The indices to reduce.
|
||||
separator: The separator to use when joining.
|
||||
"""
|
||||
num_dims = len(input_array.shape)
|
||||
truth_red_indices = reduction_indices or list(reversed(xrange(num_dims)))
|
||||
with self.test_session():
|
||||
output = tf.reduce_join(
|
||||
inputs=input_array, reduction_indices=reduction_indices,
|
||||
keep_dims=False, separator=separator)
|
||||
output_keep_dims = tf.reduce_join(
|
||||
inputs=input_array, reduction_indices=reduction_indices,
|
||||
keep_dims=True, separator=separator)
|
||||
|
||||
truth = input_array
|
||||
for index in truth_red_indices:
|
||||
truth = tf.reduce_join(
|
||||
inputs=truth, reduction_indices=index, keep_dims=True,
|
||||
separator=separator)
|
||||
truth_squeezed = tf.squeeze(truth, squeeze_dims=truth_red_indices)
|
||||
output_array = output.eval()
|
||||
output_keep_dims_array = output_keep_dims.eval()
|
||||
truth_array = truth.eval()
|
||||
truth_squeezed_array = truth_squeezed.eval()
|
||||
self.assertAllEqualUnicode(truth_array, output_keep_dims_array)
|
||||
self.assertAllEqualUnicode(truth_squeezed_array, output_array)
|
||||
|
||||
def testRankOne(self):
|
||||
input_array = ["this", "is", "a", "test"]
|
||||
truth = "thisisatest"
|
||||
self._testReduceJoin(input_array, truth, reduction_indices=0)
|
||||
|
||||
def testRankTwo(self):
|
||||
input_array = [["this", "is", "a", "test"],
|
||||
["please", "do", "not", "panic"]]
|
||||
truth_dim_zero = ["thisplease", "isdo", "anot", "testpanic"]
|
||||
truth_dim_one = ["thisisatest", "pleasedonotpanic"]
|
||||
self._testReduceJoin(input_array, truth_dim_zero, reduction_indices=0)
|
||||
self._testReduceJoin(input_array, truth_dim_one, reduction_indices=1)
|
||||
|
||||
def testRankFive(self):
|
||||
input_array = _input_array(num_dims=5)
|
||||
truths = [_joined_array(num_dims=5, reduce_dim=i) for i in xrange(5)]
|
||||
for i in xrange(5):
|
||||
self._testReduceJoin(input_array, truths[i], reduction_indices=i)
|
||||
|
||||
def testNegative(self):
|
||||
input_array = _input_array(num_dims=5)
|
||||
truths = [_joined_array(num_dims=5, reduce_dim=i) for i in xrange(5)]
|
||||
for i in xrange(5):
|
||||
self._testReduceJoin(input_array, truths[i], reduction_indices=i - 5)
|
||||
|
||||
def testSingletonDimension(self):
|
||||
input_arrays = [_input_array(num_dims=5)
|
||||
.reshape([2] * i + [1] + [2] * (5 - i))
|
||||
for i in xrange(6)]
|
||||
truth = _input_array(num_dims=5)
|
||||
for i in xrange(6):
|
||||
self._testReduceJoin(input_arrays[i], truth, reduction_indices=i)
|
||||
|
||||
def testSeparator(self):
|
||||
input_array = [["this", "is", "a", "test"],
|
||||
["please", "do", "not", "panic"]]
|
||||
truth_dim_zero = ["this please", "is do", "a not", "test panic"]
|
||||
truth_dim_one = ["this is a test", "please do not panic"]
|
||||
self._testReduceJoin(input_array, truth_dim_zero, reduction_indices=0,
|
||||
separator=" ")
|
||||
self._testReduceJoin(input_array, truth_dim_one, reduction_indices=1,
|
||||
separator=" ")
|
||||
|
||||
def testUnknownShape(self):
|
||||
input_array = [["a"], ["b"]]
|
||||
truth = ["ab"]
|
||||
with self.test_session():
|
||||
placeholder = tf.placeholder(tf.string, name="placeholder")
|
||||
reduced = tf.reduce_join(placeholder, reduction_indices=0)
|
||||
output_array = reduced.eval(feed_dict={placeholder.name: input_array})
|
||||
self.assertAllEqualUnicode(truth, output_array)
|
||||
|
||||
def testUnknownIndices(self):
|
||||
input_array = [["this", "is", "a", "test"],
|
||||
["please", "do", "not", "panic"]]
|
||||
truth_dim_zero = ["thisplease", "isdo", "anot", "testpanic"]
|
||||
truth_dim_one = ["thisisatest", "pleasedonotpanic"]
|
||||
with self.test_session():
|
||||
placeholder = tf.placeholder(tf.int32, name="placeholder")
|
||||
reduced = tf.reduce_join(input_array, reduction_indices=placeholder)
|
||||
output_array_dim_zero = reduced.eval(feed_dict={placeholder.name: [0]})
|
||||
output_array_dim_one = reduced.eval(feed_dict={placeholder.name: [1]})
|
||||
self.assertAllEqualUnicode(truth_dim_zero, output_array_dim_zero)
|
||||
self.assertAllEqualUnicode(truth_dim_one, output_array_dim_one)
|
||||
|
||||
def testKeepDims(self):
|
||||
input_array = [["this", "is", "a", "test"],
|
||||
["please", "do", "not", "panic"]]
|
||||
truth_dim_zero = [["thisplease", "isdo", "anot", "testpanic"]]
|
||||
truth_dim_one = [["thisisatest"], ["pleasedonotpanic"]]
|
||||
self._testReduceJoin(input_array, truth_dim_zero, reduction_indices=0,
|
||||
keep_dims=True)
|
||||
self._testReduceJoin(input_array, truth_dim_one, reduction_indices=1,
|
||||
keep_dims=True)
|
||||
|
||||
def testMultiIndex(self):
|
||||
num_dims = 3
|
||||
input_array = _input_array(num_dims=num_dims)
|
||||
# Also tests [].
|
||||
for i in xrange(num_dims + 1):
|
||||
for permutation in itertools.permutations(xrange(num_dims), i):
|
||||
self._testMultipleReduceJoin(input_array,
|
||||
reduction_indices=permutation)
|
||||
|
||||
def testInvalidReductionIndices(self):
|
||||
with self.test_session():
|
||||
with self.assertRaisesRegexp(ValueError, "scalar"):
|
||||
tf.reduce_join(inputs="", reduction_indices=0)
|
||||
with self.assertRaisesRegexp(ValueError,
|
||||
"Invalid reduction dimension -3"):
|
||||
tf.reduce_join(inputs=[[""]], reduction_indices=-3)
|
||||
with self.assertRaisesRegexp(ValueError, "Invalid reduction dimension 2"):
|
||||
tf.reduce_join(inputs=[[""]], reduction_indices=2)
|
||||
with self.assertRaisesRegexp(ValueError,
|
||||
"Invalid reduction dimension -3"):
|
||||
tf.reduce_join(inputs=[[""]], reduction_indices=[0, -3])
|
||||
with self.assertRaisesRegexp(ValueError, "Invalid reduction dimension 2"):
|
||||
tf.reduce_join(inputs=[[""]], reduction_indices=[0, 2])
|
||||
with self.assertRaisesRegexp(ValueError, "Duplicate reduction index 0"):
|
||||
tf.reduce_join(inputs=[[""]], reduction_indices=[0, 0])
|
||||
|
||||
def testZeroDims(self):
|
||||
valid_truth_shape = [0]
|
||||
with self.test_session():
|
||||
inputs = np.zeros([0, 1], dtype=str)
|
||||
with self.assertRaisesRegexp(ValueError, "dimension 0 with size 0"):
|
||||
tf.reduce_join(inputs=inputs, reduction_indices=0)
|
||||
valid = tf.reduce_join(inputs=inputs, reduction_indices=1)
|
||||
valid_array_shape = valid.eval().shape
|
||||
self.assertAllEqualUnicode(valid_truth_shape, valid_array_shape)
|
||||
|
||||
def testInvalidArgsUnknownShape(self):
|
||||
with self.test_session():
|
||||
placeholder = tf.placeholder(tf.string, name="placeholder")
|
||||
index_too_high = tf.reduce_join(placeholder, reduction_indices=1)
|
||||
duplicate_index = tf.reduce_join(placeholder, reduction_indices=[-1, 1])
|
||||
with self.assertRaisesOpError("Invalid reduction dimension 1"):
|
||||
index_too_high.eval(feed_dict={placeholder.name: [""]})
|
||||
with self.assertRaisesOpError("Duplicate reduction dimension 1"):
|
||||
duplicate_index.eval(feed_dict={placeholder.name: [[""]]})
|
||||
|
||||
def testInvalidArgsUnknownIndices(self):
|
||||
with self.test_session():
|
||||
placeholder = tf.placeholder(tf.int32, name="placeholder")
|
||||
reduced = tf.reduce_join(["test", "test2"],
|
||||
reduction_indices=placeholder)
|
||||
|
||||
with self.assertRaisesOpError("reduction dimension -2"):
|
||||
reduced.eval(feed_dict={placeholder.name: -2})
|
||||
with self.assertRaisesOpError("reduction dimension 2"):
|
||||
reduced.eval(feed_dict={placeholder.name: 2})
|
||||
|
||||
if __name__ == "__main__":
|
||||
tf.test.main()
|
@ -937,13 +937,14 @@ def graph_creation_static_vs_dynamic_rnn_benchmark(max_time):
|
||||
|
||||
def _create_static_rnn():
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
inputs_list_t = [tf.constant(x) for x in inputs_list]
|
||||
inputs_list_t = [
|
||||
tf.Variable(x, trainable=False).value() for x in inputs_list]
|
||||
ops = _static_vs_dynamic_rnn_benchmark_static(
|
||||
inputs_list_t, sequence_length)
|
||||
|
||||
def _create_dynamic_rnn():
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
inputs_t = tf.constant(inputs)
|
||||
inputs_t = tf.Variable(inputs, trainable=False).value()
|
||||
ops = _static_vs_dynamic_rnn_benchmark_dynamic(
|
||||
inputs_t, sequence_length)
|
||||
|
||||
@ -961,7 +962,7 @@ def _timer(sess, ops):
|
||||
sess.run(ops)
|
||||
|
||||
# Timing run
|
||||
runs = 10
|
||||
runs = 20
|
||||
start = time.time()
|
||||
for _ in range(runs):
|
||||
sess.run(ops)
|
||||
@ -983,13 +984,9 @@ def static_vs_dynamic_rnn_benchmark(batch_size, max_time, num_units, use_gpu):
|
||||
|
||||
# Using rnn()
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
if not use_gpu:
|
||||
with tf.device("/cpu:0"):
|
||||
inputs_list_t = [tf.constant(x) for x in inputs_list]
|
||||
ops = _static_vs_dynamic_rnn_benchmark_static(
|
||||
inputs_list_t, sequence_length)
|
||||
else:
|
||||
inputs_list_t = [tf.constant(x) for x in inputs_list]
|
||||
with tf.device("/cpu:0" if not use_gpu else None):
|
||||
inputs_list_t = [
|
||||
tf.Variable(x, trainable=False).value() for x in inputs_list]
|
||||
ops = _static_vs_dynamic_rnn_benchmark_static(
|
||||
inputs_list_t, sequence_length)
|
||||
tf.initialize_all_variables().run()
|
||||
@ -997,13 +994,8 @@ def static_vs_dynamic_rnn_benchmark(batch_size, max_time, num_units, use_gpu):
|
||||
|
||||
# Using dynamic_rnn()
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
if not use_gpu:
|
||||
with tf.device("/cpu:0"):
|
||||
inputs_t = tf.Variable(inputs)
|
||||
ops = _static_vs_dynamic_rnn_benchmark_dynamic(
|
||||
inputs_t, sequence_length)
|
||||
else:
|
||||
inputs_t = tf.Variable(inputs)
|
||||
with tf.device("/cpu:0" if not use_gpu else None):
|
||||
inputs_t = tf.Variable(inputs, trainable=False).value()
|
||||
ops = _static_vs_dynamic_rnn_benchmark_dynamic(
|
||||
inputs_t, sequence_length)
|
||||
tf.initialize_all_variables().run()
|
||||
@ -1016,6 +1008,59 @@ def static_vs_dynamic_rnn_benchmark(batch_size, max_time, num_units, use_gpu):
|
||||
return delta_static, delta_dynamic
|
||||
|
||||
|
||||
def _half_seq_len_vs_unroll_half_rnn_benchmark(inputs_list_t, sequence_length):
|
||||
(_, input_size) = inputs_list_t[0].get_shape().as_list()
|
||||
initializer = tf.random_uniform_initializer(-0.01, 0.01, seed=127)
|
||||
cell = tf.nn.rnn_cell.LSTMCell(
|
||||
num_units=input_size, input_size=input_size, use_peepholes=True,
|
||||
initializer=initializer)
|
||||
outputs, final_state = tf.nn.rnn(
|
||||
cell, inputs_list_t, sequence_length=sequence_length, dtype=tf.float32)
|
||||
|
||||
trainable_variables = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)
|
||||
gradients = tf.gradients(outputs + [final_state], trainable_variables)
|
||||
|
||||
return tf.group(final_state, *(gradients + outputs))
|
||||
|
||||
|
||||
def half_seq_len_vs_unroll_half_rnn_benchmark(
|
||||
batch_size, max_time, num_units, use_gpu):
|
||||
config = tf.ConfigProto()
|
||||
config.allow_soft_placement = True
|
||||
|
||||
# Set up sequence lengths
|
||||
np.random.seed([127])
|
||||
sequence_length = max_time * np.ones((batch_size,))
|
||||
inputs_list = [
|
||||
np.random.randn(batch_size, num_units).astype(np.float32)
|
||||
for _ in range(max_time)]
|
||||
|
||||
# Halve the sequence length, full static unroll
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
with tf.device("/cpu:0" if not use_gpu else None):
|
||||
inputs_list_t = [
|
||||
tf.Variable(x, trainable=False).value() for x in inputs_list]
|
||||
ops = _half_seq_len_vs_unroll_half_rnn_benchmark(
|
||||
inputs_list_t, sequence_length / 2)
|
||||
tf.initialize_all_variables().run()
|
||||
delta_half_seq_len = _timer(sess, ops)
|
||||
|
||||
# Halve the unroll size, don't use sequence length
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
with tf.device("/cpu:0" if not use_gpu else None):
|
||||
inputs_list_t = [
|
||||
tf.Variable(x, trainable=False).value() for x in inputs_list]
|
||||
ops = _half_seq_len_vs_unroll_half_rnn_benchmark(
|
||||
inputs_list_t[:(max_time // 2)], sequence_length / 2)
|
||||
tf.initialize_all_variables().run()
|
||||
delta_unroll_half = _timer(sess, ops)
|
||||
print("%d \t %d \t\t %d \t %s \t %f \t\t %f \t\t %f" %
|
||||
(batch_size, max_time, num_units, use_gpu, delta_half_seq_len,
|
||||
delta_unroll_half, delta_half_seq_len/delta_unroll_half))
|
||||
|
||||
return delta_half_seq_len, delta_unroll_half
|
||||
|
||||
|
||||
def _dynamic_rnn_swap_memory_benchmark(inputs_t, sequence_length,
|
||||
swap_memory):
|
||||
(unused_0, unused_1, input_size) = inputs_t.get_shape().as_list()
|
||||
@ -1047,7 +1092,7 @@ def dynamic_rnn_swap_memory_benchmark(batch_size, max_time, num_units):
|
||||
|
||||
# No memory swap
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
inputs_t = tf.Variable(inputs)
|
||||
inputs_t = tf.Variable(inputs, trainable=False).value()
|
||||
ops = _dynamic_rnn_swap_memory_benchmark(
|
||||
inputs_t, sequence_length, swap_memory=False)
|
||||
tf.initialize_all_variables().run()
|
||||
@ -1055,7 +1100,7 @@ def dynamic_rnn_swap_memory_benchmark(batch_size, max_time, num_units):
|
||||
|
||||
# Memory swap
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
inputs_t = tf.Variable(inputs)
|
||||
inputs_t = tf.Variable(inputs, trainable=False).value()
|
||||
ops = _dynamic_rnn_swap_memory_benchmark(
|
||||
inputs_t, sequence_length, swap_memory=True)
|
||||
tf.initialize_all_variables().run()
|
||||
@ -1082,14 +1127,15 @@ def rnn_long_sequence_benchmark(batch_size, seqlen, num_units,
|
||||
for _ in range(5):
|
||||
if dynamic:
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
inputs_t = tf.Variable(inputs)
|
||||
inputs_t = tf.Variable(inputs, trainable=False).value()
|
||||
ops = _dynamic_rnn_swap_memory_benchmark(
|
||||
inputs_t, sequence_length, swap_memory=swap_memory)
|
||||
tf.initialize_all_variables().run()
|
||||
elapsed = _timer(sess, ops)
|
||||
else:
|
||||
with tf.Session(config=config, graph=tf.Graph()) as sess:
|
||||
inputs_list_t = [tf.constant(x) for x in inputs_list]
|
||||
inputs_list_t = [
|
||||
tf.Variable(x, trainable=False).value() for x in inputs_list]
|
||||
ops = _static_vs_dynamic_rnn_benchmark_static(
|
||||
inputs_list_t, sequence_length)
|
||||
tf.initialize_all_variables().run()
|
||||
@ -1126,11 +1172,11 @@ class BenchmarkRNN(tf.test.Benchmark):
|
||||
self.report_benchmark(
|
||||
name="static_unroll_time_T%02d_B%03d_N%03d_gpu_%s"
|
||||
% (max_time, batch_size, num_units, use_gpu),
|
||||
iters=10, wall_time=s_dt)
|
||||
iters=20, wall_time=s_dt)
|
||||
self.report_benchmark(
|
||||
name="dynamic_unroll_time_T%02d_B%03d_N%03d_gpu_%s"
|
||||
% (max_time, batch_size, num_units, use_gpu),
|
||||
iters=10, wall_time=d_dt)
|
||||
iters=20, wall_time=d_dt)
|
||||
|
||||
def benchmarkDynamicLSTMNoMemorySwapVsMemorySwap(self):
|
||||
print("Calculation: Dynamic LSTM No Memory Swap vs. Memory Swap")
|
||||
@ -1143,11 +1189,31 @@ class BenchmarkRNN(tf.test.Benchmark):
|
||||
self.report_benchmark(
|
||||
name="dynamic_lstm_no_memory_swap_T%02d_B%03d_N%03d"
|
||||
% (max_time, batch_size, num_units),
|
||||
iters=10, wall_time=no_swap)
|
||||
iters=20, wall_time=no_swap)
|
||||
self.report_benchmark(
|
||||
name="dynamic_lstm_with_memory_swap_T%02d_B%03d_N%03d"
|
||||
% (max_time, batch_size, num_units),
|
||||
iters=10, wall_time=swap)
|
||||
iters=20, wall_time=swap)
|
||||
|
||||
def benchmarkStaticUnrollHalfSequenceLengthVsHalfUnroll(self):
|
||||
print("Calculation: Static Unroll with Halved Sequence Length "
|
||||
"vs. Half Static Unroll")
|
||||
print("batch \t full_t \t units \t gpu \t dt(half_seq_len) "
|
||||
"\t dt(unroll_half) \t dt(half_seq_len)/dt(unroll_half)")
|
||||
for batch_size in (128,):
|
||||
for max_time in (50,):
|
||||
for num_units in (256,):
|
||||
for use_gpu in (False, True):
|
||||
s_dt, d_dt = half_seq_len_vs_unroll_half_rnn_benchmark(
|
||||
batch_size, max_time, num_units, use_gpu)
|
||||
self.report_benchmark(
|
||||
name="half_seq_len_time_T%02d_B%03d_N%03d_gpu_%s"
|
||||
% (max_time, batch_size, num_units, use_gpu),
|
||||
iters=20, wall_time=s_dt)
|
||||
self.report_benchmark(
|
||||
name="unroll_half_time_T%02d_B%03d_N%03d_gpu_%s"
|
||||
% (max_time, batch_size, num_units, use_gpu),
|
||||
iters=20, wall_time=d_dt)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
157
tensorflow/python/kernel_tests/session_ops_test.py
Normal file
157
tensorflow/python/kernel_tests/session_ops_test.py
Normal file
@ -0,0 +1,157 @@
|
||||
# Copyright 2015 Google Inc. 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 tensorflow.ops.session_ops."""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import tensorflow as tf
|
||||
|
||||
|
||||
class SessionOpsTest(tf.test.TestCase):
|
||||
|
||||
def testHandleBasic(self):
|
||||
with self.test_session() as sess:
|
||||
# Return a handle.
|
||||
a = tf.constant(10)
|
||||
b = tf.constant(5)
|
||||
c = tf.mul(a, b)
|
||||
h = tf.get_session_handle(c)
|
||||
h = sess.run(h)
|
||||
|
||||
# Feed a tensor handle.
|
||||
f, x = tf.get_session_tensor(tf.int32)
|
||||
y = tf.mul(x, 10)
|
||||
self.assertEqual(500, sess.run(y, feed_dict={f: h.handle}))
|
||||
|
||||
def testHandleEval(self):
|
||||
with self.test_session() as sess:
|
||||
# Return a handle.
|
||||
a = tf.constant(10)
|
||||
b = tf.constant(5)
|
||||
c = tf.mul(a, b)
|
||||
h = tf.get_session_handle(c)
|
||||
h = sess.run(h)
|
||||
|
||||
# Get the tensor from its handle.
|
||||
self.assertEqual(50, h.eval())
|
||||
|
||||
def testHandleAndValue(self):
|
||||
with self.test_session() as sess:
|
||||
# Return a handle and a value.
|
||||
a = tf.constant(10)
|
||||
b = tf.constant(5)
|
||||
c = tf.mul(a, b)
|
||||
h = tf.get_session_handle(c)
|
||||
v = tf.mul(a, c)
|
||||
h, v = sess.run([h, v])
|
||||
|
||||
self.assertEqual(50, h.eval())
|
||||
self.assertEqual(500, v)
|
||||
|
||||
def testHandleCond(self):
|
||||
with self.test_session() as sess:
|
||||
# Return a handle and a value
|
||||
a = tf.constant(10)
|
||||
b = tf.constant(5)
|
||||
p = tf.less(a, b)
|
||||
c = tf.mul(a, b)
|
||||
h = tf.get_session_handle(c)
|
||||
p, h = sess.run([p, h])
|
||||
|
||||
# Run by feeding a tensor handle.
|
||||
f, x = tf.get_session_tensor(tf.int32)
|
||||
if p:
|
||||
y = tf.mul(x, 10)
|
||||
else:
|
||||
y = tf.mul(x, 100)
|
||||
result = sess.run(y, feed_dict={f: h.handle})
|
||||
|
||||
self.assertEqual(5000, result)
|
||||
|
||||
def testHandleForLoop(self):
|
||||
with self.test_session() as sess:
|
||||
# Initialize a handle.
|
||||
a = tf.constant(0)
|
||||
h = tf.get_session_handle(a)
|
||||
h = sess.run(h)
|
||||
|
||||
# Do some computation.
|
||||
f, x = tf.get_session_tensor(tf.int32)
|
||||
# Must define the loop body outside the loop.
|
||||
h_x = tf.get_session_handle(tf.add(x, 1))
|
||||
for _ in range(100):
|
||||
# This exercises garbage collection.
|
||||
h = sess.run(h_x, feed_dict={f: h.handle})
|
||||
|
||||
self.assertEqual(100, h.eval())
|
||||
|
||||
def testHandleWhileLoop(self):
|
||||
with self.test_session() as sess:
|
||||
# Initialize a handle.
|
||||
a = tf.constant(0)
|
||||
h = tf.get_session_handle(a)
|
||||
h = sess.run(h)
|
||||
|
||||
# Do some computation.
|
||||
f, x = tf.get_session_tensor(tf.int32)
|
||||
b = tf.constant(100)
|
||||
p = tf.less(x, b)
|
||||
# Must define the loop body outside the loop.
|
||||
h_x = tf.get_session_handle(tf.add(x, 1))
|
||||
while True:
|
||||
rp, h = sess.run([p, h_x], feed_dict={f: h.handle})
|
||||
if not rp:
|
||||
break
|
||||
|
||||
self.assertEqual(101, h.eval())
|
||||
|
||||
def testHandleMover(self):
|
||||
with self.test_session() as sess:
|
||||
# Return a handle.
|
||||
a = tf.constant(10)
|
||||
b = tf.constant(5)
|
||||
c = tf.mul(a, b)
|
||||
h = tf.get_session_handle(c)
|
||||
h = sess.run(h)
|
||||
|
||||
# Feed a tensor handle.
|
||||
f, x = tf.get_session_tensor(tf.int32)
|
||||
y = tf.mul(x, 10)
|
||||
self.assertEqual(500, sess.run(y, feed_dict={f: h.handle}))
|
||||
|
||||
# Feed another tensor handle.
|
||||
with tf.device("/gpu:0"):
|
||||
a = tf.constant(10)
|
||||
h = tf.get_session_handle(a)
|
||||
h = sess.run(h)
|
||||
self.assertEqual(100, sess.run(y, feed_dict={f: h.handle}))
|
||||
|
||||
def testHandleDeleter(self):
|
||||
with self.test_session() as sess:
|
||||
# Return a handle.
|
||||
a = tf.constant(10)
|
||||
b = tf.constant(5)
|
||||
c = tf.mul(a, b)
|
||||
h = tf.get_session_handle(c)
|
||||
h = sess.run(h)
|
||||
|
||||
# Delete using a raw tensor handle.
|
||||
h = h.get_raw_handle()
|
||||
f, x = tf.delete_session_tensor()
|
||||
sess.run(x, feed_dict={f: h})
|
||||
|
||||
if __name__ == "__main__":
|
||||
tf.test.main()
|
@ -24,7 +24,6 @@ import time
|
||||
import numpy as np
|
||||
import tensorflow as tf
|
||||
|
||||
from tensorflow.python.ops import control_flow_ops
|
||||
from tensorflow.python.ops import sparse_ops
|
||||
# pylint: enable=g-bad-import-order,unused-import
|
||||
|
||||
@ -131,7 +130,7 @@ def _sparse_tensor_dense_vs_dense_matmul_benchmark_dense(
|
||||
t0 = tf.constant(0)
|
||||
v0 = tf.constant(0.0)
|
||||
def _timeit(iterations, _):
|
||||
(_, final) = control_flow_ops.While(
|
||||
(_, final) = tf.while_loop(
|
||||
lambda t, _: t < iterations, body, (t0, v0),
|
||||
parallel_iterations=1, back_prop=False)
|
||||
return [final]
|
||||
@ -151,7 +150,7 @@ def _sparse_tensor_dense_vs_dense_matmul_benchmark_sparse(
|
||||
t0 = tf.constant(0)
|
||||
v0 = tf.constant(0.0)
|
||||
def _timeit(iterations, _):
|
||||
(_, final) = control_flow_ops.While(
|
||||
(_, final) = tf.while_loop(
|
||||
lambda t, _: t < iterations, body, (t0, v0),
|
||||
parallel_iterations=1, back_prop=False)
|
||||
return [final]
|
||||
|
@ -22,7 +22,6 @@ import numpy as np
|
||||
import tensorflow as tf
|
||||
|
||||
from tensorflow.python.framework import errors
|
||||
from tensorflow.python.ops import control_flow_ops
|
||||
from tensorflow.python.ops import gen_data_flow_ops
|
||||
|
||||
|
||||
@ -67,7 +66,7 @@ class StackOpTest(tf.test.TestCase):
|
||||
v = gen_data_flow_ops._stack_push(h, a, swap_memory=True)
|
||||
with tf.control_dependencies([v]):
|
||||
return tf.add(x, 1)
|
||||
r = control_flow_ops.While(c, b, [n])
|
||||
r = tf.while_loop(c, b, [n])
|
||||
|
||||
v = tf.constant(np.zeros(2000), dtype=tf.float32)
|
||||
def c1(x, y):
|
||||
@ -76,7 +75,7 @@ class StackOpTest(tf.test.TestCase):
|
||||
nx = tf.sub(x, 1)
|
||||
ny = y + gen_data_flow_ops._stack_pop(h, tf.float32)
|
||||
return [nx, ny]
|
||||
rx, ry = control_flow_ops.While(c1, b1, [r, v])
|
||||
rx, ry = tf.while_loop(c1, b1, [r, v])
|
||||
self.assertAllClose(np.ones(2000) * 10.0, ry.eval())
|
||||
|
||||
def testStackWhileSwap(self):
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user