1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 ==============================================================================*/ 15 16 #include "tensorflow/tools/graph_transforms/fold_constants_lib.h" 17 18 #include "tensorflow/core/common_runtime/constant_folding.h" 19 #include "tensorflow/core/graph/graph_constructor.h" 20 #include "tensorflow/core/graph/node_builder.h" 21 #include "tensorflow/core/graph/subgraph.h" 22 #include "tensorflow/core/platform/init_main.h" 23 #include "tensorflow/core/public/session.h" 24 #include "tensorflow/core/util/command_line_flags.h" 25 #include "tensorflow/tools/graph_transforms/transform_utils.h" 26 27 namespace tensorflow { 28 namespace graph_transforms { 29 30 // Deletes any specified types of nodes, unless they're necessary for the 31 // graph's inputs or outputs. 32 Status RemoveNodes(const GraphDef& input_graph_def, 33 const TransformFuncContext& context, 34 GraphDef* output_graph_def) { 35 if (!context.params.count("op")) { 36 return errors::InvalidArgument( 37 "remove_nodes expects at least one 'op'" 38 "argument, e.g. remove_nodes(op=Identity)"); 39 } 40 int32 max_inputs; 41 TF_RETURN_IF_ERROR( 42 context.GetOneInt32Parameter("max_inputs", 1, &max_inputs)); 43 44 // Make sure we don't get rid of any nodes used as graph inputs or outputs. 45 std::set<string> required_nodes; 46 for (const string& input : context.input_names) { 47 required_nodes.insert(NodeNameFromInput(input)); 48 } 49 for (const string& output : context.output_names) { 50 required_nodes.insert(NodeNameFromInput(output)); 51 } 52 53 std::vector<string> ops_to_remove = context.params.at("op"); 54 GraphDef current_graph_def = input_graph_def; 55 for (const string& op : ops_to_remove) { 56 for (int num_inputs = 1; num_inputs <= max_inputs; ++num_inputs) { 57 // Look for a variable number of inputs. 58 OpTypePattern pattern = {op}; 59 pattern.inputs.resize(num_inputs); 60 for (int i = 0; i < num_inputs; ++i) { 61 pattern.inputs[i] = {"*"}; 62 } 63 // Keep looking for nodes to remove until there are no more changes. 64 bool any_nodes_removed; 65 do { 66 any_nodes_removed = false; 67 std::map<string, string> inputs_to_rename; 68 GraphDef replaced_graph_def; 69 TF_RETURN_IF_ERROR(ReplaceMatchingOpTypes( 70 current_graph_def, pattern, 71 [&inputs_to_rename, &required_nodes, &any_nodes_removed]( 72 const NodeMatch& match, const std::set<string>& input_nodes, 73 const std::set<string>& output_nodes, 74 std::vector<NodeDef>* new_nodes) { 75 const NodeDef& replace_node = match.node; 76 // If this node is needed in the inputs or outputs don't replace 77 // it. 78 if (required_nodes.count(replace_node.name())) { 79 LOG(INFO) << "Skipping replacement for " << replace_node.name(); 80 CopyOriginalMatch(match, new_nodes); 81 return Status::OK(); 82 } 83 const NodeDef& input_node = match.inputs[0].node; 84 string target_name = input_node.name(); 85 for (const string& input : replace_node.input()) { 86 if (!input.compare(0, target_name.size(), target_name)) { 87 if (input.size() == target_name.size() || 88 input[target_name.size()] == ':') { 89 target_name = input; 90 break; 91 } 92 } 93 } 94 inputs_to_rename[replace_node.name()] = target_name; 95 inputs_to_rename["^" + replace_node.name()] = 96 "^" + input_node.name(); 97 new_nodes->push_back(input_node); 98 any_nodes_removed = true; 99 return Status::OK(); 100 }, 101 {true}, &replaced_graph_def)); 102 // Make sure all references to removed nodes now point to their inputs. 103 TF_RETURN_IF_ERROR( 104 RenameNodeInputs(replaced_graph_def, inputs_to_rename, 105 std::unordered_set<string>(), ¤t_graph_def)); 106 } while (any_nodes_removed); 107 } 108 } 109 110 *output_graph_def = current_graph_def; 111 return Status::OK(); 112 } 113 114 REGISTER_GRAPH_TRANSFORM("remove_nodes", RemoveNodes); 115 116 } // namespace graph_transforms 117 } // namespace tensorflow 118