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

191 lines
6.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.
#pragma once
#include "paddle/phi/backends/cpu/cpu_context.h"
#include "paddle/phi/core/dense_tensor.h"
#include "paddle/phi/kernels/cpu/reduce.h"
#include "paddle/phi/kernels/funcs/eigen/common.h"
#include "paddle/phi/kernels/funcs/eigen/eigen_function.h"
namespace phi {
namespace funcs {
// This ReduceGradFunctor is only the CPU implement.
template <typename Context, typename T, size_t D, typename Functor>
void ReduceGradFunctor(const Context& dev_ctx,
const DenseTensor& input0,
const DenseTensor& input1,
const DenseTensor& input2,
DenseTensor* output,
Functor functor,
const std::vector<int>& dims) {
auto x = EigenTensor<T, D>::From(input0);
auto x_grad = EigenTensor<T, D>::From(*output);
auto x_rank = static_cast<int>(x.dimensions().size());
auto x_dims = input0.dims();
auto reduced_dims_v = vectorize(x_dims);
std::vector<int> dims_ref = dims;
Eigen::array<int64_t, D> broadcast_dim;
for (size_t i = 0; i < D; ++i) broadcast_dim[i] = 1;
int64_t broad_cast_times = 1;
for (size_t i = 0; i < dims_ref.size(); ++i) {
if (dims_ref[i] < 0) {
dims_ref[i] = x_rank + dims_ref[i];
}
reduced_dims_v[dims_ref[i]] = 1;
broadcast_dim[dims_ref[i]] = x_dims[dims_ref[i]];
broad_cast_times *= x_dims[dims_ref[i]];
}
auto reduced_dims = make_ddim(reduced_dims_v);
auto x_reduce = EigenTensor<T, D>::From(input1, reduced_dims);
auto x_reduce_grad = EigenTensor<T, D>::From(input2, reduced_dims);
auto& place = *dev_ctx.eigen_device();
functor(place,
&x,
&x_reduce,
&x_grad,
&x_reduce_grad,
broadcast_dim,
broad_cast_times);
}
inline void GetOriginDimFromShuffled(const DDim& src_dim,
const std::vector<int>& dims,
std::vector<int>* origin_dim) {
DDim shuffled_dims(src_dim);
size_t n = src_dim.size();
std::vector<int> perm_axis(n);
std::vector<int64_t> dims_64{dims.begin(), dims.end()};
GetShuffledDim(src_dim, &shuffled_dims, dims_64, &perm_axis);
for (size_t i = 0; i < n; ++i) {
(*origin_dim)[perm_axis[i]] = i;
}
}
template <typename Context, typename T, typename Functor>
void HandleLargeDimGrad(const Context& dev_ctx,
const DenseTensor* x,
const DenseTensor* out,
const DenseTensor* dout,
DenseTensor* dx,
Functor functor,
const std::vector<int>& dims) {
const int64_t unreduced = out->numel();
const int64_t x_numel = x->numel();
// assume: 0 / 0 == 0, which allow process 0 dim tensor
const int64_t reduced = (unreduced != 0) ? (x_numel / unreduced) : 0;
PADDLE_ENFORCE_EQ(
unreduced * reduced,
x_numel,
common::errors::InvalidArgument(
"Reducing failed in HandleLargeDimGrad, when try to transpose (%d) "
"operands into 2D tensor with shape (%d, %d).",
x_numel,
unreduced,
reduced));
DDim out_dim(out->dims());
DDim x_dim(x->dims());
// transpose and reshape X
DenseTensor shuffled_x;
std::vector<int64_t> dims_64{dims.begin(), dims.end()};
GetShuffledInput<Context, T>(dev_ctx, *x, &shuffled_x, dims_64);
DDim shuffled_dim = shuffled_x.dims();
shuffled_x.Resize({unreduced, reduced});
// reshape dX {unreduced, reduced}
dx->Resize({unreduced, reduced});
ReduceGradFunctor<Context, T, 2, Functor>(
dev_ctx, shuffled_x, *out, *dout, dx, functor, {1});
// transpose dX
std::vector<int> origin_axis(x_dim.size());
GetOriginDimFromShuffled(x_dim, dims, &origin_axis);
DenseTensor dx_tmp;
phi::Copy(dev_ctx, *dx, dev_ctx.GetPlace(), false, &dx_tmp);
dx_tmp.Resize(shuffled_dim);
dx->Resize(x_dim);
funcs::TransposeNormal<Context, T> trans;
trans(dev_ctx, dx_tmp, dx, origin_axis);
}
// Only for CPU
template <typename Context, typename T, typename Functor>
void LaunchReduceGradKernel(const Context& dev_ctx,
const DenseTensor* input0,
const DenseTensor* input1,
const DenseTensor* input2,
DenseTensor* output,
Functor functor,
const std::vector<int>& dims,
bool reduce_all = false) {
if (reduce_all) {
auto x = EigenVector<T>::Flatten(*input0);
auto x_reduce = EigenVector<T>::Flatten(*input1);
auto x_reduce_grad = EigenVector<T>::Flatten(*input2);
auto x_grad = EigenVector<T>::Flatten(*output);
auto& place = *dev_ctx.eigen_device();
// *dev_ctx.eigen_device();
auto broadcast_dim =
Eigen::array<int64_t, 1>({{static_cast<int64_t>(input0->numel())}});
functor(place,
&x,
&x_reduce,
&x_grad,
&x_reduce_grad,
broadcast_dim,
broadcast_dim[0]);
} else {
int rank = input0->dims().size();
switch (rank) {
case 1:
ReduceGradFunctor<Context, T, 1, Functor>(
dev_ctx, *input0, *input1, *input2, output, functor, dims);
break;
case 2:
ReduceGradFunctor<Context, T, 2, Functor>(
dev_ctx, *input0, *input1, *input2, output, functor, dims);
break;
case 3:
ReduceGradFunctor<Context, T, 3, Functor>(
dev_ctx, *input0, *input1, *input2, output, functor, dims);
break;
case 4:
ReduceGradFunctor<Context, T, 4, Functor>(
dev_ctx, *input0, *input1, *input2, output, functor, dims);
break;
case 5:
ReduceGradFunctor<Context, T, 5, Functor>(
dev_ctx, *input0, *input1, *input2, output, functor, dims);
break;
case 6:
ReduceGradFunctor<Context, T, 6, Functor>(
dev_ctx, *input0, *input1, *input2, output, functor, dims);
break;
default:
HandleLargeDimGrad<Context, T, Functor>(
dev_ctx, input0, input1, input2, output, functor, dims);
break;
}
}
}
} // namespace funcs
} // namespace phi