def parse(graph, args=None, omit_useless_nodes=True): """This method parses an optimized PyTorch model graph and produces a list of nodes and node stats for eventual conversion to TensorBoard protobuf format. Args: graph (PyTorch module): The model to be parsed. args (tuple): input tensor[s] for the model. omit_useless_nodes (boolean): Whether to remove nodes from the graph. """ n_inputs = len(args) scope = {} nodes_py = GraphPy() for i, node in enumerate(graph.inputs()): if omit_useless_nodes: if len( node.uses() ) == 0: # number of user of the node (= number of outputs/ fanout) continue if i < n_inputs: nodes_py.append(NodePyIO(node, 'input')) else: nodes_py.append(NodePyIO(node)) # parameter for node in graph.nodes(): nodes_py.append(NodePyOP(node)) for node in graph.outputs(): # must place last. NodePyIO(node, 'output') nodes_py.find_common_root() nodes_py.populate_namespace_from_OP_to_IO() return nodes_py
def parse(self, graph, trace, args=None, omit_useless_nodes=True): """This method parses an optimized PyTorch model graph and produces a list of nodes and node stats for eventual conversion to TensorBoard protobuf format. Args: graph (PyTorch module): The model graph to be parsed. trace (PyTorch JIT TracedModule): The model trace to be parsed. args (tuple): input tensor[s] for the model. omit_useless_nodes (boolean): Whether to remove nodes from the graph. """ nodes_py = GraphPy() for node in graph.inputs(): if omit_useless_nodes: if not node.uses( ): # number of user of the node (= number of outputs/ fanout) continue if node.type().kind() != CLASSTYPE_KIND: nodes_py.append(NodePyIO(node, 'input')) attr_to_scope = dict() def node_to_name(d): return str(d).split(":")[0].strip() for node in graph.nodes(): if node.kind() == GETATTR_KIND: attr_name = node.s('name') node_name = node_to_name(node) parent = node.input().node() # If the parent node is not the top-level "self" node if parent.kind() == GETATTR_KIND: parent_scope = attr_to_scope[node_to_name(parent)] attr_scope = parent_scope.split('/')[-1] attr_to_scope[node_name] = '{}/{}.{}'.format( parent_scope, attr_scope, attr_name) else: attr_to_scope[node_name] = '__module.{}'.format(attr_name) # We don't need classtype nodes; scope will provide this information if node.output().type().kind() != CLASSTYPE_KIND: node_py = NodePyOP(node) node_py.scopeName = attr_to_scope[node_name] nodes_py.append(node_py) else: nodes_py.append(NodePyOP(node)) # Create sink nodes for output ops for i, node in enumerate(graph.outputs()): node_py = NodePyIO(node, 'output') node_py.debugName = "output.{}".format(i + 1) node_py.inputs = [node.debugName()] nodes_py.append(node_py) alias_to_name = dict() base_name = parse_traced_name(trace._name) for name, module in trace.named_modules(prefix='__module'): mod_name = parse_traced_name(module._name) attr_name = name.split('.')[-1] alias_to_name[name] = '{}[{}]'.format(mod_name, attr_name) for node in nodes_py.nodes_op: module_aliases = node.scopeName.split('/')[-1].split('.') module_name = '' for i, alias in enumerate(module_aliases): if i == 0: module_name = alias node.scopeName = base_name else: module_name += '.' + alias node.scopeName += '/' + \ (alias_to_name[module_name] if module_name in alias_to_name else alias) nodes_py.populate_namespace_from_OP_to_IO() return nodes_py.to_proto()
def add_nodes(self, node_cpps): for node_cpp in node_cpps: nodepy = NodePyOP(node_cpp) nodepy.name = node_cpp.scopeName() + '_' + node_cpp.kind() self.nodes.append(nodepy)
def add_nodes(self, node_cpps): for node_cpp in node_cpps: nodepy = NodePyOP(node_cpp) nodepy.name = str(node_cpp).split(':')[0].strip().replace('%', '') self.nodes.append(nodepy)