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领域中一样的强大的学习能力

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
迁移学习是将在一个任务上训练好的模型应用到另一个相关任务上的技术。对于Vision Transformer,迁移学习可以通过以下几种方式进行: 1. 微调:将在大规模图像数据集上预训练的Vision Transformer模型作为初始模型,然后在目标任务的数据集上进行微调。这可以通过冻结一部分或全部的网络层,只更新最后几层来实现。微调可以帮助模型适应特定任务的特征和类别。 2. 特征提取:使用预训练的Vision Transformer模型提取图像的特征向量,然后将这些特征向量输入到其他机器学习模型(如支持向量机、决策树等)或者神经网络模型(如全连接层)中进行进一步处理和分类。这种方法适用于目标任务数据集较小或者类别不平衡的情况。 3. 融合模型:结合Vision Transformer和传统的卷积神经网络(CNN)模型,以发挥它们各自的优点。CNN可以用于提取局部特征,而Transformer可以用于建模这些特征之间的关系。例如,可以将Vision Transformer的输出与CNN的输出连接在一起,然后将这个融合后的特征输入到全连接层进行分类。 需要注意的是,尽管Vision Transformer在某些计算机视觉任务中表现出色,但并不意味着它可以完全取代CNN。实际上,目前的研究中很多都是在探索如何将这两种方法结合起来,以发挥它们各自的优点。因此,在迁移学习中,可以根据具体的任务和数据集选择适合的迁移学习方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值