从NLP到CV:手把手教你用Transformers实现图像分类(附ViT实战代码)

图像识别中的 Vision Transformers (ViT) 在 ICLR 2021 上作为会议论文发表的一篇研究论文中介绍了 Vision Transformer (ViT) 模型架构,题为“An Image is Worth 16*16 Words: Transformers for Image Recognition at Scale”。它由 Neil Houlsby、Alexey Dosovitskiy 以及Google 研究大脑团队的另外 10 位作者开发和发布。微调代码 和预训练的ViT模型可在 Google 研究团队的 GitHub 上获取。你可以在。 阅读详情

从NLP到CV:手把手教你用Transformers实现图像分类(附ViT实战代码)

如果你是一位熟悉BERT、GPT等模型的NLP开发者,第一次听说Vision Transformer(ViT)时,可能会和我当初一样,心里冒出几个问号:那个处理单词序列的Transformer,怎么就能看懂图片了?把图片切成块,当成“单词”喂进去,真的能行吗?这听起来有点像用螺丝刀去拧螺母,工具虽然强大,但真的顺手吗?

事实上,这种“跨界”不仅可行,而且在足够数据量的加持下,其表现甚至开始超越统治计算机视觉领域多年的卷积神经网络(CNN)。对于希望将NLP领域经验快速复用到CV任务的开发者而言,理解并上手ViT,正成为一项极具价值的前沿技能。本文不会止步于理论探讨,我们将直接切入实战,从零开始,一步步构建一个能够识别图像的ViT模型。我会分享代码实现中的关键细节、调试经验,以及如何避免那些我踩过的“坑”。无论你是想拓展技术栈,还是为下一个项目寻找更优的视觉解决方案,这里都有你需要的干货。

1. 跨界思维:为什么Transformer能“看见”图像?

要理解ViT,首先要打破一个思维定式:我们并非要让Transformer去“理解”像素。相反,我们做的是形式转换——将图像这种结构化的二维数据,转化为Transformer天生擅长的处理对象:一维序列。

在NLP中,Transformer的输入是一个个单词(或子词)的嵌入向量序列。每个单词是一个语义单元。ViT的灵感正在于此:它将一张图片分割成多个固定大小的图像块(例如16x16像素),每个图像块经过线性投影后,就变成了一个“视觉单词”的嵌入向量。这些“视觉单词”按顺序排列,就构成了Transformer编码器的输入序列。

这里有一个至关重要的设计:位置嵌入。 在NLP中,“我 爱 你”和“你 爱 我”含义截然不同,位置信息是关键。对于图像,空间位置关系同样蕴含重要信息(天空通常在顶部,草地通常在底部)。ViT为每个图像块附加一个可学习的位置嵌入向量,这样模型就能知道“天空”这个块在上面,“草地”这个块在下面。

与CNN的“归纳偏置”(即先验假设,如局部性、平移不变性)不同,ViT的“归纳偏置”极少。它没有内置的卷积核来强制关注局部邻域,自注意力机制允许模型在网络的第一层就建立任意两个图像块之间的全局关联。这意味着,模型需要从海量数据中自己学习到“相邻的块通常更相关”、“物体的部分应该聚集在一起”这些视觉规律。这也解释了为什么ViT在大规模数据集(如JFT-300M)上预训练后,性能会爆发式增长——数据量足够大时,模型自己学到的规律,可能比我们人为设计的卷积归纳偏置更优、更灵活。

注意:对于中小型数据集,CNN因其强大的归纳偏置,通常比同等规模的ViT更容易训练、表现更好。ViT的优势需要大数据来激发。

下表直观对比了CNN与ViT在处理图像时的核心思想差异:

特性维度卷积神经网络 (CNN)视觉Transformer (ViT)
基本处理单元局部感受野内的像素整张图像切分后的图像块
核心操作卷积、池化多头自注意力、前馈网络
归纳偏置强(局部性、平移不变性)极弱(仅存在于图像块切割和位置嵌入)
信息融合范围由浅到深,逐步扩大(感受野)任意层均可实现全局交互
数据依赖性中小规模数据即可有效学习严重依赖大规模预训练数据
可解释性特征图可视化,相对直观注意力图可显示模型关注区域

理解了这套“化图为文”的跨界逻辑,我们就可以开始动手,将理论转化为代码了。

2. 环境搭建与数据准备

工欲善其事,必先利其器。我们选择PyTorch作为深度学习框架,并利用Hugging Face的 transformers 库,它提供了高质量、易用的ViT模型实现。当然,为了更透彻地理解,我们也会从更底层的地方开始构建一些关键组件。

首先,创建一个干净的Python环境并安装依赖:

# 创建并激活虚拟环境(可选但推荐)
conda create -n vit-tutorial python=3.8
conda activate vit-tutorial

