// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // 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. #pragma once #include #include #include #include "lite/core/kernel.h" #include "lite/core/op_registry.h" #include "lite/core/types.h" #include "lite/fluid/eigen.h" #include "lite/operators/batch_norm_op.h" namespace paddle { namespace lite { namespace kernels { namespace x86 { template using EigenArrayMap = Eigen::Map>; template using ConstEigenArrayMap = Eigen::Map>; template using EigenVectorArrayMap = Eigen::Map>; template using ConstEigenVectorArrayMap = Eigen::Map>; template class BatchNormCompute : public KernelLite { public: using param_t = operators::BatchNormParam; void Run() override { // auto &context = ctx_->As(); auto ¶m = *param_.get_mutable(); param.is_test = true; bool global_stats = param.is_test || param.use_global_stats; const auto *x = param.x; const auto &x_dims = x->dims(); CHECK(x_dims.size() >= 2 && x_dims.size() <= 5); const int N = x_dims[0]; const int C = param.data_layout == DATALAYOUT(kNCHW) ? x_dims[1] : x_dims[x_dims.size() - 1]; const int sample_size = x->dims().production() / N / C; // alloc memory param.y->template mutable_data(); if (!param.is_test) { param.mean_out->template mutable_data(); param.variance_out->template mutable_data(); param.saved_mean->template mutable_data(); param.saved_variance->template mutable_data(); } if (!global_stats) { // saved_xx is use just in this batch of data EigenVectorArrayMap saved_mean_e( param.saved_mean->template mutable_data(), C); EigenVectorArrayMap saved_variance_e( param.saved_variance->template mutable_data(), C); saved_mean_e.setZero(); saved_variance_e.setZero(); EigenVectorArrayMap running_mean_arr( param.mean_out->template mutable_data(), C); EigenVectorArrayMap running_var_arr( param.variance_out->template mutable_data(), C); if ((N * sample_size) == 1) { LOG(WARNING) << "Only 1 element in normalization dimension, " << "we skip the batch norm calculation, let y = x."; param.y->CopyDataFrom(*x); return; } switch (param.data_layout) { case DATALAYOUT(kNCHW): { ConstEigenArrayMap x_arr( x->template data(), sample_size, N * C); for (int nc = 0; nc < N * C; ++nc) { saved_mean_e(nc % C) += x_arr.col(nc).sum(); } saved_mean_e /= N * sample_size; for (int nc = 0; nc < N * C; ++nc) { saved_variance_e(nc % C) += (x_arr.col(nc) - saved_mean_e(nc % C)).matrix().squaredNorm(); } saved_variance_e /= N * sample_size; break; } default: LOG(FATAL) << "Unknown storage order: " << DataLayoutToStr(param.data_layout); break; } running_mean_arr = running_mean_arr * param.momentum + saved_mean_e * (1. - param.momentum); running_var_arr = running_var_arr * param.momentum + saved_variance_e * (1. - param.momentum); } // use SavedMean and SavedVariance to do normalize Eigen::Array inv_std(C); if (global_stats) { ConstEigenVectorArrayMap var_arr(param.variance->template data(), C); inv_std = (var_arr + param.epsilon).sqrt().inverse(); } else { EigenVectorArrayMap saved_inv_std( param.saved_variance->template mutable_data(), C); // inverse SavedVariance first, gradient will use it too. saved_inv_std = (saved_inv_std + param.epsilon).inverse().sqrt(); inv_std = saved_inv_std; } ConstEigenVectorArrayMap mean_arr( global_stats ? param.mean->template data() : param.saved_mean->template data(), C); // ((x - est_mean) * (inv_var) * scale + bias // formula transform ====> // (x * inv_var * scale) + (bias - est_mean * inv_var * scale) ConstEigenVectorArrayMap scale_arr(param.scale->template data(), C); ConstEigenVectorArrayMap bias_arr(param.bias->template data(), C); Eigen::Array new_scale = inv_std * scale_arr; Eigen::Array new_bias = bias_arr - mean_arr * inv_std * scale_arr; switch (param.data_layout) { case DATALAYOUT(kNCHW): { EigenArrayMap y_arr( param.y->template mutable_data(), sample_size, N * C); ConstEigenArrayMap x_arr(x->template data(), sample_size, N * C); for (int nc = 0; nc < N * C; ++nc) { y_arr.col(nc) = x_arr.col(nc) * new_scale(nc % C) + new_bias(nc % C); } break; } default: LOG(FATAL) << "Unknown storage order: " << DataLayoutToStr(param.data_layout); break; } } virtual ~BatchNormCompute() = default; }; } // namespace x86 } // namespace kernels } // namespace lite } // namespace paddle