Transformer架构及其在计算机视觉中的革命:从ViT到Swin Transformer

【Transformer】从ViT到DETR:Transformer在CV领域的范式革新与实战解析 本文深入探讨了Transformer在计算机视觉(CV)领域的革命性应用,从ViT到DETR的范式革新。通过全局注意力机制,Transformer突破了CNN的局部限制,在图像分类和目标检测中展现出卓越性能。文章还解析了ViT的数据饥渴症、DETR的端到端训练优势,以及Deformable DETR等改进模型如何加速收敛并提升小目标检测能力。 阅读详情

目录

1. 引言:一场跨领域的架构迁移

2. 技术基础:自注意力机制与Vision Transformer

2.1 自注意力机制核心思想

2.2 Vision Transformer (ViT):开创性的直接应用

3. 关键演进:解决ViT的痛点

3.1 Data-efficient Image Transformer (DeiT):告别大规模预训练

3.2 Swin Transformer:引领视觉任务的通用Backbone

4. 深远影响与领域革命

4.1 对计算机视觉子领域的颠覆

4.2 研究范式的转变

5. 挑战与未来展望

6. 结论

参考文献

1. Vision Transformer (ViT) 完整实现

2. DeiT (Data-efficient Image Transformer) 实现

3. Swin Transformer 核心组件实现

4. 训练示例代码


摘要:本文深入探讨了Transformer架构如何颠覆性地改变计算机视觉(CV)领域的发展范式。始于自然语言处理(NLP)的Transformer模型,通过Vision Transformer (ViT)、Data-efficient Image Transformer (DeiT) 和 Swin Transformer等一系列里程碑式的工作,成功解决了卷积神经网络(CNN)固有的归纳偏置限制,实现了全局建模能力的突破。本文系统分析了这些核心模型的技术创新、理论基础及其对CV各个子领域的深远影响,并展望了未来多模态统一架构的发展趋势。

关键词:Transformer;计算机视觉;自注意力机制;Vision Transformer;Swin Transformer;全局建模


1. 引言:一场跨领域的架构迁移

2017年,Vaswani等人提出的Transformer架构【1】凭借其强大的自注意力机制和并行化训练能力,彻底改变了自然语言处理领域。然而,计算机视觉长期以来一直是卷积神经网络(CNN) 的天下,其固有的局部连接、权重共享和平移不变性等归纳偏置(Inductive Bias)被证明极其有效。

但CNN也存在本质局限:感受野有限,难以建立真正的长距离依赖关系;层次化特征提取过程可能丢失细节信息。Transformer的核心优势——全局建模能力和动态权重分配——恰好针对这些痛点。本文将详细论述Transformer如何跨越模态鸿沟,引领计算机视觉的新革命。

2. 技术基础:自注意力机制与Vision Transformer

2.1 自注意力机制核心思想

自注意力机制允许序列中的每个元素("词"或"图像块")与所有其他元素直接交互,计算其相关性。其核心计算公式为:

Attention(Q, K, V) = softmax(QKᵀ/√dₖ)V

其中Q(Query)、K(Key)、V(Value)均由输入线性变换得到。这种机制实现了:

  1. 全局感受野:每个位置都能直接关注所有其他位置

  2. 动态特征权重:注意力权重根据输入内容动态计算

  3. 强可解释性:注意力图谱可视化模型关注区域

2.2 Vision Transformer (ViT):开创性的直接应用

2020年,Dosovitskiy等人提出的ViT【2】是首个完全摒弃卷积、纯Transformer架构的CV模型。

  • 核心创新:

    1. 图像分块嵌入:将输入图像分割成固定大小的 patches(如16x16),展平后线性投影为序列 tokens,加入位置编码。

    2. 类别Token:借鉴BERT的[CLS] token,用于最终分类。

    3. Transformer编码器:直接使用标准Transformer编码器堆叠。

  • 关键发现:

    • 大规模数据需求:在JFT-300M等超大数据集上预训练后,ViT性能超越当时最佳CNN。

    • ** scalability**:模型越大,性能越好,几乎没有饱和迹象。

    • 全局建模优势:在处理需要全局信息的任务(如场景分类、细粒度识别)上表现出色。

  • 局限性:

    • 缺乏CNN固有的平移不变性和局部性归纳偏置。

    • 需要大规模预训练数据,在小数据集上容易过拟合。

    • 计算复杂度与图像尺寸成平方关系(O(n²)),处理高分辨率图像开销大。

ViT证明了"Everything is a sequence" 的范式在视觉领域同样可行,是CV领域的范式转换点。

3. 关键演进:解决ViT的痛点

3.1 Data-efficient Image Transformer (DeiT):告别大规模预训练

Touvron等人提出的DeiT【3】解决了ViT对海量数据的依赖问题。

  • 核心创新:

    1. 知识蒸馏:引入一个蒸馏token,与class token一起通过注意力机制与教师CNN(如RegNet)交互学习。提出硬标签蒸馏和软标签蒸馏策略。

    2. 数据增强:结合了强数据增强策略(如Rand-Augment, Mixup, CutMix)。

    3. 优化策略调整:改进了优化器和超参数设置。

  • 革命性意义:

    • 仅使用ImageNet-1k数据即可训练出高性能ViT,使其应用门槛大幅降低。

    • 提供了Transformer+CNN协同的新思路,教师CNN的归纳偏置通过蒸馏传递给学生Transformer。

3.2 Swin Transformer:引领视觉任务的通用Backbone

