forked from ccf-ai-infra/GPUCodeForces
156 lines
4.5 KiB
Python
156 lines
4.5 KiB
Python
import torch
|
|
import torch.nn as nn
|
|
from torch.utils.cpp_extension import load_inline
|
|
|
|
cpp_source = """
|
|
#include <torch/extension.h>
|
|
#include <string>
|
|
|
|
// 函数前向声明
|
|
torch::Tensor multi_label_margin_loss_cuda_forward(
|
|
const torch::Tensor& input,
|
|
const torch::Tensor& target,
|
|
const std::string& reduction
|
|
);
|
|
"""
|
|
|
|
cuda_source = """
|
|
#include <torch/extension.h>
|
|
#include <cuda_runtime.h>
|
|
|
|
#define BLOCK_SIZE 256
|
|
// 设置一个合理的单个样本最大正类标签数,用于共享内存数组
|
|
#define MAX_POSITIVE_LABELS 32
|
|
|
|
template <typename T>
|
|
__global__ void multi_label_margin_loss_kernel(
|
|
T* output, // (N)
|
|
const T* input, // (N, C)
|
|
const long* target, // (N, C)
|
|
const int N,
|
|
const int C)
|
|
{
|
|
int sample_idx = blockIdx.x;
|
|
if (sample_idx >= N) return;
|
|
|
|
__shared__ long positive_indices[MAX_POSITIVE_LABELS];
|
|
__shared__ int num_positives;
|
|
|
|
// 线程 0 初始化共享内存计数器
|
|
if (threadIdx.x == 0) {
|
|
num_positives = 0;
|
|
}
|
|
__syncthreads();
|
|
|
|
for (int i = threadIdx.x; i < C; i += blockDim.x) {
|
|
long label = target[sample_idx * C + i];
|
|
if (label != -1) {
|
|
int index = atomicAdd(&num_positives, 1);
|
|
if (index < MAX_POSITIVE_LABELS) {
|
|
positive_indices[index] = label;
|
|
}
|
|
}
|
|
}
|
|
__syncthreads();
|
|
|
|
__shared__ T sdata[BLOCK_SIZE];
|
|
int tid = threadIdx.x;
|
|
T my_sum = 0.0f;
|
|
const T* input_row = input + sample_idx * C;
|
|
|
|
for (int neg_class_idx = tid; neg_class_idx < C; neg_class_idx += blockDim.x) {
|
|
// 检查当前类别是否为正类
|
|
bool is_positive = false;
|
|
for (int j = 0; j < num_positives; ++j) {
|
|
if (positive_indices[j] == neg_class_idx) {
|
|
is_positive = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 如果是负类,则计算与所有正类的损失
|
|
if (!is_positive) {
|
|
T x_neg = input_row[neg_class_idx];
|
|
for (int j = 0; j < num_positives; ++j) {
|
|
long pos_class_idx = positive_indices[j];
|
|
T x_pos = input_row[pos_class_idx];
|
|
T loss_term = 1.0f - (x_pos - x_neg);
|
|
if (loss_term > 0) {
|
|
my_sum += loss_term;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sdata[tid] = my_sum;
|
|
__syncthreads();
|
|
|
|
for (int s = blockDim.x / 2; s > 0; s >>= 1) {
|
|
if (tid < s) {
|
|
sdata[tid] += sdata[tid + s];
|
|
}
|
|
__syncthreads();
|
|
}
|
|
|
|
if (tid == 0) {
|
|
output[sample_idx] = sdata[0] / C;
|
|
}
|
|
}
|
|
|
|
torch::Tensor multi_label_margin_loss_cuda_forward(
|
|
const torch::Tensor& input,
|
|
const torch::Tensor& target,
|
|
const std::string& reduction)
|
|
{
|
|
TORCH_CHECK(input.is_cuda() && target.is_cuda(), "Tensors must be on CUDA");
|
|
TORCH_CHECK(input.dim() == 2, "Input must be 2D");
|
|
TORCH_CHECK(target.dim() == 2, "Target must be 2D");
|
|
TORCH_CHECK(input.size(0) == target.size(0), "Batch sizes must match");
|
|
TORCH_CHECK(input.is_contiguous() && target.is_contiguous(), "Tensors must be contiguous");
|
|
|
|
const int N = input.size(0);
|
|
const int C = input.size(1);
|
|
|
|
auto options = torch::TensorOptions().device(input.device()).dtype(input.dtype());
|
|
auto sample_losses = torch::empty({N}, options);
|
|
|
|
dim3 grid(N);
|
|
dim3 block(BLOCK_SIZE);
|
|
|
|
AT_DISPATCH_FLOATING_TYPES(input.scalar_type(), "multi_label_margin_loss_kernel", ([&] {{
|
|
multi_label_margin_loss_kernel<scalar_t><<<grid, block>>>(
|
|
sample_losses.data_ptr<scalar_t>(),
|
|
input.data_ptr<scalar_t>(),
|
|
target.data_ptr<long>(),
|
|
N, C
|
|
);
|
|
}}));
|
|
|
|
if (reduction == "none") {
|
|
return sample_losses;
|
|
} else if (reduction == "sum") {
|
|
return sample_losses.sum();
|
|
} else {{ // "mean"
|
|
return sample_losses.mean();
|
|
}}
|
|
}
|
|
"""
|
|
|
|
class ModelNew(nn.Module):
|
|
def __init__(self, reduction='mean'):
|
|
super(ModelNew, self).__init__()
|
|
self.reduction = reduction
|
|
|
|
self.op = load_inline(
|
|
name='multi_label_margin_loss_op',
|
|
cpp_sources=cpp_source,
|
|
cuda_sources=cuda_source,
|
|
functions=['multi_label_margin_loss_cuda_forward'],
|
|
verbose=False
|
|
)
|
|
|
|
def forward(self, input_tensor: torch.Tensor, target_tensor: torch.Tensor) -> torch.Tensor:
|
|
return self.op.multi_label_margin_loss_cuda_forward(
|
|
input_tensor,
|
|
target_tensor,
|
|
self.reduction
|
|
) |