Computer Vision 11 Aug 2026

Scaling Vision Transformers: A Deep Dive into Swin Transformer

#Vision Transformer #Hierarchical Architecture #Shifted Windows #Computer Vision #Image Classification #Object Detection #Semantic Segmentation #Deep Learning

Scaling Vision Transformers: A Deep Dive into Swin Transformer

In the evolution of Computer Vision, the Vision Transformer (ViT) marked a paradigm shift by proving that the Transformer architecture—originally designed for NLP—could outperform Convolutional Neural Networks (CNNs) on large-scale datasets. However, ViT came with two significant "bottlenecks": a lack of multi-scale representations and a computational cost that exploded quadratically with image resolution.

Enter the Swin Transformer. By introducing a hierarchical structure and a clever "shifted window" attention mechanism, Swin bridges the gap between the global modeling power of Transformers and the efficiency of CNNs.


The Core Intuition: Why "Swin"?

The "Swin" in Swin Transformer stands for Shifted Window. To understand why this is necessary, we have to look at the math of attention.

The Complexity Problem

In a standard ViT, every pixel (patch) attends to every other pixel. If an image has $N$ patches, the complexity is: $$\text{Complexity of Global Self-Attention: } \mathcal{O}(N^2) \rightarrow \mathcal{O}(H^2 W^2)$$

For a high-resolution image, this becomes computationally impossible. Swin solves this by computing attention within local, non-overlapping windows. This reduces the complexity to: $$\text{Complexity of Swin Window-based Self-Attention: } \mathcal{O}(N) \rightarrow \mathcal{O}(HW)$$

But there is a catch: if windows never communicate, the model cannot learn global context. Swin solves this by shifting the window partitions in consecutive layers, allowing information to "leak" across window boundaries.


Architectural Blueprint

The Swin Transformer is designed as a hierarchy of stages, mimicking the pooling layers of a ResNet.

1. Hierarchical Feature Maps

Unlike ViT, which maintains a constant resolution, Swin uses Patch Merging. This process concatenates $2 \times 2$ neighboring patches and applies a linear layer to reduce spatial resolution by half while doubling the channel depth. This allows the model to detect small objects in early stages and large, complex objects in later stages.

2. The Swin Block Logic

Each stage consists of multiple Swin Transformer blocks. These blocks alternate between two types of attention:

  • W-MSA (Window Multi-head Self-Attention): Attention is computed within fixed windows.
  • SW-MSA (Shifted Window Multi-head Self-Attention): The window grid is shifted, creating cross-window connections.

High-Level Architecture Flow

flowchart TD %% Input Stage Input["Input Image (B, 3, H, W)"] --> PatchEmbed["Patch Embedding (Conv2d)"] PatchEmbed --> Stage1_In["Feature Map (B, C, H/4, W/4)"] subgraph Stage1 ["Stage 1: Hierarchical Level 1"] S1_B1["Swin Block 1 (W-MSA)"] --> S1_B2["Swin Block 2 (SW-MSA)"] S1_B2 --> S1_Out["Output Stage 1"] end Stage1_In --> S1_B1 S1_Out --> PM1["Patch Merging (Downsample 2x, Dim 2x)"] subgraph Stage2 ["Stage 2: Hierarchical Level 2"] S2_B1["Swin Block 1 (W-MSA)"] --> S2_B2["Swin Block 2 (SW-MSA)"] S2_B2 --> S2_Out["Output Stage 2"] end PM1 --> S2_B1 S2_Out --> PM2["Patch Merging (Downsample 2x, Dim 2x)"] subgraph Stage3 ["Stage 3: Hierarchical Level 3"] S3_B1["Swin Block 1 (W-MSA)"] --> S3_B_Mid["... (Multiple Blocks) ..."] S3_B_Mid --> S3_B_Last["Swin Block N (SW-MSA)"] S3_B_Last --> S3_Out["Output Stage 3"] end PM2 --> S3_B1 S3_Out --> PM3["Patch Merging (Downsample 2x, Dim 2x)"] subgraph Stage4 ["Stage 4: Hierarchical Level 4"] S4_B1["Swin Block 1 (W-MSA)"] --> S4_B2["Swin Block 2 (SW-MSA)"] S4_B2 --> S4_Out["Output Stage 4"] end PM3 --> S4_B1 S4_Out --> GlobalPool["Global Average Pooling"] GlobalPool --> FC["Linear Classification Head"] FC --> Output["Prediction (Classes)"] %% Detail View of a Swin Block subgraph SwinBlockDetail ["Swin Block Internal Logic"] direction TB B_In["Input Tensor"] --> Shift["Cyclic Shift (if SW-MSA)"] Shift --> Partition["Window Partitioning"] Partition --> W_MSA["Window Multi-head Self-Attention (W-MSA)"] W_MSA --> Res1["Residual Connection + LayerNorm"] Res1 --> MLP["MLP (Linear -> GELU -> Linear)"] MLP --> Res2["Residual Connection + LayerNorm"] Res2 --> RevPart["Reverse Partitioning"] RevPart --> RevShift["Reverse Cyclic Shift"] RevShift --> B_Out["Output Tensor"] end S1_B1 -.-> SwinBlockDetail

Implementation in PyTorch

Below is a production-ready implementation of the Swin Transformer core components.

PYTHON
import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Tuple