Liu等人提出的Swin Transformer【4】是CV Transformer发展的又一个里程碑,成为了诸多视觉任务的通用骨干网络。

  • 核心创新:

    1. 层级化结构:像CNN一样构建层次化特征图(分辨率逐渐降低,通道数增加),而非ViT的单尺度序列。使其能与现有CV任务 pipeline(如FPN、U-Net)无缝衔接。

    2. 滑动窗口注意力:在不重叠的局部窗口内计算自注意力,将计算复杂度从O(n²) 降至O(n)。

    3. 移位窗口:下一层窗口相对上一层进行移位,使不同窗口间能够交互,实现了跨窗口连接,在保持效率的同时获得了全局建模能力。

  • 革命性意义:

    • 效率与性能的完美平衡:解决了ViT计算复杂度高的问题,并能处理高分辨率图像。

    • 成为通用Backbone:在分类、检测、分割等任务上全面达到SOTA,证明了其作为CV基础模型的潜力。

    • 统一架构的曙光:其设计思想影响了后续诸多工作,推动了视觉乃至多模态通用架构的发展。

4. 深远影响与领域革命

4.1 对计算机视觉子领域的颠覆
  1. 图像分类:ViT/DeiT/Swin系列模型在ImageNet上占据统治地位。

  2. 目标检测:Swin Transformer作为Backbone的检测器(如Mask R-CNN + Swin)在COCO数据集上取得显著提升。

  3. 语义分割:基于Swin的U-PerNet等模型在ADE20K等数据集上刷新纪录,得益于其多尺度特征表示。

  4. 图像生成:Transformer被用于自回归图像生成(如iGPT)和扩散模型(如DiT),展现出强大的序列建模能力。

  5. 视频理解:时空Transformer(TimeSformer等)通过时间、空间两个维度的注意力,有效建模视频中的长时序依赖。

4.2 研究范式的转变
  1. 从归纳偏置到规模化数据驱动:减弱了对人工设计偏置(如卷积的局部性)的依赖,更相信模型能从大规模数据中自行学习所需偏置。

  2. 架构统一趋势:NLP和CV的底层架构趋于统一(均为Transformer),为多模态学习(如CLIP, DALL-E)奠定了坚实基础,推动了“一个模型处理一切”的愿景。

  3. Scaling Law的验证:CV领域也验证了模型规模、数据规模和计算量同步增长的“缩放定律”,进入了大模型时代。

5. 挑战与未来展望

尽管革命性巨大,视觉Transformer仍面临挑战:

  1. 计算复杂度:虽然Swin等模型已优化,但全局注意力仍是计算密集型操作。

  2. 解释性:尽管有注意力图,但其决策过程相比CNN仍更复杂难解。

  3. 落地部署:模型参数量大,对硬件要求高,轻量化部署是难题。

未来展望:

  1. 更高效的注意力机制:如线性注意力、稀疏注意力等将继续发展。

  2. 神经架构搜索:自动搜索更适合视觉任务的Transformer变体。

  3. 与CNN的进一步融合:探索hybrid模型,结合二者优势。

  4. 通往AGI的多模态基础模型:Transformer已成为构建视觉-语言-音频统一大模型的核心基石。

6. 结论

Transformer架构在计算机视觉中的迁移是一场深刻的技术革命。从ViT证明其可行性,到DeiT解决数据依赖,再到Swin Transformer成为通用骨干,这一历程不仅带来了一系列性能卓越的模型,更根本地改变了计算机视觉的研究范式。它打破了视觉与语言的模态壁垒,为构建统一、强大的多模态智能系统铺平了道路,标志着我们向通用人工智能(AGI)又迈进了坚实的一步。


参考文献

[1] Vaswani, A. et al. (2017). Attention Is All You Need. Advances in Neural Information Processing Systems.

[2] Dosovitskiy, A. et al. (2020). An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale. International Conference on Learning Representations (ICLR).

[3] Touvron, H. et al. (2021). Training data-efficient image transformers & distillation through attention. International Conference on Machine Learning (ICML).

[4] Liu, Z. et al. (2021). Swin Transformer: Hierarchical Vision Transformer using Shifted Windows. IEEE/CVF International Conference on Computer Vision (ICCV).

[5] Carion, N. et al. (2020). End-to-End Object Detection with Transformers. European Conference on Computer Vision (ECCV).


附录:核心模型对比表

特性ViTDeiTSwin Transformer
核心贡献证明纯Transformer可用于CV无需大数据预训练通用、高效的Backbone
关键创新图像分块、类别Token蒸馏Token、强数据增强层级结构、滑动窗口、移位窗口
数据需求极高(JFT-300M)低(ImageNet-1K)中等(ImageNet-1K)
计算复杂度O(n²)O(n²)O(n)
主要优势全局建模、Scalability数据效率、训练速度多尺度特征、任务通用性
主要局限数据依赖、计算开销-相对复杂的设计
影响力范式开创者实用化推动者领域统一者

1. Vision Transformer (ViT) 完整实现

python

复制

下载

import torch
import torch.nn as nn
import torch.nn.functional as F
import math
from einops import rearrange, repeat

