Files
paddlepaddle--paddle/paddle/phi/kernels/gpu/sgd_kernel.cu
T
2026-07-13 12:40:42 +08:00

235 lines
8.6 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/sgd_kernel.h"
#include "paddle/phi/backends/gpu/gpu_context.h"
#include "paddle/phi/backends/gpu/gpu_helper.h"
#include "paddle/phi/backends/gpu/gpu_primitives.h"
#include "paddle/phi/common/amp_type_traits.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/mixed_vector.h"
namespace phi {
template <typename T, typename MT, typename GradT>
__global__ void SGDKernelMT(const T* param,
const GradT* grad,
const MT* learning_rate,
const int64_t num,
T* param_out,
const MT* master_param,
MT* master_param_out) {
MT lr = static_cast<MT>(learning_rate[0]);
CUDA_KERNEL_LOOP_TYPE(i, num, int64_t) {
MT p_data = master_param ? master_param[i] : static_cast<MT>(param[i]);
MT g_data = static_cast<MT>(grad[i]);
p_data = p_data - lr * g_data;
param_out[i] = static_cast<T>(p_data);
if (master_param_out) {
master_param_out[i] = p_data;
}
}
}
template <typename T, typename MT>
__global__ void SparseSGDFunctorKernel(const T* selected_rows,
const int64_t* rows,
const MT* learning_rate,
T* tensor_out,
int64_t row_numel,
int64_t limit) {
MT lr = learning_rate[0];
for (int64_t i = blockIdx.x; i < limit; i += gridDim.x) {
const T* selected_rows_ptr = selected_rows + i * row_numel;
T* tensor_out_ptr = tensor_out + rows[i] * row_numel;
for (int64_t index = threadIdx.x; index < row_numel; index += blockDim.x) {
// Since index in rows of SelectedRows can be duplicate, we have to use
// Atomic Operation to avoid concurrent write error.
CudaAtomicAdd(
tensor_out_ptr + index,
static_cast<T>(-lr * static_cast<MT>(selected_rows_ptr[index])));
}
}
}
template <typename T, typename Context>
void SGDDenseKernel(const Context& dev_ctx,
const DenseTensor& param,
const DenseTensor& learning_rate,
const DenseTensor& grad,
const optional<DenseTensor>& master_param,
bool multi_precision,
DenseTensor* param_out,
DenseTensor* master_param_out) {
using MT = typename MPTypeTrait<T>::Type;
// do check here
// if (multi_precision) {
// bool has_master =
// dev_ctx.HasInput("MasterParam") &&
// dev_ctx.HasOutput("MasterParamOut");
// }
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_out) : nullptr;
const bool use_float32_grad = grad.dtype() == DataType::FLOAT32;
int block = 512;
int64_t grid_max = dev_ctx.GetCUDAMaxGridDimSize()[0];
int grid = std::min((param.numel() + block - 1) / block, grid_max);
if (use_float32_grad) {
SGDKernelMT<T, MT, float><<<grid, block, 0, dev_ctx.stream()>>>(
param.data<T>(),
grad.data<float>(),
learning_rate.data<MT>(),
param.numel(),
dev_ctx.template Alloc<T>(param_out),
master_in_data,
master_out_data);
} else {
SGDKernelMT<T, MT, T><<<grid, block, 0, dev_ctx.stream()>>>(
param.data<T>(),
grad.data<T>(),
learning_rate.data<MT>(),
param.numel(),
dev_ctx.template Alloc<T>(param_out),
master_in_data,
master_out_data);
}
}
template <typename T, typename Context>
void SGDDenseParamSparseGradKernel(const Context& dev_ctx,
const DenseTensor& param,
const DenseTensor& learning_rate,
const SelectedRows& grad,
const optional<DenseTensor>& master_param,
bool multi_precision,
DenseTensor* param_out,
DenseTensor* master_param_out) {
using MT = typename MPTypeTrait<T>::Type;
// do some check here
// if (multi_precision) {
// bool has_master =
// dev_ctx.HasInput("MasterParam") &&
// dev_ctx.HasOutput("MasterParamOut");
// }
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_out) : nullptr;
PADDLE_ENFORCE_EQ(
param.IsSharedBufferWith(*param_out),
true,
common::errors::InvalidArgument(
"The input tensor Param of SgdOp should be equal with ParamOut "
"if variable's type is SelectedRows."));
auto in_height = grad.height();
auto out_dims = param_out->dims();
PADDLE_ENFORCE_EQ(in_height,
out_dims[0],
common::errors::InvalidArgument(
"The input tensor Grad's height of SgdOp should be "
"equal with ParamOut's dims. But received Grad's "
"height [%s] and ParamOut's dims [%s]",
in_height,
out_dims[0]));
auto& in_value = grad.value();
auto& in_rows = grad.rows();
int64_t in_row_numel = in_value.numel() / in_rows.size();
PADDLE_ENFORCE_EQ(in_row_numel,
param_out->numel() / in_height,
common::errors::InvalidArgument(
"The in_row_numel of SgdOp should be equal with "
"param_out's numel / in_height."));
auto* in_data = in_value.data<T>();
auto* out_data = param_out->data<T>();
const int kThreadsPerBlock = 256;
int thread_x = kThreadsPerBlock;
int max_threads = dev_ctx.GetMaxPhysicalThreadCount();
int max_blocks = std::max(max_threads / kThreadsPerBlock, 1);
MixVector<int64_t> mixv_in_rows(&in_rows);
SparseSGDFunctorKernel<T, MT><<<max_blocks, thread_x, 0, dev_ctx.stream()>>>(
in_data,
mixv_in_rows.CUDAData(dev_ctx.GetPlace()),
learning_rate.data<MT>(),
out_data,
in_row_numel,
in_rows.size());
}
template <typename T, typename Context>
void SGDSparseParamSparseGradKernel(const Context& dev_ctx,
const SelectedRows& param,
const DenseTensor& learning_rate,
const SelectedRows& grad,
const optional<SelectedRows>& master_param,
bool multi_precision,
SelectedRows* param_out,
SelectedRows* master_param_out) {
PADDLE_THROW("not impl");
}
} // namespace phi
#ifdef PADDLE_WITH_CUDA
PD_REGISTER_KERNEL(sgd,
GPU,
ALL_LAYOUT,
phi::SGDDenseKernel,
phi::float16,
phi::bfloat16,
float,
double) {
if (kernel_key.dtype() == phi::DataType::FLOAT16 ||
kernel_key.dtype() == phi::DataType::BFLOAT16) {
kernel->OutputAt(1).SetDataType(phi::DataType::FLOAT32);
}
}
#endif
#ifdef PADDLE_WITH_HIP
PD_REGISTER_KERNEL(
sgd, GPU, ALL_LAYOUT, phi::SGDDenseKernel, phi::float16, float, double) {
if (kernel_key.dtype() == phi::DataType::FLOAT16) {
kernel->OutputAt(1).SetDataType(phi::DataType::FLOAT32);
}
}
#endif
PD_REGISTER_KERNEL(sgd_dense_param_sparse_grad,
GPU,
ALL_LAYOUT,
phi::SGDDenseParamSparseGradKernel,
phi::float16,
float,
double) {}
PD_REGISTER_KERNEL(sgd_sparse_param_sparse_grad,
GPU,
ALL_LAYOUT,
phi::SGDSparseParamSparseGradKernel,
phi::float16,
float,
double) {}