Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / src / mkldnn_plugin / nodes / mkldnn_activation_node.cpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #include "mkldnn_activation_node.h"
6 #include "desc_iterator.hpp"
7 #include <ie_layers.h>
8 #include <algorithm>
9 #include <string>
10 #include <mkldnn_extension_utils.h>
11 #include "details/caseless.hpp"
12
13 using namespace mkldnn;
14 using namespace MKLDNNPlugin;
15 using namespace InferenceEngine;
16 using namespace InferenceEngine::details;
17
18 // TODO: (ichuraev) I don't fully sure that names of types and parameters are correct for square, abs, sqrt, linear, bounded_relu and soft_relu
19 caseless_map<std::string, std::function<void(GenericLayer*, mkldnn::algorithm&, float&, float&)>> MKLDNNActivationNode::initializers = {
20         {"relu", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
21             alpha = activationLayer->GetParamAsFloat("negative_slope", 0.0f);
22             beta = 0.0f;
23             algorithm = eltwise_relu;
24         }},
25         {"elu", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
26             alpha = activationLayer->GetParamAsFloat("alpha", 1.0f);
27             beta = 0.0f;
28             algorithm = eltwise_elu;
29         }},
30         {"tanh", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
31             alpha = 0.0f;
32             beta = 0.0f;
33             algorithm = eltwise_tanh;
34         }},
35         {"logistic", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
36             alpha = 0.0f;
37             beta = 0.0f;
38             algorithm = eltwise_logistic;
39         }},
40         {"square", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
41             alpha = 0.0f;
42             beta = 0.0f;
43             algorithm = eltwise_square;
44         }},
45         {"abs", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
46             alpha = 0.0f;
47             beta = 0.0f;
48             algorithm = eltwise_abs;
49         }},
50         {"sqrt", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
51             alpha = 0.0f;
52             beta = 0.0f;
53             algorithm = eltwise_sqrt;
54         }},
55         {"linear", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
56             alpha = activationLayer->GetParamAsFloat("alpha", 1.0f);
57             beta = activationLayer->GetParamAsFloat("beta", 0.0f);
58             algorithm = eltwise_linear;
59         }},
60         {"bounded_relu", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
61             alpha = activationLayer->GetParamAsFloat("alpha", 0.0f);
62             beta = 0.0f;
63             algorithm = eltwise_bounded_relu;
64         }},
65         {"soft_relu", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
66             alpha = 0.0f;
67             beta = 0.0f;
68             algorithm = eltwise_soft_relu;
69         }},
70         {"relu6", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
71             alpha = activationLayer->GetParamAsFloat("n", 6.0f);
72             beta = 0.0f;
73             algorithm = eltwise_bounded_relu;
74         }},
75         {"clamp", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
76             alpha = activationLayer->GetParamAsFloat("max", 1.0f);
77             beta = activationLayer->GetParamAsFloat("min", 0.0f);
78             algorithm = eltwise_clamp;
79         }},
80         {"exp", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
81             alpha = 0.0f;
82             beta = 0.0f;
83             algorithm = eltwise_exp;
84         }},
85         {"not", [](GenericLayer* activationLayer, mkldnn::algorithm& algorithm, float& alpha, float& beta) {
86             alpha = 0.0f;
87             beta = 0.0f;
88             algorithm = eltwise_not;
89         }}
90 };
91
92 MKLDNNActivationNode::MKLDNNActivationNode(const InferenceEngine::CNNLayerPtr& layer, const mkldnn::engine& eng) : MKLDNNNode(layer, eng) {}
93
94 void MKLDNNActivationNode::getSupportedDescriptors() {
95     if (!descs.empty())
96         return;
97
98     if (getParentEdges().size() != 1)
99         THROW_IE_EXCEPTION << "Incorrect number of input edges for layer " << getName();
100     if (!getChildEdges().size())
101         THROW_IE_EXCEPTION << "Incorrect number of output edges for layer " << getName();
102
103     auto parentOutDims = getParentEdgeAt(0)->getDims();
104
105     InferenceEngine::Precision precision = getCnnLayer()->insData[0].lock()->getPrecision();
106
107     // FIXME: MKLDNN doesn't support not inputs with number of dimensions less than 4 for activation
108     while (parentOutDims.ndims() < 4)
109         parentOutDims.push_back(1);
110     for (auto format : getAvailableFormatsForDims(parentOutDims)) {
111         MKLDNNMemoryDesc in_candidate(parentOutDims, MKLDNNExtensionUtils::IEPrecisionToDataType(precision), format);
112         createDescriptor({in_candidate}, {});
113     }
114 }
115
116 void MKLDNNActivationNode::createPrimitive() {
117     if (prim)
118         return;
119
120     auto prim_desc = createPrimitiveDescriptor<eltwise_forward::primitive_desc, eltwise_forward::desc>();
121
122     prim.reset(new eltwise_forward(prim_desc, getParentEdgeAt(0)->getMemory().GetPrimitive(),
123                                 getChildEdgeAt(0)->getMemory().GetPrimitive()));
124 }
125
126 bool MKLDNNActivationNode::created() const {
127     return getType() == Activation;
128 }
129
130 void MKLDNNActivationNode::initValues() {
131     GenericLayer* activationLayer = getCnnLayer().get();
132     if (activationLayer == nullptr)
133         THROW_IE_EXCEPTION << "Cannot get CNNLayer.";
134
135     std::string type = activationLayer->type;
136     CaselessEq<std::string> comparator;
137     if (comparator(type, "activation"))
138         type = activationLayer->GetParamAsString("type");
139     if (comparator(type, "sigmoid"))
140         type = "logistic";
141
142     if (initializers.find(type) == initializers.end())
143         THROW_IE_EXCEPTION << "Node " << getName() << " has unsupported activation primitive: "
144                            << activationLayer->type << " : " << type;
145     initializers[type](activationLayer, algorithm, alpha, beta);
146     initialized = true;
147 }
148
149 void MKLDNNActivationNode::createDescriptor(const std::vector<InferenceEngine::TensorDesc> &inputDesc,
150                                             const std::vector<InferenceEngine::TensorDesc> &outputDesc) {
151     MKLDNNMemoryDesc inDesc(inputDesc[0]);
152     MKLDNNDescriptor desc(std::shared_ptr<eltwise_forward::desc>(
153             new eltwise_forward::desc(prop_kind::forward_scoring, getAlgorithm(), inDesc, getAlpha(), getBeta())));
154     descs.push_back(desc);
155 }
156
157 void MKLDNNActivationNode::initOptimalPrimitiveDescriptor() {
158     auto config = getSelectedPrimitiveDescriptor()->getConfig();
159     if (isInitConfig(config))
160         return;
161
162     if (config.inConfs.size() != 1 || config.outConfs.size() != 1 ||
163             (!isUninitTensorDesc(config.inConfs[0].desc) &&
164                     !isUninitTensorDesc(config.outConfs[0].desc) && config.inConfs[0].desc != config.outConfs[0].desc))
165         THROW_IE_EXCEPTION << "Layer " << getName() << " has incorrect selected config!";
166
167     if (!isUninitTensorDesc(config.inConfs[0].desc)) {
168         config.outConfs[0].desc = config.inConfs[0].desc;
169     } else if (!isUninitTensorDesc(config.outConfs[0].desc)) {
170         config.inConfs[0].desc = config.outConfs[0].desc;
171     } else {
172         config.outConfs[0].desc = config.inConfs[0].desc = getConfiguredInputDesc(config, 0);
173     }
174
175     initDescriptor(config);
176 }
177
178 MKLDNNMemoryDesc MKLDNNActivationNode::getSrcMemDesc(mkldnn::primitive_desc_iterator &primitive_desc_it, size_t idx) {
179     InferenceEngine::TensorDesc desc = MKLDNNMemoryDesc(primitive_desc_it.src_primitive_desc(idx).desc());
180
181     auto parentOutDims = getParentEdgeAt(idx)->getDims().ToSizeVector();
182
183     SizeVector blocked_dims, order, dimOffsets, strides;
184     size_t offset = desc.getBlockingDesc().getOffsetPadding();
185
186     for (size_t i = 0; i < desc.getBlockingDesc().getStrides().size(); i++) {
187         if (desc.getBlockingDesc().getOrder()[i] >= parentOutDims.size())
188             continue;
189
190         blocked_dims.push_back(desc.getBlockingDesc().getBlockDims()[i]);
191         order.push_back(desc.getBlockingDesc().getOrder()[i]);
192         dimOffsets.push_back(desc.getBlockingDesc().getOffsetPaddingToData()[i]);
193         strides.push_back(desc.getBlockingDesc().getStrides()[i]);
194     }
195     if (desc.getLayout() == InferenceEngine::Layout::ANY)
196         return MKLDNNMemoryDesc(InferenceEngine::TensorDesc(desc.getPrecision(),
197                                                             parentOutDims,
198                                                             desc.getLayout()));
199     else
200         return MKLDNNMemoryDesc(InferenceEngine::TensorDesc(desc.getPrecision(),
201                                                             parentOutDims,
202                                                             {blocked_dims, order, offset, dimOffsets, strides}));
203 }
204
205 MKLDNNMemoryDesc MKLDNNActivationNode::getDstMemDesc(mkldnn::primitive_desc_iterator &primitive_desc_it, size_t idx) {
206     InferenceEngine::TensorDesc desc = MKLDNNMemoryDesc(primitive_desc_it.dst_primitive_desc(idx).desc());
207
208     auto childInDims = getChildEdgeAt(idx)->getDims().ToSizeVector();
209
210     SizeVector blocked_dims, order, dimOffsets, strides;
211     size_t offset = desc.getBlockingDesc().getOffsetPadding();
212
213     for (size_t i = 0; i < desc.getBlockingDesc().getStrides().size(); i++) {
214         if (desc.getBlockingDesc().getOrder()[i] >= childInDims.size())
215             continue;
216
217         blocked_dims.push_back(desc.getBlockingDesc().getBlockDims()[i]);
218         order.push_back(desc.getBlockingDesc().getOrder()[i]);
219         dimOffsets.push_back(desc.getBlockingDesc().getOffsetPaddingToData()[i]);
220         strides.push_back(desc.getBlockingDesc().getStrides()[i]);
221     }
222     if (desc.getLayout() == InferenceEngine::Layout::ANY)
223         return MKLDNNMemoryDesc(InferenceEngine::TensorDesc(desc.getPrecision(),
224                                                             childInDims,
225                                                             desc.getLayout()));
226     else
227         return MKLDNNMemoryDesc(InferenceEngine::TensorDesc(desc.getPrecision(),
228                                                             childInDims,
229                                                             {blocked_dims, order, offset, dimOffsets, strides}));
230 }