From 657e369b6cfbbdcf0cfd5f05c45c2656fac67c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AF=85?= Date: Fri, 4 Jan 2019 11:45:34 +0800 Subject: [PATCH] Add MACE visualization --- docs/development/how_to_debug.rst | 2 + mace/core/tensor.h | 6 +- mace/python/tools/BUILD | 1 + mace/python/tools/converter.py | 8 + .../converter_tool/tensorflow_converter.py | 1 + mace/python/tools/visualization/BUILD | 14 + mace/python/tools/visualization/index.html | 297 ++++++++++++++++++ .../tools/visualization/visualize_model.py | 92 ++++++ 8 files changed, 419 insertions(+), 2 deletions(-) create mode 100644 mace/python/tools/visualization/BUILD create mode 100644 mace/python/tools/visualization/index.html create mode 100644 mace/python/tools/visualization/visualize_model.py diff --git a/docs/development/how_to_debug.rst b/docs/development/how_to_debug.rst index b0e2131f..9eb04479 100644 --- a/docs/development/how_to_debug.rst +++ b/docs/development/how_to_debug.rst @@ -79,6 +79,8 @@ Debug model conversion After model is converted to MACE model, a literal model graph is generated in directory `mace/codegen/models/your_model`. You can refer to it when debugging model conversion. +MACE also provides model visualization HTML generated in `builds` directory, generated after converting model. + Debug engine using log -------------------------- diff --git a/mace/core/tensor.h b/mace/core/tensor.h index e70d48a9..da73f1fa 100644 --- a/mace/core/tensor.h +++ b/mace/core/tensor.h @@ -289,9 +289,11 @@ class Tensor { inline void Reshape(const std::vector &shape) { shape_ = shape; if (has_opencl_image()) { - MACE_CHECK(raw_size() <= 4 * buffer_->size()); + MACE_CHECK(raw_size() <= 4 * buffer_->size(), + "Must satisfy: ", raw_size(), " <= ", 4 * buffer_->size()); } else { - MACE_CHECK(raw_size() <= buffer_->size()); + MACE_CHECK(raw_size() <= buffer_->size(), + "Must satisfy: ", raw_size(), " <= ", buffer_->size()); } } diff --git a/mace/python/tools/BUILD b/mace/python/tools/BUILD index a5a35397..ecb2e9cb 100644 --- a/mace/python/tools/BUILD +++ b/mace/python/tools/BUILD @@ -45,6 +45,7 @@ py_binary( deps = [ ":converter_lib", ":model_saver_lib", + "//mace/python/tools/visualization:visualization_lib", "@six_archive//:six", ], ) diff --git a/mace/python/tools/converter.py b/mace/python/tools/converter.py index be9e65b9..21c4f9a4 100644 --- a/mace/python/tools/converter.py +++ b/mace/python/tools/converter.py @@ -25,6 +25,7 @@ from mace.python.tools import model_saver from mace.python.tools.converter_tool import base_converter as cvt from mace.python.tools.converter_tool import transformer from mace.python.tools.convert_util import mace_check +from mace.python.tools.visualization import visualize_model # ./bazel-bin/mace/python/tools/tf_converter --model_file quantized_test.pb \ # --output quantized_test_dsp.pb \ @@ -201,6 +202,13 @@ def main(unused_args): option, output_graph_def, quantize_activation_info) output_graph_def = converter.run() + try: + visualizer = visualize_model.ModelVisualizer(FLAGS.model_tag, + output_graph_def) + visualizer.save_html() + except: # noqa + print("Failed to visualize model:", sys.exc_info()[0]) + model_saver.save_model( option, output_graph_def, model_checksum, weight_checksum, FLAGS.template_dir, FLAGS.obfuscate, FLAGS.model_tag, diff --git a/mace/python/tools/converter_tool/tensorflow_converter.py b/mace/python/tools/converter_tool/tensorflow_converter.py index 825c3c00..e0fee90f 100644 --- a/mace/python/tools/converter_tool/tensorflow_converter.py +++ b/mace/python/tools/converter_tool/tensorflow_converter.py @@ -272,6 +272,7 @@ class TensorflowConverter(base_converter.ConverterInterface): print("Run transform_graph: %s" % TFTransformGraphOptions[ option.device]) try: + print ("output keys: ", option.output_nodes.keys()) transformed_graph_def = TransformGraph(tf_graph_def, option.input_nodes.keys(), option.output_nodes.keys(), diff --git a/mace/python/tools/visualization/BUILD b/mace/python/tools/visualization/BUILD new file mode 100644 index 00000000..f814afd5 --- /dev/null +++ b/mace/python/tools/visualization/BUILD @@ -0,0 +1,14 @@ +py_library( + name = "visualization_lib", + srcs = [ + "visualize_model.py", + ], + data = [ + "index.html", + ], + srcs_version = "PY2AND3", + visibility = ["//visibility:public"], + deps = [ + "//mace/proto:mace_py", + ], +) diff --git a/mace/python/tools/visualization/index.html b/mace/python/tools/visualization/index.html new file mode 100644 index 00000000..f36ea260 --- /dev/null +++ b/mace/python/tools/visualization/index.html @@ -0,0 +1,297 @@ + + + + + + + + MACE Model + + + +

MACE Visualization

+ +

Ops

+
+ +

Tensors

+
+ +

Graph

+Click node to see details at bottom of this page. +
+ +
+ +
+

+
+ + + + + + + \ No newline at end of file diff --git a/mace/python/tools/visualization/visualize_model.py b/mace/python/tools/visualization/visualize_model.py new file mode 100644 index 00000000..19841db7 --- /dev/null +++ b/mace/python/tools/visualization/visualize_model.py @@ -0,0 +1,92 @@ +import json +import numpy as np + +from google.protobuf.json_format import _Printer + +THREASHOLD = 16 + + +class NPEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, np.integer): + return int(obj) + elif isinstance(obj, np.floating): + return float(obj) + elif isinstance(obj, np.ndarray): + return obj.tolist() + else: + return super(MyEncoder, self).default(obj) + + +class ModelVisualizer(object): + def __init__(self, model_name, proto): + self._output_file = "builds/%s_index.html" % model_name + self._proto = proto + + def render_html(self): + json_obj = { + "nodes": [], + "links": [] + } + + json_printer = _Printer() + + for op in self._proto.op: + op_json = json_printer._MessageToJsonObject(op) + op_json["id"] = op_json["name"] + op_json["node_type"] = "op" + json_obj["nodes"].append(op_json) + + for tensor in self._proto.tensors: + tensor_json = json_printer._MessageToJsonObject(tensor) + + tensor_json["id"] = tensor_json["name"] + if "floatData" in tensor_json and \ + len(tensor_json["floatData"]) > THREASHOLD: + del tensor_json["floatData"] + if "int32Data" in tensor_json and \ + len(tensor_json["int32Data"]) > THREASHOLD: + del tensor_json["int32Data"] + tensor_json["node_type"] = "tensor" + json_obj["nodes"].append(tensor_json) + + node_ids = [node["id"] for node in json_obj["nodes"]] + + tensor_to_op = {} + for op in self._proto.op: + for tensor in op.output: + tensor_to_op[tensor] = op.name + + for op in json_obj["nodes"]: + if "input" in op: + for input in op["input"]: + if input in node_ids and op["name"] in node_ids: + # for weights + json_obj["links"].append( + {"source": input, "target": op["name"]}) + elif input in tensor_to_op and \ + tensor_to_op[input] in node_ids: + # for intermediate tensor + json_obj["links"].append( + {"source": tensor_to_op[input], + "target": op["name"]}) + else: + # for input + json_obj["nodes"].append({ + "id": input, + "name": input, + "node_type": "input" + }) + json_obj["links"].append( + {"source": input, "target": op["name"]}) + + json_msg = json.dumps(json_obj, cls=NPEncoder) + + with open("mace/python/tools/visualization/index.html") as f: + html = f.read() + return html % json_msg + + def save_html(self): + html = self.render_html() + with open(self._output_file, "wb") as f: + f.write(html) -- GitLab