191 lines
6.7 KiB
C++
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
|