class PatchEmbedding(nn.Module):
    """将图像分割成patches并嵌入"""
    def __init__(self, img_size=224, patch_size=16, in_channels=3, embed_dim=768):
        super().__init__()
        self.img_size = img_size
        self.patch_size = patch_size
        self.num_patches = (img_size // patch_size) ** 2
        
        self.proj = nn.Conv2d(in_channels, embed_dim, 
                             kernel_size=patch_size, stride=patch_size)
        
    def forward(self, x):
        # x: [B, C, H, W]
        x = self.proj(x)  # [B, E, H/p, W/p]
        x = x.flatten(2)  # [B, E, N]
        x = x.transpose(1, 2)  # [B, N, E]
        return x

class PositionalEncoding(nn.Module):
    """可学习的位置编码"""
    def __init__(self, num_patches, embed_dim):
        super().__init__()
        self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim))
        self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
        
    def forward(self, x):
        # x: [B, N, E]
        batch_size = x.size(0)
        cls_tokens = self.cls_token.expand(batch_size, -1, -1)
        x = torch.cat((cls_tokens, x), dim=1)
        x = x + self.pos_embed
        return x

class MultiHeadSelfAttention(nn.Module):
    """多头自注意力机制"""
    def __init__(self, embed_dim=768, num_heads=12, dropout=0.0):
        super().__init__()
        self.embed_dim = embed_dim
        self.num_heads = num_heads
        self.head_dim = embed_dim // num_heads
        
        assert self.head_dim * num_heads == embed_dim
        
        self.qkv = nn.Linear(embed_dim, embed_dim * 3)
        self.attn_drop = nn.Dropout(dropout)
        self.proj = nn.Linear(embed_dim, embed_dim)
        self.proj_drop = nn.Dropout(dropout)
        
    def forward(self, x, mask=None):
        batch_size, seq_len, embed_dim = x.shape
        
        # 生成Q, K, V
        qkv = self.qkv(x).reshape(batch_size, seq_len, 3, self.num_heads, self.head_dim)
        qkv = qkv.permute(2, 0, 3, 1, 4)  # [3, B, H, N, D]
        q, k, v = qkv[0], qkv[1], qkv[2]
        
        # 计算注意力分数
        attn = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(self.head_dim))
        
        if mask is not None:
            attn = attn.masked_fill(mask == 0, -1e9)
        
        attn = F.softmax(attn, dim=-1)
        attn = self.attn_drop(attn)
        
        # 注意力加权
        x = (attn @ v).transpose(1, 2).reshape(batch_size, seq_len, embed_dim)
        x = self.proj(x)
        x = self.proj_drop(x)
        return x, attn

class MLP(nn.Module):
    """多层感知机"""
    def __init__(self, in_features, hidden_features=None, out_features=None, dropout=0.0):
        super().__init__()
        out_features = out_features or in_features
        hidden_features = hidden_features or in_features
        
        self.fc1 = nn.Linear(in_features, hidden_features)
        self.act = nn.GELU()
        self.fc2 = nn.Linear(hidden_features, out_features)
        self.drop = nn.Dropout(dropout)
        
    def forward(self, x):
        x = self.fc1(x)
        x = self.act(x)
        x = self.drop(x)
        x = self.fc2(x)
        x = self.drop(x)
        return x

class TransformerBlock(nn.Module):
    """Transformer编码块"""
    def __init__(self, embed_dim=768, num_heads=12, mlp_ratio=4.0, dropout=0.0):
        super().__init__()
        self.norm1 = nn.LayerNorm(embed_dim)
        self.attn = MultiHeadSelfAttention(embed_dim, num_heads, dropout)
        self.norm2 = nn.LayerNorm(embed_dim)
        
        mlp_hidden_dim = int(embed_dim * mlp_ratio)
        self.mlp = MLP(embed_dim, mlp_hidden_dim, dropout=dropout)
        
    def forward(self, x, mask=None):
        # 自注意力子层
        attn_output, attn_weights = self.attn(self.norm1(x), mask)
        x = x + attn_output
        
        # MLP子层
        x = x + self.mlp(self.norm2(x))
        return x, attn_weights

class VisionTransformer(nn.Module):
    """完整的ViT模型"""
    def __init__(self, img_size=224, patch_size=16, in_channels=3, 
                 num_classes=1000, embed_dim=768, depth=12, num_heads=12, 
                 mlp_ratio=4.0, dropout=0.1, emb_dropout=0.1):
        super().__init__()
        
        self.patch_embed = PatchEmbedding(img_size, patch_size, in_channels, embed_dim)
        num_patches = self.patch_embed.num_patches
        
        self.pos_embed = PositionalEncoding(num_patches, embed_dim)
        self.dropout = nn.Dropout(emb_dropout)
        
        # Transformer编码器
        self.blocks = nn.ModuleList([
            TransformerBlock(embed_dim, num_heads, mlp_ratio, dropout)
            for _ in range(depth)
        ])
        
        self.norm = nn.LayerNorm(embed_dim)
        self.head = nn.Linear(embed_dim, num_classes)
        
        self.initialize_weights()
        
    def initialize_weights(self):
        # 初始化权重
        for m in self.modules():
            if isinstance(m, nn.Linear):
                nn.init.trunc_normal_(m.weight, std=0.02)
                if m.bias is not None:
                    nn.init.zeros_(m.bias)
            elif isinstance(m, nn.LayerNorm):
                nn.init.zeros_(m.bias)
                nn.init.ones_(m.weight)
                
        # 特殊初始化位置编码
        nn.init.trunc_normal_(self.pos_embed.pos_embed, std=0.02)
        nn.init.trunc_normal_(self.pos_embed.cls_token, std=0.02)
        
    def forward(self, x, return_attn=False):
        # 图像分块嵌入
        x = self.patch_embed(x)
        
        # 添加位置编码和cls token
        x = self.pos_embed(x)
        x = self.dropout(x)
        
        # 通过Transformer块
        attn_weights = []
        for blk in self.blocks:
            x, attn = blk(x)
            if return_attn:
                attn_weights.append(attn)
        
        # 分类头
        x = self.norm(x)
        cls_token = x[:, 0]  # 取cls token对应的特征
        logits = self.head(cls_token)
        
        if return_attn:
            return logits, attn_weights
        return logits