# 安装核心依赖
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118  # 根据你的CUDA版本调整
pip install transformers datasets timm pillow matplotlib
pip install accelerate -U  # 用于混合精度训练等加速

接下来,我们准备数据。这里以经典的CIFAR-10数据集为例,它包含10个类别的6万张32x32小图片。虽然ViT论文中强调大数据,但我们可以通过一些技巧(如更强的数据增强、更小的图像块)在小数据集上进行有益的探索。

import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 定义训练和测试的数据增强与转换
# 对于小数据集,强数据增强至关重要
train_transform = transforms.Compose([
    transforms.RandomResizedCrop(224),  # ViT通常输入224x224
    transforms.RandomHorizontalFlip(),
    transforms.RandomRotation(15),
    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), # ImageNet统计值
])

test_transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

# 下载并加载CIFAR-10数据集
# 注意:CIFAR-10原图32x32,上采样到224x224会模糊,这里仅为演示流程。
# 实际项目中,应使用ImageNet或更大尺寸数据集。
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=train_transform)
test_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=test_transform)

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4, pin_memory=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=4, pin_memory=True)

print(f'训练集大小: {len(train_dataset)}')
print(f'测试集大小: {len(test_dataset)}')
print(f'类别: {train_dataset.classes}')

数据管道搭建好后,一个常见的疑惑是:图像块到底怎么切? 在代码层面,这通常由一个名为 PatchEmbedding 的模块完成。它本质上是一个卷积操作,但有着特殊的参数设置。

3. 核心模块拆解:从图像块嵌入到Transformer编码器

现在,让我们深入ViT的核心,亲手实现几个关键模块。这能帮你彻底理解数据在模型中的流动与变换。

3.1 图像块嵌入层

这是将图像转化为序列的第一步。我们使用一个卷积层来实现,其核大小和步长等于图像块大小(如16),输出通道数等于Transformer的隐藏维度(如768)。这个卷积层相当于同时做了两件事:将图像切分成块,并对每个块进行线性投影。

import torch.nn as nn

class PatchEmbedding(nn.Module):
    """将2D图像转换为1D序列嵌入"""
    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

        # 使用卷积层实现投影:核大小=步长=patch_size
        self.projection = nn.Conv2d(in_channels, embed_dim, 
                                    kernel_size=patch_size, stride=patch_size)

    def forward(self, x):
        # x形状: (B, C, H, W)
        B, C, H, W = x.shape
        # 投影后形状: (B, embed_dim, H/patch_size, W/patch_size)
        x = self.projection(x)
        # 展平空间维度: (B, embed_dim, num_patches)
        x = x.flatten(2)
        # 调整维度为Transformer输入序列格式: (B, num_patches, embed_dim)
        x = x.transpose(1, 2)
        return x

3.2 位置嵌入与类别标记

得到图像块嵌入后,我们需要添加位置信息和一个特殊的 [CLS] 标记(借鉴自BERT)。[CLS] 标记的最终输出将用于图像分类。

class ViTEmbeddings(nn.Module):
    """组合图像块嵌入、位置嵌入和类别标记"""
    def __init__(self, img_size=224, patch_size=16, in_channels=3, embed_dim=768, dropout_rate=0.1):
        super().__init__()
        self.patch_embed = PatchEmbedding(img_size, patch_size, in_channels, embed_dim)
        self.num_patches = self.patch_embed.num_patches

        # 可学习的类别标记
        self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
        # 可学习的位置嵌入
        self.position_embeddings = nn.Parameter(torch.zeros(1, self.num_patches + 1, embed_dim))
        self.dropout = nn.Dropout(dropout_rate)

        # 初始化参数
        nn.init.trunc_normal_(self.cls_token, std=0.02)
        nn.init.trunc_normal_(self.position_embeddings, std=0.02)

    def forward(self, x):
        B = x.shape[0]
        # 获取图像块嵌入
        x = self.patch_embed(x)  # (B, num_patches, embed_dim)

        # 扩展并添加类别标记
        cls_tokens = self.cls_token.expand(B, -1, -1)  # (B, 1, embed_dim)
        x = torch.cat((cls_tokens, x), dim=1)  # (B, num_patches+1, embed_dim)

        # 添加位置嵌入
        x = x + self.position_embeddings
        x = self.dropout(x)
        return x

3.3 Transformer编码器层

这是模型的核心,由多头自注意力(MSA)和前馈网络(MLP)交替组成,并应用了层归一化(LayerNorm)和残差连接。

