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

227 lines
8.4 KiB
Plaintext

// 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.
#include "paddle/phi/kernels/adagrad_kernel.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/backends/gpu/gpu_launch_config.h"
#include "paddle/phi/backends/gpu/gpu_primitives.h"
#include "paddle/phi/common/amp_type_traits.h"
#include "paddle/phi/core/dense_tensor.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/selected_rows_functor.h"
#include "paddle/phi/kernels/impl/adagrad_kernel_impl.h"
namespace phi {
template <typename T, typename MT>
__global__ void AdagradGPUKernel(const T* param,
const T* grad,
const MT* moment,
const MT* lr,
const MT* master_param,
MT epsilon,
T* param_out,
MT* moment_out,
MT* master_param_out,
int64_t num) {
int64_t idx =
static_cast<int64_t>(blockDim.x) * static_cast<int64_t>(blockIdx.x) +
static_cast<int64_t>(threadIdx.x);
MT lr_data = static_cast<MT>(lr[0]);
for (int64_t i = idx; i < num; i += blockDim.x * gridDim.x) {
MT grad_data = static_cast<MT>(grad[i]);
MT moment_out_data = static_cast<MT>(moment[i]) + grad_data * grad_data;
moment_out[i] = static_cast<MT>(moment_out_data);
auto in = master_param_out ? master_param[i] : static_cast<MT>(param[i]);
MT param_out_data =
in - (lr_data * grad_data) / (sqrt(moment_out_data) + epsilon);
param_out[i] = static_cast<T>(param_out_data);
if (master_param_out) {
master_param_out[i] = param_out_data;
}
}
}
template <typename T>
struct DenseAdagradFunctor<GPUContext, T> {
void operator()(const GPUContext& dev_ctx,
const DenseTensor& param_t,
const DenseTensor& grad_t,
const DenseTensor& moment_t,
const DenseTensor& learning_rate,
const optional<DenseTensor>& master_param,
float epsilon_t,
bool multi_precision,
DenseTensor* param_out_tensor,
DenseTensor* moment_out_tensor,
DenseTensor* master_param_outs) {
using MT = typename dtype::template MPTypeTrait<T>::Type;
T* param_out_data = dev_ctx.template Alloc<T>(param_out_tensor);
MT* moment_out_data = dev_ctx.template Alloc<MT>(moment_out_tensor);
const MT* master_in_data =
multi_precision ? master_param->data<MT>() : nullptr;
MT* master_out_data = multi_precision
? dev_ctx.template Alloc<MT>(master_param_outs)
: nullptr;
MT epsilon = static_cast<MT>(epsilon_t);
int64_t numel = param_t.numel();
auto config = backends::gpu::GetGpuLaunchConfig1D(dev_ctx, numel, 1);
int grid = config.block_per_grid.x;
int block = config.thread_per_block.x;
auto stream = dev_ctx.stream();
AdagradGPUKernel<T, MT>
<<<block, grid, 0, stream>>>(param_t.data<T>(),
grad_t.data<T>(),
moment_t.data<MT>(),
learning_rate.data<MT>(),
master_in_data,
epsilon,
param_out_data,
moment_out_data,
master_out_data,
numel);
}
};
template <typename T, int block_size>
__global__ void MergeGradKernel(const T* grad,
const int64_t* grad_rows,
T* grad_merge,
const int64_t* grad_merge_rows,
size_t grad_merge_rows_size,
int64_t row_numel) {
const int ty = blockIdx.y;
int tid = threadIdx.x;
__shared__ size_t grad_merge_idx;
if (tid == 0) {
for (size_t i = 0; i < grad_merge_rows_size; i++) {
if (grad_rows[ty] == grad_merge_rows[i]) {
grad_merge_idx = i;
}
}
}
__syncthreads();
grad += ty * row_numel;
grad_merge += grad_merge_idx * row_numel;
for (int64_t index = tid; index < row_numel; index += block_size) {
CudaAtomicAdd(grad_merge + index, grad[index]);
}
}
template <typename T, int block_size>
__global__ void SparseAdagradFunctorKernel(const T* grad,
const int64_t* rows,
const T* learning_rate,
T* param,
T* moment,
int64_t row_numel,
T epsilon) {
const int ty = blockIdx.y;
int tid = threadIdx.x;
grad += ty * row_numel;
param += rows[ty] * row_numel;
moment += rows[ty] * row_numel;
for (int64_t index = tid; index < row_numel; index += block_size) {
// Since index in rows of SelectedRows can be duplicate, we have to use
// Atomic Operation to avoid concurrent write error.
CudaAtomicAdd(param + index,
-1.0 * learning_rate[0] * grad[index] /
(sqrt(moment[index]) + epsilon));
}
}
template <typename T>
struct SparseAdagradFunctor<GPUContext, T> {
void operator()(const GPUContext& dev_ctx,
const SelectedRows& grad,
const DenseTensor& learning_rate,
T epsilon,
DenseTensor* moment,
DenseTensor* param) {
// 1. g_m.rows = set(g.rows)
auto grad_width = grad.value().dims()[1];
funcs::scatter::MergeAdd<GPUContext, T> merge_func;
auto grad_merge = merge_func(dev_ctx, grad);
auto* grad_merge_data = grad_merge.mutable_value()->template data<T>();
Vector<int64_t> merge_rows(grad_merge.rows());
// 2. m += g_m * g_m
auto grad_square = SquareSelectedRows<GPUContext, T>(dev_ctx, grad_merge);
funcs::SelectedRowsAddToTensor<GPUContext, T> functor;
functor(dev_ctx, grad_square, moment);
// 3. update parameter
auto* lr = learning_rate.data<T>();
auto* param_data = param->data<T>();
auto* moment_data = moment->data<T>();
const int block_size = 256;
dim3 threads(block_size, 1);
dim3 grid2(1, merge_rows.size());
MixVector<int64_t> mixv_merge_rows(&merge_rows);
SparseAdagradFunctorKernel<T, 256>
<<<grid2,
threads,
0,
reinterpret_cast<const GPUContext&>(dev_ctx).stream()>>>(
grad_merge_data,
mixv_merge_rows.CUDAMutableData(dev_ctx.GetPlace()),
lr,
param_data,
moment_data,
grad_width,
epsilon);
mixv_merge_rows.CopyToCPU();
}
};
template struct SparseAdagradFunctor<GPUContext, float>;
template struct SparseAdagradFunctor<GPUContext, double>;
template struct DenseAdagradFunctor<GPUContext, float>;
template struct DenseAdagradFunctor<GPUContext, double>;
template struct DenseAdagradFunctor<GPUContext, float16>;
} // namespace phi
PD_REGISTER_KERNEL(adagrad,
GPU,
ALL_LAYOUT,
phi::AdagradDenseKernel,
float,
double,
phi::float16) {
if (kernel_key.dtype() == phi::DataType::FLOAT16) {
kernel->OutputAt(1).SetDataType(phi::DataType::FLOAT32);
kernel->OutputAt(2).SetDataType(phi::DataType::FLOAT32);
}
}
PD_REGISTER_KERNEL(adagrad_dense_param_sparse_grad,
GPU,
ALL_LAYOUT,
phi::AdagradSparseKernel,
float,
double) {}