Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / src / inference_engine / network_serializer.cpp
index f530e35..4ccf4a5 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Intel Corporation
+// Copyright (C) 2018-2019 Intel Corporation
 // SPDX-License-Identifier: Apache-2.0
 //
 
@@ -10,6 +10,7 @@
 #include "details/ie_cnn_network_tools.h"
 #include "details/caseless.hpp"
 #include "network_serializer.h"
+#include "exec_graph_info.hpp"
 #include "xml_parse_utils.h"
 
 using namespace InferenceEngine;
@@ -38,22 +39,44 @@ void NetworkSerializer::serialize(
     const std::string &xmlPath,
     const std::string &binPath,
     const InferenceEngine::ICNNNetwork& network) {
+    const std::vector<CNNLayerPtr> ordered = CNNNetSortTopologically(network);
 
-    std::ofstream ofsBin(binPath, std::ofstream::out | std::ofstream::binary);
-    if (!ofsBin) {
-        THROW_IE_EXCEPTION << "File '" << binPath << "' is not opened as out file stream";
+    // A flag for serializing executable graph information (not complete IR)
+    bool execGraphInfoSerialization = false;
+    // If first layer has perfCounter parameter set then it's executable graph info serialization.
+    // All other layers must also have this parameter set.
+    if (ordered[0]->params.find(ExecGraphInfoSerialization::PERF_COUNTER) != ordered[0]->params.end()) {
+        execGraphInfoSerialization = true;
+        for (const auto &layer : ordered) {
+            if (layer->params.find(ExecGraphInfoSerialization::PERF_COUNTER) == layer->params.end()) {
+                THROW_IE_EXCEPTION << "Each node must have " << ExecGraphInfoSerialization::PERF_COUNTER
+                                   << " parameter set in case of executable graph info serialization";
+            }
+        }
+    }
+
+    bool dumpWeights = !execGraphInfoSerialization & !binPath.empty();
+    std::ofstream ofsBin;
+    if (dumpWeights) {
+        ofsBin.open(binPath, std::ofstream::out | std::ofstream::binary);
+        if (!ofsBin) {
+            THROW_IE_EXCEPTION << "File '" << binPath << "' is not opened as out file stream";
+        }
     }
 
     pugi::xml_document doc;
-    pugi::xml_node net = doc.append_child("net");
-    net.append_attribute("name").set_value(network.getName().c_str());
-    net.append_attribute("version").set_value("3");
-    net.append_attribute("batch").set_value(network.getBatchSize());
+    pugi::xml_node netXml = doc.append_child("net");
+    netXml.append_attribute("name").set_value(network.getName().c_str());
+
+    // no need to print this information for executable graph information serialization because it is not IR.
+    if (!execGraphInfoSerialization) {
+        netXml.append_attribute("version").set_value("3");
+        netXml.append_attribute("batch").set_value(network.getBatchSize());
+    }
 
-    pugi::xml_node layers = net.append_child("layers");
+    pugi::xml_node layers = netXml.append_child("layers");
 
-    const std::vector<CNNLayerPtr> ordered = CNNNetSortTopologically(network);
-    std::map<CNNLayer::Ptr, int> matching;
+    std::map<CNNLayer::Ptr, size_t> matching;
     for (size_t i = 0; i < ordered.size(); i++) {
         matching[ordered[i]] = i;
     }
@@ -70,18 +93,20 @@ void NetworkSerializer::serialize(
         layer.append_attribute("precision").set_value(precision.name());
         layer.append_attribute("id").set_value(i);
 
-        updateStdLayerParams(node);
+        if (!execGraphInfoSerialization) {
+            updateStdLayerParams(node);
+        }
 
         const auto &params = node->params;
-        if (params.size()) {
+        if (!params.empty()) {
             pugi::xml_node data = layer.append_child(dataName.c_str());
 
-            for (const auto it : params) {
+            for (const auto &it : params) {
                 data.append_attribute(it.first.c_str()).set_value(it.second.c_str());
             }
         }
 
-        if (node->insData.size()) {
+        if (!node->insData.empty()) {
             pugi::xml_node input = layer.append_child("input");
 
             for (size_t iport = 0; iport < node->insData.size(); iport++) {
@@ -95,7 +120,7 @@ void NetworkSerializer::serialize(
                 }
             }
         }
-        if (node->outData.size()) {
+        if (!node->outData.empty()) {
             pugi::xml_node input = layer.append_child("output");
             for (size_t oport = 0; oport < node->outData.size(); oport++) {
                 pugi::xml_node port = input.append_child("port");
@@ -107,9 +132,9 @@ void NetworkSerializer::serialize(
                 }
             }
         }
-        if (node->blobs.size()) {
+        if (dumpWeights && !node->blobs.empty()) {
             auto blobsNode = layer.append_child("blobs");
-            for (const auto dataIt : node->blobs) {
+            for (const auto &dataIt : node->blobs) {
                 const char *dataPtr = dataIt.second->buffer().as<char*>();
 
                 size_t dataSize = dataIt.second->byteSize();
@@ -126,31 +151,33 @@ void NetworkSerializer::serialize(
         }
     }
 
-    ofsBin.close();
-    if (!ofsBin.good()) {
-        THROW_IE_EXCEPTION << "Error during '" << binPath << "' closing";
+    if (dumpWeights) {
+        ofsBin.close();
+        if (!ofsBin.good()) {
+            THROW_IE_EXCEPTION << "Error during '" << binPath << "' closing";
+        }
     }
 
-    pugi::xml_node edges = net.append_child("edges");
+    pugi::xml_node edges = netXml.append_child("edges");
 
-    for (size_t i = 0; i < ordered.size(); i++) {
-        const CNNLayer::Ptr node = ordered[i];
+    for (const auto &ord : ordered) {
+        const CNNLayer::Ptr node = ord;
 
-        if (node->outData.size()) {
+        if (!node->outData.empty()) {
             auto itFrom = matching.find(node);
             if (itFrom == matching.end()) {
                 THROW_IE_EXCEPTION << "Internal error, cannot find " << node->name << " in matching container during serialization of IR";
             }
             for (size_t oport = 0; oport < node->outData.size(); oport++) {
                 const DataPtr outData = node->outData[oport];
-                for (auto inputTo : outData->inputTo) {
+                for (const auto &inputTo : outData->inputTo) {
                     auto itTo = matching.find(inputTo.second);
                     if (itTo == matching.end()) {
                         THROW_IE_EXCEPTION << "Broken edge form layer " << node->name << " to layer "  << inputTo.first<< "during serialization of IR";
                     }
 
-                    size_t foundPort = -1;
-                    for (size_t iport = 0; iport < inputTo.second->insData.size(); iport++) {
+                    int foundPort = -1;
+                    for (int iport = 0; iport < inputTo.second->insData.size(); iport++) {
                         if (inputTo.second->insData[iport].lock() == outData) {
                             foundPort = iport;
                         }
@@ -171,63 +198,10 @@ void NetworkSerializer::serialize(
         }
     }
 
-
-    InputsDataMap inputInfo;
-    network.getInputsInfo(inputInfo);
-
-    // assuming that we have preprocess only for one input
-    for (auto ii : inputInfo) {
-        const PreProcessInfo& pp = ii.second->getPreProcess();
-        size_t  nInChannels = pp.getNumberOfChannels();
-        if (nInChannels) {
-            pugi::xml_node preproc = net.append_child("pre-process");
-
-            preproc.append_attribute("reference-layer-name").set_value(ii.first.c_str());
-            preproc.append_attribute("mean-precision").set_value(Precision(Precision::FP32).name());
-
-            for (size_t ch = 0; ch < nInChannels; ch++) {
-                const PreProcessChannel::Ptr &preProcessChannel = pp[ch];
-                auto channel = preproc.append_child("channel");
-                channel.append_attribute("id").set_value(ch);
-
-                auto mean = channel.append_child("mean");
-
-                if (!preProcessChannel->meanData) {
-                    mean.append_attribute("value").set_value(preProcessChannel->meanValue);
-                } else {
-                    THROW_IE_EXCEPTION << "Mean data is not supported yet for serialization of the model";
-                }
-            }
-        }
-    }
-
-
-    // adding statistic to the file if statistic exists
-    ICNNNetworkStats* netNodesStats = nullptr;
-    auto stats = net.append_child("statistics");
-    network.getStats(&netNodesStats, nullptr);
-    const NetworkStatsMap statsmap =  netNodesStats->getNodesStats();
-
-    auto joinCommas = [&](const std::vector<float>& v) -> std::string {
-        std::string res;
-
-        for (size_t i = 0; i < v.size(); ++i) {
-            res += std::to_string(v[i]);
-            if (i < v.size() - 1) {
-                res += ", ";
-            }
-        }
-
-        return res;
-    };
-
-    for (const auto itStats : statsmap) {
-        auto layer = stats.append_child("layer");
-
-        layer.append_child("name").text().set(itStats.first.c_str());
-
-        layer.append_child("min").text().set(joinCommas(itStats.second->_minOutputs).c_str());
-        layer.append_child("max").text().set(joinCommas(itStats.second->_maxOutputs).c_str());
+    // no need to print this info in case of executable graph info serialization
+    if (!execGraphInfoSerialization) {
+        updatePreProcInfo(network, netXml);
+        updateStatisticsInfo(network, netXml);
     }
 
     if (!doc.save_file(xmlPath.c_str())) {
@@ -235,20 +209,19 @@ void NetworkSerializer::serialize(
     }
 }
 
-
-void NetworkSerializer::updateStdLayerParams(const CNNLayer::Ptr layer) {
+void NetworkSerializer::updateStdLayerParams(const CNNLayer::Ptr &layer) {
     auto layerPtr = layer.get();
     auto &params = layer->params;
 
     if (CaselessEq<std::string>()(layer->type, "power")) {
-        PowerLayer *lr = dynamic_cast<PowerLayer *>(layerPtr);
+        auto *lr = dynamic_cast<PowerLayer *>(layerPtr);
 
         params["scale"] = std::to_string(lr->scale);
         params["shift"] = std::to_string(lr->offset);
         params["power"] = std::to_string(lr->power);
     } else if (CaselessEq<std::string>()(layer->type, "convolution") ||
-        CaselessEq<std::string>()(layer->type, "deconvolution")) {
-        ConvolutionLayer *lr = dynamic_cast<ConvolutionLayer *>(layerPtr);
+               CaselessEq<std::string>()(layer->type, "deconvolution")) {
+        auto *lr = dynamic_cast<ConvolutionLayer *>(layerPtr);
 
         params["kernel"] = arrayRevertToIRProperty(lr->_kernel);
         params["pads_begin"] = arrayRevertToIRProperty(lr->_padding);
@@ -258,20 +231,20 @@ void NetworkSerializer::updateStdLayerParams(const CNNLayer::Ptr layer) {
         params["output"] = std::to_string(lr->_out_depth);
         params["group"] = std::to_string(lr->_group);
     } else if (CaselessEq<std::string>()(layer->type, "relu")) {
-        ReLULayer *lr = dynamic_cast<ReLULayer *>(layerPtr);
+        auto *lr = dynamic_cast<ReLULayer *>(layerPtr);
         if (lr->negative_slope != 0.0f) {
             params["negative_slope"] = std::to_string(lr->negative_slope);
         }
     } else if (CaselessEq<std::string>()(layer->type, "norm") ||
-        CaselessEq<std::string>()(layer->type, "lrn")) {
-        NormLayer *lr = dynamic_cast<NormLayer *>(layerPtr);
+               CaselessEq<std::string>()(layer->type, "lrn")) {
+        auto *lr = dynamic_cast<NormLayer *>(layerPtr);
 
         params["alpha"] = std::to_string(lr->_alpha);
         params["beta"] = std::to_string(lr->_beta);
         params["local-size"] = std::to_string(lr->_size);
         params["region"] = lr->_isAcrossMaps ? "across" : "same";
     } else if (CaselessEq<std::string>()(layer->type, "pooling")) {
-        PoolingLayer *lr = dynamic_cast<PoolingLayer *>(layerPtr);
+        auto *lr = dynamic_cast<PoolingLayer *>(layerPtr);
 
         params["kernel"] = arrayRevertToIRProperty(lr->_kernel);
         params["pads_begin"] = arrayRevertToIRProperty(lr->_padding);
@@ -279,85 +252,85 @@ void NetworkSerializer::updateStdLayerParams(const CNNLayer::Ptr layer) {
         params["strides"] = arrayRevertToIRProperty(lr->_stride);
 
         switch (lr->_type) {
-        case PoolingLayer::MAX:
-            params["pool-method"] = "max";
-            break;
-        case PoolingLayer::AVG:
-            params["pool-method"] = "avg";
-            break;
-
-        default:
-            THROW_IE_EXCEPTION << "Found unsupported pooling method: " << lr->_type;
+            case PoolingLayer::MAX:
+                params["pool-method"] = "max";
+                break;
+            case PoolingLayer::AVG:
+                params["pool-method"] = "avg";
+                break;
+
+            default:
+                THROW_IE_EXCEPTION << "Found unsupported pooling method: " << lr->_type;
         }
     } else if (CaselessEq<std::string>()(layer->type, "split")) {
-        SplitLayer *lr = dynamic_cast<SplitLayer *>(layerPtr);
+        auto *lr = dynamic_cast<SplitLayer *>(layerPtr);
         params["axis"] = std::to_string(lr->_axis);
     } else if (CaselessEq<std::string>()(layer->type, "concat")) {
-        ConcatLayer *lr = dynamic_cast<ConcatLayer *>(layerPtr);
+        auto *lr = dynamic_cast<ConcatLayer *>(layerPtr);
         params["axis"] = std::to_string(lr->_axis);
     } else if (CaselessEq<std::string>()(layer->type, "FullyConnected") ||
-        CaselessEq<std::string>()(layer->type, "InnerProduct")) {
-        FullyConnectedLayer *lr = dynamic_cast<FullyConnectedLayer *>(layerPtr);
+               CaselessEq<std::string>()(layer->type, "InnerProduct")) {
+        auto *lr = dynamic_cast<FullyConnectedLayer *>(layerPtr);
         params["out-size"] = std::to_string(lr->_out_num);
     } else if (CaselessEq<std::string>()(layer->type, "softmax")) {
-        SoftMaxLayer *lr = dynamic_cast<SoftMaxLayer *>(layerPtr);
+        auto *lr = dynamic_cast<SoftMaxLayer *>(layerPtr);
         params["axis"] = std::to_string(lr->axis);
     } else if (CaselessEq<std::string>()(layer->type, "reshape")) {
         // need to add here support of flatten layer if it is created from API
-        ReshapeLayer *lr = dynamic_cast<ReshapeLayer *>(layerPtr);
+        auto *lr = dynamic_cast<ReshapeLayer *>(layerPtr);
         params["dim"] = arrayToIRProperty(lr->shape);
     } else if (CaselessEq<std::string>()(layer->type, "Eltwise")) {
-        EltwiseLayer *lr = dynamic_cast<EltwiseLayer *>(layerPtr);
+        auto *lr = dynamic_cast<EltwiseLayer *>(layerPtr);
 
         std::string op;
 
         switch (lr->_operation) {
-        case EltwiseLayer::Sum:
-            op = "sum";
-            break;
-        case EltwiseLayer::Prod:
-            op = "prod";
-            break;
-        case EltwiseLayer::Max:
-            op = "max";
-            break;
-        default:
-            break;
+            case EltwiseLayer::Sum:
+                op = "sum";
+                break;
+            case EltwiseLayer::Prod:
+                op = "prod";
+                break;
+            case EltwiseLayer::Max:
+                op = "max";
+                break;
+            default:
+                break;
         }
 
         params["operation"] = op;
     } else if (CaselessEq<std::string>()(layer->type, "scaleshift")) {
-        ScaleShiftLayer *lr = dynamic_cast<ScaleShiftLayer *>(layerPtr);
+        auto *lr = dynamic_cast<ScaleShiftLayer *>(layerPtr);
         params["broadcast"] = std::to_string(lr->_broadcast);
     } else if (CaselessEq<std::string>()(layer->type, "crop")) {
-        CropLayer *lr = dynamic_cast<CropLayer *>(layerPtr);
+        auto *lr = dynamic_cast<CropLayer *>(layerPtr);
         params["axis"] = arrayToIRProperty(lr->axis);
         params["offset"] = arrayToIRProperty(lr->offset);
         params["dim"] = arrayToIRProperty(lr->dim);
     } else if (CaselessEq<std::string>()(layer->type, "tile")) {
-        TileLayer *lr = dynamic_cast<TileLayer *>(layerPtr);
+        auto *lr = dynamic_cast<TileLayer *>(layerPtr);
         params["axis"] = std::to_string(lr->axis);
         params["tiles"] = std::to_string(lr->tiles);
     } else if (CaselessEq<std::string>()(layer->type, "prelu")) {
-        PReLULayer *lr = dynamic_cast<PReLULayer *>(layerPtr);
+        auto *lr = dynamic_cast<PReLULayer *>(layerPtr);
         params["channel_shared"] = std::to_string(lr->_channel_shared);
     } else if (CaselessEq<std::string>()(layer->type, "clamp")) {
-        ClampLayer *lr = dynamic_cast<ClampLayer *>(layerPtr);
+        auto *lr = dynamic_cast<ClampLayer *>(layerPtr);
         params["min"] = std::to_string(lr->min_value);
         params["max"] = std::to_string(lr->max_value);
     } else if (CaselessEq<std::string>()(layer->type, "BatchNormalization")) {
-        BatchNormalizationLayer *lr = dynamic_cast<BatchNormalizationLayer *>(layerPtr);
+        auto *lr = dynamic_cast<BatchNormalizationLayer *>(layerPtr);
         params["epsilon"] = std::to_string(lr->epsilon);
     } else if (CaselessEq<std::string>()(layer->type, "grn")) {
-        GRNLayer *lr = dynamic_cast<GRNLayer *>(layerPtr);
+        auto *lr = dynamic_cast<GRNLayer *>(layerPtr);
         params["bias"] = std::to_string(lr->bias);
     } else if (CaselessEq<std::string>()(layer->type, "mvn")) {
-        MVNLayer *lr = dynamic_cast<MVNLayer *>(layerPtr);
+        auto *lr = dynamic_cast<MVNLayer *>(layerPtr);
         params["across_channels"] = std::to_string(lr->across_channels);
         params["normalize_variance"] = std::to_string(lr->normalize);
     } else if (CaselessEq<std::string>()(layer->type, "rnn") ||
-        CaselessEq<std::string>()(layer->type, "TensorIterator") ||
-        CaselessEq<std::string>()(layer->type, "LSTMCell")) {
+               CaselessEq<std::string>()(layer->type, "TensorIterator") ||
+               CaselessEq<std::string>()(layer->type, "LSTMCell")) {
         THROW_IE_EXCEPTION << "Not covered layers for writing to IR";
     }
 
@@ -365,9 +338,8 @@ void NetworkSerializer::updateStdLayerParams(const CNNLayer::Ptr layer) {
         params["quantization_level"] = layer->params["quantization_level"];
     }
 
-
     // update of weightable layers
-    WeightableLayer *pwlayer = dynamic_cast<WeightableLayer *>(layerPtr);
+    auto *pwlayer = dynamic_cast<WeightableLayer *>(layerPtr);
     if (pwlayer) {
         if (pwlayer->_weights) {
             pwlayer->blobs["weights"] = pwlayer->_weights;
@@ -377,3 +349,64 @@ void NetworkSerializer::updateStdLayerParams(const CNNLayer::Ptr layer) {
         }
     }
 }
+
+void NetworkSerializer::updatePreProcInfo(const InferenceEngine::ICNNNetwork& network, pugi::xml_node &netXml) {
+    InputsDataMap inputInfo;
+    network.getInputsInfo(inputInfo);
+
+    // Assume that you preprocess only one input
+    for (auto ii : inputInfo) {
+        const PreProcessInfo &pp = ii.second->getPreProcess();
+        size_t nInChannels = pp.getNumberOfChannels();
+        if (nInChannels) {
+            pugi::xml_node preproc = netXml.append_child("pre-process");
+
+            preproc.append_attribute("reference-layer-name").set_value(ii.first.c_str());
+            preproc.append_attribute("mean-precision").set_value(Precision(Precision::FP32).name());
+
+            for (size_t ch = 0; ch < nInChannels; ch++) {
+                const PreProcessChannel::Ptr &preProcessChannel = pp[ch];
+                auto channel = preproc.append_child("channel");
+                channel.append_attribute("id").set_value(ch);
+
+                auto mean = channel.append_child("mean");
+
+                if (!preProcessChannel->meanData) {
+                    mean.append_attribute("value").set_value(preProcessChannel->meanValue);
+                } else {
+                    THROW_IE_EXCEPTION << "Mean data is not supported yet for serialization of the model";
+                }
+            }
+        }
+    }
+}
+
+void NetworkSerializer::updateStatisticsInfo(const InferenceEngine::ICNNNetwork& network, pugi::xml_node &netXml) {
+    // If statistics exists, add it to the file
+    ICNNNetworkStats *netNodesStats = nullptr;
+    auto stats = netXml.append_child("statistics");
+    network.getStats(&netNodesStats, nullptr);
+    const NetworkStatsMap statsmap = netNodesStats->getNodesStats();
+
+    auto joinCommas = [&](const std::vector<float> &v) -> std::string {
+        std::string res;
+
+        for (size_t i = 0; i < v.size(); ++i) {
+            res += std::to_string(v[i]);
+            if (i < v.size() - 1) {
+                res += ", ";
+            }
+        }
+
+        return res;
+    };
+
+    for (const auto &itStats : statsmap) {
+        auto layer = stats.append_child("layer");
+
+        layer.append_child("name").text().set(itStats.first.c_str());
+
+        layer.append_child("min").text().set(joinCommas(itStats.second->_minOutputs).c_str());
+        layer.append_child("max").text().set(joinCommas(itStats.second->_maxOutputs).c_str());
+    }
+}
\ No newline at end of file