# ViT模型配置
def vit_tiny(patch_size=16, **kwargs):
    return VisionTransformer(patch_size=patch_size, embed_dim=192, depth=12, num_heads=3, **kwargs)

def vit_small(patch_size=16, **kwargs):
    return VisionTransformer(patch_size=patch_size, embed_dim=384, depth=12, num_heads=6, **kwargs)

def vit_base(patch_size=16, **kwargs):
    return VisionTransformer(patch_size=patch_size, embed_dim=768, depth=12, num_heads=12, **kwargs)

def vit_large(patch_size=16, **kwargs):
    return VisionTransformer(patch_size=patch_size, embed_dim=1024, depth=24, num_heads=16, **kwargs)

2. DeiT (Data-efficient Image Transformer) 实现

python

复制

下载

class DistillationToken(nn.Module):
    """DeiT蒸馏token"""
    def __init__(self, embed_dim=768):
        super().__init__()
        self.dist_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
        
    def forward(self, x):
        batch_size = x.size(0)
        dist_tokens = self.dist_token.expand(batch_size, -1, -1)
        x = torch.cat((x, dist_tokens), dim=1)
        return x

class DeiT(VisionTransformer):
    """DeiT模型,支持知识蒸馏"""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # 添加蒸馏token
        self.dist_token = nn.Parameter(torch.zeros(1, 1, self.patch_embed.embed_dim))
        num_patches = self.patch_embed.num_patches
        self.pos_embed = nn.Parameter(
            torch.zeros(1, num_patches + 2, self.patch_embed.embed_dim))
        
        # 蒸馏头
        self.head_dist = nn.Linear(self.patch_embed.embed_dim, self.num_classes)
        
        # 重新初始化
        nn.init.trunc_normal_(self.dist_token, std=0.02)
        nn.init.trunc_normal_(self.pos_embed, std=0.02)
        nn.init.zeros_(self.head_dist.weight)
        
    def forward(self, x):
        # 图像分块嵌入
        B = x.shape[0]
        x = self.patch_embed(x)
        
        # 添加cls token和dist token
        cls_tokens = self.pos_embed.cls_token.expand(B, -1, -1)
        dist_tokens = self.dist_token.expand(B, -1, -1)
        x = torch.cat((cls_tokens, x, dist_tokens), dim=1)
        
        # 添加位置编码
        x = x + self.pos_embed
        x = self.dropout(x)
        
        # Transformer编码
        for blk in self.blocks:
            x, _ = blk(x)
        
        x = self.norm(x)
        
        # 两个输出:cls token和dist token
        x_cls = self.head(x[:, 0])
        x_dist = self.head_dist(x[:, -1])
        
        if self.training:
            return x_cls, x_dist
        else:
            # 推理时平均两个输出
            return (x_cls + x_dist) / 2

class DeiTWithTeacher(nn.Module):
    """完整的DeiT训练框架,包含教师模型"""
    def __init__(self, student_model, teacher_model):
        super().__init__()
        self.student = student_model
        self.teacher = teacher_model
        self.teacher.eval()  # 教师模型固定
        
    def forward(self, x, labels=None):
        student_logits, student_dist_logits = self.student(x)
        
        with torch.no_grad():
            teacher_logits = self.teacher(x)
        
        if labels is not None:
            # 计算损失:学生vs真实标签 + 学生vs教师
            loss_cls = F.cross_entropy(student_logits, labels)
            loss_dist = F.kl_div(
                F.log_softmax(student_dist_logits / 2.0, dim=-1),
                F.softmax(teacher_logits / 2.0, dim=-1),
                reduction='batchmean'
            ) * (2.0 * 2.0)
            
            # 硬标签蒸馏
            teacher_pred = torch.argmax(teacher_logits, dim=1)
            loss_hard = F.cross_entropy(student_dist_logits, teacher_pred)
            
            total_loss = loss_cls + 0.5 * loss_dist + 0.5 * loss_hard
            return total_loss, student_logits
        
        return student_logits

3. Swin Transformer 核心组件实现

python

复制

下载