class MultiHeadSelfAttention(nn.Module):
    """简化版多头自注意力"""
    def __init__(self, embed_dim=768, num_heads=12, dropout_rate=0.0):
        super().__init__()
        assert embed_dim % num_heads == 0, "embed_dim必须能被num_heads整除"
        self.num_heads = num_heads
        self.head_dim = embed_dim // num_heads
        self.scale = self.head_dim ** -0.5

        self.qkv = nn.Linear(embed_dim, embed_dim * 3)  # 同时计算Q, K, V
        self.attn_dropout = nn.Dropout(dropout_rate)
        self.proj = nn.Linear(embed_dim, embed_dim)
        self.proj_dropout = nn.Dropout(dropout_rate)

    def forward(self, x):
        B, N, C = x.shape  # N: 序列长度 (num_patches + 1)
        # 计算Q, K, V并重塑为多头
        qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4)
        q, k, v = qkv[0], qkv[1], qkv[2]  # 每个形状: (B, num_heads, N, head_dim)

        # 计算注意力分数
        attn = (q @ k.transpose(-2, -1)) * self.scale
        attn = attn.softmax(dim=-1)
        attn = self.attn_dropout(attn)

        # 应用注意力权重到V
        x = (attn @ v).transpose(1, 2).reshape(B, N, C)
        x = self.proj(x)
        x = self.proj_dropout(x)
        return x

class TransformerEncoderLayer(nn.Module):
    """单个Transformer编码器层"""
    def __init__(self, embed_dim=768, num_heads=12, mlp_ratio=4.0, dropout_rate=0.1):
        super().__init__()
        self.norm1 = nn.LayerNorm(embed_dim, eps=1e-6)
        self.attn = MultiHeadSelfAttention(embed_dim, num_heads, dropout_rate)
        self.norm2 = nn.LayerNorm(embed_dim, eps=1e-6)
        mlp_hidden_dim = int(embed_dim * mlp_ratio)
        self.mlp = nn.Sequential(
            nn.Linear(embed_dim, mlp_hidden_dim),
            nn.GELU(),  # ViT使用GELU激活函数
            nn.Dropout(dropout_rate),
            nn.Linear(mlp_hidden_dim, embed_dim),
            nn.Dropout(dropout_rate)
        )

    def forward(self, x):
        # 残差连接和层归一化 (Pre-Norm结构,ViT论文中使用)
        x = x + self.attn(self.norm1(x))
        x = x + self.mlp(self.norm2(x))
        return x

4. 组装与训练:构建完整的ViT模型

将上述模块组合起来,并添加分类头,就得到了完整的Vision Transformer模型。

class VisionTransformer(nn.Module):
    """完整的Vision Transformer模型"""
    def __init__(self, img_size=224, patch_size=16, in_channels=3, num_classes=10,
                 embed_dim=768, depth=12, num_heads=12, mlp_ratio=4.0, 
                 dropout_rate=0.1, representation_size=None):
        super().__init__()
        self.embeddings = ViTEmbeddings(img_size, patch_size, in_channels, embed_dim, dropout_rate)
        self.encoder = nn.Sequential(*[
            TransformerEncoderLayer(embed_dim, num_heads, mlp_ratio, dropout_rate)
            for _ in range(depth)
        ])
        self.norm = nn.LayerNorm(embed_dim, eps=1e-6)

        # 分类头
        self.head = nn.Linear(embed_dim, num_classes) if representation_size is None else nn.Sequential(
            nn.Linear(embed_dim, representation_size),
            nn.Tanh(),
            nn.Linear(representation_size, num_classes)
        )

        # 初始化权重
        self.apply(self._init_weights)

    def _init_weights(self, m):
        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)

    def forward(self, x):
        # 嵌入
        x = self.embeddings(x)
        # 通过Transformer编码器
        x = self.encoder(x)
        # 对序列进行层归一化
        x = self.norm(x)
        # 取类别标记[CLS]的输出用于分类
        cls_output = x[:, 0]
        # 分类头
        logits = self.head(cls_output)
        return logits

模型定义好后,训练环节同样充满技巧。ViT的训练对超参数比较敏感,尤其是学习率和优化器。

import torch.optim as optim
from torch.cuda.amp import GradScaler, autocast  # 混合精度训练

def train_one_epoch(model, dataloader, criterion, optimizer, device, scaler=None):
    model.train()
    running_loss = 0.0
    correct = 0
    total = 0

    for batch_idx, (images, labels) in enumerate(dataloader):
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()

        # 混合精度训练前向传播
        with autocast():
            outputs = model(images)
            loss = criterion(outputs, labels)

        # 混合精度训练反向传播
        if scaler is not None:
            scaler.scale(loss).backward()
            scaler.step(optimizer)
            scaler.update()
        else:
            loss.backward()
            optimizer.step()

        running_loss += loss.item()
        _, predicted = outputs.max(1)
        total += labels.size(0)
        correct += predicted.eq(labels).sum().item()

    epoch_loss = running_loss / len(dataloader)
    epoch_acc = 100. * correct / total
    return epoch_loss, epoch_acc

