Imported Upstream version 1.21.0
[platform/core/ml/nnfw.git] / compiler / circle-eval-diff / src / CircleEvalDiff.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "CircleEvalDiff.h"
18 #include "InputDataLoader.h"
19 #include "MetricPrinter.h"
20 #include "Tensor.h"
21
22 #include <foder/FileLoader.h>
23 #include <luci/Importer.h>
24
25 #include <stdexcept>
26
27 namespace
28 {
29
30 bool same_shape(const luci::CircleNode *a, const luci::CircleNode *b)
31 {
32   if (a->rank() != b->rank())
33     return false;
34
35   for (uint32_t i = 0; i < a->rank(); i++)
36   {
37     if (not(a->dim(i) == b->dim(i)))
38       return false;
39   }
40
41   return true;
42 }
43
44 bool same_dtype(const luci::CircleNode *a, const luci::CircleNode *b)
45 {
46   return a->dtype() == b->dtype();
47 }
48
49 std::unique_ptr<luci::Module> import(const std::string &model_path)
50 {
51   // Load model from the file
52   foder::FileLoader loader{model_path};
53   std::vector<char> model_data = loader.load();
54
55   // Verify flatbuffers
56   flatbuffers::Verifier verifier{reinterpret_cast<const uint8_t *>(model_data.data()),
57                                  model_data.size()};
58   if (not circle::VerifyModelBuffer(verifier))
59   {
60     throw std::runtime_error("Failed to verify circle '" + model_path + "'");
61   }
62
63   auto circle_model = circle::GetModel(model_data.data());
64
65   if (not circle_model)
66     throw std::runtime_error("Failed to load '" + model_path + "'");
67
68   auto module = luci::Importer().importModule(circle_model);
69
70   if (not module)
71     throw std::runtime_error("Failed to load '" + model_path + "'");
72
73   return module;
74 }
75
76 const std::vector<loco::Node *> inputs_of(const luci::Module *module)
77 {
78   return loco::input_nodes(module->graph());
79 }
80
81 const std::vector<loco::Node *> outputs_of(const luci::Module *module)
82 {
83   return loco::output_nodes(module->graph());
84 }
85
86 void writeDataToFile(const std::string &filename, const char *data, size_t data_size)
87 {
88   std::ofstream fs(filename, std::ofstream::binary);
89   if (fs.fail())
90     throw std::runtime_error("Cannot open file \"" + filename + "\".\n");
91   if (fs.write(data, data_size).fail())
92   {
93     throw std::runtime_error("Failed to write data to file \"" + filename + "\".\n");
94   }
95 }
96
97 void checkOutputs(const luci::Module *first, const luci::Module *second)
98 {
99   const auto first_output = outputs_of(first);
100   const auto second_output = outputs_of(second);
101
102   if (first_output.size() != second_output.size())
103     throw std::runtime_error("Models have different output counts");
104
105   for (uint32_t i = 0; i < first_output.size(); i++)
106   {
107     const auto first_node = loco::must_cast<luci::CircleNode *>(first_output[i]);
108     const auto second_node = loco::must_cast<luci::CircleNode *>(second_output[i]);
109
110     if (not same_shape(first_node, second_node))
111       throw std::runtime_error("Output shape mismatch (" + first_node->name() + ", " +
112                                second_node->name() + ")");
113
114     if (not same_dtype(first_node, second_node))
115       throw std::runtime_error("Output dtype mismatch (" + first_node->name() + ", " +
116                                second_node->name() + ")");
117   }
118 }
119
120 } // namespace
121
122 namespace circle_eval_diff
123 {
124
125 std::vector<std::shared_ptr<Tensor>> interpret(const luci::Module *module,
126                                                const InputDataLoader::Data &data)
127 {
128   auto interpreter = std::make_unique<luci_interpreter::Interpreter>(module);
129
130   auto input_nodes = ::inputs_of(module);
131   auto output_nodes = ::outputs_of(module);
132
133   for (uint32_t input_idx = 0; input_idx < data.size(); input_idx++)
134   {
135     auto input_node = loco::must_cast<const luci::CircleInput *>(input_nodes[input_idx]);
136     assert(input_node->index() == input_idx);
137
138     auto input_data = data.at(input_idx);
139     interpreter->writeInputTensor(input_node, input_data.buffer(), input_data.byte_size());
140   }
141
142   interpreter->interpret();
143
144   std::vector<std::shared_ptr<Tensor>> outputs;
145   for (uint32_t output_idx = 0; output_idx < output_nodes.size(); output_idx++)
146   {
147     auto output_node = loco::must_cast<const luci::CircleOutput *>(output_nodes[output_idx]);
148     assert(output_node->index() == output_idx);
149
150     auto tensor = createEmptyTensor(output_node);
151     interpreter->readOutputTensor(output_node, tensor->buffer(), tensor->byte_size());
152     outputs.emplace_back(tensor);
153   }
154
155   return outputs;
156 }
157
158 CircleEvalDiff::CircleEvalDiff(std::unique_ptr<Context> &&ctx) : _ctx(std::move(ctx))
159 {
160   // DO NOTHING
161 }
162
163 CircleEvalDiff::~CircleEvalDiff() = default;
164
165 void CircleEvalDiff::init()
166 {
167   _first_module = import(_ctx->first_model_path);
168   _second_module = import(_ctx->second_model_path);
169
170   // Check modules have the same output signature (dtype/shape)
171   // Exception will be thrown if they have different signature
172   checkOutputs(_first_module.get(), _second_module.get());
173
174   // Set metric
175   std::unique_ptr<MetricPrinter> metric;
176   for (auto metric : _ctx->metric)
177   {
178     switch (metric)
179     {
180       case Metric::MAE:
181       {
182         _metrics.emplace_back(std::make_unique<MAEPrinter>());
183         break;
184       }
185       case Metric::MAPE:
186       {
187         _metrics.emplace_back(std::make_unique<MAPEPrinter>());
188         break;
189       }
190       case Metric::MPEIR:
191       {
192         _metrics.emplace_back(std::make_unique<MPEIRPrinter>());
193         break;
194       }
195       case Metric::MTOP1:
196       {
197         _metrics.emplace_back(std::make_unique<TopKMatchPrinter>(1));
198         break;
199       }
200       case Metric::MTOP5:
201       {
202         _metrics.emplace_back(std::make_unique<TopKMatchPrinter>(5));
203         break;
204       }
205       case Metric::MSE:
206       {
207         _metrics.emplace_back(std::make_unique<MSEPrinter>());
208         break;
209       }
210       default:
211         throw std::runtime_error("Unsupported metric.");
212     }
213     _metrics.back()->init(_first_module.get(), _second_module.get());
214   }
215 }
216
217 void CircleEvalDiff::evalDiff(void) const
218 {
219   auto first_input_loader = circle_eval_diff::makeDataLoader(
220     _ctx->first_input_data_path, _ctx->input_format, ::inputs_of(_first_module.get()));
221   auto second_input_loader = circle_eval_diff::makeDataLoader(
222     _ctx->second_input_data_path, _ctx->input_format, ::inputs_of(_second_module.get()));
223
224   for (uint32_t data_idx = 0; data_idx < first_input_loader->size(); data_idx++)
225   {
226     std::cout << "Evaluating " << data_idx << "'th data" << std::endl;
227
228     auto first_data = first_input_loader->get(data_idx);
229     auto second_data = second_input_loader->get(data_idx);
230
231     auto first_output = interpret(_first_module.get(), first_data);
232     auto second_output = interpret(_second_module.get(), second_data);
233
234     for (auto &metric : _metrics)
235     {
236       metric->accumulate(first_output, second_output);
237     }
238
239     if (_ctx.get()->output_prefix.empty())
240       continue;
241
242     for (uint32_t i = 0; i < first_output.size(); i++)
243     {
244       auto out = first_output[i];
245       writeDataToFile(_ctx.get()->output_prefix + "." + std::to_string(data_idx) + ".first.output" +
246                         std::to_string(i),
247                       (char *)(out->buffer()), out->byte_size());
248     }
249     for (uint32_t i = 0; i < second_output.size(); i++)
250     {
251       auto out = second_output[i];
252       writeDataToFile(_ctx.get()->output_prefix + "." + std::to_string(data_idx) +
253                         ".second.output" + std::to_string(i),
254                       (char *)(out->buffer()), out->byte_size());
255     }
256   }
257
258   for (auto &metric : _metrics)
259   {
260     std::cout << metric.get() << std::endl;
261   }
262 }
263
264 } // namespace circle_eval_diff