DCNv4 INT8 patch (Level 1): int8 storage + fp32 arithmetic for SOFIA/MERIDIAN E9

- Add dcnv4_int8_cuda.cu/.h: CUDA kernel (int8 values, fp16 offsets, fp32 interp,
  requantize with value_scale/output_scale)
- Add dcnv4_int8_forward(): inference-only functional wrapper (@no_grad)
- Add DCNv4Strip.forward_int8(): module-level INT8 forward (without_pointwise=True)
- Add scripts/test_dcnv4_int8.py: correctness gate (<=1 LSB, >=99% exact)
  and informational fp16 vs int8 benchmark
- Update README: INT8 API section, updated structure tree, SOFIA/MERIDIAN context

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 16:19:21 +03:00
parent 1b3206b6a7
commit 71862bbeca
9 changed files with 539 additions and 14 deletions

View File

@@ -13,7 +13,7 @@ import torch
from torch import nn
import torch.nn.functional as F
from torch.nn.init import xavier_uniform_, constant_
from ..functions import DCNv4Function
from ..functions import DCNv4Function, dcnv4_int8_forward
class CenterFeatureScaleModule(nn.Module):
def forward(self,
@@ -284,3 +284,41 @@ class DCNv4Strip(nn.Module):
x = self.output_proj(x)
return x
@torch.no_grad()
def forward_int8(self, input_int8, shape, value_scale, output_scale):
"""INT8 inference forward (Level 1: int8 storage + fp32 math).
The deformable sampling consumes/produces int8; the (small) offset/mask
Linear runs in the module's own float dtype on the dequantized input.
Requires ``without_pointwise=True`` (the int8 path quantizes the
sampling op only; pointwise projections belong to the surrounding
block's quantized 1x1 convs).
Args:
input_int8: int8 [N, L, C]; real value = input_int8 * value_scale.
shape: (H, W) of the token grid.
value_scale: per-tensor input scale.
output_scale: per-tensor output scale.
Returns:
int8 [N, L, C]; real output = result * output_scale.
"""
assert self.without_pointwise, \
"forward_int8 supports without_pointwise=True only"
assert input_int8.dtype == torch.int8, "input must be int8"
N, L, C = input_int8.shape
H, W = shape
w_dtype = self.offset_mask.weight.dtype
x_deq = input_int8.to(w_dtype) * float(value_scale)
offset_mask = self.offset_mask(x_deq).to(torch.float16)
offset_mask = offset_mask.reshape(N, H, W, -1).contiguous()
out = dcnv4_int8_forward(
input_int8.reshape(N, H, W, C).contiguous(), offset_mask,
self.kernel_h, self.kernel_w, self.pad_h, self.pad_w,
self.group, self.group_channels,
self.offset_scale, value_scale, output_scale,
)
return out.reshape(N, L, C)