1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
5 #include "ie_util_internal.hpp"
6 #include "graph_tools.hpp"
7 #include "details/caseless.hpp"
8 #include "ie_utils.hpp"
10 #include <ie_layers.h>
13 #include <unordered_set>
14 #include <unordered_map>
23 using namespace InferenceEngine;
24 using namespace details;
28 InferenceEngine::LayerComplexity getComplexity(const InferenceEngine::CNNLayerPtr &layer) {
29 using namespace InferenceEngine;
30 using namespace std::placeholders;
31 auto type = layer->type;
32 auto &outDims = layer->outData[0]->getDims();
33 auto &inDims = layer->insData[0].lock()->getDims();
34 unsigned long flops = 0, params = 0;
36 size_t out_size = accumulate(outDims.begin(), outDims.end(),
37 1u, std::multiplies<size_t>{});
38 size_t in_size = accumulate(inDims.begin(), inDims.end(),
39 1u, std::multiplies<size_t>{});
41 auto eltwise_complexity = [&](CNNLayer &l, size_t flops_rate, size_t params_rate) {
42 flops = flops_rate * out_size;
43 params = params_rate * out_size;
46 auto scale_complexity = [&](CNNLayer &l) {
48 params = 2 * outDims[1];
50 const caseless_unordered_map<std::string,
51 std::function<void(CNNLayer &)>> layerComplexityLookup = {
52 {"Convolution", [&](CNNLayer &l) {
53 auto* conv = dynamic_cast<ConvolutionLayer*>(&l);
54 unsigned long filter_m = conv->_kernel[X_AXIS] * conv->_kernel[Y_AXIS] * (inDims[1] / conv->_group);
55 flops = 2 * out_size * filter_m;
56 params = filter_m * conv->_out_depth + conv->_out_depth;
59 {"Deconvolution", [&](CNNLayer &l) {
60 auto* deconv = dynamic_cast<DeconvolutionLayer*>(&l);
61 unsigned long filter_m = deconv->_kernel[X_AXIS] * deconv->_kernel[Y_AXIS] * (inDims[1] / deconv->_group);
62 flops = 2 * out_size * filter_m;
63 params = filter_m * deconv->_out_depth + deconv->_out_depth;
66 {"FullyConnected", [&](CNNLayer &l) {
67 auto* fc = dynamic_cast<FullyConnectedLayer*>(&l);
68 flops = 2 * in_size * fc->_out_num;
69 params = (in_size + 1) * fc->_out_num;
72 {"Norm", [&](CNNLayer &l) {
73 auto* lrn = dynamic_cast<NormLayer*>(&l);
74 int size = lrn->_size;
75 int flopsPerElement = lrn->_isAcrossMaps ? 2 * size * size : 2 * size;
77 flops = in_size * flopsPerElement;
80 {"Pooling", [&](CNNLayer &l) {
81 auto* pool = dynamic_cast<PoolingLayer*>(&l);
82 if (pool->_type == PoolingLayer::PoolType::ROI) {
83 // real kernel sizes are read from weights, so approximation is used.
84 unsigned long kernel_w = inDims[2] / outDims[2];
85 unsigned long kernel_h = inDims[3] / outDims[3];
87 flops = out_size * kernel_h * kernel_w;
89 flops = out_size * (pool->_kernel[Y_AXIS] * pool->_kernel[Y_AXIS]);
93 {"Eltwise", [&](CNNLayer &l) {
94 auto* eltwise = dynamic_cast<EltwiseLayer*>(&l);
95 flops = in_size * (2 * eltwise->insData.size() - 1);
98 {"Power", std::bind(eltwise_complexity, _1, 3, 0)},
99 {"Normalize", std::bind(eltwise_complexity, _1, 4, 0)},
100 {"ReLU", std::bind(eltwise_complexity, _1, 1, 0)},
101 {"Clamp", std::bind(eltwise_complexity, _1, 2, 0)},
102 {"BatchNormalization", scale_complexity},
103 {"ScaleShift", scale_complexity},
105 // roughly count exp as 1 flop
106 {"SoftMax", std::bind(eltwise_complexity, _1, 4, 0)},
109 if (layerComplexityLookup.count(type) > 0) {
110 layerComplexityLookup.at(type)(*layer);
112 return {flops, params};
117 namespace InferenceEngine {
119 std::unordered_map<std::string,
120 LayerComplexity> getNetworkComplexity(const InferenceEngine::ICNNNetwork &network) {
121 std::unordered_map<std::string, LayerComplexity> networkComplexity;
122 InferenceEngine::InputsDataMap networkInputs;
123 network.getInputsInfo(networkInputs);
124 if (networkInputs.empty()) {
125 THROW_IE_EXCEPTION << "No inputs detected.";
128 // Get all network inputs
130 for (auto input : networkInputs) {
131 for (auto l : input.second->getInputData()->inputTo) {
132 inputs.insert(l.second);
136 CNNNetForestDFS(inputs, [&](InferenceEngine::CNNLayerPtr layer) {
137 networkComplexity.emplace(layer->name, getComplexity(layer));
139 return networkComplexity;
142 } // namespace InferenceEngine