# 初始化模型、损失函数和优化器
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = VisionTransformer(img_size=224, patch_size=16, num_classes=10, embed_dim=384, depth=6, num_heads=6).to(device) # 小型配置,便于实验

criterion = nn.CrossEntropyLoss()
# 使用AdamW优化器,并设置权重衰减,这对Transformer训练很重要
optimizer = optim.AdamW(model.parameters(), lr=3e-4, weight_decay=0.05)
# 学习率预热调度器
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50, eta_min=1e-6)
# 混合精度训练缩放器
scaler = GradScaler()

print(f'模型参数量: {sum(p.numel() for p in model.parameters())/1e6:.2f}M')
print(f'使用设备: {device}')

训练循环和验证代码与常规PyTorch训练无异,但有几个关键点需要特别留意:

  1. 学习率预热:ViT训练初期不稳定,通常需要几十到几百个step的线性学习率预热。
  2. 权重衰减:使用AdamW优化器并设置适当的权重衰减(如0.05)是防止过拟合的有效手段。
  3. 梯度裁剪:对于较深的ViT模型,梯度裁剪有助于稳定训练。
  4. 数据增强:如前所述,对于小数据集,RandAugment、MixUp、CutMix等强增强策略几乎是必需品。

5. 实战技巧与性能优化

纸上得来终觉浅,绝知此事要躬行。在实际项目中应用ViT,你会遇到一些在理论论文中不会详述的挑战。下面分享几个我实践中总结的技巧。

5.1 处理高分辨率图像与微调

预训练的ViT模型(如在ImageNet-21k上训练)通常使用224x224分辨率。当你的下游任务图像分辨率不同(如384x384或512x512)时,直接调整输入尺寸会导致图像块数量变化,从而与预训练位置嵌入的序列长度不匹配。

解决方案是:对预训练的位置嵌入进行二维插值。 Hugging Face的 transformers 库已经内置了这个功能。基本原理是,将学习到的1D位置嵌入,根据其对应的原始2D坐标,插值到新的2D网格坐标上。

from transformers import ViTForImageClassification
import torch.nn.functional as F

# 加载预训练模型 (例如 google/vit-base-patch16-224)
model = ViTForImageClassification.from_pretrained('google/vit-base-patch16-224')

