Fused ConvNeXt V2 residual block for .nk CNN models. Matches the block used in Meta ConvNeXt V2 (including Atto-scale configs): depthwise 7×7 → LayerNorm → 4× MLP → GELU → GRN → project → residual.
Layer kind 8. Standalone LayerNorm2d (kind 11) is used for stem, stage downsample, and head norms in the full Atto backbone.
For input X with shape [H, W, C]:
- Depthwise conv — 7×7, stride 1, pad 3, per channel
- LayerNorm — normalize across
Cat each spatial position; scale + bias per channel - Pointwise expand — dense
C → 4Cat each pixel - GELU
- GRN —
γ * (x * Nx) + β + xwhereNx[c] = ||X_c||_2 / (mean_c(||X_c||_2) + ε) - Pointwise project — dense
4C → Cat each pixel - Residual — add input
X(Kernels::MatAddND)
Spatial size is unchanged (H, W constant).
Runtime: all sub-ops above (except the residual buffer copy in other blocks) go through Kernels:: via include/fused_kernel_ops.hpp, so CMSIS-NN / XNNPACK apply when enabled with reference fallback. GELU uses CMSIS-NN tanh on the inner polynomial when NN is linked; GRN uses reference / NetkitUtil.
| Field | Type | Notes |
|---|---|---|
kind |
uint8 |
8 |
channels |
uint32 |
Must match input channel count |
reserved |
uint32 |
0 |
eps |
float32 |
LayerNorm + GRN epsilon (default 1e-6) |
Catalog order: weight then bias for each pair.
| Pair | Weight shape | Bias shape |
|---|---|---|
| Depthwise | [C, 7, 7] |
[C] |
| LayerNorm | [C] |
[C] |
| Expand | [4C, C] |
[4C] |
| GRN | [4C] |
[4C] |
| Project | [C, 4C] |
[C] |
Total floats for channel count C: 98C + 4C² (e.g. C=2 → 162 floats).
models/convnextv2_atto_block.nk — 4×4×2 input, one block, one embedded TCAS case. Regenerate:
python tools/write_convnextv2_atto_block_fixture.pymodels/convnextv2_atto.nk — official Atto depths [2,2,6,2], dims [40,80,160,320], stem + four stages + head. Input 32×32×3 (divisible by 32), 24 layers. Regenerate:
python tools/write_convnextv2_atto_fixture.pyBuilder: python/netkit/convnextv2_atto.py (build_convnextv2_atto_arch). Uses layernorm2d (kind 11) between stem/stage convs and before the classifier pool.
Requires pip install -e "python[train]" (torch + timm):
python -m netkit pack --arch convnextv2_atto -o models/my_convnextv2_atto.nk --height 32 --width 32 --num-classes 10
# or
python tools/pack_convnextv2_atto_checkpoint.py -o models/my_convnextv2_atto.nkUses python/netkit/torch_backbone_pack.py to map timm convnextv2_atto weights into composite block tensors (LayerNorm2d, ConvNeXt V2 blocks, classifier head).
Parity: python/tests/test_torch_backbone_pack.py and test_torch_backbone_runtime_parity.py — see TESTING.md.
from netkit.arch_writer import write_nk_from_arch
arch = {
"network": "cnn",
"input": [H, W, C],
"layers": [{"type": "convnextv2_block", "channels": C, "eps": 1e-6}],
}Reference forward: netkit.reference_forward.forward_cnn.
- Kernel:
ConvNeXtV2Block::forward()insrc/convnextv2_block.cpp - Loader:
NkFormat::LayerKind::ConvNeXtV2Blockinsrc/nk_loader.cpp - Op registry:
NkConvNeXtV2BlockOpDescriptor
Scratch for the block (H*W*4*C + 4*C floats) is allocated from the load arena at init time.