```python
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.cpp_extension import load_inline


_cpp_source = r"""
#include <torch/extension.h>

torch::Tensor expand_pool_weights(
    torch::Tensor weight, int64_t pool_h, int64_t pool_w);
torch::Tensor sigmoid_sum_cuda(torch::Tensor input);
"""

_cuda_source = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAGuard.h>
#include <c10/cuda/CUDAException.h>
#include <cuda_runtime.h>
#include <algorithm>

__global__ void expand_weights_kernel(
    const float* __restrict__ weight,
    float* __restrict__ expanded,
    int64_t count,
    int kh, int kw, int ph, int pw,
    int eh, int ew)
{
    for (int64_t idx = int64_t(blockIdx.x) * blockDim.x + threadIdx.x;
         idx < count;
         idx += int64_t(blockDim.x) * gridDim.x)
    {
        const int col = idx % ew;
        const int row = (idx / ew) % eh;
        const int64_t channel_pair = idx / (int64_t(eh) * ew);
        const float* src = weight + channel_pair * kh * kw;

        const int r0 = max(0, row - ph + 1);
        const int r1 = min(kh - 1, row);
        const int c0 = max(0, col - pw + 1);
        const int c1 = min(kw - 1, col);

        float value = 0.0f;
        for (int r = r0; r <= r1; ++r)
            for (int c = c0; c <= c1; ++c)
                value += src[r * kw + c];

        expanded[idx] = value / float(ph * pw);
    }
}

__device__ __forceinline__ float warp_sum(float value)
{
    #pragma unroll
    for (int offset = 16; offset > 0; offset >>= 1)
        value += __shfl_down_sync(0xffffffff, value, offset);
    return value;
}

__device__ __forceinline__ float block_sum(float value)
{
    __shared__ float warp_sums[8];
    const int lane = threadIdx.x & 31;
    const int warp = threadIdx.x >> 5;

    value = warp_sum(value);
    if (lane == 0)
        warp_sums[warp] = value;
    __syncthreads();

    value = (threadIdx.x < 8) ? warp_sums[lane] : 0.0f;
    if (warp == 0)
        value = warp_sum(value);
    return value;
}

constexpr int REDUCTION_CHUNK = 4096;

__global__ void sigmoid_partial_kernel(
    const float* __restrict__ input,
    float* __restrict__ partial,
    int64_t sample_size,
    int parts)
{
    const int batch = blockIdx.y;
    const int part = blockIdx.x;
    const int64_t begin = int64_t(part) * REDUCTION_CHUNK;
    const int64_t end =
        min(begin + int64_t(REDUCTION_CHUNK), sample_size);
    const float* src = input + int64_t(batch) * sample_size;

    float total = 0.0f;
    for (int64_t i = begin + threadIdx.x; i < end; i += blockDim.x) {
        const float value = src[i];
        total += 1.0f / (1.0f + expf(-value));
    }

    total = block_sum(total);
    if (threadIdx.x == 0)
        partial[int64_t(batch) * parts + part] = total;
}

__global__ void finish_sum_kernel(
    const float* __restrict__ partial,
    float* __restrict__ output,
    int parts)
{
    const int batch = blockIdx.x;
    float total = 0.0f;
    for (int i = threadIdx.x; i < parts; i += blockDim.x)
        total += partial[int64_t(batch) * parts + i];

    total = block_sum(total);
    if (threadIdx.x == 0)
        output[batch] = total;
}

torch::Tensor expand_pool_weights(
    torch::Tensor weight, int64_t pool_h, int64_t pool_w)
{
    TORCH_CHECK(weight.is_cuda() && weight.scalar_type() == at::kFloat,
                "Expected CUDA float32 weights");
    TORCH_CHECK(weight.dim() == 4 && weight.is_contiguous(),
                "Expected contiguous OIHW weights");
    TORCH_CHECK(pool_h > 0 && pool_w > 0, "Invalid pooling size");

    const c10::cuda::CUDAGuard guard(weight.device());
    const int kh = weight.size(2);
    const int kw = weight.size(3);
    const int eh = kh + pool_h - 1;
    const int ew = kw + pool_w - 1;

    auto expanded = torch::empty(
        {weight.size(0), weight.size(1), eh, ew}, weight.options());
    const int64_t count = expanded.numel();
    const int blocks =
        static_cast<int>(std::min<int64_t>((count + 255) / 256, 65535));

    expand_weights_kernel<<<
        blocks, 256, 0, at::cuda::getCurrentCUDAStream()
    >>>(
        weight.data_ptr<float>(), expanded.data_ptr<float>(), count,
        kh, kw, pool_h, pool_w, eh, ew);
    C10_CUDA_KERNEL_LAUNCH_CHECK();
    return expanded;
}

torch::Tensor sigmoid_sum_cuda(torch::Tensor input)
{
    TORCH_CHECK(input.is_cuda() && input.scalar_type() == at::kFloat,
                "Expected CUDA float32 input");
    TORCH_CHECK(input.dim() == 4 && input.is_contiguous(),
                "Expected contiguous NCHW input");

    const c10::cuda::CUDAGuard guard(input.device());
    const int64_t batch = input.size(0);
    auto output = torch::empty({batch}, input.options());
    if (batch == 0)
        return output;

    const int64_t sample_size =
        input.size(1) * input.size(2) * input.size(3);
    const int parts =
        static_cast<int>((sample_size + REDUCTION_CHUNK - 1) /
                         REDUCTION_CHUNK);
    auto partial = torch::empty({batch, parts}, input.options());
    auto stream = at::cuda::getCurrentCUDAStream();

    sigmoid_partial_kernel<<<dim3(parts, batch), 256, 0, stream>>>(
        input.data_ptr<float>(), partial.data_ptr<float>(),
        sample_size, parts);
    C10_CUDA_KERNEL_LAUNCH_CHECK();

    finish_sum_kernel<<<batch, 256, 0, stream>>>(
        partial.data_ptr<float>(), output.data_ptr<float>(), parts);
    C10_CUDA_KERNEL_LAUNCH_CHECK();
    return output;
}
"""

_ops = load_inline(
    name="conv_avgpool_sigmoid_sum_fused_v1",
    cpp_sources=_cpp_source,
    cuda_sources=_cuda_source,
    functions=["expand_pool_weights", "sigmoid_sum_cuda"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3"],
    verbose=False,
)


class ModelNew(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, pool_kernel_size):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size)
        self.avg_pool = nn.AvgPool2d(pool_kernel_size)

        if isinstance(pool_kernel_size, int):
            self.pool_size = (pool_kernel_size, pool_kernel_size)
        else:
            self.pool_size = tuple(pool_kernel_size)

    def forward(self, x):
        if (
            not x.is_cuda
            or x.dtype != torch.float32
            or self.conv.weight.dtype != torch.float32
            or torch.is_autocast_enabled()
        ):
            x = self.avg_pool(self.conv(x))
            return torch.sigmoid(x).sum(dim=(1, 2, 3))

        # Average pooling after convolution is equivalent to a strided
        # convolution whose weights incorporate the pooling box filter.
        weight = _ops.expand_pool_weights(
            self.conv.weight.contiguous(), *self.pool_size
        )
        pooled = F.conv2d(
            x,
            weight,
            self.conv.bias,
            stride=self.pool_size,
        )
        return _ops.sigmoid_sum_cuda(pooled.contiguous())
```