class WindowAttention(nn.Module):
    """基于窗口的多头自注意力"""
    def __init__(self, dim, window_size, num_heads, qkv_bias=True, attn_drop=0., proj_drop=0.):
        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.relative_position_bias_table = nn.Parameter(
            torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads))
        
        # 生成相对位置索引
        coords_h = torch.arange(self.window_size[0])
        coords_w = torch.arange(self.window_size[1])
        coords = torch.stack(torch.meshgrid([coords_h, coords_w]))
        coords_flatten = torch.flatten(coords, 1)
        relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :]
        relative_coords = relative_coords.permute(1, 2, 0).contiguous()
        relative_coords[:, :, 0] += self.window_size[0] - 1
        relative_coords[:, :, 1] += self.window_size[1] - 1
        relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1
        relative_position_index = relative_coords.sum(-1)
        self.register_buffer("relative_position_index", relative_position_index)
        
        self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
        self.attn_drop = nn.Dropout(attn_drop)
        self.proj = nn.Linear(dim, dim)
        self.proj_drop = nn.Dropout(proj_drop)
        
        nn.init.trunc_normal_(self.relative_position_bias_table, std=.02)
        
    def forward(self, x, mask=None):
        B, N, C = x.shape
        qkv = self.qkv(x).reshape(B, 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
        
        # 添加相对位置偏置
        relative_position_bias = self.relative_position_bias_table[
            self.relative_position_index.view(-1)].view(
            self.window_size[0] * self.window_size[1],
            self.window_size[0] * self.window_size[1], -1)
        relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous()
        attn = attn + relative_position_bias.unsqueeze(0)
        
        if mask is not None:
            nW = mask.shape[0]
            attn = attn.view(B // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0)
            attn = attn.view(-1, self.num_heads, N, N)
        
        attn = F.softmax(attn, dim=-1)
        attn = self.attn_drop(attn)
        
        x = (attn @ v).transpose(1, 2).reshape(B, N, C)
        x = self.proj(x)
        x = self.proj_drop(x)
        return x

def window_partition(x, window_size):
    """将特征图划分成窗口"""
    B, H, W, C = x.shape
    x = x.view(B, H // window_size, window_size, W // window_size, window_size, C)
    windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C)
    return windows

def window_reverse(windows, window_size, H, W):
    """将窗口重组为特征图"""
    B = int(windows.shape[0] / (H * W / window_size / window_size))
    x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1)
    x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1)
    return x

class SwinTransformerBlock(nn.Module):
    """Swin Transformer块"""
    def __init__(self, dim, input_resolution, num_heads, window_size=7, 
                 shift_size=0, mlp_ratio=4., drop=0., attn_drop=0.):
        super().__init__()
        self.dim = dim
        self.input_resolution = input_resolution
        self.num_heads = num_heads
        self.window_size = window_size
        self.shift_size = shift_size
        self.mlp_ratio = mlp_ratio
        
        if min(self.input_resolution) <= self.window_size:
            self.shift_size = 0
            self.window_size = min(self.input_resolution)
        
        self.norm1 = nn.LayerNorm(dim)
        self.attn = WindowAttention(
            dim, window_size=(self.window_size, self.window_size), 
            num_heads=num_heads, attn_drop=attn_drop, proj_drop=drop)
        
        self.norm2 = nn.LayerNorm(dim)
        mlp_hidden_dim = int(dim * mlp_ratio)
        self.mlp = MLP(dim, mlp_hidden_dim, drop=drop)
        
        if self.shift_size > 0:
            # 生成注意力掩码
            H, W = self.input_resolution
            img_mask = torch.zeros((1, H, W, 1))
            h_slices = (slice(0, -self.window_size),
                       slice(-self.window_size, -self.shift_size),
                       slice(-self.shift_size, None))
            w_slices = (slice(0, -self.window_size),
                       slice(-self.window_size, -self.shift_size),
                       slice(-self.shift_size, None))
            cnt = 0
            for h in h_slices:
                for w in w_slices:
                    img_mask[:, h, w, :] = cnt
                    cnt += 1
            
            mask_windows = window_partition(img_mask, self.window_size)
            mask_windows = mask_windows.view(-1, self.window_size * self.window_size)
            attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
            attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0))
            attn_mask = attn_mask.masked_fill(attn_mask == 0, float(0.0))
        else:
            attn_mask = None
        
        self.register_buffer("attn_mask", attn_mask)
        
    def forward(self, x):
        H, W = self.input_resolution
        B, L, C = x.shape
        assert L == H * W, "input feature has wrong size"
        
        shortcut = x
        x = self.norm1(x)
        x = x.view(B, H, W, C)
        
        # 循环移位
        if self.shift_size > 0:
            shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2))
        else:
            shifted_x = x
        
        # 窗口划分
        x_windows = window_partition(shifted_x, self.window_size)
        x_windows = x_windows.view(-1, self.window_size * self.window_size, C)
        
        # 窗口注意力
        attn_windows = self.attn(x_windows, mask=self.attn_mask)
        
        # 窗口重组
        attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C)
        shifted_x = window_reverse(attn_windows, self.window_size, H, W)
        
        # 反向循环移位
        if self.shift_size > 0:
            x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2))
        else:
            x = shifted_x
        
        x = x.view(B, H * W, C)
        x = shortcut + x
        
        # MLP
        x = x + self.mlp(self.norm2(x))
        return x

4. 训练示例代码

python

复制

下载

def train_vit_model():
    """ViT训练示例"""
    # 初始化模型
    model = vit_base(num_classes=1000, img_size=224)
    
    # 数据增强
    transform = transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])
    
    # 优化器设置
    optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.05)
    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=300)
    criterion = nn.CrossEntropyLoss()
    
    # 训练循环
    model.train()
    for epoch in range(300):
        for batch_idx, (data, target) in enumerate(train_loader):
            optimizer.zero_grad()
            output = model(data)
            loss = criterion(output, target)
            loss.backward()
            optimizer.step()
            
            if batch_idx % 100 == 0:
                print(f'Epoch: {epoch} | Batch: {batch_idx} | Loss: {loss.item():.4f}')
        
        scheduler.step()

# 模型测试
def test_model_performance(model, test_loader):
    """测试模型性能"""
    model.eval()
    correct = 0
    total = 0
    
    with torch.no_grad():
        for data, target in test_loader:
            output = model(data)
            _, predicted = torch.max(output.data, 1)
            total += target.size(0)
            correct += (predicted == target).sum().item()
    
    accuracy = 100 * correct / total
    print(f'Test Accuracy: {accuracy:.2f}%')
    return accuracy

