<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ViT:視覺Transformer backbone網(wǎng)絡(luò)ViT論文與代碼詳解

          共 9157字,需瀏覽 19分鐘

           ·

          2021-06-08 15:24

          Visual Transformer

          Author:louwill

          Machine Learning Lab

              

          今天開始Visual Transformer系列的第一篇文章,主題是Vision Transformer。Vision Transformer (ViT) 可以算是整個(gè)Visuier任務(wù)的backbone網(wǎng)絡(luò)。

          提出ViT模型的這篇文章題名為An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale,發(fā)表于2020年10月份,雖然相較于一些Transformer的視覺任務(wù)應(yīng)用模型 (如DETR) 提出要晚了一些,但作為一個(gè)純Transformer結(jié)構(gòu)的視覺分類網(wǎng)絡(luò),其工作還是有較大的開創(chuàng)性意義的。

          ViT的總體想法是基于純Transformer結(jié)構(gòu)來(lái)做圖像分類任務(wù),論文中相關(guān)實(shí)驗(yàn)證明在大規(guī)模數(shù)據(jù)集上做完預(yù)訓(xùn)練后的ViT模型,在遷移到中小規(guī)模數(shù)據(jù)集的分類任務(wù)上以后,能夠取得比CNN更好的性能。

          ViT模型詳解
          ViT模型整體結(jié)構(gòu)概覽如圖1所示。

          ViT的核心流程包括圖像分塊處理 (make patches)、圖像塊嵌入 (patch embedding)與位置編碼、Transformer編碼器和MLP分類處理等4個(gè)主要部分。下面分別從這四個(gè)流程部分來(lái)闡述ViT的基本設(shè)計(jì)。

          圖像分塊處理 (make patches)
          第一步可以看作是一個(gè)圖像預(yù)處理步驟。在CNN中,直接對(duì)圖像進(jìn)行二維卷積處理即可,不需要特殊的預(yù)處理流程。但Transformer結(jié)構(gòu)不能直接處理圖像,在此之前需要對(duì)其進(jìn)行分塊處理。

          假設(shè)一個(gè)圖像x∈H×W×C,現(xiàn)在將其分成P×P×C的patches,那么實(shí)際有N=HW/P2個(gè)patches,全部patches的維度就可以寫為N×P×P×C。然后將每個(gè)patch進(jìn)行展平,相應(yīng)的數(shù)據(jù)維度就可以寫為N×(P2×C)。這里N可以理解為輸入到Transformer的序列長(zhǎng)度,C為輸入圖像的通道數(shù),P為圖像patch的大小。

          圖像塊嵌入 (patch embedding)
          圖像分塊僅僅是一道預(yù)處理流程,要將N×(P2×C)的向量維度,轉(zhuǎn)化為N×D大小的二維輸入,還需要做一個(gè)圖像塊嵌入的操作,類似NLP中的詞嵌入,塊嵌入也是一種將高維向量轉(zhuǎn)化為低維向量的方式。

          所謂圖像塊嵌入,其實(shí)就是對(duì)每一個(gè)展平后的patch向量做一個(gè)線性變換,即全連接層,降維后的維度為D。

          上式中的E即為塊嵌入的全連接層,其輸入大小為(P2×C),輸出大小為D。

          值得注意的是,上式中給長(zhǎng)度為N的向量還追加了一個(gè)分類向量,用于Transformer訓(xùn)練過程中的類別信息學(xué)習(xí)。假設(shè)將圖像分為9個(gè)patch,即N=9,輸入到Transformer編碼器中就有9個(gè)向量,但對(duì)于這9個(gè)向量而言,該取哪一個(gè)向量做分類預(yù)測(cè)呢?取哪一個(gè)都不合適。一個(gè)合理的做法就是人為添加一個(gè)類別向量,該向量是可學(xué)習(xí)的嵌入向量,與其他9個(gè)patch嵌入向量一起輸入到Transformer編碼器中,最后取第一個(gè)向量作為類別預(yù)測(cè)結(jié)果。所以,這個(gè)追加的向量可以理解為其他9個(gè)圖像patch尋找的類別信息。

          位置編碼 (position encoding)
          為了保持輸入圖像patch之間的空間位置信息,還需要對(duì)圖像塊嵌入中添加一個(gè)位置編碼向量,如上式中的Epos所示,ViT的位置編碼沒有使用更新的2D位置嵌入方法,而是直接用的一維可學(xué)習(xí)的位置嵌入變量,原先是論文作者發(fā)現(xiàn)實(shí)際使用時(shí)2D并沒有展現(xiàn)出比1D更好的效果。

          ViT前向流程
          集合了類別向量追加、圖像塊嵌入和位置編碼為一體的嵌入輸入向量后,就可以直接進(jìn)入Transformer編碼器部分了,主要包括MSA和MLP兩個(gè)部分。所以,ViT的編碼器前向計(jì)算過程可以歸納如下:

          第一個(gè)式子即前述的圖像塊嵌入、類別向量追加和位置編碼;第二個(gè)式子為MSA部分,包括多頭自注意力、跳躍連接 (Add) 和層規(guī)范化 (Norm) 三個(gè)部分,可以重復(fù)L個(gè)MSA block;第三個(gè)式子為MLP部分,包括前饋網(wǎng)絡(luò) (FFN)、跳躍連接 (Add) 和層規(guī)范化 (Norm) 三個(gè)部分,也可以重復(fù)L個(gè)MSA block。第四個(gè)式子為層規(guī)范化。最后以一個(gè)MLP作為分類頭 (Classification Head)。

          為了更加清晰的展示ViT模型結(jié)構(gòu)和訓(xùn)練過程中的向量變化,下圖給出了ViT的向量維度變化圖。
          ??
          圖來(lái)自于極市平臺(tái)


          ViT訓(xùn)練與實(shí)驗(yàn)
          ViT訓(xùn)練方法
          ViT的基本訓(xùn)練策略是在大數(shù)據(jù)集上先做預(yù)訓(xùn)練,然后在在小數(shù)據(jù)集上做遷移使用。ViT做預(yù)訓(xùn)練使用到的大數(shù)據(jù)集包括:
          • ILSVRC-2012 ImageNet dataset:1000 classes
          • ImageNet-21k:21k classes
          • JFT:18k High Resolution Images

          其中JFT是一個(gè)谷歌的內(nèi)部大規(guī)模圖像數(shù)據(jù)集,約有300M圖像18291個(gè)類別標(biāo)注。
          ViT預(yù)訓(xùn)練遷移到的數(shù)據(jù)集包括:
          • CIFAR-10/100
          • Oxford-IIIT Pets
          • Oxford Flowers-102
          • VTAB

          論文共設(shè)計(jì)了Base、Large和Huge三款不同大小的ViT模型,分別表示基礎(chǔ)模型、大模型和超大模型,三款模型的各參數(shù)如下表所示。

          比如說(shuō),ViT-B/16就表示patch size為16的ViT-Base模型。

          ViT實(shí)驗(yàn)設(shè)計(jì)
          ViT最核心的實(shí)驗(yàn)就是將前述的訓(xùn)練方法進(jìn)行實(shí)現(xiàn),即在大規(guī)模數(shù)據(jù)集上預(yù)訓(xùn)練后遷移到小數(shù)據(jù)集上看模型效果。為了比對(duì)CNN模型,論文特地用了Big Transfer (BiT),該模型使用大的ResNet進(jìn)行監(jiān)督遷移學(xué)習(xí),是2020 ECCV上提出的一個(gè)大CNN模型。另外一個(gè)比對(duì)CNN模型是2020年CVPR上的Noisy Student模型,是一個(gè)半監(jiān)督的大型CNN模型。

          ViT、BiT和Nosiy Student模型經(jīng)三大數(shù)據(jù)集預(yù)訓(xùn)練后在各小數(shù)據(jù)集上的準(zhǔn)確率如下表所示。


          可以看到,ViT經(jīng)過大數(shù)據(jù)集的預(yù)訓(xùn)練后,在各小數(shù)據(jù)集上的遷移后準(zhǔn)確率超過了一些SOTA CNN模型的結(jié)果。但要取得這種超越CNN的性能效果,需要大的預(yù)訓(xùn)練數(shù)據(jù)集和大模型的結(jié)合。

          所以第二個(gè)實(shí)驗(yàn)就是ViT對(duì)預(yù)訓(xùn)練數(shù)據(jù)集規(guī)模到底有怎樣的要求?論文針對(duì)此問題做了一個(gè)對(duì)比實(shí)驗(yàn)。分別在ImageNet、ImageNet-21k和JFT-300M進(jìn)行預(yù)訓(xùn)練,三個(gè)數(shù)據(jù)集規(guī)模分別為小數(shù)據(jù)集、中等規(guī)模數(shù)據(jù)集和超大數(shù)據(jù)集,預(yù)訓(xùn)練效果如下圖所示。

          從圖中可以看到,在最小的數(shù)據(jù)集ImageNet上進(jìn)行預(yù)訓(xùn)練時(shí),盡管作者加了大量的正則化操作,ViT-Large模型性能不如ViT-base模型,更遠(yuǎn)不如BiT的性能。在中等規(guī)模的ImageNet-21k數(shù)據(jù)集上,大家的表現(xiàn)都差不多,只有到了JFT-30M這樣的超大數(shù)據(jù)集上,ViT模型才能發(fā)揮出它的優(yōu)勢(shì)和效果。

          總而言之,大的預(yù)訓(xùn)練數(shù)據(jù)集加上大模型,是ViT取得SOTA性能的關(guān)鍵因素。

          ViT代碼使用與解讀
          ViT模型實(shí)現(xiàn)目前已經(jīng)有開源的框架vit-pytorch可以直接調(diào)用,直接pip安裝即可:
          pip install vit-pytorch
          vit-pytorch用法如下:
          import torchfrom vit_pytorch import ViT# 創(chuàng)建ViT模型實(shí)例v = ViT(    image_size = 256,    patch_size = 32,    num_classes = 1000,    dim = 1024,    depth = 6,    heads = 16,    mlp_dim = 2048,    dropout = 0.1,    emb_dropout = 0.1)# 隨機(jī)化一個(gè)圖像輸入img = torch.randn(1, 3, 256, 256)# 獲取輸出preds = v(img) # (1, 1000)

          各參數(shù)含義分別為:
          • image_size:原始圖像尺寸

          • patch_size:圖像塊的尺寸

          • num_classes:類別數(shù)量

          • dim:Transformer隱變量維度大小

          • depth:Transformer編碼器層數(shù)

          • Heads:MSA中的head數(shù)

          • dropout:失活比例

          • emb_dropout:嵌入層失活比例


          下面我們重點(diǎn)看一下vit.py的代碼解讀。ViT以Attention和Transformer為基礎(chǔ),所以搭建邏輯跟Transformer是一樣的,先把底層各組件搭建好后,按照ViT的前向流程進(jìn)行封裝即可。ViT所需的底層搭建組件包括規(guī)范化層、FFN、Attention,然后在此三個(gè)組件基礎(chǔ)上搭建Transformer,最后基于Transformer和ViT前向流程搭建ViT。下面我們分三個(gè)步驟來(lái)看ViT的搭建過程。
          (1) 底層組件規(guī)范化層、FFNAttention
          # 導(dǎo)入相關(guān)模塊import torchfrom torch import nn, einsumimport torch.nn.functional as Ffrom einops import rearrange, repeatfrom einops.layers.torch import Rearrange
          # 輔助函數(shù),生成元組def pair(t): return t if isinstance(t, tuple) else (t, t)
          # 規(guī)范化層的類封裝class PreNorm(nn.Module): def __init__(self, dim, fn): super().__init__() self.norm = nn.LayerNorm(dim) self.fn = fn def forward(self, x, **kwargs): return self.fn(self.norm(x), **kwargs)# FFNclass FeedForward(nn.Module): def __init__(self, dim, hidden_dim, dropout = 0.): super().__init__() self.net = nn.Sequential( nn.Linear(dim, hidden_dim), nn.GELU(), nn.Dropout(dropout), nn.Linear(hidden_dim, dim), nn.Dropout(dropout) ) def forward(self, x): return self.net(x)# Attentionclass Attention(nn.Module): def __init__(self, dim, heads = 8, dim_head = 64, dropout = 0.): super().__init__() inner_dim = dim_head * heads project_out = not (heads == 1 and dim_head == dim)
          self.heads = heads self.scale = dim_head ** -0.5
          self.attend = nn.Softmax(dim = -1) self.to_qkv = nn.Linear(dim, inner_dim * 3, bias = False)
          self.to_out = nn.Sequential( nn.Linear(inner_dim, dim), nn.Dropout(dropout) ) if project_out else nn.Identity()
          def forward(self, x): b, n, _, h = *x.shape, self.heads qkv = self.to_qkv(x).chunk(3, dim = -1)        q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = h), qkv)        dots = einsum('b h i d, b h j d -> b h i j', q, k) * self.scale        attn = self.attend(dots) out = einsum('b h i j, b h j d -> b h i d', attn, v) out = rearrange(out, 'b h n d -> b n (h d)') return self.to_out(out)

          (2) 搭建Transformer
          # 基于PreNorm、Attention和FFN搭建Transformerclass Transformer(nn.Module):    def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout = 0.):        super().__init__()        self.layers = nn.ModuleList([])        for _ in range(depth):            self.layers.append(nn.ModuleList([                PreNorm(dim, Attention(dim, heads = heads, dim_head = dim_head, dropout = dropout)),                PreNorm(dim, FeedForward(dim, mlp_dim, dropout = dropout))            ]))    def forward(self, x):        for attn, ff in self.layers:            x = attn(x) + x            x = ff(x) + x        return x


          (3) 搭建ViT

          class ViT(nn.Module):    def __init__(self, *, image_size, patch_size, num_classes, dim, depth, heads, mlp_dim, pool = 'cls', channels = 3, dim_head = 64, dropout = 0., emb_dropout = 0.):        super().__init__()        image_height, image_width = pair(image_size)        patch_height, patch_width = pair(patch_size)
          assert image_height % patch_height == 0 and image_width % patch_width == 0, 'Image dimensions must be divisible by the patch size.' # patch數(shù)量 num_patches = (image_height // patch_height) * (image_width // patch_width) # patch維度 patch_dim = channels * patch_height * patch_width assert pool in {'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)' # 定義塊嵌入 self.to_patch_embedding = nn.Sequential( Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_height, p2 = patch_width), nn.Linear(patch_dim, dim), ) # 定義位置編碼 self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim)) # 定義類別向量 self.cls_token = nn.Parameter(torch.randn(1, 1, dim)) self.dropout = nn.Dropout(emb_dropout)
          self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout)
          self.pool = pool self.to_latent = nn.Identity() # 定義MLP self.mlp_head = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, num_classes) ) # ViT前向流程 def forward(self, img): # 塊嵌入 x = self.to_patch_embedding(img) b, n, _ = x.shape # 追加類別向量 cls_tokens = repeat(self.cls_token, '() n d -> b n d', b = b) x = torch.cat((cls_tokens, x), dim=1) # 追加位置編碼 x += self.pos_embedding[:, :(n + 1)] # dropout x = self.dropout(x) # 輸入到transformer x = self.transformer(x) x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0] x = self.to_latent(x) # MLP return self.mlp_head(x)


          小結(jié)

          ViT作為Visual Transformer的一篇開創(chuàng)性研究,可以算是了解該方向的一篇必讀論文了。今年上半年以來(lái),大量基于ViT的視覺任務(wù)研究不斷的被提出,ViT在其中基本上扮演了類似VGG16或者ResNet-52在CNN中Backbone的角色。雖然是一篇開創(chuàng)性的工作,但ViT仍有大量的使用限制,大數(shù)據(jù)集和大模型,這兩點(diǎn)就已經(jīng)將大多數(shù)人望而卻步了。當(dāng)然,這些缺陷,在后來(lái)的研究中也在不斷的被克服。


          參考資料:

          An Image Is Worth 16X16 Words: Transformers for Image Recognition at Scale

          https://github.com/lucidrains/vit-pytorch

          https://mp.weixin.qq.com/s/ozUHHGMqIC0-FRWoNGhVYQ

          往期精彩:

          【原創(chuàng)首發(fā)】機(jī)器學(xué)習(xí)公式推導(dǎo)與代碼實(shí)現(xiàn)30講.pdf

          【原創(chuàng)首發(fā)】深度學(xué)習(xí)語(yǔ)義分割理論與實(shí)戰(zhàn)指南.pdf

           談中小企業(yè)算法崗面試

           算法工程師研發(fā)技能表

           真正想做算法的,不要害怕內(nèi)卷

           算法工程師的日常,一定不能脫離產(chǎn)業(yè)實(shí)踐

           技術(shù)學(xué)習(xí)不能眼高手低

           技術(shù)人要學(xué)會(huì)自我營(yíng)銷

           做人不能過擬合

          求個(gè)在看

          ?
          瀏覽 359
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99爱视频精品在线观看 | 人人看人人摸人人干 | 蜜桃视频网址 | 免费无码三级片在线观看 | 免费亚洲视频在线观看 |