From: Dmitry Mozolev/AI Tools Lab /SRR/Engineer/삼성전자 Date: Sun, 15 Jul 2018 06:33:30 +0000 (+0300) Subject: Add utility functions for processing Caffe layer options (#637) X-Git-Tag: nncc_backup~2424 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4ed712dccaa6d9bf507a5a955c512fca5a48bfa5;p=platform%2Fcore%2Fml%2Fnnfw.git Add utility functions for processing Caffe layer options (#637) Add Caffe layer to Model IR converter skeleton Added a class for converting Caffe layers to Model IR operations. Signed-off-by: Dmitry Mozolev --- diff --git a/contrib/nnc/libs/frontend/caffe/src/caffe_op_creator.cpp b/contrib/nnc/libs/frontend/caffe/src/caffe_op_creator.cpp index a063b68..35f7556 100644 --- a/contrib/nnc/libs/frontend/caffe/src/caffe_op_creator.cpp +++ b/contrib/nnc/libs/frontend/caffe/src/caffe_op_creator.cpp @@ -21,6 +21,242 @@ namespace frontend namespace caffe { +namespace util +{ + +template +static inline bool has2DStride(const OptsType& opts) +{ + if (opts.has_stride_h() != opts.has_stride_w()) + { + throw PluginException("Conv or Pool layer has only 1 out of 2 2D strides, investigate"); + } + // We already checked that both 2D strides are both present or both are not + return opts.has_stride_h(); +} + +static inline Shape getStrideFromOneValue(bool hasStride, unsigned int stride) +{ + if (hasStride) + { + return Shape{stride, stride, 1}; + } + else + { + return Shape{1, 1, 1}; + } +} + +static inline Shape getStrideFromTwoValues(unsigned int stride1, unsigned int stride2) +{ + return Shape{stride1, stride2, 1}; +} + +template +static inline Shape getStride(const OptsType& opts) +{ + // stride might have size 0, then it defaults to 1 + if (opts.stride_size() == 0) + { + return Shape{1, 1, 1}; + } + else if (opts.stride_size() == 1) + { + return getStrideFromOneValue(true, opts.stride(0)); + } + else + { + return getStrideFromTwoValues(opts.stride(0), opts.stride(1)); + } +} + +template +static inline bool has2DPad(const OptsType& opts) +{ + return opts.has_pad_h() || opts.has_pad_w(); +} + +static ops::PaddingType getPadTypeFromOneValue(bool hasPad, unsigned int pad) +{ + if (hasPad) + { + if (pad == 0) + { + return ops::PaddingType::Valid; + } + else + { + return ops::PaddingType::Same; + } + } + else + { + return ops::PaddingType::Valid; + } +} + +static ops::PaddingType getPadTypeFromTwoValues(unsigned int pad1, unsigned int pad2) +{ + bool areBothPadsZero = pad1 == 0 && pad2 == 0; + bool doPadsDiffer = pad1 != pad2; + if (areBothPadsZero) + { + return ops::PaddingType::Valid; + } + else if (doPadsDiffer) + { + std::string pads = std::to_string(pad1) + ", " + std::to_string(pad2); + throw PluginException(std::string("Unsupported pads: ") + pads); + } + else + { + return ops::PaddingType::Same; + } +} + +template +static inline ops::PaddingType getPadTypeFromTwoValues(const OptsType &opts) +{ + unsigned int pad1, pad2; + pad1 = opts.has_pad_h() ? opts.pad_h() : 0; + pad2 = opts.has_pad_w() ? opts.pad_w() : 0; + + return getPadTypeFromTwoValues(pad1, pad2); +} + +template +static ops::PaddingType getPadType(const OptsType &opts) +{ + if (opts.pad_size() == 0) + { + return ops::PaddingType::Valid; + } + else if (opts.pad_size() == 1) + { + return getPadTypeFromOneValue(true, opts.pad(0)); + } + else + { + return getPadTypeFromTwoValues(opts.pad(0), opts.pad(1)); + } +} + +/** + * @brief Tries to determine whether padding is SAME or VALID given + * numeric pad values from Caffe layer options. Only 2D convolutions/pools + * are supported currently. + * @todo Currently, pad_h and pad_w options take precedence if they are present, + * but maybe it is not correct logic. Check how it really is done. + * @todo Specific calculations are required to check that padding is indeed SAME, + * currently we just return SAME if padding is non-zero. + */ +__attribute__ ((unused)) static ops::PaddingType getConvPadType(const ConvolutionParameter& opts) +{ + if (has2DPad(opts)) + return getPadTypeFromTwoValues(opts); + else + return getPadType(opts); +} + +__attribute__ ((unused)) static ops::PaddingType getPoolPadType(const PoolingParameter& opts) +{ + if (has2DPad(opts)) + return getPadTypeFromTwoValues(opts); + else + return getPadTypeFromOneValue(opts.has_pad(), opts.pad()); +} + +/** + * @brief Determines stride values from Caffe layer options. + * Only 2D convolutions/pools are supported currently. + * @todo Currently, stride_h and stride_w options take precedence if they are present, + * but maybe it is not correct logic. Check how it really is done. + */ +__attribute__ ((unused)) static Shape getConvStride(const ConvolutionParameter& opts) +{ + if (has2DStride(opts)) + return getStrideFromTwoValues(opts.stride_h(), opts.stride_w()); + else + return getStride(opts); +} + +__attribute__ ((unused)) static Shape getPoolStride(const PoolingParameter& opts) +{ + if (has2DStride(opts)) + return getStrideFromTwoValues(opts.stride_h(), opts.stride_w()); + else + return getStrideFromOneValue(opts.has_stride(), opts.stride()); +} + +__attribute__ ((unused)) static Shape getPoolWindowShape(const PoolingParameter &opts) +{ + if (opts.has_kernel_h() != opts.has_kernel_w()) + { + throw PluginException("Pool layer has only 1 out of 2 kernel dimensions, investigate"); + } + + if (opts.has_kernel_h()) + { + return Shape{opts.kernel_h(), opts.kernel_w(), 1}; + } + else if (opts.has_kernel_size()) + { + return Shape{opts.kernel_size(), opts.kernel_size(), 1}; + } + else + { + throw PluginException("Pooling layer doesn't have kernel size data, investigate"); + } +} + +__attribute__ ((unused)) static ops::PoolOp::PoolingType getPoolingType(const PoolingParameter& opts) +{ + using PoolingType = ops::PoolOp::PoolingType; + + if (opts.pool() == PoolingParameter::MAX) + return PoolingType::MAX; + else if (opts.pool() == PoolingParameter::AVE) + return PoolingType::AVG; + else + throw PluginException("Unsupported pooling type: " + + PoolingParameter::PoolMethod_Name(opts.pool())); +} + +/** + * @brief Determines correct value for Caffe Softmax/Concat axis parameter. + * @todo Change cout to a log library call. + * @todo Decide how to process negative axis values. + * @todo Decide how to process axis in general. + */ +template +__attribute__ ((unused)) static int getAxisValue(const OptsType& opts) +{ + int axis = 2; + if (opts.has_axis()) + { + axis = opts.axis(); + if (axis == 0) + { + std::cout << "WARNING: axis parameter equals 0. It is normal," + "but implies that the model might not have a batch dimension," + "so make sure import works correctly." << std::endl; + } + else if (axis < 0) + { + throw PluginException("Softmax/Concat layer negative axis values are not supported yet."); + } + else if (axis != 1) + { + throw PluginException("Softmax/Concat layer axis param is not 1, which implies" + "unsupported NN architecture."); + } + } + + return axis; +} + +} // namespace util + std::vector OpCreator::createConv2D(InputOps inputs, InputParams params, const caffe::ConvolutionParameter& opts) {