• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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