Paper · 2022-09-29 · cs.CV, cs.LG
3D UX-Net: A Large Kernel Volumetric ConvNet Modernizing Hierarchical Transformer for Medical Image Segmentation
3D UX-Net is a pure 3D ConvNet for volumetric medical segmentation. It mimics Swin Transformer blocks with 7x7x7 depthwise convolutions and a per-channel 4x scaling step. Author-reported mean Dice: 0.874 on FeTA 2021 (SwinUNETR 0.867), 0.934 on FLARE 2021 (0.929), 0.900 on AMOS 2022 fine-tuning (0.880).
Semantic segmentationHierarchical vision transformerssegmentation3d-medical-imagingconvolutionlarge-kernel
arXivPDFCodeMarkdown11 min read
AI-compiled by claude-haiku-5-5 on 2026-11-05, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Medical scans in 3D, such as brain MRIs or abdominal CTs, need every voxel labelled by organ or tissue. The strongest recent models for this borrow from transformers, which look at the whole scan through attention. That works well but is heavy: many parameters and slow training.
This paper asks whether a plain convolutional network can behave like those transformers. Its trick is to use very large 3D filters, applied one channel at a time, so that each output sees a wide neighbourhood at low cost. It also copies a transformer habit of widening the channels inside each block and then narrowing them again, and it replaces some normalisation and activation layers with simpler ones. The result is a U-shaped network that the authors say matches or beats a transformer baseline on three public benchmarks while using fewer parameters. The gains are small, and the authors report them themselves.
TL;DR
3D UX-Net is a volumetric ConvNet for 3D medical image segmentation. Its encoder copies the block layout of the Swin Transformer: a large depthwise convolution in place of window attention, and a per-channel expand-and-compress step in place of the MLP. The authors report mean Dice gains over SwinUNETR on FeTA 2021, FLARE 2021 and AMOS 2022 fine-tuning, with fewer parameters but more FLOPs. Use it when you need a pure-CNN 3D segmenter that a transformer baseline can be compared against. The official code is public, but several training settings differ from the paper (see Paper vs code).
Problem
Hierarchical transformers such as Swin and SwinUNETR set the state of the art for 3D medical segmentation. Their advantage is attributed to a large receptive field from windowed self-attention and to a large parameter count §1. Global self-attention over dense 3D volumes is too expensive, so windowed variants are used §1. Existing 3D ConvNets rely on small kernels, and the few large-kernel 3D designs reported cost in FLOPs and speed §2. The paper's question is whether large-kernel depthwise convolutions can give the same receptive-field effect as shifted-window attention at lower parameter cost §1 §3. The assumptions are three public volumetric datasets, supervised training, and fixed 96×96×96 sub-volumes §5 tab. 4.
Method
The network is a U-shaped encoder-decoder. The encoder has four stages, each with two large-kernel blocks; the resolution halves between stages, from H/2 to H/16 §4.1. The decoder is a convolutional U-Net-style path with skip connections §4.2.
Three design choices come from the Swin Transformer block §3:
- Patch projection. A 7×7×7 convolution with stride 2 maps the input to 48 channels, in place of a linear patch embedding §4.1. The kernel is fixed at 7 in the code uxnet_encoder.py.
- Large-kernel depthwise convolution (DWC). Each channel is convolved with its own 7×7×7 kernel, starting from 7 and varied in ablations §3 §4.1. The paper argues this stands in for windowed attention §3 §7.
- Inverted bottleneck with depthwise convolutional scaling (DCS). Each channel is expanded to four times its width and compressed back by 1×1×1 operations that act on each channel independently. This adds no cross-channel mixing and keeps the parameter count down §3 §4.1.
The block recursion is written in eq. 1. In prose: each block applies a DWC with a residual connection, then a DCS with a residual connection, with layer normalisation (LN) before each. Stage-to-stage downsampling uses a 2×2×2 convolution with stride 2, which exchanges information across channels, in place of an MLP §4.1.
# One large-kernel block: two residual sub-layers [§4.1] [eq. 1]
# Input: z_prev (B, C, H, W, D)
# Output: z (B, C, H, W, D)
def ux_block(z_prev):
# Depthwise 7x7x7 convolution after LN, with residual [§3] [eq. 1]
hat_z = depthwise_conv_7(layer_norm(z_prev)) + z_prev
# Per-channel expand to 4C, then compress to C, with residual [§3] [eq. 1]
# (no channel mixing in either step) [§4.1]
z = channel_scale_4x(layer_norm(hat_z)) + hat_z
return zEncoder stages and downsampling:
# Encoder: four stages of two blocks each [§4.1]
# Channels per stage: 48, 96, 192, 384 [tab. 4]
def encoder(volume):
# Patch projection: 7x7x7 conv, stride 2, to 48 channels [§4.1]
x = conv3d_k7_stride2(volume)
for stage in range(4):
# Downsample by 2 with a 2x2x2 conv, stride 2, before stages 2-4 [§4.1]
if stage > 0:
x = conv3d_k2_stride2(x)
# Two large-kernel blocks per stage [§4.1]
x = ux_block(ux_block(x))
skips[stage] = x
return skipsDecoder:
# Decoder: U-shaped, convolutional [§4.2]
def decoder(skips, volume):
# Two post-normalised 3x3x3 convs with instance norm per stage [§4.2]
# Each stage upsamples with a transpose conv, concatenates the
# skip from the stage above, then applies a residual block [§4.2]
# Final: residual block with 1x1x1 conv and softmax [§4.2]
return probabilitiesThe paper describes encoder and decoder normalisation as instance normalisation in the decoder and LN in the encoder §4.2 §3.
Hyperparameters
Stated values come from tab. 4 and app. A.1 unless marked otherwise. "Code" shows what the official repository does; where it differs, see Paper vs code.
| Name | Value | Where | Notes |
|---|---|---|---|
| encoder stages | 4 (2 blocks each) | §4.1 tab. 4 | L = 8 layers total §4.1 |
| channels per stage | 48, 96, 192, 384 | tab. 4 main_train.py | |
| bottleneck hidden size | 768 | tab. 4 network_backbone.py | |
| depthwise kernel | 7×7×7 (start point) | §4.1 §6.3 | Ablation over 3 to 13 in tab. 3; code hard-codes 7 uxnet_encoder.py |
| patch size | 96×96×96 | tab. 4 app. A.1 | sub-volumes cropped at foreground |
| sub-volumes per subject, direct training | 2 | tab. 4 | code default 2 main_train.py |
| sub-volumes per subject, fine-tuning | 1 | tab. 4 | code default 2 for fine-tuning too main_finetune.py |
| batch size, direct training | 2 | tab. 4 | code default 1 main_train.py |
| batch size, fine-tuning | 1 | tab. 4 | code default 1 |
| training steps | 40000 | app. A.1 tab. 4 | code default main_train.py |
| optimiser | AdamW, β = (0.9, 0.999), ε = 1e-8 | tab. 4 | code uses PyTorch defaults for β and ε main_train.py |
| peak learning rate | 1e-4 | app. A.1 tab. 4 | main_train.py |
| LR scheduler | ReduceLROnPlateau, factor 0.9, patience 10 | tab. 4 | not applied: line is commented out main_train.py |
| weight decay | 0.08 | tab. 4 | not set in code; AdamW default applies main_train.py |
| dropout | "X" in table, meaning not explained | tab. 4 | no dropout in encoder network_backbone.py |
| stochastic depth | not stated | tab. 4 | code uses 0 main_train.py |
| layer-scale init | not stated | paper silent | code uses 1e-6 uxnet_encoder.py |
| random intensity shift | offset 0.1, probability 0.5 | tab. 4 | load_datasets_transforms.py |
| random scaling | 0.1 on x, y and z | tab. 4 | load_datasets_transforms.py |
| random rotation | −30° to +30° | tab. 4 | code: ±π/15 for FeTA, ±π/30 for FLARE and AMOS load_datasets_transforms.py load_datasets_transforms.py |
| foreground-cropped sampling | yes | tab. 4 | 1 positive and 1 negative crop per draw load_datasets_transforms.py |
| intensity window, FLARE and AMOS | −175 to 250 | app. A.1 | code uses −125 to 275 load_datasets_transforms.py |
| intensity window, FeTA | not stated | app. A.1 | code uses 0 to 1000 load_datasets_transforms.py |
| resampling, FLARE | not stated | paper silent | code: 1.0 × 1.0 × 1.2 mm load_datasets_transforms.py |
| resampling, AMOS | not stated | paper silent | code: 1.5 × 1.5 × 2.0 mm load_datasets_transforms.py |
| loss | Dice + cross-entropy, softmax | §4.2 main_train.py | |
| validation interval | not stated | paper silent | code: every 500 steps main_train.py |
| checkpoint selection | best validation Dice | paper silent | main_train.py |
| seed | not stated | paper silent | code: 0 main_train.py |
| hardware | RTX Quadro 5000 (FeTA, FLARE); RTX A6000 (AMOS) | app. A.1 | about 1, 10 and 7 minutes per epoch respectively app. A.1 |
| test-time sliding window | 96³ window, batch 4, overlap 0.5 default | paper silent | test_seg.py; README uses overlap 0.7 README.md |
Results
Author-reported. All numbers are mean Dice on the held-out test folds unless stated. Setup: five-fold cross-validation on FeTA 2021 and FLARE 2021 with training from scratch; AMOS 2022 is fine-tuned from the best FLARE 2021 fold §5 app. A.2.
FeTA 2021, seven tissues tab. 1:
| Method | Params | FLOPs | Mean Dice |
|---|---|---|---|
| nn-UNet | 31.2M | 743.3G | 0.870 |
| SwinUNETR | 62.2M | 328.4G | 0.867 |
| TransBTS | 31.6M | 110.4G | 0.868 |
| 3D UX-Net | 53.0M | 639.4G | 0.874 |
FLARE 2021, four organs tab. 1:
| Method | Mean Dice |
|---|---|
| nn-UNet | 0.926 |
| SwinUNETR | 0.929 |
| 3D UX-Net | 0.934 |
AMOS 2022 fine-tuning, fifteen organs tab. 2:
| Method | Mean Dice |
|---|---|
| nn-UNet | 0.878 |
| SwinUNETR | 0.880 |
| 3D UX-Net | 0.900 |
The paper marks the 3D UX-Net means with an asterisk (p < 0.01, Wilcoxon signed-rank test against all listed state-of-the-art methods) tab. 1 tab. 2.
Ablations tab. 3, FeTA / FLARE mean Dice:
| Variant | Params | FeTA | FLARE |
|---|---|---|---|
| SwinUNETR (reference) | 62.2M | 0.867 | 0.929 |
| standard conv. instead of depthwise | 186.9M | 0.875 | 0.937 |
| depthwise (default) | 53.0M | 0.874 | 0.934 |
| kernel 3×3×3 | 52.5M | 0.867 | 0.928 |
| kernel 5×5×5 | 52.7M | 0.869 | 0.931 |
| kernel 7×7×7 | 53.0M | 0.874 | 0.934 |
| kernel 9×9×9 | 53.6M | 0.870 | 0.934 |
| kernel 11×11×11 | 54.4M | 0.871 | 0.936 |
| kernel 13×13×13 | 55.7M | 0.871 | 0.938 |
| no MLP (no per-channel scaling) | 51.1M | 0.869 | 0.915 |
| MLP scaling | 56.3M | 0.872 | 0.933 |
| DCS 1×1×1 (default) | 53.0M | 0.874 | 0.934 |
nn-UNet structure with 3D UX-Net blocks tab. 6, FeTA / FLARE: plain 0.874 / 0.934 becomes 0.876 / 0.940 without deep supervision and 0.881 / 0.944 with it. The authors trained these at a learning rate of 0.002 and gave epoch counts instead of steps app. A.3.
Efficiency variant tab. 7: depth 2,2,8,2 with hidden size 384 has 32.1M parameters and 536.8G FLOPs, with mean Dice 0.873 (FeTA) and 0.932 (FLARE). Default: 53.0M, 639.4G, 0.874 and 0.934.
Qualitative figures: fig. 4 shows boundary and organ examples. fig. 3 shows validation Dice curves on the three datasets.
Paper vs code
Official code: github.com/MASILab/3DUX-Net at commit 14ea46b. The repository has no LICENSE file; the README says MIT README.md.
- Layer normalisation placement. Eq. 1 applies LN before the DWC and before the DCS, so each block has two LN layers eq. 1. The code applies a single LN after the DWC and none before it uxnet_encoder.py.
- Activation between the scaling layers. Eq. 1 shows no activation in DCS. The code puts GELU between the two per-channel 1×1×1 layers uxnet_encoder.py.
- Layer scale. The code multiplies each block output by a learnable per-channel scale initialised to 1e-6 uxnet_encoder.py uxnet_encoder.py. Eq. 1 has no such term.
- Kernel size is not configurable. The 7×7×7 depthwise kernel is written into the block uxnet_encoder.py. The kernel-size ablation in tab. 3 therefore needs code edits, not a flag.
- Learning-rate scheduler. tab. 4 lists ReduceLROnPlateau. In both training scripts the scheduler is commented out, so the learning rate is constant main_train.py main_finetune.py.
- Weight decay. tab. 4 gives 0.08. The AdamW call passes no weight decay main_train.py, so the PyTorch default applies.
- Batch size and crops. tab. 4 gives batch size 2 and 2 crops for direct training. The default batch size is 1 and the TRAINING.md commands use 1 main_train.py TRAINING.md. Fine-tuning uses 2 crops in the commands, but tab. 4 gives 1 TRAINING.md.
- Intensity window. app. A.1 gives −175 to 250 for FLARE and AMOS. The code uses −125 to 275 load_datasets_transforms.py load_datasets_transforms.py.
- Rotation range. tab. 4 gives ±30°. The code uses ±π/15 (about ±12°) for FeTA load_datasets_transforms.py and ±π/30 (about ±6°) for FLARE and AMOS load_datasets_transforms.py load_datasets_transforms.py.
- Validation metric. The paper's per-organ means exclude background, since the FeTA mean covers seven columns. The code's Dice metric includes the background channel main_train.py. Checkpoint selection therefore uses a different mean from the one reported in tab. 1.
- Inference overlap. The README evaluation command uses overlap 0.7 README.md. The script default is 0.5 test_seg.py. The paper does not state the setting.
- Five-fold splits. The paper reports five-fold cross-validation §5. The code reads
imagesTrandimagesValfolders load_datasets_transforms.py and does not implement the fold split.KFoldis imported but unused load_datasets_transforms.py. The split files are not in the repository. - Reported FLARE number. The README lists 0.936 for 3D UX-Net on FLARE 2021 "(latest)" README.md. The paper reports 0.934 tab. 1.
- Training-time metric. The training loop uses a sliding window with batch size 2 and no overlap argument main_train.py.
- Extra normalisation not in the paper. The code applies LN before each stage-to-stage downsampling uxnet_encoder.py. It also passes each stage output through a further channels-first LN before the decoder uses it uxnet_encoder.py. The paper describes neither.
- Other networks in the repo. The repository also contains baseline code for other networks. Only the UX-Net path was checked against the paper.
No discrepancies were found in the 7×7×7 stem, the 2×2×2 stride-2 downsampling, or the four stage widths of 48, 96, 192 and 384.
Pitfalls
- Compute is higher than the parameter count suggests. 3D UX-Net has fewer parameters than SwinUNETR (53.0M against 62.2M) but about twice the FLOPs (639.4G against 328.4G) tab. 1. The abstract reports only the parameter and accuracy gains. Training and inference time may be the real constraint.
- Per-organ means do not reproduce the printed AMOS average. The fifteen 3D UX-Net values in tab. 2 average to 0.898, not 0.900. The FLARE four-organ values average to 0.933, not 0.934. The 2.27% gain quoted in §6.2 is computed from 0.900. The paper does not say how the averages were formed.
- FeTA baseline value. The abstract puts SwinUNETR at 0.867 on FeTA. §6.1 quotes the FeTA improvement as 0.870 to 0.874. In tab. 1, 0.870 is nn-UNet, not SwinUNETR.
- FLARE case count. §5 states 511 CTs for FLARE 2021. tab. 5 totals 361 cases (272 + 69 + 20).
- Wrong figure references in the text. §6.1 cites Figure 2 for convergence curves and Figure 3 for qualitative results. In the compiled paper, Figure 3 shows validation curves and Figure 4 shows the qualitative examples fig. 3 fig. 4.
- Efficiency table versus text. app. A.4 says FLOPs fall to 536.1G and gives nn-UNet as "743.3M". tab. 7 shows 536.8G and 743.3G.
- Undefined learning rate in the nn-UNet comparison. app. A.3 says the "proposed" initial learning rate is 0.01, but tab. 4 gives 1e-4. The 0.002 rate and epoch counts in that section are not explained and do not match the step counts (40000 steps is stated as about 150 epochs for FLARE).
- Steps as epochs. app. A.3 equates 40000 steps with 150 epochs for FLARE at batch size 2. The number of epochs depends on how many crops are drawn per case, which the text does not give.
- Hyperparameter defaults hide the choices. The code uses one-crop-per-sample defaults and never sets the weight decay. Running it with defaults gives a different optimisation from tab. 4.
- Pretrained weights. AMOS fine-tuning requires FLARE-trained weights. The README links Google Drive files for the trained weights README.md README.md. The weights' licence is not stated in the repository.
- Dataset licences. The three challenge datasets carry their own terms. This card does not cover them.
- Rounding. Several printed means differ from the mean of their columns by 0.001 to 0.002 (for example SwinUNETR FeTA, 0.868 against 0.867 printed). Treat third-decimal comparisons between methods with care.
Lineage
- Builds on: Swin Transformer and its use in SwinUNETR §1 §3; ConvNeXt, whose large-kernel 2D depthwise design the paper adapts §2 §3; nn-UNet, used as a baseline and as a training recipe in app. A.3. The paper cites these without arXiv identifiers, and no cards for them exist yet.
- Compared against: SwinUNETR, nnFormer, UNETR, TransBTS, nn-UNet and 3D U-Net tab. 1 tab. 2.
- Superseded by: not known from content already in this wiki.
Source and license
Adapted from “3D UX-Net: A Large Kernel Volumetric ConvNet Modernizing Hierarchical Transformer for Medical Image Segmentation” by Ho Hin Lee, Shunxing Bao, Yuankai Huo, Bennett A. Landman (arXiv:2209.15076), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/MASILab/3DUX-Net at commit 14ea46b7b4c4 (MIT (per README; the repository has no LICENSE file)). Any quoted code is shown under that license.