From d9e6125c82f4e3775b8c868751b1657a6a147f55 Mon Sep 17 00:00:00 2001 From: Francois Fleuret Date: Mon, 21 Aug 2017 08:19:09 +0200 Subject: [PATCH] Style variable nodes differently, shows the tensor size, invoke the dot command in mlp.py. --- README.md | 36 ++++++++++++++++++++++++++---------- agtree2dot.py | 48 ++++++++++++++++++++++++++++++------------------ mlp.pdf | Bin 16495 -> 18158 bytes mlp.py | 23 ++++++++++++++++++++--- 4 files changed, 76 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 6b996b4..452aa9c 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,17 @@ from a [pytorch](http://pytorch.org) autograd graph. ### agtree2dot.save_dot(variable, variable_labels, result_file) ### -Saves into `result_file` a dot file corresponding to the autograd graph for `variable`, which can be either a single `Variable` or a set of `Variable`s. The dictionary `variable_labels` associates strings to some variables, which will be used in the resulting graph. +Saves into `result_file` a dot file corresponding to the autograd +graph for the `Variable` `variable`. The dictionary `variable_labels` +associates strings to some variables, which will be used in the +resulting graph. ## Example ## -A typical use would be: +A typical use is provided in [mlp.py](https://fleuret.org/git-extract/agtree2dot/mlp.py): ```python -import torch +import subprocess from torch import nn from torch.nn import functional as fn @@ -47,15 +50,28 @@ criterion = nn.MSELoss() loss = criterion(output, target) agtree2dot.save_dot(loss, - { input: 'input', target: 'target', loss: 'loss' }, + { + input: 'input', + target: 'target', + loss: 'loss', + mlp.fc1.weight: 'weight1', + mlp.fc1.bias: 'bias1', + mlp.fc2.weight: 'weight2', + mlp.fc2.bias: 'bias2', + }, open('./mlp.dot', 'w')) -``` -which would generate a file mlp.dot, which can then be translated to -pdf using the [Graphviz tools](http://www.graphviz.org/) +print('Generated mlp.dot') -``` -dot mlp.dot -Lg -T pdf -o mlp.pdf +try: + subprocess.check_call(["dot", "mlp.dot", "-Lg", "-T", "pdf", "-o", "mlp.pdf" ]) +except subprocess.CalledProcessError: + print('Calling the dot command failed. Is Graphviz installed?') + sys.exit(1) + +print('Generated mlp.pdf') ``` -to produce [mlp.pdf.](https://fleuret.org/git-extract/agtree2dot/mlp.pdf) +which would generate a file mlp.dot and try to generate +[mlp.pdf](https://fleuret.org/git-extract/agtree2dot/mlp.pdf) from it +with [Graphviz tools.](http://www.graphviz.org/) diff --git a/agtree2dot.py b/agtree2dot.py index 8931e36..8cc9e8c 100755 --- a/agtree2dot.py +++ b/agtree2dot.py @@ -75,7 +75,7 @@ def add_link(node_list, link_list, u, nu, v, nv): ###################################################################### -def build_ag_graph_lists(u, node_labels, node_list, link_list): +def fill_graph_lists(u, node_labels, node_list, link_list): if u is not None and not u in node_list: node = Node(len(node_list) + 1, @@ -84,15 +84,19 @@ def build_ag_graph_lists(u, node_labels, node_list, link_list): node_list[u] = node if isinstance(u, torch.autograd.Variable): - build_ag_graph_lists(u.grad_fn, node_labels, node_list, link_list) + fill_graph_lists(u.grad_fn, node_labels, node_list, link_list) add_link(node_list, link_list, u, 0, u.grad_fn, 0) - else: - if hasattr(u, 'next_functions'): - i = 0 - for v, j in u.next_functions: - build_ag_graph_lists(v, node_labels, node_list, link_list) - add_link(node_list, link_list, u, i, v, j) - i += 1 + + if hasattr(u, 'variable'): + fill_graph_lists(u.variable, node_labels, node_list, link_list) + add_link(node_list, link_list, u, 0, u.variable, 0) + + if hasattr(u, 'next_functions'): + i = 0 + for v, j in u.next_functions: + fill_graph_lists(v, node_labels, node_list, link_list) + add_link(node_list, link_list, u, i, v, j) + i += 1 ###################################################################### @@ -102,14 +106,22 @@ def print_dot(node_list, link_list, out): for n in node_list: node = node_list[n] - out.write( - ' ' + \ - str(node.id) + ' [shape=record,label="{ ' + \ - slot_string(node.max_out, for_input = True) + \ - node.label + \ - slot_string(node.max_in, for_input = False) + \ - ' }"]\n' - ) + if isinstance(n, torch.autograd.Variable): + out.write( + ' ' + \ + str(node.id) + ' [shape=note,label="' + \ + node.label + ' ' + re.search('torch\.Size\((.*)\)', str(n.data.size())).group(1) + \ + '"]\n' + ) + else: + out.write( + ' ' + \ + str(node.id) + ' [shape=record,label="{ ' + \ + slot_string(node.max_out, for_input = True) + \ + node.label + \ + slot_string(node.max_in, for_input = False) + \ + ' }"]\n' + ) for n in link_list: out.write(' ' + \ @@ -124,7 +136,7 @@ def print_dot(node_list, link_list, out): def save_dot(x, node_labels = {}, out = sys.stdout): node_list, link_list = {}, [] - build_ag_graph_lists(x, node_labels, node_list, link_list) + fill_graph_lists(x, node_labels, node_list, link_list) print_dot(node_list, link_list, out) ###################################################################### diff --git a/mlp.pdf b/mlp.pdf index 0f41f8131d27832267aff919d6a12e58793dbfe1..52dea893faf1a73c9f251ef3f33838a02d40583f 100644 GIT binary patch delta 16687 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 delta 15030 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 for comments & bug reports # ######################################################################### +import subprocess + from torch import nn from torch.nn import functional as fn from torch import Tensor @@ -45,8 +47,23 @@ criterion = nn.MSELoss() loss = criterion(output, target) agtree2dot.save_dot(loss, - { input: 'input', target: 'target', loss: 'loss' }, + { + input: 'input', + target: 'target', + loss: 'loss', + mlp.fc1.weight: 'weight1', + mlp.fc1.bias: 'bias1', + mlp.fc2.weight: 'weight2', + mlp.fc2.bias: 'bias2', + }, open('./mlp.dot', 'w')) -print('Generated mlp.dot. You can convert it to pdf with') -print('> dot mlp.dot -Lg -T pdf -o mlp.pdf') +print('Generated mlp.dot') + +try: + subprocess.check_call(["dot", "mlp.dot", "-Lg", "-T", "pdf", "-o", "mlp.pdf" ]) +except subprocess.CalledProcessError: + print('Calling the dot command failed. Is Graphviz installed?') + sys.exit(1) + +print('Generated mlp.pdf') -- 2.20.1