GPUCodeForces/S1/15/hingeembeddingloss_cuda.py

119 lines
3.7 KiB
Python

# hingeembeddingloss_cuda.py
import torch
from torch.utils.cpp_extension import load_inline
from hingeembeddingloss_torch import BATCH_SIZE, FEATURE_DIM, MARGIN
N_ELEMENTS = BATCH_SIZE * FEATURE_DIM
class ModelNew(torch.nn.Module):
def __init__(self):
super().__init__()
self._compile_cuda_kernel()
def _compile_cuda_kernel(self):
cpp_source = """
#include <torch/extension.h>
torch::Tensor hel_forward_cuda(torch::Tensor input, torch::Tensor target, float margin_val);
"""
cuda_source = """
#include <cuda_runtime.h>
#include <cmath>
#include <float.h>
#define BLOCK_SIZE 256
__device__ __forceinline__ float warp_reduce_sum(float val) {{
for (int offset = BLOCK_SIZE / 2; offset > 0; offset /= 2) {{
val += __shfl_down_sync(0xffffffff, val, offset);
}}
return val;
}}
__global__ void hel_fused_kernel(
const float* __restrict__ input,
const float* __restrict__ target,
float* __restrict__ loss_sum_out,
int n_elements,
float margin_val
) {{
__shared__ float s_data[BLOCK_SIZE];
float thread_loss = 0.0f;
int grid_stride = gridDim.x * blockDim.x;
for (int idx = blockIdx.x * blockDim.x + threadIdx.x;
idx < n_elements;
idx += grid_stride)
{{
float x = input[idx];
float y = target[idx];
float loss_val;
if (y >= 0.0f) {{ // 相似 (y = +1)
// Loss = x
loss_val = x;
}} else {{
// Loss = max(0, margin - x)
float term = margin_val - x;
loss_val = fmaxf(0.0f, term);
}}
thread_loss += loss_val;
}}
s_data[threadIdx.x] = thread_loss;
__syncthreads();
for (int offset = BLOCK_SIZE / 2; offset > 0; offset >>= 1) {{
if (threadIdx.x < offset) {{
s_data[threadIdx.x] += s_data[threadIdx.x + offset];
}}
__syncthreads();
}}
if (threadIdx.x == 0) {{
loss_sum_out[blockIdx.x] = s_data[0];
}}
}}
torch::Tensor hel_forward_cuda(torch::Tensor input, torch::Tensor target, float margin_val) {
TORCH_CHECK(input.is_cuda(), "Input must be a CUDA tensor");
input = input.contiguous();
target = target.contiguous();
int n_elements = input.numel();
const int block_size = BLOCK_SIZE;
const int grid_size = (n_elements + block_size - 1) / block_size;
auto block_loss_sums = torch::empty({grid_size}, input.options());
hel_fused_kernel<<<grid_size, block_size>>>(
input.data_ptr<float>(),
target.data_ptr<float>(),
block_loss_sums.data_ptr<float>(),
n_elements,
margin_val
);
return block_loss_sums.sum() / n_elements;
}
"""
self.hel_op = load_inline(
name="hel_fused_op_safest_v4",
cpp_sources=cpp_source,
cuda_sources=cuda_source,
functions=["hel_forward_cuda"],
extra_cuda_cflags=["-O3", "--use_fast_math"],
verbose=True
)
def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor:
return self.hel_op.hel_forward_cuda(input, target, MARGIN)