diff options
author | Georgios Pinitas <georgios.pinitas@arm.com> | 2017-06-21 16:44:24 +0100 |
---|---|---|
committer | Anthony Barbier <anthony.barbier@arm.com> | 2018-09-17 14:15:39 +0100 |
commit | 7b7858df42fccefbe6eb086ad516d5c011becd07 (patch) | |
tree | 92d8671160cd440647b6ee8aa7f949cf9896f6bb /tests/validation | |
parent | d0ae8b8ac8a371fd552c46d4b3be3db8628a5ade (diff) | |
download | ComputeLibrary-7b7858df42fccefbe6eb086ad516d5c011becd07.tar.gz |
COMPMID-359: Implement NEON ROIPoolingLayer
Change-Id: Ibffa738d4016d7221968bd43a4e6e1dab85baee8
Reviewed-on: http://mpd-gerrit.cambridge.arm.com/78623
Reviewed-by: Moritz Pflanzer <moritz.pflanzer@arm.com>
Reviewed-by: Gian Marco Iodice <gianmarco.iodice@arm.com>
Tested-by: Kaizen <jeremy.johnson+kaizengerrit@arm.com>
Diffstat (limited to 'tests/validation')
-rw-r--r-- | tests/validation/Helpers.cpp | 72 | ||||
-rw-r--r-- | tests/validation/Helpers.h | 14 | ||||
-rw-r--r-- | tests/validation/NEON/ROIPoolingLayer.cpp | 106 | ||||
-rw-r--r-- | tests/validation/Reference.cpp | 23 | ||||
-rw-r--r-- | tests/validation/Reference.h | 8 | ||||
-rw-r--r-- | tests/validation/ReferenceCPP.cpp | 8 | ||||
-rw-r--r-- | tests/validation/ReferenceCPP.h | 10 | ||||
-rw-r--r-- | tests/validation/TensorOperations.h | 72 | ||||
-rw-r--r-- | tests/validation/TensorVisitors.h | 23 |
9 files changed, 335 insertions, 1 deletions
diff --git a/tests/validation/Helpers.cpp b/tests/validation/Helpers.cpp new file mode 100644 index 0000000000..747260ea99 --- /dev/null +++ b/tests/validation/Helpers.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "validation/Helpers.h" + +using namespace arm_compute::test; + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +std::vector<ROI> generate_random_rois(const TensorShape &shape, const ROIPoolingLayerInfo &pool_info, unsigned int num_rois, std::random_device::result_type seed) +{ + ARM_COMPUTE_ERROR_ON((pool_info.pooled_width() < 4) || (pool_info.pooled_height() < 4)); + + std::vector<ROI> rois; + std::mt19937 gen(seed); + const int pool_width = pool_info.pooled_width(); + const int pool_height = pool_info.pooled_height(); + const float roi_scale = pool_info.spatial_scale(); + + // Calculate distribution bounds + const auto scaled_width = static_cast<int>((shape.x() / roi_scale) / pool_width); + const auto scaled_height = static_cast<int>((shape.y() / roi_scale) / pool_height); + const auto min_width = static_cast<int>(pool_width / roi_scale); + const auto min_height = static_cast<int>(pool_height / roi_scale); + + // Create distributions + std::uniform_int_distribution<int> dist_batch(0, shape[3] - 1); + std::uniform_int_distribution<int> dist_x(0, scaled_width); + std::uniform_int_distribution<int> dist_y(0, scaled_height); + std::uniform_int_distribution<int> dist_w(min_width, std::max(min_width, (pool_width - 2) * scaled_width)); + std::uniform_int_distribution<int> dist_h(min_height, std::max(min_height, (pool_height - 2) * scaled_height)); + + for(unsigned int r = 0; r < num_rois; ++r) + { + ROI roi{}; + roi.batch_idx = dist_batch(gen); + roi.rect.x = dist_x(gen); + roi.rect.y = dist_y(gen); + roi.rect.width = dist_w(gen); + roi.rect.height = dist_h(gen); + rois.push_back(roi); + } + + return rois; +} +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/Helpers.h b/tests/validation/Helpers.h index c0c5865e56..d92699d93e 100644 --- a/tests/validation/Helpers.h +++ b/tests/validation/Helpers.h @@ -27,8 +27,12 @@ #include "Types.h" #include "ValidationUserConfiguration.h" +#include "arm_compute/core/Types.h" + +#include <random> #include <type_traits> #include <utility> +#include <vector> namespace arm_compute { @@ -168,6 +172,16 @@ inline void fill_mask_from_pattern(uint8_t *mask, int cols, int rows, MatrixPatt } } +/** Create a vector of random ROIs. + * + * @param[in] shape The shape of the input tensor. + * @param[in] pool_info The ROI pooling information. + * @param[in] num_rois The number of ROIs to be created. + * @param[in] seed The random seed to be used. + * + * @return A vector that contains the requested number of random ROIs + */ +std::vector<ROI> generate_random_rois(const TensorShape &shape, const ROIPoolingLayerInfo &pool_info, unsigned int num_rois, std::random_device::result_type seed); } // namespace validation } // namespace test } // namespace arm_compute diff --git a/tests/validation/NEON/ROIPoolingLayer.cpp b/tests/validation/NEON/ROIPoolingLayer.cpp new file mode 100644 index 0000000000..20aaefd3de --- /dev/null +++ b/tests/validation/NEON/ROIPoolingLayer.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "NEON/Helper.h" +#include "NEON/NEAccessor.h" +#include "TypePrinter.h" +#include "arm_compute/runtime/NEON/functions/NEROIPoolingLayer.h" +#include "validation/Datasets.h" +#include "validation/Helpers.h" +#include "validation/Reference.h" +#include "validation/Validation.h" +#include "validation/ValidationUserConfiguration.h" + +#include <random> + +using namespace arm_compute; +using namespace arm_compute::test; +using namespace arm_compute::test::neon; +using namespace arm_compute::test::validation; + +namespace +{ +Tensor compute_roi_pooling_layer(const TensorShape &shape, DataType dt, const std::vector<ROI> &rois, ROIPoolingLayerInfo pool_info) +{ + TensorShape shape_dst; + shape_dst.set(0, pool_info.pooled_width()); + shape_dst.set(1, pool_info.pooled_height()); + shape_dst.set(2, shape.z()); + shape_dst.set(3, rois.size()); + + // Create tensors + Tensor src = create_tensor(shape, dt); + Tensor dst = create_tensor(shape_dst, dt); + Array<ROI> rois_array = create_array(rois); + + // Create and configure function + NEROIPoolingLayer roi_pool; + roi_pool.configure(&src, &rois_array, &dst, pool_info); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + + BOOST_TEST(!src.info()->is_resizable()); + BOOST_TEST(!dst.info()->is_resizable()); + + // Fill tensors + std::uniform_real_distribution<> distribution(-1, 1); + library->fill(NEAccessor(src), distribution, 0); + + // Compute function + roi_pool.run(); + + return dst; +} +} // namespace + +#ifndef DOXYGEN_SKIP_THIS +BOOST_AUTO_TEST_SUITE(NEON) +BOOST_AUTO_TEST_SUITE(ROIPoolingLayer) + +BOOST_AUTO_TEST_SUITE(Float) +BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit")) +BOOST_DATA_TEST_CASE(RunSmall, CNNFloatDataTypes() * boost::unit_test::data::make({ 10, 20, 40 }) * boost::unit_test::data::make({ 7, 9 }) * boost::unit_test::data::make({ 1.f / 8.f, 1.f / 16.f }), + dt, num_rois, roi_pool_size, roi_scale) +{ + TensorShape shape(50U, 47U, 2U, 3U); + ROIPoolingLayerInfo pool_info(roi_pool_size, roi_pool_size, roi_scale); + + // Construct ROI vector + std::vector<ROI> rois = generate_random_rois(shape, pool_info, num_rois, user_config.seed); + + // Compute function + Tensor dst = compute_roi_pooling_layer(shape, dt, rois, pool_info); + + // Compute reference + RawTensor ref_dst = Reference::compute_reference_roi_pooling_layer(shape, dt, rois, pool_info); + + // Validate output + validate(NEAccessor(dst), ref_dst); +} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +#endif diff --git a/tests/validation/Reference.cpp b/tests/validation/Reference.cpp index 761bc20ea1..2388fb6fe3 100644 --- a/tests/validation/Reference.cpp +++ b/tests/validation/Reference.cpp @@ -30,6 +30,7 @@ #include "validation/Helpers.h" #include <random> +#include <vector> using namespace arm_compute::test; @@ -620,6 +621,28 @@ RawTensor Reference::compute_reference_pooling_layer(const TensorShape &shape_in return ref_dst; } +RawTensor Reference::compute_reference_roi_pooling_layer(const TensorShape &shape, DataType dt, const std::vector<ROI> &rois, const ROIPoolingLayerInfo &pool_info) +{ + TensorShape shape_dst; + shape_dst.set(0, pool_info.pooled_width()); + shape_dst.set(1, pool_info.pooled_height()); + shape_dst.set(2, shape.z()); + shape_dst.set(3, rois.size()); + + // Create reference + RawTensor ref_src = library->get(shape, dt); + RawTensor ref_dst = library->get(shape_dst, dt); + + // Fill reference + std::uniform_real_distribution<> distribution(-1, 1); + library->fill(ref_src, distribution, 0.0); + + // Compute reference + ReferenceCPP::roi_pooling_layer(ref_src, ref_dst, rois, pool_info); + + return ref_dst; +} + RawTensor Reference::compute_reference_softmax_layer(const TensorShape &shape, DataType dt, int fixed_point_position) { // Create reference diff --git a/tests/validation/Reference.h b/tests/validation/Reference.h index 41d6a60edb..1794ae6f23 100644 --- a/tests/validation/Reference.h +++ b/tests/validation/Reference.h @@ -331,6 +331,14 @@ public: * @return Computed raw tensor. */ static RawTensor compute_reference_pooling_layer(const TensorShape &shape_in, const TensorShape &shape_out, DataType dt, PoolingLayerInfo pool_info, int fixed_point_position = 0); + /** Compute reference roi pooling layer. + * + * @param[in] shape Shape of the input tensor. + * @param[in] dt Data type of input and output tensors. + * @param[in] rois Region of interest vector. + * @param[in] pool_info ROI Pooling Layer information. + */ + static RawTensor compute_reference_roi_pooling_layer(const TensorShape &shape, DataType dt, const std::vector<ROI> &rois, const ROIPoolingLayerInfo &pool_info); /** Compute reference softmax layer. * * @param[in] shape Shape of the input and output tensors. diff --git a/tests/validation/ReferenceCPP.cpp b/tests/validation/ReferenceCPP.cpp index 7c50f508a0..c89b737598 100644 --- a/tests/validation/ReferenceCPP.cpp +++ b/tests/validation/ReferenceCPP.cpp @@ -318,6 +318,14 @@ void ReferenceCPP::pooling_layer(const RawTensor &src, RawTensor &dst, PoolingLa boost::apply_visitor(tensor_visitors::pooling_layer_visitor(s, pool_info, fixed_point_position), d); } +// ROI Pooling Layer +void ReferenceCPP::roi_pooling_layer(const RawTensor &src, RawTensor &dst, const std::vector<ROI> &rois, const ROIPoolingLayerInfo &pool_info) +{ + const TensorVariant s = TensorFactory::get_tensor(src); + TensorVariant d = TensorFactory::get_tensor(dst); + boost::apply_visitor(tensor_visitors::roi_pooling_layer_visitor(s, rois, pool_info), d); +} + // Softmax Layer void ReferenceCPP::softmax_layer(const RawTensor &src, RawTensor &dst) { diff --git a/tests/validation/ReferenceCPP.h b/tests/validation/ReferenceCPP.h index 8ee8f31d9b..9debdeeeba 100644 --- a/tests/validation/ReferenceCPP.h +++ b/tests/validation/ReferenceCPP.h @@ -276,7 +276,7 @@ public: * @param[in] norm_info Normalization Layer information. */ static void normalization_layer(const RawTensor &src, RawTensor &dst, NormalizationLayerInfo norm_info); - /** Pooling layer of @p src based on the information from @p norm_info. + /** Pooling layer of @p src based on the information from @p pool_info. * * @param[in] src Input tensor. * @param[out] dst Result tensor. @@ -284,6 +284,14 @@ public: * @param[in] fixed_point_position Fixed point position. (Optional) */ static void pooling_layer(const RawTensor &src, RawTensor &dst, PoolingLayerInfo pool_info, int fixed_point_position = 0); + /** ROI Pooling layer of @p src based on the information from @p pool_info and @p rois. + * + * @param[in] src Input tensor. + * @param[out] dst Result tensor. + * @param[in] rois Region of Interest points. + * @param[in] pool_info ROI Pooling Layer information. + */ + static void roi_pooling_layer(const RawTensor &src, RawTensor &dst, const std::vector<ROI> &rois, const ROIPoolingLayerInfo &pool_info); /** Softmax Layer of @p src. * * @param[in] src Input tensor. diff --git a/tests/validation/TensorOperations.h b/tests/validation/TensorOperations.h index 4f7765613c..5499e58f35 100644 --- a/tests/validation/TensorOperations.h +++ b/tests/validation/TensorOperations.h @@ -1438,6 +1438,78 @@ void pooling_layer(const Tensor<T> &in, Tensor<T> &out, PoolingLayerInfo pool_in } } +// Pooling layer +template <typename T> +void roi_pooling_layer(const Tensor<T> &in, Tensor<T> &out, const std::vector<ROI> &rois, const ROIPoolingLayerInfo &pool_info) +{ + const int num_rois = rois.size(); + const int width_in = in.shape().x(); + const int height_in = in.shape().y(); + const int fms = in.shape().z(); + const int volume_in = width_in * height_in * fms; + const int pool_w = pool_info.pooled_width(); + const int pool_h = pool_info.pooled_height(); + const int volume_out = pool_w * pool_h * fms; + const float roi_scale = pool_info.spatial_scale(); + + // Iterate through all rois + for(int roi_idx = 0; roi_idx < num_rois; ++roi_idx) + { + // Get dimensions of current ROI + const ROI &roi = rois[roi_idx]; + + int batch_id = roi.batch_idx; + int roi_start_x = support::cpp11::round(roi.rect.x * roi_scale); + int roi_start_y = support::cpp11::round(roi.rect.y * roi_scale); + int roi_width = std::max(support::cpp11::round(roi.rect.width * roi_scale), 1.f); + int roi_height = std::max(support::cpp11::round(roi.rect.height * roi_scale), 1.f); + + // Determine pooling regions + float pool_region_size_x = static_cast<float>(roi_width) / pool_w; + float pool_region_size_y = static_cast<float>(roi_height) / pool_h; + + // Iterate through all channel + for(int fm = 0; fm < fms; ++fm) + { + // Calculate each output pixel + for(int py = 0; py < pool_h; ++py) + { + for(int px = 0; px < pool_w; ++px) + { + int region_start_x = static_cast<int>(std::floor(px * pool_region_size_x)); + int region_end_x = static_cast<int>(std::ceil((px + 1) * pool_region_size_x)); + int region_start_y = static_cast<int>(std::floor(py * pool_region_size_y)); + int region_end_y = static_cast<int>(std::ceil((py + 1) * pool_region_size_y)); + + region_start_x = std::min(std::max(region_start_x + roi_start_x, 0), width_in); + region_end_x = std::min(std::max(region_end_x + roi_start_x, 0), width_in); + region_start_y = std::min(std::max(region_start_y + roi_start_y, 0), height_in); + region_end_y = std::min(std::max(region_end_y + roi_start_y, 0), height_in); + + // Iterate through each pixel in the pooling region + if((region_end_x <= region_start_x) || (region_end_y <= region_start_y)) + { + out[roi_idx * volume_out + fm * pool_w * pool_h + py * pool_w + px] = 0; + } + else + { + T curr_max = std::numeric_limits<T>::lowest(); + for(int j = region_start_y; j < region_end_y; ++j) + { + for(int i = region_start_x; i < region_end_x; ++i) + { + const auto val = in[batch_id * volume_in + fm * width_in * height_in + j * width_in + i]; + curr_max = std::max(val, curr_max); + } + } + out[roi_idx * volume_out + fm * pool_w * pool_h + py * pool_w + px] = curr_max; + } + } + } + } + } +} + // Softmax Layer template <typename T, typename std::enable_if<is_floating_point<T>::value, int>::type * = nullptr> void softmax_layer(const Tensor<T> &in, Tensor<T> &out) diff --git a/tests/validation/TensorVisitors.h b/tests/validation/TensorVisitors.h index a274140734..1b58a16e4c 100644 --- a/tests/validation/TensorVisitors.h +++ b/tests/validation/TensorVisitors.h @@ -316,6 +316,29 @@ private: PoolingLayerInfo _pool_info; int _fixed_point_position; }; + +// ROI Pooling layer +struct roi_pooling_layer_visitor : public boost::static_visitor<> +{ +public: + explicit roi_pooling_layer_visitor(const TensorVariant &in, const std::vector<ROI> &rois, ROIPoolingLayerInfo pool_info) + : _in(in), _rois(rois), _pool_info(pool_info) + { + } + + template <typename T> + void operator()(Tensor<T> &out) const + { + const Tensor<T> &in = boost::get<Tensor<T>>(_in); + tensor_operations::roi_pooling_layer(in, out, _rois, _pool_info); + } + +private: + const TensorVariant &_in; + const std::vector<ROI> &_rois; + ROIPoolingLayerInfo _pool_info; +}; + // Softmax Layer visitor struct softmax_layer_visitor : public boost::static_visitor<> { |