Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / src / mkldnn_plugin / utils / blob_dump.cpp
1 //
2 // Copyright 2016-2018 Intel Corporation.
3 //
4 // This software and the related documents are Intel copyrighted materials,
5 // and your use of them is governed by the express license under which they
6 // were provided to you (End User License Agreement for the Intel(R) Software
7 // Development Products (Version May 2017)). Unless the License provides
8 // otherwise, you may not use, modify, copy, publish, distribute, disclose or
9 // transmit this software or the related documents without Intel's prior
10 // written permission.
11 //
12 // This software and the related documents are provided as is, with no
13 // express or implied warranties, other than those that are expressly
14 // stated in the License.
15 //
16
17 #include "blob_dump.h"
18 #include "blob_factory.hpp"
19 #include "mkldnn_memory.h"
20
21 // It's so bad to include by relative path :-(
22 #include "../../thirdparty/mkl-dnn/src/common/memory_desc_wrapper.hpp"
23
24 #include <fstream>
25
26 using namespace InferenceEngine;
27
28 namespace MKLDNNPlugin {
29
30 // IEB file format routine
31 static unsigned char IEB_MAGIC[4] = {'I', 'E', 'B', '0'};
32 static unsigned char NO_SCALES = 0xFF;
33
34 struct IEB_HEADER {
35     unsigned char magic[4];
36     unsigned char ver[2];
37
38     unsigned char precision;  // 0-8
39     unsigned char ndims;
40     unsigned int  dims[7];  // max is 7-D blob
41
42     unsigned char scaling_axis;  // FF - no scaling
43     unsigned char reserved[3];
44
45     unsigned long data_offset;
46     unsigned long data_size;
47     unsigned long scaling_data_offset;
48     unsigned long scaling_data_size;
49 };
50
51 static IEB_HEADER prepare_header(const TensorDesc& desc) {
52     IEB_HEADER header;
53
54     header.magic[0] = IEB_MAGIC[0];
55     header.magic[1] = IEB_MAGIC[1];
56     header.magic[2] = IEB_MAGIC[2];
57     header.magic[3] = IEB_MAGIC[3];
58
59     // IEB file format version 0.1
60     header.ver[0] = 0;
61     header.ver[1] = 1;
62
63     header.precision = desc.getPrecision();
64
65     if (desc.getDims().size() > 7)
66         THROW_IE_EXCEPTION << "Dumper support max 7D blobs";
67
68     header.ndims = desc.getDims().size();
69     for (int i = 0; i < header.ndims; i++)
70         header.dims[i] = desc.getDims()[i];
71
72     header.scaling_axis = NO_SCALES;
73
74     return header;
75 }
76
77 static TensorDesc parse_header(IEB_HEADER &header) {
78     if (header.magic[0] != IEB_MAGIC[0] ||
79         header.magic[1] != IEB_MAGIC[1] ||
80         header.magic[2] != IEB_MAGIC[2] ||
81         header.magic[3] != IEB_MAGIC[3])
82         THROW_IE_EXCEPTION << "Dumper cannot parse file. Wrong format.";
83
84     if (header.ver[0] != 0 ||
85         header.ver[1] != 1)
86         THROW_IE_EXCEPTION << "Dumper cannot parse file. Unsupported IEB format version.";
87
88     Precision prc = Precision(static_cast<Precision::ePrecision>(header.precision));
89     SizeVector dims(header.ndims);
90     for (int i = 0; i < header.ndims; i++)
91         dims[i] = header.dims[i];
92
93     return TensorDesc {prc, dims, plain_layout(dims)};
94 }
95
96
97 bool is_plain(Blob::Ptr blob) {
98     bool res = true;
99
100     auto orig_strides = blob->getTensorDesc().getBlockingDesc().getStrides();
101     auto orig_order = blob->getTensorDesc().getBlockingDesc().getOrder();
102     auto dims = blob->getTensorDesc().getDims();
103
104     for (int stride = 1, i = dims.size()-1; i >= 0; --i) {
105         if (stride != orig_strides[i] || i != orig_order[i]) res = false;
106         stride *= dims[i];
107     }
108
109     return res;
110 }
111
112 static Blob::Ptr prepare_plain_data(Blob::Ptr blob) {
113     // check if it already plain
114     if (is_plain(blob)) return blob;
115
116     Blob::Ptr pln_blob = make_plain_blob(blob->precision(), blob->getTensorDesc().getDims());
117     pln_blob->allocate();
118
119     // Copy to plain
120     MKLDNNMemoryDesc mdesc(blob->getTensorDesc());
121     mkldnn::memory::desc desc = mdesc;
122     mkldnn::impl::memory_desc_wrapper blob_wrp(desc.data);
123
124     int data_size = blob->size();
125
126     // TODO: make it with blob_copy utility
127     switch (blob->precision()) {
128         case Precision::FP32:
129         case Precision::I32: {
130             int32_t *pln_blob_ptr = pln_blob->buffer().as<int32_t*>();
131             int32_t *blob_ptr = blob->buffer().as<int32_t*>();
132             for (size_t i = 0; i < data_size; i++)
133                 pln_blob_ptr[i] = blob_ptr[blob_wrp.off_l(i)];
134             break;
135         }
136         case Precision::I16:
137         case Precision::U16: {
138             int16_t *pln_blob_ptr = pln_blob->buffer().as<int16_t*>();
139             int16_t *blob_ptr = blob->buffer().as<int16_t *>();
140             for (size_t i = 0; i < data_size; i++)
141                 pln_blob_ptr[i] = blob_ptr[blob_wrp.off_l(i)];
142             break;
143         }
144         case Precision::I8:
145         case Precision::U8: {
146             int8_t *pln_blob_ptr = pln_blob->buffer().as<int8_t*>();
147             int8_t *blob_ptr = blob->buffer().as<int8_t *>();
148             for (size_t i = 0; i < data_size; i++)
149                 pln_blob_ptr[i] = blob_ptr[blob_wrp.off_l(i)];
150             break;
151         }
152         default:
153             THROW_IE_EXCEPTION << "Dumper. Unsupported precision";
154     }
155
156     return pln_blob;
157 }
158
159 void BlobDumper::dump(std::ostream &stream) {
160     if (!_blob)
161         THROW_IE_EXCEPTION << "Dumper cannot dump empty Blob";
162
163     if (_blob->buffer().as<float*>() == nullptr)
164         THROW_IE_EXCEPTION << "Dumper cannot dump. Blob is not allocated.";
165
166     IEB_HEADER header = prepare_header(_blob->getTensorDesc());
167     Blob::Ptr pln_blob = prepare_plain_data(_blob);
168
169     header.data_offset = sizeof(header);
170     header.data_size = pln_blob->byteSize();
171     header.scaling_data_offset = 0;
172     header.scaling_data_size = 0;
173
174     if (_scales) {
175         header.scaling_axis = 1;
176         header.scaling_data_offset = header.data_offset + header.data_size;
177         header.scaling_data_size = _scales->byteSize();
178     }
179
180     stream.write(reinterpret_cast<char*>(&header), sizeof(header));
181     stream.write(pln_blob->buffer().as<char*>(), pln_blob->byteSize());
182
183     if (_scales) {
184         stream.write(_scales->buffer().as<char*>(), _scales->byteSize());
185     }
186 }
187
188 void BlobDumper::dumpAsTxt(std::ostream &stream) {
189     if (!_blob)
190         THROW_IE_EXCEPTION << "Dumper cannot dump empty Blob";
191
192     if (_blob->buffer().as<float*>() == nullptr)
193         THROW_IE_EXCEPTION << "Dumper cannot dump. Blob is not allocated.";
194
195     SizeVector dims = _blob->getTensorDesc().getDims();
196
197     // Header like "U8 4D shape: 2 3 224 224 ()
198     stream << _blob->precision().name() << " "
199            << dims.size() << "D "
200            << "shape: ";
201     for (size_t d : dims) stream << d << " ";
202     stream << "(" << _blob->size() << ")" <<std::endl;
203
204     // Dump data
205     MKLDNNMemoryDesc mdesc(_blob->getTensorDesc());
206     mkldnn::memory::desc desc = mdesc;
207     mkldnn::impl::memory_desc_wrapper blob_wrp(desc.data);
208
209     int data_size = _blob->size();
210     switch (_blob->precision()) {
211         case Precision::FP32: {
212             auto *blob_ptr = _blob->buffer().as<float*>();
213             for (size_t i = 0; i < data_size; i++)
214                 stream << blob_ptr[blob_wrp.off_l(i)] << std::endl;
215             break;
216         }
217         case Precision::I32: {
218             auto *blob_ptr = _blob->buffer().as<int32_t*>();
219             for (size_t i = 0; i < data_size; i++)
220                 stream << blob_ptr[blob_wrp.off_l(i)] << std::endl;
221             break;
222         }
223         case Precision::I16: {
224             auto *blob_ptr = _blob->buffer().as<int16_t*>();
225             for (size_t i = 0; i < data_size; i++)
226                 stream << static_cast<int>(blob_ptr[blob_wrp.off_l(i)]) << std::endl;
227             break;
228         }
229         case Precision::U16: {
230             auto *blob_ptr = _blob->buffer().as<uint16_t*>();
231             for (size_t i = 0; i < data_size; i++)
232                 stream << static_cast<int>(blob_ptr[blob_wrp.off_l(i)]) << std::endl;
233             break;
234         }
235         case Precision::I8: {
236             auto *blob_ptr = _blob->buffer().as<int8_t*>();
237             for (size_t i = 0; i < data_size; i++)
238                 stream << static_cast<int>(blob_ptr[blob_wrp.off_l(i)]) << std::endl;
239             break;
240         }
241         case Precision::U8: {
242             auto *blob_ptr = _blob->buffer().as<uint8_t*>();
243             for (size_t i = 0; i < data_size; i++)
244                 stream << static_cast<int>(blob_ptr[blob_wrp.off_l(i)]) << std::endl;
245             break;
246         }
247         default:
248             THROW_IE_EXCEPTION << "Dumper. Unsupported precision";
249     }
250 }
251
252 BlobDumper BlobDumper::read(std::istream &stream) {
253     IEB_HEADER header;
254     stream.read(reinterpret_cast<char*>(&header), sizeof(header));
255
256     TensorDesc desc = parse_header(header);
257     Blob::Ptr blob = make_blob_with_precision(desc);
258     blob->allocate();
259
260     stream.seekg(header.data_offset, stream.beg);
261     stream.read(blob->buffer().as<char*>(), header.data_size);
262
263     BlobDumper res(blob);
264
265     // Parse scales fields.
266     if (header.scaling_axis != NO_SCALES) {
267         if (header.scaling_axis != 1)
268             THROW_IE_EXCEPTION << "Dumper support scaling only for channel dims.";
269
270         size_t scl_size = header.scaling_data_size / sizeof(float);
271         auto scl = make_blob_with_precision({Precision::FP32, {scl_size}, C});
272         scl->allocate();
273
274         stream.seekg(header.scaling_data_offset, stream.beg);
275         stream.read(scl->buffer().as<char*>(), header.scaling_data_size);
276
277         res._scales = scl;
278     }
279     return res;
280 }
281
282 BlobDumper BlobDumper::read(const std::string &file_path) {
283     std::ifstream file;
284     file.open(file_path);
285     if (!file.is_open())
286         THROW_IE_EXCEPTION << "Dumper cannot open file " << file_path;
287
288     auto res = read(file);
289     file.close();
290     return res;
291 }
292
293 void BlobDumper::dump(const std::string &dump_path) {
294     std::ofstream dump_file;
295     dump_file.open(dump_path);
296     if (!dump_file.is_open())
297         THROW_IE_EXCEPTION << "Dumper cannot create dump file";
298
299     dump(dump_file);
300     dump_file.close();
301 }
302
303 void BlobDumper::dumpAsTxt(const std::string dump_path) {
304     std::ofstream dump_file;
305     dump_file.open(dump_path);
306     if (!dump_file.is_open())
307         THROW_IE_EXCEPTION << "Dumper cannot create dump file";
308
309     dumpAsTxt(dump_file);
310     dump_file.close();
311 }
312
313 Blob::Ptr BlobDumper::get() {
314     return _blob;
315 }
316
317 template <typename data_t>
318 static void plain_copy(const Blob::Ptr &from, const Blob::Ptr &scls, Blob::Ptr &to) {
319     auto dims = from->getTensorDesc().getDims();
320
321     size_t data_size = from->size();
322     size_t outer_size = dims[0];
323     size_t c_size = dims.size() > 1 ? dims[1] : 1;
324     size_t inner_size = dims.size() == 4 ? dims[2]*dims[3] :
325                         dims.size() == 3 ? dims[2] : 1;
326
327     auto to_data  = to->buffer().as<float*>();
328     auto from_data = from->buffer().as<data_t*>();
329
330     if (scls) {
331         auto scls_data = scls->buffer().as<float*>();
332
333         for (size_t o=0; o < outer_size; o++)
334         for (size_t c=0; c < c_size; c++)
335         for (size_t i=0; i < inner_size; i++)
336             *to_data++ = static_cast<float>(*from_data++) * scls_data[c];
337     } else {
338         for (size_t i=0; i < data_size; i++)
339             *to_data++ = static_cast<float>(*from_data++);
340     }
341 }
342
343 Blob::Ptr BlobDumper::getRealValue() {
344     if (_blob->precision() == Precision::FP32 && !_scales)
345         return _blob;
346
347     auto res = make_plain_blob(Precision::FP32, _blob->getTensorDesc().getDims());
348     res->allocate();
349
350     switch (_blob->precision()) {
351         case Precision::U8: plain_copy<uint8_t>(_blob, _scales, res); break;
352         case Precision::FP32: plain_copy<float>(_blob, _scales, res); break;
353         case Precision::I8: plain_copy<int8_t >(_blob, _scales, res); break;
354         default: THROW_IE_EXCEPTION << "Unsupported precesion for getRealValue method.";
355     }
356
357     return res;
358 }
359
360
361 BlobDumper& BlobDumper::withScales(InferenceEngine::Blob::Ptr scales) {
362     if ( _blob->getTensorDesc().getDims().size() < 2  ||
363         scales->getTensorDesc().getDims().size() != 1 ||
364         scales->getTensorDesc().getDims()[0] != _blob->getTensorDesc().getDims()[1] ||
365         scales->getTensorDesc().getPrecision() != Precision::FP32)
366         THROW_IE_EXCEPTION << "Dumper cannot use passed scales. Blob has incompatible shape.";
367
368     _scales = scales;
369     return *this;
370 }
371
372 BlobDumper& BlobDumper::withoutScales() {
373     _scales.reset();
374     return *this;
375 }
376
377
378 const InferenceEngine::Blob::Ptr& BlobDumper::getScales() const {
379     return _scales;
380 }
381
382 }  // namespace MKLDNNPlugin