ConvReLU3d
Conv3dConvReLU3d(args: object = (), qconfig: QConfig | None = None, kwargs: object = {})Quantization-aware fused 3-D conv + ReLU — trainable, fake-quant per forward.
A Conv3d whose forward applies ReLU before the output fake-quant, so the
activation observer sees the true post-ReLU (non-negative) range and calibrates a grid
that spends all of its codes on values the layer can actually emit. Fusing conv and
ReLU also lets a single activation observer stand in for both, which is exactly the
shape lucid.quantization.convert needs to collapse them into one kernel.
Like the base Conv3d, the weight and the (post-ReLU) output are fake-quantized
every forward under a straight-through estimator (STE): rounding happens in the forward
pass while gradients pass straight through to the float kernel, so it stays fully
trainable and learns to compensate for the eventual int8 rounding. Built from a fused
float lucid.nn.intrinsic.ConvReLU3d by
lucid.quantization.prepare_qat; lucid.quantization.convert folds it into
a single quantized lucid.nn.quantized.ConvReLU3d.
On each forward the kernel is fake-quantized, F.conv3d runs, ReLU clips, and the
result is fake-quantized onto the observed post-ReLU grid:
where is the 3-D cross-correlation, are the observer's scale / zero-point and the grid bounds. Because the activation observer sits outside the ReLU, the calibrated describe the non-negative output.
Parameters
lucid.quantization.FakeQuantize modules applied during training.
Required — constructing the layer without one raises ValueError.*argsobject= ()lucid.nn.Conv3d
constructor, exactly as for Conv3d.**kwargsobject= ()lucid.nn.Conv3d
constructor, exactly as for Conv3d.Attributes
weight_fake_quantFakeQuantizeqconfig.weight(); rounds the float
kernel each forward and tracks its range for convert.activation_post_processFakeQuantizeqconfig.activation(); here it
observes the post-ReLU range so the baked-in grid matches inference.Notes
- STE differentiability. Rounding is applied forward but the gradient passes through as the identity, so the float kernel trains normally.
- Post-ReLU calibration. Observing after the ReLU means the calibrated grid covers only , roughly doubling resolution versus a symmetric pre-ReLU range.
- 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_qatbuilds this from a fused floatlucid.nn.intrinsic.ConvReLU3d;lucid.quantization.convertfolds it into the quantizedlucid.nn.quantized.ConvReLU3d.
Examples
>>> import lucid, lucid.nn as nn
>>> import lucid.quantization as Q
>>> import lucid.nn.qat as nnqat
>>> m = nn.Sequential(nn.Conv3d(3, 8, 3, padding=1), nn.ReLU())
>>> fused = Q.fuse_modules(m, [["0", "1"]]) # -> nni.ConvReLU3d marker
>>> qat = Q.prepare_qat(fused, Q.get_default_qat_qconfig_mapping())
>>> isinstance(qat[0], nnqat.ConvReLU3d)
True
>>> loss = (qat(lucid.randn(2, 3, 4, 4, 4)) ** 2).mean()
>>> loss.backward() # STE trains the fused float kernel
>>> qat.eval()
>>> qc = Q.convert(qat) # -> quantized.ConvReLU3d
>>> type(qc[0]).__name__
'ConvReLU3d'See Also
- lucid.nn.quantized.ConvReLU3d—The fused int8 inference conv
convertbakes into. - lucid.nn.intrinsic.ConvReLU3d—The plain-float fused marker this is built from.
- lucid.nn.qat.Conv3d—The un-fused QAT base class.
- lucid.quantization.fuse_modules_qat—Fuses
Conv3d+ReLUstraight to QAT.