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

319 lines
13 KiB
Plaintext

// Copyright (c) 2024 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/common/errors.h"
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/blas/blas.h"
#include "paddle/phi/kernels/funcs/broadcast_function.h"
#include "paddle/phi/kernels/funcs/elementwise_functor.h"
#include "paddle/phi/kernels/funcs/layer_norm_impl.cu.h"
#include "paddle/phi/kernels/fusion/gpu/fused_attention_utils.h"
#include "paddle/phi/kernels/fusion/gpu/fused_dropout_helper.h"
#include "paddle/phi/kernels/impl/matmul_grad_kernel_impl.h"
namespace phi {
namespace fusion {
template <typename T, typename Context>
void MatMul(const GPUContext& dev_ctx,
const DenseTensor& a,
const DenseTensor& b,
DenseTensor* c) {
auto blas = funcs::GetBlas<Context, T>(dev_ctx);
auto a_2d = phi::FoldInitDims(a);
auto b_2d = phi::FoldInitDims(b);
auto mat_dim_a = funcs::CreateMatrixDescriptor(a_2d.dims(), 0, false);
auto mat_dim_b = funcs::CreateMatrixDescriptor(b_2d.dims(), 0, false);
T alpha = static_cast<T>(1.0);
blas.MatMul(a, mat_dim_a, b, mat_dim_b, alpha, c, T(0));
}
template <typename T, typename Context>
void FFN(const GPUContext& dev_ctx,
const DenseTensor& x,
const DenseTensor& linear1_weight,
const DenseTensor* linear1_bias,
const DenseTensor& linear2_weight,
const DenseTensor* linear2_bias,
const DenseTensor* ln1_scale,
const DenseTensor* ln1_bias,
const DenseTensor* ln2_scale,
const DenseTensor* ln2_bias,
DenseTensor* out,
DenseTensor* dropout1_mask,
DenseTensor* dropout2_mask,
DenseTensor* ln1_mean,
DenseTensor* ln1_variance,
DenseTensor* ln2_mean,
DenseTensor* ln2_variance,
DenseTensor* linear1_out,
DenseTensor* ln1_out,
DenseTensor* dropout1_out,
DenseTensor* dropout2_out,
const int bsz_seq,
const int d_model,
const int dim_feedforward,
const std::string& act_method,
const bool pre_layer_norm,
const float epsilon1,
const float epsilon2,
const bool add_residual,
const int ring_id,
const fusion::DropoutParam& dropout_param1,
const fusion::DropoutParam& dropout_param2) {
fusion::FusedDropoutLayerNormHelper<T, uint8_t> pre_layernorm_helper(
bsz_seq, d_model, epsilon1);
fusion::FusedDropoutHelper<T, uint8_t> fused_act_dropout_helper(
dev_ctx, bsz_seq, dim_feedforward, dropout_param1);
fusion::FusedDropoutLayerNormHelper<T, uint8_t>
fused_dropout_layernorm_helper(
dev_ctx, bsz_seq, d_model, dropout_param2, epsilon2);
using U = funcs::LayerNormParamType<T>;
const DenseTensor* in = &x;
const U* ln1_scale_ptr =
ln1_scale == nullptr ? nullptr : ln1_scale->data<U>();
const U* ln1_bias_ptr = ln1_bias == nullptr ? nullptr : ln1_bias->data<U>();
const U* ln2_scale_ptr =
ln2_scale == nullptr ? nullptr : ln2_scale->data<U>();
const U* ln2_bias_ptr = ln2_bias == nullptr ? nullptr : ln2_bias->data<U>();
const T* linear1_bias_ptr =
linear1_bias == nullptr ? nullptr : linear1_bias->data<T>();
const T* linear2_bias_ptr =
linear2_bias == nullptr ? nullptr : linear2_bias->data<T>();
if (pre_layer_norm) {
pre_layernorm_helper.LayerNorm(dev_ctx,
x.data<T>(),
ln1_scale_ptr,
ln1_bias_ptr,
ln1_out->data<T>(),
ln1_mean->data<U>(),
ln1_variance->data<U>());
in = ln1_out;
}
MatMul<T, Context>(dev_ctx, *in, linear1_weight, linear1_out);
fused_act_dropout_helper.DropoutActBias(dev_ctx,
linear1_out->data<T>(),
linear1_bias_ptr,
act_method,
dropout1_out->data<T>(),
dropout1_mask->data<uint8_t>());
DenseTensor linear2_out;
linear2_out.Resize({bsz_seq, d_model});
dev_ctx.template Alloc<T>(&linear2_out, linear2_out.numel() * sizeof(T));
MatMul<T, Context>(dev_ctx, *dropout1_out, linear2_weight, &linear2_out);
// tensor model parallel
phi::fusion::AllReduce<T>(linear2_out, ring_id, dev_ctx);
const T* residual_ptr = add_residual ? x.data<T>() : nullptr;
if (!pre_layer_norm) {
// TODO(Xreki): support post layer_norm case when add_residual is false.
PADDLE_ENFORCE_EQ(add_residual,
true,
common::errors::InvalidArgument(
"Attribute add_residual is expected to be true "
"when pre_layer_norm is false."));
fused_dropout_layernorm_helper.LayernormResidualDropoutBias(
dev_ctx,
linear2_out.data<T>(),
residual_ptr,
linear2_bias_ptr,
ln2_scale_ptr,
ln2_bias_ptr,
dropout2_out->data<T>(),
dropout2_mask->data<uint8_t>(),
out->data<T>(),
ln2_mean->data<U>(),
ln2_variance->data<U>());
} else {
fused_dropout_layernorm_helper.ResidualDropoutBias(
dev_ctx,
linear2_out.data<T>(),
residual_ptr,
linear2_bias_ptr,
out->data<T>(),
dropout2_mask->data<uint8_t>());
}
}
template <typename T, typename Context>
void FusedFeedForwardKernel(const Context& dev_ctx,
const DenseTensor& x,
const optional<DenseTensor>& dropout1_seed,
const optional<DenseTensor>& dropout2_seed,
const DenseTensor& linear1_weight,
const optional<DenseTensor>& linear1_bias,
const DenseTensor& linear2_weight,
const optional<DenseTensor>& linear2_bias,
const optional<DenseTensor>& ln1_scale,
const optional<DenseTensor>& ln1_bias,
const optional<DenseTensor>& ln2_scale,
const optional<DenseTensor>& ln2_bias,
bool pre_layer_norm,
float ln1_epsilon,
float ln2_epsilon,
const std::string& act_method,
float dropout1_prob,
float dropout2_prob,
const std::string& dropout1_implementation,
const std::string& dropout2_implementation,
bool is_test,
bool dropout1_fix_seed,
bool dropout2_fix_seed,
int dropout1_seed_val,
int dropout2_seed_val,
bool add_residual,
int ring_id,
DenseTensor* out,
DenseTensor* dropout1_mask,
DenseTensor* dropout2_mask,
DenseTensor* ln1_mean,
DenseTensor* ln1_variance,
DenseTensor* ln2_mean,
DenseTensor* ln2_variance,
DenseTensor* linear1_out,
DenseTensor* ln1_out,
DenseTensor* dropout1_out,
DenseTensor* dropout2_out) {
auto* x_ptr = &x;
auto* linear1_weight_ptr = &linear1_weight;
auto* linear1_bias_ptr = linear1_bias.get_ptr();
auto* linear2_weight_ptr = &linear2_weight;
auto* linear2_bias_ptr = linear2_bias.get_ptr();
auto* ln1_scale_ptr = pre_layer_norm ? ln1_scale.get_ptr() : nullptr;
auto* ln1_bias_ptr = pre_layer_norm ? ln1_bias.get_ptr() : nullptr;
auto* ln2_scale_ptr = !pre_layer_norm ? ln2_scale.get_ptr() : nullptr;
auto* ln2_bias_ptr = !pre_layer_norm ? ln2_bias.get_ptr() : nullptr;
if (!pre_layer_norm) {
ln1_mean = nullptr;
ln1_variance = nullptr;
ln1_out = nullptr;
} else {
ln2_mean = nullptr;
ln2_variance = nullptr;
}
bool is_upscale_in_train1 = dropout1_implementation == "upscale_in_train";
bool is_upscale_in_train2 = dropout2_implementation == "upscale_in_train";
auto* dropout1_seed_ptr = dropout1_seed.get_ptr();
auto* dropout2_seed_ptr = dropout2_seed.get_ptr();
fusion::DropoutParam dropout_param1(dropout1_fix_seed,
0,
is_test,
is_upscale_in_train1,
dropout1_prob,
dropout1_seed_ptr,
dropout1_seed_val);
fusion::DropoutParam dropout_param2(dropout2_fix_seed,
0,
is_test,
is_upscale_in_train2,
dropout2_prob,
dropout2_seed_ptr,
dropout2_seed_val);
using U = funcs::LayerNormParamType<T>;
dev_ctx.template Alloc<T>(out, out->numel() * sizeof(T));
dev_ctx.template Alloc<uint8_t>(dropout1_mask,
dropout1_mask->numel() * sizeof(uint8_t));
dev_ctx.template Alloc<uint8_t>(dropout2_mask,
dropout2_mask->numel() * sizeof(uint8_t));
if (pre_layer_norm) {
dev_ctx.template Alloc<U>(ln1_mean, ln1_mean->numel() * sizeof(U));
dev_ctx.template Alloc<U>(ln1_variance, ln1_variance->numel() * sizeof(U));
dev_ctx.template Alloc<T>(ln1_out, ln1_out->numel() * sizeof(T));
} else {
dev_ctx.template Alloc<U>(ln2_mean, ln2_mean->numel() * sizeof(U));
dev_ctx.template Alloc<U>(ln2_variance, ln2_variance->numel() * sizeof(U));
}
dev_ctx.template Alloc<T>(linear1_out, linear1_out->numel() * sizeof(T));
dev_ctx.template Alloc<T>(dropout1_out, dropout1_out->numel() * sizeof(T));
dev_ctx.template Alloc<T>(dropout2_out, dropout2_out->numel() * sizeof(T));
if (out->numel() == 0) {
return;
}
auto x_dim = x_ptr->dims();
auto mat_dim_x =
funcs::CreateMatrixDescriptor(phi::RowMatrixFromVector(x_dim), 0, false);
auto dim = linear1_weight_ptr->dims();
int d_model = dim[0];
int dim_feedforward = dim[dim.size() - 1];
int bsz_seq = mat_dim_x.batch_size_ * mat_dim_x.height_;
phi::fusion::FFN<T, Context>(dev_ctx,
x,
linear1_weight,
linear1_bias_ptr,
linear2_weight,
linear2_bias_ptr,
ln1_scale_ptr,
ln1_bias_ptr,
ln2_scale_ptr,
ln2_bias_ptr,
out,
dropout1_mask,
dropout2_mask,
ln1_mean,
ln1_variance,
ln2_mean,
ln2_variance,
linear1_out,
ln1_out,
dropout1_out,
dropout2_out,
bsz_seq,
d_model,
dim_feedforward,
act_method,
pre_layer_norm,
ln1_epsilon,
ln2_epsilon,
add_residual,
ring_id,
dropout_param1,
dropout_param2);
}
} // namespace fusion
} // namespace phi
PD_REGISTER_KERNEL(fused_feedforward,
GPU,
ALL_LAYOUT,
phi::fusion::FusedFeedForwardKernel,
float,
double,
phi::float16) {
kernel->OutputAt(1).SetDataType(phi::DataType::UINT8);
kernel->OutputAt(2).SetDataType(phi::DataType::UINT8);
if (kernel_key.dtype() == phi::DataType::FLOAT16) {
kernel->OutputAt(3).SetDataType(phi::DataType::FLOAT32);
kernel->OutputAt(4).SetDataType(phi::DataType::FLOAT32);
kernel->OutputAt(5).SetDataType(phi::DataType::FLOAT32);
kernel->OutputAt(6).SetDataType(phi::DataType::FLOAT32);
}
}