Vision transformer学习笔记

1. transformer基本架构

在了解cv中的transformer之前,我们先来复习一下transformer的基本架构

1.1 编码器与解码器

我们可以将网络分成两个部分,编码器与解码器。

        编码器:将输入编程成中间表达形式(抽取特征)

        解码器:将中间表示(特征)解码成任务需要的输出

从编解码的角度来理解神经网络,就是说我先通过编码器将原始数据编码成机器学习比较好理解的结构,再通过解码器解码成你需要的任务输出。

对应到CNN,就是说前面使用卷积层进行特征抽取称为编码器,后面将抽取的特征变为所需的输出称为解码器。

对应到RNN中对文本进行处理,同样可以分为编码器和解码器

我们将上述结构抽象成一个更一般的结构

一个模型被分成两块:

        解码器处理输入

        解码器生成输出

1.2 transformer块

2. vision transformer

        Vision Transformer(ViT)是视觉Transformer的开山之作,其开创性的工作是:纯粹地使用了标准的transformer结构(简单、扩展性很好),实现了能够媲美CNN中SOTA模型的效果(在这之前也有一些工作与它想法类似,但效果很差,VIT论文中给出的解释是transformer 没有像CNN那样包含图片的归纳偏置,因此,需要给它喂很大量的数据做训练,让它自己学到充分的特征,效果才能好)。

        回忆一下,在transformer里面计算自注意力时,我们是将一个可变长的序列传进去,每个元素都要跟每个元素去做互动,算得一个注意力得分,也就是一个自注意力图,然后用这个自注意力图去做加权平均,最后得到输出,因为每个元素之间都要做互动,所以这个计算复杂度是跟序列长度n成平方关系的,目前硬件能支持的序列长度,一般也就是几百或者上千,如在bert模型中,序列长度也就是512。

        换到视觉领域,如果想使用transformer的话,需要解决的第一个问题就是,如何把二维的图片变成一维的序列。

        最直观的方式就是把图片按像素点拉直成一维的,但这样计算复杂度太高,比如我们做图片分类的一般大小是224x224,拉直以后序列长度就是224x224=50176,已经接近bert的100倍,显然是不可行的。

那么怎么去降低序列长度呢?

        vision transformer之前在视觉领域里使用注意力机制

                1. 将注意力机制加到cnn里面,比如SE模块、PAM模块、BEAM模块,他是使用特征图作为输入,如resnet50最后一层特征图大小7x7,拉皮以后长度也只有49,就降低了这个序列长度

                2. 孤立自注意力:类似于卷积,计算注意力的时候,不对整个图片一起计算,而是选择一个小窗口,基于这个局部窗口去计算,这样复杂度也降低了

                3. 轴自注意力:先在高度这个维度上做一次自注意力,再在宽度这个维度上做一次自注意力

        对于第一种方法,本质上主体还是卷积神经网络,只是加了一些插件,对于后两种方法,虽然理论计算复杂度很低,但由于尚未针对性在gpu硬件上做一些计算优化,因此很难能用这两种思路训练出一个大模型

        vision transformer的想法就是把224x224的图片打成一个个16x16大小patch,然后每个patch作为一个基本元素,这样W=H=224/16=14,拉直以后的序列长度就只有14 x 14 = 196了,这样就直接可以将原图传进transformer进行训练了

        224x224大小的RGB图片,拆分成16x16的patch,每个patch的维度就是16x16x3=768,总序列维度就是196x768,实现起来也很简单,就是对图片做一个 kernel_size = stride = 要拆分的patch大小,out_channle=768的卷积操作就可以了

代码实现

class PatchEmbed(nn.Module):
    """
    2D Image to Patch Embedding
    """
    def __init__(self, img_size=224, patch_size=16, in_c=3, embed_dim=768, norm_layer=None):
        super().__init__()
        img_size = (img_size, img_size)
        patch_size = (patch_size, patch_size)
        self.img_size = img_size
        self.patch_size = patch_size
        self.grid_size = (img_size[0] // patch_size[0], img_size[1] // patch_size[1])
        self.num_patches = self.grid_size[0] * self.grid_size[1]


        self.proj = nn.Conv2d(in_c, embed_dim, kernel_size=patch_size, stride=patch_size)
        self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()


    def forward(self, x):
        B, C, H, W = x.shape
        assert H == self.img_size[0] and W == self.img_size[1], \
            f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})."


        # flatten: [B, C, H, W] -> [B, C, HW]
        # transpose: [B, C, HW] -> [B, HW, C]
        x = self.proj(x).flatten(2).transpose(1, 2)
        x = self.norm(x)
        return x

  1. 先将batchsize x 224x244x3的图片打成16x16大小的patch,得到b x 196x768(b是batch_size的维度)的向量

  2. 再经过一个全连接层(768x768)做线性投影,还是一个bx196x768的向量

  3. 参照NLP中的bert模型,加上一个extra learnable class embedding作为class token(1x768),向量变为bx197x768,在给每个位置都加上一个位置编码P,向量变为bx197x768,传进transformer块

  4. 先经过一个LN,向量维度仍为bx197x768,再经过一个多头自注意力和残差连接 ,向量维度仍为bx197x768,再经过LN后,经过第一个全连接层(768x3072,扩大四倍),向量维度变为bx197x3072,再经过第二个全连接层(3072x768),向量维度变回bx197x768,最后再跟一个残差连接,向量维度仍为bx197x768。

  5. 经过L次上述的transformer块后,只取第0位class token位置对应的向量作为特征(bx1x768),像CNN做分类任务一样,接一个全连接层后接softmax最后得到分类

根据参数的不同VIT有小、中、大三个版本

