Conv3d
Conv3dConv3d(args: object = (), qconfig: QConfig | None = None, kwargs: object = {})Implementing kernel
C++ engine symbols that back this Python API.Quantization-aware 3-D convolution — trainable float kernel, fake-quant per forward.
The training-time stand-in that lucid.quantization.prepare_qat installs in
place of a float lucid.nn.Conv3d (volumetric / video backbones). It keeps a
trainable float kernel but routes both the weight and the layer output through a
lucid.quantization.FakeQuantize on every forward, so the network feels the
int8 rounding error as it learns and adapts its kernel to the eventual int8 grid. This
closes the train / inference accuracy gap: a network trained with the rounding baked
in barely moves when its weights are finally frozen to int8, whereas plain
post-training quantization perturbs a kernel that never saw rounding.
Straight-through estimator (STE). Rounding is a step function whose derivative is
zero almost everywhere, so a literal round would block all gradient flow. The
fake-quant rounds in the forward pass but, in the backward pass, pretends it was the
identity — the incoming gradient passes straight through to the float kernel (clipped
to the quantization range). The kernel thus stays fully trainable while every forward
value it produces is the dequantized number an int8 conv kernel would have computed.
Where it sits. lucid.quantization.prepare_qat deep-copies the float model
and swaps each lucid.nn.Conv3d for this class, attaching the weight and
activation FakeQuantize modules from the layer's qconfig. After training,
lucid.quantization.convert reads the trained kernel together with the
activation observer's final (scale, zero_point) and bakes them into an inference
lucid.nn.quantized.Conv3d whose weight is stored as (typically
per-output-channel) int8 codes.
On each forward the kernel is fake-quantized, the ordinary F.conv3d runs, and the
result is fake-quantized onto the observed activation grid:
where is the 3-D cross-correlation, are the scale /
zero-point the enclosing FakeQuantize derives from its observer (per-output-channel
for the weight), and are the grid bounds. The
straight-through unit derivative is what keeps the layer differentiable despite the
non-differentiable round.
Parameters
lucid.quantization.FakeQuantize modules applied during training.
Required — constructing the layer without one raises ValueError.*argsobject= ()lucid.nn.Conv3d constructor (in_channels, out_channels,
kernel_size, stride, padding, dilation, groups, bias),
which owns the actual trainable kernel and bias.**kwargsobject= ()lucid.nn.Conv3d constructor (in_channels, out_channels,
kernel_size, stride, padding, dilation, groups, bias),
which owns the actual trainable kernel and bias.Attributes
weight_fake_quantFakeQuantizeqconfig.weight(); rounds the float
kernel each forward and tracks its range so convert can pick the int8 qparams.activation_post_processFakeQuantizeqconfig.activation(); calibrates
the activation grid whose (scale, zero_point) convert later bakes in.Notes
- STE differentiability.
roundis applied forward but the gradient is passed through as the identity, so the float kernel trains normally. - Deferred string padding. Integer / tuple
paddingis supported; string padding ("same"/"valid") is deferred, matching the quantized conv. - Both directions are wired for you.
lucid.quantization.prepare_qatswaps the float conv in;lucid.quantization.convertfolds this layer out into the matchinglucid.nn.quantized.Conv3d. Manual construction is rarely needed and requires an explicitqconfig. - Training is slower, not faster. Only the int8 numerics are simulated; the
convolution itself still runs in float with two extra fake-quant ops, so the speed /
memory win arrives only after
convert.
Examples
>>> import lucid, lucid.nn as nn
>>> import lucid.quantization as Q
>>> import lucid.nn.qat as nnqat
>>> model = nn.Sequential(nn.Conv3d(3, 8, 3, padding=1))
>>> qat = Q.prepare_qat(model) # nn.Conv3d -> qat.Conv3d
>>> isinstance(qat[0], nnqat.Conv3d)
True
>>> loss = (qat(lucid.randn(2, 3, 4, 4, 4)) ** 2).mean()
>>> loss.backward() # STE routes grads to the float kernel
>>> qat.eval()
>>> qc = Q.convert(qat) # -> quantized.Conv3d (int8 weight)
>>> type(qc[0]).__name__
'Conv3d'
A qconfig is mandatory — constructing the layer directly without one raises:
>>> nnqat.Conv3d(3, 8, 3)
Traceback (most recent call last):
...
ValueError: qat conv requires a qconfigSee Also
- lucid.nn.quantized.Conv3d—The int8 inference conv
convertbakes this into. - lucid.nn.qat.ConvReLU3d—QAT conv with a fused ReLU before the output observer.
- lucid.quantization.prepare_qat—Swaps the float
Conv3dfor this QAT layer. - lucid.quantization.convert—Bakes the trained QAT layer into the quantized conv.