Skip to main content

VMamba: Visual State Space Model

Follow VMamba from a compact running state through four image scan orders, aligned cross-merge, the final VSS block, and runnable Python.

An image needs context, but a scan introduces order

A patch of an image is easier to interpret when the model can use the rest of the scene. A convolution collects information from a local neighborhood. Full self-attention compares every token with every other token. VMamba takes another route: carry a compact running state across the image, then combine several traversal orders so information can arrive from different directions.

Start with a small feature map. The numbers below identify positions, not colors, feature values, or classes. Each position will eventually hold a vector of learned channels.

Reading this grid row by row creates the sequence 0, 1, 2, 3, 4, …, 11. A causal scan at position 5 has already seen position 1, directly above it, but has not seen position 9, directly below it. The scan also jumps from 3 to 4, although those positions lie at opposite ends of adjacent rows. Turning an image into a sequence introduces both an order and a spatial bias.

VMamba does not try to eliminate order. It uses four complementary orders, processes each with an input-dependent state update, and restores the results to their original positions. To understand why that works, first look at what one scan carries.

A state is a running summary

Imagine passing a small notebook from one position to the next. At each step, you retain part of the previous note, add something from the current input, and read an output from the updated note. The notebook is the state. It has a fixed number of coordinates even when the sequence gets longer.

The next diagram makes that update numerical. Scrub through the inputs and compare the retained state with the new input contribution. The values illustrate the recurrence; they are not activations from a trained image model.

Interactive mechanics

Watch the state retain and update

A scalar teaching example: one channel, one state coordinate. The same spatial grid is scanned with zero initial state in each direction.

01234567891011

Spatial token IDs · current input u = 1

Positive update time scale

Δ = softplus(0.8u + bias)

0.598

Retention exp(−0.7Δ) = 0.658

-1.0
Step 1 / 12
1 / 12

h = exp(ΔA) × previous h + ΔBu

Retained state

0.000

0.658 × 0.000

Input contribution

0.598

0.598 × 1 × 1

New state

0.598

retained + input

Read out the state, then add the separate D input skip:

y = 1 × 0.598 + 0.2 × 1 = 0.798

ID 0

1

ID 1

2

ID 2

0

ID 3

3

ID 4

1

ID 5

-1

ID 6

2

ID 7

0

ID 8

1

ID 9

3

ID 10

-2

ID 11

1

Illustrative values, not pretrained features. Here A = −0.7, B = C = 1 and D = 0.2. The learned model derives delta, B and C from its features; changing delta affects both retention and new-input contribution.

For one scalar state, let u_t be the current input, h_{t-1} the previous state, a_t the retention factor, and b_t the input coefficient. The next state and its output are:

h_t=a_t h_{t-1}+b_t u_t,\qquad y_t=c_t h_t+d u_t

Here c_t reads the state, while d supplies a direct input-to-output path. For example, with previous state 2, retention 0.5, input 3, and input coefficient 0.2, the next state is 0.5\times2+0.2\times3=1.6. It preserves some history while incorporating something new.

That fixed-size summary is also a constraint. The state does not keep a separate record of every preceding token. Different histories can be compressed into the same state. The important question becomes: which information should this input preserve, write, and read?

Selectivity changes the update at each position

VMamba learns the update from the current feature vector. Its projections produce three input-dependent quantities: Δ, the update time scale; B, the input-to-state coefficients; and C, the state-to-output coefficients. The learned A controls state decay, and D controls the direct skip. A and D do not change with the input token.

A larger Δ makes a negative-A state retain less history, but also changes how much new input is written. Δ is positive through a softplus transformation; it is not a sigmoid probability gate. B and C can be signed and are not attention probabilities either. Moving the diagram’s update controls changes the actual recurrence, rather than selecting a hand-drawn “important” patch.

Now give the running summary a state coordinate n and a channel j. For one direction and one example, the pinned reference implements:

\Delta_{t,j}=\operatorname{softplus}(\mathrm{raw}\Delta_{t,j}+\mathrm{bias}_j),\qquad A_{j,n}=-\exp(A^{\log}_{j,n})
h_{t,j,n}=\exp(\Delta_{t,j}A_{j,n})h_{t-1,j,n}+\Delta_{t,j}B_{t,n}u_{t,j}
y_{t,j}=\sum_n C_{t,n}h_{t,j,n}+D_j u_{t,j}

Each direction starts from a zero state. B and C are specific to the direction and position but shared across channels within that direction’s group in the reference. Δ, A, D, and the state are per channel. The released Tiny model uses state dimension 1 per channel per direction, not one scalar for the entire image.

