Files
paddlepaddle--paddle/paddle/fluid/prim/api/composite_backward/composite_backward_api.h
T
2026-07-13 12:40:42 +08:00

2206 lines
73 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.
#pragma once
#ifndef _USE_MATH_DEFINES
#define _USE_MATH_DEFINES
#endif
#include <math.h>
#include "paddle/common/ddim.h"
#include "paddle/fluid/prim/api/all.h"
#include "paddle/fluid/prim/api/composite_backward/composite_double_backward_api.h"
#include "paddle/fluid/prim/api/generated_prim/prim_generated_api.h"
#include "paddle/phi/common/amp_type_traits.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/phi/kernels/funcs/common_infer_shape_functions.h"
namespace paddle {
namespace prim {
using IntArray = paddle::experimental::IntArrayBase<Tensor>;
// This function should have as same signature as phi, which defined in
// paddle/phi/api/backward/backward_api_base.h
template <typename T>
void pow_grad(const Tensor& x,
const Tensor& out_grad,
const Scalar& y,
Tensor* x_grad) {
// dx = y * x^(y-1) * out_grad
if (x_grad) {
auto y_value = y.to<float>();
auto dx_res = y_value * x.pow(y_value - 1) * out_grad;
set_output<T>(dx_res, x_grad);
} // indicate we will compute dx
}
template <typename T>
void hardswish_grad(const Tensor& x, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
auto offset =
full<T>(common::vectorize(x.dims()), 3.0, x.dtype(), x.place());
auto condition = less_equal<T>(x, offset);
auto tmp1 = where<T>(condition, out_grad * ((x / 3.0) + 0.5), out_grad);
auto res = where<T>(
less_than<T>(
x,
full<T>(common::vectorize(x.dims()), -3.0, x.dtype(), x.place())),
full<T>(common::vectorize(x.dims()), 0.0, x.dtype(), x.place()),
tmp1);
set_output<T>(res, x_grad);
}
}
template <typename T>
void leaky_relu_grad(const Tensor& out,
const Tensor& out_grad,
double negative_slope,
Tensor* x_grad) {
if (x_grad) {
auto condition = greater_than<T>(
out,
full<T>(common::vectorize(out.dims()), 0.0, out.dtype(), out.place()));
auto res = where<T>(condition, out_grad, out_grad * negative_slope);
set_output<T>(res, x_grad);
}
}
template <typename T>
void silu_grad(const Tensor& x,
const Tensor& out,
const Tensor& out_grad,
Tensor* x_grad) {
if (x_grad) {
auto org_dtype = x.dtype();
bool need_cast =
org_dtype == DataType::FLOAT16 || org_dtype == DataType::BFLOAT16;
if (need_cast) {
auto x_cast = cast<T>(x, DataType::FLOAT32);
auto out_cast = cast<T>(out, DataType::FLOAT32);
auto out_grad_cast = cast<T>(out_grad, DataType::FLOAT32);
auto sigmoid = 1.0 / (1.0 + exp<T>(-x_cast));
auto res = out_grad_cast * sigmoid * (1.0 + x_cast - out_cast);
set_output<T>(cast<T>(res, org_dtype), x_grad);
} else {
auto sigmoid = 1.0 / (1.0 + exp<T>(-x));
auto res = out_grad * sigmoid * (1.0 + x - out);
set_output<T>(res, x_grad);
}
}
}
template <typename T>
void relu_grad(const Tensor& out, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
auto mask = greater_than<T>(
out,
full<T>(common::vectorize(out.dims()), 0.0, out.dtype(), out.place()));
auto res = cast<T>(mask, out.dtype()) * out_grad;
set_output<T>(res, x_grad);
}
}
template <typename T>
void softmax_grad(const Tensor& out,
const Tensor& out_grad,
int axis,
Tensor* x_grad) {
if (x_grad) {
if (axis < 0) {
axis += out.dims().size();
}
if (out_grad.dims().size() > 0) {
// dx = dy * y - y * (dy*y).sum(axis)
auto new_out_grad = out_grad * out;
auto tmp_x_grad =
new_out_grad - out * sum<T>(new_out_grad, {axis}, out.dtype(), true);
set_output<T>(tmp_x_grad, x_grad);
} else {
set_output<T>(full<T>(common::vectorize(out_grad.dims()),
0.0,
out_grad.dtype(),
out_grad.place()),
x_grad);
}
}
}
template <typename T>
void cast_grad(const Tensor& x, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
auto res = cast<T>(out_grad, x.dtype());
set_output<T>(res, x_grad);
}
}
template <typename T>
void tanh_grad(const Tensor& out, const Tensor& grad_out, Tensor* grad_x) {
if (!grad_x) return;
auto grad_x_tmp = grad_out * (1 - out * out);
set_output<T>(grad_x_tmp, grad_x);
}
template <typename T>
void reduce_as_grad(const Tensor& x,
const Tensor& target,
const Tensor& out_grad,
Tensor* x_grad) {
if (!x_grad) {
return;
}
std::vector<int64_t> x_dim = common::vectorize<int64_t>(x.dims());
std::vector<int64_t> axis =
common::vectorize<int64_t>(get_reduce_dims(x.dims(), target.dims()));
int64_t axis_size = axis.size();
int64_t x_dim_size = x_dim.size();
bool reduce_all = false;
if (reduce_all || axis_size == 0 || axis_size == x_dim_size) {
reduce_all = true;
} else {
reduce_all = false;
}
auto x_grad_tmp = Tensor();
if (x_dim_size == 1) {
x_grad_tmp = expand<T>(out_grad, IntArray(x_dim));
} else {
auto axis_ = std::vector<int64_t>();
if (reduce_all) {
for (int64_t i = 0; i < x_dim_size; i++) {
axis_.push_back(i);
}
} else {
for (int64_t i = 0; i < axis_size; i++) {
axis_.push_back(axis[i]);
if (axis[i] < 0) {
axis_[i] += x_dim_size;
}
}
}
auto out_grad_shape = get_unsqueeze_dims(out_grad, axis_);
auto out_grad_ = reshape<T>(out_grad, out_grad_shape);
x_grad_tmp = expand<T>(out_grad_, IntArray(x_dim));
}
set_output<T>(x_grad_tmp, x_grad);
}
template <typename T>
void reshape_grad(const Tensor& x, const Tensor& grad_out, Tensor* grad_x) {
if (grad_x) {
auto grad_x_tmp = reshape<T>(grad_out, common::vectorize(x.dims()));
set_output<T>(grad_x_tmp, grad_x);
}
}
template <typename T>
void transpose_grad(const Tensor& grad_out,
const std::vector<int>& perm,
Tensor* grad_x) {
if (grad_x) {
std::vector<int> reverse_perm(perm);
// make origin ranks
for (int i = 0; i < static_cast<int>(perm.size()); ++i) {
if (perm[i] >= 0) {
reverse_perm[perm[i]] = i;
} else {
reverse_perm[perm[i] + perm.size()] = i;
}
}
auto grad_x_tmp = transpose<T>(grad_out, reverse_perm);
set_output<T>(grad_x_tmp, grad_x);
}
}
template <typename T>
void subtract_grad(const Tensor& x,
const Tensor& y,
const Tensor& out_grad,
int axis,
Tensor* dx,
Tensor* dy) {
if (dy) {
auto scale_out_grad = scale<T>(out_grad, -1.0, 0.0, true);
if (out_grad.dims() != y.dims()) {
// Maybe need reduce here
DDim reduce_dim = get_reduce_dims(y.dims(), out_grad.dims());
if (!reduce_dim.size()) {
by_pass<T>(scale_out_grad, dy);
} else {
auto dy_reduce_res =
scale_out_grad.sum(common::vectorize(reduce_dim),
y.dtype(),
scale_out_grad.dims().size() == y.dims().size());
if (dy_reduce_res.dims() != y.dims()) {
dy_reduce_res =
reshape<T>(dy_reduce_res, common::vectorize(y.dims()));
}
set_output<T>(dy_reduce_res, dy);
}
} else {
by_pass<T>(scale_out_grad, dy);
}
}
if (dx) {
if (out_grad.dims() != x.dims()) {
// Maybe need reduce here
auto reduce_dim = get_reduce_dims(x.dims(), out_grad.dims());
if (!reduce_dim.size()) {
by_pass<T>(out_grad, dx);
} else {
auto dx_reduce_res =
out_grad.sum(common::vectorize(reduce_dim),
x.dtype(),
out_grad.dims().size() == x.dims().size());
if (dx_reduce_res.dims() != x.dims()) {
dx_reduce_res =
reshape<T>(dx_reduce_res, common::vectorize(x.dims()));
}
set_output<T>(dx_reduce_res, dx);
}
} else {
by_pass<T>(out_grad, dx);
}
}
}
template <typename T>
void add_grad(const Tensor& x,
const Tensor& y,
const Tensor& out_grad,
int axis,
Tensor* dx,
Tensor* dy) {
if (dy) {
if (out_grad.dims() != y.dims()) {
// Maybe need reduce here
DDim reduce_dim = get_reduce_dims(y.dims(), out_grad.dims());
if (!reduce_dim.size()) {
by_pass<T>(out_grad, dy);
} else {
auto dy_reduce_res =
out_grad.sum(common::vectorize(reduce_dim),
y.dtype(),
out_grad.dims().size() == y.dims().size());
if (dy_reduce_res.dims() != y.dims()) {
dy_reduce_res =
reshape<T>(dy_reduce_res, common::vectorize(y.dims()));
}
set_output<T>(dy_reduce_res, dy);
}
} else {
by_pass<T>(out_grad, dy);
}
}
if (dx) {
if (out_grad.dims() != x.dims()) {
// Maybe need reduce here
auto reduce_dim = get_reduce_dims(x.dims(), out_grad.dims());
if (!reduce_dim.size()) {
by_pass<T>(out_grad, dx);
} else {
auto dx_reduce_res =
out_grad.sum(common::vectorize(reduce_dim),
x.dtype(),
out_grad.dims().size() == x.dims().size());
if (dx_reduce_res.dims() != x.dims()) {
dx_reduce_res =
reshape<T>(dx_reduce_res, common::vectorize(x.dims()));
}
set_output<T>(dx_reduce_res, dx);
}
} else {
by_pass<T>(out_grad, dx);
}
}
}
template <typename T>
void sum_grad(const Tensor& x,
const Tensor& out_grad,
const IntArray& axis,
bool keepdim,
bool reduce_all,
Tensor* x_grad) {
if (!x_grad) {
return;
}
std::vector<int64_t> x_dim = common::vectorize<int64_t>(x.dims());
int64_t axis_size = axis.size();
int64_t x_dim_size = x_dim.size();
reduce_all = false;
if (reduce_all || axis_size == 0 || axis_size == x_dim_size) {
reduce_all = true;
} else {
reduce_all = false;
}
auto x_grad_tmp = Tensor();
if (x_dim_size == 1) {
x_grad_tmp = out_grad.expand(IntArray(x_dim));
} else {
if (!keepdim) {
auto axis_ = std::vector<int64_t>();
if (reduce_all) {
for (int64_t i = 0; i < x_dim_size; i++) {
axis_.push_back(i);
}
} else {
axis_ = axis.GetData();
for (int64_t i = 0; i < axis_size; i++) {
if (axis[i] < 0) {
axis_[i] = axis[i] + x_dim_size;
}
}
}
auto out_grad_shape = get_unsqueeze_dims(out_grad, axis_);
auto out_grad_ = reshape<T>(out_grad, out_grad_shape);
x_grad_tmp = out_grad_.expand(IntArray(x_dim));
} else {
x_grad_tmp = out_grad.expand(IntArray(x_dim));
}
}
set_output<T>(x_grad_tmp, x_grad);
}
template <typename T>
void divide_grad(const Tensor& x,
const Tensor& y,
const Tensor& out,
const Tensor& out_grad,
int axis,
Tensor* dx,
Tensor* dy) {
if (dy) {
// dy = -(x/y^2) * dout = -out * dout / y
auto dy_res = -out * out_grad / y;
if (out.dims() != y.dims()) {
// Maybe need reduce here
DDim reduce_dim = get_reduce_dims(y.dims(), out.dims());
if (!reduce_dim.size()) {
set_output<T>(dy_res, dy);
} else {
auto dy_reduce_res =
dy_res.sum(common::vectorize(reduce_dim),
y.dtype(),
dy_res.dims().size() == y.dims().size());
if (dy_reduce_res.dims() != y.dims()) {
dy_reduce_res =
reshape<T>(dy_reduce_res, common::vectorize(y.dims()));
}
set_output<T>(dy_reduce_res, dy);
}
} else {
set_output<T>(dy_res, dy);
}
} // indicate we will compute dy
if (dx) {
// dx = (1/y) * dout = dout / y
auto dx_res = out_grad / y;
if (out_grad.dims() != x.dims()) {
// Maybe need reduce here
auto reduce_dim = get_reduce_dims(x.dims(), out_grad.dims());
if (!reduce_dim.size()) {
set_output<T>(dx_res, dx);
} else {
auto dx_reduce_res =
dx_res.sum(common::vectorize(reduce_dim),
x.dtype(),
dx_res.dims().size() == x.dims().size());
if (dx_reduce_res.dims() != x.dims()) {
dx_reduce_res =
reshape<T>(dx_reduce_res, common::vectorize(x.dims()));
}
set_output<T>(dx_reduce_res, dx);
}
} else {
set_output<T>(dx_res, dx);
}
} // indicate we will compute dx
}
template <typename T>
void elementwise_pow_grad(const Tensor& x,
const Tensor& y,
const Tensor& out_grad,
Tensor* dx,
Tensor* dy) {
if (dy) {
// dy = lnx * x^y
auto lnx = log<T>(x);
auto x_pow_y = elementwise_pow<T>(x, y);
auto dy_res = lnx * x_pow_y * out_grad;
if (out_grad.dims() != y.dims()) {
// Maybe need reduce here
DDim reduce_dim = get_reduce_dims(y.dims(), out_grad.dims());
if (!reduce_dim.size()) {
set_output<T>(dy_res, dy);
} else {
auto dy_reduce_res =
dy_res.sum(common::vectorize(reduce_dim),
y.dtype(),
dy_res.dims().size() == y.dims().size());
if (dy_reduce_res.dims() != y.dims()) {
dy_reduce_res =
reshape<T>(dy_reduce_res, common::vectorize(y.dims()));
}
set_output<T>(dy_reduce_res, dy);
}
} else {
set_output<T>(dy_res, dy);
}
} // indicate we will compute dy
if (dx) {
// dx = y * x^(y-1)
auto tmp_z = y - 1.0;
auto x_pow_z = elementwise_pow<T>(x, tmp_z);
auto dx_res = y * x_pow_z * out_grad;
if (out_grad.dims() != x.dims()) {
// Maybe need reduce here
auto reduce_dim = get_reduce_dims(x.dims(), out_grad.dims());
if (!reduce_dim.size()) {
set_output<T>(dx_res, dx);
} else {
auto dx_reduce_res =
dx_res.sum(common::vectorize(reduce_dim),
x.dtype(),
dx_res.dims().size() == x.dims().size());
if (dx_reduce_res.dims() != x.dims()) {
dx_reduce_res =
reshape<T>(dx_reduce_res, common::vectorize(x.dims()));
}
set_output<T>(dx_reduce_res, dx);
}
} else {
set_output<T>(dx_res, dx);
}
} // indicate we will compute dx
}
template <typename T>
void sqrt_grad(const Tensor& out, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
// This calculation is important for resnet.
auto x_grad_tmp = (0.5 / out) * out_grad;
set_output<T>(x_grad_tmp, x_grad);
}
}
template <typename T>
void rsqrt_grad(const Tensor& out, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
// This calculation is important for resnet.
auto x_grad_tmp = -0.5 * out * out * out * out_grad;
set_output<T>(x_grad_tmp, x_grad);
}
}
template <typename T>
void floor_grad(const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
auto zero_tensor = full<T>(common::vectorize(out_grad.dims()),
0.0,
out_grad.dtype(),
out_grad.place());
set_output<T>(zero_tensor, x_grad);
}
}
template <typename T>
void concat_grad(const std::vector<Tensor>& x,
const Tensor& out_grad,
const Scalar& axis,
std::vector<Tensor*> x_grad) {
int axis_value = axis.to<int>();
int rank = x[0].dims().size();
if (axis_value < 0) {
axis_value = axis_value + rank;
}
axis_value = axis_value > 0 ? axis_value : 0;
std::vector<int> sections;
int x_num = x.size();
for (int i = 0; i < x_num; ++i) {
sections.push_back(x[i].dims()[axis_value]);
}
std::vector<Tensor> x_grad_tmp =
split<T>(out_grad, phi::IntArray(sections), axis_value);
for (int i = 0; i < x_num; ++i) {
if (x_grad[i]) {
set_output<T>(x_grad_tmp[i], x_grad[i]);
}
}
}
template <typename T>
void stack_grad(const std::vector<Tensor>& x,
const Tensor& out_grad,
int axis,
std::vector<Tensor*> x_grad) {
// use rank of **stacked** tensor as len of axes
int out_rank = out_grad.dims().size(); // len(x[0].shape)
// ensure axis >= 0
if (axis < 0) {
axis = ((axis % out_rank) + out_rank) % out_rank;
}
// split out_grad to grads for each input tensor
int x_num = x.size();
std::vector<int> sections(x_num, 1);
std::vector<Tensor> x_grad_tmp =
split<T>(out_grad, phi::IntArray(sections), axis);
// compose shape for each input tensor
std::vector<int64_t> grad_shape;
auto out_dim = out_grad.dims().size();
for (int i = 0; i < out_dim; ++i) {
if (i != axis) {
grad_shape.push_back(out_grad.dims()[i]);
}
}
// assign to each input tensor if need grad(stop_gradient=False)
for (int i = 0; i < x_num; ++i) {
if (x_grad[i]) {
set_output<T>(reshape<T>(x_grad_tmp[i], grad_shape), x_grad[i]);
}
}
}
template <typename T>
void multiply_grad(const Tensor& x,
const Tensor& y,
const Tensor& out_grad,
int axis,
Tensor* x_grad,
Tensor* y_grad) {
if (x_grad) {
auto x_grad_unreduce = out_grad * y;
if (x_grad_unreduce.dims() != x.dims()) {
auto axes = get_reduce_dims_from_out(x_grad_unreduce.dims(), x.dims());
if (!axes.size()) {
set_output<T>(x_grad_unreduce, x_grad);
} else {
auto x_grad_reduced = x_grad_unreduce.sum(
common::vectorize(axes),
x_grad_unreduce.dtype(),
x_grad_unreduce.dims().size() == x.dims().size());
if (x_grad_reduced.dims() != x.dims()) {
x_grad_reduced = reshape<T>(x_grad_reduced, x.shape());
}
set_output<T>(x_grad_reduced, x_grad);
}
} else {
set_output<T>(x_grad_unreduce, x_grad);
}
}
if (y_grad) {
auto y_grad_unreduce = out_grad * x;
if (y_grad_unreduce.dims() != y.dims()) {
auto axes = get_reduce_dims_from_out(y_grad_unreduce.dims(), y.dims());
if (!axes.size()) {
set_output<T>(y_grad_unreduce, y_grad);
} else {
auto y_grad_reduced = y_grad_unreduce.sum(
common::vectorize(axes),
y_grad_unreduce.dtype(),
y_grad_unreduce.dims().size() != y.dims().size());
if (y_grad_reduced.dims() != y.dims()) {
y_grad_reduced = reshape<T>(y_grad_reduced, y.shape());
}
set_output<T>(y_grad_reduced, y_grad);
}
} else {
set_output<T>(y_grad_unreduce, y_grad);
}
}
}
template <typename T>
void expand_grad(const Tensor& x,
const Tensor& out_grad,
const IntArray& shape,
Tensor* x_grad) {
if (x_grad) {
auto out_dims = common::make_ddim(shape.GetData());
if (out_dims != x.dims()) {
auto axes = get_reduce_dims(x.dims(), out_dims);
if (!axes.size()) {
by_pass<T>(out_grad, x_grad);
} else {
auto reduced = out_grad.sum(common::vectorize(axes),
x.dtype(),
out_grad.dims().size() == x.dims().size());
if (reduced.dims() != x.dims()) {
reduced = reshape<T>(reduced, x.shape());
}
set_output<T>(reduced, x_grad);
}
} else {
by_pass<T>(out_grad, x_grad);
}
}
}
template <typename T>
void log_grad(const Tensor& x, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
// dx = dout / conj(x) for complex; equals dout / x for real
set_output<T>(out_grad / conj<T>(x), x_grad);
}
}
template <typename T>
void exp_grad(const Tensor& out, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
if (out.dtype() == DataType::FLOAT16 || out.dtype() == DataType::BFLOAT16) {
Tensor out_promote = cast<T>(out, DataType::FLOAT32);
Tensor out_grad_promote = cast<T>(out_grad, DataType::FLOAT32);
set_output<T>(cast<T>(out_promote * out_grad_promote, out.dtype()),
x_grad);
} else {
set_output<T>(out_grad * out, x_grad);
}
}
}
template <typename T>
void sigmoid_grad(const Tensor& out, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
set_output<T>(out_grad * (out * (1 - out)), x_grad);
}
}
template <typename T>
void abs_grad(const Tensor& x, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
auto sign_tmp = sign<T>(x);
set_output<T>(out_grad * sign_tmp, x_grad);
}
}
template <typename T>
void slice_grad(const Tensor& input,
const Tensor& out_grad,
const std::vector<int64_t>& axes,
const IntArray& starts,
const IntArray& ends,
const std::vector<int64_t>& infer_flags,
const std::vector<int64_t>& decrease_axis,
Tensor* input_grad) {
if (input_grad) {
size_t rank = input.dims().size();
auto out_dims = out_grad.dims();
std::vector<int64_t> origin_out_shape;
auto in_dims = input.dims();
auto decrease_size = decrease_axis.size();
if (decrease_size > 0) {
if (decrease_size == static_cast<size_t>(in_dims.size())) {
// all dims decrease
out_dims = common::make_ddim(std::vector<int>(decrease_size, 1));
} else {
origin_out_shape.resize(out_dims.size() + decrease_size, -1);
for (size_t i = 0; i < decrease_size; ++i) {
origin_out_shape[decrease_axis[i]] = 1;
}
int index = 0;
for (size_t i = 0; i < origin_out_shape.size(); ++i) {
if (origin_out_shape[i] == -1) {
origin_out_shape[i] = out_dims[index];
++index;
}
}
out_dims = common::make_ddim(origin_out_shape);
}
}
std::vector<int> offsets(rank, 0);
std::vector<int> extents(rank, 0);
for (size_t i = 0; i < rank; ++i) {
offsets[i] = 0;
extents[i] = out_dims[i];
}
for (size_t i = 0; i < axes.size(); ++i) {
int axis = axes[i];
int64_t start = starts[i] < 0 ? (starts[i] + in_dims[axis]) : starts[i];
start = std::max(start, static_cast<int64_t>(0));
offsets[axis] = start;
}
std::vector<int> paddings;
for (size_t i = 0; i < rank; ++i) {
paddings.push_back(offsets[i]);
paddings.push_back((in_dims[i] - out_dims[i]) - offsets[i]);
}
Tensor reshape_out_grad;
if (out_grad.shape().size() == 0) {
reshape_out_grad = full<T>({1}, 1, input.dtype(), input.place());
} else {
reshape_out_grad = out_grad;
}
if (decrease_size > 0 &&
(decrease_size != static_cast<size_t>(in_dims.size()))) {
auto out_tmp =
pad<T>(reshape<T>(reshape_out_grad, origin_out_shape), paddings, 0.0);
set_output<T>(out_tmp, input_grad);
} else {
auto out_tmp = pad<T>(reshape_out_grad, paddings, 0.0);
set_output<T>(out_tmp, input_grad);
}
}
}
template <typename T>
void group_norm_grad(const Tensor& x,
const optional<Tensor>& scale,
const optional<Tensor>& bias,
const Tensor& y,
const Tensor& mean,
const Tensor& variance,
const Tensor& out_grad,
float epsilon,
int groups,
const std::string& data_layout,
Tensor* x_grad,
Tensor* scale_grad,
Tensor* bias_grad) {
// x.shape=[n,c,h,w]
// y.shape=[n,c,h,w]
// g_size = c/g
// scale.shape=[c]
// mean, var: shape=[n, g]
// inv_std = rsqrt(var + epsilon)
// ds = sum(dy * x, axes=(2,3))
// db = sum(dy, axes=(2,3))
//
// cal d_x:
// s = g / (h*w*c)
// if scale:
// ds_val = sum((ds * scale).reshape(n, g, g_size), axes=2)
// db_val = sum((db * scale).reshape(n, g, g_size), axes=2)
// p1 = (inv_std.reshape(n, g, 1)) * (scale.reshape(1, g, g_size))
// else:
// ds_val = sum(ds.reshape(n, g, g_size), axes=2)
// db_val = sum(db.reshape(n, g, g_size), axes=2)
// p1 = (inv_std.reshape(n, g, 1)) * (ones(1, g, g_size))
// p2 = (db_val * mean - ds_val) * inv_std * inv_std * inv_std * s
// p3 = -p2 * mean - db_val * inv_std * s
// p1.reshape(n, g, g_size, 1)
// p2.reshape(n, g, 1, 1)
// p3.reshape(n, g, 1, 1)
// d_x = dy.reshape(n, g, g_size, h*w) * p1 + x.reshape(n, g, g_size, h*w)* p2
// + p3
//
// cal d_scale:
// temp = ds.reshape(n, g, g_size) - db.reshape(n, g, g_size) *
// mean.reshape(n, g, 1)
// d_scale = sum(temp * inv_std.reshape(n, g, 1), axes=0).reshape(c)
//
// cal d_bias:
// d_bias = sum(dy, axes=(0,2,3))
DataLayout data_layout_ = common::StringToDataLayout(data_layout);
if (data_layout_ != DataLayout::NCHW) {
PADDLE_THROW(common::errors::InvalidArgument(
"Unsupported storage order: %s", data_layout));
}
Tensor x_data = x;
Tensor out_grad_data = out_grad;
if (x.dtype() == DataType::FLOAT16 || x.dtype() == DataType::BFLOAT16) {
x_data = cast<T>(x, DataType::FLOAT32);
}
if (out_grad.dtype() == DataType::FLOAT16 ||
out_grad.dtype() == DataType::BFLOAT16) {
out_grad_data = cast<T>(out_grad, DataType::FLOAT32);
}
std::vector<int64_t> x_dims = common::vectorize<int64_t>(x.dims());
auto add_axis = std::vector<int64_t>({-1});
const int N = x_dims[0];
const int C = x_dims[1];
const int hw = x_dims[2] * x_dims[3];
const int g_num = C / groups;
auto reduce_axis = IntArray(std::vector<int64_t>({2, 3}));
auto shape_group = IntArray(std::vector<int64_t>({N, groups, g_num}));
auto whole_group_shape =
IntArray(std::vector<int64_t>({N, groups, g_num, hw}));
auto scale_ptr = scale.get_ptr();
auto bias_ptr = bias.get_ptr();
auto inv_std = sqrt<T>(1.0 / (variance + epsilon));
auto inv_std_mul_s = inv_std / hw / g_num;
auto dtype = x_data.dtype();
auto sum_y_grad_mul_x =
sum<T>(out_grad_data * x_data, reduce_axis, dtype, false);
auto sum_y_grad = sum<T>(out_grad_data, reduce_axis, dtype, false);
if (x_grad) {
Tensor d1;
Tensor d2;
Tensor p1;
if (scale_ptr) {
auto scale_data = scale.get();
if (scale_data.dtype() == DataType::FLOAT16 ||
scale_data.dtype() == DataType::BFLOAT16) {
scale_data = cast<T>(scale_data, DataType::FLOAT32);
}
d1 = (reshape<T>(sum_y_grad_mul_x * scale_data, shape_group))
.sum(std::vector<int64_t>({2}), dtype, false);
d2 = (reshape<T>(sum_y_grad * scale_data, shape_group))
.sum(std::vector<int64_t>({2}), dtype, false);
p1 = reshape<T>(inv_std, std::vector<int64_t>({N, groups, 1})) *
reshape<T>(scale_data, std::vector<int64_t>({1, groups, g_num}));
} else {
d1 = (reshape<T>(sum_y_grad_mul_x, shape_group))
.sum(std::vector<int64_t>({2}), dtype, false);
d2 = (reshape<T>(sum_y_grad, shape_group))
.sum(std::vector<int64_t>({2}), dtype, false);
p1 = (reshape<T>(inv_std, std::vector<int64_t>({N, groups, 1})))
.expand(IntArray(shape_group));
}
auto p2 = (d2 * mean - d1) * (inv_std_mul_s * inv_std * inv_std);
auto p3 = -p2 * mean - d2 * inv_std_mul_s;
auto first_shape = get_unsqueeze_dims(p1, std::vector<int64_t>({3}));
auto second_shape = get_unsqueeze_dims(p2, std::vector<int64_t>({2, 3}));
p1 = reshape<T>(p1, first_shape);
p2 = reshape<T>(p2, second_shape);
p3 = reshape<T>(p3, second_shape);
auto tmp_1 = reshape<T>(out_grad_data, whole_group_shape) * p1;
auto tmp_2 = reshape<T>(x_data, whole_group_shape) * p2 + p3;
auto x_grad_data = tmp_1 + tmp_2;
x_grad_data = reshape<T>(x_grad_data, x.shape());
if (x.dtype() == DataType::FLOAT16 || x.dtype() == DataType::BFLOAT16) {
x_grad_data = cast<T>(x_grad_data, x.dtype());
}
set_output<T>(x_grad_data, x_grad);
}
if (scale_grad) {
if (scale_ptr) {
auto third_shape = get_unsqueeze_dims(mean, std::vector<int64_t>({2}));
auto tmp1 = (reshape<T>(sum_y_grad_mul_x, shape_group) -
reshape<T>(sum_y_grad, shape_group) *
reshape<T>(mean, third_shape)) *
reshape<T>(inv_std, third_shape);
auto scale_grad_tmp = reshape<T>(
tmp1.sum(std::vector<int64_t>({0}), scale_ptr->dtype(), false),
IntArray(std::vector<int64_t>({C})));
set_output<T>(scale_grad_tmp, scale_grad);
}
}
if (bias_grad) {
if (bias_ptr) {
auto bias_grad_tmp =
sum_y_grad.sum(std::vector<int64_t>({0}), bias_ptr->dtype(), false);
set_output<T>(bias_grad_tmp, bias_grad);
}
}
}
template <typename T>
void layer_norm_grad(const Tensor& x,
const optional<Tensor>& scale,
const optional<Tensor>& bias,
const Tensor& mean,
const Tensor& variance,
const Tensor& out_grad,
double epsilon,
int begin_norm_axis,
Tensor* x_grad,
Tensor* scale_grad,
Tensor* bias_grad) {
auto x_dims = x.dims();
auto shape_1 = 1; // front part
auto shape_2 = 1; // back part
for (int i = 0; i < begin_norm_axis; ++i) {
shape_1 *= x_dims[i];
}
for (int i = begin_norm_axis; i < x.dims().size(); ++i) {
shape_2 *= x_dims[i];
}
auto scale_ptr = scale.get_ptr();
auto bias_ptr = bias.get_ptr();
auto x_cast = reshape<T>(x, std::vector<int64_t>({shape_1, shape_2}));
auto out_grad_cast =
reshape<T>(out_grad, std::vector<int64_t>({shape_1, shape_2}));
auto mean_ = reshape<T>(mean, std::vector<int64_t>({shape_1, 1}));
auto variance_ = reshape<T>(variance, std::vector<int64_t>({shape_1, 1}));
Tensor scale_cast;
if (scale_ptr) {
scale_cast = reshape<T>(*scale_ptr, std::vector<int64_t>({1, shape_2}));
}
// cast dtype to float32 if dtype =float16 or bfloat16
if (x.dtype() == DataType::FLOAT16 || x.dtype() == DataType::BFLOAT16) {
x_cast = cast<T>(x_cast, DataType::FLOAT32);
out_grad_cast = cast<T>(out_grad_cast, DataType::FLOAT32);
if (scale_ptr) {
scale_cast = cast<T>(scale_cast, DataType::FLOAT32);
}
}
auto x_sub_mean = x_cast - mean_; // M,N
auto tmp = (1.0 / (variance_ + epsilon)); // M,1
auto sqrt_var_1 = sqrt<T>(tmp); // M,1
auto x_sub_mean_mul_sqrt_var_1 = x_sub_mean * sqrt_var_1;
if (x_grad) {
auto out_grad_scale = out_grad_cast; // M,N
if (scale_ptr) {
out_grad_scale = out_grad_cast * scale_cast; // M,N * 1,N = M,N
}
auto dx_end = sqrt_var_1 * out_grad_scale;
auto d_mean =
dx_end.sum(std::vector<int64_t>({1}), x_cast.dtype(), true); // M,1
auto d_std_1 =
(tmp * x_sub_mean * out_grad_scale)
.sum(std::vector<int64_t>({1}), x_cast.dtype(), true); // M,1
auto d_std = d_std_1 * x_sub_mean_mul_sqrt_var_1; // M,1 * M,N = M,N
auto d_mean_d_std = (1.0 / shape_2) * (d_mean + d_std);
auto x_grad_tmp = dx_end - d_mean_d_std;
x_grad_tmp = reshape<T>(x_grad_tmp, common::vectorize(x.dims()));
if (x.dtype() == DataType::FLOAT16 || x.dtype() == DataType::BFLOAT16) {
x_grad_tmp = cast<T>(x_grad_tmp, x.dtype());
}
set_output<T>(x_grad_tmp, x_grad);
}
if (scale_grad) {
if (scale_ptr) {
auto scale_grad_tmp =
(x_sub_mean_mul_sqrt_var_1 * out_grad_cast)
.sum(std::vector<int64_t>({0}), x_cast.dtype(), true);
scale_grad_tmp = reshape<T>(scale_grad_tmp, scale_ptr->shape());
if (scale_ptr->dtype() == DataType::FLOAT16 ||
scale_ptr->dtype() == DataType::BFLOAT16) {
scale_grad_tmp = cast<T>(scale_grad_tmp, scale_ptr->dtype());
}
set_output<T>(scale_grad_tmp, scale_grad);
}
}
if (bias_grad) {
if (bias_ptr) {
auto bias_grad_tmp =
out_grad_cast.sum(std::vector<int64_t>({0}), x_cast.dtype(), true);
bias_grad_tmp = reshape<T>(bias_grad_tmp, bias_ptr->shape());
if (bias_ptr->dtype() == DataType::FLOAT16 ||
bias_ptr->dtype() == DataType::BFLOAT16) {
bias_grad_tmp = cast<T>(bias_grad_tmp, bias_ptr->dtype());
}
set_output<T>(bias_grad_tmp, bias_grad);
}
}
}
template <typename T>
void cumsum_grad(const Tensor& x,
const Tensor& out_grad,
const Scalar& axis,
bool flatten,
bool exclusive,
bool reverse,
Tensor* x_grad) {
if (x_grad) {
auto grad = cumsum<T>(out_grad, axis, flatten, exclusive, !reverse);
grad = reshape<T>(grad, x.shape());
set_output<T>(grad, x_grad);
}
}
template <typename T>
void split_grad(const std::vector<Tensor>& out_grad,
const Scalar& axis,
Tensor* x_grad) {
if (x_grad) {
auto grad = concat<T>(out_grad, axis);
set_output<T>(grad, x_grad);
}
}
template <typename T>
void topk_grad(const Tensor& x,
const Tensor& indices,
const Tensor& out_grad,
const Scalar& k,
const int& axis,
const bool& largest,
const bool& sorted,
Tensor* x_grad) {
if (x_grad) {
// put_along_axis doesn't support zero dim
if (x.dims().size() == 0) {
by_pass<T>(out_grad, x_grad);
return;
}
auto zero_tensor =
full<T>(common::vectorize(x.dims()), 0, x.dtype(), x.place());
auto x_grad_tmp = put_along_axis<T>(zero_tensor, indices, out_grad, axis);
set_output<T>(x_grad_tmp, x_grad);
}
}
template <typename T>
void gather_nd_grad(const Tensor& x,
const Tensor& index,
const Tensor& out_grad,
Tensor* x_grad) {
if (x_grad) {
auto zero_tensor =
full<T>(common::vectorize(x.dims()), 0.0, x.dtype(), x.place());
auto x_grad_tmp = scatter_nd_add<T>(zero_tensor, index, out_grad);
set_output<T>(x_grad_tmp, x_grad);
}
}
template <typename T>
void cumprod_grad(const Tensor& x,
const Tensor& out,
const Tensor& out_grad,
int dim,
bool exclusive,
bool reverse,
Tensor* x_grad) {
if (x_grad) {
// dx = cumsum(out * out_grad, dim, false, exclusive, !reverse) / x
std::vector<int64_t> x_dim = common::vectorize<int64_t>(x.dims());
auto zero_tensor = full<T>(x_dim, 0.0, x.dtype(), x.place());
auto zero_mask = cast<T>(equal<T>(x, zero_tensor), x.dtype());
// determine the index of first zero
auto zero_mask_cumsum_exclusive =
cumsum<T>(zero_mask, dim, false, true, reverse);
auto zero_mask_cumsum = scale<T>(zero_mask_cumsum_exclusive, 2) + zero_mask;
auto ones_tensor = full<T>(x_dim, 1.0, x.dtype(), x.place());
auto first_zero_mask =
cast<T>(equal<T>(zero_mask_cumsum, ones_tensor), x.dtype());
// compute the grad for position with value not equal to 0
auto common_dx = cumsum<T>(out * out_grad, dim, false, exclusive, !reverse);
// fill the positions of 0 with 1.
auto replace_one = (1 - zero_mask) * x + zero_mask;
// fill the first positions of 0 with 1.
auto replace_first_one = (1 - first_zero_mask) * x + first_zero_mask;
// recompute the grad of the first position with 0
auto cumprod_recompute =
cumprod<T>(replace_first_one, dim, exclusive, reverse);
auto zeros_dx = cumsum<T>(
cumprod_recompute * out_grad, dim, false, exclusive, !reverse);
auto x_grad_res =
((1 - first_zero_mask) * common_dx + first_zero_mask * zeros_dx) /
replace_one;
set_output<T>(x_grad_res, x_grad);
}
}
template <typename T>
void prod_grad(const Tensor& x,
const Tensor& out,
const Tensor& out_grad,
const IntArray& axis,
bool keep_dim,
bool reduce_all,
Tensor* x_grad) {
if (x_grad) {
std::vector<int64_t> x_dim = common::vectorize<int64_t>(x.dims());
int64_t axis_size = axis.size();
int64_t x_dim_size = x_dim.size();
reduce_all = false;
if (reduce_all || axis_size == 0 || axis_size == x_dim_size) {
reduce_all = true;
} else {
reduce_all = false;
}
auto out_grad_tmp = Tensor();
auto x_reshape = Tensor();
std::vector<int64_t> unchange_axis, change_axis, transpose_shape,
cumprod_shape;
std::vector<int> transpose_dim, origin_position;
if (x_dim_size == 1) {
out_grad_tmp = out_grad.expand(IntArray(x_dim));
} else {
if (!keep_dim) {
auto axis_ = std::vector<int64_t>();
if (reduce_all) {
for (int64_t i = 0; i < x_dim_size; i++) {
axis_.push_back(i);
}
} else {
axis_ = axis.GetData();
for (int64_t i = 0; i < axis_size; i++) {
if (axis[i] < 0) {
axis_[i] = axis[i] + x_dim_size;
}
}
}
auto out_grad_shape = get_unsqueeze_dims(out_grad, axis_);
auto out_grad_ = reshape<T>(out_grad, out_grad_shape);
out_grad_tmp = out_grad_.expand(IntArray(x_dim));
} else {
out_grad_tmp = out_grad.expand(IntArray(x_dim));
}
}
auto axis_ = std::vector<int64_t>();
if (reduce_all) {
int64_t numel = 1;
for (int64_t i = 0; i < x_dim_size; i++) {
axis_.push_back(i);
numel *= x_dim[i];
}
cumprod_shape.push_back(numel);
x_reshape = reshape<T>(x, cumprod_shape);
auto left_cumprod = cumprod<T>(x_reshape, -1, true, false);
auto right_cumprod = cumprod<T>(x_reshape, -1, true, true);
auto x_grad_tmp = left_cumprod * right_cumprod;
auto x_grad_tmp2 = reshape<T>(x_grad_tmp, x.shape());
auto x_grad_res = x_grad_tmp2 * out_grad_tmp;
set_output<T>(x_grad_res, x_grad);
} else {
int64_t unchange_size = x_dim_size - axis_size;
int64_t unchange_index = 0;
for (int64_t i = 0; i < axis_size; i++) {
if (axis[i] < 0) {
axis_.push_back(axis[i] + x_dim_size);
} else {
axis_.push_back(axis[i]);
}
}
for (int64_t i = 0; i < x_dim_size; i++) {
auto it = find(axis_.begin(), axis_.end(), i);
if (it != axis_.end()) {
int64_t index = it - axis_.begin();
origin_position.push_back(static_cast<int>(unchange_size + index));
} else {
unchange_axis.push_back(i);
origin_position.push_back(static_cast<int>(unchange_index));
unchange_index += 1;
}
}
int64_t numel = 1;
for (int64_t i = 0; i < unchange_size; i++) {
transpose_shape.push_back(x_dim[unchange_axis[i]]);
cumprod_shape.push_back(x_dim[unchange_axis[i]]);
transpose_dim.push_back(static_cast<int>(unchange_axis[i]));
}
for (int64_t i = 0; i < axis_size; i++) {
transpose_shape.push_back(x_dim[axis_[i]]);
transpose_dim.push_back(static_cast<int>(axis_[i]));
numel *= x_dim[axis_[i]];
}
cumprod_shape.push_back(numel);
auto x_transpose = transpose<T>(x, transpose_dim);
x_reshape = reshape<T>(x_transpose, cumprod_shape);
auto left_cumprod = cumprod<T>(x_reshape, -1, true, false);
auto right_cumprod = cumprod<T>(x_reshape, -1, true, true);
auto x_grad_tmp = left_cumprod * right_cumprod;
auto x_grad_reshape = reshape<T>(x_grad_tmp, transpose_shape);
auto x_grad_tmp2 = transpose<T>(x_grad_reshape, origin_position);
auto x_grad_res = x_grad_tmp2 * out_grad_tmp;
set_output<T>(x_grad_res, x_grad);
}
}
}
template <typename T>
void max_grad(const Tensor& x,
const Tensor& out,
const Tensor& out_grad,
const IntArray& axis,
bool keepdim,
bool reduce_all,
Tensor* x_grad) {
if (!x_grad) {
return;
}
auto zero_tensor =
full<T>(common::vectorize(x.dims()), 0.0, x.dtype(), x.place());
std::vector<int64_t> x_dim = common::vectorize<int64_t>(x.dims());
int64_t axis_size = axis.size();
int64_t x_dim_size = x_dim.size();
reduce_all = false;
if (reduce_all || axis_size == 0 || axis_size == x_dim_size) {
reduce_all = true;
} else {
reduce_all = false;
}
auto x_grad_tmp = Tensor();
if (x_dim_size == 0 || x_dim_size == 1 || keepdim) {
auto out_grad_tmp = out_grad.expand(IntArray(x_dim));
auto out_tmp = out.expand(IntArray(x_dim));
auto mask = equal<T>(x, out_tmp);
auto mask_sum = sum<T>(mask, axis, x.dtype(), keepdim = true);
auto grad_tmp = out_grad_tmp / mask_sum;
x_grad_tmp = where<T>(mask, grad_tmp, zero_tensor);
} else {
auto axis_ = std::vector<int64_t>();
if (reduce_all) {
for (int64_t i = 0; i < x_dim_size; i++) {
axis_.push_back(i);
}
} else {
axis_ = axis.GetData();
for (int64_t i = 0; i < axis_size; i++) {
if (axis[i] < 0) {
axis_[i] = axis[i] + x_dim_size;
}
}
}
auto out_grad_shape = get_unsqueeze_dims(out_grad, axis_);
auto out_grad_ = reshape<T>(out_grad, out_grad_shape);
auto out_ = reshape<T>(out, out_grad_shape);
auto out_grad_tmp = out_grad_.expand(IntArray(x_dim));
auto out_tmp = out_.expand(IntArray(x_dim));
auto mask = equal<T>(x, out_tmp);
auto mask_sum = sum<T>(mask, axis_, x.dtype(), keepdim = true);
auto grad_tmp = out_grad_tmp / mask_sum;
x_grad_tmp = where<T>(mask, grad_tmp, zero_tensor);
}
set_output<T>(x_grad_tmp, x_grad);
}
template <typename T>
void min_grad(const Tensor& x,
const Tensor& out,
const Tensor& out_grad,
const IntArray& axis,
bool keepdim,
bool reduce_all,
Tensor* x_grad) {
if (!x_grad) {
return;
}
auto zero_tensor =
full<T>(common::vectorize(x.dims()), 0.0, x.dtype(), x.place());
std::vector<int64_t> x_dim = common::vectorize<int64_t>(x.dims());
int64_t axis_size = axis.size();
int64_t x_dim_size = x_dim.size();
reduce_all = false;
if (reduce_all || axis_size == 0 || axis_size == x_dim_size) {
reduce_all = true;
} else {
reduce_all = false;
}
auto x_grad_tmp = Tensor();
if (x_dim_size == 0 || x_dim_size == 1 || keepdim) {
auto out_grad_tmp = out_grad.expand(IntArray(x_dim));
auto out_tmp = out.expand(IntArray(x_dim));
auto mask = equal<T>(x, out_tmp);
auto mask_sum = sum<T>(mask, axis, x.dtype(), keepdim = true);
auto grad_tmp = out_grad_tmp / mask_sum;
x_grad_tmp = where<T>(mask, grad_tmp, zero_tensor);
} else {
auto axis_ = std::vector<int64_t>();
if (reduce_all) {
for (int64_t i = 0; i < x_dim_size; i++) {
axis_.push_back(i);
}
} else {
axis_ = axis.GetData();
for (int64_t i = 0; i < axis_size; i++) {
if (axis[i] < 0) {
axis_[i] = axis[i] + x_dim_size;
}
}
}
auto out_grad_shape = get_unsqueeze_dims(out_grad, axis_);
auto out_grad_ = reshape<T>(out_grad, out_grad_shape);
auto out_ = reshape<T>(out, out_grad_shape);
auto out_grad_tmp = out_grad_.expand(IntArray(x_dim));
auto out_tmp = out_.expand(IntArray(x_dim));
auto mask = equal<T>(x, out_tmp);
auto mask_sum = sum<T>(mask, axis_, x.dtype(), keepdim = true);
auto grad_tmp = out_grad_tmp / mask_sum;
x_grad_tmp = where<T>(mask, grad_tmp, zero_tensor);
}
set_output<T>(x_grad_tmp, x_grad);
}
template <typename T>
void assign_grad(const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
by_pass<T>(out_grad, x_grad);
}
}
template <typename T>
void erf_grad(const Tensor& x, const Tensor& out_grad, Tensor* x_grad) {
if (x_grad) {
auto m_2_sqrt_pi =
full<T>(common::vectorize(x.dims()), M_2_SQRTPI, x.dtype(), x.place());
auto neg_one =
full<T>(common::vectorize(x.dims()), -1.0, x.dtype(), x.place());
auto neg_tmp = neg_one * x * x;
auto mul_tmp = m_2_sqrt_pi * exp<T>(neg_tmp);
set_output<T>(out_grad * mul_tmp, x_grad);
}
}
template <typename T>
void maximum_grad(const Tensor& x,
const Tensor& y,
const Tensor& out_grad,
Tensor* x_grad,
Tensor* y_grad) {
if (out_grad.numel() == 0) {
if (x_grad) {
set_output<T>(full<T>(x.shape(), 0, x.dtype(), x.place()), x_grad);
}
if (y_grad) {
set_output<T>(full<T>(y.shape(), 0, y.dtype(), y.place()), y_grad);
}
return;
}
Tensor half_tensor;
Tensor out_grad_copy = out_grad;
if (x_grad || y_grad) {
// cast, because divide and add kernel is not support bf16 and fp16 on CPU
if (out_grad.dtype() == DataType::BFLOAT16 ||
out_grad.dtype() == DataType::FLOAT16) {
out_grad_copy = cast<T>(out_grad, DataType::FLOAT32);
}
auto equal_tensor = cast<T>(equal<T>(x, y), out_grad_copy.dtype());
auto tmp_tensor =
full<T>({1}, 2.0, out_grad_copy.dtype(), out_grad_copy.place());
half_tensor = (out_grad_copy / tmp_tensor) * equal_tensor;
}
if (x_grad) {
auto x_tmp = cast<T>(greater_than<T>(x, y), out_grad_copy.dtype());
auto dx_res = out_grad_copy * x_tmp + half_tensor;
if (out_grad.dtype() == DataType::BFLOAT16 ||
out_grad.dtype() == DataType::FLOAT16) {
dx_res = cast<T>(dx_res, out_grad.dtype());
}
if (out_grad.dims() != x.dims()) {
// Maybe need reduce here
auto reduce_dim = get_reduce_dims(x.dims(), out_grad.dims());
if (!reduce_dim.size()) {
set_output<T>(dx_res, x_grad);
} else {
auto dx_reduce_res =
dx_res.sum(common::vectorize(reduce_dim),
x.dtype(),
dx_res.dims().size() == x.dims().size());
if (dx_reduce_res.dims() != x.dims()) {
dx_reduce_res =
reshape<T>(dx_reduce_res, common::vectorize(x.dims()));
}
set_output<T>(dx_reduce_res, x_grad);
}
} else {
set_output<T>(dx_res, x_grad);
}
}
if (y_grad) {
auto y_tmp = cast<T>(less_than<T>(x, y), out_grad_copy.dtype());
auto dy_res = out_grad_copy * y_tmp + half_tensor;
if (out_grad.dtype() == DataType::BFLOAT16 ||
out_grad.dtype() == DataType::FLOAT16) {
dy_res = cast<T>(dy_res, out_grad.dtype());
}
if (out_grad.dims() != y.dims()) {
// Maybe need reduce here
DDim reduce_dim = get_reduce_dims(y.dims(), out_grad.dims());
if (!reduce_dim.size()) {
set_output<T>(dy_res, y_grad);
} else {
auto dy_reduce_res =
dy_res.sum(common::vectorize(reduce_dim),
y.dtype(),
dy_res.dims().size() == y.dims().size());
if (dy_reduce_res.dims() != y.dims()) {
dy_reduce_res =
reshape<T>(dy_reduce_res, common::vectorize(y.dims()));
}
set_output<T>(dy_reduce_res, y_grad);
}
} else {
set_output<T>(dy_res, y_grad);
}
}
}
template <typename T>
void dropout_grad(const Tensor& mask,
const Tensor& out_grad,
const Scalar& p,
bool is_test,
const std::string& mode,
Tensor* x_grad) {
if (!x_grad) return;
if (is_test) {
if (mode == "upscale_in_train") {
by_pass<T>(out_grad, x_grad);
} else {
set_output<T>(out_grad * (1.0 - p.to<float>()), x_grad);
}
} else {
if (mode == "upscale_in_train") {
if (p.to<float>() == 1.0f) {
set_output<T>(scale<T>(out_grad, 0.0), x_grad);
} else {
set_output<T>(scale<T>(out_grad * cast<T>(mask, out_grad.dtype()),
1.0 / (1.0 - p.to<float>())),
x_grad);
}
} else {
set_output<T>(out_grad * cast<T>(mask, out_grad.dtype()), x_grad);
}
}
}
template <typename T>
void sin_grad(const Tensor& x, const Tensor& out_grad, Tensor* x_grad) {
auto x_grad_tmp = cos<T>(x) * out_grad;
set_output<T>(x_grad_tmp, x_grad);
}
template <typename T>
void cos_grad(const Tensor& x, const Tensor& out_grad, Tensor* x_grad) {
auto x_grad_tmp = -sin<T>(x) * out_grad;
set_output<T>(x_grad_tmp, x_grad);
}
template <typename T>
void scatter_grad(const Tensor& index,
const Tensor& updates,
const Tensor& out_grad,
bool overwrite,
Tensor* x_grad,
Tensor* updates_grad) {
if (x_grad) {
auto zero_tensor = full<T>(common::vectorize(updates.dims()),
0.0,
updates.dtype(),
updates.place());
auto tmp_grad = scatter<T>(out_grad, index, zero_tensor, false);
set_output<T>(tmp_grad, x_grad);
}
if (updates_grad) {
Scalar tmp_zero = 0;
auto tmp_updates_grad = gather<T>(out_grad, index, tmp_zero);
// NOTE: len(index) can be smaller than len(updates) when updates is not a
// scalar
auto updates_dims = common::vectorize(updates.dims());
auto index_dims = common::vectorize(index.dims());
if (updates_dims.size() > 0 && updates_dims[0] > index_dims[0]) {
// Pad zeros to the end of tmp_updates_grad to make its shape the same as
// updates.
decltype(updates_dims) padding_dims = updates_dims;
padding_dims[0] = updates_dims[0] - index_dims[0];
auto padding_zeros =
full<T>(padding_dims, 0, updates.dtype(), updates.place());
tmp_updates_grad =
concat<T>({tmp_updates_grad, std::move(padding_zeros)}, 0);
}
set_output<T>(tmp_updates_grad, updates_grad);
}
}
template <typename T>
void batch_norm_grad(const Tensor& x,
const optional<Tensor>& scale,
const optional<Tensor>& bias,
const optional<Tensor>& mean_out,
const optional<Tensor>& variance_out,
const Tensor& saved_mean,
const Tensor& saved_variance,
const optional<Tensor>& reserve_space,
const Tensor& out_grad,
float momentum,
float epsilon,
const std::string& data_layout,
bool is_test,
bool use_global_stats,
bool trainable_statistics,
Tensor* x_grad,
Tensor* scale_grad,
Tensor* bias_grad) {
use_global_stats = is_test || use_global_stats;
DataLayout data_layout_ = common::StringToDataLayout(data_layout);
Tensor x_data = x;
Tensor out_grad_data = out_grad;
bool need_cast =
x.dtype() == DataType::FLOAT16 || x.dtype() == DataType::BFLOAT16;
if (need_cast) {
x_data = cast<T>(x, DataType::FLOAT32);
}
if (out_grad.dtype() == DataType::FLOAT16 ||
out_grad.dtype() == DataType::BFLOAT16) {
out_grad_data = cast<T>(out_grad, DataType::FLOAT32);
}
auto x_dims = x_data.dims();
const int C = (data_layout_ == DataLayout::NCHW ? x_dims[1]
: x_dims[x_dims.size() - 1]);
int nume = 1;
for (auto i = 0; i < x_dims.size(); i++) {
nume = nume * x_dims[i];
}
const int nhw = nume / C;
if (x_dims.size() == 2 && data_layout_ == DataLayout::NCHW) {
data_layout_ = DataLayout::NHWC;
}
auto run_var = variance_out.get();
auto run_mean = mean_out.get();
Tensor mean_data;
Tensor rsqrt_var;
if (use_global_stats) {
auto eps = full<T>(common::vectorize(run_var.dims()),
epsilon,
run_var.dtype(),
run_var.place());
mean_data = run_mean;
rsqrt_var = (run_var + eps).pow(-0.5);
} else {
mean_data = saved_mean;
rsqrt_var = saved_variance;
}
// inv_var = 1 / sqrt(var + eps)
// reduce_axis = [0, 2, 3] (NCHW) [0, 1, 2] (NHWC)
//
// d_bias = np.sum(d_y, reduce_axis)
// d_scale = np.sum((X - mean) / inv_var * dy, reduce_axis)
//
// train mode
// d_x = (1. / nhw) * scale * inv_var
// *(nhw * d_y - np.sum(d_y, reduce_axis) - (X - mean) * inv_var * inv_var *
// np.sum(d_y * (X - mean), reduce_axis))
//
// test mode
// d_x = d_y * scale * inv_var
std::vector<int> nchw_to_nhwc_dim = {0, 2, 3, 1};
std::vector<int> nhwc_to_nchw_dim = {0, 3, 1, 2};
auto reduce_axis = IntArray(std::vector<int64_t>{0, 1, 2});
auto dtype = x_data.dtype();
switch (data_layout_) {
case DataLayout::NCHW: {
auto nhwc_x = transpose<T>(x_data, nchw_to_nhwc_dim);
auto nhwc_out_grad = transpose<T>(out_grad_data, nchw_to_nhwc_dim);
auto nhwc_out_grad_sum = sum<T>(nhwc_out_grad, reduce_axis, dtype, false);
auto sum_dout_mul_diff = sum<T>(
nhwc_out_grad * (nhwc_x - mean_data), reduce_axis, dtype, false);
if (x_grad) {
if (use_global_stats) {
auto nhwc_x_grad = rsqrt_var * nhwc_out_grad;
if (scale) {
nhwc_x_grad = scale.get() * nhwc_x_grad;
}
auto nchw_x_grad = transpose<T>(nhwc_x_grad, nhwc_to_nchw_dim);
if (need_cast) {
nchw_x_grad = cast<T>(nchw_x_grad, x.dtype());
}
set_output<T>(nchw_x_grad, x_grad);
} else {
auto part1 = rsqrt_var;
if (scale) {
part1 = scale.get() * part1;
}
auto mean_temp1 = nhwc_out_grad_sum / nhw;
auto mean_temp2 = sum_dout_mul_diff / nhw * rsqrt_var * rsqrt_var;
auto part2 =
nhwc_out_grad - mean_temp1 - (nhwc_x - mean_data) * mean_temp2;
auto x_grad_data = part1 * part2;
auto nchw_x_grad = transpose<T>(x_grad_data, nhwc_to_nchw_dim);
if (need_cast) {
nchw_x_grad = cast<T>(nchw_x_grad, x.dtype());
}
set_output<T>(nchw_x_grad, x_grad);
}
}
if (scale_grad) {
auto scale_grad_data = sum_dout_mul_diff * rsqrt_var;
set_output<T>(scale_grad_data, scale_grad);
}
if (bias_grad) {
set_output<T>(assign<T>(nhwc_out_grad_sum), bias_grad);
}
break;
}
case DataLayout::NHWC: {
if (x_grad) {
auto out_grad_data_sum =
sum<T>(out_grad_data, reduce_axis, dtype, false);
auto nhwc_sum_dout_mul_diff = sum<T>(
out_grad_data * (x_data - mean_data), reduce_axis, dtype, false);
if (use_global_stats) {
auto x_grad_data = rsqrt_var * out_grad_data;
if (scale) {
x_grad_data = scale.get() * x_grad_data;
}
if (need_cast) {
x_grad_data = cast<T>(x_grad_data, x.dtype());
}
set_output<T>(x_grad_data, x_grad);
} else {
auto part1 = rsqrt_var;
if (scale) {
part1 = scale.get() * part1;
}
auto mean_temp1 = out_grad_data_sum / nhw;
auto mean_temp2 =
nhwc_sum_dout_mul_diff / nhw * rsqrt_var * rsqrt_var;
auto part2 =
out_grad_data - mean_temp1 - (x_data - mean_data) * mean_temp2;
auto x_grad_data = part1 * part2;
if (need_cast) {
x_grad_data = cast<T>(x_grad_data, x.dtype());
}
set_output<T>(x_grad_data, x_grad);
}
if (scale_grad) {
auto scale_grad_data = nhwc_sum_dout_mul_diff * rsqrt_var;
set_output<T>(scale_grad_data, scale_grad);
}
if (bias_grad) {
set_output<T>(assign<T>(out_grad_data_sum), bias_grad);
}
}
break;
}
default:
PADDLE_THROW(common::errors::InvalidArgument("Unknown storage order: %s",
data_layout));
}
}
template <typename T>
void instance_norm_grad(const Tensor& x,
const optional<Tensor>& scale,
const optional<Tensor>& bias UNUSED,
const Tensor& saved_mean,
const Tensor& saved_variance,
const Tensor& y_grad,
float epsilon,
Tensor* x_grad,
Tensor* scale_grad,
Tensor* bias_grad) {
// TODO(large-tensor): downstream functors may still use int; guard until
// upgraded.
int64_t n = x.dims()[0];
// TODO(large-tensor): downstream functors may still use int; guard until
// upgraded.
int64_t c = x.dims()[1];
// TODO(large-tensor): downstream functors may still use int; guard until
// upgraded.
int64_t h = x.dims()[2];
// TODO(large-tensor): downstream functors may still use int; guard until
// upgraded.
int64_t w = x.dims()[3];
auto promoted_y_grad = y_grad;
if (x.dtype() == DataType::FLOAT16 || x.dtype() == DataType::BFLOAT16) {
promoted_y_grad = cast<T>(y_grad, DataType::FLOAT32);
}
Tensor x_hat;
Tensor std_inv;
if (scale_grad || x_grad) {
auto promoted_x = x;
auto promoted_saved_mean = saved_mean;
auto promoted_saved_var = saved_variance;
if (x.dtype() == DataType::FLOAT16 || x.dtype() == DataType::BFLOAT16) {
promoted_x = cast<T>(x, DataType::FLOAT32);
promoted_saved_mean = cast<T>(saved_mean, DataType::FLOAT32);
promoted_saved_var = cast<T>(saved_variance, DataType::FLOAT32);
}
auto mean = reshape<T>(promoted_saved_mean, IntArray({n, c, 1, 1}))
.tile(IntArray({1, 1, h, w}));
std_inv = reshape<T>(promoted_saved_var, IntArray({n, c, 1, 1}))
.tile(IntArray({1, 1, h, w}));
x_hat = (promoted_x - mean) * std_inv;
}
// x_grad = scale * inv_var * (y_grad - y_grad.mean(2,3) - x_hat * (y_grad *
// x_hat).mean((h,w)))
if (x_grad) {
auto scale_data =
reshape<T>(scale.get_ptr()
? scale.get()
: full<T>(IntArray({c}), 1., x.dtype(), x.place()),
IntArray({1, c, 1, 1}))
.tile(IntArray({n, 1, h, w}));
auto promoted_scale = scale_data;
if (scale_data.dtype() == DataType::FLOAT16 ||
scale_data.dtype() == DataType::BFLOAT16) {
promoted_scale = cast<T>(scale_data, DataType::FLOAT32);
}
auto result =
(promoted_scale * std_inv) *
(promoted_y_grad -
promoted_y_grad.sum(IntArray({2, 3}), promoted_y_grad.dtype(), true) /
(h * w) -
(x_hat * ((promoted_y_grad * x_hat)
.sum(IntArray({2, 3}), promoted_y_grad.dtype(), true) /
(h * w))));
if (x.dtype() == DataType::FLOAT16 || x.dtype() == DataType::BFLOAT16) {
set_output<T>(cast<T>(result, x.dtype()), x_grad);
} else {
set_output<T>(result, x_grad);
}
}
// scale_grad = x_hat * y_grad.sum(n, h, w)
if (scale_grad) {
auto result = (promoted_y_grad * x_hat).sum(IntArray({0, 2, 3}));
auto scale_dtype = scale.get_ptr() ? scale.get().dtype() : x.dtype();
if (scale_dtype == DataType::FLOAT16 || scale_dtype == DataType::BFLOAT16) {
set_output<T>(cast<T>(result, scale_dtype), scale_grad);
} else {
set_output<T>(result, scale_grad);
}
}
// d_bias = y_grad.sum(n, h, w)
if (bias_grad) {
auto result = promoted_y_grad.sum(IntArray({0, 2, 3}));
auto scale_dtype = scale.get_ptr() ? scale.get().dtype() : x.dtype();
if (scale_dtype == DataType::FLOAT16 || scale_dtype == DataType::BFLOAT16) {
set_output<T>(cast<T>(result, scale_dtype), bias_grad);
} else {
set_output<T>(result, bias_grad);
}
}
}
template <typename T>
void gelu_grad(const Tensor& x,
const Tensor& out_grad,
bool approximate,
Tensor* x_grad) {
if (!x_grad) return;
// Promote to fp32 when the input type is fp16 for keeping consistent with
// phi kernel
if (x.dtype() == DataType::FLOAT16 || x.dtype() == DataType::BFLOAT16) {
auto promoted_x = cast<T>(x, DataType::FLOAT32);
auto promoted_out_grad = cast<T>(out_grad, DataType::FLOAT32);
if (approximate) {
float kbeta = M_SQRT2 * M_2_SQRTPI * 0.5;
float kkappa = 0.044715;
auto x_sq = promoted_x * promoted_x;
auto x_cube = x_sq * promoted_x;
auto inner = kbeta * (promoted_x + kkappa * x_cube);
auto tanh_inner = tanh<T>(inner);
auto left = scale<T>(promoted_x, 0.5);
auto right = scale<T>(tanh_inner, 1., 1.);
auto left_derivative = scale<T>(right, 0.5);
auto tanh_derivative = scale<T>(tanh_inner * tanh_inner, -1., 1.);
auto inner_derivative = kbeta * (scale<T>(3 * kkappa * x_sq, 1., 1.));
auto right_derivative = left * tanh_derivative * inner_derivative;
set_output<T>(
cast<T>(promoted_out_grad * (left_derivative + right_derivative),
x.type()),
x_grad);
} else {
float kalpha = M_SQRT1_2;
float kbeta = M_2_SQRTPI * M_SQRT1_2 * 0.5;
auto cdf = scale<T>(scale<T>(erf<T>(kalpha * promoted_x), 1., 1.), 0.5);
auto pdf = kbeta * exp<T>(scale<T>(promoted_x * promoted_x, -0.5));
set_output<T>(
cast<T>(promoted_out_grad * (cdf + promoted_x * pdf), x.type()),
x_grad);
}
} else {
// Scale only support fp32 attr in static graph mode, use elementwise_xx
// when precision is over fp32.
if (approximate) {
auto kBeta = M_SQRT2 * M_2_SQRTPI * 0.5;
auto kKappa = 0.044715;
auto x_sq = x * x;
auto x_cube = x_sq * x;
auto inner = kBeta * (x + kKappa * x_cube);
auto tanh_inner = tanh<T>(inner);
auto left = scale<T>(x, 0.5);
auto right = scale<T>(tanh_inner, 1., 1.);
auto left_derivative = scale<T>(right, 0.5);
auto tanh_derivative = scale<T>(tanh_inner * tanh_inner, -1., 1.);
auto inner_derivative = kBeta * (scale<T>(3 * kKappa * x_sq, 1., 1.));
auto right_derivative = left * tanh_derivative * inner_derivative;
set_output<T>(out_grad * (left_derivative + right_derivative), x_grad);
} else {
auto kAlpha = M_SQRT1_2;
auto kBeta = M_2_SQRTPI * M_SQRT1_2 * 0.5;
auto cdf = scale<T>(scale<T>(erf<T>(kAlpha * x), 1., 1.), 0.5);
auto pdf = kBeta * exp<T>(scale<T>(x * x, -0.5));
set_output<T>(out_grad * (cdf + x * pdf), x_grad);
}
}
}
template <typename T>
void minimum_grad(const Tensor& x,
const Tensor& y,
const Tensor& out_grad,
Tensor* x_grad,
Tensor* y_grad) {
if (out_grad.numel() == 0) {
if (x_grad) {
set_output<T>(full<T>(x.shape(), 0, x.dtype(), x.place()), x_grad);
}
if (y_grad) {
set_output<T>(full<T>(y.shape(), 0, y.dtype(), y.place()), y_grad);
}
return;
}
Tensor half_tensor;
Tensor out_grad_copy = out_grad;
if (x_grad || y_grad) {
// cast, because divide and add kernel is not support bf16 and fp16 on CPU
if (out_grad.dtype() == DataType::BFLOAT16 ||
out_grad.dtype() == DataType::FLOAT16) {
out_grad_copy = cast<T>(out_grad, DataType::FLOAT32);
}
auto equal_tensor = cast<T>(equal<T>(x, y), out_grad_copy.dtype());
auto tmp_tensor =
full<T>({1}, 2.0, out_grad_copy.dtype(), out_grad_copy.place());
half_tensor = (out_grad_copy / tmp_tensor) * equal_tensor;
}
if (x_grad) {
auto x_tmp = cast<T>(less_than<T>(x, y), out_grad_copy.dtype());
auto dx_res = out_grad_copy * x_tmp + half_tensor;
if (out_grad.dtype() == DataType::BFLOAT16 ||
out_grad.dtype() == DataType::FLOAT16) {
dx_res = cast<T>(dx_res, out_grad.dtype());
}
if (out_grad.dims() != x.dims()) {
// Maybe need reduce here
auto reduce_dim = get_reduce_dims(x.dims(), out_grad.dims());
if (!reduce_dim.size()) {
set_output<T>(dx_res, x_grad);
} else {
auto dx_reduce_res =
dx_res.sum(common::vectorize(reduce_dim),
x.dtype(),
dx_res.dims().size() == x.dims().size());
if (dx_reduce_res.dims() != x.dims()) {
dx_reduce_res =
reshape<T>(dx_reduce_res, common::vectorize(x.dims()));
}
set_output<T>(dx_reduce_res, x_grad);
}
} else {
set_output<T>(dx_res, x_grad);
}
}
if (y_grad) {
auto y_tmp = cast<T>(greater_than<T>(x, y), out_grad_copy.dtype());
auto dy_res = out_grad_copy * y_tmp + half_tensor;
if (out_grad.dtype() == DataType::BFLOAT16 ||
out_grad.dtype() == DataType::FLOAT16) {
dy_res = cast<T>(dy_res, out_grad.dtype());
}
if (out_grad.dims() != y.dims()) {
// Maybe need reduce here
DDim reduce_dim = get_reduce_dims(y.dims(), out_grad.dims());
if (!reduce_dim.size()) {
set_output<T>(dy_res, y_grad);
} else {
auto dy_reduce_res =
dy_res.sum(common::vectorize(reduce_dim),
y.dtype(),
dy_res.dims().size() == y.dims().size());
if (dy_reduce_res.dims() != y.dims()) {
dy_reduce_res =
reshape<T>(dy_reduce_res, common::vectorize(y.dims()));
}
set_output<T>(dy_reduce_res, y_grad);
}
} else {
set_output<T>(dy_res, y_grad);
}
}
}
template <typename T>
void tile_grad(const Tensor& x,
const Tensor& out_grad,
const IntArray& repeat_times,
Tensor* x_grad) {
if (x_grad) {
auto repeat_times_data = repeat_times.GetData();
auto out_grad_shape = common::vectorize<int>(out_grad.dims());
auto result = out_grad;
for (int i = 0; i < static_cast<int>(repeat_times_data.size()); i++) {
int size = out_grad_shape[i] / repeat_times_data[i];
std::vector<int> sections(repeat_times_data[i], size);
auto split_arr = split<T>(result, IntArray(sections), i);
result = full<T>(
common::vectorize(split_arr[0].dims()), 0.0, x.dtype(), x.place());
for (int j = 0; j < static_cast<int>(split_arr.size()); j++) {
result = split_arr[j] + result;
}
}
result = reshape<T>(result, x.shape());
set_output<T>(result, x_grad);
}
}
template <typename T>
void roll_grad(const Tensor& x,
const Tensor& out_grad,
const IntArray& shifts,
const std::vector<int64_t>& axis,
Tensor* x_grad) {
if (x_grad) {
auto shifts_ = shifts.GetData();
int64_t nums = shifts_.size();
for (int64_t i = 0; i < nums; i++) {
shifts_[i] = 0 - shifts_[i];
}
auto x_grad_output = roll<T>(out_grad, shifts_, axis);
set_output<T>(x_grad_output, x_grad);
}
}
template <typename T>
void pad_grad(const Tensor& input,
const Tensor& out_grad,
const std::vector<int>& paddings,
const Scalar& pad_value,
Tensor* input_grad) {
if (input_grad) {
size_t rank = input.dims().size();
auto out_dims = out_grad.dims();
std::vector<int64_t> starts(rank, 0);
std::vector<int64_t> ends(rank, 0);
std::vector<int64_t> axes(rank, 0);
std::vector<int64_t> infer_flags(rank, 1);
std::vector<int64_t> decrease_axis({});
for (size_t i = 0; i < rank; ++i) {
starts[i] = static_cast<int64_t>(paddings[2 * i]);
ends[i] = static_cast<int64_t>(out_dims[i] - paddings[2 * i + 1]);
axes[i] = i;
}
auto out_tmp =
slice<T>(out_grad, axes, starts, ends, infer_flags, decrease_axis);
set_output<T>(out_tmp, input_grad);
}
}
template <typename T>
void scatter_nd_add_grad(const Tensor& index,
const Tensor& updates,
const Tensor& out_grad,
Tensor* x_grad,
Tensor* updates_grad) {
if (x_grad) {
by_pass<T>(out_grad, x_grad);
}
if (updates_grad) {
// Gradient by Gather: dUpdates = dO[Ids]
auto tmp_updates_grad = gather_nd<T>(out_grad, index);
set_output<T>(tmp_updates_grad, updates_grad);
}
}
template <typename T>
void p_norm_grad(const Tensor& x,
/*output of forward was reserved for efficient backward*/
const Tensor& out,
const Tensor& out_grad,
float porder,
int axis,
float epsilon,
bool keepdim,
bool asvector,
Tensor* x_grad) {
if (x_grad) {
if (axis < 0) {
axis += x.dims().size();
}
Tensor x_grad_tmp;
if (porder == 0.0) {
// dx = 0
x_grad_tmp = full<T>(x.shape(), 0, x.dtype(), x.place());
} else {
/* generic case formula:
dx = {
dy * y^(1-p) * |x|^(p-1) * sgn(x), if p != +-inf,
dy * sgn(x) * (x==y), if p == +-inf.
}
*/
Tensor expand_out = out;
Tensor expand_out_grad = out_grad;
// firstly expand output_grad to same ndim with x for convenience
if (!keepdim) {
auto expand_shape = common::vectorize(out_grad.dims());
expand_shape.insert(expand_shape.begin() + axis, 1);
expand_out_grad = reshape<T>(out_grad, expand_shape);
expand_out = reshape<T>(out, expand_shape);
}
if (porder == 1.0) {
// dx = dy * sign(x)
auto x_sign = sign<T>(x);
x_grad_tmp = x_sign * expand_out_grad;
} else if (porder == 2.0) {
// dx = dy * (x / y)
x_grad_tmp = x / expand_out;
// fill zero to avoid division by zero
auto _zero_tensor =
full<T>(common::vectorize(x.dims()), 0.0, x.dtype(), x.place());
auto finite_mask = isfinite<T>(x_grad_tmp);
x_grad_tmp = where<T>(finite_mask, x_grad_tmp, _zero_tensor);
x_grad_tmp = expand_out_grad * (x_grad_tmp);
} else if (porder == INFINITY || porder == -INFINITY) {
// dy * sgn(x) * (x==y), if p == +-inf.
auto x_abs = abs<T>(x);
auto mask =
cast<T>(bitwise_or<T>(equal<T>(x_abs, expand_out), isnan<T>(x_abs)),
expand_out.dtype());
auto x_sign = sign<T>(x);
x_grad_tmp =
x_sign * ((expand_out_grad /
sum<T>(mask, {axis}, expand_out_grad.dtype(), true)) *
mask);
} else if (porder < 1.0) {
// dx = dy * y^(1-p) * |x|^(p-1) * sgn(x)
auto x_sign = sign<T>(x);
auto x_abs_pow = abs<T>(x);
x_abs_pow = x_abs_pow.pow(porder - 1);
auto x_scaled = x_sign * x_abs_pow;
x_grad_tmp = x_scaled * expand_out_grad * expand_out.pow(1 - porder);
} else if (porder < 2.0) {
// dx = dy * y^(1-p) * |x|^(p-1) * sgn(x)
auto x_sign = sign<T>(x);
auto x_abs_pow = abs<T>(x);
x_abs_pow = x_abs_pow.pow(porder - 1);
// auto scale_v = expand_out_grad / expand_out.pow(porder - 1);
// auto _zero_tensor =
// full<T>(common::vectorize(x.dims()), 0.0, x.dtype());
// auto out_non_zero_mask = not_equal<T>(expand_out, _zero_tensor);
// scale_v = scale_v * cast<T>(out_non_zero_mask, scale_v.dtype());
// x_grad_tmp = x_sign * x_abs_pow * scale_v;
auto scale_v = expand_out_grad * expand_out.pow(1 - porder);
x_grad_tmp = x_sign * x_abs_pow * scale_v;
} else {
// dx = dy * y^(1-p) * |x|^(p-1) * sgn(x)
auto x_sign = sign<T>(x);
auto x_abs_pow = abs<T>(x);
x_abs_pow = x_abs_pow.pow(porder - 1);
auto x_scaled = x_sign * x_abs_pow;
x_grad_tmp = x_scaled * expand_out_grad * expand_out.pow(1 - porder);
}
}
set_output<T>(x_grad_tmp, x_grad);
}
}
template <typename T>
void var_grad(const Tensor& x,
const Tensor& out_grad,
const IntArray& axis,
bool keepdim,
bool unbiased,
double correction,
Tensor* x_grad) {
if (x_grad) {
auto axis_vec = axis.GetData();
auto x_dims = common::vectorize<int64_t>(x.dims());
int64_t x_rank = x_dims.size();
if (axis_vec.empty()) {
for (int64_t i = 0; i < x_rank; ++i) {
axis_vec.push_back(i);
}
}
for (size_t i = 0; i < axis_vec.size(); ++i) {
if (axis_vec[i] < 0) {
axis_vec[i] += x_rank;
}
}
auto ones_x =
full<T>(common::vectorize(x.dims()), 1.0, x.dtype(), x.place());
auto n_tensor = sum<T>(ones_x, axis, x.dtype(), true);
auto correction_tensor = full<T>(
common::vectorize(n_tensor.dims()), correction, x.dtype(), x.place());
auto divisor = n_tensor - correction_tensor;
auto sum_val = sum<T>(x, axis, x.dtype(), true);
auto mean_val = sum_val / n_tensor;
auto diff = x - mean_val;
Tensor out_grad_broadcast = out_grad;
if (!keepdim) {
auto out_grad_shape = get_unsqueeze_dims(out_grad, axis_vec);
out_grad_broadcast = reshape<T>(out_grad, out_grad_shape);
}
auto two = full<T>(common::vectorize(x.dims()), 2.0, x.dtype(), x.place());
auto res = two * out_grad_broadcast * diff / divisor;
set_output<T>(res, x_grad);
}
}
} // namespace prim
} // namespace paddle