Paper · 2023-07-18 · cs.CV
RepViT: Revisiting Mobile CNN From ViT Perspective
RepViT modernises MobileNetV3-L with design choices from lightweight ViTs: split token and channel mixers, a 2x-expansion FFN, a convolutional stem, deeper downsampling, cross-block SE, and re-parameterised depthwise convs. Author-reported: 80.0% ImageNet top-1 at 1.0 ms on iPhone 12 (RepViT-M1.0, distilled) and 83.7% at 2.3 ms (RepViT-M2.3).
Efficient vision backbonesConvolutional neural networkVision Transformer (ViT) backbonesKnowledge distillationefficient-visionmobile-inferencecnnreparameterization
arXivPDFCodeMarkdown11 min read
AI-compiled by claude-haiku-5-5 on 2026-10-19, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Small image models for phones come in two families: convolutional networks (CNNs) and Vision Transformers (ViTs). Recently, small ViTs have matched or beaten small CNNs on accuracy and speed on phones. The authors ask whether the design choices behind those ViTs can be brought back into a plain CNN.
They start from MobileNetV3, a well-known small CNN, and change it step by step. Each change borrows a layout choice from the small ViTs: the spatial mixing and the per-pixel mixing become separate steps, and the network gets a different stem, a deeper downsampling step and a simpler classifier. The result, RepViT, is a family of pure CNNs that the authors report beat small ViTs on latency and accuracy on a phone.
The main point for an engineer: how many operations a model has does not predict its speed on a phone well, so the paper measures latency on the device.
TL;DR
- RepViT is a family of pure CNNs built from a MobileNetV3-L starting point by adopting lightweight-ViT layout choices §3.
- The core block separates the spatial token mixer (a re-parameterised depthwise conv) from the channel mixer (an FFN with expansion 2) §3.2.
- Author-reported ImageNet-1K top-1 with distillation: 78.7% (M0.9, 0.9 ms) to 83.7% (M2.3, 2.3 ms) on iPhone 12 with Core ML tab. 1.
- Dense prediction and SAM results are also reported, with latencies on phone and laptop hardware §4.2 §4.3.
- Several architecture details exist only in the supplementary material (not part of our source); the released code fills most of them in (see Paper vs code).
Problem
Lightweight ViTs beat lightweight CNNs on accuracy and latency on mobile devices, and the usual explanation is global self-attention §1 §2. The architectural differences between lightweight ViTs and lightweight CNNs, in the block structure and in the macro and micro design, were not examined closely §1. FLOPs and model size correlate poorly with measured on-device latency §3.1, so the paper measures latency directly.
Assumptions: all models are trained on ImageNet-1K at 224x224 §4.1; latency is measured on an iPhone 12 (iOS 16) compiled with Core ML Tools at batch size 1 §3.1 §4.1.
Method
The paper starts from MobileNetV3-L and changes it in five groups, each cited to the section where it is introduced §3.2–§3.5:
- Training recipe: align the baseline with the lightweight-ViT recipe (DeiT style, with distillation excluded for now) §3.1.
- Block design: split token mixer and channel mixer, re-parameterise the depthwise conv, expansion ratio 2, widths doubling per stage §3.2.
- Macro design: two-conv stem, deeper downsampling, a simple classifier, stage ratio change §3.3.
- Micro design: 3x3 kernels only, cross-block SE placement §3.4.
- Network family: RepViT-M0.9, M1.0, M1.1, M1.5, M2.3, where the suffix is the latency in ms on iPhone 12 §3.5.
The paper gives no numbered equations and no numbered algorithms. The pseudocode below is our reading of the layout in the text, checked against the released code repvit.py.
# RepViT block, stride 1 [§3.2]
# Input x: (B, C, H, W); channel mixer expansion 2 [§3.2]
def repvit_block_stride1(x, use_se):
# Token mixer: depthwise 3x3 and depthwise 1x1 branches plus the
# identity, summed then batch-normalised [§3.2]
# [code: model/repvit.py#L83-L92]
mixed = batch_norm(dw_3x3(x) + dw_1x1(x) + x)
# Optional squeeze-and-excitation, placed after the token mixer [§3.4]
# [code: model/repvit.py#L149]
if use_se:
mixed = squeeze_excite(mixed)
# Channel mixer: pointwise expand to 2C, GELU, pointwise back to C,
# residual add [§3.2] [code: model/repvit.py#L151-L157]
hidden = gelu(conv1x1_bn(mixed, out_channels=2 * C))
return mixed + conv1x1_bn(hidden, out_channels=C, init_gamma=0)# RepViT block, stride 2 (downsampling) [§3.3]
# Depthwise stride-2 conv changes resolution, then a 1x1 conv changes channels
def repvit_block_stride2(x, out_channels, use_se):
# Depthwise 3x3, stride 2, with BN [§3.3]
# [code: model/repvit.py#L133-L136]
x = batch_norm(dw_3x3_stride2(x))
# Optional squeeze-and-excitation [code: model/repvit.py#L135]
if use_se:
x = squeeze_excite(x)
# Pointwise 1x1 conv sets the new channel count [§3.3]
x = conv1x1_bn(x, out_channels=out_channels)
# FFN with expansion 2 after the 1x1, residual [§3.3]
# [code: model/repvit.py#L138-L143]
hidden = gelu(conv1x1_bn(x, out_channels=2 * out_channels))
return x + conv1x1_bn(hidden, out_channels=out_channels, init_gamma=0)# RepViT network [§3.3] [§3.5] [code: model/repvit.py#L218-L243]
# image: (B, 3, 224, 224); configs: one row per block (kernel, expansion,
# channels, SE flag, activation flag, stride)
def repvit(image, configs, channels):
# Stem: two stride-2 3x3 convs with GELU between [§3.3]
# first conv has channels/2 filters (24 for M0.9) [§3.3]
x = conv3x3_bn_stride2(image, channels // 2)
x = gelu(x)
x = conv3x3_bn_stride2(x, channels)
# Stacked blocks, stride-1 blocks and stride-2 blocks [§3.2] [§3.3]
for block_config in configs:
x = repvit_block(x, block_config)
# Head: global average pool, then batch norm and linear [§3.3]
# (the paper says linear only; the code adds a BN first)
# [code: model/repvit.py#L163-L186]
pooled = global_average_pool(x)
return bn_linear(pooled)Re-parameterisation: during training the depthwise branches and the identity are separate; at inference they fold into one 3x3 depthwise conv §3.2 repvit.py. The paper says the skip connection cost is removed at inference §3.2.
Distillation (used for the headline numbers): the student gets two heads, one for the labels and one for the teacher. The loss mixes both with weight alpha losses.py. The paper states the teacher only §4.1.
L = (1 - \alpha)\, L_{\text{base}} + \alpha\, L_{\text{kd}} losses.py, with alpha = 0.5 and, for the hard variant, L_kd = cross-entropy against the teacher's argmax losses.py main.py. This formula is not in the paper.
Hyperparameters
Values marked "paper" are stated in the paper. Values marked "code" are the defaults of the released training script; the paper defers the full recipe to its supplementary material, which is not in our source. Code values were not confirmed against the paper.
| Name | Value | Where | Notes |
|---|---|---|---|
| input resolution | 224 x 224, train and test | §4.1 | paper |
| epochs | 300 or 450 | §4.1 tab. 1 | paper; 450 is the second column of tab. 1 |
| optimizer | AdamW | §3.1 main.py | paper states AdamW for the DeiT-style recipe; code default |
| LR schedule | cosine | §3.1 main.py | paper; code default |
| base learning rate | 1e-3, scaled as lr x batch x GPUs / 512 | main.py main.py | code only; not stated in paper |
| warmup | 5 epochs | main.py | code only |
| minimum LR | 1e-5 | main.py | code only |
| weight decay | 0.025 | main.py | code only |
| batch size | 256 (per process) | main.py | code only; global size under 8 GPUs not stated |
| EMA of weights | on, decay 0.99996 | main.py | code only |
| gradient clipping | AGC, 0.02 | main.py | code only |
| label smoothing | 0.1 | main.py | code only; paper names label smoothing §3.1 |
| mixup / cutmix alpha | 0.8 / 1.0; switch prob 0.5 | main.py | code only |
| random erasing | p = 0.25, pixel mode | main.py | code only |
| auto-augment | rand-m9-mstd0.5-inc1 | main.py | code only; paper names auto-augment §3.1 |
| colour jitter | 0.4 | main.py | code only |
| interpolation | bicubic | main.py | code only |
| distillation teacher | RegNetY-16GF, 82.9% top-1 | §4.1 | paper |
| teacher in code | regnety_160 | main.py | code default; not the same name as the paper |
| distillation type | hard, alpha 0.5 | main.py | code only; paper does not say hard or soft |
| stem | 3x3 stride-2 convs, 24 and 48 filters (M0.9) | §3.3 repvit.py | paper and code agree |
| channel-mixer expansion | 2 | §3.2 | paper; code uses t = 2 in every config |
| stage widths (M0.9) | 48, 96, 192, 384 | §3.2 repvit.py | paper and code agree |
| kernel size | 3x3 in all blocks | §3.4 | paper |
| SE reduction ratio | 0.25 | repvit.py repvit.py | code only; not stated in paper |
| stage ratio | 1:1:7:1; depth 2:2:14:2 | §3.3 | paper; conflicts with code, see Paper vs code |
| latency device | iPhone 12, iOS 16, Core ML Tools, batch 1 | §3.1 §4.1 | paper |
| throughput device | RTX 3090, largest power-of-two batch that fits | tab. 1 caption | paper |
| SAM encoder training | RepViT-M2.3, 8 epochs, 1% of SA-1B | §4.2 | paper |
| detection / segmentation | Mask R-CNN on COCO 2017; Semantic FPN on ADE20K | §4.3 | paper; schedules not stated in the paper body |
Model configs (per block: kernel, expansion, channels, SE, activation, stride) are in repvit.py for M0.9, repvit.py for M1.0, repvit.py for M1.1, repvit.py for M1.5 and repvit.py for M2.3. The paper refers to its supplementary for these tables §3.5; the supplementary is not in our source.
Results
Author-reported. ImageNet-1K, 224 x 224, latency on iPhone 12 with Core ML §4.1. Table 1 reports the models trained with distillation; Table 2 reports them without §4.1.
| Model | Latency (ms) | Top-1 (300 / 450 ep) | Params (M) | GMACs | Source |
|---|---|---|---|---|---|
| RepViT-M0.9 | 0.9 | 78.7 / 79.1 | 5.1 | 0.8 | tab. 1 |
| RepViT-M1.0 | 1.0 | 80.0 / 80.3 | 6.8 | 1.1 | tab. 1 |
| RepViT-M1.1 | 1.1 | 80.7 / 81.2 | 8.2 | 1.3 | tab. 1 |
| RepViT-M1.5 | 1.5 | 82.3 / 82.5 | 14.0 | 2.3 | tab. 1 |
| RepViT-M2.3 | 2.3 | 83.3 / 83.7 | 22.9 | 4.5 | tab. 1 |
| FastViT-T8 (baseline) | 0.9 | 76.7 (300) | 3.6 | 0.7 | tab. 1 |
| EfficientFormerV2-S0 (baseline) | 0.9 | 75.7 / 76.2 | 3.5 | 0.4 | tab. 1 |
| EfficientFormerV2-S1 (baseline) | 1.1 | 79.0 / 79.7 | 6.1 | 0.7 | tab. 1 |
| EfficientFormerV2-S2 (baseline) | 1.6 | 81.6 / 82.0 | 12.6 | 1.3 | tab. 1 |
| EfficientFormerV2-L (baseline) | 2.7 | 83.3 / 83.5 | 26.1 | 2.6 | tab. 1 |
Without distillation (300 epochs) tab. 2: RepViT-M0.9 77.4% at 0.9 ms, M1.0 78.6% at 1.0 ms, M1.1 79.4% at 1.1 ms, M1.5 81.2% at 1.5 ms, M2.3 82.5% at 2.3 ms. The paper reports, at similar latency, MobileOne-S1 75.9% (0.9 ms), FastViT-T8 75.6% (0.9 ms) and EfficientFormerV2-S0 73.7% (0.9 ms) tab. 2.
Step-by-step ablation on ImageNet-1K, each row adding one change to the previous one. Values are as stated in the text; the row for the classifier gives only the drop §3.1–§3.4 fig. 2:
| Change | Top-1 (%) | Latency (ms) | Source |
|---|---|---|---|
| MobileNetV3-L with the aligned recipe | 71.5 | 1.01 | §3.1 |
| RepViT block (split mixers, reparam DW) | 68.3 | 0.81 | §3.2 |
| Expansion 2 and doubling widths | 73.5 | 0.89 | §3.2 |
| Early convolutions as stem | 73.9 | 0.86 | §3.3 |
| Deeper downsampling | 75.4 | 0.96 | §3.3 |
| Simple classifier | -0.6 (from previous) | 0.77 | §3.3 |
| Stage ratio 1:1:7:1, depth 2:2:14:2 | 76.9 | 0.91 | §3.3 |
| 3x3 convs only | 76.9 | 0.89 | §3.4 |
| Cross-block SE placement (= RepViT) | 77.4 | 0.87 | §3.4 |
Ablations on the RepViT block tab. 6 tab. 7:
| Setting | Result | Source |
|---|---|---|
| Without structural re-parameterisation, M0.9 / M1.5 / M2.3 | 78.47 / 82.09 / 83.10 % | tab. 6 |
| With structural re-parameterisation | 78.74 / 82.29 / 83.30 % | tab. 6 |
| SE removed, M0.9 top-1 / latency | 77.92% / 0.83 ms | tab. 7 |
| SE in every block, M0.9 top-1 / latency | 78.75% / 0.92 ms | tab. 7 |
| SE cross-block (ours), M0.9 top-1 / latency | 78.74% / 0.87 ms | tab. 7 |
Downstream, backbone latency at 512 x 512 on iPhone 12 tab. 5: RepViT-M1.1 (4.9 ms) gives 39.8 AP-box, 37.2 AP-mask and 40.6 mIoU, against EfficientFormer-L1 (5.4 ms) at 37.9, 35.4 and 38.9. RepViT-M2.3 (9.9 ms, with 450-epoch ImageNet init) gives 44.6, 40.8 and 46.1, against EfficientFormerV2-L (18.2 ms) at 44.7, 40.4 and 45.2 tab. 5.
RepViT-SAM (RepViT-M2.3 as the SAM image encoder) §4.2 tab. 3 tab. 4:
| Setting | RepViT-SAM | MobileSAM | ViT-B-SAM | Source |
|---|---|---|---|---|
| Encoder latency, iPhone 12 (1024 x 1024) | 48.9 ms | OOM | OOM | tab. 3 |
| Encoder latency, MacBook M1 Pro | 44.8 ms | 482.2 ms | 6249.5 ms | tab. 3 |
| Zero-shot edge ODS / OIS / AP | .764 / .786 / .773 | .756 / .768 / .746 | .743 / .764 / .726 | tab. 4 |
| Zero-shot instance AP / SegInW mean AP | 44.4 / 46.1 | 42.7 / 43.9 | 42.5 / 44.8 | tab. 4 |
The paper calls RepViT-SAM "nearly 10x faster" than MobileSAM on MacBook M1 Pro §4.2; the table ratio is 482.2 / 44.8 = 10.8.
Paper vs code
Repository: the link is in the abstract, so no further search was needed. The
owner is THU-MIG, and the README lists the paper's five authors. Code read at
commit
298f42075eda5d2e6102559fad260c970769d34e. The main code paths are
model/repvit.py, main.py, losses.py, logs/ and README.md. The
detection, segmentation and SAM sub-projects were not checked against the
paper in this card.
- Stage depth. Paper: stage ratio 1:1:7:1, depth 2:2:14:2 §3.3. Code: the M0.9 config has 3, 4, 16 and 3 blocks per stage, where each stage after the first starts with its stride-2 block repvit.py. How the paper counts depth is not stated, so this is unresolved.
- Head. Paper: global average pooling and a linear layer §3.3. Code: a BatchNorm1d before the linear layer repvit.py. For the distillation model, the two heads' logits are averaged at evaluation repvit.py.
- SE placement. Paper: SE in the 1st, 3rd, 5th... block of each stage §3.4. Code: the M1.5 first stage has SE on blocks 1 and 3 [code: model/repvit.py#L391-L395], which matches; the M0.9 first stage has SE on block 1 only repvit.py, which does not.
- Activation flag. The config's HS column selects between two identical
nn.GELU()calls, so it has no effect repvit.py [code: model/repvit.py#L154]. The paper says GELU is used throughout §3.1. - Distillation. Paper: teacher RegNetY-16GF §4.1. Code default: regnety_160 main.py. Code also uses hard-label distillation [code: main.py#L137-L140] losses.py; the paper does not say which.
- Learning rate. Paper: not stated. Code: 1e-3 scaled by batch x GPUs / 512 main.py. The shipped 300-epoch M0.9 log peaks at about 3.0e-3 repvit_m0_9_distill_300e.txt. Under the scaling rule that corresponds to 6 GPUs, not the 8 in the README README.md — the reason is not stated.
- Reported accuracy. Table 1 gives 78.7% for M0.9 at 300 epochs tab. 1. The shipped log's final evaluation (epoch 299) gives 78.58% [code: logs/repvit_m0_9_distill_300e.txt#L300].
- Parameter count. Table 1 gives 5.1M for M0.9 tab. 1. The shipped log reports n_parameters = 5,489,328 for the training model, which includes the distillation head repvit_m0_9_distill_300e.txt. The 5.1M figure is presumably the fused inference model; the paper does not say.
- Extra variant. The code defines RepViT-M0.6, which is not in this paper repvit.py. The README reports it separately README.md.
Pitfalls
- Missing supplementary. The architecture tables and full training details are in the supplementary material §3.5, which is not in our source. The configs and recipe here come from the code, so they carry the code's status, not the paper's.
- Latency is device- and toolchain-specific. Numbers come from Core ML on iPhone 12 with iOS 16 and Xcode's tool §3.1 README.md. Other devices, runtimes or Core ML versions may rank models differently.
- Throughput is a separate metric. Table 1 throughput uses an RTX 3090 with the largest power-of-two batch that fits tab. 1 caption. It is not latency.
- Distillation changes the headline. Table 1 numbers use distillation from a RegNet teacher. Table 2 gives the no-distillation numbers, which are 1 to 2 points lower for the same model tab. 2.
- Re-parameterisation is a deployment step. The training form has separate
depthwise branches and BN; the inference form is a single depthwise conv. The
latency numbers assume the inference form §3.2. The code has
fuse()methods for it repvit.py repvit.py. - Last FFN norm starts at zero. The last BN of each FFN is initialised to zero, so each residual branch starts as identity repvit.py repvit.py. The paper does not mention this.
- Ambiguous SE pattern. "1st, 3rd, 5th" is the paper's wording §3.4, but the released configs vary by variant and stage. Copy the config table from the code, not the rule in the text.
- Speed claims are relative. The "10x" SAM claim compares against MobileSAM on a laptop §4.2. On a phone, only RepViT-SAM ran at all, since the other two returned OOM tab. 3.
- Ablation noise. Re-parameterisation gains are 0.1 to 0.3 points on single runs tab. 6. No variance across seeds is reported.
Lineage
- Builds on: MobileNetV3-L as the starting point §3; the separated token and channel mixers from MetaFormer-style designs §3.2; structural re-parameterisation from RepVGG §3.2. None of these has a card in this wiki.
- Compared against: EfficientFormerV2 (in this wiki) and EfficientFormer, FastViT, MobileOne and MobileSAM, which have no cards tab. 1 tab. 3.
- Superseded by: none known from content in this wiki.
Source and license
Adapted from “RepViT: Revisiting Mobile CNN From ViT Perspective” by Ao Wang, Hui Chen, Zijia Lin, Jungong Han, Guiguang Ding (arXiv:2307.09283), 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/THU-MIG/RepViT at commit 298f42075eda (Apache-2.0). Any quoted code is shown under that license.