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

316 lines
11 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/phi/kernels/cast_kernel.h"
#include "paddle/phi/kernels/impl/margin_cross_entropy.cu.h"
namespace phi {
template <typename T, typename MT, typename IndexT>
__global__ void AddMarginToPositiveLogitsKernel(T* logit,
const IndexT* label,
const float margin1,
const float margin2,
const float margin3,
const int rank,
const int nranks,
const int64_t N,
const int64_t D,
const int* class_interval_ptr) {
int64_t start_index = class_interval_ptr[rank];
int64_t end_index = class_interval_ptr[rank + 1];
int num_classes = class_interval_ptr[nranks];
CUDA_KERNEL_LOOP_TYPE(i, N, int64_t) {
auto real_label = label[i];
PADDLE_ENFORCE((real_label < num_classes) && (real_label >= 0),
"The index is out of bounds, "
"please check whether the value of label and "
"input meet the number of class. It should "
"be less than [%d], but received [%d]",
num_classes,
real_label);
if (real_label >= start_index && real_label < end_index) {
int64_t offset = i * D + real_label - start_index;
MT x = static_cast<MT>(logit[offset]);
MT theta = acos(x);
theta *= static_cast<MT>(margin1);
theta += static_cast<MT>(margin2);
MT y = cos(theta) - static_cast<MT>(margin3);
logit[offset] = static_cast<T>(y);
}
}
}
template <typename T, typename MT>
__global__ void ScaleLogitKernel(T* logits,
const float scale,
const int64_t N,
const int64_t D) {
CUDA_KERNEL_LOOP_TYPE(i, N * D, int64_t) {
logits[i] = static_cast<MT>(logits[i]) * (scale);
}
}
template <typename T, typename MT>
__global__ void LogitsMinusMaxKernel(T* logits,
const T* logits_max_per_row,
const int64_t N,
const int64_t D) {
CUDA_KERNEL_LOOP_TYPE(i, N * D, int64_t) {
auto row = i / D;
logits[i] =
static_cast<MT>(logits[i]) - static_cast<MT>(logits_max_per_row[row]);
}
}
template <typename T, typename MT>
__global__ void LogitsMinusLogSumKernel(T* logits,
const T* logits_sum_per_row,
const int64_t N,
const int64_t D) {
CUDA_KERNEL_LOOP_TYPE(i, N * D, int64_t) {
auto row = i / D;
logits[i] = static_cast<MT>(logits[i]) -
static_cast<MT>(kps::details::Log(logits_sum_per_row[row]));
}
}
template <typename T, typename IndexT>
__global__ void HardLabelSoftmaxWithCrossEntropyKernel(
T* loss,
T* log_softmax,
const IndexT* labels,
const int rank,
const int64_t N,
const int64_t D,
const int* class_interval_ptr) {
int start_index = class_interval_ptr[rank];
CUDA_KERNEL_LOOP_TYPE(i, N * D, int64_t) {
auto row = i / D;
auto col = i % D;
if ((col + start_index) == labels[row]) {
auto softmax = log_softmax[i];
loss[row] = -softmax;
log_softmax[i] = kps::details::Exp(softmax);
} else {
log_softmax[i] = kps::details::Exp(log_softmax[i]);
}
}
}
template <typename T, typename Context>
void MarginCrossEntropyKernel(const Context& dev_ctx,
const DenseTensor& logits,
const DenseTensor& labels,
bool return_softmax,
int ring_id,
int rank,
int nranks,
float margin1,
float margin2,
float margin3,
float scale,
DenseTensor* softmax,
DenseTensor* loss) {
const auto& place = dev_ctx.GetPlace(); // old code
using MT = typename MPTypeTrait<T>::Type;
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
distributed::NCCLCommContext* comm_ctx = nullptr;
gpuStream_t stream;
if (nranks > 1) {
comm_ctx =
static_cast<distributed::NCCLCommContext*>(dev_ctx.GetCommContext());
PADDLE_ENFORCE_NE(comm_ctx,
nullptr,
common::errors::Unavailable(
"NCCLCommContext is nullptr, collective op should "
"has ring_id attr."));
// use global calculate stream
stream = static_cast<GPUContext*>(DeviceContextPool::Instance().Get(place))
->stream();
}
#endif
// allocate memory on device.
T* softmax_ptr = dev_ctx.template Alloc<T>(softmax);
T* loss_ptr = dev_ctx.template Alloc<T>(loss);
const auto& logits_dims = logits.dims();
const auto& labels_dims = labels.dims();
const int axis = logits_dims.size() - 1;
const int64_t N = funcs::SizeToAxis(axis, logits_dims);
const int64_t D = funcs::SizeFromAxis(axis, logits_dims);
int blocks = NumBlocks(N);
int threads = kNumCUDAThreads;
const auto& label_type = labels.dtype();
// copy logits to softmax variable since we can't modify logits,
// and it also be used when calculate grad
Copy<Context>(dev_ctx, logits, dev_ctx.GetPlace(), true, softmax);
DenseTensor softmax_2d;
softmax_2d.ShareDataWith(*softmax).Resize({N, D});
T* logits_ptr = softmax_2d.data<T>();
DenseTensor class_interval;
GetClassInterval<T, Context>(dev_ctx.stream(),
dev_ctx.GetPlace(),
dev_ctx,
ring_id,
rank,
nranks,
D,
&class_interval);
// step 1, preprocess logits
// add margin for positive elements
// theta = acos(x_i)
// (cos(m1 * theta + m2) - m3)
// save match_logits, used for gradient computation.
if (label_type == DataType::INT32) {
typedef int32_t LabelT;
AddMarginToPositiveLogitsKernel<T, MT>
<<<NumBlocks(N), threads, 0, dev_ctx.stream()>>>(
logits_ptr,
labels.data<LabelT>(),
margin1,
margin2,
margin3,
rank,
nranks,
N,
D,
class_interval.data<int>());
} else if (label_type == DataType::INT64) {
typedef int64_t LabelT;
AddMarginToPositiveLogitsKernel<T, MT>
<<<NumBlocks(N), threads, 0, dev_ctx.stream()>>>(
logits_ptr,
labels.data<LabelT>(),
margin1,
margin2,
margin3,
rank,
nranks,
N,
D,
class_interval.data<int>());
} else {
PADDLE_THROW(errors::Unimplemented(
"margin_cross_entropy label type noly support int32 and int64, "
"but got %s",
label_type));
}
// scale by s
ScaleLogitKernel<T, MT><<<NumBlocks(N * D), threads, 0, dev_ctx.stream()>>>(
logits_ptr, scale, N, D);
// step 2, obtain logit_max
DenseTensor logits_max;
logits_max.Resize({N, 1});
dev_ctx.template Alloc<T>(&logits_max);
T* logits_max_buff = dev_ctx.template Alloc<T>(&logits_max);
funcs::ReduceKernel<T, T, kps::MaxFunctor, kps::IdentityFunctor<T>>(
static_cast<const GPUContext&>(dev_ctx),
softmax_2d,
&logits_max,
kps::IdentityFunctor<T>(),
{1});
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
if (nranks > 1) {
comm_ctx->AllReduce(&logits_max, logits_max, ncclMax, stream);
}
#endif
// step 3, logit - logit_max
LogitsMinusMaxKernel<T, MT>
<<<NumBlocks(N * D), threads, 0, dev_ctx.stream()>>>(
logits_ptr, logits_max_buff, N, D);
// step 4, sum(exp(logit - logit_max))
DenseTensor sum_exp_logits;
sum_exp_logits.Resize({N, 1});
dev_ctx.template Alloc<T>(&sum_exp_logits);
T* sum_exp_logits_buff = dev_ctx.template Alloc<T>(&sum_exp_logits);
funcs::ReduceKernel<T, T, kps::AddFunctor, kps::ExpFunctor<T>>(
static_cast<const GPUContext&>(dev_ctx),
softmax_2d,
&sum_exp_logits,
kps::ExpFunctor<T>(),
{1});
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
if (nranks > 1) {
comm_ctx->AllReduce(&sum_exp_logits, sum_exp_logits, ncclSum, stream);
}
#endif
// step 5, (logit - logit_max) - log(sum(exp(logit - logit_max)))
LogitsMinusLogSumKernel<T, MT>
<<<NumBlocks(N * D), threads, 0, dev_ctx.stream()>>>(
logits_ptr, sum_exp_logits_buff, N, D);
// step 6, prob = exp((logit - logit_max) - log(sum(exp(logit -
// logit_max))))
// loss = -((logit_i - logit_max) - log(sum(exp(logit - logit_max))))
funcs::SetConstant<Context, T> functor;
functor(dev_ctx, loss, static_cast<T>(0.0));
if (label_type == DataType::INT32) {
typedef int32_t LabelT;
HardLabelSoftmaxWithCrossEntropyKernel<T, LabelT>
<<<blocks, threads, 0, dev_ctx.stream()>>>(loss_ptr,
logits_ptr,
labels.data<LabelT>(),
rank,
N,
D,
class_interval.data<int>());
} else if (label_type == DataType::INT64) {
typedef int64_t LabelT;
HardLabelSoftmaxWithCrossEntropyKernel<T, LabelT>
<<<blocks, threads, 0, dev_ctx.stream()>>>(loss_ptr,
logits_ptr,
labels.data<LabelT>(),
rank,
N,
D,
class_interval.data<int>());
}
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
if (nranks > 1) {
comm_ctx->AllReduce(loss, *loss, ncclSum, stream);
}
#endif
}
} // namespace phi
PD_REGISTER_KERNEL(margin_cross_entropy,
GPU,
ALL_LAYOUT,
phi::MarginCrossEntropyKernel,
float,
double,
phi::float16,
phi::bfloat16) {}