From: 박종현/동작제어Lab(SR)/Staff Engineer/삼성전자 Date: Tue, 4 Sep 2018 02:20:15 +0000 (+0900) Subject: [enco.caffe] Initial PoolingSpec implementation (#1312) X-Git-Tag: nncc_backup~1960 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fa982719bee7e426d4976a7da4b031330ad1fe38;p=platform%2Fcore%2Fml%2Fnnfw.git [enco.caffe] Initial PoolingSpec implementation (#1312) This commit submits the initial implementation of PoolingSpec. Signed-off-by: Jonghyun Park --- diff --git a/contrib/enco/frontend/caffe/src/PoolingSpec.cpp b/contrib/enco/frontend/caffe/src/PoolingSpec.cpp new file mode 100644 index 0000000..e9b316d --- /dev/null +++ b/contrib/enco/frontend/caffe/src/PoolingSpec.cpp @@ -0,0 +1,57 @@ +#include "PoolingSpec.h" + +#include + +PoolingSpec::PoolingSpec(const ::caffe::PoolingParameter ¶m) : _param(param) +{ + // DO NOTHING +} + +uint32_t PoolingSpec::window_height(void) const +{ + // NOTE Global pooling is not supported, yet + // TODO Support global pooling + assert(!_param.global_pooling()); + + if (_param.has_kernel_h()) + { + return _param.kernel_h(); + } + + assert(_param.has_kernel_size()); + return _param.kernel_size(); +} + +uint32_t PoolingSpec::window_width(void) const +{ + // NOTE Global pooling is not supported, yet + // TODO Support global pooling + assert(!_param.global_pooling()); + + if (_param.has_kernel_w()) + { + return _param.kernel_w(); + } + + assert(_param.has_kernel_size()); + return _param.kernel_size(); +} + +nncc::core::ADT::tensor::Shape PoolingSpec::ofm_shape(void) const +{ + nncc::core::ADT::tensor::Shape res; + + // NOTE Caffe supports only pooling over rank-4 tensor + assert(_ifm_shape.rank() == 4); + res.resize(4); + + // N (= the number of bacths) SHOULD be same + res.dim(0) = _ifm_shape.dim(0); + // C (= the number of chaanels) SHOULD be same + res.dim(1) = _ifm_shape.dim(1); + + // H and W are derived from IFM, Window, and Padding + res.dim(2) = _ifm_shape.dim(2) - window_height() + 1; + res.dim(3) = _ifm_shape.dim(3) - window_width() + 1; + return res; +} diff --git a/contrib/enco/frontend/caffe/src/PoolingSpec.h b/contrib/enco/frontend/caffe/src/PoolingSpec.h new file mode 100644 index 0000000..8993501 --- /dev/null +++ b/contrib/enco/frontend/caffe/src/PoolingSpec.h @@ -0,0 +1,29 @@ +#ifndef __POOLING_SPEC_H__ +#define __POOLING_SPEC_H__ + +#include + +#include + +class PoolingSpec +{ +public: + PoolingSpec(const ::caffe::PoolingParameter ¶m); + +public: + const nncc::core::ADT::tensor::Shape &ifm_shape(void) const { return _ifm_shape; } + void ifm_shape(const nncc::core::ADT::tensor::Shape &shape) { _ifm_shape = shape; } + +public: + uint32_t window_height(void) const; + uint32_t window_width(void) const; + +public: + nncc::core::ADT::tensor::Shape ofm_shape(void) const; + +private: + const ::caffe::PoolingParameter &_param; + nncc::core::ADT::tensor::Shape _ifm_shape; +}; + +#endif // __POOLING_SPEC_H__ diff --git a/contrib/enco/frontend/caffe/src/PoolingSpec.test.cpp b/contrib/enco/frontend/caffe/src/PoolingSpec.test.cpp new file mode 100644 index 0000000..c36e9aa --- /dev/null +++ b/contrib/enco/frontend/caffe/src/PoolingSpec.test.cpp @@ -0,0 +1,149 @@ +#include "PoolingSpec.h" +#include "Importer.h" + +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +using namespace nncc::core::ADT; + +using nncc::foundation::make_unique; + +#define STRING(content) #content + +namespace +{ + +class SequentialBuilder +{ +public: + SequentialBuilder(::caffe::NetParameter *net) : _net{net} + { + // DO NOTHING + } + +public: + bool addLayer(const std::string &prototxt) + { + auto layer = _net->add_layer(); + std::stringstream ss{prototxt}; + ::google::protobuf::io::IstreamInputStream iis{&ss}; + return google::protobuf::TextFormat::Parse(&iis, layer); + } + + bool addInputLayer(const tensor::Shape &shape) + { + auto param = new ::caffe::InputParameter; + { + auto s = param->add_shape(); + for (uint32_t n = 0; n < shape.rank(); ++n) + { + s->add_dim(shape.dim(n)); + } + } + + auto layer = _net->add_layer(); + + layer->set_name("data"); + layer->set_type("Input"); + layer->add_top("data"); + layer->set_allocated_input_param(param); + + return true; + } + +private: + ::caffe::NetParameter *_net; +}; + +} // namespace + +namespace +{ + +class PoolingSpecTest : public ::testing::Test +{ +protected: + tensor::Shape as_tensor_shape(const std::vector &dims) + { + const uint32_t rank = dims.size(); + + tensor::Shape res; + + res.resize(rank); + + for (uint32_t axis = 0; axis < rank; ++axis) + { + res.dim(axis) = dims.at(axis); + } + + return res; + } +}; +} // namespace + +TEST_F(PoolingSpecTest, ifm_shape) +{ + ::caffe::PoolingParameter param; + PoolingSpec spec{param}; + + const tensor::Shape ifm_shape{1, 3, 244, 244}; + + spec.ifm_shape(ifm_shape); + + ASSERT_EQ(spec.ifm_shape(), ifm_shape); +} + +namespace +{ +} // namespace + +TEST_F(PoolingSpecTest, kernel_size_same_for_all) +{ + const tensor::Shape ifm_shape{1, 3, 16, 16}; + + ::caffe::NetParameter param; + { + SequentialBuilder builder{¶m}; + + builder.addInputLayer(ifm_shape); + + // clang-format off + const char *prototxt = STRING( + name : "pool" + type : "Pooling" + bottom : "data" + top : "pool" + pooling_param { kernel_size : 3 } + ); + // clang-format on + + builder.addLayer(prototxt); + } + + ::caffe::Net net{param}; + + PoolingSpec spec{param.layer(1).pooling_param()}; + + spec.ifm_shape(ifm_shape); + + ASSERT_EQ(spec.window_height(), 3); + ASSERT_EQ(spec.window_width(), 3); + + // Check 'ofm_shape' + { + auto expected = as_tensor_shape(net.blob_by_name("pool")->shape()); + auto obtained = spec.ofm_shape(); + + ASSERT_EQ(expected, obtained); + } +}