{"record":{"id":"e7a2beebdceea71f","repo":"huggingface/pytorch-image-models","slug":"image-size-h-w-must-be-divisible-by-patch","errorCode":null,"errorMessage":"Image size ({H}, {W}) must be divisible by (patch_size * pooling_kernel_size) = ({cell_h}, {cell_w}) when global_pool='soft'. Resize to multiples of ({cell_h}, {cell_w}), or use global_pool='avg'/'none'.","messagePattern":"Image size \\((.+?), (.+?)\\) must be divisible by \\(patch_size \\* pooling_kernel_size\\) = \\((.+?), (.+?)\\) when global_pool='soft'\\. Resize to multiples of \\((.+?), (.+?)\\), or use global_pool='avg'/'none'\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"timm/models/gemma4_vit.py","lineNumber":816,"sourceCode":"        reversible and checkpoint-safe).\n        \"\"\"\n        for mod in self.modules():\n            if isinstance(mod, Gemma4ClippableLinear):\n                mod.use_clipped = enabled\n\n    def _assert_raw_img_conformant(self, x: torch.Tensor) -> None:\n        \"\"\"When using the soft-token pooler, raw-image H/W must divide\n        by ``patch_size * pooling_kernel_size`` so the pool cell grid is integral.\n        Pre-patchified / NaFlex inputs are assumed to be conformant already.\n        \"\"\"\n        if x.ndim != 4 or self.global_pool != 'soft':\n            return\n        H, W = x.shape[-2:]\n        ph, pw = self.patch_size\n        k = self.pooling_kernel_size\n        cell_h, cell_w = ph * k, pw * k\n        if H % cell_h != 0 or W % cell_w != 0:\n            raise ValueError(\n                f\"Image size ({H}, {W}) must be divisible by \"\n                f\"(patch_size * pooling_kernel_size) = ({cell_h}, {cell_w}) when global_pool='soft'. \"\n                f\"Resize to multiples of ({cell_h}, {cell_w}), or use global_pool='avg'/'none'.\"\n            )\n\n    def _encode(\n            self,\n            x: torch.Tensor,\n            position_ids: torch.Tensor,\n            padding_positions: torch.Tensor,\n            block_callback: Optional[Callable[[int, torch.Tensor], None]] = None,\n            max_block_index: Optional[int] = None,\n    ) -> torch.Tensor:\n        \"\"\"RoPE + transformer-block pipeline over already-embedded tokens.\"\"\"\n        B, N = x.shape[:2]\n        rope_cos, rope_sin = self.rotary_emb(x, position_ids)\n\n        attn_mask: Optional[torch.Tensor] = None","sourceCodeStart":798,"sourceCodeEnd":834,"githubUrl":"https://github.com/huggingface/pytorch-image-models/blob/9a5261e31b3b5128526eb2658333b4c0a54464ae/timm/models/gemma4_vit.py#L798-L834","documentation":"Gemma4ViT's forward paths assert the raw image is conformant with soft pooling: H and W must each be divisible by patch_size * pooling_kernel_size (the pooling cell). _assert_raw_img_conformant runs in forward_features/forward/forward_intermediates and raises with the exact required cell dimensions when the raw image is off-grid.","triggerScenarios":"Calling any forward method with global_pool='soft' active and a raw (B,C,H,W) image whose H or W is not a multiple of patch_size*k (e.g. 512 with cell 48).","commonSituations":"Deploying on arbitrary user-uploaded image sizes; fine-tuning with a resolution valid for the patch size but not the pooling grid; switching from avg-pooled to soft-pooled checkpoints without resizing the data pipeline.","solutions":["Resize/pad inputs to multiples of the reported cell (cell_h, cell_w) shown in the message","Use global_pool='avg' or 'none' when arbitrary resolutions must be supported","Validate image dims in the preprocessing step and reject or pad non-conformant sizes"],"exampleFix":"# before\nimg = F.interpolate(img, size=(500, 500))\nout = model(img)  # 500 % 48 != 0\n# after\nimg = F.interpolate(img, size=(480, 480))  # multiple of 48\nout = model(img)","handlingStrategy":"validation","validationCode":"ph, pw = model.patch_size\ncell = (ph * model.pooling_kernel_size, pw * model.pooling_kernel_size)\nH, W = img.shape[-2:]\nif H % cell[0] or W % cell[1]:\n    img = F.interpolate(img, size=((H // cell[0] + 1) * cell[0], (W // cell[1] + 1) * cell[1]))\nout = model(img)","typeGuard":null,"tryCatchPattern":"try:\n    out = model(img)\nexcept ValueError as e:\n    if 'must be divisible by' in str(e):\n        out = model(F.interpolate(img, size=(384, 384), mode='bilinear'))\n    else:\n        raise","preventionTips":["Validate image dimensions against patch_size*k in the dataloader","Resize or pad uploads to valid multiples before inference","Create an avg-pooled model variant for unconstrained-resolution use cases"],"tags":["timm","gemma4-vit","image-size","pooling","validation"],"backgroundTag":"input-size-not-divisible","analyzedSha":"9a5261e31b3b5128526eb2658333b4c0a54464ae","analyzedAt":"2026-08-27T02:34:25.417Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}