A convolution-based vision foundation model that matches and beats large ViTs — by replacing the rigid 3×3 kernel with DCNv3, a learnable operator that samples where the content is and weights what it finds. Scaled to ~1B parameters, it set new records on COCO detection, ADE20K segmentation, and Places365.
You're building a detector. In the image is a bicycle, photographed from the side. A bicycle is mostly empty space — two thin wheel rims, a frame, handlebars — wrapped around a large rectangular hole. To recognize "wheel," a neuron must gather evidence from a thin curved ring of pixels. To recognize "bicycle," a neuron higher up must connect the front wheel to the back wheel, which might be 200 pixels apart.
A standard 3×3 convolution cannot do either gracefully. Its receptive field is a fixed 3×3 grid of adjacent pixels. It samples the same nine offsets — (-1,-1), (-1,0), … (1,1) — at every spatial location, regardless of what the image contains. Over a thin curved rim, most of those nine samples land on background. To connect two wheels 200 pixels apart, you must stack dozens of 3×3 layers until the receptive field finally grows large enough — and even then the connection is indirect, diluted through many intermediate features.
This is the central tension the paper opens with. In 2020–2022, Vision Transformers (ViTs) had pulled ahead of CNNs on large-scale recognition. The community's explanation was that ViTs enjoy two properties convolutions lack:
InternImage's thesis is a direct rebuttal: you do not need self-attention to get those two properties. A convolution can have both — if you make the sampling locations learnable (long range) and the aggregation weights input-dependent (adaptive). The operator that does this is DCNv3 (Deformable Convolution v3), and an entire foundation-model backbone built from it is InternImage.
Left: a standard 3×3 conv samples the same fixed grid everywhere — over a thin diagonal edge, most taps hit background. Right: a deformable conv can move its taps onto the structure. Drag the slider to bend the sampling pattern toward the edge and watch how many taps land on signal vs. background.
| Property | 3×3 Conv | Self-Attention (ViT) | DCNv3 |
|---|---|---|---|
| Receptive field | Fixed 3×3, grows only by depth | Global from layer 1 | Long-range, learned per location |
| Sampling locations | Fixed integer grid | All positions (dense) | Learned fractional offsets |
| Aggregation weights | Fixed kernel (input-independent) | Input-dependent (Q·K softmax) | Input-dependent modulation |
| Cost in tokens | O(N) — linear | O(N²) — quadratic | O(N) — linear |
| Inductive bias | Strong locality & translation | Weak (learned from data) | Locality + learned flexibility |
K = 3×3 = 9 sampled points (not all N positions like attention), yet those 9 points can be anywhere and are weighted by content. That is the whole paper in a sentence; the next nine chapters make it precise.Why does this matter beyond beating ViT on a leaderboard? Because detection and segmentation backbones are deployed at enormous scale, and the O(N²) memory of attention is brutal at high resolution. A 2048×2048 input has >4 million tokens at stride 4; the full attention matrix is unthinkable. A linear-cost operator that still captures long-range structure is the practical sweet spot — and InternImage demonstrates it scales cleanly to ~1 billion parameters (InternImage-H), reaching 65.4 AP on COCO test-dev detection, 62.9 mIoU on ADE20K segmentation, and 61.2% top-1 on Places365 at the time of publication.
Let's build the operator from the ground up, starting with where ordinary convolution comes from.
Write a standard convolution as a sum over a fixed set of offsets. For an output location p0 and a 3×3 kernel, define the grid R = {(-1,-1), (-1,0), …, (1,1)} — nine offsets. With kernel weights wk and input feature map x:
Here K = 9, pk ranges over R, and wk is a learned weight (one per kernel tap, per output channel). Two things about this equation are frozen: the offsets pk are integers fixed at every location, and the weights wk are the same for every image.
Deformable convolution v1 (Dai et al., 2017) unfroze the first frozen thing: it added a learned offset Δpk to each tap, predicted from the input by a small offset-network:
Now each tap can move. Because Δpk is generally fractional, x(·) at a non-integer location is read by bilinear interpolation (Chapter 2). DCNv2 (Zhu et al., 2019) unfroze a second thing by adding a per-tap scalar modulation mk ∈ [0,1], letting the operator turn a tap down or off:
InternImage's DCNv3 makes three more changes — and the cleanest way to see them is to notice what is still wrong with DCNv2 at scale. We'll preview them here and derive each carefully:
Take a tiny 1-D signal x = [0, 0, 9, 0, 0] (a spike at index 2), and a single output at p0 = 0 with one tap (K=1, w=1). A rigid conv with offset 0 reads x(0) = 0 — it completely misses the spike. Give the tap a learned offset Δp = +2: it now reads x(0 + 0 + 2) = x(2) = 9. The same weight, the same compute — but by moving the sample, the operator captured the signal a rigid kernel was blind to. That single degree of freedom is the entire idea; the rest is bookkeeping for fractional offsets, channels, and stability.
Click through the four operators. Watch which terms are fixed (gray) and which are learned/input-dependent (colored), and how the 3×3 tap cloud changes from a rigid grid to a content-shaped, weighted constellation.
y(p₀) = ∑k wk mk x(p₀ + pk + Δpk), which quantities are input-dependent (recomputed per image/location) rather than fixed parameters?An offset Δpk is a real-valued 2-vector, so p0 + pk + Δpk almost never lands on an integer pixel. We need to read x at a fractional location, and we need it differentiable so gradients can flow back to the offset network. The answer is bilinear interpolation, and deriving it by hand is the most important skill in this whole paper.
Let the target location be p = (px, py), fractional. It sits inside a unit square whose four integer corners are the floor and ceil of each coordinate. Write x0 = ⌊px⌋, y0 = ⌊py⌋, x1 = x0+1, y1 = y0+1, and the fractional parts a = px − x0, b = py − y0 (both in [0,1)). The four corner values are the actual stored pixels. Bilinear interpolation is a weighted average where each corner's weight is the area of the opposite sub-rectangle:
Read it as: the closer p is to a corner (small distance → the opposite area is large), the more that corner contributes. The four weights are non-negative and sum to 1 — (1−a)(1−b) + a(1−b) + (1−a)b + ab = (1−a+a)(1−b+b) = 1. It is a convex combination, so x(p) always lies in the convex hull of the four corners (no overshoot).
Suppose the four corners of the unit cell are
and we want the value at p = (0.25, 0.75). Then a = 0.25, b = 0.75, so 1−a = 0.75, 1−b = 0.25. Plug in term by term:
Sum = 1.875 + 1.25 + 16.875 + 7.5 = 27.5. Sanity check: p is near the top-left region (high y, low x), so the value should lean toward x(0,1)=30 and x(1,1)=40 — and 27.5 does. The weights summed to 0.1875+0.0625+0.5625+0.1875 = 1.0, as required.
Each corner weight is a smooth (piecewise-linear) function of a and b, hence of px and py, hence of the predicted offset Δp. So ∂x(p)/∂Δp exists almost everywhere — the offset network receives a gradient that says "shift the sample this way to increase the loss-reducing signal." This is the hinge that lets the network learn where to look by gradient descent. A nearest-neighbor read would be piecewise-constant (zero gradient everywhere), and the offsets could never train.
Drag the sample point inside the unit cell. The four corner contributions update live; each corner's bar is the area of the opposite rectangle times that corner's value. The interpolated read is the sum — always inside [min, max] of the corners.
python # Bilinear read of feature map x at one fractional point p=(px,py) import numpy as np def bilinear(x, px, py): H, W = x.shape x0, y0 = int(np.floor(px)), int(np.floor(py)) x1, y1 = x0 + 1, y0 + 1 a, b = px - x0, py - y0 # fractional parts def get(yy, xx): # zero-pad out of bounds if 0 <= yy < H and 0 <= xx < W: return x[yy, xx] return 0.0 return ((1-a)*(1-b)*get(y0,x0) + a*(1-b)*get(y0,x1) + (1-a)*b*get(y1,x0) + a*b*get(y1,x1)) x = np.array([[10,20],[30,40]], dtype=float) print(bilinear(x, 0.25, 0.75)) # 27.5 (matches the hand calc)
Now we assemble the full DCNv3 equation. With G groups (we cover groups in Chapter 4; here read G=1 for clarity), K sampling points, depthwise weights and softmax-normalized modulations, the operator computing one output location p0 is:
Compare this to DCNv2 (Chapter 1) and read off the three deliberate differences DCNv3 introduces. Each is a specific engineering decision with a reason:
| Term | DCNv2 | DCNv3 | Why the change |
|---|---|---|---|
| Weight w | Dense wk of shape [Cin, Cout] per tap | Depthwise wg (shared across taps within a group) + a separate pointwise 1×1 | Decouples spatial mixing (depthwise) from channel mixing (pointwise) — the separable-conv trick — cutting params & FLOPs so it scales to 1B |
| Offsets Δp | One field, all channels share | Per-group field Δpgk | Different groups specialize in different sampling geometries (like attention heads) |
| Modulation m | Per-tap sigmoid in [0,1], independent | Per-group softmax over the K taps: ∑k mgk = 1 | Bounds the aggregation as a convex combination → stable training at scale; no exploding magnitudes |
Single group, K=3 taps. Suppose the three taps (after offsets & bilinear reads) returned sampled values v = [2, 8, 4], the depthwise weight w = 1, and the modulation network produced pre-softmax logits z = [0, 2, 1]. First, softmax the logits:
Then the output is w · ∑k mk vk = 1 · (0.090·2 + 0.665·8 + 0.245·4) = 0.180 + 5.320 + 0.980 = 6.480. Because the modulations sum to 1, the output is a weighted average of the sampled values — it always lies in [min v, max v] = [2, 8]. The tap that the network deemed most relevant (the one reading 8, logit 2) dominates, exactly as a high attention weight would.
Concretely, for an input feature map of shape [B, H, W, C] (channels-last, as the official kernel uses), a DCNv3 layer with G groups and K=9 produces, from a small projection of the input:
[B, H, W, G×K×2] — an (x,y) offset per group, per tap, per location[B, H, W, G×K] — softmax over the K axis within each group[B,H,W,C], weight by m, sum → output [B, H, W, C]The output spatial size equals the input (stride-1 DCNv3 inside a block; downsampling happens in separate stem/downsample layers). The operator is O(H·W·C·K) — linear in the number of pixels, because each output reads only K=9 points regardless of image size. That is the decisive contrast with self-attention's O((HW)²).
Walk the four stages on one output location: (1) predict offsets & logits, (2) bend the K taps and bilinearly sample, (3) softmax the modulations, (4) weighted sum. Click "Next Step" and watch the tap cloud, the modulation bars, and the running output.
python # One DCNv3 output location, single group, NumPy (no autograd, for clarity) import numpy as np def softmax(z): e = np.exp(z - z.max()); return e / e.sum() # Suppose the sub-network already gave us, for this location: sampled = np.array([2., 8., 4.]) # K=3 bilinear reads at bent taps logits = np.array([0., 2., 1.]) # pre-softmax modulation logits w = 1.0 # depthwise weight (shared over taps) m = softmax(logits) # [0.090, 0.665, 0.245], sums to 1 y = w * np.dot(m, sampled) # convex combo -> 6.48 print(round(float(y), 3)) # 6.48
A single offset field forces every channel to sample the same K locations. But different channels encode different things — one channel might respond to vertical edges, another to skin texture, another to large flat regions. They should be allowed to look in different places. DCNv3 solves this exactly the way multi-head attention does: split the channels into G groups, and give each group its own offsets and its own modulations.
Within a group, all C/G channels share that group's K offsets and K modulations (this is the weight sharing). Across groups, the patterns differ. The parallel is exact:
| Concept | Multi-Head Attention | DCNv3 Groups |
|---|---|---|
| Splitting unit | h heads | G groups |
| Per-unit specialization | Each head: own Q/K/V projection | Each group: own offsets & modulation |
| What is shared inside a unit | One attention pattern over its subspace | One sampling pattern over its channels |
| Recombination | Concat heads → WO | Concat groups → pointwise 1×1 |
Parameter economy and a useful prior. If every channel had its own offset field, the offset sub-network would output C×K×2 numbers per location — enormous and prone to overfitting. Sharing a group's offsets across its C/G channels assumes those channels want similar spatial structure (a reasonable prior, since grouped channels tend to co-activate), and it shrinks the offset head to G×K×2 outputs. G is a small knob: the paper finds more groups help, because they add sampling diversity at modest cost — the same reason multi-head beats single-head attention.
Take C=64 channels, K=9 taps. With G=1 the offset head must emit 1×9×2 = 18 offset numbers and 1×9 = 9 modulation logits per location — one sampling pattern shared by all 64 channels. With G=4 it emits 4×9×2 = 72 offsets and 4×9 = 36 logits — four independent patterns, each shared by 16 channels. The cost grew 4× on the tiny offset head (negligible vs. the value path), but the model gained four distinct ways to look around. That is a great trade, which is why InternImage configurations use multiple groups and increase G with model size.
Each colored constellation is one group's K=9 learned taps, sampling its own slice of channels. Drag the slider to add groups; notice how more groups cover more of the object (edges, interior, far context) — exactly the diversity multi-head attention provides.
DCNv3 is the operator; InternImage is the network built around it. The design deliberately mirrors a modern Transformer block (like ViT/Swin) but with DCNv3 in the mixing slot — so we get a fair, apples-to-apples comparison where only the token-mixer differs.
Each InternImage block is a residual stack with two sub-layers, each preceded by LayerNorm (pre-norm), exactly like a Transformer:
Both sub-layers use residual connections. The first replaces self-attention with DCNv3; the second is the ordinary position-wise FFN found in every Transformer. This is the key structural claim: swap the mixer, keep everything else — so any performance difference is attributable to DCNv3 vs. attention, not to other architectural tricks.
Like Swin and ConvNeXt, InternImage is hierarchical: it produces feature maps at 4 resolutions (strides 4, 8, 16, 32) so it can serve as a drop-in backbone for FPN-based detectors and segmentation heads, which need multi-scale features. The pipeline:
[H,W,3] → [H/4, W/4, C]. A patchify-style downsample to stride 4.Input image 224×224×3. Stem conv #1 (stride 2, pad 1, 3×3) → 112×112×(C/2). Stem conv #2 (stride 2) → 56×56×C. That is stride 4 (224/56 = 4), matching the standard backbone convention — a 56×56 = 3136-token grid at width C entering stage 1. After stage 1's downsample: 28×28 (stride 8). Stage 2: 14×14 (stride 16). Stage 3: 7×7 (stride 32). The four stage outputs are precisely the pyramid levels P2–P5 that an FPN expects. No special head surgery is needed — InternImage plugs into the existing detection/segmentation ecosystem unchanged.
Top: the residual block (LN → DCNv3 → add, LN → MLP → add). Bottom: the hierarchical stem & 4 stages with resolutions and strides. Click "Highlight" to step through and read the tensor shape at each stage.
Designing one block is easy; making a family of models that scales predictably from tens of millions to ~1 billion parameters is the hard part, and historically the part CNNs lost to ViTs. InternImage scales along two axes with explicit rules so the family stays well-behaved.
Depth (number of blocks per stage) and width (channels C) are grown together, with the group count G tied to width so each group keeps a sensible channel slice. The InternImage variants span a wide range:
| Variant | Channels C (stage 1) | Params | Role |
|---|---|---|---|
| InternImage-T (Tiny) | 64 | ~30M | ResNet-50 / Swin-T tier |
| InternImage-S (Small) | 80 | ~50M | Swin-S tier |
| InternImage-B (Base) | 112 | ~97M | Swin-B tier |
| InternImage-L (Large) | 160 | ~220M | Large ViT tier |
| InternImage-XL | 192 | ~335M | Beyond ConvNeXt-XL |
| InternImage-H (Huge) | 320 | ~1.08B | Foundation-scale, SOTA backbone |
Two specific failure modes appear only at extreme scale, and InternImage-H addresses each:
InternImage-H is not trained from scratch on the target task. It is pretrained on large-scale image data (the authors assemble a sizable classification corpus, ~400M+ images in their largest setting) and then fine-tuned on detection (COCO) and segmentation (ADE20K). This is the standard foundation-model playbook — pretrain broad, fine-tune narrow — and is part of why InternImage is framed as a vision foundation model, not just an architecture. The linear O(N) cost is what makes pretraining at high resolution on hundreds of millions of images affordable; an O(N²) attention backbone at the same resolution would be far costlier.
Schematic scaling: accuracy rises with parameter count and does not plateau through InternImage-H (~1B). Drag to move along the curve; the model variant and its tier read out. The curve illustrates the paper's claim that deformable-conv backbones scale like ViTs.
The paper's empirical case rests on three pillars: classification, detection, and segmentation — plus controlled ablations isolating why DCNv3 works.
| Benchmark | Metric | InternImage result | Significance |
|---|---|---|---|
| COCO detection (test-dev) | box AP | 65.4 AP (InternImage-H) | New SOTA at publication, surpassing the best ViT-based detectors |
| ADE20K segmentation | mIoU | 62.9 mIoU (InternImage-H) | New SOTA on the standard scene-parsing benchmark |
| Places365 classification | top-1 | 61.2% | Strong scene-recognition result for the foundation backbone |
| ImageNet-1K classification | top-1 | competitive with / above Swin & ConvNeXt at matched size | Confirms the operator is strong even on pure classification |
The crucial comparison is matched-budget: at equal parameter count, InternImage-T/S/B beat Swin-T/S/B and ConvNeXt-T/S/B on detection and segmentation. The gains are larger on dense-prediction tasks (detection, segmentation) than on classification — which is exactly what the theory predicts, since long-range adaptive sampling matters most when you must localize objects, not just label whole images.
The paper removes one ingredient at a time. The qualitative findings (the ones to remember):
A striking qualitative result: the effective receptive field (ERF) of InternImage is large and adaptive — it expands to cover the relevant object, not a fixed circular blob centered on the pixel. Visualizations show the sampled points clustering on the foreground object (a cat's whole body, a car's full outline) rather than spreading uniformly. This is direct evidence that the learned offsets do what the math promised: gather evidence from where the content is.
Toggle each DCNv3 ingredient off and watch the schematic accuracy bar drop. The largest fall comes from freezing the offsets (losing long-range adaptive sampling); softmax modulation and multiple groups each contribute meaningfully too.
This is the payoff: a live, multi-control DCNv3 operator on a synthetic feature map. You control the input content, the learned offset strength, the modulation temperature, and the number of groups — and watch the sampled taps move, the modulation weights redistribute, and the aggregated output change. Everything below is computed from the actual equations of Chapters 2–4.
The grid is a feature map with a movable bright object. The colored constellations are each group's K=9 deformable taps. The bars are the (softmax) modulation weights for the selected group. Move the object, bend the offsets toward it, sharpen/soften the modulation, and add groups — the live output (top-right) is the convex-weighted aggregate of the bilinearly-sampled values.
Things to try, and what they reveal:
DCNv3 and InternImage are not the end of the story. Knowing where the operator strains is as important as knowing where it shines.
| Lesson | Why read it next |
|---|---|
| Attention Is All You Need (Veanor) | The operator DCNv3 was designed to rival — understand softmax aggregation here first |
| Vision Transformer (Veanor) | The ViT baseline InternImage matches/beats; the patchify stem & hierarchy comparison |
| DETR (Veanor) & RF-DETR (Veanor) | Deformable attention in detection decoders; where an InternImage backbone plugs in |
| Convolutional Networks (Gleam) | The standard conv DCNv3 generalizes — receptive fields, kernels, separable convolution |
| Detection & Segmentation (Gleam) | The dense-prediction tasks where DCNv3's adaptive sampling pays off most |
| Vision Transformer (Gleam) | From-zero intuition for self-attention on images, the contrast point |
| Concept | One-line summary |
|---|---|
| Core problem | Convs lack long-range & adaptive aggregation; attention has both but costs O(N²) |
| DCNv3 equation | y(p₀) = ∑g ∑k wg mgk xg(p₀ + pk + Δpgk) |
| Deformable sampling | Bilinearly read the feature map at learned fractional offsets — differentiable, so offsets train |
| Modulation | Softmax over the K taps (per group) → bounded convex weighting; the key to stable scaling |
| Groups | Split channels into G groups, each with own offsets/modulation — like multi-head attention |
| Weights | Separable: depthwise (spatial) + pointwise 1×1 (channels) for FLOP economy |
| Cost | O(N) in pixels: each output reads only K=9 points, any image size |
| Block | Transformer layout, mixer swapped: LN→DCNv3→+, LN→MLP→+ |
| Backbone | Hierarchical, 4 stages, strides 4/8/16/32 — FPN-ready for detection/segmentation |
| Scale | InternImage-T (~30M) → InternImage-H (~1B); scales like ViTs at linear cost |
| Results | 65.4 AP COCO, 62.9 mIoU ADE20K, 61.2% Places365 (InternImage-H, at publication) |