# 注意力可视化
def visualize_attention(model, image):
    """可视化注意力图"""
    model.eval()
    with torch.no_grad():
        _, attn_weights = model(image.unsqueeze(0), return_attn=True)
    
    # 选择最后一层的注意力图
    last_layer_attn = attn_weights[-1]
    # 平均所有头的注意力
    attn_map = last_layer_attn.mean(dim=1)[0, 0, 1:]  # 取cls token对其他patch的注意力
    
    # 重塑为2D注意力图
    patch_size = model.patch_embed.patch_size
    h = w = int(math.sqrt(attn_map.shape[0]))
    attn_map = attn_map.reshape(h, w)
    
    return attn_map

if __name__ == "__main__":
    # 示例用法
    model = vit_base(num_classes=1000)
    print(f"ViT参数量: {sum(p.numel() for p in model.parameters()):,}")
    
    # 测试前向传播
    dummy_input = torch.randn(1, 3, 224, 224)
    output = model(dummy_input)
    print(f"输入形状: {dummy_input.shape}")
    print(f"输出形状: {output.shape}")

这些代码提供了完整的Transformer视觉模型实现,包含了:

  1. ViT:基础Vision Transformer架构

  2. DeiT:包含知识蒸馏的改进版本

  3. Swin Transformer:层级化和窗口注意力机制

  4. 训练框架:完整的训练和评估逻辑

计算机视觉4大任务演进史:从AlexNet到SAM,10年关键模型盘点 本文系统梳理了计算机视觉四大核心任务(分类、检测、分割和关键点检测)从AlexNet到SAM的技术演进历程。重点分析了各阶段里程碑模型的创新突破,如ResNet的残差连接、YOLO的实时检测、SAM的通用分割能力,揭示了深度学习如何推动机器感知边界的持续扩展。文章还展望了多模态理解、3D视觉等未来趋势,为计算机视觉研究者提供全景式技术发展脉络。 阅读详情

相关推荐

计算机视觉十年演进:从特征工程到多模态大模型

计算机视觉作为人工智能的核心领域,经历了从传统特征工程到深度学习的范式转移。早期依赖手工特征(如SIFT、HOG)与机器学习算法(如SVM)结合的方式,随着AlexNet、ResNet等CNN架构的出现,特征提取进入自动化阶段。近年来,Vision Transformer和自监督学习技术(如MoCo)的突破,大幅降低了标注数据依赖。在工业落地中,模型轻量化(如知识蒸馏、量化感知训练)和多模态融合(如CLIP)成为关键技术。这些进步推动着视觉感知在医疗影像、工业检测等场景的应用,同时脉冲神经网络、神经渲染等新

weixin_29047919的博客 218

Swin-Transformer 详解

用动画深入解释Swin-Transformer

欢乐的小树的博客 8993

从 AlexNet 到 Swin Transformer:计算机视觉架构演进图谱

在计算机视觉领域,模型架构的演进宛如一场精彩绝伦的技术接力赛,从早期的 CNN 逐步过渡到如今大放异彩的 Transformer,每一次关键节点的突破都为整个领域注入了强大的动力,推动着视觉任务的性能迈向新的高度。本文将沿着时间轴,梳理这一演进路径中具有里程碑意义的模型以及 Transformer 的突破性成果,展现技术迭代的脉络。

u012374012的专栏 1493

ViT、DETR 和 Swin Transformer :基于 Transformer 的计算机视觉(CV)模型

图像输入 → 线性 Patch Embedding → Swin Transformer Block(窗口注意力) → 多尺度特征提取 → 适用于分类、检测、分割。

lqq 1816

vit和swin的区别

是两种基于 Transformer 的视觉模型,它们的核心目标都是将 Transformer 架构应用于计算机视觉任务,但在设计理念、结构和适用场景上有显著差异。

weixin_41012399的博客 1310

Swin Trasnformer 新型的Transformer架构,专为计算机视觉任务设计 解决了传统Transformer从自然语言处理迁移到计算机视觉任务时面临的两大问题

Swin Transformer是由微软亚洲研究院在今年公布的一篇利用transformer架构处理计算机视觉任务的论文。Swin Transformer 在图像分类,图像分割,目标检测等各个领域已经屠榜,在论文中,作者分析表明,Transformer从NLP迁移到CV上没有大放异彩主要有两点原因:1. 两个领域涉及的scale不同,NLP的token是标准固定的大小,而CV的特征尺度变化范围非常大。

万有文的博客 2003

理解CV中的transformer系列(Swin,VIT,DTER)

看了很多transformer的讲解,有人从RNN开始讲起,有人从自注意力开始讲起,但是很少有人从CV开始讲。所以本文收集了一些从CV角度出发来理解transformer的文章。 Transformer在CV界火的原因是?_idol24的博客-优快云博客 https://www.jianshu.com/p/eb199b0fc98c 要正统学习还是去跟李沐大佬: 台大李宏毅21年机器学习课程 self-attention和transformer_哔哩哔哩_bilibili ...

目睹闰土刺猹的瓜的博客 5434

从ViT到Swin,10篇顶会论文看Transformer在CV领域的发展历程

本文梳理了近期10篇Transformer、Attention机制在计算机视觉领域的应用,从ViT到Swin Transformer,完整了解CV Transformer的发展过程。

fareise的博客 3102

4、Swin Transformer:视觉Transformer的革新之路