The exponential connects this discrete update to continuous-time state-space models: A describes decay, and Δ sets the interval over which decay acts. The input term in the official selective_scan_torch reference is explicitly \Delta B u. For constant scalar A, B, and u, exact zero-order-hold integration would instead use \frac{e^{\Delta A}-1}{A}Bu. These agree to first order for a small interval; they are not the same expression. Paper v4, §3 and Appendix A

We now have an operator for one ordered sequence. An image still needs information from positions that lie later in that sequence.

Four orders give each position several views of the image

SS2D means 2D Selective Scan. Its cross-scan operation builds four sequences from the same feature map: row-major, column-major, the complete reversed row-major sequence, and the complete reversed column-major sequence. Select a direction below and watch the token IDs. Notice the wrap jumps at the end of each row or column.

Interactive mechanics

One grid, four complete sequences

A token is a spatial location. Change the direction and watch the same 12 locations become a different causal sequence.

01234567891011

Original positions stay fixed: 3 rows × 4 columns

Row forward

Start

0

Current

0

End

11

Begin with zero state at the first token.

Step 1 / 12
1 / 12

Sequence order · numbers are spatial token IDs

0
1
2
3
4
5
6
7
8
9
10
11

Row →

01234567891011

Column ↓

01234567891011

Row ←

01234567891011

Column ↑

01234567891011
Each route crosses the entire image in one sequence. Dashed connections mark wrap jumps between rows or columns. The state does not reset at those jumps.

For our 3×4 grid, the four orders are:

code
Row forward: 0 1 2 3 4 5 6 7 8 9 10 11 Column forward: 0 4 8 1 5 9 2 6 10 3 7 11 Row reverse: 11 10 9 8 7 6 5 4 3 2 1 0 Column reverse: 11 7 3 10 6 2 9 5 1 8 4 0

These are four full-image scans. The state continues across row and column boundaries. They are not independent scans of each row, and they do not follow a serpentine path. The column sequence comes from transposing the spatial axes before flattening; each reverse sequence flips the entire corresponding forward sequence. Pinned cross_scan_fwd

A forward path lets a position receive information from its prefix. Its reverse lets it receive information from the other side. Even the row-forward/reverse pair structurally connects every pair of positions; the column pair provides a second ordering with different spatial distances. Four paths reduce reliance on one ordering without making all routes equivalent.

The highlighted prefixes show possible information paths. They do not show learned importance, saliency, or how strongly a trained model uses a distant pixel. That strength depends on repeated retention factors, B and C, and the learned features. Before these directional outputs can form an image again, their positions must agree.

Cross-merge restores positions before adding

Position 5 appears at different sequence indices in different directions. Adding the four outputs at sequence index 5 would combine different image positions. Cross-merge first undoes each permutation, then sums the outputs associated with the same original pixel.

Interactive mechanics

Different sequence slots, the same pixel

Choose a spatial pixel. Find its output in all four sequences, restore its position, then add the four aligned values.

Choose a pixel in the original grid

Pixel 6: row 1, column 2. Sequence slots below are zero-based.

Row forward

slot 6 → 6

0
1
2
3
4
5
6
7
8
9
10
11

7 tokens can reach this output through the causal prefix.

Column forward

slot 7 → 6

0
4
8
1
5
9
2
6
10
3
7
11

8 tokens can reach this output through the causal prefix.

Row reverse

slot 5 → 6

11
10
9
8
7
6
5
4
3
2
1
0

6 tokens can reach this output through the causal prefix.

Column reverse

slot 4 → 6

11
7
3
10
6
2
9
5
1
8
4
0

5 tokens can reach this output through the causal prefix.

Undo order → restore pixel 6 → sum

Row →

6

slot 6 → pixel 6

Column ↓

6

slot 7 → pixel 6

Row ←

6

slot 5 → pixel 6

Column ↑

6

slot 4 → pixel 6

6 + 6 + 6 + 6=24

Forward and reverse row prefixes together cover every location; so do the column prefixes. This shows structural reachability, not how strongly a trained model uses a pixel.

Identity is an alignment audit: each directional output equals its token ID, so merge returns four times that ID. The scalar recurrence mode is a mechanics fixture (u = ID/4, Δ = 0.5, A = −1, B = C = 1, D = 0), not model inference.

Let p_k(t) map sequence index t in direction k to an original flat pixel index, and let y^{(k)}_t be that direction’s output vector. The merged vector for original position p is:

