Fused ResNet BasicBlock used in ResNet-18 and ResNet-34 (not the bottleneck block in ResNet-50+).
Layer kind 10. A full ResNet-18 backbone (stem + 8 blocks + classifier) can be expressed in one .nk file with kMaxLayers=100.
Matches the standard ResNet BasicBlock (inference BN folded into scale/bias):
- Conv 3×3 —
in_channels → out_channels, strides, pad 1 - BatchNorm + ReLU
- Conv 3×3 —
out_channels → out_channels, stride 1, pad 1 - BatchNorm
- Residual — identity when
stride == 1andin_channels == out_channels; else 1×1 conv + BN projection with strides - ReLU
Runtime: conv uses Conv2D::forward → Kernels::; batch norm, ReLU, and residual merge use fused_kernel_ops.hpp → Kernels:: (CMSIS when enabled, reference fallback otherwise). Skip/residual is MatAddND — no separate skip kernel.
| Field | Type |
|---|---|
in_channels |
uint32 |
out_channels |
uint32 |
stride |
uint8 (1 or 2) |
reserved |
3 bytes |
Identity shortcut (stride=1, in == out):
- conv1
[out, 3, 3, in]+ bias[out]; BN scale + beta[out]each - conv2
[out, 3, 3, out]+ bias; BN scale + beta
Projection shortcut (otherwise), append:
- shortcut
[out, 1, 1, in]+ bias; BN scale + beta
models/resnet18_basic_block.nk — 4×4×4 input, identity BasicBlock, one TCAS case.
python tools/write_resnet18_basic_block_fixture.pymodels/resnet18.nk — stem (7×7 conv, ReLU, max pool) + 8 BasicBlocks + avg pool + dense head. Input 56×56×3, 13 layers, ~11.2M weights. Regenerate:
python tools/write_resnet18_fixture.pyBuilder: python/netkit/resnet18.py (build_resnet18_arch). Embedded TCAS tolerance 1e-4 (deep-network float accumulation).
Packager fusion: expand_resnet_basic_block_to_linear() + fuse_composite_blocks() in python/netkit/nk_fuse.py roundtrip the composite block from a linear conv/bn stack (same pattern as UIB). ONNX import with --no-fuse emits primitives; default convert optimize runs packager fuse — see models/import_resnet_basic_block.nk.
python -m netkit pack --arch resnet18 -o models/my_resnet18.nk --height 56 --width 56 --num-classes 10
# or
python tools/pack_resnet18_checkpoint.py -o models/my_resnet18.nkUses python/netkit/torch_backbone_pack.py to fold BatchNorm and map timm resnet18 weights into composite BasicBlock tensors.
Parity: python/tests/test_torch_backbone_pack.py (NumPy reference vs timm) and test_torch_backbone_runtime_parity.py (C++ tools/nk_infer vs timm) — see TESTING.md.
arch = {
"network": "cnn",
"input": [H, W, C_in],
"layers": [{
"type": "resnet_basic_block",
"in_channels": C_in,
"out_channels": C_out,
"stride": 1,
}],
}- Kernel:
ResNetBasicBlock::forward()insrc/resnet_basic_block.cpp - Loader:
NkFormat::LayerKind::ResNetBasicBlock - Op registry:
NkResNetBasicBlockOpDescriptor