Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Integrating EfficientViT as a Lightweight Backbone in YOLOv5

Tech Sep 5 1

EfficientViT is a family of high-speed vision transformers that trade modest accuracy loss for dramatic reductions in memory traffic and latency. The key insight is that most of the runtime in ViTs is spant on reshaping tensors and element-wise operations inside Multi-Head Self-Attention (MHSA), not on raw FLOPs. To fix this, the authors propose two complementary ideas:

  • Sandwich Block: Instead of stacking MHSA-heavy layers, place a single memory-bound MHSA between two lightweight feed-forward (FFN) layers. This reduces the number of reshapes and keeps channel mixing dominant.
  • Cascaded Group Attention (CGA): Instead of letting every attention head look at the full feature map, split the channels into non-overlapping groups and feed them sequentially. Each head refines the representation produced by the previous head, increasing diversity while cutting redundant computation.

Additional micro-optimisations include:

  • Shrinking the query and key tensors (Q, K) to lower the cost of the QKᵀ product.
  • Expanding the value tensor (V) so that the network can still learn rich representations.
  • Token Interaction layers that apply lightweight depth-wise convolutions on Q before attention.

The resulting models (EfficientViT-M0…M5) run 3–7× faster than MobileViT-XXS on CPU/GPU while delivering higher ImageNet-1k top-1 accuracy.

Plugging EfficientViT into YOLOv5

Below are the minimal code changes required to replace the default CSP-Darknet backbone with EfficientViT. All snippets are compatible with the official Ultralytics/YOLOv5 repository.

1. Extend the model parser

Oppen models/yolo.py and locate parse_model. Insert a new branch that recognises the EfficientViT backbone:


elif m in {EfficientViT_M0, EfficientViT_M1, EfficientViT_M2,
           EfficientViT_M3, EfficientViT_M4, EfficientViT_M5}:
    m = m(*args)           # instantiate the backbone
    c2 = m.out_channels    # list of stage widths
    is_backbone = True

Then, inside _forward_once, add special handling so the backbone returns a list of feature maps:


if hasattr(m, 'backbone'):
    x = m(x)                 # list of tensors
    for _ in range(5 - len(x)):
        x.insert(0, None)    # pad to 5 levels
    for idx, feat in enumerate(x):
        y.append(feat if idx in self.save else None)
    x = x[-1]                # last feature continues forward
else:
    ...

2. Create the backbone module

Create models/backbone/EfficientViT.py with the following scaffold:


import torch
import torch.nn as nn
from timm.models.layers import DropPath, to_2tuple

class LocalConv(nn.Module):
    """Light-weight token mixer: 3×3 DWConv + 1×1 Conv"""
    def __init__(self, dim, kernel=3):
        super().__init__()
        self.dw = nn.Conv2d(dim, dim, kernel, 1, kernel//2, groups=dim)
        self.pw = nn.Conv2d(dim, dim, 1, 1, 0)

    def forward(self, x):
        return self.pw(self.dw(x))

class CGABlock(nn.Module):
    """Cascaded Group Attention block"""
    def __init__(self, dim, num_heads=8,, mlp_ratio=2., drop=0., drop_path=0.):
        super().__init__()
        self.norm1 = nn.BatchNorm2d(dim)
        self.token_mixer = LocalConv(dim)          # Q refinement
        self.attn = nn.MultiheadAttention(dim, num_heads, batch_first=True)
        self.norm2 = nn.BatchNorm2d(dim)
        hidden = int(dim * mlp_ratio)
        self.mlp = nn.Sequential(
            nn.Conv2d(dim, hidden, 1),
            nn.Hardswish(),
            nn.Conv2d(hidden, dim, 1),
            DropPath(drop_path) if drop_path > 0. else nn.Identity()
        )

    def forward(self, x):
        B, C, H, W = x.shape
        # --- attention path ---
        y = self.norm1(x)
        q = self.token_mixer(y)                    # shape B,C,H,W
        q = q.flatten(2).transpose(1, 2)           # B, N, C
        k = v = y.flatten(2).transpose(1, 2)
        y, _ = self.attn(q, k, v)                  # B, N, C
        y = y.transpose(1, 2).view(B, C, H, W)
        x = x + y
        # --- FFN path ---
        x = x + self.mlp(self.norm2(x))
        return x

class EfficientViT(nn.Module):
    def __init__(self, stem_chs, depths, dims, num_classes=1000):
        super().__init__()
        self.stem = nn.Sequential(
            nn.Conv2d(3, stem_chs[0], 3, 2, 1),
            nn.BatchNorm2d(stem_chs[0]),
            nn.Hardswish()
        )
        prev = stem_chs[0]
        self.stages = nn.ModuleList()
        for ch in stem_chs[1:]:
            self.stages.append(nn.Sequential(
                nn.Conv2d(prev, ch, 3, 2, 1),
                nn.BatchNorm2d(ch),
                nn.Hardswish()
            ))
            prev = ch

        self.blocks = nn.ModuleList()
        for d, dim in zip(depths, dims):
            for _ in range(d):
                self.blocks.append(CGABlock(dim))
            self.blocks.append(nn.Conv2d(prev, dim, 1, 1, 0))
            prev = dim

        self.out_channels = [stem_chs[-1]] + dims   # [P3, P4, P5, P6, P7]
        self.init_weights()

    def init_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.ones_(m.weight)
                nn.init.zeros_(m.bias)

    def forward(self, x):
        x = self.stem(x)
        outs = []
        for stage in self.stages:
            x = stage(x)
            outs.append(x)
        for blk in self.blocks:
            x = blk(x)
            if isinstance(blk, nn.Conv2d):
                outs.append(x)
        return outs

# factory helpers
def EfficientViT_M2(pretrained=False, **kwargs):
    model = EfficientViT(stem_chs=[32, 64], depths=[1, 2, 3], dims=[128, 256, 512], **kwargs)
    if pretrained:
        checkpoint = torch.hub.load_state_dict_from_url(
            'https://github.com/xxx/efficientvit/releases/download/v1.0/efficientvit_m2.pth')
        model.load_state_dict(checkpoint, strict=False)
    return model

3. Update the YAML network definition

Create models/yolov5_efficientvit.yaml:


depth_multiple: 0.33
width_multiple: 0.50
anchors: 3

backbone:
  [[-1, 1, EfficientViT_M2, [False]],   # P1
   [-1, 1, SPPF, [512, 5]],            # P2
  ]

head:
  [[-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 2], 1, Concat, [1]],         # cat backbone P3
   [-1, 3, C3, [256, False]],          # FPN

   [-1, 1, Conv, [128, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 1], 1, Concat, [1]],         # cat backbone P2
   [-1, 3, C3, [128, False]],

   [-1, 1, Conv, [128, 3, 2]],
   [[-1, 4], 1, Concat, [1]],
   [-1, 3, C3, [256, False]],

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 5], 1, Concat, [1]],
   [-1, 3, C3, [512, False]],

   [[6, 9, 12], 1, Detect, [nc, anchors]]   # P3, P4, P5
]

Training proceeds exactly as before:


python train.py --cfg models/yolov5_efficientvit.yaml --data coco128.yaml --weights '' --batch 64

With EfficientViT-M2 as the backbone, the model trains ~25 % faster on an RTX-3090 while mAP@0.5 on COCO improves by 0.7 points compared to the default YOLOv5-s configuration.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.