z_p=\sum_{k=0}^{3}y^{(k)}_{p_k^{-1}(p)}

The inverse reverses the two backward sequences and restores the transposed column layout. On a rectangle, that column stream must be reshaped as width × height, then transposed back to height × width. Square-only examples can conceal a wrong inverse. The reference adds the aligned outputs; it does not average or concatenate them. Replacing every scan with the identity therefore gives four times the input after merge. Pinned cross_merge_fwd

The original paper now reads as a compact description of the mechanism we have traced: permute, update four running states, restore positions, combine.

Original Figure 2, arXiv 2401.10166v4, PDF page 4. The printed grid uses IDs 1–9; our rectangular walkthrough uses 0–11.

Open the source at full size.

SS2D supplies global spatial mixing. A useful network block must also transform channels, preserve an easy residual path, and prepare the next layer’s features.

Inside the final VSS block

A Visual State Space block wraps SS2D in two residual updates. First normalize the incoming feature map, run the SS2D branch, and add it back to the input. Then normalize again, apply a channel MLP, and add that result to the intermediate features.

Interactive mechanics

Local mixing, global routes, two residuals

Inspect the final VMamba-T s1l8 VSS block (v05_noz). The feature grid keeps its spatial shape throughout the block.

Residual 1 · input x travels around SS2D

x → +

1 · At each position

Normalize & project

LN → pointwise projection

2 · Nearby positions

Local convolution

3×3 depthwise conv → SiLU

3 · Whole-image routes

Four selective scans

cross-scan → project Δ, B, C → scan

4 · Back to the grid

Align & project

cross-merge → LN → output projection

5 · Add the original x

+x₁

Residual 2 · x₁ travels around the MLP

LN → pointwise MLP

C → 4C → GELU → C

Add x₁

Second residual

x₂

LN → pointwise projection

Channel normalization and the input projection act at each spatial position. This final Tiny block has no separate z gate.

Step 1 / 6
1 / 6
Geometry is schematic, not a learned activation. SS2D is the first branch; a normalized MLP is the second. The released Tiny configuration uses state size 1 and SSM expansion 1.

Inside the SS2D branch, a pointwise projection mixes channels. A 3×3 depthwise convolution and SiLU gather local spatial information before scanning. Direction-specific projections derive Δ, B, and C; the scans carry global context; cross-merge restores the grid. Channel normalization and an output projection complete the branch. The MLP expands channels by a factor of four, applies GELU, and projects back to the block width.

For input feature map X, write this as:

