e8542c3f5bee7ab8a273cbad005eed8e2957dd98
[platform/core/ml/nnfw.git] / runtime / contrib / labs / tflite_examples / src / conv.cpp
1 /*
2  * Copyright (c) 2018 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 "tflite/ext/kernels/register.h"
18 #include "tensorflow/lite/model.h"
19 #include "tensorflow/lite/builtin_op_data.h"
20
21 #include <iostream>
22
23 using namespace tflite;
24 using namespace nnfw::tflite;
25
26 namespace vector
27 {
28
29 template <typename T> struct View
30 {
31   virtual ~View() = default;
32
33   virtual int32_t size(void) const = 0;
34   virtual T at(uint32_t off) const = 0;
35 };
36 } // namespace vector
37
38 namespace feature
39 {
40
41 struct Shape
42 {
43   int32_t C;
44   int32_t H;
45   int32_t W;
46 };
47
48 template <typename T> struct View
49 {
50   virtual ~View() = default;
51
52   virtual const Shape &shape(void) const = 0;
53   virtual T at(uint32_t ch, uint32_t row, uint32_t col) const = 0;
54 };
55 } // namespace feature
56
57 namespace kernel
58 {
59
60 struct Shape
61 {
62   int32_t N;
63   int32_t C;
64   int32_t H;
65   int32_t W;
66 };
67
68 template <typename T> struct View
69 {
70   virtual ~View() = default;
71
72   virtual const Shape &shape(void) const = 0;
73   virtual T at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const = 0;
74 };
75 } // namespace kernel
76
77 const int32_t N = 1;
78 const int32_t C = 2;
79
80 class SampleBiasObject final : public vector::View<float>
81 {
82 public:
83   SampleBiasObject() : _size(N)
84   {
85     // DO NOTHING
86   }
87
88 public:
89   int32_t size(void) const override { return _size; }
90
91   float at(uint32_t off) const override { return 0.0f; }
92
93 private:
94   int32_t _size;
95 };
96
97 class SampleFeatureObject final : public feature::View<float>
98 {
99 public:
100   SampleFeatureObject()
101   {
102     _shape.C = C;
103     _shape.H = 3;
104     _shape.W = 4;
105
106     const uint32_t size = _shape.C * _shape.H * _shape.W;
107
108     for (uint32_t off = 0; off < size; ++off)
109     {
110       _value.emplace_back(off);
111     }
112
113     assert(_value.size() == size);
114   }
115
116 public:
117   const feature::Shape &shape(void) const override { return _shape; };
118
119   float at(uint32_t ch, uint32_t row, uint32_t col) const override
120   {
121     return _value.at(ch * _shape.H * _shape.W + row * _shape.W + col);
122   }
123
124 public:
125   float &at(uint32_t ch, uint32_t row, uint32_t col)
126   {
127     return _value.at(ch * _shape.H * _shape.W + row * _shape.W + col);
128   }
129
130 private:
131   feature::Shape _shape;
132   std::vector<float> _value;
133 };
134
135 class SampleKernelObject final : public kernel::View<float>
136 {
137 public:
138   SampleKernelObject()
139   {
140     _shape.N = N;
141     _shape.C = C;
142     _shape.H = 3;
143     _shape.W = 4;
144
145     const uint32_t size = _shape.N * _shape.C * _shape.H * _shape.W;
146
147     for (uint32_t off = 0; off < size; ++off)
148     {
149       _value.emplace_back(off);
150     }
151
152     assert(_value.size() == size);
153   }
154
155 public:
156   const kernel::Shape &shape(void) const override { return _shape; };
157
158   float at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const override
159   {
160     return _value.at(nth * _shape.C * _shape.H * _shape.W + ch * _shape.H * _shape.W +
161                      row * _shape.W + col);
162   }
163
164 private:
165   kernel::Shape _shape;
166   std::vector<float> _value;
167 };
168
169 int main(int argc, char **argv)
170 {
171   const SampleFeatureObject ifm;
172   const SampleKernelObject kernel;
173   const SampleBiasObject bias;
174
175   const int32_t IFM_C = ifm.shape().C;
176   const int32_t IFM_H = ifm.shape().H;
177   const int32_t IFM_W = ifm.shape().W;
178
179   const int32_t KER_N = kernel.shape().N;
180   const int32_t KER_C = kernel.shape().C;
181   const int32_t KER_H = kernel.shape().H;
182   const int32_t KER_W = kernel.shape().W;
183
184   const int32_t OFM_C = kernel.shape().N;
185   const int32_t OFM_H = (IFM_H - KER_H) + 1;
186   const int32_t OFM_W = (IFM_W - KER_W) + 1;
187
188   // Assumption on this example
189   assert(IFM_C == KER_C);
190   assert(KER_N == bias.size());
191
192   // Comment from 'context.h'
193   //
194   // Parameters for asymmetric quantization. Quantized values can be converted
195   // back to float using:
196   //    real_value = scale * (quantized_value - zero_point);
197   //
198   // Q: Is this necessary?
199   TfLiteQuantizationParams quantization;
200
201   quantization.scale = 1;
202   quantization.zero_point = 0;
203
204   Interpreter interp;
205
206   // On AddTensors(N) call, T/F Lite interpreter creates N tensors whose index is [0 ~ N)
207   interp.AddTensors(5);
208
209   // Configure OFM
210   interp.SetTensorParametersReadWrite(0, kTfLiteFloat32 /* type */, "output" /* name */,
211                                       {1 /*N*/, OFM_H, OFM_W, OFM_C} /* dims */, quantization);
212
213   // Configure IFM
214   interp.SetTensorParametersReadWrite(1, kTfLiteFloat32 /* type */, "input" /* name */,
215                                       {1 /*N*/, IFM_H, IFM_W, IFM_C} /* dims */, quantization);
216
217   // Configure Filter
218   const uint32_t kernel_size = KER_N * KER_C * KER_H * KER_W;
219   float kernel_data[kernel_size] = {
220       0.0f,
221   };
222
223   // Fill kernel data in NHWC order
224   {
225     uint32_t off = 0;
226
227     for (uint32_t nth = 0; nth < KER_N; ++nth)
228     {
229       for (uint32_t row = 0; row < KER_H; ++row)
230       {
231         for (uint32_t col = 0; col < KER_W; ++col)
232         {
233           for (uint32_t ch = 0; ch < KER_C; ++ch)
234           {
235             const auto value = kernel.at(nth, ch, row, col);
236             kernel_data[off++] = value;
237           }
238         }
239       }
240     }
241
242     assert(kernel_size == off);
243   }
244
245   interp.SetTensorParametersReadOnly(
246       2, kTfLiteFloat32 /* type */, "filter" /* name */, {KER_N, KER_H, KER_W, KER_C} /* dims */,
247       quantization, reinterpret_cast<const char *>(kernel_data), sizeof(kernel_data));
248
249   // Configure Bias
250   const uint32_t bias_size = bias.size();
251   float bias_data[bias_size] = {
252       0.0f,
253   };
254
255   // Fill bias data
256   for (uint32_t off = 0; off < bias.size(); ++off)
257   {
258     bias_data[off] = bias.at(off);
259   }
260
261   interp.SetTensorParametersReadOnly(3, kTfLiteFloat32 /* type */, "bias" /* name */,
262                                      {bias.size()} /* dims */, quantization,
263                                      reinterpret_cast<const char *>(bias_data), sizeof(bias_data));
264
265   // Add Convolution Node
266   //
267   // NOTE AddNodeWithParameters take the ownership of param, and deallocate it with free
268   //      So, param should be allocated with malloc
269   TfLiteConvParams *param = reinterpret_cast<TfLiteConvParams *>(malloc(sizeof(TfLiteConvParams)));
270
271   param->padding = kTfLitePaddingValid;
272   param->stride_width = 1;
273   param->stride_height = 1;
274   param->activation = kTfLiteActRelu;
275
276   // Run Convolution and store its result into Tensor #0
277   //  - Read IFM from Tensor #1
278   //  - Read Filter from Tensor #2,
279   //  - Read Bias from Tensor #3
280   interp.AddNodeWithParameters({1, 2, 3}, {0}, nullptr, 0, reinterpret_cast<void *>(param),
281                                BuiltinOpResolver().FindOp(BuiltinOperator_CONV_2D, 1));
282
283   // Set Tensor #1 as Input #0, and Tensor #0 as Output #0
284   interp.SetInputs({1});
285   interp.SetOutputs({0});
286
287   // Let's use NNAPI (if possible)
288   interp.UseNNAPI(true);
289
290   // Allocate Tensor
291   interp.AllocateTensors();
292
293   // Fill IFM data in HWC order
294   {
295     uint32_t off = 0;
296
297     for (uint32_t row = 0; row < ifm.shape().H; ++row)
298     {
299       for (uint32_t col = 0; col < ifm.shape().W; ++col)
300       {
301         for (uint32_t ch = 0; ch < ifm.shape().C; ++ch)
302         {
303           const auto value = ifm.at(ch, row, col);
304           interp.typed_input_tensor<float>(0)[off++] = value;
305         }
306       }
307     }
308   }
309
310   // Let's Rock-n-Roll!
311   interp.Invoke();
312
313   // Print OFM
314   {
315     uint32_t off = 0;
316
317     for (uint32_t row = 0; row < OFM_H; ++row)
318     {
319       for (uint32_t col = 0; col < OFM_W; ++col)
320       {
321         for (uint32_t ch = 0; ch < kernel.shape().N; ++ch)
322         {
323           std::cout << interp.typed_output_tensor<float>(0)[off++] << std::endl;
324         }
325       }
326     }
327   }
328
329   return 0;
330 }