class WindowAttention(nn.Module):
    """Window-based Multi-head Self-Attention (W-MSA)."""
    def __init__(self, dim, window_size, num_heads):
        super().__init__()
        self.dim = dim
        self.window_size = window_size
        self.num_heads = num_heads
        head_dim = dim // num_heads
        self.scale = head_dim ** -0.5

        self.qkv = nn.Linear(dim, dim * 3)
        self.proj = nn.Linear(dim, dim)

    def forward(self, x):
        B_win, N, C = x.shape
        # Generate Q, K, V tensors
        qkv = self.qkv(x).reshape(B_win, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
        q, k, v = qkv[0], qkv[1], qkv[2] 

        attn = (q @ k.transpose(-2, -1)) * self.scale
        attn = attn.softmax(dim=-1)

        x = (attn @ v).transpose(1, 2).reshape(B_win, N, C)
        return self.proj(x)

class SwinBlock(nn.Module):
    """A single Swin Transformer Block with Shifted Window support."""
    def __init__(self, dim, input_resolution, num_heads, window_size=7, shift_size=0):
        super().__init__()
        self.dim = dim
        self.shift_size = shift_size
        self.window_size = window_size

        self.norm1 = nn.LayerNorm(dim)
        self.attn = WindowAttention(dim, window_size, num_heads)
        self.norm2 = nn.LayerNorm(dim)
        self.mlp = nn.Sequential(
            nn.Linear(dim, 4 * dim),
            nn.GELU(),
            nn.Linear(4 * dim, dim)
        )

    def forward(self, x):
        B, H, W, C = x.shape
        X = x
        
        # 1. Shift Window (SW-MSA)
        if self.shift_size > 0:
            X = torch.roll(X, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2))

        # 2. Partition into windows: (B, H, W, C) -> (B*num_win, ws*ws, C)
        X = X.view(B, H // self.window_size, self.window_size, W // self.window_size, self.window_size, C)
        X = X.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, self.window_size**2, C)

        # 3. Window Attention + MLP
        shortcut = X
        X = self.norm1(X)
        X = self.attn(X)
        X = shortcut + X
        X = X + self.mlp(self.norm2(X))

        # 4. Reverse Partition & Shift
        X = X.view(B, H // self.window_size, W // self.window_size, self.window_size, self.window_size, C)
        X = X.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, C)

        if self.shift_size > 0:
            X = torch.roll(X, shifts=(self.shift_size, self.shift_size), dims=(1, 2))

        return X

class PatchMerging(nn.Module):
    """Downsamples spatial resolution by 2x and increases channel depth by 2x."""
    def __init__(self, input_dim, output_dim):
        super().__init__()
        self.reduction = nn.Linear(4 * input_dim, output_dim, bias=False)

    def forward(self, x):
        B, H, W, C = x.shape
        # Space-to-Depth: Partition into 2x2 patches
        x0 = x[:, 0::2, 0::2, :] 
        x1 = x[:, 1::2, 0::2, :] 
        x2 = x[:, 0::2, 1::2, :] 
        x3 = x[:, 1::2, 1::2, :] 
        
        x = torch.cat([x0, x1, x2, x3], dim=-1) 
        return self.reduction(x)

class SwinTransformer(nn.Module):
    """Swin Transformer Backbone."""
    def __init__(self, img_size=224, patch_size=4, in_chans=3, embed_dim=96, depths=[2, 2, 6, 2], num_heads=[3, 6, 12, 24], window_size=7, num_classes=10):
        super().__init__()
        self.patch_embed = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
        
        self.layers = nn.ModuleList()
        for i in range(len(depths)):
            # Stage Blocks
            stage = nn.Sequential(*[
                SwinBlock(
                    dim=embed_dim * (2**i), 
                    input_resolution=(img_size // (patch_size * (2**i))), 
                    num_heads=num_heads[i], 
                    window_size=window_size, 
                    shift_size=(0 if j % 2 == 0 else window_size // 2)
                ) for j in range(depths[i])
            ])
            self.layers.append(stage)
            
            # Patch Merging (except last stage)
            if i < len(depths) - 1:
                self.layers.append(PatchMerging(embed_dim * (2**i), embed_dim * (2**(i+1))))

        self.head = nn.Linear(embed_dim * (2**(len(depths)-1)), num_classes)

    def forward(self, x):
        x = self.patch_embed(x)
        x = x.permute(0, 2, 3, 1) # (B, H, W, C)

        for layer in self.layers:
            if isinstance(layer, nn.Sequential):
                for block in layer: x = block(x)
            else:
                x = layer(x)

        x = x.mean(dim=(1, 2)) # Global Average Pooling
        return self.head(x)

Key Takeaways for Practitioners

When to use Swin Transformer?

  • Dense Prediction Tasks: Because of its hierarchical nature, Swin is significantly better than ViT for Object Detection and Semantic Segmentation (it works naturally with FPNs).
  • High-Resolution Images: If your input images are larger than $224 \times 224$, the linear complexity of Swin makes it the only viable Transformer choice.

Summary Table: ViT vs. Swin

Feature Vision Transformer (ViT) Swin Transformer
Attention Global (All-to-All) Local Window $\rightarrow$ Shifted Window
Complexity Quadratic $\mathcal{O}(N^2)$ Linear $\mathcal{O}(N)$
Resolution Single Scale Hierarchical (Multi-scale)
Best Use Case Image Classification Detection, Segmentation, Classification

By combining the local inductive bias of CNNs with the long-range dependency modeling of Transformers, the Swin Transformer provides a scalable, efficient, and powerful backbone for the next generation of computer vision models.