Fused float Conv3d + ReLU marker — a plain-float lucid.nn.Sequential.
A thin lucid.nn.Sequential of [conv, relu] emitted by
lucid.quantization.fuse_modules. It runs as plain float — it carries no
observers and no fake-quant, and computing through it is identical to running the conv
then the ReLU. Its whole purpose is structural: by packaging the two ops as one unit
it tells the quantization pipeline to place a single activation observer after the
ReLU rather than between the conv and the ReLU.
Why fuse for quantization. If an observer sat on the raw conv output it would calibrate a symmetric range that includes values the ReLU is about to discard, wasting half the int8 codes on negatives that never reach inference. Observing the post-ReLU range instead lets the eventual quantized layer pick a grid over , roughly doubling resolution. Fusion also drops the intermediate float tensor between conv and ReLU, so the converted kernel does conv → ReLU → requantize in one step.
Where it sits. This marker is the hand-off point between float and quantized worlds.
lucid.quantization.prepare_qat replaces it with a trainable
lucid.nn.qat.ConvReLU3d (weight + post-ReLU fake-quant under an STE), and
lucid.quantization.convert maps it — or the QAT layer it became — to a fused
inference lucid.nn.quantized.ConvReLU3d.
As a plain-float module it simply computes, with the 3-D cross-correlation:
Downstream, once an observer is attached, that fused output is fake-quantized under a straight-through estimator — rounded in the forward pass, identity in the backward:
Parameters
Notes
- Structural tag only. It carries no quantization state — no
weight_fake_quant, noactivation_post_process. Those are attached later bylucid.quantization.prepare_qat(or bylucid.quantization.preparefor the post-training path). - Post-ReLU observer placement. The single reason to fuse is so the downstream activation observer sees the non-negative post-ReLU range.
- Both directions are wired for you.
lucid.quantization.fuse_modulesemits this marker in;lucid.quantization.prepare_qat/lucid.quantization.convertmap it out to the QAT / quantized fused layer.
Examples
>>> import lucid, lucid.nn as nn
>>> import lucid.quantization as Q
>>> import lucid.nn.intrinsic as nni
>>> m = nn.Sequential(nn.Conv3d(3, 8, 3, padding=1), nn.ReLU())
>>> fused = Q.fuse_modules(m, [["0", "1"]])
>>> isinstance(fused[0], nni.ConvReLU3d) # [conv, relu] collapsed to one marker
True
>>> fused(lucid.randn(2, 3, 4, 4, 4)).shape # runs as plain float
(2, 8, 4, 4, 4)See Also
- lucid.nn.qat.ConvReLU3d—The trainable QAT layer
prepare_qatswaps this for. - lucid.nn.quantized.ConvReLU3d—The fused int8 inference conv
convertmaps this to. - lucid.quantization.fuse_modules—Emits this marker from
[conv, relu]. - lucid.quantization.fuse_modules_qat—Fuses straight to the QAT layer.