From 4d59d0afda6f549fe7905ffa7c7b44eea2f64fb1 Mon Sep 17 00:00:00 2001 From: tedder59 Date: Tue, 14 Nov 2017 10:19:37 +0800 Subject: [PATCH] Add depthwise Deconvolution. (#187) * add depthwise deconvolution. * add depthwise deconvolution. * fix some syntax error and uncessary modification --- src/CMakeLists.txt | 1 + src/layer/arm/deconvolution_3x3.h | 128 +++++++ src/layer/arm/deconvolution_4x4.h | 339 +++++++++++++++++++ src/layer/arm/deconvolution_arm.cpp | 26 +- src/layer/arm/deconvolutiondepthwise_arm.cpp | 133 ++++++++ src/layer/arm/deconvolutiondepthwise_arm.h | 30 ++ src/layer/deconvolutiondepthwise.cpp | 181 ++++++++++ src/layer/deconvolutiondepthwise.h | 39 +++ tools/caffe/caffe2ncnn.cpp | 13 + 9 files changed, 887 insertions(+), 3 deletions(-) create mode 100644 src/layer/arm/deconvolution_4x4.h create mode 100644 src/layer/arm/deconvolutiondepthwise_arm.cpp create mode 100644 src/layer/arm/deconvolutiondepthwise_arm.h create mode 100644 src/layer/deconvolutiondepthwise.cpp create mode 100644 src/layer/deconvolutiondepthwise.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 549fba3a4..5d93f87b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -137,6 +137,7 @@ ncnn_add_layer(Permute) ncnn_add_layer(PriorBox) ncnn_add_layer(DetectionOutput) ncnn_add_layer(Interp) +ncnn_add_layer(DeconvolutionDepthWise) add_library(ncnn STATIC ${ncnn_SRCS}) diff --git a/src/layer/arm/deconvolution_3x3.h b/src/layer/arm/deconvolution_3x3.h index 2379a2fbf..5a1fb5182 100644 --- a/src/layer/arm/deconvolution_3x3.h +++ b/src/layer/arm/deconvolution_3x3.h @@ -237,3 +237,131 @@ static void deconv3x3s1_neon(const Mat& bottom_blob, Mat& top_blob, const Mat& _ } } } + +static void deconv3x3s2_neon(const Mat& bottom_blob, Mat& top_blob, const Mat& _kernel, const Mat& _bias) +{ + int w = bottom_blob.w; + int h = bottom_blob.h; + int inch = bottom_blob.c; + + int outw = top_blob.w; + int outch = top_blob.c; + + const float* kernel = _kernel; + const float* bias = _bias; + + #pragma omp parallel for + for (int p=0; p +#endif // __ARM_NEON + +static void deconv4x4s1_neon(const Mat& bottom_blob, Mat& top_blob, const Mat& _kernel, const Mat& _bias) +{ + int w = bottom_blob.w; + int h = bottom_blob.h; + int inch = bottom_blob.c; + + int outw = top_blob.w; + int outch = top_blob.c; + + const float* kernel = _kernel; + const float* bias = _bias; + + #pragma omp parallel for + for (int p=0; p 2 || dilation != 1) { return Deconvolution::forward(bottom_blob, top_blob); } typedef void (*deconv_func)(const Mat&, Mat&, const Mat&, const Mat&); - deconv_func deconv = deconv3x3s1_neon; + // kernel_size x stride + deconv_func deconv_func_table[2][2] = + { + { + deconv3x3s1_neon, + deconv3x3s2_neon + }, // kernel_size = 3 + { + deconv4x4s1_neon, + deconv4x4s2_neon + } // kernel_size = 4 + }; + + deconv_func deconv = deconv_func_table[kernel_size-3][stride-1]; + if (!deconv) + { + return Deconvolution::forward(bottom_blob, top_blob); + } int w = bottom_blob.w; int h = bottom_blob.h; - int channels = bottom_blob.c; int outw = (w - 1) * stride + kernel_size; int outh = (h - 1) * stride + kernel_size; diff --git a/src/layer/arm/deconvolutiondepthwise_arm.cpp b/src/layer/arm/deconvolutiondepthwise_arm.cpp new file mode 100644 index 000000000..57ee3b59f --- /dev/null +++ b/src/layer/arm/deconvolutiondepthwise_arm.cpp @@ -0,0 +1,133 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "deconvolutiondepthwise_arm.h" + +#ifdef _OPENMP +#include +#endif + +namespace ncnn { + +#include "deconvolution_3x3.h" +#include "deconvolution_4x4.h" + +DEFINE_LAYER_CREATOR(DeconvolutionDepthWise_arm) + +int DeconvolutionDepthWise_arm::forward(const Mat& bottom_blob, Mat& top_blob) const +{ + // convolv with NxN kernel + // value = value + bias + + if ((kernel_size != 3 && kernel_size != 4) || stride > 2 || dilation != 1) + { + return DeconvolutionDepthWise::forward(bottom_blob, top_blob); + } + + typedef void (*deconv_func)(const Mat&, Mat&, const Mat&, const Mat&); + + // kernel_size x stride + deconv_func deconv_func_table[2][2] = + { + { + deconv3x3s1_neon, + deconv3x3s2_neon + }, // kernel_size = 3 + { + deconv4x4s1_neon, + deconv4x4s2_neon + } // kernel_size = 4 + }; + + deconv_func deconv = deconv_func_table[kernel_size-3][stride-1]; + if (!deconv) + { + return DeconvolutionDepthWise::forward(bottom_blob, top_blob); + } + + int w = bottom_blob.w; + int h = bottom_blob.h; + int channels = bottom_blob.c; + + const int kernel_extent = dilation * (kernel_size - 1) + 1; + + int outw = (w - 1) * stride + kernel_extent; + int outh = (h - 1) * stride + kernel_extent; + + Mat top_blob_bordered(outw, outh, num_output); + if (top_blob_bordered.empty()) + return -100; + + const int maxk = kernel_size * kernel_size; + + // depth-wise + if (channels == group && group == num_output) + { +#ifdef _OPENMP + int nested_current = omp_get_nested(); + omp_set_nested(0); +#endif + + #pragma omp parallel for + for (int g=0; g 0) + { + copy_cut_border(top_blob_bordered, top_blob, pad, pad, pad, pad); + if (top_blob.empty()) + return -100; + + outw = top_blob.w; + outh = top_blob.h; + } + + return 0; + +} + +}// namespace ncnn diff --git a/src/layer/arm/deconvolutiondepthwise_arm.h b/src/layer/arm/deconvolutiondepthwise_arm.h new file mode 100644 index 000000000..472311da7 --- /dev/null +++ b/src/layer/arm/deconvolutiondepthwise_arm.h @@ -0,0 +1,30 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef LAYER_DECONVOLUTIONDEPTHWISE_ARM_H +#define LAYER_DECONVOLUTIONDEPTHWISE_ARM_H + +#include "deconvolutiondepthwise.h" + +namespace ncnn { + +class DeconvolutionDepthWise_arm : public DeconvolutionDepthWise +{ +public: + virtual int forward(const Mat& bottom_blob, Mat& top_blob) const; +}; + +} // namespace ncnn + +#endif // LAYER_DECONVOLUTIONDEPTHWISE_ARM_H diff --git a/src/layer/deconvolutiondepthwise.cpp b/src/layer/deconvolutiondepthwise.cpp new file mode 100644 index 000000000..c24bd744f --- /dev/null +++ b/src/layer/deconvolutiondepthwise.cpp @@ -0,0 +1,181 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "deconvolutiondepthwise.h" + +namespace ncnn { + +DEFINE_LAYER_CREATOR(DeconvolutionDepthWise) + +DeconvolutionDepthWise::DeconvolutionDepthWise() +{ + one_blob_only = true; + support_inplace = false; +} + +DeconvolutionDepthWise::~DeconvolutionDepthWise() +{ +} + +int DeconvolutionDepthWise::load_param(const ParamDict& pd) +{ + Deconvolution::load_param(pd); + + group = pd.get(7, 1); + + return 0; +} + +int DeconvolutionDepthWise::forward(const Mat& bottom_blob, Mat& top_blob) const +{ + if (group == 1) + { + return Deconvolution::forward(bottom_blob, top_blob); + } + + // deconvolv with NxN kernel + // value = value + bias + + int w = bottom_blob.w; + int h = bottom_blob.h; + int channels = bottom_blob.c; + + if (channels % group != 0 || num_output % group != 0) + { + // reject invalid group + return -100; + } + + const int kernel_extent = dilation * (kernel_size - 1) + 1; + + int outw = (w - 1) * stride + kernel_extent; + int outh = (h - 1) * stride + kernel_extent; + + Mat top_blob_bordered; + top_blob_bordered.create(outw, outh, num_output); + if (top_blob_bordered.empty()) + return -100; + + const int maxk = kernel_size * kernel_size; + + // kernel offsets + std::vector _space_ofs(maxk); + int* space_ofs = &_space_ofs[0]; + { + int p1 = 0; + int p2 = 0; + int gap = outw * dilation - kernel_size * dilation;; + for (int i = 0; i < kernel_size; i++) + { + for (int j = 0; j < kernel_size; j++) + { + space_ofs[p1] = p2; + p1++; + p2 += dilation; + } + p2 += gap; + } + } + + // depth-wise + if (channels == group && group == num_output) + { + #pragma omp parallel for + for (int g=0; g 0) + { + copy_cut_border(top_blob_bordered, top_blob, pad, pad, pad, pad); + if (top_blob.empty()) + return -100; + + outw = top_blob.w; + outh = top_blob.h; + } + + return 0; +} + +} // namespace ncnn diff --git a/src/layer/deconvolutiondepthwise.h b/src/layer/deconvolutiondepthwise.h new file mode 100644 index 000000000..e2d932ebd --- /dev/null +++ b/src/layer/deconvolutiondepthwise.h @@ -0,0 +1,39 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef LAYER_DECONVOLUTIONDEPTHWISE_H +#define LAYER_DECONVOLUTIONDEPTHWISE_H + +#include "layer.h" +#include "deconvolution.h" + +namespace ncnn { + +class DeconvolutionDepthWise : public Deconvolution +{ +public: + DeconvolutionDepthWise(); + virtual ~DeconvolutionDepthWise(); + + virtual int load_param(const ParamDict& pd); + + virtual int forward(const Mat& bottom_blob, Mat& top_blob) const; + +public: + int group; +}; + +} // namespace ncnn + +#endif // LAYER_DECONVOLUTIONDEPTHWISE_H diff --git a/tools/caffe/caffe2ncnn.cpp b/tools/caffe/caffe2ncnn.cpp index 81883ebe6..12c5e776a 100644 --- a/tools/caffe/caffe2ncnn.cpp +++ b/tools/caffe/caffe2ncnn.cpp @@ -332,6 +332,14 @@ int main(int argc, char** argv) else fprintf(pp, "%-16s", "Convolution"); } + else if (layer.type() == "Deconvolution") + { + const caffe::ConvolutionParameter& convolution_param = layer.convolution_param(); + if (convolution_param.group() != 1) + fprintf(pp, "%-16s", "DeconvolutionDepthWise"); + else + fprintf(pp, "%-16s", "Deconvolution"); + } else if (layer.type() == "Python") { const caffe::PythonParameter& python_param = layer.python_param(); @@ -542,6 +550,11 @@ int main(int argc, char** argv) fprintf(pp, " 5=%d", convolution_param.bias_term()); fprintf(pp, " 6=%d", weight_blob.data_size()); + if (convolution_param.group() != 1) + { + fprintf(pp, " 7=%d", convolution_param.group()); + } + int quantized_weight = 0; fwrite(&quantized_weight, sizeof(int), 1, bp);