1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #ifdef INTEL_MKL
17 #define EIGEN_USE_THREADS
18
19 #include <functional>
20 #include <memory>
21 #include <vector>
22
23 #include "tensorflow/core/framework/allocator.h"
24 #include "tensorflow/core/framework/fake_input.h"
25 #include "tensorflow/core/framework/node_def_builder.h"
26 #include "tensorflow/core/framework/op_kernel.h"
27 #include "tensorflow/core/framework/tensor.h"
28 #include "tensorflow/core/framework/tensor_testutil.h"
29 #include "tensorflow/core/framework/types.h"
30 #include "tensorflow/core/framework/types.pb.h"
31 #include "tensorflow/core/kernels/ops_testutil.h"
32 #include "tensorflow/core/kernels/ops_util.h"
33 #include "tensorflow/core/kernels/quantization_utils.h"
34 #include "tensorflow/core/lib/core/status_test_util.h"
35 #include "tensorflow/core/platform/test.h"
36
37 namespace tensorflow {
38
39 // Helper class for converting MKL tensors to TF tensors and comparing to
40 // expected values
41
42 static const uint8 dummy_tensor[] = {0, 0, 0, 0, 0, 0, 0, 0};
43 static const TensorShape dummy_shape({8});
44
45 class ConvMklToTF : public OpsTestBase {
46 public:
47 // TODO(bhavanis): Move the below ConvertMklToTF() to mkl_util.h
48 template <typename T>
ConvertMklToTF(DataType dtype,const Tensor & input,const Tensor & input_metadata_tensor,Tensor & output)49 void ConvertMklToTF(DataType dtype, const Tensor& input,
50 const Tensor& input_metadata_tensor, Tensor& output) {
51 // Create an MKL to TF conversion node and execute it
52 TF_EXPECT_OK(NodeDefBuilder("mkl_to_tf_op", "_MklToTf")
53 .Input(FakeInput(dtype)) // Input
54 .Input(FakeInput(DT_UINT8)) // MKL metadata tensor
55 .Attr("T", dtype)
56 .Attr("_kernel", "MklLayoutDependentOp")
57 .Finalize(node_def()));
58 TF_EXPECT_OK(InitOp());
59 AddInputFromArray<T>(input.shape(), input.flat<T>());
60 AddInputFromArray<uint8>(input_metadata_tensor.shape(),
61 input_metadata_tensor.flat<uint8>());
62 TF_ASSERT_OK(RunOpKernel());
63
64 output = *GetOutput(0);
65 }
TestBody()66 void TestBody() {}
67 };
68
69 class QuantizedConv2DTest : public OpsTestBase {
70 protected:
ConfigureQuantizedConv2D(const int & stride=1)71 void ConfigureQuantizedConv2D(const int& stride = 1) {
72 TF_ASSERT_OK(NodeDefBuilder("quantized_conv_op", "_MklQuantizedConv2D")
73 .Input(FakeInput(DT_QUINT8)) // Input
74 .Input(FakeInput(DT_QINT8)) // Filter
75 .Input(FakeInput(DT_FLOAT)) // Min input
76 .Input(FakeInput(DT_FLOAT)) // Max input
77 .Input(FakeInput(DT_FLOAT)) // Min filter
78 .Input(FakeInput(DT_FLOAT)) // Max filter
79 // MKL metadata tensors //
80 .Input(FakeInput(DT_UINT8))
81 .Input(FakeInput(DT_UINT8))
82 .Input(FakeInput(DT_UINT8))
83 .Input(FakeInput(DT_UINT8))
84 .Input(FakeInput(DT_UINT8))
85 .Input(FakeInput(DT_UINT8))
86 ///////////////////////////
87 .Attr("Tinput", DataTypeToEnum<quint8>::v())
88 .Attr("Tfilter", DataTypeToEnum<qint8>::v())
89 .Attr("T", DataTypeToEnum<quint8>::v())
90 .Attr("out_type", DataTypeToEnum<qint32>::v())
91 .Attr("strides", {1, stride, stride, 1})
92 .Attr("padding", "SAME")
93 .Attr("_kernel", "QuantizedMklOp")
94 .Finalize(node_def()));
95 TF_ASSERT_OK(InitOp());
96 }
97
RunQuantizedDepthwiseConv2DOp(const bool & bias_enabled)98 void RunQuantizedDepthwiseConv2DOp(const bool& bias_enabled) {
99 const int depth = 2;
100 const int image_width = 2;
101 const int image_height = 3;
102 const int image_batch_count = 1;
103 // The image matrix is ('first/second' channel):
104 // | 1/2 | 3/4 |
105 // | 5/6 | 7/8 |
106 // | 9/10 | 11/12 |
107 AddInputFromArray<quint8>(
108 TensorShape({image_batch_count, image_height, image_width, depth}),
109 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
110
111 // The filter matrix is:
112 // | 1/2 | 7/8 | 13/14 |
113 // | 3/4 | 9/10 | 15/16 |
114 // | 5/6 | 11/12 | 17/18 |
115 const int filter_size = 3;
116 const int filter_count = 1;
117 AddInputFromArray<qint8>(
118 TensorShape({filter_size, filter_size, depth, filter_count}),
119 {1, 2, 7, 8, 13, 14, 3, 4, 9, 10, 15, 16, 5, 6, 11, 12, 17, 18});
120
121 if (bias_enabled) {
122 // Bias -> float
123 AddInputFromArray<float>(TensorShape({depth}), {1.0f, 1.0f});
124 }
125
126 // Image -> uint8
127 AddInputFromArray<float>(TensorShape({1}), {0.0f});
128 AddInputFromArray<float>(TensorShape({1}), {255.0f});
129
130 // Filter -> int8 with symmetric range
131 AddInputFromArray<float>(TensorShape({1}), {-127.0f});
132 AddInputFromArray<float>(TensorShape({1}), {127.0f});
133
134 if (bias_enabled) {
135 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
136 }
137
138 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
139 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
140 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
141 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
142 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
143 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
144
145 TF_ASSERT_OK(RunOpKernel());
146
147 // We're sliding two 3x3 filters across the 3x2 image, with accesses outside
148 // the input set to zero because we're using the 'SAME' padding mode.
149 // This means we should end up with this matrix:
150 // | 228/300 | 132/180 |
151 // | 482/596 | 266/344 |
152 // | 372/452 | 180/236 |
153 //
154 // Similarly, after adding a bias of 1.0f across each channel, we should end
155 // up with this matrix:
156 // | 229/301 | 133/181 |
157 // | 483/597 | 267/345 |
158 // | 373/453 | 181/237 |
159
160 // Output -> qint32
161 Tensor expected(DT_QINT32, TensorShape({image_batch_count, image_height,
162 image_width, depth}));
163 if (bias_enabled) {
164 test::FillValues<qint32>(&expected, {229, 301, 133, 181, 483, 597, 267,
165 345, 373, 453, 181, 237});
166 } else {
167 test::FillValues<qint32>(&expected, {228, 300, 132, 180, 482, 596, 266,
168 344, 372, 452, 180, 236});
169 }
170
171 const Tensor& output = *GetOutput(0);
172 const Tensor& output_mkl_metadata = *GetOutput(3);
173
174 ConvMklToTF conv_comp;
175 Tensor output_quantized;
176 conv_comp.ConvertMklToTF<qint32>(DT_QINT32, output, output_mkl_metadata,
177 output_quantized);
178
179 test::ExpectTensorEqual<qint32>(expected, output_quantized);
180 }
181 };
182
183 // Output -> float
TEST_F(QuantizedConv2DTest,Small)184 TEST_F(QuantizedConv2DTest, Small) {
185 const int stride = 1;
186 ConfigureQuantizedConv2D(stride);
187
188 const int depth = 1;
189 const int image_width = 4;
190 const int image_height = 3;
191 const int image_batch_count = 1;
192
193 // Image -> uint8
194 const float image_min = 0.0f;
195 const float image_max = 255.0f;
196
197 // The image matrix is:
198 // | 1 | 2 | 3 | 4 |
199 // | 5 | 6 | 7 | 8 |
200 // | 9 | 10 | 11 | 12 |
201 Tensor image_float(DT_FLOAT,
202 {image_batch_count, image_height, image_width, depth});
203 test::FillValues<float>(&image_float,
204 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
205 Tensor image_quantized =
206 FloatTensorToQuantized<quint8>(image_float, image_min, image_max);
207
208 const int filter_size = 3;
209 const int filter_count = 1;
210
211 // Filter -> int8 with symmetric range
212 const float filter_min = -127.0f;
213 const float filter_max = 127.0f;
214
215 // The filter matrix is:
216 // | 1 | 4 | 7 |
217 // | 2 | 5 | 8 |
218 // | 3 | 6 | 9 |
219 Tensor filter_float(DT_FLOAT,
220 {filter_size, filter_size, depth, filter_count});
221 test::FillValues<float>(&filter_float, {1, 4, 7, 2, 5, 8, 3, 6, 9});
222 Tensor filter_quantized =
223 FloatTensorToQuantized<qint8>(filter_float, filter_min, filter_max);
224
225 AddInputFromArray<quint8>(image_quantized.shape(),
226 image_quantized.flat<quint8>());
227 AddInputFromArray<qint8>(filter_quantized.shape(),
228 filter_quantized.flat<qint8>());
229 AddInputFromArray<float>(TensorShape({1}), {image_min});
230 AddInputFromArray<float>(TensorShape({1}), {image_max});
231 AddInputFromArray<float>(TensorShape({1}), {filter_min});
232 AddInputFromArray<float>(TensorShape({1}), {filter_max});
233
234 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
235 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
236 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
237 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
238 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
239 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
240
241 TF_ASSERT_OK(RunOpKernel());
242
243 // We're sliding the 3x3 filter across the 3x4 image, with accesses outside
244 // the input set to zero because we're using the 'SAME' padding mode.
245 // The calculations behind the expected output are:
246 // (1*0)+(4*0)+(7*0)+(2*0)+(5*1)+(8*2)+(3*0)+(6*5)+(9*6)=105
247 // (1*0)+(4*0)+(7*0)+(2*1)+(5*2)+(8*3)+(3*5)+(6*6)+(9*7)=150
248 // (1*0)+(4*0)+(7*0)+(2*2)+(5*3)+(8*4)+(3*6)+(6*7)+(9*8)=183
249 // (1*0)+(4*0)+(7*0)+(2*3)+(5*4)+(8*0)+(3*7)+(6*8)+(9*0)=95
250 // (1*0)+(4*1)+(7*2)+(2*0)+(5*5)+(8*6)+(3*0)+(6*9)+(9*10)=235
251 // (1*1)+(4*2)+(7*3)+(2*5)+(5*6)+(8*7)+(3*9)+(6*10)+(9*11)=312
252 // (1*2)+(4*3)+(7*4)+(2*6)+(5*7)+(8*8)+(3*10)+(6*11)+(9*12)=357
253 // (1*3)+(4*4)+(7*0)+(2*7)+(5*8)+(8*0)+(3*11)+(6*12)+(9*0)=178
254 // (1*0)+(4*5)+(7*6)+(2*0)+(5*9)+(8*10)+(3*0)+(6*0)+(9*0)=187
255 // (1*5)+(4*6)+(7*7)+(2*9)+(5*10)+(8*11)+(3*0)+(6*0)+(9*0)=234
256 // (1*6)+(4*7)+(7*8)+(2*10)+(5*11)+(8*12)+(3*0)+(6*0)+(9*0)=261
257 // (1*7)+(4*8)+(7*0)+(2*11)+(5*12)+(8*0)+(3*0)+(6*0)+(9*0)=121
258 // This means we should end up with this matrix:
259 // | 105 | 150 | 183 | 95 |
260 // | 235 | 312 | 357 | 178 |
261 // | 187 | 234 | 261 | 121 |
262
263 // Output -> float
264 const int expected_width = image_width;
265 const int expected_height = image_height;
266 Tensor expected_float(
267 DT_FLOAT, TensorShape({image_batch_count, expected_height, expected_width,
268 filter_count}));
269 test::FillValues<float>(&expected_float, {105, 150, 183, 95, 235, 312, 357,
270 178, 187, 234, 261, 121});
271
272 const Tensor& output = *GetOutput(0);
273 const Tensor& output_mkl_metadata = *GetOutput(3);
274
275 ConvMklToTF conv_comp;
276 Tensor output_quantized;
277 conv_comp.ConvertMklToTF<qint32>(DT_QINT32, output, output_mkl_metadata,
278 output_quantized);
279
280 const float output_min = GetOutput(1)->flat<float>()(0);
281 const float output_max = GetOutput(2)->flat<float>()(0);
282 Tensor output_float =
283 QuantizedTensorToFloat<qint32>(output_quantized, output_min, output_max);
284
285 test::ExpectTensorNear<float>(expected_float, output_float, 1.0);
286 }
287
TEST_F(QuantizedConv2DTest,SmallS8)288 TEST_F(QuantizedConv2DTest, SmallS8) {
289 const int stride = 1;
290 const int depth = 1;
291 const int image_width = 3;
292 const int image_height = 3;
293 const int image_batch_count = 1;
294
295 // Image -> uint8
296 const float image_min = -127.0f;
297 const float image_max = 127.0f;
298
299 TF_ASSERT_OK(NodeDefBuilder("quantized_conv_op", "_MklQuantizedConv2D")
300 .Input(FakeInput(DT_QINT8)) // Input
301 .Input(FakeInput(DT_QINT8)) // Filter
302 .Input(FakeInput(DT_FLOAT)) // Min input
303 .Input(FakeInput(DT_FLOAT)) // Max input
304 .Input(FakeInput(DT_FLOAT)) // Min filter
305 .Input(FakeInput(DT_FLOAT)) // Max filter
306 // MKL metadata tensors //
307 .Input(FakeInput(DT_UINT8))
308 .Input(FakeInput(DT_UINT8))
309 .Input(FakeInput(DT_UINT8))
310 .Input(FakeInput(DT_UINT8))
311 .Input(FakeInput(DT_UINT8))
312 .Input(FakeInput(DT_UINT8))
313 ///////////////////////////
314 .Attr("Tinput", DataTypeToEnum<qint8>::v())
315 .Attr("Tfilter", DataTypeToEnum<qint8>::v())
316 .Attr("T", DataTypeToEnum<quint8>::v())
317 .Attr("padding", "VALID")
318 .Attr("out_type", DataTypeToEnum<qint32>::v())
319 .Attr("strides", {1, stride, stride, 1})
320 .Attr("_kernel", "QuantizedMklOp")
321 .Finalize(node_def()));
322 TF_ASSERT_OK(InitOp());
323 // The image matrix is:
324 // | 2 | 3 | 4 |
325 // | 6 | -4 | -2 |
326 // | 3 | 0 | 4 |
327 Tensor image_float(DT_FLOAT,
328 {image_batch_count, image_height, image_width, depth});
329 test::FillValues<float>(&image_float, {2, 3, 4, 6, -4, -2, 3, 0, 4});
330 Tensor image_quantized =
331 FloatTensorToQuantized<qint8>(image_float, image_min, image_max);
332
333 const int filter_size = 3;
334 const int filter_count = 1;
335
336 // Filter -> int8 with symmetric range
337 const float filter_min = -127.0f;
338 const float filter_max = 127.0f;
339
340 // The filter matrix is:
341 // | 1 | 4 | 2 |
342 // | 0 | 5 |-1 |
343 // | 3 |-1 |-3 |
344 Tensor filter_float(DT_FLOAT,
345 {filter_size, filter_size, depth, filter_count});
346 test::FillValues<float>(&filter_float, {1, 4, 2, 0, 5, -1, 3, -1, -3});
347 Tensor filter_quantized =
348 FloatTensorToQuantized<qint8>(filter_float, filter_min, filter_max);
349
350 AddInputFromArray<qint8>(image_quantized.shape(),
351 image_quantized.flat<qint8>());
352 AddInputFromArray<qint8>(filter_quantized.shape(),
353 filter_quantized.flat<qint8>());
354 AddInputFromArray<float>(TensorShape({1}), {image_min});
355 AddInputFromArray<float>(TensorShape({1}), {image_max});
356 AddInputFromArray<float>(TensorShape({1}), {filter_min});
357 AddInputFromArray<float>(TensorShape({1}), {filter_max});
358
359 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
360 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
361 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
362 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
363 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
364 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
365
366 TF_ASSERT_OK(RunOpKernel());
367
368 // Output -> float
369 const int expected_width = 1;
370 const int expected_height = 1;
371 Tensor expected_float(
372 DT_FLOAT, TensorShape({image_batch_count, expected_height, expected_width,
373 filter_count}));
374 test::FillValues<float>(&expected_float, {1});
375
376 const Tensor& output = *GetOutput(0);
377 const Tensor& output_mkl_metadata = *GetOutput(3);
378
379 ConvMklToTF conv_comp;
380 Tensor output_quantized;
381 conv_comp.ConvertMklToTF<qint32>(DT_QINT32, output, output_mkl_metadata,
382 output_quantized);
383
384 const float output_min = GetOutput(1)->flat<float>()(0);
385 const float output_max = GetOutput(2)->flat<float>()(0);
386 Tensor output_float =
387 QuantizedTensorToFloat<qint32>(output_quantized, output_min, output_max);
388
389 test::ExpectTensorNear<float>(expected_float, output_float, 1.0);
390 }
391 // Output -> qint32
TEST_F(QuantizedConv2DTest,Small32Bit)392 TEST_F(QuantizedConv2DTest, Small32Bit) {
393 const int stride = 1;
394 ConfigureQuantizedConv2D(stride);
395
396 // The illustrations and details regarding inputs and outputs
397 // are in TEST_F(QuantizedConv2DTest, Small)
398 const int depth = 1;
399 const int image_width = 4;
400 const int image_height = 3;
401 const int image_batch_count = 1;
402 AddInputFromArray<quint8>(
403 TensorShape({image_batch_count, image_height, image_width, depth}),
404 {10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120});
405
406 const int filter_size = 3;
407 const int filter_count = 1;
408 AddInputFromArray<qint8>(
409 TensorShape({filter_size, filter_size, depth, filter_count}),
410 {10, 40, 70, 20, 50, 80, 30, 60, 90});
411
412 // Image -> uint8
413 AddInputFromArray<float>(TensorShape({1}), {0.0f});
414 AddInputFromArray<float>(TensorShape({1}), {255.0f});
415
416 // Filter -> int8 with symmetric range
417 AddInputFromArray<float>(TensorShape({1}), {-127.0f});
418 AddInputFromArray<float>(TensorShape({1}), {127.0f});
419
420 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
421 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
422 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
423 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
424 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
425 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
426
427 TF_ASSERT_OK(RunOpKernel());
428
429 // Output -> qint32
430 const int expected_width = image_width;
431 const int expected_height = image_height;
432 Tensor expected(DT_QINT32, TensorShape({image_batch_count, expected_height,
433 expected_width, filter_count}));
434 test::FillValues<qint32>(
435 &expected, {10500, 15000, 18300, 9500, 23500, 31200, 35700, 17800, 18700,
436 23400, 26100, 12100});
437
438 const Tensor& output = *GetOutput(0);
439 const Tensor& output_mkl_metadata = *GetOutput(3);
440
441 ConvMklToTF conv_comp;
442 Tensor output_quantized;
443 conv_comp.ConvertMklToTF<qint32>(DT_QINT32, output, output_mkl_metadata,
444 output_quantized);
445
446 test::ExpectTensorEqual<qint32>(expected, output_quantized);
447 }
448
449 // Output -> qint32
TEST_F(QuantizedConv2DTest,Small32BitWithPadding)450 TEST_F(QuantizedConv2DTest, Small32BitWithPadding) {
451 const int stride = 1;
452 TF_ASSERT_OK(NodeDefBuilder("quantized_conv_op", "_MklQuantizedConv2D")
453 .Input(FakeInput(DT_QUINT8)) // Input
454 .Input(FakeInput(DT_QINT8)) // Filter
455 .Input(FakeInput(DT_FLOAT)) // Min input
456 .Input(FakeInput(DT_FLOAT)) // Max input
457 .Input(FakeInput(DT_FLOAT)) // Min filter
458 .Input(FakeInput(DT_FLOAT)) // Max filter
459 // MKL metadata tensors //
460 .Input(FakeInput(DT_UINT8))
461 .Input(FakeInput(DT_UINT8))
462 .Input(FakeInput(DT_UINT8))
463 .Input(FakeInput(DT_UINT8))
464 .Input(FakeInput(DT_UINT8))
465 .Input(FakeInput(DT_UINT8))
466 ///////////////////////////
467 .Attr("Tinput", DataTypeToEnum<quint8>::v())
468 .Attr("Tfilter", DataTypeToEnum<qint8>::v())
469 .Attr("T", DataTypeToEnum<quint8>::v())
470 .Attr("out_type", DataTypeToEnum<qint32>::v())
471 .Attr("strides", {1, stride, stride, 1})
472 .Attr("padding", "SAME")
473 .Attr("padding_list", {0, 0, 1, 1, 1, 1, 0, 0})
474 .Attr("_kernel", "QuantizedMklOp")
475 .Finalize(node_def()));
476 TF_ASSERT_OK(InitOp());
477
478 // The illustrations and details regarding inputs and outputs
479 // are in TEST_F(QuantizedConv2DTest, Small)
480 const int depth = 1;
481 const int image_width = 4;
482 const int image_height = 3;
483 const int image_batch_count = 1;
484 AddInputFromArray<quint8>(
485 TensorShape({image_batch_count, image_height, image_width, depth}),
486 {10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120});
487
488 const int filter_size = 3;
489 const int filter_count = 1;
490 AddInputFromArray<qint8>(
491 TensorShape({filter_size, filter_size, depth, filter_count}),
492 {10, 40, 70, 20, 50, 80, 30, 60, 90});
493
494 // Image -> uint8
495 AddInputFromArray<float>(TensorShape({1}), {0.0f});
496 AddInputFromArray<float>(TensorShape({1}), {255.0f});
497
498 // Filter -> int8 with symmetric range
499 AddInputFromArray<float>(TensorShape({1}), {-127.0f});
500 AddInputFromArray<float>(TensorShape({1}), {127.0f});
501
502 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
503 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
504 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
505 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
506 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
507 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
508
509 TF_ASSERT_OK(RunOpKernel());
510
511 // Output -> qint32
512 const int expected_width = image_width;
513 const int expected_height = image_height;
514 Tensor expected(DT_QINT32, TensorShape({image_batch_count, expected_height,
515 expected_width, filter_count}));
516 test::FillValues<qint32>(
517 &expected, {10500, 15000, 18300, 9500, 23500, 31200, 35700, 17800, 18700,
518 23400, 26100, 12100});
519
520 const Tensor& output = *GetOutput(0);
521 const Tensor& output_mkl_metadata = *GetOutput(3);
522
523 ConvMklToTF conv_comp;
524 Tensor output_quantized;
525 conv_comp.ConvertMklToTF<qint32>(DT_QINT32, output, output_mkl_metadata,
526 output_quantized);
527
528 test::ExpectTensorEqual<qint32>(expected, output_quantized);
529 }
530
531 // Output -> qint32
TEST_F(QuantizedConv2DTest,OddPadding)532 TEST_F(QuantizedConv2DTest, OddPadding) {
533 const int stride = 2;
534 ConfigureQuantizedConv2D(stride);
535
536 const int depth = 1;
537 const int image_width = 4;
538 const int image_height = 4;
539 const int image_batch_count = 1;
540 AddInputFromArray<quint8>(
541 TensorShape({image_batch_count, image_height, image_width, depth}),
542 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
543
544 const int filter_size = 3;
545 const int filter_count = 1;
546 AddInputFromArray<qint8>(
547 TensorShape({filter_size, filter_size, depth, filter_count}),
548 {1, 2, 3, 4, 5, 6, 7, 8, 9});
549
550 // Image -> uint8
551 AddInputFromArray<float>(TensorShape({1}), {0.0f});
552 AddInputFromArray<float>(TensorShape({1}), {255.0f});
553
554 // Filter -> int8 with symmetric range
555 AddInputFromArray<float>(TensorShape({1}), {-127.0f});
556 AddInputFromArray<float>(TensorShape({1}), {127.0f});
557
558 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
559 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
560 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
561 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
562 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
563 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
564
565 TF_ASSERT_OK(RunOpKernel());
566
567 // Output -> qint32
568 const int expected_width = image_width / stride;
569 const int expected_height = image_height / stride;
570 Tensor expected(DT_QINT32, TensorShape({image_batch_count, expected_height,
571 expected_width, filter_count}));
572 test::FillValues<qint32>(&expected, {348, 252, 274, 175});
573
574 const Tensor& output = *GetOutput(0);
575 const Tensor& output_mkl_metadata = *GetOutput(3);
576
577 ConvMklToTF conv_comp;
578 Tensor output_quantized;
579 conv_comp.ConvertMklToTF<qint32>(DT_QINT32, output, output_mkl_metadata,
580 output_quantized);
581
582 test::ExpectTensorEqual<qint32>(expected, output_quantized);
583 }
584
585 // Output -> qint32
TEST_F(QuantizedConv2DTest,OddPaddingBatch)586 TEST_F(QuantizedConv2DTest, OddPaddingBatch) {
587 const int stride = 2;
588 ConfigureQuantizedConv2D(stride);
589
590 const int depth = 1;
591 const int image_width = 4;
592 const int image_height = 4;
593 const int image_batch_count = 3;
594 AddInputFromArray<quint8>(
595 TensorShape({image_batch_count, image_height, image_width, depth}),
596 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
597 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
598 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
599
600 const int filter_size = 3;
601 const int filter_count = 1;
602 AddInputFromArray<qint8>(
603 TensorShape({filter_size, filter_size, depth, filter_count}),
604 {1, 2, 3, 4, 5, 6, 7, 8, 9});
605
606 // Image -> uint8
607 AddInputFromArray<float>(TensorShape({1}), {0.0f});
608 AddInputFromArray<float>(TensorShape({1}), {255.0f});
609
610 // Filter -> int8 with symmetric range
611 AddInputFromArray<float>(TensorShape({1}), {-127.0f});
612 AddInputFromArray<float>(TensorShape({1}), {127.0f});
613
614 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
615 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
616 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
617 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
618 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
619 AddInputFromArray<uint8>(dummy_shape, dummy_tensor);
620
621 TF_ASSERT_OK(RunOpKernel());
622
623 // Output -> qint32
624 const int expected_width = image_width / stride;
625 const int expected_height = image_height / stride;
626 Tensor expected(DT_QINT32, TensorShape({image_batch_count, expected_height,
627 expected_width, filter_count}));
628 test::FillValues<qint32>(
629 &expected, {348, 252, 274, 175, 348, 252, 274, 175, 348, 252, 274, 175});
630
631 const Tensor& output = *GetOutput(0);
632 const Tensor& output_mkl_metadata = *GetOutput(3);
633
634 ConvMklToTF conv_comp;
635 Tensor output_quantized;
636 conv_comp.ConvertMklToTF<qint32>(DT_QINT32, output, output_mkl_metadata,
637 output_quantized);
638
639 test::ExpectTensorEqual<qint32>(expected, output_quantized);
640 }
641
TEST_F(QuantizedConv2DTest,DepthwiseConv2D)642 TEST_F(QuantizedConv2DTest, DepthwiseConv2D) {
643 const int stride = 1;
644 TF_ASSERT_OK(NodeDefBuilder("quantized_depthwise_conv_op",
645 "_MklQuantizedDepthwiseConv2D")
646 .Input(FakeInput(DT_QUINT8)) // Input
647 .Input(FakeInput(DT_QINT8)) // Filter
648 .Input(FakeInput(DT_FLOAT)) // Min input
649 .Input(FakeInput(DT_FLOAT)) // Max input
650 .Input(FakeInput(DT_FLOAT)) // Min filter
651 .Input(FakeInput(DT_FLOAT)) // Max filter
652 // MKL metadata tensors //
653 .Input(FakeInput(DT_UINT8))
654 .Input(FakeInput(DT_UINT8))
655 .Input(FakeInput(DT_UINT8))
656 .Input(FakeInput(DT_UINT8))
657 .Input(FakeInput(DT_UINT8))
658 .Input(FakeInput(DT_UINT8))
659 ///////////////////////////
660 .Attr("Tinput", DataTypeToEnum<quint8>::v())
661 .Attr("Tfilter", DataTypeToEnum<qint8>::v())
662 .Attr("T", DataTypeToEnum<quint8>::v())
663 .Attr("out_type", DataTypeToEnum<qint32>::v())
664 .Attr("strides", {1, stride, stride, 1})
665 .Attr("padding", "SAME")
666 .Attr("_kernel", "QuantizedMklOp")
667 .Finalize(node_def()));
668 TF_ASSERT_OK(InitOp());
669 RunQuantizedDepthwiseConv2DOp(false);
670 }
671
TEST_F(QuantizedConv2DTest,DepthwiseConv2DWithBias)672 TEST_F(QuantizedConv2DTest, DepthwiseConv2DWithBias) {
673 const int stride = 1;
674 TF_ASSERT_OK(NodeDefBuilder("quantized_depthwise_conv_op",
675 "_MklQuantizedDepthwiseConv2DWithBias")
676 .Input(FakeInput(DT_QUINT8)) // Input
677 .Input(FakeInput(DT_QINT8)) // Filter
678 .Input(FakeInput(DT_FLOAT)) // Bias
679 .Input(FakeInput(DT_FLOAT)) // Min input
680 .Input(FakeInput(DT_FLOAT)) // Max input
681 .Input(FakeInput(DT_FLOAT)) // Min filter
682 .Input(FakeInput(DT_FLOAT)) // Max filter
683 // MKL metadata tensors //
684 .Input(FakeInput(DT_UINT8))
685 .Input(FakeInput(DT_UINT8))
686 .Input(FakeInput(DT_UINT8))
687 .Input(FakeInput(DT_UINT8))
688 .Input(FakeInput(DT_UINT8))
689 .Input(FakeInput(DT_UINT8))
690 .Input(FakeInput(DT_UINT8))
691 ///////////////////////////
692 .Attr("Tinput", DataTypeToEnum<quint8>::v())
693 .Attr("Tfilter", DataTypeToEnum<qint8>::v())
694 .Attr("T", DataTypeToEnum<quint8>::v())
695 .Attr("out_type", DataTypeToEnum<qint32>::v())
696 .Attr("strides", {1, stride, stride, 1})
697 .Attr("padding", "SAME")
698 .Attr("_kernel", "QuantizedMklOp")
699 .Finalize(node_def()));
700 TF_ASSERT_OK(InitOp());
701 RunQuantizedDepthwiseConv2DOp(true);
702 }
703
TEST_F(QuantizedConv2DTest,DepthwiseConv2DWithBiasAndRelu)704 TEST_F(QuantizedConv2DTest, DepthwiseConv2DWithBiasAndRelu) {
705 const int stride = 1;
706 TF_ASSERT_OK(NodeDefBuilder("quantized_depthwise_conv_op",
707 "_MklQuantizedDepthwiseConv2DWithBiasAndRelu")
708 .Input(FakeInput(DT_QUINT8)) // Input
709 .Input(FakeInput(DT_QINT8)) // Filter
710 .Input(FakeInput(DT_FLOAT)) // Bias
711 .Input(FakeInput(DT_FLOAT)) // Min input
712 .Input(FakeInput(DT_FLOAT)) // Max input
713 .Input(FakeInput(DT_FLOAT)) // Min filter
714 .Input(FakeInput(DT_FLOAT)) // Max filter
715 // MKL metadata tensors //
716 .Input(FakeInput(DT_UINT8))
717 .Input(FakeInput(DT_UINT8))
718 .Input(FakeInput(DT_UINT8))
719 .Input(FakeInput(DT_UINT8))
720 .Input(FakeInput(DT_UINT8))
721 .Input(FakeInput(DT_UINT8))
722 .Input(FakeInput(DT_UINT8))
723 ///////////////////////////
724 .Attr("Tinput", DataTypeToEnum<quint8>::v())
725 .Attr("Tfilter", DataTypeToEnum<qint8>::v())
726 .Attr("T", DataTypeToEnum<quint8>::v())
727 .Attr("out_type", DataTypeToEnum<qint32>::v())
728 .Attr("strides", {1, stride, stride, 1})
729 .Attr("padding", "SAME")
730 .Attr("_kernel", "QuantizedMklOp")
731 .Finalize(node_def()));
732 TF_ASSERT_OK(InitOp());
733 RunQuantizedDepthwiseConv2DOp(true);
734 }
735 } // namespace tensorflow
736 #endif // INTEL_MKL
737