diff options
author | 2018-07-10 09:44:04 -0700 | |
---|---|---|
committer | 2018-07-10 09:49:29 -0700 | |
commit | 89e172f6ae5a52f8ceca6b4690331e1dce89d456 (patch) | |
tree | c942f2b89a8bf6b4d464e09da9b3ce263a0eb6a7 /tensorflow/core/grappler/costs | |
parent | 0c7e26bcf14afa88e882bfb8eeff682d268f52e7 (diff) |
Function shape inference for Grappler.
PiperOrigin-RevId: 203962286
Diffstat (limited to 'tensorflow/core/grappler/costs')
8 files changed, 1938 insertions, 17 deletions
diff --git a/tensorflow/core/grappler/costs/BUILD b/tensorflow/core/grappler/costs/BUILD index b054068299..f3dc2c2091 100644 --- a/tensorflow/core/grappler/costs/BUILD +++ b/tensorflow/core/grappler/costs/BUILD @@ -41,6 +41,7 @@ cc_library( visibility = ["//visibility:public"], deps = [ ":utils", + "//tensorflow/core/grappler/utils:functions", "//tensorflow/core/grappler/utils:topological_sort", "//tensorflow/core/grappler:graph_view", "//tensorflow/core/grappler:op_types", diff --git a/tensorflow/core/grappler/costs/graph_properties.cc b/tensorflow/core/grappler/costs/graph_properties.cc index 0c02876ac5..83a8326e79 100644 --- a/tensorflow/core/grappler/costs/graph_properties.cc +++ b/tensorflow/core/grappler/costs/graph_properties.cc @@ -28,6 +28,7 @@ limitations under the License. #include "tensorflow/core/grappler/graph_view.h" #include "tensorflow/core/grappler/op_types.h" #include "tensorflow/core/grappler/utils.h" +#include "tensorflow/core/grappler/utils/functions.h" #include "tensorflow/core/grappler/utils/topological_sort.h" #include "tensorflow/core/lib/strings/str_util.h" @@ -422,11 +423,108 @@ class SymbolicShapeRefiner { return it->second.inference_context.get(); } - // Forward the shapes from the function's fanin to the function body, - // then call PropagateShapes. - // Returns an error if 'node' is not a function node. - Status UpdateFunction(const NodeDef* node, bool* refined) { - return UpdateNode(node, refined); + // Forward the shapes from the function input nodes to + // the argument nodes (which are Placeholder nodes), then + // perform shape inference on the function body. + // + // Propagate shape information of final function body node + // to function node `node`. + // + // In the event of an error, UpdateNode will simply set `node`'s + // output shape to be Unknown. + Status UpdateFunction(const NodeDef* node) { + auto it = fun_to_grappler_function_item_.find(node->op()); + if (it == fun_to_grappler_function_item_.end()) { + return errors::InvalidArgument( + node->op(), " was not previously added to SymbolicShapeRefiner."); + } + + GrapplerFunctionItem& grappler_function_item = it->second; + GraphView gv(&grappler_function_item.graph); + + // Forward shapes from function input nodes to argument nodes. + for (int i = 0; i < grappler_function_item.inputs().size(); ++i) { + auto& fun_input = grappler_function_item.input(i); + if (fun_input.placeholders.size() > 1) { + // TODO(jmdecker): Handle case with multiple input placeholders + return errors::Unimplemented( + "Input arguments with multiple placeholders are not yet " + "supported."); + } + NodeDef* fun_node = gv.GetNode(fun_input.input_name); + const string& input = node->input(i); + const string& node_name = NodeName(input); + + if (IsControlInput(input)) { + return errors::FailedPrecondition( + "Function inputs should not contain control nodes."); + } + + NodeDef* input_node = graph_.GetNode(node_name); + if (input_node == nullptr) { + return errors::FailedPrecondition(node_name, + " was not found in the graph."); + } + + InferenceContext* input_inference_context = GetContext(input_node); + if (input_inference_context == nullptr) { + return errors::FailedPrecondition( + "Inference context has not been created for ", node_name); + } + + int output_port_num = NodePosition(input); + AttrValue attr_output_shape; + TensorShapeProto proto; + const auto& handle = input_inference_context->output(output_port_num); + input_inference_context->ShapeHandleToProto(handle, &proto); + *attr_output_shape.mutable_shape() = proto; + (*fun_node->mutable_attr())["shape"] = attr_output_shape; + } + + // Perform inference on function body. + GraphProperties gp(grappler_function_item); + TF_RETURN_IF_ERROR(gp.InferStatically(true)); + + // Add return nodes for output shapes. + auto ic = GetContext(node); + int output = 0; + for (auto const& out_arg : grappler_function_item.outputs()) { + if (out_arg.output_tensors.size() > 1) { + // TODO(jmdecker): Handle case of multiple output tensors + return errors::Unimplemented( + "Output arguments with multiple output tensors are not yet " + "supported."); + } + + string out_tensor = out_arg.output_tensors[0]; + auto out_tensor_pieces = str_util::Split(out_tensor, ","); + string node_name = out_tensor_pieces[0]; + int port_id; + + // Check if port_id was included in out_tensor + if (out_tensor_pieces.size() <= 1) { + port_id = 0; + } else if (!strings::safe_strto32(out_tensor_pieces[1], &port_id)) { + return errors::FailedPrecondition( + "Failed string to integer conversion for ", out_tensor_pieces[1]); + } + + const NodeDef* retnode = gv.GetNode(node_name); + if (retnode == nullptr) { + return errors::FailedPrecondition("Unable to find return node ", + node_name, " for ", node->name()); + } + + auto output_properties = gp.GetOutputProperties(retnode->name()); + auto const& outprop = output_properties[port_id]; + const TensorShapeProto& shape = outprop.shape(); + ShapeHandle out; + TF_RETURN_IF_ERROR(ic->MakeShapeFromShapeProto(shape, &out)); + ic->set_output(output, out); + output++; + } + + return Status::OK(); } Status UpdateNode(const NodeDef* node, bool* refined) { @@ -436,6 +534,7 @@ class SymbolicShapeRefiner { node_context = CHECK_NOTNULL(GetNodeContext(node)); *refined = true; } + // Check if the shapes of the nodes in the fan-in of this node have changed, // and if they have, update the node input shapes. InferenceContext* inference_context = node_context->inference_context.get(); @@ -455,7 +554,8 @@ class SymbolicShapeRefiner { if (c == nullptr) { return errors::FailedPrecondition( "Input ", dst_input, " ('", input->name(), "') for '", - node->name(), "' was not previously added to ShapeRefiner."); + node->name(), + "' was not previously added to SymbolicShapeRefiner."); } if (IsConstant(*input)) { @@ -565,6 +665,21 @@ class SymbolicShapeRefiner { node_context->inference_context->set_input_tensors_as_shapes( input_tensors_as_shapes); + // Properly handle function nodes. + if (node_context->op_data && node_context->op_data->is_function_op) { + // TODO(jmdecker): Detect if the input shapes have changed for this + // function. Note that when we hit a function call node, refined will be + // true, as the updates to the call node will have changed, even if it's + // the same function being called twice with the same input shapes. + // Example: simple_function.pbtxt + if (UpdateFunction(node).ok()) { + return Status::OK(); + } else { + VLOG(1) << "UpdateFunction failed for " << node->op() + << ". Defaulting to ShapeUnknown."; + } + } + // Update the shapes of the outputs. return InferShapes(*node, node_context); } @@ -681,7 +796,39 @@ class SymbolicShapeRefiner { return true; } - Status AddFunction(const NodeDef* node) { return Status::OK(); } + Status AddFunction(const NodeDef* function_node) { + auto it = fun_to_grappler_function_item_.find(function_node->op()); + if (it != fun_to_grappler_function_item_.end()) { + return Status::OK(); + } + + const FunctionDef* function_def = + CHECK_NOTNULL(function_library_.Find(function_node->op())); + + GrapplerFunctionItem grappler_function_item; + TF_RETURN_IF_ERROR(MakeGrapplerFunctionItem( + *function_def, function_library_, &grappler_function_item)); + + if (grappler_function_item.inputs().size() > function_node->input_size()) { + return errors::FailedPrecondition( + "Function input size should be smaller than node input size."); + } + + for (int i = grappler_function_item.inputs().size(); + i < function_node->input_size(); ++i) { + const string& input = function_node->input(i); + if (!IsControlInput(input)) { + return errors::FailedPrecondition( + "Found regular input (", input, + ") instead of control nodes for node ", function_node->name()); + } + } + + fun_to_grappler_function_item_[function_def->signature().name()] = + grappler_function_item; + + return Status::OK(); + } Status AddNode(const NodeDef* node) { NodeContext& node_ctx = node_to_context_[node]; @@ -911,6 +1058,8 @@ class SymbolicShapeRefiner { std::unordered_map<const NodeDef*, NodeContext> node_to_context_; std::unordered_map<ShapeId, ShapeHandle, HashShapeId> unknown_shapes_; std::unordered_map<DimId, DimensionHandle, HashDimId> unknown_dims_; + std::unordered_map<string, GrapplerFunctionItem> + fun_to_grappler_function_item_; FunctionLibraryDefinition function_library_; const std::unordered_map<string, std::unordered_set<int>>& fed_ports_; }; @@ -1082,13 +1231,9 @@ Status GraphProperties::UpdateShapes( // Set shapes and types of Queue ops, if needed. TF_RETURN_IF_ERROR(UpdateQueue(n, shape_refiner, new_shapes)); } else { - auto c = shape_refiner->GetNodeContext(n); - if (c && c->op_data && c->op_data->is_function_op) { - TF_RETURN_IF_ERROR(shape_refiner->UpdateFunction(n, new_shapes)); - } else { - // Rely on regular TF shape refinement for all the other nodes. - TF_RETURN_IF_ERROR(shape_refiner->UpdateNode(n, new_shapes)); - } + // Rely on regular TF shape refinement for all the other nodes. + // UpdateNode calls UpdateFunction if a function node is detected. + TF_RETURN_IF_ERROR(shape_refiner->UpdateNode(n, new_shapes)); } return Status::OK(); } diff --git a/tensorflow/core/grappler/costs/graph_properties_test.cc b/tensorflow/core/grappler/costs/graph_properties_test.cc index aa787ae620..1be19d291a 100644 --- a/tensorflow/core/grappler/costs/graph_properties_test.cc +++ b/tensorflow/core/grappler/costs/graph_properties_test.cc @@ -783,7 +783,7 @@ TEST_F(GraphPropertiesTest, InferRestoreOpShape_WithTwoNodesShareSameOutput) { EXPECT_EQ("float: [128,256]", PropToString(prop)); } -TEST_F(GraphPropertiesTest, FunctionStaticShapeInference) { +TEST_F(GraphPropertiesTest, SimpleFunctionStaticShapeInference) { // Test graph produced in python using: /* @function.Defun(*[tf.float32] * 2, noinline=True) @@ -796,7 +796,6 @@ TEST_F(GraphPropertiesTest, FunctionStaticShapeInference) { z = MyAdd(x, y) z = MyAdd(x, z) */ - // Check that the shape inference code infers what it can. GrapplerItem item; string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath, "simple_function.pbtxt"); @@ -806,15 +805,258 @@ TEST_F(GraphPropertiesTest, FunctionStaticShapeInference) { const auto out_props = properties.GetOutputProperties("MyAdd_55e046a8"); const OpInfo::TensorProperties& out_prop = out_props[0]; EXPECT_EQ(DT_FLOAT, out_prop.dtype()); - EXPECT_TRUE(out_prop.shape().unknown_rank()); + EXPECT_FALSE(out_prop.shape().unknown_rank()); + EXPECT_EQ(2, out_prop.shape().dim_size()); + EXPECT_EQ(1, out_prop.shape().dim(0).size()); + EXPECT_EQ(2, out_prop.shape().dim(1).size()); const auto in_props = properties.GetInputProperties("MyAdd_55e046a8"); + EXPECT_EQ(2, in_props.size()); + + const OpInfo::TensorProperties& in_prop = in_props[0]; + EXPECT_EQ(DT_FLOAT, in_prop.dtype()); + EXPECT_FALSE(in_prop.shape().unknown_rank()); + EXPECT_EQ(2, in_prop.shape().dim_size()); + EXPECT_EQ(1, in_prop.shape().dim(0).size()); + EXPECT_EQ(2, in_prop.shape().dim(1).size()); + + const OpInfo::TensorProperties& in_prop1 = in_props[1]; + EXPECT_EQ(DT_FLOAT, in_prop1.dtype()); + EXPECT_FALSE(in_prop1.shape().unknown_rank()); + EXPECT_EQ(2, in_prop1.shape().dim_size()); + EXPECT_EQ(1, in_prop1.shape().dim(0).size()); + EXPECT_EQ(2, in_prop1.shape().dim(1).size()); +} + +TEST_F(GraphPropertiesTest, LargeFunctionStaticShapeInference) { + GrapplerItem item; + string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath, + "large_function_graph.pbtxt"); + TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph)); + GraphProperties properties(item); + TF_CHECK_OK(properties.InferStatically(false)); + + const auto out_props = properties.GetOutputProperties("y0"); + EXPECT_EQ(2, out_props.size()); + + const OpInfo::TensorProperties& out_prop0 = out_props[0]; + EXPECT_EQ(DT_FLOAT, out_prop0.dtype()); + EXPECT_EQ(4, out_prop0.shape().dim_size()); + EXPECT_EQ(128, out_prop0.shape().dim(0).size()); + EXPECT_EQ(112, out_prop0.shape().dim(1).size()); + EXPECT_EQ(112, out_prop0.shape().dim(2).size()); + EXPECT_EQ(64, out_prop0.shape().dim(3).size()); + + const OpInfo::TensorProperties& out_prop1 = out_props[1]; + EXPECT_EQ(DT_FLOAT, out_prop1.dtype()); + EXPECT_EQ(128, out_prop1.shape().dim(0).size()); + EXPECT_EQ(112, out_prop1.shape().dim(1).size()); + EXPECT_EQ(112, out_prop1.shape().dim(2).size()); + EXPECT_EQ(24, out_prop1.shape().dim(3).size()); + + const auto in_props = properties.GetInputProperties("y0"); + EXPECT_EQ(4, in_props.size()); + + const OpInfo::TensorProperties& in_prop0 = in_props[0]; + EXPECT_EQ(DT_FLOAT, in_prop0.dtype()); + EXPECT_EQ(1, in_prop0.shape().dim_size()); + EXPECT_EQ(64, in_prop0.shape().dim(0).size()); + + const OpInfo::TensorProperties& in_prop1 = in_props[1]; + EXPECT_EQ(DT_FLOAT, in_prop1.dtype()); + EXPECT_EQ(4, in_prop1.shape().dim_size()); + EXPECT_EQ(1, in_prop1.shape().dim(0).size()); + EXPECT_EQ(1, in_prop1.shape().dim(1).size()); + EXPECT_EQ(24, in_prop1.shape().dim(2).size()); + EXPECT_EQ(64, in_prop1.shape().dim(3).size()); + + const OpInfo::TensorProperties& in_prop2 = in_props[2]; + EXPECT_EQ(DT_FLOAT, in_prop2.dtype()); + EXPECT_EQ(4, in_prop2.shape().dim_size()); + EXPECT_EQ(128, in_prop2.shape().dim(0).size()); + EXPECT_EQ(224, in_prop2.shape().dim(1).size()); + EXPECT_EQ(224, in_prop2.shape().dim(2).size()); + EXPECT_EQ(3, in_prop2.shape().dim(3).size()); + + const OpInfo::TensorProperties& in_prop3 = in_props[3]; + EXPECT_EQ(DT_FLOAT, in_prop3.dtype()); + EXPECT_EQ(4, in_prop3.shape().dim_size()); + EXPECT_EQ(7, in_prop3.shape().dim(0).size()); + EXPECT_EQ(7, in_prop3.shape().dim(1).size()); + EXPECT_EQ(3, in_prop3.shape().dim(2).size()); + EXPECT_EQ(8, in_prop3.shape().dim(3).size()); +} + +TEST_F(GraphPropertiesTest, FunctionWithErrorStaticShapeInference) { + GrapplerItem item; + string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath, + "function_error.pbtxt"); + TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph)); + GraphProperties properties(item); + TF_CHECK_OK(properties.InferStatically(false)); + + const auto out_props = properties.GetOutputProperties("MyAdd_yabA4wXEdM4"); + EXPECT_EQ(1, out_props.size()); + + const OpInfo::TensorProperties& out_prop = out_props[0]; + EXPECT_EQ(DT_FLOAT, out_prop.dtype()); + EXPECT_TRUE(out_prop.shape().unknown_rank()); + + const auto in_props = properties.GetInputProperties("MyAdd_yabA4wXEdM4"); + EXPECT_EQ(2, in_props.size()); + const OpInfo::TensorProperties& in_prop = in_props[0]; EXPECT_EQ(DT_FLOAT, in_prop.dtype()); EXPECT_FALSE(in_prop.shape().unknown_rank()); EXPECT_EQ(2, in_prop.shape().dim_size()); EXPECT_EQ(1, in_prop.shape().dim(0).size()); EXPECT_EQ(2, in_prop.shape().dim(1).size()); + + const OpInfo::TensorProperties& in_prop1 = in_props[1]; + EXPECT_EQ(DT_FLOAT, in_prop1.dtype()); + EXPECT_FALSE(in_prop1.shape().unknown_rank()); + EXPECT_EQ(2, in_prop1.shape().dim_size()); + EXPECT_EQ(1, in_prop1.shape().dim(0).size()); + EXPECT_EQ(2, in_prop1.shape().dim(1).size()); +} + +TEST_F(GraphPropertiesTest, FunctionSwitchStaticShapeInference) { + // Test graph produced in python using: + /* + @function.Defun(*[tf.float32] * 2, noinline=True) + def MyAdd(x, y): + return tf.add(x, y) + + with tf.Graph().as_default(): + x = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32) + y = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32) + z = tf.constant(2.0, shape=[1, 2], dtype=tf.float32) + z2 = MyAdd(tf.case([(tf.less(0, 1), x)], default=y), z) + */ + GrapplerItem item; + string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath, + "function_switch.pbtxt"); + TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph)); + GraphProperties properties(item); + TF_CHECK_OK(properties.InferStatically(false)); + const auto out_props = properties.GetOutputProperties("MyAdd_MPaeanipb7o"); + const OpInfo::TensorProperties& out_prop = out_props[0]; + EXPECT_EQ(DT_FLOAT, out_prop.dtype()); + EXPECT_FALSE(out_prop.shape().unknown_rank()); + EXPECT_EQ(2, out_prop.shape().dim_size()); + EXPECT_EQ(1, out_prop.shape().dim(0).size()); + EXPECT_EQ(2, out_prop.shape().dim(1).size()); + + const auto in_props = properties.GetInputProperties("MyAdd_MPaeanipb7o"); + EXPECT_EQ(2, in_props.size()); + + const OpInfo::TensorProperties& in_prop = in_props[0]; + EXPECT_EQ(DT_FLOAT, in_prop.dtype()); + EXPECT_FALSE(in_prop.shape().unknown_rank()); + EXPECT_EQ(2, in_prop.shape().dim_size()); + EXPECT_EQ(1, in_prop.shape().dim(0).size()); + EXPECT_EQ(2, in_prop.shape().dim(1).size()); + + const OpInfo::TensorProperties& in_prop1 = in_props[1]; + EXPECT_EQ(DT_FLOAT, in_prop1.dtype()); + EXPECT_FALSE(in_prop1.shape().unknown_rank()); + EXPECT_EQ(2, in_prop1.shape().dim_size()); + EXPECT_EQ(1, in_prop1.shape().dim(0).size()); + EXPECT_EQ(2, in_prop1.shape().dim(1).size()); +} + +TEST_F(GraphPropertiesTest, FunctionSwitch2StaticShapeInference) { + // Test graph produced in python using: + /* + @function.Defun(*[tf.float32] * 2, noinline=True) + def MyAdd(x, y): + return tf.add(x, y) + + with tf.Graph().as_default(): + x = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32) + y = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32) + z = tf.constant(2.0, shape=[1, 2], dtype=tf.float32) + z2 = MyAdd(tf.case([(tf.less(1, 0), x)], default=y), z) + */ + GrapplerItem item; + string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath, + "function_switch_2.pbtxt"); + TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph)); + GraphProperties properties(item); + TF_CHECK_OK(properties.InferStatically(false)); + const auto out_props = properties.GetOutputProperties("MyAdd_MPaeanipb7o"); + const OpInfo::TensorProperties& out_prop = out_props[0]; + EXPECT_EQ(DT_FLOAT, out_prop.dtype()); + EXPECT_FALSE(out_prop.shape().unknown_rank()); + EXPECT_EQ(2, out_prop.shape().dim_size()); + EXPECT_EQ(1, out_prop.shape().dim(0).size()); + EXPECT_EQ(2, out_prop.shape().dim(1).size()); + + const auto in_props = properties.GetInputProperties("MyAdd_MPaeanipb7o"); + EXPECT_EQ(2, in_props.size()); + + const OpInfo::TensorProperties& in_prop = in_props[0]; + EXPECT_EQ(DT_FLOAT, in_prop.dtype()); + EXPECT_FALSE(in_prop.shape().unknown_rank()); + EXPECT_EQ(2, in_prop.shape().dim_size()); + EXPECT_EQ(1, in_prop.shape().dim(0).size()); + EXPECT_EQ(2, in_prop.shape().dim(1).size()); + + const OpInfo::TensorProperties& in_prop1 = in_props[1]; + EXPECT_EQ(DT_FLOAT, in_prop1.dtype()); + EXPECT_FALSE(in_prop1.shape().unknown_rank()); + EXPECT_EQ(2, in_prop1.shape().dim_size()); + EXPECT_EQ(1, in_prop1.shape().dim(0).size()); + EXPECT_EQ(2, in_prop1.shape().dim(1).size()); +} + +TEST_F(GraphPropertiesTest, FunctionSwitchShapesStaticShapeInference) { + // Test graph produced in python using: + /* + @function.Defun(*[tf.float32] * 2, noinline=True) + def MyAdd(x, y): + a = tf.constant(2.0, shape=[1, 2], dtype=tf.float32) + b = tf.constant(2.0, shape=[1, 3], dtype=tf.float32) + c = tf.add(x, a) + d = tf.add(y, b) + return c + + with tf.Graph().as_default(): + x = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32) + y = lambda: tf.constant(2.0, shape=[1, 2], dtype=tf.float32) + z = tf.constant(2.0, shape=[1, 3], dtype=tf.float32) + z2 = MyAdd(tf.case([(tf.less(1, 0), x)], default=y), z) + */ + GrapplerItem item; + string filename = io::JoinPath(testing::TensorFlowSrcRoot(), kTestDataPath, + "function_switch_shapes.pbtxt"); + TF_CHECK_OK(ReadGraphDefFromFile(filename, &item.graph)); + GraphProperties properties(item); + TF_CHECK_OK(properties.InferStatically(false)); + const auto out_props = properties.GetOutputProperties("MyAdd_lEKAAnIwI5I"); + const OpInfo::TensorProperties& out_prop = out_props[0]; + EXPECT_EQ(DT_FLOAT, out_prop.dtype()); + EXPECT_FALSE(out_prop.shape().unknown_rank()); + EXPECT_EQ(2, out_prop.shape().dim_size()); + EXPECT_EQ(1, out_prop.shape().dim(0).size()); + EXPECT_EQ(2, out_prop.shape().dim(1).size()); + + const auto in_props = properties.GetInputProperties("MyAdd_lEKAAnIwI5I"); + EXPECT_EQ(2, in_props.size()); + + const OpInfo::TensorProperties& in_prop = in_props[0]; + EXPECT_EQ(DT_FLOAT, in_prop.dtype()); + EXPECT_FALSE(in_prop.shape().unknown_rank()); + EXPECT_EQ(2, in_prop.shape().dim_size()); + EXPECT_EQ(1, in_prop.shape().dim(0).size()); + EXPECT_EQ(2, in_prop.shape().dim(1).size()); + + const OpInfo::TensorProperties& in_prop1 = in_props[1]; + EXPECT_EQ(DT_FLOAT, in_prop1.dtype()); + EXPECT_FALSE(in_prop1.shape().unknown_rank()); + EXPECT_EQ(2, in_prop1.shape().dim_size()); + EXPECT_EQ(1, in_prop1.shape().dim(0).size()); + EXPECT_EQ(3, in_prop1.shape().dim(1).size()); } TEST_F(GraphPropertiesTest, SymbolicShapes) { diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/function_error.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/function_error.pbtxt new file mode 100644 index 0000000000..c3f0a6c95d --- /dev/null +++ b/tensorflow/core/grappler/costs/graph_properties_testdata/function_error.pbtxt @@ -0,0 +1,117 @@ +node { + name: "Const" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "Const_1" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "MyAdd_yabA4wXEdM4" + op: "MyAdd_yabA4wXEdM4" + input: "Const" + input: "Const_1" +} +library { + function { + signature { + name: "MyAdd_yabA4wXEdM4" + input_arg { + name: "x" + type: DT_FLOAT + } + input_arg { + name: "y" + type: DT_FLOAT + } + output_arg { + name: "add_1" + type: DT_FLOAT + } + } + node_def { + name: "Add" + op: "Add" + input: "x" + input: "Add:z:0" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + } + node_def { + name: "Add_1" + op: "Add" + input: "Add:z:0" + input: "y" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + } + ret { + key: "add_1" + value: "Add_1:z:0" + } + attr { + key: "_noinline" + value { + b: true + } + } + } +} +versions { + producer: 26 + min_consumer: 12 +} diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch.pbtxt new file mode 100644 index 0000000000..d6d856ce41 --- /dev/null +++ b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch.pbtxt @@ -0,0 +1,251 @@ +node { + name: "Const" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "Less/x" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 0 + } + } + } +} +node { + name: "Less/y" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 1 + } + } + } +} +node { + name: "Less" + op: "Less" + input: "Less/x" + input: "Less/y" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "case/cond/Switch" + op: "Switch" + input: "Less" + input: "Less" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/switch_t" + op: "Identity" + input: "case/cond/Switch:1" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/switch_f" + op: "Identity" + input: "case/cond/Switch" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/pred_id" + op: "Identity" + input: "Less" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/Const" + op: "Const" + input: "^case/cond/switch_t" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "case/cond/Const_1" + op: "Const" + input: "^case/cond/switch_f" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "case/cond/Merge" + op: "Merge" + input: "case/cond/Const_1" + input: "case/cond/Const" + attr { + key: "N" + value { + i: 2 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "MyAdd_MPaeanipb7o" + op: "MyAdd_MPaeanipb7o" + input: "case/cond/Merge" + input: "Const" +} +library { + function { + signature { + name: "MyAdd_MPaeanipb7o" + input_arg { + name: "x" + type: DT_FLOAT + } + input_arg { + name: "y" + type: DT_FLOAT + } + output_arg { + name: "Add" + type: DT_FLOAT + } + } + node_def { + name: "Add" + op: "Add" + input: "x" + input: "y" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + } + ret { + key: "Add" + value: "Add:z:0" + } + attr { + key: "_noinline" + value { + b: true + } + } + } +} +versions { + producer: 26 + min_consumer: 12 +} diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_2.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_2.pbtxt new file mode 100644 index 0000000000..e57d9d7076 --- /dev/null +++ b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_2.pbtxt @@ -0,0 +1,251 @@ +node { + name: "Const" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "Less/x" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 1 + } + } + } +} +node { + name: "Less/y" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 0 + } + } + } +} +node { + name: "Less" + op: "Less" + input: "Less/x" + input: "Less/y" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "case/cond/Switch" + op: "Switch" + input: "Less" + input: "Less" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/switch_t" + op: "Identity" + input: "case/cond/Switch:1" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/switch_f" + op: "Identity" + input: "case/cond/Switch" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/pred_id" + op: "Identity" + input: "Less" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/Const" + op: "Const" + input: "^case/cond/switch_t" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "case/cond/Const_1" + op: "Const" + input: "^case/cond/switch_f" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "case/cond/Merge" + op: "Merge" + input: "case/cond/Const_1" + input: "case/cond/Const" + attr { + key: "N" + value { + i: 2 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "MyAdd_MPaeanipb7o" + op: "MyAdd_MPaeanipb7o" + input: "case/cond/Merge" + input: "Const" +} +library { + function { + signature { + name: "MyAdd_MPaeanipb7o" + input_arg { + name: "x" + type: DT_FLOAT + } + input_arg { + name: "y" + type: DT_FLOAT + } + output_arg { + name: "Add" + type: DT_FLOAT + } + } + node_def { + name: "Add" + op: "Add" + input: "x" + input: "y" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + } + ret { + key: "Add" + value: "Add:z:0" + } + attr { + key: "_noinline" + value { + b: true + } + } + } +} +versions { + producer: 26 + min_consumer: 12 +} diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_shapes.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_shapes.pbtxt new file mode 100644 index 0000000000..e9afa91886 --- /dev/null +++ b/tensorflow/core/grappler/costs/graph_properties_testdata/function_switch_shapes.pbtxt @@ -0,0 +1,317 @@ +node { + name: "Const" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 3 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "Less/x" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 1 + } + } + } +} +node { + name: "Less/y" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 0 + } + } + } +} +node { + name: "Less" + op: "Less" + input: "Less/x" + input: "Less/y" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "case/cond/Switch" + op: "Switch" + input: "Less" + input: "Less" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/switch_t" + op: "Identity" + input: "case/cond/Switch:1" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/switch_f" + op: "Identity" + input: "case/cond/Switch" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/pred_id" + op: "Identity" + input: "Less" + attr { + key: "T" + value { + type: DT_BOOL + } + } +} +node { + name: "case/cond/Const" + op: "Const" + input: "^case/cond/switch_t" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "case/cond/Const_1" + op: "Const" + input: "^case/cond/switch_f" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } +} +node { + name: "case/cond/Merge" + op: "Merge" + input: "case/cond/Const_1" + input: "case/cond/Const" + attr { + key: "N" + value { + i: 2 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "MyAdd_lEKAAnIwI5I" + op: "MyAdd_lEKAAnIwI5I" + input: "case/cond/Merge" + input: "Const" +} +library { + function { + signature { + name: "MyAdd_lEKAAnIwI5I" + input_arg { + name: "x" + type: DT_FLOAT + } + input_arg { + name: "y" + type: DT_FLOAT + } + output_arg { + name: "Add" + type: DT_FLOAT + } + } + node_def { + name: "Const" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 2 + } + } + float_val: 2.0 + } + } + } + } + node_def { + name: "Const_1" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 1 + } + dim { + size: 3 + } + } + float_val: 2.0 + } + } + } + } + node_def { + name: "Add" + op: "Add" + input: "x" + input: "Const:output:0" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + } + node_def { + name: "Add_1" + op: "Add" + input: "y" + input: "Const_1:output:0" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + } + ret { + key: "Add" + value: "Add:z:0" + } + attr { + key: "_noinline" + value { + b: true + } + } + } +} +versions { + producer: 26 + min_consumer: 12 +} diff --git a/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt b/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt new file mode 100644 index 0000000000..415c347a1d --- /dev/null +++ b/tensorflow/core/grappler/costs/graph_properties_testdata/large_function_graph.pbtxt @@ -0,0 +1,597 @@ +node { + name: "Const/Const" + op: "Const" + device: "/cpu:0" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + dim { + size: 1 + } + } + int_val: 64 + } + } + } +} +node { + name: "input_0_0" + op: "RandomUniform" + input: "Const/Const" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "seed" + value { + i: 0 + } + } + attr { + key: "seed2" + value { + i: 0 + } + } +} +node { + name: "Const_1/Const" + op: "Const" + device: "/cpu:0" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + dim { + size: 4 + } + } + tensor_content: "\001\000\000\000\001\000\000\000\030\000\000\000@\000\000\000" + } + } + } +} +node { + name: "input_1_0" + op: "RandomUniform" + input: "Const_1/Const" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "seed" + value { + i: 0 + } + } + attr { + key: "seed2" + value { + i: 0 + } + } +} +node { + name: "Const_2/Const" + op: "Const" + device: "/cpu:0" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + dim { + size: 4 + } + } + tensor_content: "\200\000\000\000\340\000\000\000\340\000\000\000\003\000\000\000" + } + } + } +} +node { + name: "input_2_0" + op: "RandomUniform" + input: "Const_2/Const" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "seed" + value { + i: 0 + } + } + attr { + key: "seed2" + value { + i: 0 + } + } +} +node { + name: "Const_3/Const" + op: "Const" + device: "/cpu:0" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + dim { + size: 4 + } + } + tensor_content: "\007\000\000\000\007\000\000\000\003\000\000\000\010\000\000\000" + } + } + } +} +node { + name: "input_3_0" + op: "RandomUniform" + input: "Const_3/Const" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "seed" + value { + i: 0 + } + } + attr { + key: "seed2" + value { + i: 0 + } + } +} +node { + name: "y0" + op: "BiasAddx1_Conv2Dx1_DepthwiseConv2dNativex1_Relux1_95" + input: "input_0_0" + input: "input_1_0" + input: "input_2_0" + input: "input_3_0" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" +} +node { + name: "shape" + op: "Shape" + input: "y0" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "out_type" + value { + type: DT_INT32 + } + } +} +node { + name: "zeros" + op: "ZerosLike" + input: "shape" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "ones" + op: "OnesLike" + input: "shape" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "slice_0" + op: "Slice" + input: "y0" + input: "zeros" + input: "ones" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "Index" + value { + type: DT_INT32 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "identity_0" + op: "Identity" + input: "slice_0" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "shape_1" + op: "Shape" + input: "y0:1" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "out_type" + value { + type: DT_INT32 + } + } +} +node { + name: "zeros_1" + op: "ZerosLike" + input: "shape_1" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "ones_1" + op: "OnesLike" + input: "shape_1" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_INT32 + } + } +} +node { + name: "slice_1" + op: "Slice" + input: "y0:1" + input: "zeros_1" + input: "ones_1" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "Index" + value { + type: DT_INT32 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "identity_1" + op: "Identity" + input: "slice_1" + input: "^input_0_0" + input: "^input_1_0" + input: "^input_2_0" + input: "^input_3_0" + device: "/cpu:0" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +library { + function { + signature { + name: "BiasAddx1_Conv2Dx1_DepthwiseConv2dNativex1_Relux1_95" + input_arg { + name: "InceptionV2/Conv2d_1a_7x7/biases/read" + type: DT_FLOAT + } + input_arg { + name: "InceptionV2/Conv2d_1a_7x7/pointwise_weights/read" + type: DT_FLOAT + } + input_arg { + name: "random_uniform" + type: DT_FLOAT + } + input_arg { + name: "InceptionV2/Conv2d_1a_7x7/depthwise_weights/read" + type: DT_FLOAT + } + output_arg { + name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/Relu" + type: DT_FLOAT + } + output_arg { + name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise" + type: DT_FLOAT + } + } + node_def { + name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/BiasAdd" + op: "BiasAdd" + input: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d:output:0" + input: "InceptionV2/Conv2d_1a_7x7/biases/read" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + } + node_def { + name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/Relu" + op: "Relu" + input: "InceptionV2/InceptionV2/Conv2d_1a_7x7/BiasAdd:output:0" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + } + node_def { + name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d" + op: "Conv2D" + input: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise:output:0" + input: "InceptionV2/Conv2d_1a_7x7/pointwise_weights/read" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + attr { + key: "dilations" + value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + } + } + } + attr { + key: "padding" + value { + s: "VALID" + } + } + attr { + key: "strides" + value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + } + } + } + attr { + key: "use_cudnn_on_gpu" + value { + b: true + } + } + } + node_def { + name: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise" + op: "DepthwiseConv2dNative" + input: "random_uniform" + input: "InceptionV2/Conv2d_1a_7x7/depthwise_weights/read" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + attr { + key: "dilations" + value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + } + } + } + attr { + key: "padding" + value { + s: "SAME" + } + } + attr { + key: "strides" + value { + list { + i: 1 + i: 2 + i: 2 + i: 1 + } + } + } + } + ret { + key: "InceptionV2/InceptionV2/Conv2d_1a_7x7/Relu" + value: "InceptionV2/InceptionV2/Conv2d_1a_7x7/Relu:activations:0" + } + ret { + key: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise" + value: "InceptionV2/InceptionV2/Conv2d_1a_7x7/separable_conv2d/depthwise:output:0" + } + attr { + key: "_noinline" + value { + b: true + } + } + } +} +versions { + producer: 26 + min_consumer: 12 +} |