huggingface/pytorch-image-models · error · ValueError

Expected input ndim in (3, 4, 5); got {x.ndim}.

Error message

Expected input ndim in (3, 4, 5); got {x.ndim}.

What it means

Gemma4Vit (NaFlex ViT supporting variable-resolution patchified input) accepts only 3D, 4D, or 5D tensors in patch embedding forward: (B,N,Ph*Pw*C) pre-patchified, image-like, or (B,C,Ph,Pw,N) layouts. Any other rank raises ValueError with the observed ndim.

Source

Thrown at timm/models/gemma4_vit.py:320

        ph, pw = self.patch_size
        if x.ndim == 4:
            # Raw (B, C, H, W): patchify to C-Ph-Pw (Gemma4 native layout).
            B, _, H, W = x.shape
            if patch_coord is None:
                patch_coord, patch_valid = self._default_patch_coord(B, H // ph, W // pw, x.device)
            x, _ = batch_patchify(x, (ph, pw), pad=False, channels_last=False)  # (B, N, C*Ph*Pw)
        elif x.ndim == 5:
            # (B, N, Ph, Pw, C) pre-patchified unflattened (NaFlex loader convention).
            # Permute channels in from last to second to produce C-Ph-Pw flat.
            x = x.permute(0, 1, 4, 2, 3).reshape(x.shape[0], x.shape[1], -1)
        elif x.ndim == 3:
            # (B, N, Ph*Pw*C) pre-patchified flat in NaFlex P-P-C layout; reinterpret as
            # (B, N, Ph, Pw, C) then permute to C-Ph-Pw flat so input_proj matches layout.
            B, N, PPC = x.shape
            C = PPC // (ph * pw)
            x = x.view(B, N, ph, pw, C).permute(0, 1, 4, 2, 3).reshape(B, N, PPC)
        else:
            raise ValueError(
                f"Expected input ndim in (3, 4, 5); got {x.ndim}."
            )

        if patch_coord is None:
            raise ValueError("patch_coord is required for pre-patchified input.")

        if patch_valid is None:
            sentinel = (patch_coord == -1).all(dim=-1)
            if sentinel.any():
                patch_valid = ~sentinel
            else:
                patch_valid = torch.ones(
                    patch_coord.shape[:2], dtype=torch.bool, device=patch_coord.device,
                )

        # Scale [0, 1] pixels to [-1, 1] (matches original Gemma4's `2 * (pixel_values - 0.5)`)
        x = 2 * (x - 0.5)
        x = self.input_proj(x.to(self.input_proj.weight.dtype))

View on GitHub (pinned to 9a5261e31b)

Solutions

  1. Check x.ndim and x.shape before calling the model; add/collapse the batch dimension so ndim is 3-5
  2. For raw images pass (B, C, H, W) (4D); for pre-patchified input pass (B, N, Ph*Pw*C) with patch_coord
  3. Log shapes at the boundary of your data pipeline to find where the rank changes

Example fix

# before
feats = model(x)  # x.shape == (C, H, W), ndim=3 image-like mixed up with patch input
# after
x = x.unsqueeze(0)  # (1, N, Ph*Pw*C) pre-patchified, ndim=3 with patch_coord passed
feats = model(x, patch_coord=coords)
Defensive patterns

Strategy: type-guard

Validate before calling

assert x.ndim in (3, 4, 5), f'expected ndim 3-5, got {x.ndim}'
if x.ndim == 3:
    assert patch_coord is not None, '3D pre-patchified input requires patch_coord'
out = model(x, patch_coord=patch_coord)

Type guard

def is_valid_gemma4_input(x: torch.Tensor) -> bool:
    return isinstance(x, torch.Tensor) and x.ndim in (3, 4, 5)

Try / catch

try:
    out = model(x)
except ValueError as e:
    if 'Expected input ndim' in str(e):
        raise ValueError(f'pipeline produced ndim={x.ndim}; check squeeze/unsqueeze ops') from e
    raise

Prevention

When it happens

Trigger: Calling model.forward_features / forward on Gemma4ViT with a 2D or 6D tensor, e.g. flattened (N, C) embeddings or a double-batched 6D array.

Common situations: Pipeline bugs where tensors are squeezed/unsqueezed incorrectly (loss of batch dim making input 3D image-like when pre-patchified 3D expected, or extra dims from video (B,T,C,H,W)); feeding raw embeddings instead of images or patch grids.

Related errors


AI-assisted analysis of huggingface/pytorch-image-models@9a5261e31b (2026-08-27). Data as JSON: /api/errors/c8bab2a7cbba6f73. Report an issue: GitHub.