From 1baefb95e3f535a75ddf6ff46f8a47ca0fa4e6f6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EB=B0=95=EC=84=B8=ED=9D=AC/=EB=8F=99=EC=9E=91=EC=A0=9C?= =?utf8?q?=EC=96=B4Lab=28SR=29/Principal=20Engineer/=EC=82=BC=EC=84=B1?= =?utf8?q?=EC=A0=84=EC=9E=90?= Date: Wed, 11 Apr 2018 13:22:59 +0900 Subject: [PATCH] Add 3x5 2 kernel convolution test (#552) * Add 3x5 2 kernel convolution test This will add 3x5 with 2 kernel convolution test and fix format conversions Signed-off-by: SaeHie Park * apply review comments --- src/kernel/acl/src/Conv2D_acl.cpp | 37 ++++++++++++++++++++++- src/kernel/acl/src/Conv2D_acl.test.cpp | 55 ++++++++++++++++++++++++++++++++++ src/kernel/acl/src/IO_accessor.cpp | 29 +++++++++++++----- 3 files changed, 112 insertions(+), 9 deletions(-) diff --git a/src/kernel/acl/src/Conv2D_acl.cpp b/src/kernel/acl/src/Conv2D_acl.cpp index a48f982..8f729e7 100644 --- a/src/kernel/acl/src/Conv2D_acl.cpp +++ b/src/kernel/acl/src/Conv2D_acl.cpp @@ -84,6 +84,36 @@ private: ::arm_compute::CLTensor _tensor; }; +// TODO move to separate file +uint32_t getNumItems(const android::nn::Shape& shape) +{ + uint32_t items = 1; + for (uint32_t d = 0; d < shape.dimensions.size(); d++) { + items *= android::nn::getSizeOfDimension(shape, d); + } + return items; +} + +void NCHW2NHWC(const float* nchw, float* nhwc, const android::nn::Shape& shape) +{ + uint32_t N = android::nn::getSizeOfDimension(shape, 0); + uint32_t H = android::nn::getSizeOfDimension(shape, 1); + uint32_t W = android::nn::getSizeOfDimension(shape, 2); + uint32_t C = android::nn::getSizeOfDimension(shape, 3); + + for (uint32_t n = 0; n < N; n++) { + for (uint32_t c = 0; c < C; c++) { + for (uint32_t h = 0; h < H; h++) { + for (uint32_t w = 0; w < W; w++) { + uint32_t soffset = w + (h * W) + (c * W * H) + (n * W * H * C); + uint32_t doffset = c + (w * C) + (h * C * W) + (n * C * W * H); + *(nhwc + doffset) = *(nchw + soffset); + } + } + } + } +} + bool convFloat32(const float* inputData, const android::nn::Shape& inputShape, const float* filterData, const android::nn::Shape& filterShape, const float* biasData, const android::nn::Shape& biasShape, @@ -123,7 +153,12 @@ bool convFloat32(const float* inputData, const android::nn::Shape& inputShape, arm_compute::CLScheduler::get().sync(); - TensorAccess(output.ref(), outputData, outputShape); + // TODO put conversion inside OutputAccessor + uint32_t numItems = getNumItems(outputShape); + float* outputDataT = new float[numItems]; + TensorAccess(output.ref(), outputDataT, outputShape); + NCHW2NHWC(outputDataT, outputData, outputShape); + delete [] outputDataT; return true; } diff --git a/src/kernel/acl/src/Conv2D_acl.test.cpp b/src/kernel/acl/src/Conv2D_acl.test.cpp index e2baff9..8e632d3 100644 --- a/src/kernel/acl/src/Conv2D_acl.test.cpp +++ b/src/kernel/acl/src/Conv2D_acl.test.cpp @@ -17,6 +17,9 @@ bool convFloat32(const float* inputData, const android::nn::Shape& inputShape, int32_t activation, float* outputData, const android::nn::Shape& outputShape); +// TODO move to separate header +void NCHW2NHWC(const float* nchw, float* nhwc, const android::nn::Shape& shape); + } // namespace acl } // namespace kernel } // namespace nnfw @@ -103,3 +106,55 @@ TEST(KernelACL_TC, convFloat32_3x3to3x3) EXPECT_EQ(bret, true); } +TEST(KernelACL_TC, convFloat32_3x5to3x3) +{ + float inputData[15] = { + 1,2,3,4,5, + 6,7,8,9,10, + 11,12,13,14,15 + }; + const android::nn::Shape inputShape = { OperandType::FLOAT32, {1,3,5,1}, 1.0, 0 }; + float filterData[18] = { + 1,1,1, 1,1,1, 1,1,1, + 2,2,2, 2,2,2, 2,2,2 + }; + const android::nn::Shape filterShape = { OperandType::FLOAT32, {2,3,3,1}, 1.0, 0 }; + float biasData[2] = { 1.0, 1.0 }; + const android::nn::Shape biasShape = { OperandType::FLOAT32, {2}, 1.0, 0 }; + int32_t padding_left = 1; + int32_t padding_right = 1; + int32_t padding_top = 1; + int32_t padding_bottom = 1; + int32_t stride_width = 1; + int32_t stride_height = 1; + int32_t activation = static_cast(FusedActivationFunc::RELU); + float outputData[30]; + const android::nn::Shape outputShape = { OperandType::FLOAT32, {1,3,5,2}, 1.0, 0 }; + bool bret; + + util::initData(outputData, sizeof(outputData) / sizeof(outputData[0]), 0.0); + + bret = convFloat32(inputData, inputShape, + filterData, filterShape, + biasData, biasShape, + padding_left, padding_right, + padding_top, padding_bottom, + stride_width, stride_height, + activation, + outputData, outputShape); + EXPECT_EQ(bret, true); + + float expectNCHW[] = { + 17.0f, 28.0f, 34.0f, 40.0f, 29.0f, + 40.0f, 64.0f, 73.0f, 82.0f, 58.0f, + 37.0f, 58.0f, 64.0f, 70.0f, 49.0f, + + 33.0f, 55.0f, 67.0f, 79.0f, 57.0f, + 79.0f, 127.0f, 145.0f, 163.0f, 115.0f, + 73.0f, 115.0f, 127.0f, 139.0f, 97.0f + }; + float expectData[30]; + NCHW2NHWC(expectNCHW, expectData, outputShape); + bret = util::compareData(outputData, expectData, outputShape); + EXPECT_EQ(bret, true); +} diff --git a/src/kernel/acl/src/IO_accessor.cpp b/src/kernel/acl/src/IO_accessor.cpp index 441ef47..400fd83 100644 --- a/src/kernel/acl/src/IO_accessor.cpp +++ b/src/kernel/acl/src/IO_accessor.cpp @@ -28,6 +28,23 @@ OutputAccessor::OutputAccessor(float* outputData, const android::nn::Shape& outp { } +static uint32_t getOffsetNCHW(const android::nn::Shape& shape, const arm_compute::Coordinates& id) +{ + // get offset for ACL(NCHW) from data of NNAPI(NHWC) + uint32_t num = getSizeOfDimension(shape, 0); + uint32_t height = getSizeOfDimension(shape, 1); + uint32_t width = getSizeOfDimension(shape, 2); + uint32_t chann = getSizeOfDimension(shape, 3); + uint32_t stride = 1; + uint32_t offset = 0; + uint32_t numdim = id.num_dimensions(); + offset += numdim > 0 ? id[0] * stride : 0; stride *= width; + offset += numdim > 1 ? id[1] * stride : 0; stride *= height; + offset += numdim > 2 ? id[2] * stride : 0; stride *= chann; + offset += numdim > 3 ? id[3] * stride : 0; stride *= num; + return offset; +} + bool InputAccessor::access_tensor(arm_compute::ITensor &tensor) { arm_compute::Window window; @@ -35,8 +52,7 @@ bool InputAccessor::access_tensor(arm_compute::ITensor &tensor) execute_window_loop(window, [&](const arm_compute::Coordinates& id) { - uint32_t width = android::nn::getSizeOfDimension(_inputShape, 2); - uint32_t offset = id.y() * width + id.x(); + uint32_t offset = getOffsetNCHW(_inputShape, id); *reinterpret_cast(tensor.ptr_to_element(id)) = *(_inputData + offset); }); @@ -50,8 +66,7 @@ bool WeightAccessor::access_tensor(arm_compute::ITensor &tensor) execute_window_loop(window, [&](const arm_compute::Coordinates& id) { - uint32_t width = android::nn::getSizeOfDimension(_filterShape, 2); - uint32_t offset = id.y() * width + id.x(); + uint32_t offset = getOffsetNCHW(_filterShape, id); *reinterpret_cast(tensor.ptr_to_element(id)) = *(_filterData + offset); }); @@ -65,8 +80,7 @@ bool BiasAccessor::access_tensor(arm_compute::ITensor &tensor) execute_window_loop(window, [&](const arm_compute::Coordinates& id) { - uint32_t width = android::nn::getSizeOfDimension(_biasShape, 2); - uint32_t offset = id.y() * width + id.x(); + uint32_t offset = getOffsetNCHW(_biasShape, id); *reinterpret_cast(tensor.ptr_to_element(id)) = *(_biasData + offset); }); @@ -80,8 +94,7 @@ bool OutputAccessor::access_tensor(arm_compute::ITensor &tensor) execute_window_loop(window, [&](const arm_compute::Coordinates& id) { - uint32_t width = android::nn::getSizeOfDimension(_outputShape, 2); - uint32_t offset = id.y() * width + id.x(); + uint32_t offset = getOffsetNCHW(_outputShape, id); *(_outputData + offset) = *reinterpret_cast(tensor.ptr_to_element(id)); }); -- 2.7.4