call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_src = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor first_conv_bn_relu6_forward(torch::Tensor x, torch::Tensor w, torch::Tensor gamma, torch::Tensor beta, torch::Tensor mean, torch::Tensor var, double eps);\ntorch::Tensor dw_conv_bn_relu6_forward(torch::Tensor x, torch::Tensor w, torch::Tensor gamma, torch::Tensor beta, torch::Tensor mean, torch::Tensor var, int64_t stride, double eps);\ntorch::Tensor avgpool2d_forward(torch::Tensor x);\n\"\"\"\n\n_cuda_src = r\"\"\"\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <algorithm>\n\nstatic inline int div_up_int64(int64_t a, int b) { return (int)((a + b - 1) / b); }\n\n__global__ void first_conv_bn_relu6_kernel(\n const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ mean,\n const float* __restrict__ var,\n float* __restrict__ y,\n int N, int H, int W, int OH, int OW, float eps)\n{\n int64_t idx = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;\n int64_t total = (int64_t)N * 32 * OH * OW;\n if (idx >= total) return;\n\n int ow = idx % OW;\n int t = idx / OW;\n int oh = t % OH;\n t /= OH;\n int oc = t % 32;\n int n = t / 32;\n\n int ih0 = oh * 2 - 1;\n int iw0 = ow * 2 - 1;\n float acc = 0.0f;\n #pragma unroll\n for (int ic = 0; ic < 3; ++ic) {\n #pragma unroll\n for (int kh = 0; kh < 3; ++kh) {\n int ih = ih0 + kh;\n if ((unsigned)ih >= (unsigned)H) continue;\n #pragma unroll\n for (int kw = 0; kw < 3; ++kw) {\n int iw = iw0 + kw;\n if ((unsigned)iw < (unsigned)W) {\n float xv = x[((n * 3 + ic) * H + ih) * W + iw];\n float wv = w[((oc * 3 + ic) * 3 + kh) * 3 + kw];\n acc = fmaf(xv, wv, acc);\n }\n }\n }\n }\n float scale = gamma[oc] * rsqrtf(var[oc] + eps);\n float out = fmaf(acc - mean[oc], scale, beta[oc]);\n out = fminf(fmaxf(out, 0.0f), 6.0f);\n y[idx] = out;\n}\n\n__global__ void dw_conv_bn_relu6_kernel(\n const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ mean,\n const float* __restrict__ var,\n float* __restrict__ y,\n int N, int C, int H, int W, int OH, int OW, int stride, float eps)\n{\n int64_t idx = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;\n int64_t total = (int64_t)N * C * OH * OW;\n if (idx >= total) return;\n\n int ow = idx % OW;\n int t = idx / OW;\n int oh = t % OH;\n t /= OH;\n int c = t % C;\n int n = t / C;\n\n int ih0 = oh * stride - 1;\n int iw0 = ow * stride - 1;\n float acc = 0.0f;\n const float* wp = w + c * 9;\n #pragma unroll\n for (int kh = 0; kh < 3; ++kh) {\n int ih = ih0 + kh;\n if ((unsigned)ih >= (unsigned)H) continue;\n #pragma unroll\n for (int kw = 0; kw < 3; ++kw) {\n int iw = iw0 + kw;\n if ((unsigned)iw < (unsigned)W) {\n float xv = x[((n * C + c) * H + ih) * W + iw];\n float wv = wp[kh * 3 + kw];\n acc = fmaf(xv, wv, acc);\n }\n }\n }\n float scale = gamma[c] * rsqrtf(var[c] + eps);\n float out = fmaf(acc - mean[c], scale, beta[c]);\n out = fminf(fmaxf(out, 0.0f), 6.0f);\n y[idx] = out;\n}\n\n__global__ void avgpool2d_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int H, int W)\n{\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = N * C;\n if (idx >= total) return;\n int c = idx % C;\n int n = idx / C;\n const float* xp = x + ((n * C + c) * H) * W;\n float s = 0.0f;\n int HW = H * W;\n for (int i = 0; i < HW; ++i) s += xp[i];\n y[idx] = s / (float)HW;\n}\n\ntorch::Tensor first_conv_bn_relu6_forward(torch::Tensor x, torch::Tensor w, torch::Tensor gamma, torch::Tensor beta, torch::Tensor mean, torch::Tensor var, double eps) {\n x = x.contiguous();\n w = w.contiguous();\n int N = (int)x.size(0);\n int H = (int)x.size(2);\n int W = (int)x.size(3);\n int OH = (H + 1) / 2;\n int OW = (W + 1) / 2;\n auto y = torch::empty({N, 32, OH, OW}, x.options());\n int threads = 256;\n int64_t total = (int64_t)N * 32 * OH * OW;\n first_conv_bn_relu6_kernel<<<div_up_int64(total, threads), threads, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), w.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(), mean.data_ptr<float>(), var.data_ptr<float>(), y.data_ptr<float>(), N, H, W, OH, OW, (float)eps);\n return y;\n}\n\ntorch::Tensor dw_conv_bn_relu6_forward(torch::Tensor x, torch::Tensor w, torch::Tensor gamma, torch::Tensor beta, torch::Tensor mean, torch::Tensor var, int64_t stride, double eps) {\n x = x.contiguous();\n w = w.contiguous();\n int N = (int)x.size(0);\n int C = (int)x.size(1);\n int H = (int)x.size(2);\n int W = (int)x.size(3);\n int s = (int)stride;\n int OH = (H + 2 - 3) / s + 1;\n int OW = (W + 2 - 3) / s + 1;\n auto y = torch::empty({N, C, OH, OW}, x.options());\n int threads = 256;\n int64_t total = (int64_t)N * C * OH * OW;\n dw_conv_bn_relu6_kernel<<<div_up_int64(total, threads), threads, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), w.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(), mean.data_ptr<float>(), var.data_ptr<float>(), y.data_ptr<float>(), N, C, H, W, OH, OW, s, (float)eps);\n return y;\n}\n\ntorch::Tensor avgpool2d_forward(torch::Tensor x) {\n x = x.contiguous();\n int N = (int)x.size(0);\n int C = (int)x.size(1);\n int H = (int)x.size(2);\n int W = (int)x.size(3);\n auto y = torch::empty({N, C}, x.options());\n int threads = 256;\n int total = N * C;\n avgpool2d_kernel<<<div_up_int64(total, threads), threads, 0, at::cuda::getCurrentCUDAStream()>>>(x.data_ptr<float>(), y.data_ptr<float>(), N, C, H, W);\n return y;\n}\n\"\"\"\n\n_mobilenet_ext = load_inline(\n name=\"mobilenetv2_fused_depthwise_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"first_conv_bn_relu6_forward\", \"dw_conv_bn_relu6_forward\", \"avgpool2d_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes=1000):\n super(ModelNew, self).__init__()\n def _make_divisible(v, divisor, min_value=None):\n if min_value is None:\n min_value = divisor\n new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)\n if new_v < 0.9 * v:\n new_v += divisor\n return new_v\n def _inverted_residual_block(inp, oup, stride, expand_ratio):\n hidden_dim = int(inp * expand_ratio)\n layers = []\n if expand_ratio != 1:\n layers.append(nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False))\n layers.append(nn.BatchNorm2d(hidden_dim))\n layers.append(nn.ReLU6(inplace=True))\n layers.extend([\n nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),\n nn.BatchNorm2d(hidden_dim),\n nn.ReLU6(inplace=True),\n nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),\n nn.BatchNorm2d(oup),\n ])\n return nn.Sequential(*layers), (stride == 1 and inp == oup)\n\n input_channel = 32\n last_channel = 1280\n inverted_residual_setting = [[1,16,1,1],[6,24,2,2],[6,32,3,2],[6,64,4,2],[6,96,3,1],[6,160,3,2],[6,320,1,1]]\n features = [nn.Conv2d(3, input_channel, 3, 2, 1, bias=False), nn.BatchNorm2d(input_channel), nn.ReLU6(inplace=True)]\n for t, c, n, s in inverted_residual_setting:\n output_channel = _make_divisible(c, 8)\n for i in range(n):\n stride = s if i == 0 else 1\n features.append(_inverted_residual_block(input_channel, output_channel, stride, expand_ratio=t)[0])\n input_channel = output_channel\n features.append(nn.Conv2d(input_channel, last_channel, 1, 1, 0, bias=False))\n features.append(nn.BatchNorm2d(last_channel))\n features.append(nn.ReLU6(inplace=True))\n features.append(nn.AdaptiveAvgPool2d((1, 1)))\n self.features = nn.Sequential(*features)\n self.classifier = nn.Sequential(nn.Dropout(0.0), nn.Linear(last_channel, num_classes))\n for m in self.modules():\n if isinstance(m, nn.Conv2d):\n nn.init.kaiming_normal_(m.weight, mode='fan_out')\n if m.bias is not None:\n nn.init.zeros_(m.bias)\n elif isinstance(m, nn.BatchNorm2d):\n nn.init.ones_(m.weight)\n nn.init.zeros_(m.bias)\n elif isinstance(m, nn.Linear):\n nn.init.normal_(m.weight, 0, 0.01)\n nn.init.zeros_(m.bias)\n\n def _forward_inner_seq_eval(self, seq, x):\n i = 0\n L = len(seq)\n while i < L:\n m = seq[i]\n # Fuse depthwise 3x3 conv + following BatchNorm + ReLU6 in eval mode.\n if (i + 2 < L and isinstance(m, nn.Conv2d) and m.groups == m.in_channels and\n m.in_channels == m.out_channels and m.kernel_size == (3, 3) and\n m.padding == (1, 1) and isinstance(seq[i+1], nn.BatchNorm2d) and\n isinstance(seq[i+2], nn.ReLU6) and x.is_cuda and x.dtype is torch.float32):\n bn = seq[i+1]\n x = _mobilenet_ext.dw_conv_bn_relu6_forward(\n x, m.weight, bn.weight, bn.bias, bn.running_mean, bn.running_var,\n int(m.stride[0]), float(bn.eps))\n i += 3\n else:\n x = m(x)\n i += 1\n return x\n\n def forward(self, x):\n # Training-mode BatchNorm uses batch statistics; keep the exact PyTorch path there.\n if self.training or (not x.is_cuda) or x.dtype is not torch.float32:\n x = self.features(x)\n x = x.view(x.size(0), -1)\n return self.classifier(x)\n\n # First conv + BN + ReLU6 fused.\n conv0 = self.features[0]\n bn0 = self.features[1]\n x = _mobilenet_ext.first_conv_bn_relu6_forward(\n x, conv0.weight, bn0.weight, bn0.bias, bn0.running_mean, bn0.running_var, float(bn0.eps))\n\n # Run all layers except the final adaptive average pool; depthwise conv blocks are fused.\n for j in range(3, len(self.features) - 1):\n layer = self.features[j]\n if isinstance(layer, nn.Sequential):\n x = self._forward_inner_seq_eval(layer, x)\n else:\n x = layer(x)\n\n x = _mobilenet_ext.avgpool2d_forward(x)\n # Dropout probability is zero in the reference, so this is exactly the classifier's Linear.\n lin = self.classifier[1]\n return F.linear(x, lin.weight, lin.bias)\n"
}