From 492e8e6de208643d0c4aa7e2aa6bfe2d8cd6300d Mon Sep 17 00:00:00 2001 From: "Efimov Alexander/AI Tools Lab/./Samsung Electronics" Date: Tue, 18 Sep 2018 17:26:09 +0300 Subject: [PATCH] Import pads from caffe model directly (#1545) Do not inference paddings in ShapeInference, instead use numbers from model Signed-off-by: Efimov Alexander --- contrib/nnc/core/modelIR/ShapeInference.cpp | 68 ++++------ contrib/nnc/core/modelIR/ir_dot_node_info.cpp | 18 ++- .../nnc/include/core/modelIR/operations/common.h | 3 +- .../nnc/passes/caffe_frontend/caffe_op_creator.cpp | 146 ++++++--------------- 4 files changed, 90 insertions(+), 145 deletions(-) diff --git a/contrib/nnc/core/modelIR/ShapeInference.cpp b/contrib/nnc/core/modelIR/ShapeInference.cpp index 844531e..9fc9884 100644 --- a/contrib/nnc/core/modelIR/ShapeInference.cpp +++ b/contrib/nnc/core/modelIR/ShapeInference.cpp @@ -30,16 +30,18 @@ namespace model using nncc::contrib::core::data::Shape; -std::vector calculate2DPaddings(ops::PaddingType paddingType, const Shape& inShape, - const Shape& windowShape, const Shape& strides, Shape& outShape) +template +void fillHWShapesForPaddedOperations(Op &op, const Shape &windowShape, Shape &outShape) { + auto &strides = op.getStrides(); + auto &inShape = op.getInputShape(0); auto inRank = inShape.rank(); - // Assuming input tensor is 3-dimensional. Will support more general cases as needed. - assert(inRank == 3); - std::vector paddings(3); + outShape.resize(inRank); - if (paddingType == ops::PaddingType::Same) + ops::PaddingType pType = op.getPaddingType(); + switch (pType) { + case ops::PaddingType::Same: for (uint32_t d = 0; d < inRank - 1; ++d) { outShape.dim(d) = (inShape.dim(d) - 1) / strides.dim(d) + 1; @@ -54,19 +56,27 @@ std::vector calculate2DPaddings(ops::PaddingType paddingType, const Shape& (int)windowShape.dim(d) - (int)inShape.dim(d), 0); } - paddings[d] = pad_along_axis / 2; + op.setPadding(d, pad_along_axis / 2); } - } - else - { + break; + case ops::PaddingType::Valid: for (uint32_t d = 0; d < inRank - 1; ++d) { - outShape.dim(d) = (inShape.dim(d) - windowShape.dim(d)) / strides.dim(d) + 1; - paddings[d] = 0; + op.setPadding(d, 0); } + // FALLTHROUGH + case ops::PaddingType::Custom: + for (uint32_t d = 0; d < inRank - 1; ++d) + { + outShape.dim(d) = (inShape.dim(d) + 2*op.getPadding(d) - windowShape.dim(d)) / strides.dim(d) + 1; + } + break; + default: + assert(false && "invalid padding type"); + break; } - - return paddings; + // For now padding for channels is not supported, initialize it with zero + op.setPadding(inRank - 1, 0); } void ShapeInference::visit(ADT::INode::Ref node, ops::ConcatOp &op) @@ -96,20 +106,12 @@ void ShapeInference::visit(ADT::INode::Ref node, ops::Conv2DOp &op) fillInputShapes(node, op); Shape outShape; - outShape.resize(3); - auto &strides = op.getStrides(); auto &kernel = op.getKernel(); - auto &inShape = op.getInputShape(0); auto &kernelShape = kernel.getShape(); - uint32_t inRank = inShape.rank(); - auto pads = calculate2DPaddings(op.getPaddingType(), inShape, kernelShape, strides, outShape); - for (size_t i = 0; i < pads.size(); ++i) - { - op.setPadding(i, pads[i]); - } + fillHWShapesForPaddedOperations(op, kernelShape, outShape); - outShape.dim(inRank - 1) = kernelShape.dim(kernelShape.rank() - 1); + outShape.dim(outShape.rank() - 1) = kernelShape.dim(kernelShape.rank() - 1); op.setOutputShape(0, outShape); } @@ -147,20 +149,14 @@ void ShapeInference::visit(ADT::INode::Ref node, ops::PoolOp &op) fillInputShapes(node, op); Shape outShape; - outShape.resize(3); - auto &strides = op.getStrides(); auto &windowShape = op.getWindowShape(); auto &inShape = op.getInputShape(0); const uint32_t inRank = inShape.rank(); - // Assuming input tensor is 3-dimensional. Will support more general cases when needed. assert(inRank == 3); - auto pads = calculate2DPaddings(op.getPaddingType(), inShape, windowShape, strides, outShape); - for (uint32_t d = 0; d < inShape.rank(); ++d) - { - op.setPadding(d, pads[d]); - } + fillHWShapesForPaddedOperations(op, windowShape, outShape); + outShape.dim(inRank - 1) = inShape.dim(inRank - 1); op.setOutputShape(0, outShape); } @@ -198,8 +194,6 @@ void ShapeInference::visit(ADT::INode::Ref node, ops::DepthwiseConv2DOp &op) fillInputShapes(node, op); Shape outShape; - outShape.resize(3); - auto &strides = op.getStrides(); auto &kernelShape = op.getKernel().getShape(); auto &inShape = op.getInputShape(0); int inRank = inShape.rank(); @@ -209,11 +203,7 @@ void ShapeInference::visit(ADT::INode::Ref node, ops::DepthwiseConv2DOp &op) assert(inRank == 3); assert(inShape.dim(2) == kernelShape.dim(2)); - auto pads = calculate2DPaddings(op.getPaddingType(), inShape, kernelShape, strides, outShape); - for (uint32_t d = 0; d < inShape.rank(); ++d) - { - op.setPadding(d, pads[d]); - } + fillHWShapesForPaddedOperations(op, kernelShape, outShape); outShape.dim(inRank - 1) = inShape.dim(inRank - 1) * kernelShape.dim(kernelRank - 1); op.setOutputShape(0, outShape); diff --git a/contrib/nnc/core/modelIR/ir_dot_node_info.cpp b/contrib/nnc/core/modelIR/ir_dot_node_info.cpp index 20ab89b..c928b50 100644 --- a/contrib/nnc/core/modelIR/ir_dot_node_info.cpp +++ b/contrib/nnc/core/modelIR/ir_dot_node_info.cpp @@ -116,8 +116,22 @@ std::string DotIrNodeInfo::labelForPadAndPool() const if (hasPad) { - ss << "{"; - ss << "PadType: " << (padType == PadType::Valid ? "VALID" : "SAME"); + ss << "{PadType: "; + switch (padType) + { + case PadType::Valid: + ss << "VALID"; + break; + case PadType::Same: + ss << "SAME"; + break; + case PadType::Custom: + ss << "CUSTOM"; + break; + default: + assert(false && "Unknown Padding type"); + break; + } if (hasPool) ss << " | "; else ss << "}"; } diff --git a/contrib/nnc/include/core/modelIR/operations/common.h b/contrib/nnc/include/core/modelIR/operations/common.h index 5dd17ba..f4f45aa 100644 --- a/contrib/nnc/include/core/modelIR/operations/common.h +++ b/contrib/nnc/include/core/modelIR/operations/common.h @@ -16,7 +16,8 @@ namespace ops enum class PaddingType { Same, - Valid //no padding + Valid, // no padding + Custom // paddings are set by importer }; } // namespace ops diff --git a/contrib/nnc/passes/caffe_frontend/caffe_op_creator.cpp b/contrib/nnc/passes/caffe_frontend/caffe_op_creator.cpp index d4be975..3e3b69b 100644 --- a/contrib/nnc/passes/caffe_frontend/caffe_op_creator.cpp +++ b/contrib/nnc/passes/caffe_frontend/caffe_op_creator.cpp @@ -83,105 +83,6 @@ static inline Shape getStride(const OptsType& opts) } } -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; - } -} - -/** - * @brief Determine padding type (SAME/VALID) from two numeric values - height and width padding. - * In general, knowing the input tensor shape is required to properly determine the padding type. - * Also, these two padding values might not map to SAME or VALID padding. - * @todo Change cout to logging call. - */ -static ops::PaddingType getPadTypeFromTwoValues(unsigned int pad1, unsigned int pad2) -{ - bool areBothPadsZero = pad1 == 0 && pad2 == 0; - if (areBothPadsZero) - { - return ops::PaddingType::Valid; - } - else - { - std::string pads = "[" + std::to_string(pad1) + ", " + std::to_string(pad2) + "]"; - std::cout << "WARNING! Encountered padding " << pads - << ", assuming padding SAME, but it is not guaranteed to be correct." << std::endl; - 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. @@ -336,10 +237,9 @@ static std::shared_ptr fixGroupedKernel(int groups, std::shared_ptr OpCreator::createConv2D(InputOps inputs, InputParams params, const caffe::ConvolutionParameter& opts) { - assert(opts.pad_size() <= 2); assert(opts.stride_size() <= 2); - ops::PaddingType padType = util::getConvPadType(opts); + ops::PaddingType padType = ops::PaddingType::Custom; Shape strideShape = util::getConvStride(opts); std::shared_ptr unfoldedTensor = params[0]; @@ -351,6 +251,29 @@ std::vector OpCreator::createConv2D(InputOps inputs, InputParams par auto outputs = createOp(inputs, std::move(*unfoldedTensor), strideShape, padType); + // Set pads + auto *op = static_cast(outputs[0]->getOperation()); + + if (opts.pad_size() != 0 && (opts.has_pad_h() || opts.has_pad_w())) + throw PassException("Conflicting padding properties in convolution"); + + int pad_h = opts.has_pad_h() ? opts.pad_h() : 0; + int pad_w = opts.has_pad_w() ? opts.pad_w() : 0; + switch (opts.pad_size()) + { + case 0: + // no common padding property set + break; + case 1: + pad_h = pad_w = opts.pad(0); + break; + default: + throw PassException("Unsupported number of pads"); + } + op->setPadding(0, pad_h); + op->setPadding(1, pad_w); + op->setPadding(2, 0); + // bias_term is optional (so might not be present) and defaults to true if (!opts.has_bias_term() || opts.bias_term()) return createOp(outputs, std::move(*params[1])); @@ -415,10 +338,27 @@ std::vector OpCreator::createPool(InputOps inputs, InputParams param Shape windowShape = util::getPoolWindowShape(opts); ops::PoolOp::PoolingType poolType = util::getPoolingType(opts); - ops::PaddingType padType = util::getPoolPadType(opts); + ops::PaddingType padType = ops::PaddingType::Custom; Shape stride = util::getPoolStride(opts); - return createOp(inputs, windowShape, stride, poolType, padType); + auto pooling = createOp(inputs, windowShape, stride, poolType, padType); + + // Set pads + auto op = static_cast(pooling[0]->getOperation()); + if (opts.has_pad() && (opts.has_pad_h() || opts.has_pad_w())) + throw PassException("Conflicting padding properties in pooling"); + + int pad_h = opts.has_pad_h() ? opts.pad_h() : 0; + int pad_w = opts.has_pad_w() ? opts.pad_w() : 0; + if (opts.has_pad()) + { + pad_h = pad_w = opts.pad(); + } + op->setPadding(0, pad_h); + op->setPadding(1, pad_w); + op->setPadding(2, 0); + + return pooling; } std::vector OpCreator::createSoftmax(InputOps inputs, InputParams params, -- 2.7.4