1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
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"
17 #include <shape_infer/const_infer/ie_const_infer_holder.hpp>
19 namespace InferenceEngine {
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);
42 layer->insData.clear();
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;
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);
60 network->removeData(outData->name);
62 network->removeLayer(layer->name);
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;
68 auto outData = layer->outData[0];
69 for (const auto& inputTo : outData->getInputTo()) {
70 if (constLayers.find(inputTo.first) != constLayers.end()) {
71 keepConstData = false;
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;
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",
89 auto newLayer = std::make_shared<CNNLayer>(layerParams);
90 for (const auto& data : layer->outData) {
91 data->creatorLayer = newLayer;
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);
99 // Layer with `Const` type should be also considered on trimming shape inputs
100 remainingConstLayers.push_back(layer->name);
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;
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);
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",
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;
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);
139 for (const auto& data : layer->outData) {
140 network->removeData(data->name);
142 network->removeLayer(layer->name);
147 return remainingConstLayers;
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;
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;
165 if (isAllInputsConst && !layer->insData.empty()) mapConstLayers[layer->name] = false;
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();
180 creatorName = creator->name;
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;
193 if (isCurrentConst) {
194 if (mapConstLayers.at(currentLayerName)) {
195 if (!mapConstLayers[creatorName]) {
196 if (!mapVisitedLayers.at(creatorName)) {
197 mapConstLayers[creatorName] = true;
201 mapConstLayers[creatorName] = false;
204 mapConstLayers[creatorName] = false;
208 mapVisitedLayers[creatorName] = true;
210 mapVisitedLayers[currentLayerName] = true;
212 return mapConstLayers;
215 const BlobMap ConstTransformer::getConstData(const std::map<std::string, bool>& constLayers, const std::vector<CNNLayerPtr>& sortedLayers) {
216 ShapeInfer::ConstInferHolder holder;
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()) {
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));
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);
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());
244 outputBlobs.push_back(blob);
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";
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];
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;
289 if (it != insData.end() && std::distance(insData.begin(), it) == 1) {
290 inputToLayer->insData.erase(it);
291 constData->getInputTo().erase(inputTo.first);
295 if (constData->inputTo.empty()) {
296 network->removeData(constData->name);
297 network->removeLayer(layer->name);
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);
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);
318 } // namespace InferenceEngine