Files
2026-07-13 12:40:42 +08:00

168 lines
5.7 KiB
C++

// Copyright (c) 2022 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.
#include "paddle/phi/kernels/layer_norm_grad_kernel.h"
#include "paddle/phi/backends/cpu/cpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/tensor_utils.h"
#include "paddle/phi/kernels/cpu/elementwise.h"
#include "paddle/phi/kernels/full_kernel.h"
#include "paddle/phi/kernels/funcs/blas/blas.h"
#include "paddle/phi/kernels/funcs/elementwise_base.h"
#include "paddle/phi/kernels/funcs/elementwise_functor.h"
#include "paddle/phi/kernels/funcs/layer_norm_util.h"
#include "paddle/phi/kernels/funcs/math_function.h"
namespace phi {
template <typename T, typename Context>
void LayerNormGradKernel(const Context& dev_ctx,
const DenseTensor& x,
const optional<DenseTensor>& scale_opt,
const optional<DenseTensor>& bias_opt UNUSED,
const DenseTensor& mean,
const DenseTensor& variance,
const DenseTensor& out_grad,
double epsilon,
int begin_norm_axis,
DenseTensor* x_grad,
DenseTensor* scale_grad,
DenseTensor* bias_grad) {
if (x.numel() == 0) {
dev_ctx.template Alloc<T>(x_grad);
if (scale_grad)
Full<T, Context>(dev_ctx, scale_grad->dims(), 0, scale_grad);
if (bias_grad) Full<T, Context>(dev_ctx, bias_grad->dims(), 0, bias_grad);
return;
}
auto* scale = scale_opt.get_ptr();
auto d_y = out_grad;
// init output
auto* d_x = x_grad;
auto* d_scale = scale_grad;
auto* d_bias = bias_grad;
const auto& x_dims = x.dims();
auto matrix_dim = common::flatten_to_2d(x_dims, begin_norm_axis);
int left = static_cast<int>(matrix_dim[0]);
int right = static_cast<int>(matrix_dim[1]);
DDim matrix_shape({left, right});
DDim var_shape({left});
d_y.Resize(matrix_shape);
// resize mean and var to match the shape of resized d_y for broadcast (Resize
// will not modify the underline data)
auto mean_tmp = mean;
mean_tmp.Resize(var_shape);
auto variance_tmp = variance;
variance_tmp.Resize(var_shape);
funcs::ColwiseSum2D<CPUContext, T> colwise_sum(left, right, dev_ctx);
DenseTensor x_tmp = x;
DenseTensor temp;
DenseTensor temp_norm;
if (d_scale || d_x) {
x_tmp.Resize(matrix_shape);
temp.Resize(matrix_shape);
dev_ctx.template Alloc<T>(&temp);
temp_norm.Resize(matrix_shape);
dev_ctx.template Alloc<T>(&temp_norm);
// get x_norm
funcs::ElementwiseCompute<funcs::SubtractFunctor<T>, T>(
dev_ctx, x_tmp, mean_tmp, funcs::SubtractFunctor<T>(), &temp_norm, 0);
funcs::ElementwiseCompute<funcs::DivAndSqrtFunctor<T>, T>(
dev_ctx,
temp_norm,
variance_tmp,
funcs::DivAndSqrtFunctor<T>(static_cast<T>(epsilon)),
&temp_norm,
0);
}
if (d_bias) {
dev_ctx.template Alloc<T>(d_bias);
colwise_sum(dev_ctx, d_y, d_bias);
}
if (d_scale) {
dev_ctx.template Alloc<T>(d_scale);
funcs::ElementwiseCompute<funcs::MultiplyFunctor<T>, T>(
dev_ctx, temp_norm, d_y, funcs::MultiplyFunctor<T>(), &temp, 0);
colwise_sum(dev_ctx, temp, d_scale);
}
if (d_x) {
DDim vec_shape({left});
dev_ctx.template Alloc<T>(d_x);
auto dx_dim = d_x->dims();
DenseTensor temp_vec;
temp_vec.Resize(vec_shape);
dev_ctx.template Alloc<T>(&temp_vec);
funcs::RowwiseMean2D<CPUContext, T> row_mean(left, right, dev_ctx);
if (d_scale) {
// dy_dx
funcs::ElementwiseCompute<funcs::MultiplyFunctor<T>, T>(
dev_ctx, d_y, *scale, funcs::MultiplyFunctor<T>(), &temp, 1);
Copy<Context>(dev_ctx, temp, dev_ctx.GetPlace(), false, d_x);
// dy_dmean_dx
row_mean(dev_ctx, temp, &temp_vec);
funcs::ElementwiseCompute<funcs::SubtractFunctor<T>, T>(
dev_ctx, *d_x, temp_vec, funcs::SubtractFunctor<T>(), d_x, 0);
// dy_var_dx
funcs::ElementwiseCompute<funcs::MultiplyFunctor<T>, T>(
dev_ctx, temp, temp_norm, funcs::MultiplyFunctor<T>(), &temp, 0);
} else {
// dy_dx
Copy<Context>(dev_ctx, d_y, dev_ctx.GetPlace(), false, d_x);
// dy_dmean_dx
row_mean(dev_ctx, d_y, &temp_vec);
funcs::ElementwiseCompute<funcs::SubtractFunctor<T>, T>(
dev_ctx, *d_x, temp_vec, funcs::SubtractFunctor<T>(), d_x, 0);
// dy_var_dx
funcs::ElementwiseCompute<funcs::MultiplyFunctor<T>, T>(
dev_ctx, d_y, temp_norm, funcs::MultiplyFunctor<T>(), &temp, 0);
}
// dy_var_dx
row_mean(dev_ctx, temp, &temp_vec);
funcs::ElementwiseCompute<funcs::MultiplyFunctor<T>, T>(
dev_ctx, temp_norm, temp_vec, funcs::MultiplyFunctor<T>(), &temp, 0);
funcs::ElementwiseCompute<funcs::SubtractFunctor<T>, T>(
dev_ctx, *d_x, temp, funcs::SubtractFunctor<T>(), d_x, 0);
funcs::ElementwiseCompute<funcs::DivAndSqrtFunctor<T>, T>(
dev_ctx,
*d_x,
variance_tmp,
funcs::DivAndSqrtFunctor<T>(static_cast<T>(epsilon)),
d_x,
0);
d_x->Resize(dx_dim);
}
}
} // namespace phi
PD_REGISTER_KERNEL(
layer_norm_grad, CPU, ALL_LAYOUT, phi::LayerNormGradKernel, float, double) {
}