1 // 2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 // 5 6 #include "ParserFlatbuffersFixture.hpp" 7 #include <sstream> 8 9 TEST_SUITE("TensorflowLiteParser_Conv2D") 10 { 11 struct SimpleConv2DFixture : public ParserFlatbuffersFixture 12 { SimpleConv2DFixtureSimpleConv2DFixture13 explicit SimpleConv2DFixture() 14 { 15 m_JsonString = R"( 16 { 17 "version": 3, 18 "operator_codes": [ { "builtin_code": "CONV_2D" } ], 19 "subgraphs": [ { 20 "tensors": [ 21 { 22 "shape": [ 1, 3, 3, 1 ], 23 "type": "UINT8", 24 "buffer": 0, 25 "name": "inputTensor", 26 "quantization": { 27 "min": [ 0.0 ], 28 "max": [ 255.0 ], 29 "scale": [ 1.0 ], 30 "zero_point": [ 0 ], 31 } 32 }, 33 { 34 "shape": [ 1, 1, 1, 1 ], 35 "type": "UINT8", 36 "buffer": 1, 37 "name": "outputTensor", 38 "quantization": { 39 "min": [ 0.0 ], 40 "max": [ 511.0 ], 41 "scale": [ 2.0 ], 42 "zero_point": [ 0 ], 43 } 44 }, 45 { 46 "shape": [ 1, 3, 3, 1 ], 47 "type": "UINT8", 48 "buffer": 2, 49 "name": "filterTensor", 50 "quantization": { 51 "min": [ 0.0 ], 52 "max": [ 255.0 ], 53 "scale": [ 1.0 ], 54 "zero_point": [ 0 ], 55 } 56 } 57 ], 58 "inputs": [ 0 ], 59 "outputs": [ 1 ], 60 "operators": [ 61 { 62 "opcode_index": 0, 63 "inputs": [ 0, 2 ], 64 "outputs": [ 1 ], 65 "builtin_options_type": "Conv2DOptions", 66 "builtin_options": { 67 "padding": "VALID", 68 "stride_w": 1, 69 "stride_h": 1, 70 "fused_activation_function": "NONE" 71 }, 72 "custom_options_format": "FLEXBUFFERS" 73 } 74 ], 75 } ], 76 "buffers" : [ 77 { }, 78 { }, 79 { "data": [ 2,1,0, 6,2,1, 4,1,2 ], }, 80 { }, 81 ] 82 } 83 )"; 84 SetupSingleInputSingleOutput("inputTensor", "outputTensor"); 85 } 86 }; 87 88 TEST_CASE_FIXTURE(SimpleConv2DFixture, "ParseSimpleConv2D") 89 { 90 RunTest<4, armnn::DataType::QAsymmU8>( 91 0, 92 { 93 1, 2, 3, 94 4, 5, 6, 95 7, 8, 9, 96 }, 97 // because of the output scaling we need to take half of the values 98 { 99 (1*2 + 2*1 + 3*0 + 100 4*6 + 5*2 + 6*1 + 101 7*4 + 8*1 + 9*2) /2 102 }); 103 } 104 105 struct Conv2DWithBiasesFixture : public ParserFlatbuffersFixture 106 { Conv2DWithBiasesFixtureConv2DWithBiasesFixture107 explicit Conv2DWithBiasesFixture(const std::string& inputShape, 108 const std::string& outputShape, 109 const std::string& filterShape, 110 const std::string& filterData, 111 const std::string& biasShape, 112 const std::string& biasData, 113 const std::string& strides, 114 const std::string& activation="NONE", 115 const std::string& filterScale="1.0", 116 const std::string& filterZeroPoint="0", 117 const std::string& outputScale="2.0", 118 const std::string& outputZeroPoint="0", 119 const std::string& dataType = "UINT8", 120 const std::string& filterDataType = "UINT8", 121 const std::string& biasDataType = "INT32") 122 { 123 m_JsonString = R"( 124 { 125 "version": 3, 126 "operator_codes": [ { "builtin_code": "CONV_2D" } ], 127 "subgraphs": [ { 128 "tensors": [ 129 { 130 "shape": )" + inputShape + R"(, 131 "type": )" + dataType + R"(, 132 "buffer": 0, 133 "name": "inputTensor", 134 "quantization": { 135 "min": [ 0.0 ], 136 "max": [ 255.0 ], 137 "scale": [ 1.0 ], 138 "zero_point": [ 0 ], 139 } 140 }, 141 { 142 "shape": )" + outputShape + R"(, 143 "type": )" + dataType + R"(, 144 "buffer": 1, 145 "name": "outputTensor", 146 "quantization": { 147 "min": [ 0.0 ], 148 "max": [ 511.0 ], 149 "scale": [ )" + outputScale + R"( ], 150 "zero_point": [ )" + outputZeroPoint + R"( ], 151 } 152 }, 153 { 154 "shape": )" + filterShape + R"( , 155 "type": )" + filterDataType + R"(, 156 "buffer": 2, 157 "name": "filterTensor", 158 "quantization": { 159 "min": [ 0.0 ], 160 "max": [ 255.0 ], 161 "scale": [ )" + filterScale + R"( ], 162 "zero_point": [ )" + filterZeroPoint + R"( ], 163 } 164 }, 165 { 166 "shape": )" + biasShape + R"( , 167 "type": )" + biasDataType + R"(, 168 "buffer": 3, 169 "name": "biasTensor", 170 "quantization": { 171 "min": [ 0.0 ], 172 "max": [ 255.0 ], 173 "scale": [ 1.0 ], 174 "zero_point": [ 0 ], 175 } 176 } 177 ], 178 "inputs": [ 0 ], 179 "outputs": [ 1 ], 180 "operators": [ 181 { 182 "opcode_index": 0, 183 "inputs": [ 0, 2, 3 ], 184 "outputs": [ 1 ], 185 "builtin_options_type": "Conv2DOptions", 186 "builtin_options": { 187 "padding": "SAME", 188 "stride_w": )" + strides + R"(, 189 "stride_h": )" + strides + R"(, 190 "fused_activation_function": )" + activation + R"( 191 }, 192 "custom_options_format": "FLEXBUFFERS" 193 } 194 ], 195 } ], 196 "buffers" : [ 197 { }, 198 { }, 199 { "data": )" + filterData + R"(, }, 200 { "data": )" + biasData + R"(, }, 201 ] 202 } 203 )"; 204 SetupSingleInputSingleOutput("inputTensor", "outputTensor"); 205 } 206 }; 207 208 struct SimpleConv2DWithBiasesFixture : Conv2DWithBiasesFixture 209 { SimpleConv2DWithBiasesFixtureSimpleConv2DWithBiasesFixture210 SimpleConv2DWithBiasesFixture() 211 : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape 212 "[ 1, 2, 2, 1 ]", // outputShape 213 "[ 1, 2, 2, 1 ]", // filterShape 214 "[ 2,1, 0,6 ]", // filterData 215 "[ 1 ]", // biasShape 216 "[ 10, 0, 0, 0 ]", // biasData 217 "1") // stride w and h 218 {} 219 }; 220 221 TEST_CASE_FIXTURE(SimpleConv2DWithBiasesFixture, "ParseConv2DWithBias") 222 { 223 RunTest<4, armnn::DataType::QAsymmU8>( 224 0, 225 { 226 1, 2, 227 3, 4, 228 }, 229 // because of the output scaling we need to take half of the values 230 { 231 (1*2 + 2*1 + 3*0 + 4*6 + 10)/2, 232 (2*2 + 0*1 + 4*0 + 0*6 + 10)/2, 233 (3*2 + 4*1 + 0*0 + 0*6 + 10)/2, 234 (4*2 + 0*1 + 0*0 + 0*6 + 10)/2 235 }); 236 } 237 238 struct DynamicConv2DWithBiasesFixture : Conv2DWithBiasesFixture 239 { DynamicConv2DWithBiasesFixtureDynamicConv2DWithBiasesFixture240 DynamicConv2DWithBiasesFixture() 241 : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape 242 "[ ]", // outputShape 243 "[ 1, 2, 2, 1 ]", // filterShape 244 "[ 2,1, 0,6 ]", // filterData 245 "[ 1 ]", // biasShape 246 "[ 10, 0, 0, 0 ]", // biasData 247 "1") // stride w and h 248 {} 249 }; 250 251 TEST_CASE_FIXTURE(DynamicConv2DWithBiasesFixture, "ParseDynamicConv2DWithBias") 252 { 253 RunTest<4, 254 armnn::DataType::QAsymmU8, 255 armnn::DataType::QAsymmU8>(0, 256 { { "inputTensor", { 1, 2, 3, 4, } } }, 257 { { "outputTensor", { (1*2 + 2*1 + 3*0 + 4*6 + 10)/2, 258 (2*2 + 0*1 + 4*0 + 0*6 + 10)/2, 259 (3*2 + 4*1 + 0*0 + 0*6 + 10)/2, 260 (4*2 + 0*1 + 0*0 + 0*6 + 10)/2} } }, 261 true); 262 } 263 264 struct Conv2DShapeTestFixture : Conv2DWithBiasesFixture 265 { GenerateIntsConv2DShapeTestFixture266 static std::string GenerateInts(unsigned int n) 267 { 268 std::stringstream ss; 269 ss << " [ "; 270 for( unsigned int i=0; i<n; ++i ) { 271 if (i > 0 ) 272 { 273 ss << " , "; 274 } 275 ss << " " << (i%256); 276 } 277 ss << " ] "; 278 return ss.str(); 279 } 280 Conv2DShapeTestFixtureConv2DShapeTestFixture281 Conv2DShapeTestFixture() 282 : Conv2DWithBiasesFixture("[ 1, 224, 224, 3 ]", // inputShape 283 "[ 1, 112, 112, 32 ]", // outputShape 284 "[ 32, 3, 3, 3 ]", // filterShape 285 GenerateInts(32*3*3*3), // filterData 286 "[ 32 ]", // biasShape 287 GenerateInts(32*4), // biasData 288 "2") // stride w and h 289 {} 290 }; 291 292 TEST_CASE_FIXTURE(Conv2DShapeTestFixture, "ParseConv2D_112x112_out") 293 { 294 } 295 296 struct ReluConv2DWithBiasesFixture : Conv2DWithBiasesFixture 297 { ReluConv2DWithBiasesFixtureReluConv2DWithBiasesFixture298 ReluConv2DWithBiasesFixture() 299 : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape 300 "[ 1, 2, 2, 1 ]", // outputShape 301 "[ 1, 2, 2, 1 ]", // filterShape 302 "[ 2,1, 0,6 ]", // filterData 303 "[ 1 ]", // biasShape 304 "[ 16, 0, 0, 0 ]", // biasData 305 "1", // stride w and h 306 "RELU", // activation 307 "1.0", // filter scale 308 "4", // filter zero point 309 "2.0", // output scale 310 "20") // output zero point 311 {} 312 }; 313 314 TEST_CASE_FIXTURE(ReluConv2DWithBiasesFixture, "ParseConv2DAndReluWithBias") 315 { 316 uint8_t bias = 16; 317 uint8_t outZero = 20; 318 uint8_t fz = 4; // filter zero point 319 320 RunTest<4, armnn::DataType::QAsymmU8>( 321 0, 322 { 323 1, 2, 324 4, 8, 325 }, 326 // factors to consider: 327 // - the filter zero point is non zero, hence the (x-fz) 328 // - the output scale is 2 hence the /2 329 // - output zero point is non zero, hence the +outZero 330 // - RELU cuts negative values and then we add the output zero point 331 { 332 std::max(outZero, static_cast<uint8_t>((1*(2-fz) + 2*(1-fz) + 4*(0-fz) + 8*(6-fz) + bias)/2 + outZero)), 333 std::max(outZero, static_cast<uint8_t>((2*(2-fz) + 0*(1-fz) + 8*(0-fz) + 0*(6-fz) + bias)/2 + outZero)), 334 std::max(outZero, static_cast<uint8_t>((4*(2-fz) + 8*(1-fz) + 0*(0-fz) + 0*(6-fz) + bias)/2 + outZero)), 335 std::max(outZero, static_cast<uint8_t>((8*(2-fz) + 0*(1-fz) + 0*(0-fz) + 0*(6-fz) + bias)/2 + outZero)) 336 }); 337 } 338 339 struct Relu6Conv2DWithBiasesFixture : Conv2DWithBiasesFixture 340 { Relu6Conv2DWithBiasesFixtureRelu6Conv2DWithBiasesFixture341 Relu6Conv2DWithBiasesFixture() 342 : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape 343 "[ 1, 2, 2, 1 ]", // outputShape 344 "[ 1, 2, 2, 1 ]", // filterShape 345 "[ 2,1, 0,6 ]", // filterData 346 "[ 1 ]", // biasShape 347 "[ 0, 0, 0, 0 ]", // biasData 348 "1", // stride w and h 349 "RELU6", // activation 350 "1.0", // filter scale 351 "0", // filter zero point 352 "2.0", // output scale 353 "0") // output zero point 354 {} 355 }; 356 357 TEST_CASE_FIXTURE(Relu6Conv2DWithBiasesFixture, "ParseConv2DAndRelu6WithBias") 358 { 359 uint8_t relu6Min = 6 / 2; // divide by output scale 360 361 RunTest<4, armnn::DataType::QAsymmU8>( 362 0, 363 { 364 1, 2, 365 4, 1, 366 }, 367 // factors to consider: 368 // - the output scale is 2 hence the /2 369 // - RELU6 cuts output values at +6 370 { 371 std::min(relu6Min, static_cast<uint8_t>((1*2 + 2*1 + 4*0 + 1*6)/2)), 372 std::min(relu6Min, static_cast<uint8_t>((2*2 + 0*1 + 1*0 + 0*6)/2)), 373 std::min(relu6Min, static_cast<uint8_t>((4*2 + 1*1 + 0*0 + 0*6)/2)), 374 std::min(relu6Min, static_cast<uint8_t>((1*2 + 0*1 + 0*0 + 0*6)/2)) 375 }); 376 } 377 378 379 struct PerChannelConv2DFixture : public ParserFlatbuffersFixture 380 { PerChannelConv2DFixturePerChannelConv2DFixture381 explicit PerChannelConv2DFixture() 382 { 383 m_JsonString = R"( 384 { 385 "version": 3, 386 "operator_codes": [ 387 { 388 "builtin_code": "CONV_2D", 389 "version": 3 390 } 391 ], 392 "subgraphs": [ 393 { 394 "tensors": [ 395 { 396 "shape": [ 397 1, 398 4, 399 4, 400 2 401 ], 402 "type": "INT8", 403 "buffer": 1, 404 "name": "input", 405 "quantization": { 406 "min": [ 407 -50.0 408 ], 409 "max": [ 410 49.0 411 ], 412 "scale": [ 413 0.388235 414 ], 415 "zero_point": [ 416 1 417 ], 418 "details_type": "NONE", 419 "quantized_dimension": 0 420 }, 421 "is_variable": false 422 }, 423 { 424 "shape": [ 425 4 426 ], 427 "type": "INT32", 428 "buffer": 2, 429 "name": "model/conv2d/Conv2D", 430 "quantization": { 431 "scale": [ 432 0.001523, 433 0.001197, 434 0.001517, 435 0.001364 436 ], 437 "zero_point": [ 438 0, 439 0, 440 0, 441 0 442 ], 443 "details_type": "NONE", 444 "quantized_dimension": 0 445 }, 446 "is_variable": false 447 }, 448 { 449 "shape": [ 450 4, 451 2, 452 2, 453 2 454 ], 455 "type": "INT8", 456 "buffer": 3, 457 "name": "model/conv2d/Conv2D1", 458 "quantization": { 459 "min": [ 460 -0.498056, 461 -0.362561, 462 -0.307959, 463 -0.207799 464 ], 465 "max": [ 466 0.339136, 467 0.391629, 468 0.496193, 469 0.446191 470 ], 471 "scale": [ 472 0.003922, 473 0.003084, 474 0.003907, 475 0.003513 476 ], 477 "zero_point": [ 478 0, 479 0, 480 0, 481 0 482 ], 483 "details_type": "NONE", 484 "quantized_dimension": 0 485 }, 486 "is_variable": false 487 }, 488 { 489 "shape": [ 490 1, 491 4, 492 4, 493 4 494 ], 495 "type": "INT8", 496 "buffer": 4, 497 "name": "Identity", 498 "quantization": { 499 "min": [ 500 -66.578751 501 ], 502 "max": [ 503 70.137619 504 ], 505 "scale": [ 506 0.536143 507 ], 508 "zero_point": [ 509 -4 510 ], 511 "details_type": "NONE", 512 "quantized_dimension": 0 513 }, 514 "is_variable": false 515 } 516 ], 517 "inputs": [ 518 0 519 ], 520 "outputs": [ 521 3 522 ], 523 "operators": [ 524 { 525 "opcode_index": 0, 526 "inputs": [ 527 0, 528 2, 529 1 530 ], 531 "outputs": [ 532 3 533 ], 534 "builtin_options_type": "Conv2DOptions", 535 "builtin_options": { 536 "padding": "SAME", 537 "stride_w": 1, 538 "stride_h": 1, 539 "fused_activation_function": "NONE", 540 "dilation_w_factor": 1, 541 "dilation_h_factor": 1 542 }, 543 "custom_options_format": "FLEXBUFFERS" 544 } 545 ], 546 "name": "main" 547 } 548 ], 549 "description": "MLIR Converted.", 550 "buffers": [ 551 { 552 }, 553 { 554 }, 555 { 556 "data": [ 557 0, 558 0, 559 0, 560 0, 561 0, 562 0, 563 0, 564 0, 565 0, 566 0, 567 0, 568 0, 569 0, 570 0, 571 0, 572 0 573 ] 574 }, 575 { 576 "data": [ 577 157, 578 201, 579 86, 580 129, 581 17, 582 33, 583 209, 584 13, 585 76, 586 249, 587 127, 588 138, 589 35, 590 18, 591 250, 592 233, 593 15, 594 205, 595 98, 596 127, 597 68, 598 196, 599 246, 600 177, 601 65, 602 197, 603 230, 604 246, 605 127, 606 66, 607 212, 608 30 609 ] 610 }, 611 { 612 }, 613 { 614 "data": [ 615 49, 616 46, 617 53, 618 46, 619 48, 620 0, 621 0, 622 0, 623 0, 624 0, 625 0, 626 0, 627 0, 628 0, 629 0, 630 0 631 ] 632 } 633 ], 634 "metadata": [ 635 { 636 "name": "min_runtime_version", 637 "buffer": 5 638 } 639 ] 640 } 641 )"; 642 SetupSingleInputSingleOutput("input", "Identity"); 643 } 644 }; 645 646 TEST_CASE_FIXTURE(PerChannelConv2DFixture, "ParsePerChannelConv2D") 647 { 648 RunTest<4, armnn::DataType::QAsymmS8>( 649 0, 650 { 651 -11, 40,-26, 11,-28, 8, 0, -8, 652 -10, 34, 47, 0,-33,-14, 28, 35, 653 6,-28,-26, 8, 13, 33,-31,-41, 654 31,-20,-31,-16, 8,-18,-44, 0 655 }, 656 { 657 -21,-17,-23,-14, -1,-14, 1, 9, 658 1,-12,-22,-23, 2, -1, -3, 12, 659 7, 6, 8,-13,-21, -6,-31, 0, 660 9, -6, 24, 0,-22, -4, -7,-22, 661 -7, -9, 9, 11,-11,-16, 9,-27, 662 -1, 0,-26, 0, 9,-12, -8,-18, 663 -11, -3,-15, 7, 16, -2, -8, -7, 664 -14,-15,-14, 3, 9,-12, -6,-11 665 }); 666 } 667 668 struct Conv2FloatWithInt8WeightsAndBiasesFixture : Conv2DWithBiasesFixture 669 { Conv2FloatWithInt8WeightsAndBiasesFixtureConv2FloatWithInt8WeightsAndBiasesFixture670 Conv2FloatWithInt8WeightsAndBiasesFixture() 671 : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape 672 "[ 1, 2, 2, 1 ]", // outputShape 673 "[ 1, 2, 2, 1 ]", // filterShape 674 "[ 2,1, 0,6 ]", // filterData 675 "[ 1 ]", // biasShape 676 "[ 10 ]", // biasData 677 "1", // stride w and h 678 "NONE", // activation 679 "1.0", // filterScale 680 "0", // filterZeroPoint 681 "2.0", // outputScale 682 "0", // outputZeroPoint 683 "FLOAT32", // dataType 684 "INT8", // filterDataType 685 "INT8") // biasDataType 686 {} 687 }; 688 689 TEST_CASE_FIXTURE(Conv2FloatWithInt8WeightsAndBiasesFixture, "ParseConv2FloatWithInt8WeightsAndBiasesFixture") 690 { 691 RunTest<4, armnn::DataType::Float32>( 692 0, 693 { 694 1, 2, 695 3, 4, 696 }, 697 { 698 (1*2 + 2*1 + 3*0 + 4*6 + 10), 699 (2*2 + 0*1 + 4*0 + 0*6 + 10), 700 (3*2 + 4*1 + 0*0 + 0*6 + 10), 701 (4*2 + 0*1 + 0*0 + 0*6 + 10) 702 }); 703 } 704 705 } 706