Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / src / inference_engine / graph_transformer.cpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #include <cpp/ie_cnn_network.h>
6 #include <details/ie_cnn_network_tools.h>
7 #include <details/caseless.hpp>
8 #include "graph_transformer.h"
9 #include "cnn_network_impl.hpp"
10 #include "blob_factory.hpp"
11 #include "graph_tools.hpp"
12 #include <vector>
13 #include <string>
14 #include <iterator>
15 #include <map>
16 #include <memory>
17 #include <shape_infer/const_infer/ie_const_infer_holder.hpp>
18
19 namespace InferenceEngine {
20
21 std::vector<std::string>
22 ConstTransformer::foldConstSubgraphsInternal(const std::map<std::string, bool>& constLayers, const BlobMap& constData,
23                                                const std::vector<CNNLayerPtr>& sortedLayers) {
24     std::vector<std::string> remainingConstLayers;
25     for (const auto& layer : sortedLayers) {
26         if (constLayers.find(layer->name) != constLayers.end()) {
27             // const layer doesn't need parent connections -> erase them
28             for (const auto& insData : layer->insData) {
29                 auto& inputTo = insData.lock()->getInputTo();
30                 inputTo.erase(layer->name);
31                 // Notr: to resolve corner case above layers can be marked as const with const data, just to be removed properly..
32                 // and maybe this logic wouldn't be needed
33                 if (inputTo.empty()) {
34                     auto creator = insData.lock()->creatorLayer.lock();
35                     auto it = std::find(creator->outData.begin(), creator->outData.end(), insData.lock());
36                     if (it != creator->outData.end()) {
37                         network->removeData((*it)->name);
38                         creator->outData.erase(it);
39                     }
40                 }
41             }
42             layer->insData.clear();
43
44             if (constLayers.at(layer->name)) {
45                 for (const auto& outData : layer->outData) {
46                     for (const auto& inputTo : outData->getInputTo()) {
47                         CNNLayerPtr inputToLayer;
48                         std::string inputToName;
49                         std::tie(inputToName, inputToLayer) = inputTo;
50                         auto& insData = inputToLayer->insData;
51                         auto insDataIt = std::find_if(insData.begin(), insData.end(),
52                                                       [&outData](const DataWeakPtr& current) {
53                                                           return current.lock()->name == outData->name;
54                                                       });
55                         // remove connection with const data, because for const child it's not needed, for dynamic - new one will be created
56                         if (insDataIt != insData.end()) {
57                             insDataIt = inputToLayer->insData.erase(insDataIt);
58                         }
59                     }
60                     network->removeData(outData->name);
61                 }
62                 network->removeLayer(layer->name);
63             } else {
64                 // if only one output data is not const - do nothing, otherwise - run procedure below
65                 // note: multiple const output data requires multiple layers with blob["custom"] to keep const data
66                 bool keepConstData = layer->outData.size() == 1;
67                 if (keepConstData) {
68                     auto outData = layer->outData[0];
69                     for (const auto& inputTo : outData->getInputTo()) {
70                         if (constLayers.find(inputTo.first) != constLayers.end()) {
71                             keepConstData = false;
72                         }
73                     }
74                 }
75                 if (keepConstData) {
76                     if (!constLayers.at(layer->name)) {
77                         auto outData = layer->outData[0];
78                         if (layer->blobs.find("custom") == layer->blobs.end()) {
79                             // if there's no const data - set it
80                             const auto it = constData.find(outData->name);
81                             if (it != constData.end()) {
82                                 layer->blobs["custom"] = it->second;
83                             }
84                         }
85                         if (layer->type != "Const") {
86                             // layer was calculated during the Const Propagation, need to hide its semantic (type, params)
87                             LayerParams layerParams{layer->name + "__" + outData->name + "__Const", "Const",
88                                                     layer->precision};
89                             auto newLayer = std::make_shared<CNNLayer>(layerParams);
90                             for (const auto& data : layer->outData) {
91                                 data->creatorLayer = newLayer;
92                             }
93                             newLayer->outData = layer->outData;
94                             newLayer->blobs["custom"] = layer->blobs["custom"];
95                             network->removeLayer(layer->name);
96                             network->addLayer(newLayer);
97                             remainingConstLayers.push_back(newLayer->name);
98                         } else {
99                             // Layer with `Const` type should be also considered on trimming shape inputs
100                             remainingConstLayers.push_back(layer->name);
101                         }
102                     }
103                 } else {
104                     for (const auto& outData : layer->outData) {
105                         for (const auto& inputTo : outData->getInputTo()) {
106                             CNNLayerPtr inputToLayer;
107                             std::string inputToName;
108                             std::tie(inputToName, inputToLayer) = inputTo;
109                             auto& insData = inputToLayer->insData;
110                             auto insDataIt = std::find_if(insData.begin(), insData.end(),
111                                                           [&outData](const DataWeakPtr& current) {
112                                                               return current.lock()->name == outData->name;
113                                                           });
114                             // remove connection with const data, because for const child it's not needed, for dynamic - new one will be created
115                             if (insDataIt != insData.end()) {
116                                 insDataIt = inputToLayer->insData.erase(insDataIt);
117                             }
118                             if (constLayers.find(inputToName) == constLayers.end()) {
119                                 // next layer is not const, need to attach const data to it via blobs["custom"] of new Const layer
120                                 LayerParams layerParams{layer->name + "__" + outData->name + "__Const", "Const",
121                                                         layer->precision};
122                                 auto newLayer = std::make_shared<CNNLayer>(layerParams);
123                                 remainingConstLayers.push_back(newLayer->name);
124                                 const auto it = constData.find(outData->name);
125                                 if (it != constData.end()) {
126                                     newLayer->blobs["custom"] = it->second;
127                                 }
128                                 auto newData = std::make_shared<Data>(outData->name + "__" + inputToName,
129                                                                       outData->getTensorDesc());
130                                 newData->creatorLayer = newLayer;
131                                 newData->inputTo[inputToName] = inputToLayer;
132                                 newLayer->outData = {newData};
133                                 network->addLayer(newLayer);
134                                 network->getData(newData->name) = newData;
135                                 inputToLayer->insData.insert(insDataIt, newData);
136                             }
137                         }
138                     }
139                     for (const auto& data : layer->outData) {
140                         network->removeData(data->name);
141                     }
142                     network->removeLayer(layer->name);
143                 }
144             }
145         }
146     }
147     return remainingConstLayers;
148 }
149
150 const std::map<std::string, bool> ConstTransformer::getConstLayers(const std::vector<CNNLayerPtr>& sortedLayers) {
151     std::map<std::string, bool> mapConstLayers;
152     // collect all const layers, which inputs are const layers.
153     for (const auto& layer : sortedLayers) {
154         // Layers with "Shape" and "Const" type are Const by definition
155         if (layer->type == "Shape" || layer->type == "Const") {
156             mapConstLayers[layer->name] = false;
157         } else {
158             bool isAllInputsConst = true;
159             for (auto const& data : layer->insData) {
160                 auto creatorName = data.lock()->creatorLayer.lock()->name;
161                 if (mapConstLayers.find(creatorName) == mapConstLayers.end()) {
162                     isAllInputsConst = false;
163                 }
164             }
165             if (isAllInputsConst && !layer->insData.empty()) mapConstLayers[layer->name] = false;
166         }
167     }
168     // Add mark for const layers, if it's used for shape taking layers as second input
169     // true - is used and can be deleted from graph, as no influence on data, false - opposite
170     std::map<std::string, bool> mapVisitedLayers = mapConstLayers;
171     for (auto rit = sortedLayers.rbegin(); rit != sortedLayers.rend(); rit++) {
172         auto currentLayer = (*rit);
173         std::string currentLayerName = currentLayer->name;
174         bool isCurrentConst = mapConstLayers.find(currentLayerName) != mapConstLayers.end();
175         for (int i = 0; i < currentLayer->insData.size(); i++) {
176             std::string creatorName;
177             if (currentLayer->insData[i].lock()) {
178                 auto creator = currentLayer->insData[i].lock()->creatorLayer.lock();
179                 if (creator) {
180                     creatorName = creator->name;
181                 }
182             }
183             bool isCreatorConst = mapConstLayers.find(creatorName) != mapConstLayers.end();
184             if (isCreatorConst) {
185                 // mark second const input of shape taking layers (Reshape, Interp..), if they wasn't visited before
186                 if ((i == 1) && (shapeTaking.find(currentLayer->type)) != shapeTaking.end()) {
187                     if (!mapConstLayers[creatorName]) {
188                         if (!mapVisitedLayers.at(creatorName)) {
189                             mapConstLayers[creatorName] = true;
190                         }
191                     }
192                 } else {
193                     if (isCurrentConst) {
194                         if (mapConstLayers.at(currentLayerName)) {
195                             if (!mapConstLayers[creatorName]) {
196                                 if (!mapVisitedLayers.at(creatorName)) {
197                                     mapConstLayers[creatorName] = true;
198                                 }
199                             }
200                         } else {
201                             mapConstLayers[creatorName] = false;
202                         }
203                     } else {
204                         mapConstLayers[creatorName] = false;
205                     }
206                 }
207             }
208             mapVisitedLayers[creatorName] = true;
209         }
210         mapVisitedLayers[currentLayerName] = true;
211     }
212     return mapConstLayers;
213 }
214
215 const BlobMap ConstTransformer::getConstData(const std::map<std::string, bool>& constLayers, const std::vector<CNNLayerPtr>& sortedLayers) {
216     ShapeInfer::ConstInferHolder holder;
217     BlobMap constData;
218     auto getInputBlobs = [&constData](const std::vector<DataWeakPtr>& insData,
219                                       bool isForShape) -> std::vector<Blob::CPtr> {
220         std::vector<Blob::CPtr> inputBlobs;
221         // special case of Const layers: no inputs, no input blobs
222         if (insData.empty()) {
223             return {};
224         }
225         for (const auto& data : insData) {
226             std::string dataName = data.lock()->name;
227             if (constData.find(dataName) != constData.end()) {
228                 // get blobs, inferred before
229                 inputBlobs.push_back(constData.at(dataName));
230             } else {
231                 // special case of Shape layer: no input data, but blob contains info about dimensions, layout and etc...
232                 auto blob = make_blob_with_precision(data.lock()->getTensorDesc());
233                 inputBlobs.push_back(blob);
234             }
235         }
236         return inputBlobs;
237     };
238
239     auto getOutputBlobs = [](const std::vector<DataPtr>& outData) -> std::vector<Blob::Ptr> {
240         std::vector<Blob::Ptr> outputBlobs;
241         for (const auto& data : outData) {
242             auto blob = make_blob_with_precision(data->getTensorDesc());
243             blob->allocate();
244             outputBlobs.push_back(blob);
245         }
246         return outputBlobs;
247     };
248
249     for (const auto& layer : sortedLayers) {
250         if (constLayers.find(layer->name) != constLayers.end()) {
251             std::string layerName = layer->name;
252             bool isForShape = constLayers.at(layerName);
253             CNNNetwork cnnNetwork(network);
254             auto layer = cnnNetwork.getLayerByName(layerName.c_str());
255             auto implPtr = holder.getConstInferImpl(layer->type);
256             if (!implPtr && !isForShape)
257                 THROW_IE_EXCEPTION << "Failed to find reference implementation for `"
258                                       + layer->name + "` Layer with `" + layer->type + "` Type on constant propagation";
259             if (!isForShape) {
260                 auto outputBlobs = getOutputBlobs(layer->outData);
261                 implPtr->infer(getInputBlobs(layer->insData, isForShape), layer->params, layer->blobs, outputBlobs);
262                 for (int i = 0; i < layer->outData.size(); i++) {
263                     std::string dataName = layer->outData[i]->name;
264                     auto shapes = layer->outData[i]->getTensorDesc().getDims();
265                     outputBlobs[i]->Reshape(SizeVector(shapes.rbegin(), shapes.rend()),
266                                             TensorDesc::getLayoutByDims(shapes));
267                     constData[dataName] = outputBlobs[i];
268                 }
269             }
270         }
271     }
272     return constData;
273 }
274
275 void ConstTransformer::trimShapeInputs(const std::vector<std::string>& constLayers) {
276     for (const auto& layerName : constLayers) {
277         auto layer = cnnNetwork.getLayerByName(layerName.c_str());
278         if (layer->outData.size() == 1 && layer->type == "Const" && layer->insData.empty()) {
279             auto constData = layer->outData[0];
280             std::map<std::string, CNNLayerPtr> inputToMap = constData->getInputTo();
281             for (const auto& inputTo : inputToMap) {
282                 CNNLayerPtr inputToLayer = inputTo.second;
283                 if (shapeTaking.find(inputToLayer->type) != shapeTaking.end()) {
284                     auto& insData = inputToLayer->insData;
285                     auto it = std::find_if(insData.begin(), insData.end(),
286                                            [&constData](const DataWeakPtr& current) {
287                                                return current.lock()->name == constData->name;
288                                            });
289                     if (it != insData.end() && std::distance(insData.begin(), it) == 1) {
290                         inputToLayer->insData.erase(it);
291                         constData->getInputTo().erase(inputTo.first);
292                     }
293                 }
294             }
295             if (constData->inputTo.empty()) {
296                 network->removeData(constData->name);
297                 network->removeLayer(layer->name);
298             }
299         }
300     }
301 }
302
303 void ConstTransformer::foldConstSubgraphs() {
304     auto sortedLayers = details::CNNNetSortTopologically(*network);
305     auto constLayers = getConstLayers(sortedLayers);
306     auto constData = getConstData(constLayers, sortedLayers);
307     foldConstSubgraphsInternal(constLayers, constData, sortedLayers);
308 }
309
310 void ConstTransformer::fullTrim() {
311     auto sortedLayers = details::CNNNetSortTopologically(*network);
312     auto constMapLayers = getConstLayers(sortedLayers);
313     auto constData = getConstData(constMapLayers, sortedLayers);
314     auto constLayers = foldConstSubgraphsInternal(constMapLayers, constData, sortedLayers);
315     trimShapeInputs(constLayers);
316 }
317
318 }  // namespace InferenceEngine