From 4ad5230c2aa8ffdc2ae94aaa351c1415ecf6f789 Mon Sep 17 00:00:00 2001 From: nihuini Date: Tue, 18 Aug 2020 14:21:47 +0800 Subject: [PATCH] groupnorm layer --- src/CMakeLists.txt | 1 + src/layer/groupnorm.cpp | 108 +++++++++++++++++++++++++++++++++++++ src/layer/groupnorm.h | 46 ++++++++++++++++ src/layer/instancenorm.cpp | 2 +- tests/CMakeLists.txt | 1 + tests/test_groupnorm.cpp | 57 ++++++++++++++++++++ 6 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 src/layer/groupnorm.cpp create mode 100644 src/layer/groupnorm.h create mode 100644 tests/test_groupnorm.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a4c0f0750..cad01ab93 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -138,6 +138,7 @@ ncnn_add_layer(Mish) ncnn_add_layer(StatisticsPooling) ncnn_add_layer(Swish) ncnn_add_layer(Gemm) +ncnn_add_layer(GroupNorm) if(NCNN_VULKAN) ncnn_add_shader(${CMAKE_CURRENT_SOURCE_DIR}/convert_ycbcr.comp) diff --git a/src/layer/groupnorm.cpp b/src/layer/groupnorm.cpp new file mode 100644 index 000000000..521d39a17 --- /dev/null +++ b/src/layer/groupnorm.cpp @@ -0,0 +1,108 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2020 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 "groupnorm.h" + +#include + +namespace ncnn { + +GroupNorm::GroupNorm() +{ + one_blob_only = true; + support_inplace = true; +} + +int GroupNorm::load_param(const ParamDict& pd) +{ + group = pd.get(0, 1); + channels = pd.get(1, 0); + eps = pd.get(2, 0.001f); + + return 0; +} + +int GroupNorm::load_model(const ModelBin& mb) +{ + gamma_data = mb.load(channels, 1); + if (gamma_data.empty()) + return -100; + + beta_data = mb.load(channels, 1); + if (beta_data.empty()) + return -100; + + return 0; +} + +int GroupNorm::forward_inplace(Mat& bottom_top_blob, const Option& opt) const +{ + // x = (x - mean) / sqrt(var + eps) * gamma + beta + + int w = bottom_top_blob.w; + int h = bottom_top_blob.h; + int size = w * h; + + int channels_per_group = channels / group; + + #pragma omp parallel for num_threads(opt.num_threads) + for (int g = 0; g < group; g++) + { + Mat bottom_top_blob_g = bottom_top_blob.channel_range(g * channels_per_group, channels_per_group); + + // mean and var + float sum = 0.f; + for (int q = 0; q < channels_per_group; q++) + { + const float* ptr = bottom_top_blob_g.channel(q); + for (int i = 0; i < size; i++) + { + sum += ptr[i]; + } + } + float mean = sum / (channels_per_group * size); + + float sqsum = 0.f; + for (int q = 0; q < channels_per_group; q++) + { + const float* ptr = bottom_top_blob_g.channel(q); + for (int i = 0; i < size; i++) + { + float tmp = ptr[i] - mean; + sqsum += tmp * tmp; + } + } + float var = sqsum / (channels_per_group * size); + + for (int q = 0; q < channels_per_group; q++) + { + float gamma = gamma_data[g * channels_per_group + q]; + float beta = beta_data[g * channels_per_group + q]; + + float a = static_cast(gamma / sqrt(var + eps)); + float b = -mean * a + beta; + + float* ptr = bottom_top_blob_g.channel(q); + + for (int i = 0; i < size; i++) + { + ptr[i] = ptr[i] * a + b; + } + } + } + + return 0; +} + +} // namespace ncnn diff --git a/src/layer/groupnorm.h b/src/layer/groupnorm.h new file mode 100644 index 000000000..3c34027a4 --- /dev/null +++ b/src/layer/groupnorm.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2020 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_GROUPNORM_H +#define LAYER_GROUPNORM_H + +#include "layer.h" + +namespace ncnn { + +class GroupNorm : public Layer +{ +public: + GroupNorm(); + + virtual int load_param(const ParamDict& pd); + + virtual int load_model(const ModelBin& mb); + + virtual int forward_inplace(Mat& bottom_top_blob, const Option& opt) const; + +public: + // param + int group; + int channels; + float eps; + + // model + Mat gamma_data; + Mat beta_data; +}; + +} // namespace ncnn + +#endif // LAYER_GROUPNORM_H diff --git a/src/layer/instancenorm.cpp b/src/layer/instancenorm.cpp index 12e0ff85f..bed6e8583 100644 --- a/src/layer/instancenorm.cpp +++ b/src/layer/instancenorm.cpp @@ -47,7 +47,7 @@ int InstanceNorm::load_model(const ModelBin& mb) int InstanceNorm::forward_inplace(Mat& bottom_top_blob, const Option& opt) const { - // x = (x - mean) / (sqrt(var) + eps) * gamma + beta + // x = (x - mean) / (sqrt(var + eps)) * gamma + beta int w = bottom_top_blob.w; int h = bottom_top_blob.h; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3dfcf272d..7ff339580 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -43,6 +43,7 @@ ncnn_add_layer_test(Eltwise) ncnn_add_layer_test(ELU) ncnn_add_layer_test(Flatten) ncnn_add_layer_test(Gemm) +ncnn_add_layer_test(GroupNorm) ncnn_add_layer_test(HardSigmoid) ncnn_add_layer_test(HardSwish) ncnn_add_layer_test(InnerProduct) diff --git a/tests/test_groupnorm.cpp b/tests/test_groupnorm.cpp new file mode 100644 index 000000000..e1e831c06 --- /dev/null +++ b/tests/test_groupnorm.cpp @@ -0,0 +1,57 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2020 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 "layer/groupnorm.h" +#include "testutil.h" + +static int test_groupnorm(const ncnn::Mat& a, int group, float eps) +{ + int channels = a.c; + + ncnn::ParamDict pd; + pd.set(0, group); + pd.set(1, channels); + pd.set(2, eps); + + std::vector weights(2); + weights[0] = RandomMat(channels); + weights[1] = RandomMat(channels); + + int ret = test_layer("GroupNorm", pd, weights, a); + if (ret != 0) + { + fprintf(stderr, "test_groupnorm failed a.dims=%d a=(%d %d %d) group=%d eps=%f\n", a.dims, a.w, a.h, a.c, group, eps); + } + + return ret; +} + +static int test_groupnorm_0() +{ + return 0 + || test_groupnorm(RandomMat(6, 4, 2), 1, 0.01f) + || test_groupnorm(RandomMat(3, 3, 8), 2, 0.002f) + || test_groupnorm(RandomMat(4, 5, 6), 3, 0.01f) + || test_groupnorm(RandomMat(5, 6, 12), 4, 0.02f) + || test_groupnorm(RandomMat(6, 7, 24), 2, 0.001f) + || test_groupnorm(RandomMat(8, 9, 24), 3, 0.0001f); +} + +int main() +{ + SRAND(7767517); + + return 0 + || test_groupnorm_0(); +}