一些实验和讨论:

  1. class token:使用class token是为了和nlp中的方式保持一致,也可以像cnn那样,对所有的特征做一个全局平均池化(将197x768池化成1x768),再传进全连接层进行分类,可以看到,在调好参的情况下,区别不大

        2. 位置编码使用一维、二维或相对位置编码区别不大

        3. transformer没有内含任何的归纳偏置,位置编码也是随机初始化的(只保证了每个位置的值不一样),所有的信息都得从头学,因此在中小数据集上的表现不如CNN,但注意力机制关注的是全局信息,它具有强大学习能力和扩展性,所以在更大数据集上表现更好

        4. 混合模型:前面是将图片打成patch缩小尺寸,混合模型直接利用CNN来缩小尺寸,即将CNN的特征图作为输入传入transformer。

在计算量相同的情况下,对于更小的模型,混合模型表现好于VIT,但对于更大的模型,区别不大

        5. CNN在微调的时候如果使用更大的图片尺寸,通常效果会更好一些,但VIT难以改变输入尺寸的大小。虽然transformer可以处理任意长输入,但位置编码矩阵具有明确的信息,如果图片变大了,patch_size不变,那么你可能得到更多的patch,这时需要对位置进行插值。(如果图片大小差距过大,这样直接简单插值,会导致模型效果变差)

        6. 做自监督预训练,简单来说就是对图片的一些patch随机打上马赛克,去预测这些patch,使用这种方式去做预训练,但在VIT这篇论文中实验效果一般(一年以后,也就是去年,MAE模型在这上面取得了突破)

        7. 在更大的数据集上(JFT或imagenet21K)做预训练,再在别的数据集上做微调,效果更好,训练起来更便宜(相对来说,使用一个TPUv3训练需要2500天),证明了transformer在CV上同样具有和在nlp领域中一样的强大的学习能力

<think>好的,用户想了解关于Vision Transformer(ViT)的学习资料和笔记。首先,我需要整理ViT的基础知识,确保覆盖其核心结构和关键概念。根据提供的引用,ViT将Transformer架构应用于计算机视觉,特别是图像分类任务。需要提到编码器、解码器部分,不过根据引用[1],ViT主要基于Transformer的编码器结构,这可能与原始Transformer不同。 接下来,用户可能需要学习资料,包括论文、GitHub代码库和教程。引用[2]提到了Google Research的GitHub仓库,应该推荐这个。另外,用户可能对ViT的工作原理感兴趣,比如如何处理图像数据。传统CNN会先提取特征图,ViT则通过分块(patches)将图像转换为序列,如引用[3]所述。需要解释分词器的作用,将图像分块并线性嵌入成标记序列,然后输入Transformer编码器。 还要注意比较ViT与CNN的差异,强调自注意力机制的优势。可能的问题包括ViT的计算效率、训练数据需求,以及应用场景。用户可能想知道如何实现ViT,所以需要提供代码示例或库的链接。此外,引用中的资料可以作为主要参考文献,确保引用标识正确。 最后,生成相关问题时,要考虑用户可能的后续疑问,比如ViT的优化方法、与CNN的结合,或者在不同任务中的应用。需要确保回答结构清晰,分点说明,并推荐可靠资源,帮助用户深入学习。</think>### Vision Transformer (ViT) 学习笔记 #### 1. **核心思想** Vision Transformer (ViT)Transformer架构应用于图像分类任务,通过将图像分割为固定大小的块(patches),并将这些块线性嵌入为序列输入Transformer编码器[^1][^3]。与传统CNN不同,ViT直接利用自注意力机制捕捉全局依赖关系。 #### 2. **关键步骤** - **图像分块**:将输入图像 $x \in \mathbb{R}^{H \times W \times C}$ 划分为 $N$ 个块,每个块大小为 $P \times P$,得到 $N = \frac{HW}{P^2}$ 个块。 - **线性嵌入**:将每个块展平为向量 $x_p \in \mathbb{R}^{P^2C}$,并通过可学习的线性层映射为 $D$ 维向量。 - **位置编码**:添加可学习的位置嵌入(position embedding)以保留空间信息: $$ z_0 = [x_{\text{class}}; x_p^1E; x_p^2E; \dots; x_p^NE] + E_{\text{pos}} $$ 其中 $E \in \mathbb{R}^{(P^2C) \times D}$ 为嵌入矩阵,$E_{\text{pos}}$ 为位置编码。 - **Transformer编码器**:由多头自注意力(MSA)和前馈网络(FFN)交替组成,公式为: $$ z'_l = \text{MSA}(\text{LayerNorm}(z_{l-1})) + z_{l-1} $$ $$ z_l = \text{FFN}(\text{LayerNorm}(z'_l)) + z'_l $$ 最终输出用于分类[^1][^2]。 #### 3. **与CNN的对比** - **优势**:ViT通过自注意力捕获长距离依赖,无需局部卷积归纳偏置,适合大规模数据场景。 - **局限**:小规模数据训练效果可能不如CNN,需大量预训练数据(如JFT-300M)。 #### 4. **学习资料推荐** - **论文**:[*An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale*](https://arxiv.org/abs/2010.11929) - **代码实现**:Google Research官方GitHub仓库 [vision_transformer](https://github.com/google-research/vision_transformer) - **教程**: - [PyTorch实现ViT](https://pytorch.org/vision/stable/models/vision_transformer.html) - 李宏毅《深度学习》课程中Transformer与ViT章节 #### 5. **代码示例(简化版)** ```python import torch from torchvision.models import vision_transformer # 加载预训练ViT模型 model = vision_transformer.vit_b_16(pretrained=True) # 输入图像分块处理 x = torch.randn(1, 3, 224, 224) # 输入尺寸需为224x224 output = model(x) # 输出分类结果 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值