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

791 lines
22 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 <math.h> // for sqrt in CPU and CUDA
#include <Eigen/Dense>
#include "paddle/phi/kernels/funcs/algorithm.h"
#ifdef PADDLE_WITH_XPU
#include "paddle/phi/backends/xpu/enforce_xpu.h"
#include "paddle/phi/backends/xpu/xpu_header.h"
#include "paddle/phi/common/memory_utils.h"
#endif
namespace phi {
namespace funcs {
#ifdef PADDLE_WITH_XPU
template <typename Context, typename T1, typename T2>
static int ConvertDataByType(const T1* x,
T2** y,
int64_t len,
bool allocateFlag,
const Context& dev_ctx,
xpu::ctx_guard* ctx_guard) {
if (nullptr == x || nullptr == y || len <= 0)
return xpu::Error_t::INVALID_PARAM;
if (allocateFlag) {
*y = ctx_guard->alloc_l3_or_gm<T2>(len);
PADDLE_ENFORCE_XDNN_NOT_NULL(*y);
}
T1* cpu_data = reinterpret_cast<T1*>(malloc(sizeof(T1) * len));
memory_utils::Copy(
CPUPlace(), cpu_data, dev_ctx.GetPlace(), x, len * sizeof(T1));
T2* cpu_real_data = reinterpret_cast<T2*>(malloc(sizeof(T2) * len));
for (int i = 0; i < len; i++) cpu_real_data[i] = static_cast<T2>(cpu_data[i]);
memory_utils::Copy(
dev_ctx.GetPlace(), *y, CPUPlace(), cpu_real_data, len * sizeof(T2));
free(cpu_data);
free(cpu_real_data);
return xpu::Error_t::SUCCESS;
}
template <typename Context, typename T>
static void GetDataPointer(const DenseTensor& tensorData,
T** result,
const Context& dev_ctx,
xpu::ctx_guard* ctx_guard) {
if (tensorData.dtype() == DataType::FLOAT16) {
const float16* real_data = tensorData.template data<float16>();
int64_t len = tensorData.numel();
int r = ConvertDataByType<Context, float16, T>(
real_data, result, len, true, dev_ctx, ctx_guard);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "adam");
}
}
template <typename Context, typename T>
static void GetOutDataPointer(DenseTensor* tensorData,
DenseTensor* out,
T** result,
const Context& dev_ctx) {
if (tensorData->dtype() == DataType::FLOAT16) {
*result = dev_ctx.template Alloc<T>(out);
} else {
*result = dev_ctx.template Alloc<T>(tensorData);
}
}
template <typename Context, typename T>
static void CopyOutData(const DenseTensor& srcTensor,
DenseTensor* dstTensor,
const Context& dev_ctx,
xpu::ctx_guard* ctx_guard) {
if (dstTensor->dtype() == DataType::FLOAT16) {
const T* xpu_out_data = srcTensor.template data<T>();
float16* out_data = dev_ctx.template Alloc<float16>(dstTensor);
int64_t len = srcTensor.numel();
int r = ConvertDataByType<Context, T, float16>(
xpu_out_data, &out_data, len, false, dev_ctx, ctx_guard);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "adam");
}
}
template <typename Context, typename T>
static void SetBetaData(const DenseTensor& beta_pow,
DenseTensor* beta_pow_out,
const T& beta,
const Context& dev_ctx) {
if (beta_pow.dtype() == DataType::FLOAT16) {
const float16* beta_pow_p = beta_pow.template data<float16>();
dev_ctx.template HostAlloc<float16>(beta_pow_out)[0] =
static_cast<float16>(beta) * beta_pow_p[0];
} else {
const T* beta_pow_p = beta_pow.template data<T>();
dev_ctx.template HostAlloc<T>(beta_pow_out)[0] = beta * beta_pow_p[0];
}
}
template <typename Context, typename T>
static void Scale(DenseTensor* beta_pow_out,
const DenseTensor& beta_pow,
T* beta_pow_ptr,
const T& beta,
const Context& dev_ctx,
xpu::ctx_guard* ctx_guard) {
float16* beta_pow_out_p2 = dev_ctx.template Alloc<float16>(beta_pow_out);
DenseTensor xpu_beta_pow_out;
const DenseTensorMeta meta_beta_pow_out(DataType::FLOAT32,
beta_pow_out->dims());
xpu_beta_pow_out.set_meta(meta_beta_pow_out);
T* beta_pow_out_ptr = dev_ctx.template Alloc<T>(&xpu_beta_pow_out);
int r = xpu::scale(dev_ctx.x_context(),
beta_pow_ptr,
beta_pow_out_ptr,
beta_pow.numel(),
false,
beta,
0.0f);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "adam");
const float* xpu_beta_pow_out_data =
dev_ctx.template Alloc<T>(&xpu_beta_pow_out);
int64_t len = xpu_beta_pow_out.numel();
r = ConvertDataByType<Context, T, float16>(
xpu_beta_pow_out_data, &beta_pow_out_p2, len, false, dev_ctx, ctx_guard);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "adam");
}
#endif
struct GPUAdam;
struct CPUAdam;
template <typename T, typename Flavour>
class AdamFunctor;
template <typename T>
class AdamFunctor<T, GPUAdam> {
private:
T beta1_;
T beta2_;
T epsilon_;
const T* beta1_pow_;
const T* beta2_pow_;
const T* moment1_;
T* moment1_out_;
const T* moment2_;
T* moment2_out_;
const T* moment2_max_;
T* moment2_max_out_;
const T* lr_;
const T* grad_;
const T* param_;
T* param_out_;
bool amsgrad_;
public:
AdamFunctor(T beta1,
T beta2,
T epsilon,
const T* beta1_pow,
const T* beta2_pow,
const T* mom1,
T* mom1_out,
const T* mom2,
T* mom2_out,
const T* mom2_max,
T* mom2_max_out,
const T* lr,
const T* grad,
const T* param,
T* param_out,
bool amsgrad)
: beta1_(beta1),
beta2_(beta2),
epsilon_(epsilon),
beta1_pow_(beta1_pow),
beta2_pow_(beta2_pow),
moment1_(mom1),
moment1_out_(mom1_out),
moment2_(mom2),
moment2_out_(mom2_out),
moment2_max_(mom2_max),
moment2_max_out_(mom2_max_out),
lr_(lr),
grad_(grad),
param_(param),
param_out_(param_out),
amsgrad_(amsgrad) {}
inline HOSTDEVICE void operator()(size_t i) const {
// Merge all memory access together.
T g = grad_[i];
T mom1 = moment1_[i];
T mom2 = moment2_[i];
T lr = *lr_;
T beta1_pow = *beta1_pow_;
T beta2_pow = *beta2_pow_;
T p = param_[i];
// Calculation
lr *= sqrt(1 - beta2_pow) / (1 - beta1_pow);
mom1 = beta1_ * mom1 + (1 - beta1_) * g;
mom2 = beta2_ * mom2 + (1 - beta2_) * g * g;
if (amsgrad_) {
T mom2_max_ = std::max(mom2, moment2_max_[i]);
p -= lr * (mom1 / (sqrt(mom2_max_) + epsilon_ * sqrt(1 - beta2_pow)));
// Write back to global memory
moment2_max_out_[i] = mom2_max_;
} else {
p -= lr * (mom1 / (sqrt(mom2) + epsilon_ * sqrt(1 - beta2_pow)));
}
// Write back to global memory
moment1_out_[i] = mom1;
moment2_out_[i] = mom2;
param_out_[i] = p;
}
};
template <typename T>
class AdamFunctor<T, CPUAdam> {
private:
T beta1_;
T beta2_;
T epsilon_;
const T* beta1_pow_;
const T* beta2_pow_;
const T* moment1_;
T* moment1_out_;
const T* moment2_;
T* moment2_out_;
const T* moment2_max_;
T* moment2_max_out_;
const T* lr_;
const T* grad_;
const T* param_;
T* param_out_;
bool amsgrad_;
public:
AdamFunctor(T beta1,
T beta2,
T epsilon,
const T* beta1_pow,
const T* beta2_pow,
const T* mom1,
T* mom1_out,
const T* mom2,
T* mom2_out,
const T* mom2_max,
T* mom2_max_out,
const T* lr,
const T* grad,
const T* param,
T* param_out,
bool amsgrad)
: beta1_(beta1),
beta2_(beta2),
epsilon_(epsilon),
beta1_pow_(beta1_pow),
beta2_pow_(beta2_pow),
moment1_(mom1),
moment1_out_(mom1_out),
moment2_(mom2),
moment2_out_(mom2_out),
moment2_max_(mom2_max),
moment2_max_out_(mom2_max_out),
lr_(lr),
grad_(grad),
param_(param),
param_out_(param_out),
amsgrad_(amsgrad) {}
void operator()(size_t numel) const {
Eigen::Map<const Eigen::Array<T, 1, Eigen::Dynamic>> g{
grad_, static_cast<Eigen::Index>(numel)};
Eigen::Map<const Eigen::Array<T, 1, Eigen::Dynamic>> mom1{
moment1_, static_cast<Eigen::Index>(numel)};
Eigen::Map<const Eigen::Array<T, 1, Eigen::Dynamic>> mom2{
moment2_, static_cast<Eigen::Index>(numel)};
Eigen::Map<const Eigen::Array<T, 1, Eigen::Dynamic>> param{
param_, static_cast<Eigen::Index>(numel)};
Eigen::Map<Eigen::Array<T, 1, Eigen::Dynamic>> param_out{
param_out_, static_cast<Eigen::Index>(numel)};
Eigen::Map<Eigen::Array<T, 1, Eigen::Dynamic>> moment1_out{
moment1_out_, static_cast<Eigen::Index>(numel)};
Eigen::Map<Eigen::Array<T, 1, Eigen::Dynamic>> moment2_out{
moment2_out_, static_cast<Eigen::Index>(numel)};
T lr = *lr_;
T beta1_pow = *beta1_pow_;
T beta2_pow = *beta2_pow_;
// Calculation
lr *= sqrt(1 - beta2_pow) / (1 - beta1_pow);
moment1_out = beta1_ * mom1 + (1 - beta1_) * g;
moment2_out = beta2_ * mom2 + (1 - beta2_) * g * g;
if (amsgrad_) {
Eigen::Map<const Eigen::Array<T, 1, Eigen::Dynamic>> mom2_max{
moment2_max_, static_cast<Eigen::Index>(numel)};
Eigen::Map<Eigen::Array<T, 1, Eigen::Dynamic>> moment2_max_out{
moment2_max_out_, static_cast<Eigen::Index>(numel)};
moment2_max_out = moment2_out.cwiseMax(mom2_max);
param_out = param - lr * (moment1_out / (moment2_max_out.sqrt() +
epsilon_ * sqrt(1 - beta2_pow)));
} else {
param_out = param - lr * (moment1_out / (moment2_out.sqrt() +
epsilon_ * sqrt(1 - beta2_pow)));
}
}
};
template <typename T, typename Flavour, typename MT = T>
class SparseAdamFunctor;
template <typename T, typename MT>
class SparseAdamFunctor<T, GPUAdam, MT> {
private:
MT beta1_;
MT beta2_;
MT epsilon_;
const MT* beta1_pow_;
const MT* beta2_pow_;
const MT* moment1_;
MT* moment1_out_;
const MT* moment2_;
MT* moment2_out_;
const MT* moment2_max_;
MT* moment2_max_out_;
const double* lr_;
const T* grad_;
const T* param_;
T* param_out_;
const MT* master_param_;
MT* master_param_out_;
const int64_t* rows_;
int64_t row_numel_;
int64_t row_count_;
bool lazy_mode_;
bool amsgrad_;
public:
SparseAdamFunctor(MT beta1,
MT beta2,
MT epsilon,
const MT* beta1_pow,
const MT* beta2_pow,
const MT* mom1,
MT* mom1_out,
const MT* mom2,
MT* mom2_out,
const MT* mom2_max,
MT* mom2_max_out,
const double* lr,
const T* grad,
const T* param,
T* param_out,
const MT* master_param,
MT* master_param_out,
const int64_t* rows,
int64_t row_numel,
int64_t row_count,
bool lazy_mode,
bool amsgrad)
: beta1_(beta1),
beta2_(beta2),
epsilon_(epsilon),
beta1_pow_(beta1_pow),
beta2_pow_(beta2_pow),
moment1_(mom1),
moment1_out_(mom1_out),
moment2_(mom2),
moment2_out_(mom2_out),
moment2_max_(mom2_max),
moment2_max_out_(mom2_max_out),
lr_(lr),
grad_(grad),
param_(param),
param_out_(param_out),
master_param_(master_param),
master_param_out_(master_param_out),
rows_(rows),
row_numel_(row_numel),
row_count_(row_count),
lazy_mode_(lazy_mode),
amsgrad_(amsgrad) {}
inline HOSTDEVICE void adam_update(size_t i, MT g) const {
// The following code is the same as dense
MT mom1 = moment1_[i];
MT mom2 = moment2_[i];
MT lr = static_cast<MT>(*lr_);
MT beta1_pow = *beta1_pow_;
MT beta2_pow = *beta2_pow_;
MT p = master_param_ ? master_param_[i] : static_cast<MT>(param_[i]);
// Calculation
lr *= sqrt(static_cast<MT>(1.0) - beta2_pow) /
(static_cast<MT>(1.0) - beta1_pow);
mom1 = beta1_ * mom1 + (static_cast<MT>(1.0) - beta1_) * g;
mom2 = beta2_ * mom2 + (static_cast<MT>(1.0) - beta2_) * g * g;
if (amsgrad_) {
MT mom2_max_ = std::max(mom2, moment2_max_[i]);
p -= lr * (mom1 / (sqrt(mom2_max_) +
epsilon_ * sqrt(static_cast<MT>(1.0) - beta2_pow)));
// Write back to global memory
moment2_max_out_[i] = mom2_max_;
} else {
p -= lr * (mom1 / (sqrt(mom2) +
epsilon_ * sqrt(static_cast<MT>(1.0) - beta2_pow)));
}
// Write back to global memory
moment1_out_[i] = mom1;
moment2_out_[i] = mom2;
param_out_[i] = static_cast<T>(p);
if (master_param_out_) {
master_param_out_[i] = p;
}
}
inline HOSTDEVICE void operator()(size_t i) const {
auto row_idx =
funcs::BinarySearch<int64_t>(rows_, row_count_, i / row_numel_);
if (lazy_mode_ && row_idx < 0) {
return;
} else {
MT g = row_idx >= 0
? static_cast<MT>(grad_[row_idx * row_numel_ + i % row_numel_])
: static_cast<MT>(0);
adam_update(i, g);
}
}
};
template <typename T>
class SparseAdamFunctor<T, CPUAdam, T> {
private:
T beta1_;
T beta2_;
T epsilon_;
const T* beta1_pow_;
const T* beta2_pow_;
const T* moment1_;
T* moment1_out_;
const T* moment2_;
T* moment2_out_;
const T* moment2_max_;
T* moment2_max_out_;
const T* lr_;
const T* grad_;
const T* param_;
T* param_out_;
const int64_t* rows_;
int64_t row_numel_;
int64_t row_count_;
bool amsgrad_;
public:
SparseAdamFunctor(T beta1,
T beta2,
T epsilon,
const T* beta1_pow,
const T* beta2_pow,
const T* mom1,
T* mom1_out,
const T* mom2,
T* mom2_out,
const T* mom2_max,
T* mom2_max_out,
const T* lr,
const T* grad,
const T* param,
T* param_out,
const int64_t* rows,
int64_t row_numel,
int64_t row_count,
bool lazy_mode UNUSED,
bool amsgrad)
: beta1_(beta1),
beta2_(beta2),
epsilon_(epsilon),
beta1_pow_(beta1_pow),
beta2_pow_(beta2_pow),
moment1_(mom1),
moment1_out_(mom1_out),
moment2_(mom2),
moment2_out_(mom2_out),
moment2_max_(mom2_max),
moment2_max_out_(mom2_max_out),
lr_(lr),
grad_(grad),
param_(param),
param_out_(param_out),
rows_(rows),
row_numel_(row_numel),
row_count_(row_count),
amsgrad_(amsgrad) {}
inline HOSTDEVICE void adam_update(size_t i, T g) const {
// The following code is the same as dense
T mom1 = moment1_[i];
T mom2 = moment2_[i];
T lr = *lr_;
T beta1_pow = *beta1_pow_;
T beta2_pow = *beta2_pow_;
T p = param_[i];
// Calculation
lr *= sqrt(1 - beta2_pow) / (1 - beta1_pow);
mom1 = beta1_ * mom1 + (1 - beta1_) * g;
mom2 = beta2_ * mom2 + (1 - beta2_) * g * g;
if (amsgrad_) {
T mom2_max_ = std::max(mom2, moment2_max_[i]);
p -= lr * (mom1 / (sqrt(mom2_max_) + epsilon_ * sqrt(1 - beta2_pow)));
// Write back to global memory
moment2_max_out_[i] = mom2_max_;
} else {
p -= lr * (mom1 / (sqrt(mom2) + epsilon_ * sqrt(1 - beta2_pow)));
}
// Write back to global memory
moment1_out_[i] = mom1;
moment2_out_[i] = mom2;
param_out_[i] = p;
}
inline void operator()(size_t numel) const {
// lr could be reuse
T lr = *lr_;
T beta1_pow = *beta1_pow_;
T beta2_pow = *beta2_pow_;
lr *= sqrt(1 - beta2_pow) / (1 - beta1_pow);
int64_t row_count = static_cast<int64_t>(numel / row_numel_);
for (int64_t i = 0, j = 0; i != row_count; ++i) {
if (i == *(rows_ + j)) {
for (int64_t k = 0; k != row_numel_; ++k) {
T g = grad_[j * row_numel_ + k];
adam_update(i * row_numel_ + k, g);
}
++j;
} else {
for (int64_t k = 0; k != row_numel_; ++k) {
T mom1 = moment1_[i * row_numel_ + k];
T mom2 = moment2_[i * row_numel_ + k];
T p = param_[i * row_numel_ + k];
mom1 = beta1_ * mom1;
mom2 = beta2_ * mom2;
if (amsgrad_) {
T mom2_max = moment2_max_[i * row_numel_ + k];
T mom2_max_ = std::max(mom2, mom2_max);
p -= lr * (mom1 / (sqrt(mom2_max_) + epsilon_));
// Write back to global memory
moment2_max_out_[i * row_numel_ + k] = mom2_max_;
} else {
p -= lr * (mom1 / (sqrt(mom2) + epsilon_));
}
// Write back to global memory
moment1_out_[i * row_numel_ + k] = mom1;
moment2_out_[i * row_numel_ + k] = mom2;
param_out_[i * row_numel_ + k] = p;
}
}
}
}
};
struct GPUAdamW;
struct CPUAdamW;
template <typename T, typename Flavour>
class AdamWFunctor;
template <typename T>
class AdamWFunctor<T, CPUAdamW> {
private:
const T coeff_;
const T lr_ratio_;
const T* lr_;
T* param_;
public:
AdamWFunctor(const T coeff, const T lr_ratio, const T* lr, T* param)
: coeff_(coeff), lr_ratio_(lr_ratio), lr_(lr), param_(param) {}
inline HOSTDEVICE void operator()(size_t numel) const {
Eigen::Map<Eigen::Array<T, 1, Eigen::Dynamic>> param{
param_, static_cast<Eigen::Index>(numel)};
T lr = *lr_;
// Calculation
param -= lr * lr_ratio_ * coeff_ * param;
}
};
template <typename T, typename Flavour, typename MT = T>
class SparseAdamWFunctor;
template <typename T, typename MT>
class SparseAdamWFunctor<T, GPUAdamW, MT> {
private:
MT beta1_;
MT beta2_;
MT epsilon_;
MT coeff_;
MT lr_ratio_;
const MT* beta1_pow_;
const MT* beta2_pow_;
const MT* moment1_;
MT* moment1_out_;
const MT* moment2_;
MT* moment2_out_;
const MT* moment2_max_;
MT* moment2_max_out_;
const MT* lr_;
const T* grad_;
const T* param_;
T* param_out_;
const MT* master_param_;
MT* master_param_out_;
const int64_t* rows_;
int64_t row_numel_;
int64_t row_count_;
bool lazy_mode_;
bool amsgrad_;
public:
SparseAdamWFunctor(MT beta1,
MT beta2,
MT epsilon,
MT coeff,
MT lr_ratio,
const MT* beta1_pow,
const MT* beta2_pow,
const MT* mom1,
MT* mom1_out,
const MT* mom2,
MT* mom2_out,
const MT* mom2_max,
MT* mom2_max_out,
const MT* lr,
const T* grad,
const T* param,
T* param_out,
const MT* master_param,
MT* master_param_out,
const int64_t* rows,
int64_t row_numel,
int64_t row_count,
bool lazy_mode,
bool amsgrad)
: beta1_(beta1),
beta2_(beta2),
epsilon_(epsilon),
coeff_(coeff),
lr_ratio_(lr_ratio),
beta1_pow_(beta1_pow),
beta2_pow_(beta2_pow),
moment1_(mom1),
moment1_out_(mom1_out),
moment2_(mom2),
moment2_out_(mom2_out),
moment2_max_(mom2_max),
moment2_max_out_(mom2_max_out),
lr_(lr),
grad_(grad),
param_(param),
param_out_(param_out),
master_param_(master_param),
master_param_out_(master_param_out),
rows_(rows),
row_numel_(row_numel),
row_count_(row_count),
lazy_mode_(lazy_mode),
amsgrad_(amsgrad) {}
inline HOSTDEVICE void adamw_update(size_t i, MT g) const {
// The following code is the same as dense
MT mom1 = moment1_[i];
MT mom2 = moment2_[i];
MT lr = *lr_ * lr_ratio_;
MT lr_orig = lr;
MT beta1_pow = *beta1_pow_;
MT beta2_pow = *beta2_pow_;
MT p = master_param_ ? master_param_[i] : static_cast<MT>(param_[i]);
// Calculation
lr *= sqrt(static_cast<MT>(1.0) - beta2_pow) /
(static_cast<MT>(1.0) - beta1_pow);
mom1 = beta1_ * mom1 + (static_cast<MT>(1.0) - beta1_) * g;
mom2 = beta2_ * mom2 + (static_cast<MT>(1.0) - beta2_) * g * g;
p -= lr_orig * coeff_ * p;
if (amsgrad_) {
MT mom2_max_ = std::max(mom2, moment2_max_[i]);
p -= lr * (mom1 / (sqrt(mom2_max_) +
epsilon_ * sqrt(static_cast<MT>(1.0) - beta2_pow)));
// Write back to global memory
moment2_max_out_[i] = mom2_max_;
} else {
p -= lr * (mom1 / (sqrt(mom2) +
epsilon_ * sqrt(static_cast<MT>(1.0) - beta2_pow)));
}
// Write back to global memory
moment1_out_[i] = mom1;
moment2_out_[i] = mom2;
param_out_[i] = static_cast<T>(p);
if (master_param_out_) {
master_param_out_[i] = p;
}
}
inline HOSTDEVICE void operator()(size_t i) const {
auto row_idx =
funcs::BinarySearch<int64_t>(rows_, row_count_, i / row_numel_);
if (lazy_mode_ && row_idx < 0) {
return;
} else {
MT g = row_idx >= 0
? static_cast<MT>(grad_[row_idx * row_numel_ + i % row_numel_])
: static_cast<MT>(0);
adamw_update(i, g);
}
}
};
} // namespace funcs
} // namespace phi