Utility to dump TIDL networks to dot graphs
authorAjay Jayaraj <ajayj@ti.com>
Fri, 4 May 2018 22:30:11 +0000 (17:30 -0500)
committerAjay Jayaraj <ajayj@ti.com>
Mon, 7 May 2018 21:36:16 +0000 (16:36 -0500)
(MCT-973)

utils/Makefile [new file with mode: 0644]
utils/inc/tinn_utils.h [new file with mode: 0644]
utils/main.cpp [new file with mode: 0644]
utils/src/tinn_utils.cpp [new file with mode: 0644]

diff --git a/utils/Makefile b/utils/Makefile
new file mode 100644 (file)
index 0000000..369e7dc
--- /dev/null
@@ -0,0 +1,52 @@
+# Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Texas Instruments Incorporated nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+TARGET ?= arm
+
+EXE = $(TARGET)/network_display
+
+TINN_API_DIR=../tinn_api
+
+include $(TINN_API_DIR)/make.inc
+
+UNAME_M :=$(shell uname -m)
+
+ifeq ($(TARGET),x86)
+       CXX = g++
+       CXXFLAGS = -m32
+       LDFLAGS =
+endif
+
+CXXFLAGS += -o3 --std=c++11 -Wall -Werror
+CXXFLAGS += -Iinc -I$(TINN_API_DIR)/inc -I$(TINN_API_DIR)/src
+SOURCES = main.cpp src/tinn_utils.cpp $(TINN_API_DIR)/src/util.cpp
+
+$(EXE): $(TINN_LIB) $(HEADERS) $(SOURCES)
+       mkdir -p $(TARGET)
+       $(CXX) $(CXXFLAGS) $(SOURCES) $(TINN_LIB) $(LDFLAGS) -o $@
+
+clean::
+       $(RM) $(EXE) *.dot
diff --git a/utils/inc/tinn_utils.h b/utils/inc/tinn_utils.h
new file mode 100644 (file)
index 0000000..8b8a28d
--- /dev/null
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * Copyright (c) 2017-18 Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+//! @file utils.h
+
+#pragma once
+
+#include <string>
+#include <ostream>
+
+
+namespace tinn { namespace util {
+
+bool PrintNetwork(const std::string& network_binary, std::ostream& os = std::cout);
+bool GenerateDotGraphForNetwork(const std::string& network_binary,
+                                const std::string& dot_file);
+
+}} // namespace tinn::util
diff --git a/utils/main.cpp b/utils/main.cpp
new file mode 100644 (file)
index 0000000..1337279
--- /dev/null
@@ -0,0 +1,131 @@
+/******************************************************************************
+ * Copyright (c) 2017-18, Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <signal.h>
+#include <getopt.h>
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <cassert>
+#include <string>
+
+#include "tinn_utils.h"
+
+
+static void ProcessArgs(int argc, char *argv[], std::string& network_file,
+                        bool& do_print, std::string& dot_file);
+
+static void DisplayHelp();
+
+int main(int argc, char *argv[])
+{
+    // Catch ctrl-c to ensure a clean exit
+    signal(SIGABRT, exit);
+    signal(SIGTERM, exit);
+
+    // Process arguments
+    std::string network_file;
+    std::string dot_file;
+    bool do_print = false;
+    ProcessArgs(argc, argv, network_file, do_print, dot_file);
+
+    bool status = true;
+
+    if (do_print)
+        status &= tinn::util::PrintNetwork(network_file);
+
+    if (!dot_file.empty())
+        status &= tinn::util::GenerateDotGraphForNetwork(network_file, dot_file);
+
+    if (!status)
+        return EXIT_FAILURE;
+
+    return EXIT_SUCCESS;
+}
+
+
+void ProcessArgs(int argc, char *argv[], std::string& network_file,
+                 bool& do_print, std::string& dot_file)
+{
+    if (argc < 2)
+    {
+        DisplayHelp();
+        exit(EXIT_SUCCESS);
+    }
+
+    const struct option long_options[] =
+    {
+        {"help",         no_argument,       0, 'h'},
+        {"dot",          required_argument, 0, 'd'},
+        {"print",        no_argument,       0, 'p'},
+        {0, 0, 0, 0}
+    };
+
+    int option_index = 0;
+
+    while (true)
+    {
+        int this_option_optind = optind ? optind : 1;
+        int c = getopt_long(argc, argv, "-d:ph", long_options, &option_index);
+
+        if (c == -1)
+            break;
+
+        switch (c)
+        {
+            case 1:   network_file = argv[this_option_optind];
+                      break;
+
+            case 'd': dot_file = optarg;
+                      break;
+
+            case 'p': do_print = true;
+                      break;
+
+            case 'h': DisplayHelp();
+                      exit(EXIT_SUCCESS);
+                      break;
+
+            case '?': // Error in getopt_long
+                      exit(EXIT_FAILURE);
+                      break;
+
+            default:
+                      std::cerr << "Unsupported option: " << c << std::endl;
+                      break;
+        }
+    }
+}
+
+void DisplayHelp()
+{
+    std::cout << "Usage: tinn_display [option] <Network binary file>\n"
+                 "Options:  \n"
+                 " -p              Print network layer info\n"
+                 " -d <file name>  Generate network graph (dot file)\n"
+                 " -h              Display this help message\n";
+}
diff --git a/utils/src/tinn_utils.cpp b/utils/src/tinn_utils.cpp
new file mode 100644 (file)
index 0000000..c6932c7
--- /dev/null
@@ -0,0 +1,314 @@
+/******************************************************************************
+ * Copyright (c) 2017-18, Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <boost/graph/graphviz.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/subgraph.hpp>
+#include <iostream>
+
+#include "util.h"
+#include "tinn_utils.h"
+#include "tidl_create_params.h"
+
+using namespace tinn::util;
+
+const char* TIDL_LayerString[] =
+{
+    "Data",
+    "Convolution",
+    "Pooling",
+    "ReLU",
+    "PReLU",
+    "EltWise",
+    "InnerProduct",
+    "SoftMax",
+    "BatchNorm",
+    "Bias",
+    "Scale",
+    "Deconv2D",
+    "Concat",
+    "Split",
+    "Slice",
+    "Crop",
+    "Flatten",
+    "DropOut",
+    "ArgMax",
+    "DetectionOutput",
+    "Reshape",
+};
+
+
+static bool CreateGraph(const sTIDL_Network_t& net,
+                        const std::string& dot_file);
+
+static const char* GetVertexColor(uint32_t layer_type);
+
+bool tinn::util::PrintNetwork(const std::string& network_binary,
+                              std::ostream& os)
+{
+    if (network_binary.empty())
+        return false;
+
+    sTIDL_Network_t net;
+    bool status = ReadBinary(network_binary,
+                             reinterpret_cast<char *>(&net),
+                             sizeof(sTIDL_Network_t));
+    if (!status)
+       return false;
+
+    printf("%3s  %-20s  %3s  %3s  %3s "
+           " %3s  %3s  %3s  %3s  %3s  %3s  %3s  %3s %3s "
+           " %5s  %5s  %5s  %5s  %5s  %5s  %5s  %5s\n",
+            "#", "Name", "gId", "#i", "#o",
+            "i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7", "o",
+            "#roi", "#ch", "h", "w", "#roi", "#ch", "h", "w");
+
+    for (int i = 0 ; i < net.numLayers; i++)
+    {
+        printf("%3d, %-20s,",i,
+                    TIDL_LayerString[net.TIDLLayers[i].layerType]);
+        printf("%3d, %3d ,%3d ,",net.TIDLLayers[i].layersGroupId,
+                                 net.TIDLLayers[i].numInBufs,
+                                 net.TIDLLayers[i].numOutBufs);
+
+        for (int j = 0; j < net.TIDLLayers[i].numInBufs; j++)
+        {
+          printf("%3d ,",net.TIDLLayers[i].inData[j].dataId);
+        }
+        for (int j = (net.TIDLLayers[i].numInBufs > 0 ?
+              net.TIDLLayers[i].numInBufs : 0); j < 8; j++)
+        {
+          printf("  x ,");
+        }
+        printf("%3d ,",net.TIDLLayers[i].outData[0].dataId);
+        for (int j = 0; j < 4; j++)
+        {
+          printf("%5d ,",net.TIDLLayers[i].inData[0].dimValues[j]);
+        }
+        for (int j = 0; j < 4; j++)
+        {
+          printf("%5d ,",net.TIDLLayers[i].outData[0].dimValues[j]);
+        }
+        printf("\n");
+    }
+
+    return true;
+}
+
+bool tinn::util::GenerateDotGraphForNetwork(const std::string& network_binary,
+                                            const std::string& dot_file)
+{
+    if (network_binary.empty())
+        return false;
+
+    sTIDL_Network_t net;
+    bool status = ReadBinary(network_binary,
+                             reinterpret_cast<char *>(&net),
+                             sizeof(sTIDL_Network_t));
+    if (!status)
+       return false;
+
+    return CreateGraph(net, dot_file);
+}
+
+
+
+using boost::adjacency_list;
+using boost::property;
+using boost::subgraph;
+using boost::get_property;
+using boost::add_vertex;
+using boost::add_edge;
+using boost::edge_index_t;
+using boost::edge_attribute_t;
+using boost::vertex_attribute_t;
+using boost::vertex_attribute;
+using boost::graph_name_t;
+using boost::graph_name;
+using boost::graph_graph_attribute_t;
+using boost::graph_graph_attribute;
+using boost::graph_vertex_attribute_t;
+using boost::graph_vertex_attribute;
+using boost::graph_edge_attribute_t;
+using boost::graph_edge_attribute;
+using boost::vecS;
+
+using GraphvizAttributes = std::map<std::string, std::string>;
+
+
+// Boost graph with notes and edges annotated with Dot properties.
+// These properties are used by the write_graphviz function.
+//
+// From https://www.boost.org/doc/libs/1_55_0/libs/graph/doc/subgraph.html
+// When creating a subgraph, the underlying graph type is required to have
+// vertex_index and edge_index internal properties. Add an edge index property
+// to the adjacency list. We do not need to add a vertex index property
+// because it is built in to the adjacency_list.
+using Graph =
+  subgraph<
+    adjacency_list<vecS, vecS, boost::directedS,
+      property<vertex_attribute_t, GraphvizAttributes>,
+      property<edge_index_t,int,property<edge_attribute_t, GraphvizAttributes>>,
+      property<graph_name_t, std::string,
+       property<graph_graph_attribute_t,  GraphvizAttributes,
+        property<graph_vertex_attribute_t, GraphvizAttributes,
+         property<graph_edge_attribute_t,   GraphvizAttributes>
+      >>>
+  >>;
+
+using Edge =  boost::graph_traits<Graph>::edge_descriptor;
+
+template<typename T>
+using VertexPropertyMap = typename boost::property_map<T, boost::vertex_attribute_t>::type;
+
+template<typename T>
+using EdgePropertyMap = typename boost::property_map<T, boost::edge_attribute_t>::type;
+
+using LayerIdMap = std::map<uint32_t, Graph*>;
+
+
+bool CreateGraph(const sTIDL_Network_t& net, const std::string& dot_file)
+{
+    static const char* FILLED = "filled";
+    static const char* LABEL  = "label";
+    static const char* COLOR  = "color";
+    static const char* STYLE  = "style";
+    static const char* SHAPE  = "shape";
+
+    Graph tidl(net.numLayers);
+
+    // Add a vertex for each layer
+    LayerIdMap layerid_map;
+    for (int i = 0 ; i < net.numLayers; i++)
+    {
+        uint32_t layer_type = net.TIDLLayers[i].layerType;
+
+        // Special handling for input & output data layers:
+        // Do not put them in the same subgraph
+        if (net.TIDLLayers[i].layerType == TIDL_DataLayer)
+        {
+            auto V = vertex(i, tidl);
+            VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, tidl);
+            vpm[V][LABEL] = TIDL_LayerString[layer_type];
+            vpm[V][COLOR] = GetVertexColor(layer_type);
+            vpm[V][STYLE] = FILLED;
+            vpm[V][SHAPE] = "ellipse";
+
+            continue;
+        }
+
+        // Use the layer group ID to create subgraphs
+        // Place all layers with the same ID into a single subgraph
+        uint32_t layerGroupId = net.TIDLLayers[i].layersGroupId;
+        Graph* sub = nullptr;
+        if (layerid_map.find(layerGroupId) == layerid_map.end())
+        {
+            sub = &(tidl.create_subgraph());
+            layerid_map[layerGroupId] = sub;
+            get_property(*sub, graph_name) = std::string("cluster") +
+                                             std::to_string(layerGroupId);
+            get_property(*sub, graph_graph_attribute)[LABEL] =
+                                    std::to_string(layerGroupId);
+            get_property(*sub, graph_graph_attribute)[STYLE] = FILLED;
+            get_property(*sub, graph_graph_attribute)["fillcolor"] = "lightgrey";
+        }
+        else
+            sub = layerid_map[layerGroupId];
+
+        auto V = add_vertex(i, *sub);
+        VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, *sub);
+        vpm[V][LABEL] = TIDL_LayerString[layer_type];
+        vpm[V][COLOR] = GetVertexColor(layer_type);
+        vpm[V][STYLE] = FILLED;
+
+    }
+
+    // Add edges based on dataId i.e. there is an edge from layer A -> B
+    // iff dataId is in outData for A and inData for B.
+    EdgePropertyMap<Graph> ep = boost::get(boost::edge_attribute, tidl);
+    for (int i = 0 ; i < net.numLayers; i++)
+    {
+        if (net.TIDLLayers[i].numOutBufs < 0)
+            continue;
+
+        // Create a string to  annotate the edge - num_channels, height, width
+        int32_t out_id = net.TIDLLayers[i].outData[0].dataId;
+        std::string edge_info =
+                    std::to_string(net.TIDLLayers[i].outData[0].dimValues[1])+
+                    "x" +
+                    std::to_string(net.TIDLLayers[i].outData[0].dimValues[2])+
+                    "x" +
+                    std::to_string(net.TIDLLayers[i].outData[0].dimValues[3]);
+
+        for (int j = 0; j < net.numLayers; j++)
+        {
+            if (j == i) continue;
+
+            for (int x = 0; x < net.TIDLLayers[j].numInBufs; x++)
+                if (net.TIDLLayers[j].inData[x].dataId == out_id)
+                {
+                    Edge e = add_edge(i, j, tidl).first;
+                    ep[e][LABEL] = edge_info;
+                }
+
+        }
+    }
+
+    get_property(tidl, graph_name) = "TIDL Network";
+    get_property(tidl, graph_vertex_attribute)[SHAPE] = "Mrecord";
+
+    // Generate dot file from boost graph
+    std::ofstream dot(dot_file);
+    boost::write_graphviz(dot, tidl);
+
+    return true;
+}
+
+
+const char* GetVertexColor(uint32_t layer_type)
+{
+    static const char* LayerColors[] =
+    {
+        "lightblue",
+        "lightseagreen",
+        "lightpink",
+        "sienna",
+        "lightyellow",
+        "palegoldenrod",
+        "lightsalmon",
+        "lightcyan",
+        "wheat",
+        "olivedrab",
+        "lightskyblue",
+        "beige",
+        "lavender",
+        "linen"
+
+    };
+
+    return LayerColors[layer_type % (sizeof(LayerColors)/sizeof(char *))];
+}