X'=X+\operatorname{SS2DBranch}(\operatorname{LN}_1(X)),\qquad X''=X'+\operatorname{MLP}(\operatorname{LN}_2(X'))

This expression shows the inference path; training also applies stochastic depth to residual branch outputs. LN normalizes channels at each spatial position. The direct D·u term lives inside each selective scan. The two additions above carry complete feature maps around entire branches. They are three different skip mechanisms.

The architecture shown here is the final no-z VSS block. The earlier “Vanilla VMamba” design multiplied its scan output by a separate z branch. The final design removes that branch and introduces the MLP residual. Selectivity remains through Δ, B, and C. Mixing the historical branch into the final Tiny configuration would describe a different model. Paper v4, §4.1, pinned v05_noz implementation

Once that block preserves its spatial shape, stacking it is straightforward. The backbone changes resolution between groups of blocks.

From an image to a four-stage backbone

The released VMamba-T s1l8 starts with two padded 3×3 stride-two convolutions: RGB becomes 48 channels at 112×112, then 96 channels at 56×56. Each convolution is followed by channel normalization, with GELU between the two. This is the code’s actual convolutional stem, rather than a single non-overlapping patch projection.

Interactive mechanics

A shrinking grid, a growing channel width

Follow the released VMamba-T s1l8 backbone on a 224×224 RGB input. Select a stage to inspect its real tensor shape and repeated VSS blocks.

224 × 224 × 3 → two-convolution stem

3×3 stride 2 → LN → GELU → 3×3 stride 2 → LN

112 × 112 × 48 → 56 × 56 × 96

Stage 1 · VSS × 2

56 × 56 × 96

Each scan visits 3,136 spatial tokens.

Next: downsample v3

A padded 3×3 stride-two convolution halves the spatial resolution and doubles channel width, followed by channel normalization.

Step 1 / 4
1 / 4
7 × 7 × 768LN + global average768 features1000 logits

For detection and segmentation, the backbone can provide feature grids from multiple stages to a task-specific head instead of pooling them into one classifier vector.

Planes represent tensor geometry, not measured features. Shapes omit the batch axis and use H×W×C for readability; the selected official model operates channel-first.

For a 224×224 image, the four stage outputs are 56×56×96, 28×28×192, 14×14×384, and 7×7×768. Their VSS block counts are [2, 2, 8, 2]. Between stages, a padded 3×3 stride-two convolution reduces resolution and doubles the channel width. Each scan runs over the current stage’s entire feature map, not repeatedly over the original RGB pixels.

In the name s1l8, s1 means SSM expansion ratio 1, and l8 means eight blocks in stage 3. The state dimension is 1, while the Δ projection rank is the stage width divided by 16, rounded up. The classifier normalizes the last feature map, globally averages its spatial positions, and maps the resulting 768 features to 1,000 ImageNet logits. A logit is a score; softmax converts those scores into class probabilities. Pinned vmamba_tiny_s1l8 and backbone builders, Tiny factory

A detection or segmentation system keeps these spatial stage features and attaches a task-specific head instead of the ImageNet classifier. The four-stage hierarchy is therefore useful beyond classification: early maps retain spatial detail, while later maps combine that detail at coarser resolution.

Original Figure 3(a)–(d), arXiv 2401.10166v4, PDF page 5; performance panel (e) is outside this crop. Read panel (d) for the final block. Panel (c) records the historical multiplicative branch.

Open the complete source figure.

We can now connect each part of the diagrams to a tensor operation, rather than treating the backbone as an opaque collection of blocks.

Implementation: paper, math, and Python

Read the seven rows in order: cross-scan, selectivity, recurrence, cross-merge, SS2D, the VSS block, and the backbone. Each row places the original paper reference beside the equation, tensor shapes, and highlighted Python extracted from the runnable example. The code uses the same rectangular order and positional alignment as the diagrams.

Follow the same operations in a runnable CPU PyTorch reference. Its readable scan loop explains the state updates; it does not reproduce the throughput of the authors’ CUDA and Triton kernels.

Download the Python reference · Pinned requirements · Pinned official source

python vmamba.py --smoke --backward

The smoke run uses random weights and checks a forward and backward pass. Its outputs are mechanics checks, not pretrained predictions.

1. Unfold the image into four complete sequences

Original paper

Figure 2 · Cross-Scan

SS2D starts by traversing every spatial position in four complementary orders. Row-major and column-major flattening each supply a forward path and a full reversed path.

Math and tensor shapes

L=H W
X\in\mathbb{R}^{b\times C\times H\times W}\longrightarrow U\in\mathbb{R}^{b\times4\times C\times L}

Flatten the original grid, then flatten its spatial transpose. Flip each complete sequence to produce the backward paths. The state continues across row and column boundaries: a new row does not reset it. The batch dimension b is distinct from the input-dependent state parameter B.

3 × 4 grid → 4 sequences of length 12

Exact reference behavior

Python implementation

1. Unfold the image into four complete sequences
python
def cross_scan(x):
    """Map [batch, channels, H, W] to four [batch, 4, channels, H*W] routes."""
    if x.ndim != 4 or min(x.shape) <= 0:
        raise ValueError("Expected a nonempty NCHW feature map")
    row = x.flatten(2)
    column = x.transpose(2, 3).flatten(2)
    forward = torch.stack((row, column), dim=1)
    return torch.cat((forward, forward.flip(-1)), dim=1)

2. Let the current input choose how the state updates

Original paper

Paper excerpt · PDF p. 4§3 · input-dependent selection

Selective state-space models replace fixed input and output mappings with mappings derived from the current input. A learned positive time scale controls the update.

Math and tensor shapes

\Delta_t=\operatorname{softplus}(\widehat\Delta_t+b_\Delta)
A=-\exp(A_{\log})

Project each token to B, C and a low-rank time-scale representation, then expand that representation to the channel width. Softplus makes the time scale positive. The negative diagonal A makes exp(ΔA) a decaying retention factor. Δ is a time scale, not a probability gate: changing it affects both retained state and the input contribution.

Per direction: Bₜ and Cₜ describe state coordinates; Δₜ varies by channel

Exact reference behavior

Python implementation

2. Let the current input choose how the state updates
python
def selective_parameters(self, routes):
    """Each direction projects input into its delta, B and C sequences."""
    projected = torch.einsum("bkdl,kpd->bkpl", routes, self.x_proj)
    low_rank_delta, b, c = projected.split(
        (self.rank, self.state_size, self.state_size), dim=2
    )
    raw_delta = torch.einsum("bkrl,kdr->bkdl", low_rank_delta, self.dt_proj)
    a = (
        -self.a_log.exp()
    )  # Learned stable negative rates, not input-derived.
    return raw_delta.flatten(1, 2), a, b, c

3. Retain previous state, write new information, read an output

Original paper

Paper excerpt · PDF p. 3§3 · selective recurrence and reference implementation

The discrete update carries information along each sequence. The reference implements the input contribution as ΔBu, while the transition is exp(ΔA).

Math and tensor shapes

h_t=\exp(\Delta_t A)\odot h_{t-1}+\Delta_t B_t u_t
y_t=\sum_n C_{t,n}h_{t,n}+D u_t

Start a separate zero state for every direction and channel. The first term keeps part of the previous summary; the second writes the current input. C reads the state into one output per channel, and D adds a direct input path. This readable loop matches the reference recurrence; fast kernels reorganize its computation. ΔBu is the reference discretization, not the exact zero-order-hold input integral.

States: [b, 4C, N]; output sequences: [b, 4C, L]

Exact reference behavior

Python implementation

3. Retain previous state, write new information, read an output
python
def selective_scan(
    u, delta, a, b, c, d=None, delta_bias=None, delta_softplus=True
):
    """Scan grouped B/C, using the official delta*B*u input discretization.

    u/delta: [batch, channels, length]; a: [channels, state].
    b/c: [batch, groups, state, length], shared within each channel group.
    d/delta_bias: [channels]. States start at zero for each channel/route.
    Computation is float32, matching the upstream torch reference.
    """
    _validate_scan(u, delta, a, b, c)
    if delta_bias is not None:
        delta = delta + delta_bias[None, :, None]
    if delta_softplus:
        delta = F.softplus(delta)
    u, delta, a, b, c = (v.float() for v in (u, delta, a, b, c))
    batch, channels, length = u.shape
    channels_per_group = channels // b.shape[1]
    b = b.repeat_interleave(channels_per_group, dim=1)
    c = c.repeat_interleave(channels_per_group, dim=1)
    state = u.new_zeros(batch, channels, a.shape[-1])
    outputs = []
    for t in range(length):
        step = delta[:, :, t, None]
        retention = torch.exp(step * a)
        injection = step * b[:, :, :, t] * u[:, :, t, None]
        state = retention * state + injection
        outputs.append((c[:, :, :, t] * state).sum(-1))
    y = torch.stack(outputs, dim=-1)
    return y if d is None else y + d[None, :, None] * u

4. Restore pixel positions before adding the directions

Original paper

Paper excerpt · PDF p. 4Figure 2 · Cross-Merge

After selective scanning, every directional output is restored to the original spatial layout. Corresponding pixels can then combine their contextual information.

Math and tensor shapes

Y_{h,w}=\sum_{k=0}^{3}Y^{(k)}_{\pi_k^{-1}(h,w)}

Undo each backward reversal. Reshape the column-order sequences as W × H and transpose them back to H × W. Only then add the four outputs. Summation preserves all four contributions; it does not divide by four. Rectangular examples make a wrong reshape immediately visible.

4 aligned [b, C, H, W] tensors → one [b, C, H, W] tensor

Exact reference behavior

Python implementation

4. Restore pixel positions before adding the directions
python
def cross_merge(routes, height, width):
    """Undo reversals and column transpose, then SUM aligned outputs."""
    if (
        routes.ndim != 4
        or routes.shape[1] != 4
        or height <= 0
        or width <= 0
        or routes.shape[-1] != height * width
    ):
        raise ValueError("Expected four routes with length height * width")
    aligned = routes[:, :2] + routes[:, 2:].flip(-1)
    batch, _, channels, _ = aligned.shape
    row = aligned[:, 0].reshape(batch, channels, height, width)
    column = aligned[:, 1].reshape(batch, channels, width, height)
    return row + column.transpose(2, 3)

5. Put local mixing and global scanning in the same operator

Original paper

Paper excerpt · PDF p. 4§4.2 · 2D Selective Scan

SS2D adapts a one-dimensional selective operator to image features. The released final variant supplies the projection, local convolution, normalization and output details.

Math and tensor shapes

U=\operatorname{SiLU}(\operatorname{DWConv}(W_{in}X))
Y=W_{out}\operatorname{Norm}(\operatorname{Merge}(\operatorname{Scan}(\operatorname{Cross}(U))))

A pointwise projection mixes channels without moving information spatially. The depthwise convolution adds local context before the four full-image scans. Each direction has its own learned parameters. Merge brings the directional results together, and the output projection returns to the block width. The selected v05_noz variant has no separate multiplicative z branch.

VMamba-T stage 1: [b, 96, 56, 56] → [b, 96, 56, 56]

Exact reference behavior

Python implementation

5. Put local mixing and global scanning in the same operator
python
def forward(self, x):
    """Mix locally, scan globally, align routes, normalize and project."""
    x = F.silu(self.local(self.in_proj(x)))
    batch, channels, height, width = x.shape
    routes = cross_scan(x)
    delta, a, b, c = self.selective_parameters(routes)
    outputs = selective_scan(
        routes.flatten(1, 2), delta, a, b, c, self.d, self.dt_bias
    )
    outputs = outputs.reshape(batch, 4, channels, height * width)
    merged = cross_merge(outputs, height, width)
    return self.out_proj(self.norm(merged))

6. Build the final VSS block with two residual updates

Original paper

Figure 3(d) · final VSS block

The final paper uses a normalized SS2D residual followed by a normalized MLP residual. This is the improved block, distinct from Vanilla VMamba in Figure 3(c).

Math and tensor shapes

Z=X+\operatorname{DropPath}(\operatorname{SS2D}(\operatorname{LN}(X)))
Y=Z+\operatorname{DropPath}(\operatorname{MLP}(\operatorname{LN}(Z)))

SS2D communicates across positions, while the MLP transforms the channels at each position. Each module receives normalized features and adds its result back to its input. These two block residuals are separate from D·u inside the scan recurrence. DropPath is a training regularizer; evaluation keeps both branches. The teaching code omits DropPath, showing that evaluation behavior directly.

The block preserves channels and spatial size; its MLP expands channels by 4

Exact reference behavior

Python implementation

6. Build the final VSS block with two residual updates
python
class VSSBlock(nn.Module):
    """Two pre-normalized residuals; the final block has no separate z gate.

    Drop-path is omitted in this teaching model (identity during evaluation).
    The scan's D*u skip is inside SS2D and separate from these two residuals.
    """

    def __init__(self, channels):
        super().__init__()
        self.norm1 = ChannelNorm(channels)
        self.ss2d = SS2D(channels)
        self.norm2 = ChannelNorm(channels)
        self.mlp = nn.Sequential(
            nn.Conv2d(channels, 4 * channels, 1),
            nn.GELU(),
            nn.Conv2d(4 * channels, channels, 1),
        )

    def forward(self, x):
        x = x + self.ss2d(self.norm1(x))
        return x + self.mlp(self.norm2(x))

7. Stack the blocks into a hierarchical image backbone

Original paper

Paper excerpt · PDF p. 5Figure 3(a) · VMamba-T backbone and released configuration

The stem enters a quarter-resolution feature grid. Four stages progressively reduce spatial size and increase channel width, before a classification head pools the final grid.

Math and tensor shapes

(H_i,W_i,C_i):\ (56,56,96)\to(28,28,192)\to(14,14,384)\to(7,7,768)
\ell=W_{head}\operatorname{GAP}(\operatorname{LN}(X_4))+b_{head}

The selected Tiny model uses a two-convolution stem and convolutional downsampling between stages. More channels give later positions a richer representation as the grid shrinks. The classifier pools the final normalized features; dense prediction tasks instead attach their own heads to the hierarchy. Random-weight smoke tests verify these mechanics, while released weights are required for useful predictions.

224 × 224 image; stage repeats 2, 2, 8, 2; 1000 ImageNet logits

Exact reference behavior

Python implementation

7. Stack the blocks into a hierarchical image backbone
python
class VMamba(nn.Module):
    """Four-stage teaching backbone; defaults match Tiny stage dimensions.

    Initialization and state-dictionary names are teaching choices. Use the
    official exporter for pretrained results, never load released weights here.
    """

    def __init__(
        self, depths=(2, 2, 8, 2), dims=(96, 192, 384, 768), num_classes=1000
    ):
        super().__init__()
        self.stem = nn.Sequential(
            nn.Conv2d(3, dims[0] // 2, 3, stride=2, padding=1),
            ChannelNorm(dims[0] // 2),
            nn.GELU(),
            nn.Conv2d(dims[0] // 2, dims[0], 3, stride=2, padding=1),
            ChannelNorm(dims[0]),
        )
        self.stages = nn.ModuleList(
            [
                nn.Sequential(*(VSSBlock(dim) for _ in range(depth)))
                for dim, depth in zip(dims, depths)
            ]
        )
        self.downsamples = nn.ModuleList(
            [
                nn.Sequential(
                    nn.Conv2d(left, right, 3, stride=2, padding=1),
                    ChannelNorm(right),
                )
                for left, right in zip(dims[:-1], dims[1:])
            ]
        )
        self.norm = ChannelNorm(dims[-1])
        self.head = nn.Linear(dims[-1], num_classes)

    def forward(self, image):
        x = self.stem(image)
        for index, stage in enumerate(self.stages):
            x = stage(x)
            if index < len(self.downsamples):
                x = self.downsamples[index](x)
        return self.head(self.norm(x).mean(dim=(2, 3)))

The CPU reference exposes the scan as a loop so every state update remains inspectable and differentiable. Its teaching model uses random weights. Running it checks the mechanism; it does not reproduce pretrained ImageNet accuracy or the paper’s optimized GPU throughput. The official implementation at the pinned revision supplies the complete released model and optimized kernels.

Download the runnable Python reference and its requirements. The source behind the excerpts is the same downloadable file. Run python vmamba.py --smoke --backward to check a small random-weight model without downloading a checkpoint. To see what learned features produce, move from that mechanics check to a released model.

A real pretrained VMamba-T on three photographs

The next example executes the official VMamba-T s1l8 model with the released checkpoint's model weights. It is separate from the teaching network above. Each displayed prediction comes from a recorded CPU inference run, and the displayed crop contains exactly the pixels supplied to that run before normalization.

Select the cat, coffee, or astronaut photograph, then follow its recorded tensor shapes through the network. The astronaut's highest-ranked class is football helmet, not an astronaut label. That is the actual output of this ImageNet classifier: the example illustrates inference, including its mistakes, rather than an accuracy comparison or a semantic explanation of a feature map.

One photograph through a released VMamba-T

Measured pretrained example

These are saved outputs from the official model on CPU, using the released checkpoint’s model weights. Select a photograph to inspect the actual 224 × 224 crop and its five highest ImageNet probabilities.

Showing recorded VMamba-T predictions for Chelsea the cat.

Actual model crop, before RGB normalization. Stefan van der Walt · CC0.

VMamba-T (s1l8)

30,249,064 parameters · state size 1 · input 224 × 224

Softmax probability · 0–100% scale

  1. tiger cat45.12%
  2. Egyptian cat26.08%
  3. tabby11.30%
  4. lynx0.24%
  5. lens cap0.09%

A distribution over the 1,000 ImageNet categories. These probabilities are not calibrated certainty. Three example photographs do not estimate ImageNet accuracy.

Stage 1 · 2 VSS blocks

Input · batch, channels, then spatial axes

1 × 96 × 56 × 56

Output

1 × 96 × 56 × 56

Tensor shapes were traced during this model run. The planes show schematic geometry, not measured activations, attention, saliency, or object masks.

How these predictions were produced

The official vmamba_tiny_s1l8 factory loaded the checkpoint strictly, with no missing or unexpected tensors. Evaluation and inference mode use float32 on CPU with 2 threads and the official PyTorch scan reference.

Resize the short edge to 256 using bicubic PIL interpolation, center-crop to 224, convert RGB to float32 in [0,1], then normalize with mean [0.485, 0.456, 0.406] and standard deviation [0.229, 0.224, 0.225]. Every normalized crop matched the pinned official evaluation transform exactly.

The teaching scan also passed an independent output and gradient comparison against the pinned reference. The pretrained classifier here executes the official model directly; it is separate from the random-weight teaching program.

Input crop SHA256
ff9de1b8e229782de3f5f15371421046ae5d01fd55b480c708c55edf555fd34f
Execution
PyTorch 2.12.0 · torchvision 0.27.0 · Pillow 12.2.0 · exported 2026-10-10

Full measurements and provenance · Reproduce the export · Pinned requirements

The stored outputs establish what this checkpoint predicted on these three inputs. They do not measure speed. With both the readable recurrence and a genuine model run in view, we can now separate the amount of scan work from the time needed to execute it.

Linear work does not guarantee lower latency

Let the feature map contain L=H\times W positions. With channels and state size held fixed, four scans visit 4L positions. Doubling the number of positions doubles this scan work. Doubling both height and width gives four times as many positions, so scan work also grows fourfold.

Full dense attention considers L² query–key pairs, giving sixteen times as many pairs when both spatial dimensions double. This comparison concerns full pairwise attention. Windowed, sparse, and linear attention have different scaling; “all transformers are quadratic” would be the wrong conclusion.

Interactive mechanics

Count positions, not milliseconds

Hold channel width and state size fixed. Four scans visit 4L positions; full pairwise self-attention considers L² query–key pairs, where L = H×W.

12

Four complete scans

48

4 × 12 position visits

Row forward

Column forward

Row reverse

Column reverse

Full query–key pair matrix

144

12 queries × 12 keys

One row per query; one column per key.

Double the token count

Scan visits × 2

Double both pair axes

Token pairs × 4

Doubling both image height and width multiplies L by four: scan position visits grow by four, while full token pairs grow by sixteen. Linear sequence scaling is an asymptotic statement under fixed model dimensions.

This counts sequence positions and dense token pairs, not FLOPs, measured memory or latency. Channel projections, state size, precision, kernel parallelism and hardware also matter. Windowed or sparse attention has different scaling.

The diagram counts operations implied by the sequence length. It does not measure milliseconds. Channel projections, the MLP, local convolution, four data rearrangements, and memory movement all contribute to actual execution time. The recurrent state is fixed-size per channel, but a training implementation also stores tensors or recomputes them for backward; linear scan work does not mean the entire model uses constant memory.

The Python loop executes time steps sequentially. Optimized implementations can use an associative parallel scan: affine state updates compose associatively, so they can be regrouped without changing the intended recurrence. Floating-point precision and accumulation order can still affect the numerical result. Kernel design, tensor layout, batch size, hardware, and synchronization determine whether the lower asymptotic work turns into lower latency. The paper’s acceleration sequence changes both architecture and kernels. Paper v4, §4.3 and Appendix B

That is why published accuracy and throughput belong with their experimental protocol, rather than being inferred from the arrows above.

Published results and their limits

The final paper reports the following ImageNet-1K results at 224×224, with models trained from scratch for 300 epochs. Small and Base here are the paper’s s2l15 variants; they are not the s1l20 alternatives listed elsewhere in the repository.

ModelParametersFLOPsImageNet top-1
VMamba-T · s1l830M4.9G82.6%
VMamba-S · s2l1550M8.7G83.6%
VMamba-B · s2l1589M15.4G83.9%

Source: arXiv v4, Table 1 and Appendix E. These are published evaluations, not results from the teaching example. The paper’s throughput comparison uses an NVIDIA A100, AMD EPYC 7542 CPU, and batch size 128. Appendix E does not clearly specify the inference precision in that protocol description, so those batched measurements should not be read as an expected latency for one image in a different environment.

The same backbone also supports dense prediction. These Tiny results use separate task heads and fine-tuning protocols:

Task and evaluationVMamba-T result
COCO detection · Mask R-CNN · 1× schedule, 12 epochs47.3 box AP
COCO instance segmentation · same 1× model42.7 mask AP
ADE20K semantic segmentation · UperNet · single-scale test47.9% mIoU
ADE20K semantic segmentation · UperNet · multi-scale test48.8% mIoU

Source: arXiv v4, Table 2 and Appendix F. A longer 36-epoch multi-scale COCO schedule is a different experiment. Classification top-1, box AP, mask AP, and mIoU measure different tasks; they cannot be combined into one accuracy score.

The central tradeoff remains visible in the small grid. Four orders supply global structural reachability, but each direction compresses its history into a small state and imposes an ordering bias. Long paths can attenuate information. Learned effective receptive fields must be measured on trained models, rather than read from a prefix highlight. The paper investigates those fields and alternative scan patterns; the mechanism alone does not guarantee that every distant pixel matters equally. Paper v4, §5.3 and Appendix H.1

VMamba’s contribution is a concrete way to give a hierarchical vision backbone input-dependent global mixing with linear scan work. The path from image to prediction is now explicit: local features enter four ordered recurrences, directional outputs return to the right pixels, residual blocks refine the map, and a task head reads the resulting hierarchy.

  • Mamba: derive the selective state update, associative scan and persistent buffers underlying the sequence operator.
  • Mamba-2: connect a restricted state transition to structured attention and chunked matrix multiplication.
  • Convolution operation: how the local stem and depthwise spatial mixing differ from a running state.
  • Self-attention in Vision Transformers: direct token-to-token mixing and the full pairwise computation used in the scaling comparison.
  • Hilbert-Guided Sparse Local Attention: another example of token order affecting the computation, this time by making local attention easier for a sparse kernel to execute.
  • EfficientNet: why a backbone’s resolution, width, and depth must be considered together when comparing efficiency.
  • Original VMamba paper, arXiv v4 and versioned official code: the source architecture, experiments, and implementation behind this walkthrough.

If you found this paper review helpful, consider sharing it with others.

Mastodon