在此转换之后,一些窗口可能由特征映射中不相邻的几个子窗口组成(这些不连续的部分是不应该参与注意力计算的),因此采用mask机制(NLP中的masking 屏蔽不应该需要的信息)将注意力计算计算限制在每个子窗口内。总结来说,Swin Transformer巧妙地融合了CNN和Transformer的优点,突破了传统Transformer在视觉任务中的局限性,为视觉领域的研究开辟了新的方向,展现出卓越的性能和广泛的应用前景。故而引入SW-MSA通过移位窗口的方式,引入跨窗口连接的同时保持非重叠窗口的高效计算。

sinat_34461199的博客 2405

从ViT到Swin Transformer:一文搞懂Transformer在CV中的进化史(附实战代码)

本文系统梳理了Transformer在计算机视觉领域的演进历程,从开创性的Vision Transformer(ViT)引入全局注意力机制,到DETR实现端到端目标检测,再到里程碑式的Swin Transformer通过滑动窗口和层次化设计高效桥接CNN与ViT。文章深入剖析了各模型的核心思想、技术突破与实战应用,并提供了可运行的代码示例,帮助读者从理论到实践全面掌握这一关键技术脉络。

cola5的博客 713

【计算机视觉】Transformer在视觉中的崛起:ViT、DETR与Swin Transformer

Transformer在视觉中的崛起:ViT、DETR与Swin Transformer,人工智能、大模型、AI深度学习在计算机视觉领域取得了令人瞩目的进展,从早期的卷积神经网络(CNN)主导,到近年来 Transformer 架构的异军突起,彻底改变了视觉任务的格局。Transformer 最初在自然语言处理(NLP)领域大放异彩,以其出色的捕捉长距离依赖关系的能力而闻名。随着研究的深入,研究者们开始探索将 Transformer 应用于视觉领域,这一尝试催生了一系列具有开创性的模型。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 11万+

第七周:ViT 与 Swin Transformer

Transformer是一种用于自然语言处理(NLP)和其他序列到序列任务的深度学习模型架构,它在2017年由Vaswani等人首次提出。Transformer架构引入了自注意力机制(self-attention mechanism),使其在处理序列数据时表现出色。在Transformer出现之前,处理序列数据的主流模型是循环神经网络(RNN) 和长短期记忆网络(LSTM)。但它们存在两个致命缺点,必须逐个词地按顺序处理,无法并行化,导致训练速度非常慢;

一个研究生的博客 746

视觉Transformer技术路线图:从ViT到DeiT再到Swin的架构演进

1. ViT的开创性贡献首次证明了纯Transformer架构可以在视觉任务上超越CNN提出了将图像分割为patch并线性投影的简洁方法展示了Transformer在大规模数据集上的强大表示学习能力为视觉领域引入了自注意力机制的全局建模能力2. DeiT的效率突破通过知识蒸馏解决了ViT对大规模数据的依赖引入蒸馏标记作为知识传递的专用通道设计了高效的训练策略和数据增强方法降低了视觉Transformer的入门门槛,推动了社区研究3. Swin Transformer的架构创新。

2401_88760782的博客 938

swin transformer及其进化过程——学习笔记

swin transformer 是一种基于 transformer 架构的计算机视觉模型,旨在解决传统 transformer 应用在图像处理时存在的计算成本高和效率低的问题。

qq_55009448的博客 1631

从零开始:用Xilinx FPGA实现RISC-V单周期CPU(附完整Verilog代码)

本文详细介绍了如何使用Xilinx FPGA从零开始实现RISC-V单周期CPU,包括开发环境搭建、核心架构设计、Verilog代码实现及功能验证。通过实战指南和完整代码示例,帮助开发者掌握RISC-V CPU设计的关键技术,适用于嵌入式系统和定制化计算场景。

weixin_30664051的博客 547

华为2022硬件逻辑笔试题

单选题(每题两分) 1.以下关于过程赋值的描述,不正确的是() A在非阻塞性过程赋值中,使用赋值符号“< =” B赋值操作符是“=”的过程赋值是阻塞性过程赋值 C在非阻塞性过程赋值中,对目标的赋值是非阻塞的(因为时延),但可预定在将来某个时间步发生(根据时延:如果是0时延,那么在当前时间步结束) D非阻塞性过程赋值在其后所有语句执行前执行 解:D 2.芯片的功耗和下列哪种不强相关_____________。 A资源利用率 B核电压 C组合逻辑级数 D翻转率 解:A 3.下列说

weixin_44072819的博客 1万+

Transformer主干网络——ViT保姆级解析

前言 最近准备跑点主干网络的实验,transformer在cv都杀疯了,整理下最近这些网络的发展以及创新点。从”万恶之源“ViT开始吧。 结构 根据上图介绍大致流程,首先一幅建筑的图片,作者将它分为大小相同的9块, 然后每一块通过展平以及线性层编码成9个特征, 然后额外拼接一个形状一样的一个特征用于分类: 接下来,因为将图片分块他们原始对于图像中的位置信息没有了,所以作者加了一个可以学习的位置特征在原特征上, 注意这里是加,不是拼接,比如说图像块1的特征是(0,0,0,0,0.5,1.0),加上一

qq_37668436的博客 1万+

BERTopic安装指南:hdbscan依赖问题全解析(附解决方案)

本文详细解析了安装BERTopic时常见的hdbscan依赖报错问题,深入剖析了C++编译环境缺失、Python版本兼容性以及包管理渠道混用三大核心原因。针对Windows、macOS和Linux不同系统,提供了创建虚拟环境、使用conda安装预编译包、安装C++编译工具等具体解决方案,帮助用户顺利安装BERTopic并开始文本主题建模实践。

