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

144 lines
4.4 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/core/dense_tensor.h"
#include "paddle/phi/kernels/funcs/blas/blas.h"
#include "paddle/phi/kernels/funcs/eigen/common.h"
namespace phi {
template <typename T, typename Context>
void BilinearGradKernel(const Context& dev_ctx,
const DenseTensor& x,
const DenseTensor& y,
const DenseTensor& weight,
const DenseTensor& dout,
DenseTensor* dx,
DenseTensor* dy,
DenseTensor* dweight,
DenseTensor* dbias) {
auto batch_size = x.dims()[0];
auto weight_dims = weight.dims();
int out_dim = weight_dims[0];
auto x_dim = weight_dims[1];
auto y_dim = weight_dims[2];
auto x_mat = EigenMatrix<T>::From(x);
auto y_mat = EigenMatrix<T>::From(y);
auto dout_mat = EigenMatrix<T>::From(dout);
auto& place = *dev_ctx.eigen_device();
// Create the intermediate variable to calculate the Output(Y@GRAD).
DenseTensor x_scale;
x_scale.Resize({batch_size, x_dim});
dev_ctx.template Alloc<T>(&x_scale);
auto x_scale_mat = EigenMatrix<T>::From(x_scale);
// Create the intermediate variable to calculate the Output(X@GRAD).
DenseTensor y_scale;
y_scale.Resize({batch_size, y_dim});
dev_ctx.template Alloc<T>(&y_scale);
auto y_scale_mat = EigenMatrix<T>::From(y_scale);
funcs::SetConstant<Context, T> set_zero;
if (dx) {
dev_ctx.template Alloc<T>(dx);
set_zero(dev_ctx, dx, static_cast<T>(0));
}
if (dy) {
dev_ctx.template Alloc<T>(dy);
set_zero(dev_ctx, dy, static_cast<T>(0));
}
if (dweight) {
dev_ctx.template Alloc<T>(dweight);
}
auto blas = funcs::GetBlas<Context, T>(dev_ctx);
// Calculate the Output(X@GRAD) and Output(Y@GRAD).
if (dx || dy || dweight) {
Eigen::DSizes<int, 2> bcast_for_x(1, y_dim);
Eigen::DSizes<int, 2> bcast_for_y(1, x_dim);
Eigen::DSizes<int, 2> bcast_for_weight(1, x_dim);
for (int i = 0; i < out_dim; ++i) {
DenseTensor weight_i = weight.Slice(i, i + 1).Resize({x_dim, y_dim});
auto output_vec = dout_mat.chip(i, 1);
if (dx) {
y_scale_mat.device(place) =
output_vec.reshape(Eigen::DSizes<int, 2>(batch_size, 1))
.broadcast(bcast_for_x) *
y_mat;
blas.GEMM(CblasNoTrans,
CblasTrans,
batch_size,
x_dim,
y_dim,
1,
y_scale.data<T>(),
weight_i.data<T>(),
1,
dx->data<T>());
}
if (dy || dweight) {
auto output_vec_y =
output_vec.reshape(Eigen::DSizes<int, 2>(batch_size, 1))
.broadcast(bcast_for_y);
x_scale_mat.device(place) = output_vec_y * x_mat;
if (dy) {
blas.GEMM(CblasNoTrans,
CblasNoTrans,
batch_size,
y_dim,
x_dim,
1,
x_scale.data<T>(),
weight_i.data<T>(),
1,
dy->data<T>());
}
if (dweight) {
DenseTensor dweight_i =
dweight->Slice(i, i + 1).Resize({x_dim, y_dim});
blas.GEMM(CblasTrans,
CblasNoTrans,
x_dim,
y_dim,
batch_size,
1,
x_scale.data<T>(),
y.data<T>(),
0,
dweight_i.data<T>());
}
}
}
}
// calculate the gradient of Input(Bias).
if (dbias) {
dev_ctx.template Alloc<T>(dbias);
auto dbias_mat = EigenVector<T>::Flatten(*dbias);
dbias_mat.device(place) = dout_mat.sum(Eigen::DSizes<int, 1>(0));
}
}
} // namespace phi