# 假设我们需要将输入分辨率从224调整为384
new_image_size = 384
patch_size = 16
original_num_patches = (224 // patch_size) ** 2
new_num_patches = (new_image_size // patch_size) ** 2

# 获取原始位置嵌入 (1, num_patches+1, hidden_dim)
original_pos_embed = model.vit.embeddings.position_embeddings
# 分离出类别标记的位置嵌入和图像块的位置嵌入
cls_pos_embed = original_pos_embed[:, 0:1, :]
img_pos_embed = original_pos_embed[:, 1:, :]

# 将1D序列位置嵌入重塑为2D网格 (假设原始为14x14网格)
original_grid_size = int(original_num_patches ** 0.5)
img_pos_embed_2d = img_pos_embed.reshape(1, original_grid_size, original_grid_size, -1).permute(0, 3, 1, 2)

# 计算新的网格大小并插值
new_grid_size = int(new_num_patches ** 0.5)
# 使用双线性插值,因为位置嵌入在2D空间是连续的
img_pos_embed_2d_resized = F.interpolate(img_pos_embed_2d, size=(new_grid_size, new_grid_size), mode='bilinear', align_corners=False)
img_pos_embed_resized = img_pos_embed_2d_resized.permute(0, 2, 3, 1).reshape(1, new_num_patches, -1)

# 重新组合类别标记和插值后的图像块位置嵌入
new_pos_embed = torch.cat([cls_pos_embed, img_pos_embed_resized], dim=1)

# 替换模型中的位置嵌入
model.vit.embeddings.position_embeddings = nn.Parameter(new_pos_embed)
# 同时需要更新模型配置中的image_size信息(如果模型配置中有)
model.config.image_size = new_image_size
print(f'位置嵌入已从 {original_grid_size}x{original_grid_size} 插值到 {new_grid_size}x{new_grid_size}')

5.2 注意力可视化:模型在看哪里?

Transformer的可解释性是其一大优点。我们可以通过提取注意力权重,来观察模型在做出分类决策时,更关注图像的哪些区域。

import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

def visualize_attention(model, image_path, device, patch_size=16):
    """可视化最后一层自注意力头对[CLS]标记的注意力图"""
    model.eval()
    # 预处理图像
    image = Image.open(image_path).convert('RGB')
    transform = test_transform  # 使用之前的测试转换
    input_tensor = transform(image).unsqueeze(0).to(device)

    # 使用钩子获取注意力权重
    attention_maps = []
    def hook_fn(module, input, output):
        # output 通常是 (attention_output, attention_weights)
        # 对于某些实现,attention_weights在output的元组中
        if isinstance(output, tuple):
            attention_weights = output[1]  # 假设第二个元素是注意力权重
        else:
            # 可能需要根据具体模型结构调整
            attention_weights = module.attention_map  # 如果模型存储了它
        attention_maps.append(attention_weights.detach().cpu())

    # 注册钩子到最后一个编码器层的自注意力模块
    # 注意:这里需要根据你的模型结构找到正确的模块路径
    last_attn_layer = model.vit.encoder.layer[-1].attention.attention
    handle = last_attn_layer.register_forward_hook(hook_fn)

    with torch.no_grad():
        outputs = model(input_tensor)

    handle.remove()  # 移除钩子

    if attention_maps:
        attn = attention_maps[0]  # (batch_size, num_heads, seq_len, seq_len)
        # 取第一个样本,所有注意力头对[CLS]标记(索引0)的注意力
        # 平均所有头的注意力
        attn_to_cls = attn[0, :, 0, 1:].mean(dim=0)  # 形状: (num_patches,)
        num_patches = attn_to_cls.shape[0]
        grid_size = int(num_patches ** 0.5)
        attn_map = attn_to_cls.reshape(grid_size, grid_size).numpy()

        # 将注意力图上采样到原图尺寸进行叠加显示
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
        ax1.imshow(image)
        ax1.set_title('Original Image')
        ax1.axis('off')

        im = ax2.imshow(attn_map, cmap='hot', interpolation='nearest')
        ax2.set_title('Attention Map (averaged over heads)')
        ax2.axis('off')
        plt.colorbar(im, ax=ax2)
        plt.tight_layout()
        plt.show()

运行这段代码,你可能会发现一个有趣的现象:在分类任务中,模型最后几层的注意力往往高度集中在与目标物体相关的语义区域上,这直观地展示了模型“看”的过程。

5.3 使用预训练模型与迁移学习

从头开始训练一个ViT,尤其是在CIFAR-10这样的小数据集上,很难达到理想效果。最实用的方法是迁移学习。Hugging Face Model Hub提供了大量预训练的ViT模型,我们可以轻松加载并针对自己的任务进行微调。

from transformers import ViTForImageClassification, ViTImageProcessor
from datasets import load_dataset

# 1. 加载预训练模型和处理器
model_name = 'google/vit-base-patch16-224-in21k'  # 在ImageNet-21k上预训练
processor = ViTImageProcessor.from_pretrained(model_name)
model = ViTForImageClassification.from_pretrained(model_name, num_labels=10, ignore_mismatched_sizes=True) # 忽略分类头尺寸不匹配

# 2. 准备自定义数据集 (以CIFAR-10为例,但需处理为ImageFolder格式或使用datasets库)
# 假设我们有一个图像文件夹结构: data/train/class1/, data/train/class2/...
from torchvision.datasets import ImageFolder
train_dataset = ImageFolder(root='./data/train', transform=processor)
# 注意:processor会负责将图像转换为模型所需的像素值、尺寸和归一化。

# 3. 微调模型(仅训练分类头或全部参数)
# 通常先冻结主干,只训练分类头
for name, param in model.named_parameters():
    if 'classifier' not in name:  # 假设分类层名称为'classifier'
        param.requires_grad = False

# 然后使用较小的学习率训练所有参数
optimizer = optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=5e-5)

# 训练循环...

在实际微调时,经验告诉我,渐进式解冻(gradual unfreezing)策略有时效果更好:先解冻最后几层Transformer块进行训练,然后逐步解冻更前面的层。这有助于在保留预训练知识的同时,让模型更好地适应新任务。

6. 超越分类:ViT的扩展应用与未来展望

图像分类只是ViT的起点。其强大的序列建模能力,正在计算机视觉的各个子领域催生革命性的变化。了解这些扩展方向,能帮助你更好地定位ViT在项目中的潜力。

  • 目标检测:DETR系列模型将目标检测视为一个集合预测问题,使用Transformer编码器-解码器架构,直接输出一组目标框和类别,省去了传统方法中复杂的锚框设计和后处理(如NMS)。
  • 图像分割:SETR和Segmenter等模型将语义分割任务视为对每个图像块进行分类。通过Transformer编码器捕获全局上下文,再通过解码器上采样恢复细节,在多个数据集上取得了领先结果。
  • 图像生成:Vision Transformer同样被用于生成模型。例如,ViT-VQGAN结合了VQ-VAE和Transformer,能够生成高质量、高分辨率的图像,展示了其在理解和生成视觉内容方面的双重能力。
  • 多模态学习:CLIP和ALIGN等模型将ViT与文本Transformer结合,在海量“图像-文本”对上进行对比学习,实现了强大的零样本图像分类和跨模态检索能力。

这些应用的核心,依然是Transformer处理序列数据的灵活性和强大的全局建模能力。当你掌握了ViT的基本原理和实现后,探索这些前沿方向将是一个自然的过程。我最近在一个细粒度图像检索项目中尝试了基于ViT的特征提取器,发现其全局注意力机制对于捕捉物体间细微的上下文差异非常有帮助,这是传统CNN池化层难以做到的。

技术的迭代从未停止,从最初的ViT到Swin Transformer(引入了局部窗口和位移窗口注意力以提升效率),再到最近层出不穷的变体,这个领域充满活力。作为开发者,最重要的不是追逐每一个新模型,而是深刻理解其核心思想——将数据转化为序列,并用注意力机制建立全局依赖。无论未来架构如何变化,这一思想很可能继续闪耀。当你下次面对一个复杂的视觉问题时,不妨想一想:它能被转化为一个序列问题吗?也许,Transformer就是那把钥匙。

ViT——nlp和cv进行了统一,使多模态成为可能 紧接着在一系列 token 的前面加上加上一个新的 token(对应着 * 那个位置),此外还需要加上位置的信息,对应着 0~9(本文讨论了1d,2d和相对位置编码的输入,结论是无区别)。之前的transformer在cv中应用,大部分是将CNN模型中部分替换成transformer block(整体网络结构不变)或者用transformer将不同网络连接起来,而本文提出:一个针对图像patch的纯的transformer可以很好地完成图像分类任务,无需CNN的参与,这无疑打通了nlp和cv的壁垒。 阅读详情

相关推荐

【论文笔记】An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale (ViT)

【论文笔记】An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(Vision Transformer, ViT) 文章题目:An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale 作者:Dosovitskiy, A., Lucas Beyer, Alexander Kolesnikov, Dirk Weissenbo

qq_44055705的博客 1万+

从NLP到CV:手把手教你用Transformers搞定图像分类(附ViT实战代码)

本文详细介绍了如何将Transformer架构从自然语言处理领域迁移到计算机视觉任务,特别是图像分类。通过解析Vision Transformer的核心思想,从图像分块嵌入、位置编码到完整的Transformer编码器,并提供了基于PyTorch的ViT实战代码,帮助开发者掌握这一前沿技术。

ik678901234的博客 750

从图像分类到目标检测:手把手教你用Hugging Face Transformers库玩转ViT和DETR

本文详细介绍了如何使用Hugging Face Transformers库快速上手ViT(Vision Transformer)和DETR(Detection Transformer)模型,涵盖从环境配置到实战应用的完整流程。通过对比分析ViT和DETR的特性与适用场景,帮助开发者高效实现图像分类和目标检测任务,并提供了混合精度训练、量化推理等性能优化技巧。

weixin_30723433的博客 296

Visual Transformer (ViT)模型详解

ViT是2020年Google团队提出的将Transformer应用在图像分类的模型,虽然不是第一篇将transformer应用在视觉任务的论文,但是因为其模型“简单”且效果好,可扩展性强(scalable,模型越大效果越好),成为了transformer在CV领域应用的里程碑著作,也引爆了后续相关研究。

十年以上架构设计经验,专注于软件架构和人工智能领域,对机器视觉、NLP、音视频等领域都有涉猎 9万+

李沐精读论文:ViT 《An Image Is Worth 16x16 Words: Transformers For Image Recognition At Scale》

李沐精读论文:ViT 《An Image Is Worth 16x16 Words: Transformers For Image Recognition At Scale》 position embedding patch embedding ViT-B/16 vision transformer MLP Head vision transformer

iwill323的博客 3410

ViT(Vision Transformer)论文笔记

ViT(Vision Transformer)论文笔记 (AN IMAGE IS WORTH 16X16 WORDS: TRANSFORMERS FOR IMAGE RECOGNITION AT SCALE) Abstact 虽然Transformer架构已经成为自然语言处理任务的事实标准,但它在计算机视觉中的应用仍然有限。在视觉中,注意力要么与卷积网络结合使用,要么用于替换卷积网络的某些组件(替换CNN模型的一部分),同时保持其整体结构。我们表明,这种对CNN的依赖是不必要的,直接应用于图像块序列的 pu

charles_zhang_的博客 7777

Transformers:开启NLP与CV新篇章

Transformers:开启NLP与CV新篇章 项目介绍 Transformers项目是一个深入浅出地介绍Transformer架构从自然语言处理(NLP)到计算机视觉(CV)的应用实践。它全面展示了Transformer如何在不同领域发挥作用,并通过一系列的实际例子和模型,让读者理解其核心原理和应用方法。 项目技术分析 Transformer架构最初是为了解决自然语言处理中的序列到序列(seq...

gitblog_00641的博客 580

量化实战:如何用Barra多因子模型给你的投资组合做一次‘风险体检’?

本文详细介绍了如何利用Barra多因子模型对投资组合进行全面的风险体检,帮助投资者识别行业和风格因子的风险暴露。通过实战案例和Python代码示例,展示了数据采集、风险归因和组合优化的完整流程,提升投资组合的风险调整收益。

weixin_27215337的博客 321

【人工智能】Transformers之Pipeline(六):图像分类(image-classification)

本文对transformers之pipeline的图像分类(image-classification)从概述、技术原理、pipeline参数、pipeline实战、模型排名等方面进行介绍,读者可以基于pipeline使用文中的2行代码极简的使用计算机视觉中的图像分类(image-classification)模型。

人工智能领域博客 4658

Innovus Function ECO实战:5步搞定数字后端设计中的网表修正(含Calibre LVS验证技巧)

本文提供了一份完整的Innovus Function ECO实战指南,详细解析了数字后端设计中网表修正的五步流程。从理解Function ECO概念、准备关键文件,到在Innovus中执行逻辑比对、自动布线及GUI检查,并重点攻克了ECO后Calibre LVS验证的难点与调试技巧,帮助工程师高效、精准地完成流片前的设计微调。

day7的博客 944

Arcgis 10.3 与 ArcGIS Engine 10.3 安装与破解

Arcgis 10.3 与 ArcGIS Engine 10.3 安装与破解

weixin_52127098的博客 4723

论文理解【Vision Transformer】—— 【VIT】An Image is Worth 16x16 Words: Transformers for Image Recognition

本文介绍经典的 ViT 论文,它是第一个将 Transformer 模型有效应用到较大规模 CV 任务上的工作,并且保持了 Transformer 模型本身的 Scaling 能力,启发了很多后续工作

佚失的诗篇 4206

Unity游戏开发实战:手把手教你用C#实现一个简单的反向运动学(IK)控制器

本文详细介绍了在Unity游戏开发中如何使用C#实现一个简单的反向运动学(IK)控制器。通过建立骨骼层级结构、实现CCD算法和角度限制,开发者可以轻松创建自然的角色动画效果。文章还提供了可视化调试工具和性能优化方案,帮助提升开发效率。

weixin_42523711的博客 241

【机器学习】—Transformers的扩展应用:从NLP到多领域突破

好久不见!个人主页,喜欢就关注吧~自从Google在2017年提出Transformer架构以来(论文《Attention is All You Need》),这一模型在NLP(自然语言处理)中引发了革命。从BERT到GPT,Transformer的性能在文本生成、分类、翻译等任务中大幅领先传统方法。然而,随着研究的深入,Transformer不再局限于NLP,它在计算机视觉(CV)、时间序列分析、多模态学习等领域的表现同样令人瞩目。

稻草人 2899

手把手教你学Simulink——基于Simulink的轮毂电机独立驱动转矩矢量分配

阐述了轮毂电机独立驱动的核心价值与转矩分配的必要性在 Simulink 中实现了完整的“上层稳定控制 + 下层最优分配”架构对比了三种分配策略在极限工况下的性能差异提供了工程落地的关键考量特斯拉 Cybertruck(后轮双电机矢量控制)Rivian R1T(四电机坦克掉头)宝马 iX(精准牵引力控制)核心思想“以软件定义底盘,以算法驾驭动力;化四轮之独立,为整车之协同。—— 让汽车在极限边缘依然从容不迫。

MHD0815的博客 368

基于POI-TL实现动态Word模板数据填充(含图表):从需求到落地的完整开发实践

本文聚焦企业级报告生成场景中 “在线编辑模板 + 动态数据填充” 的核心需求,详细阐述基于 POI-TL、OnlyOffice 与 MinIO 的全栈实现方案。通过代码示例拆解模板加载、文字填充、图表匹配渲染(含通过关系 ID 关联图表与段落的关键逻辑)、报告存储全步骤。最终通过技术组合实现业务人员可视化编辑模板、后端高效填充数据的目标,为同类动态 Word 生成需求提供可复用的实践参考。

imHanweihu的博客 1849

从零开始手把手搭建Vision Transformers(Pytorch版本)

更多内容关注公众号“所向披靡的张大刀” 导言 Vision Transformers (ViT)在2020年Dosovitskiy et. al.提出后,在计算机视觉领域逐渐占领主导位置,在图像分类以及目标检测、语义分割等下游任务中获得了很好的性能,掀起transformer系列在CV领域的浪潮。这里将介绍如何从头开始基于Pytorch 框架一步步实现ViT模型。 前言 如果你还没有熟悉自然语言处理(NLP)中使用的Transformer模型,可能会对transformer在CV领域的应用有点懵圈,对ViT

zqwwwm的博客 6284

ESP32蓝牙广播数据自定义指南:让你的设备更容易被手机发现

本文详细介绍了如何通过自定义ESP32蓝牙广播数据提升设备在手机端的可发现性。从广播数据结构、扫描响应优化到跨平台兼容性设置,提供了全面的技术指南和实战代码示例,帮助开发者解决智能硬件开发中的蓝牙连接难题。

weixin_29172201的博客 271

Vision Transformer(ViT)

ViT模型将Transformer引入到图像的分类中,更准确的说是Transformer中的Encoder模块。为了能够尽可能少地对原始模型的修改,在ViT中将图像转换成一维的序列表示,以改成标准的文本形式,通过这种方式实现Transformer在CV中的应用。

null的专栏 5135

PCB设计必看:铜皮厚度与载流能力对照表(附Excel计算工具)

本文深入解析了PCB设计中铜皮厚度与载流能力的核心关系,提供了基于IPC-2152标准的动态计算逻辑与实用Excel工具。文章不仅分享了载流对照表,更重点剖析了影响载流能力的关键因素(如铜厚、线宽、温升、布线层),并给出了高电流设计的实战技巧与测试验证方法,帮助工程师规避常见陷阱,实现从理论到实践的闭环设计。

weixin_29228781的博客 719

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Transformers在计算机视觉领域中的应用【第1篇:ViT——Transformer杀入CV界之开山之作】

本文详细介绍了ViT的模型结构、前向过程、思考和结论。

https://github.com/Donvink,专注于计算机视觉、目标检测、目标跟踪等方向 1507

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【ViT系列(1)】《AN IMAGE IS WORTH 16X16 WORDS:TRANSFORMERS FOR IMAGE RECOGNITION AT SCALE》论文超详细解读(翻译+精读)

ViT论文:《AN IMAGE IS WORTH 16X16 WORDS:TRANSFORMERS FOR IMAGE RECOGNITION AT SCALE》论文超详细解读

路人贾的博客 7177

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

【计算机视觉】Transformer 从 NLP 到 CV

一、前言 CV(计算机视觉)领域一直是引领机器学习的弄潮儿。近年来更是因为 Transformer模型的横空出世而掀起了一阵腥风血雨。本文主要介绍 Transformers 背后的技术思想、在计算机视觉领域的应用情况、最新动态以及该架构相对于 CNN 的优势。读完这篇文章之后,你将能知道: 为什么 Transformers 模型在 NLP 任务中能够力压群雄,变成 SOTA 模型的必备组件之一。 Transformers 模型的计算原理。 为什么说 Transformers 是对 CNN 的...

闻韶 2404

ViT网络模型

一、背景介绍: 自2017年Google提出的Transformer结构以来,迅速引发一波热潮,最初《Attention is all you need》这篇论文的提出是针对于NLP领域的,通过自注意力机制代替传统处理序列数据时采用的循环神经网络结构,不仅实现了并行训练,提升了训练的效率,同时也在应用中取得很好的结果。之后的一段时间中,各种基于Transformer改进的网络结构涌现出来,在不同领域中都达到SOTA的效果。 2020年Google又提出了《AN IMAGE IS WORTH 16X16 WO

Yore_的博客 1万+

Py之transformers:transformers的简介、安装、使用方法、案例应用之详细攻略

​ Py之transformers:transformers的简介、安装、使用方法、案例应用之详细攻略 目录 transformers的简介 transformers的使用方法 transformers的安装 transformers的案例应用 可提供的模型架构—目前194种 transformers的简介 transformers提供了数千个预先训练好的模型来执行不同模式的任务,如文本、视觉和音频。这些模型可应用于: >> 文本:用于文本分类、信息提取、问题回

头部AI社区如有邀博主AI主题演讲请私信—心比天高,仗剑走天涯,保持热爱,奔赴向梦想!低调,专注,谦虚,自律,反思,成长,还算比较正能量的博主,公益免费传播…内心特别想在AI界做出一些可以推进历史进程影响力的技术(兴趣使然,有点小情怀,也有点使命感呀 1万+
上一篇: 保姆级教程:如何在Ubuntu 18.04上一步步搭建PX4飞控和ROS开发环境
下一篇: SSD训练自定义数据集实战:VOC格式标注与模型调参技巧
php55
博客等级 码龄15年 51粉丝 1553原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值