weixin_29061041的博客 216

计算机视觉|Swin Transformer:视觉 Transformer 的新方向

在计算机视觉领域的发展历程中,卷积神经网络(CNN)) 长期占据主导地位。从早期的 LeNet 到后来的 AlexNet、VGGNet、ResNet 等,CNN 在图像分类、目标检测、语义分割等任务中取得了显著成果。然而,CNN 在捕捉全局信息和处理长距离依赖关系方面存在局限性。与此同时,Transformer Architektur 在自然语言处理(NLP)领域表现出色,凭借自注意力机制有效捕捉序列数据中的长距离依赖关系,例如 GPT 系列模型在语言生成和问答系统中的成功应用。

紫雾凌寒 4999

全球AI算力最强公司分析报告

全球AI算力最强公司分析报告显示,Google凭借自研TPU技术和庞大数据中心网络位居榜首,拥有约100万至150万块等效H100 GPU算力。微软、Meta和亚马逊紧随其后,分别拥有75万、55万和25万块等效H100 GPU。中国企业中,百度、阿里巴巴和腾讯表现突出,华为在国产AI芯片领域领先。技术指标上,NVIDIA H100相比A100性能显著提升,但功耗也大幅增加。未来AI算力需求将持续激增,预计2028年全球市场将超2000亿美元。新兴挑战者如xAI可能改变竞争格局。

MyySophia的博客 5722

10、贝叶斯曲线拟合与预测分布详解

本文详细介绍了贝叶斯曲线拟合与预测分布的核心概念与计算方法。从最大似然估计出发,引入参数的先验分布,推导出后验分布与最大后验估计,并进一步展示如何通过积分获得完整的预测分布。文章解析了预测分布中均值与方差的构成,强调贝叶斯方法在量化不确定性方面的优势,涵盖金融、医疗、气象等实际应用场景。最后总结了操作步骤与未来研究方向,帮助读者系统掌握贝叶斯方法在回归问题中的应用。

android的专栏 106

Swin Transformer vs ViT:为什么Swin更适合密集预测任务?深度对比与性能分析

本文深度对比了Swin Transformer与ViT在密集预测任务上的性能差异。Swin Transformer凭借其层次化架构和创新的窗口注意力机制,在目标检测、语义分割等任务中展现出显著优势。它通过局部计算和移位窗口实现了高效的多尺度特征提取,有效解决了ViT全局注意力计算开销大、缺乏层次化表示的痛点,成为当前视觉Transformer领域更适合密集预测任务的骨干网络选择。

qqq44的博客 791

从 SEGW 到 SADL 的那根线,彻底理解 OData Data Source Mapping

本文摘要(150字): 在SAP Gateway的SADL型OData服务开发中,"Map to Data Source"是关键配置环节。与传统RFC映射不同,SADL映射不是针对单个CRUDQ操作,而是建立OData模型与SADL业务实体间的完整语义对应关系。通过属性映射和关联映射,系统能自动处理数据获取和导航请求。映射编辑器支持三种模式:属性映射处理字段对应,关联映射处理模型间关系,动作映射处理业务操作。映射的本质是建立语义翻译表,决定了后续OData请求如何转化为后端业务实体访问,而非简单的字段重命名

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 63

Swin-Transformer详解

本文主要介绍了SwinTransformer的结构及其相关内容,SwinTransformer的设计借鉴了ViT对图像数据的处理方法,通过分层设计结合多个等级的窗口划分降低了计算复杂度,提出位移窗口(Shifted windows)使相邻的窗口之间进行交互,从而达到全局建模的能力。

damadashen的博客 5562

华为MetaERP的交易计税模块(即“税务引擎”)在设计上确实受到Oracle E-Business Tax(EBTax)等国际先进税务架构理念的影响,但结合了华为自身全球化运营、多准则合规、高并发处

华为MetaERP税务引擎在借鉴Oracle EBTax三层架构基础上,创新升级为五层智能税务架构,实现从"税码驱动"到"场景+规则+AI驱动"的转变。该模块采用云原生微服务架构,支持全球190+国家税务规则动态加载,具备实时计税、多准则并行处理、AI风险预警等核心能力,深度适配金税工程等国内合规要求。通过税务数字孪生、RPA机器人等创新,形成自主可控的智能税务中枢,为跨国企业提供全栈国产化税务解决方案,重新定义ERP税务管理范式。

金牌架构师 115

ANSYS软件二次开发(2):二次开发环境搭建与配置

ADK是ANSYS提供的一个开发工具包,它包含了用于二次开发的API、示例代码和开发指南。ADK支持Python和Tcl,但Python因其易用性和强大的社区支持,成为首选。在ANSYS软件中,自定义菜单与工具栏是实现二次开发的重要途径之一,它允许用户根据自己的需求和工作流程,创建个性化的操作界面。通过自定义菜单与工具栏,可以将常用的功能、自定义的脚本或宏命令整合到一起,提高工作效率。原因:材料参数输入错误,或者模型的物理假设与实际情况不符。解决方案仔细校验材料参数,确保与实验数据一致。

kkchenjj的博客 2206
上一篇: 卷积神经网络在图像识别中的演进与应用:从LeNet到ResNet 及算法改造
下一篇: 生成对抗网络与图像合成技术:理论、演进与应用
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

交通上的硅基思维

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值