IT评测·应用市场-qidao123.com

标题: Vision Transformer (ViT):将Transformer带入计算机视觉的革命性实验(代 [打印本页]

作者: 杀鸡焉用牛刀    时间: 2025-3-18 02:11
标题: Vision Transformer (ViT):将Transformer带入计算机视觉的革命性实验(代
Vision Transformer (ViT):将Transformer带入计算机视觉的革命性实验

作为一名深度学习研究者,假如你对自然语言处置处罚(NLP)领域的Transformer架构了如指掌,那么你一定不会对它在序列建模中的强大本事感到陌生。然而,2021年由Google Research团队在ICLR上发表的论文《AN IMAGE IS WORTH 16x16 WORDS: TRANSFORMERS FOR IMAGE RECOGNITION AT SCALE》却将这一认识的架构带入了一个全新的领域——计算机视觉,提出了Vision Transformer(ViT)。这篇博客将为你详细剖析ViT的原理,连合你对Transformer的深厚理解,带你走进这一开创性的模型。
配景:从NLP到视觉的跨界思考

在NLP领域,Transformer(Vaswani et al., 2017)凭借其自注意力机制(Self-Attention)彻底改变了序列建模的范式。通过预练习大规模语言模型(如BERT、GPT),Transformer展现了惊艳的泛化本事和计算效率。然而,在计算机视觉领域,卷积神经网络(CNN)一直是无可争议的主宰,凭借其局部性、平移不变性等归纳偏置(Inductive Bias),在图像分类、目标检测等任务中占据主导职位。
ViT的核心思想大胆而简单:假如Transformer在NLP中能处置处罚单词序列(Token Sequence),为什么不能将图像也看作一种序列呢?作者提出,通过将图像分割成固定大小的Patch,并将这些Patch作为输入序列直接交给标准Transformer处置处罚,可以完全抛弃CNN的架构。这一实验不仅挑战了CNN的统治职位,还展现了大规模数据预练习对模型性能的深远影响。
ViT的架构:从图像到序列的转变


来源:https://arxiv.org/pdf/2010.11929
ViT的架构设计险些是对NLP Transformer的“照搬”,但在输入处置处罚上做了一些关键调整。以下是ViT的核心步调,信赖你会发现它与NLP中的处置处罚流程有惊人的相似之处:
1. 图像分块与嵌入(Patch Embedding)

在NLP中,输入是一个单词序列,每个单词通过词嵌入(Word Embedding)映射为固定维度的向量。ViT将这一思想移植到图像上:

最终,输入序列为:
                                                    z                               0                                      =                            [                                       x                               class                                      ;                                       x                               p                               1                                      E                            ;                                       x                               p                               2                                      E                            ;                            ⋯                             ;                                       x                               p                               N                                      E                            ]                            +                                       E                               pos                                            \mathbf{z}_0 = [\mathbf{x}_{\text{class}}; \mathbf{x}_p^1 \mathbf{E}; \mathbf{x}_p^2 \mathbf{E}; \cdots; \mathbf{x}_p^N \mathbf{E}] + \mathbf{E}_{\text{pos}}                     z0​=[xclass​;xp1​E;xp2​E;⋯;xpN​E]+Epos​
此中,(                                    E                         ∈                                   R                                       (                                           P                                  2                                          ⋅                               C                               )                               ×                               D                                                 \mathbf{E} \in \mathbb{R}^{(P^2 \cdot C) \times D}                  E∈R(P2⋅C)×D ) 是投影矩阵,(                                              E                            pos                                  ∈                                   R                                       (                               N                               +                               1                               )                               ×                               D                                                 \mathbf{E}_{\text{pos}} \in \mathbb{R}^{(N+1) \times D}                  Epos​∈R(N+1)×D ) 是位置编码。
2. 分类标记(Class Token)

假如你认识BERT(可以参考笔者的另一篇博客:BERT模型详解:双向Transformer的语言理解革命(预练习和微调代码实现)),你一定知道它的 [CLS] Token。ViT也借鉴了这一设计,在Patch序列前添加一个可学习的分类标记(Class Token),记为 (                                              x                            class                                       \mathbf{x}_{\text{class}}                  xclass​ )。这个Token的作用是在Transformer编码后,作为整个图像的表示,用于后续分类任务。
3. Transformer编码器

接下来,ViT将序列 (                                              z                            0                                       \mathbf{z}_0                  z0​ ) 输入标准Transformer编码器,与NLP中的架构完全一致:

经过 (                                    L                              L                  L ) 层Transformer编码后,输出序列为 (                                              z                            L                                       \mathbf{z}_L                  zL​ )。此中,(                                              z                            L                            0                                       \mathbf{z}_L^0                  zL0​ )(即Class Token的输出)被用作图像表示:
                                         y                            =                            LN                            ⁡                            (                                       z                               L                               0                                      )                                  \mathbf{y} = \operatorname{LN}(\mathbf{z}_L^0)                     y=LN(zL0​)
4. 分类头

在预练习阶段,(                                    y                              \mathbf{y}                  y ) 被送入一个带有单隐藏层的MLP进行分类;在微调阶段,则简化为一个线性层,输出类别数 (                                    K                              K                  K ) 的预测。
关键特性:极简与归纳偏置的取舍

ViT的设计极简,险些没有引入图像特有的归纳偏置:

这种“无偏置”设计带来了一个紧张标题:ViT是否能在数据量不敷时泛化良好?答案是否定的。论文指出,当在中小规模数据集(如ImageNet,1.3M图像)上从头练习时,ViT的体现不如划一规模的ResNet。然而,当预练习数据规模扩大到14M(ImageNet-21k)或300M(JFT-300M)时,ViT开始展现出超越CNN的潜力。这表明,大规模数据可以弥补归纳偏置的缺失。
性能体现:数据驱动的胜利

ViT在多个基准测试中取得了令人瞩目的成绩:

更紧张的是,ViT的预练习计算成本远低于CNN。例如,ViT-H/14在JFT-300M上预练习耗时2500 TPUv3-core-days,而BiT-L需要9900,Noisy Student更是高达12300。这种效率得益于Transformer的并行性和可扩展性。
深入分析:ViT如何“看”图像?

为了理解ViT的内部机制,论文提供了一些可视化分析:

自监督预练习的开端探索

假如你对BERT的掩码语言建模(Masked Language Modeling)情有独钟,那么ViT的开端自监督实验可能会让你兴奋。作者实验了掩码Patch预测(Masked Patch Prediction),类似BERT的策略,将50%的Patch更换为掩码,并预测其均值颜色。在JFT-300M上预练习后,ViT-B/16的ImageNet精度从头练习的77.9%提升至79.9%,只管仍落伍于监督预练习的83.97%。这表明自监督ViT有潜力,但仍需进一步优化。
对研究者的启示

对于认识NLP的你,ViT不仅是一个视觉模型,更是一个跨领域思想的桥梁:

结语

Vision Transformer以其简洁而大胆的设计,冲破了CNN在计算机视觉中的把持职位。它告诉我们,当数据和算力足够时,模型可以从头学习复杂的空间关系,而无需依赖传统归纳偏置。作为一名NLP领域的深度学习研究者,你是否也从中看到了Transformer无限可能的未来?欢迎留言分享你的见解!

参考文献
Dosovitskiy, A., et al. (2021). An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale. ICLR 2021.
ViT代码实现

以下是一个基于 PyTorch 的 Vision Transformer (ViT) 的完整、可运行的代码实现。这个实现参考了原始论文《An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale》的核心思想,包含了 Patch Embedding、Multi-Head Self-Attention 和 Transformer Encoder 的紧张组件,并以 MNIST 数据集为例进行练习和测试。为了确保代码可运行,尽量保持简洁并提供解释。
环境要求


完整代码

  1. import torch
  2. import torch.nn as nn
  3. import torch.optim as optim
  4. import torch.nn.functional as F
  5. from torch.utils.data import DataLoader
  6. import torchvision
  7. import torchvision.transforms as transforms
  8. # 超参数设置
  9. image_size = 28  # MNIST 图像大小为 28x28
  10. patch_size = 7   # Patch 大小为 7x7
  11. num_patches = (image_size // patch_size) ** 2  # 16 个 Patch
  12. patch_dim = patch_size * patch_size * 1  # 输入通道为 1 (灰度图)
  13. dim = 64         # 嵌入维度
  14. depth = 6        # Transformer 层数
  15. heads = 8        # 注意力头数
  16. mlp_dim = 128    # MLP 隐藏层维度
  17. num_classes = 10 # MNIST 类别数
  18. dropout = 0.1    # Dropout 率
  19. # 设备设置
  20. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  21. # Patch Embedding 模块
  22. class PatchEmbedding(nn.Module):
  23.     def __init__(self, image_size, patch_size, patch_dim, dim, dropout):
  24.         super().__init__()
  25.         self.num_patches = (image_size // patch_size) ** 2
  26.         # 线性投影:将 Patch 展平并映射到 dim 维度
  27.         self.proj = nn.Linear(patch_dim, dim)
  28.         # 位置编码
  29.         self.pos_embedding = nn.Parameter(torch.randn(1, self.num_patches + 1, dim))
  30.         # CLS Token
  31.         self.cls_token = nn.Parameter(torch.randn(1, 1, dim))
  32.         self.dropout = nn.Dropout(dropout)
  33.     def forward(self, x):
  34.         B = x.shape[0]  # Batch Size
  35.         # 将图像分割为 Patch 并展平
  36.         x = x.unfold(2, patch_size, patch_size).unfold(3, patch_size, patch_size)  # (B, C, H/p, W/p, p, p)
  37.         x = x.permute(0, 2, 3, 1, 4, 5).contiguous()  # (B, H/p, W/p, C, p, p)
  38.         x = x.view(B, self.num_patches, -1)  # (B, num_patches, patch_dim)
  39.         # 线性投影
  40.         x = self.proj(x)  # (B, num_patches, dim)
  41.         # 添加 CLS Token
  42.         cls_tokens = self.cls_token.expand(B, -1, -1)  # (B, 1, dim)
  43.         x = torch.cat((cls_tokens, x), dim=1)  # (B, num_patches + 1, dim)
  44.         # 添加位置编码
  45.         x = x + self.pos_embedding
  46.         x = self.dropout(x)
  47.         return x
  48. # 多头自注意力模块
  49. class MultiHeadAttention(nn.Module):
  50.     def __init__(self, dim, heads, dropout):
  51.         super().__init__()
  52.         self.heads = heads
  53.         self.scale = (dim // heads) ** -0.5
  54.         self.qkv = nn.Linear(dim, dim * 3, bias=False)  # 查询、键、值投影
  55.         self.dropout = nn.Dropout(dropout)
  56.         self.proj = nn.Linear(dim, dim)  # 输出投影
  57.     def forward(self, x):
  58.         B, N, C = x.shape  # (Batch, num_patches + 1, dim)
  59.         # 生成 Q, K, V
  60.         qkv = self.qkv(x).reshape(B, N, 3, self.heads, C // self.heads).permute(2, 0, 3, 1, 4)
  61.         q, k, v = qkv[0], qkv[1], qkv[2]  # (B, heads, N, dim/heads)
  62.         # 注意力计算
  63.         attn = (q @ k.transpose(-2, -1)) * self.scale  # (B, heads, N, N)
  64.         attn = F.softmax(attn, dim=-1)
  65.         attn = self.dropout(attn)
  66.         # 加权求和
  67.         x = (attn @ v).transpose(1, 2).reshape(B, N, C)  # (B, N, dim)
  68.         x = self.proj(x)
  69.         x = self.dropout(x)
  70.         return x
  71. # Transformer Encoder 层
  72. class TransformerEncoderLayer(nn.Module):
  73.     def __init__(self, dim, heads, mlp_dim, dropout):
  74.         super().__init__()
  75.         self.norm1 = nn.LayerNorm(dim)
  76.         self.attn = MultiHeadAttention(dim, heads, dropout)
  77.         self.norm2 = nn.LayerNorm(dim)
  78.         self.mlp = nn.Sequential(
  79.             nn.Linear(dim, mlp_dim),
  80.             nn.GELU(),
  81.             nn.Dropout(dropout),
  82.             nn.Linear(mlp_dim, dim),
  83.             nn.Dropout(dropout)
  84.         )
  85.     def forward(self, x):
  86.         x = x + self.attn(self.norm1(x))  # 残差连接
  87.         x = x + self.mlp(self.norm2(x))  # 残差连接
  88.         return x
  89. # Vision Transformer 模型
  90. class ViT(nn.Module):
  91.     def __init__(self, image_size, patch_size, patch_dim, dim, depth, heads, mlp_dim, num_classes, dropout):
  92.         super().__init__()
  93.         self.patch_embed = PatchEmbedding(image_size, patch_size, patch_dim, dim, dropout)
  94.         self.layers = nn.ModuleList([
  95.             TransformerEncoderLayer(dim, heads, mlp_dim, dropout) for _ in range(depth)
  96.         ])
  97.         self.norm = nn.LayerNorm(dim)
  98.         self.head = nn.Linear(dim, num_classes)
  99.     def forward(self, x):
  100.         x = self.patch_embed(x)
  101.         for layer in self.layers:
  102.             x = layer(x)
  103.         x = self.norm(x)
  104.         cls_token = x[:, 0]  # 提取 CLS Token
  105.         x = self.head(cls_token)
  106.         return x
  107. # 数据加载
  108. transform = transforms.Compose([
  109.     transforms.ToTensor(),
  110.     transforms.Normalize((0.1307,), (0.3081,))  # MNIST 均值和标准差
  111. ])
  112. train_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transform, download=True)
  113. test_dataset = torchvision.datasets.MNIST(root='./data', train=False, transform=transform, download=True)
  114. train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
  115. test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
  116. # 初始化模型、损失函数和优化器
  117. model = ViT(
  118.     image_size=image_size,
  119.     patch_size=patch_size,
  120.     patch_dim=patch_dim,
  121.     dim=dim,
  122.     depth=depth,
  123.     heads=heads,
  124.     mlp_dim=mlp_dim,
  125.     num_classes=num_classes,
  126.     dropout=dropout
  127. ).to(device)
  128. criterion = nn.CrossEntropyLoss()
  129. optimizer = optim.Adam(model.parameters(), lr=0.001)
  130. # 训练函数
  131. def train(model, train_loader, criterion, optimizer, epoch):
  132.     model.train()
  133.     running_loss = 0.0
  134.     for i, (images, labels) in enumerate(train_loader):
  135.         images, labels = images.to(device), labels.to(device)
  136.         optimizer.zero_grad()
  137.         outputs = model(images)
  138.         loss = criterion(outputs, labels)
  139.         loss.backward()
  140.         optimizer.step()
  141.         running_loss += loss.item()
  142.         if i % 100 == 99:
  143.             print(f'[Epoch {epoch+1}, Batch {i+1}] Loss: {running_loss / 100:.3f}')
  144.             running_loss = 0.0
  145. # 测试函数
  146. def test(model, test_loader):
  147.     model.eval()
  148.     correct = 0
  149.     total = 0
  150.     with torch.no_grad():
  151.         for images, labels in test_loader:
  152.             images, labels = images.to(device), labels.to(device)
  153.             outputs = model(images)
  154.             _, predicted = torch.max(outputs.data, 1)
  155.             total += labels.size(0)
  156.             correct += (predicted == labels).sum().item()
  157.     accuracy = 100 * correct / total
  158.     print(f'Test Accuracy: {accuracy:.2f}%')
  159.     return accuracy
  160. # 主训练循环
  161. num_epochs = 10
  162. for epoch in range(num_epochs):
  163.     train(model, train_loader, criterion, optimizer, epoch)
  164.     test(model, test_loader)
复制代码
代码分析

运行效果

在 CPU 或 GPU 上运行此代码,将下载 MNIST 数据集并开始练习。预期效果:

注意事项


参考


希望这个实现对你理解 ViT 的工作原理有所帮助!假如需要更复杂的版本(例如支持 ImageNet 数据集或预练习),可以进一步扩展。欢迎反馈或提问!

ViT 层的行为分析

弁言

作为深度学习研究者,你对 Transformer 在 NLP 中的层级行为可能已非常认识:早期层关注语法和局部依赖,深层捕获语义和长距离关系。那么,在计算机视觉的 Vision Transformer(ViT)中,层的行为是否类似?本文将深入探讨 ViT 的层级特征提取,特别关注其与 CNN 的对比,并分析第 31、32 层等深层的特性,连合现有研究提供全面见解。
CNN 的层级特征提取:从低级到高级

CNN (具体可以参考笔者的另一篇博客:卷积神经网络(CNN):深度剖析其原理与特性)的强大之处在于其深层结构:

这种条理结构是 CNN 的归纳偏置(inductive bias),使其在数据量有限时体现良好。
ViT 的层级行为:从 Patch 到语义

ViT 的输入是将图像分割为固定大小的 Patch(如 16x16),每个 Patch 线性嵌入后添加位置编码,输入 Transformer 编码器。编码器由多头自注意力(MSA)和多层感知机(MLP)块交替构成。以下是层的行为分析:
早期层(如第 1、2 层)


中间层(如第 15、16 层)


深层(如第 31、32 层)


与 CNN 的对比:条理结构的差异


研究论文与结论

以下是关键研究:

结论:

表 1:ViT 与 CNN 层级行为的对比

特性CNNViT早期层关注低级特征(如边沿、纹理)局部和全局信息,部分头全局关注深层关注高级语义(如对象部件、整体)更全局,聚焦任务相关语义地域条理结构明确,低级到高级逐步构建数据驱动,无严格条理,灵活性高归纳偏置强(如局部性、平移不变性)弱,依赖大数据练习深层(如第 31、32 层)特性捕获全局对象,明确语义假设深,聚焦语义,任务依赖 讨论与未来方向

对于第 31、32 层,当前研究多集中于 12-24 层的标准 ViT 模型,深层(如 32 层)行为需更多实验验证。未来可探索自监督预练习(如掩码 Patch 预测)如何影响深层特征,及如何设计更高效的层级结构,连合 CNN 和 ViT 的优势。

跋文

2025年3月12日19点34分于上海,在Grok 3大模型辅助下完成。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4