Visual Transformer の始まり - ViT とそのコード実装


ディープラーニングのナレッジポイントのまとめ

コラムリンク:
https://blog.csdn.net/qq_39707285/article/details/124005405

此专栏主要总结深度学习中的知识点,从各大数据集比赛开始,介绍历年冠军算法;同时总结深度学习中重要的知识点,包括损失函数、优化器、各种经典算法、各种算法的优化策略Bag of Freebies (BoF)等。


RNNからTransformerシリーズへの注目まで

コラムリンク:
https://blog.csdn.net/qq_39707285/category_11814303.html

此专栏主要介绍RNN、LSTM、Attention、Transformer及其代码实现。


YOLOシリーズのターゲット検出アルゴリズム

コラムリンク:
https://blog.csdn.net/qq_39707285/category_12009356.html

此专栏详细介绍YOLO系列算法,包括官方的YOLOv1、YOLOv2、YOLOv3、YOLOv4、Scaled-YOLOv4、YOLOv7,和YOLOv5,以及美团的YOLOv6,还有PaddlePaddle的PP-YOLO、PP-YOLOv2等,还有YOLOR、YOLOX、YOLOS等。


ビジュアルトランスフォーマー

コラムリンク:
https://blog.csdn.net/qq_39707285/category_12184436.html

此专栏详细介绍各种Visual Transformer,包括应用到分类、检测和分割的多种算法。



ViT
ViT は Visual Transformer の始まりであり、CV フィールドに Transformer を適用したのは初めてです。論文: 「画像は 16X16 語の価値がある: 大規模な画像認識のためのトランスフォーマー」。

1 はじめに

  Transformer アーキテクチャは自然言語処理タスクにとって不可欠な標準となっていますが、コンピューター ビジョンでの応用はまだ限定されています。ビジョンでは、アテンションは畳み込みネットワークと組み合わせて使用​​されるか、全体の構造を変更せずに畳み込みネットワークの一部のコンポーネントを置き換えるために使用されます。この論文は、ニューラル ネットワークへの依存が不要であり、一連の画像パッチを直接適用する純粋な Transformer が画像分類タスクを適切に実行できることを示しています。Visual Transformer (ViT) は、大量のデータで事前トレーニングされ、いくつかの中小規模の画像認識ベンチマーク (ImageNet、CIFAR-100、VTAB など) に転送された場合、最先端の畳み込みネットワークを上回るパフォーマンスを発揮します。その結果、トレーニングに必要なコンピューティング リソースが大幅に削減されます。

2.モデル

  モデル全体図は次のとおりです。モデル全体図。画像を固定サイズのパッチに分割し、各パッチを線形に埋め込み、位置埋め込みを追加して、結果のベクトルのシーケンスを標準の Transformer エンコーダに送ります。分類のために、追加の学習可能な「クラス トークン」がシーケンスに追加されます。
ここに画像の説明を挿入

2.1 入力映像 2D から 1D

  標準の Transformer 入力は 1D シーケンスです。2D 画像を処理するには、画像 x-(H×W×C) を一連の平坦化された 2D パッチ xpshape に再形成します: ( N × ( P 2 ⋅ C ) ) x_p 形状:( N×(P^2・C))バツp大丈夫_ _ _:( N×( P2C)),其中 H 、 W H、W H Wは元の画像の高さと幅、CCCはチャネル数、( P , P ) (P,P)( P P )は各画像パッチの解像度、N = HW / P 2 N=HW/P^2N=HW / P _2は、最終パッチの総数と Transformer 入力シーケンスの長さです。Transformer はすべてのレイヤーで固定サイズのベクトル D を使用するため、パッチを平坦化し、トレーニング可能な線形投影 (式 1) を使用して D 次元にマッピングする必要があります。この投影の出力は、パッチ埋め込み (パッチ埋め込み) と呼ばれます。

ここに画像の説明を挿入

2.2 [クラス]トークン

  BERT の [class] トークンと同様に、埋め込みパッチ シーケンス( z 0 0 = xclass ) (z^0_0=x_{class})内にあります。z00=バツクラス_ _ _)学習可能な埋め込みを追加します。その状態は Transformer エンコーダー( z L 0 ) (z^0_L)zL0)は、画像の表現 y として使用されます (式 4)。事前トレーニングおよび微調整中、z L 0 z^0_LzL0両方に選別ヘッドが搭載されています。分類ヘッドは、事前トレーニング中に 1 つの隠れ層、微調​​整中に 1 つの線形層を備えた MLP によって実装されます。

2.3 位置埋め込み

  位置情報を保存するために、位置埋め込みがパッチ埋め込みに追加されます。より高度な 2D 対応位置エン​​ベディングを使用しても大幅なパフォーマンスの向上は観察されていないため、この論文では標準的な学習可能な 1D 位置エンベディングを使用します。加算後に得られる埋め込みベクトルのシーケンスは、エンコーダーへの入力として使用されます。
Transformer エンコーダは、交互のマルチヘッド セルフ アテンション (MSA) ブロックと MLP ブロックで構成されます。Layernorm (LN) は各ブロックの前に適用され、残りの接続は各ブロックの後に適用されます。

2.4 誘導バイアス

  Vision Transformer には、CNN よりも画像固有の誘導バイアスが少ないことに注意してください。ニューラル ネットワークでは、モデル全体の各層の局所性、2 次元近傍構造、および並進不変性を反映できます。ViT では、MLP 層のみがローカルで変換不変であり、セルフアテンション層はグローバルです。2D 近傍構造は非常に慎重に使用されます。モデルの開始時に画像を小さな断片に切り出し、微調整中に異なる解像度の画像の位置的な埋め込みを調整します。それに加えて、初期化時の位置埋め込みにはパッチの 2D 位置に関する情報が含まれていないため、パッチ間のすべての空間関係を最初から学習する必要があります。

2.5 ハイブリッドアーキテクチャ

  生の画像パッチの代わりに、CNN の特徴マップから入力シーケンスを形成できます。このハイブリッド モデルでは、パッチ埋め込み射影 E (式 1) が CNN 特徴マップから抽出されたパッチに適用されます。特殊なケースとして、パッチは空間サイズ 1x1 を持つことができます。これは、入力シーケンスが、特徴マップの空間次元を単純に平坦化し、Transformer 次元に投影することによって取得されることを意味します。上記のように、カテゴリ入力埋め込みと場所埋め込みを追加します。

3. コードの実装

3.1 パラメータの定義

  • 入力画像サイズ: image_size=256
  • 各パッチのサイズ: patch_size=16
  • 出力カテゴリの総数: num_classes=1000
  • 画像パッチのエンコーディング次元: dim=1024
  • トランスエンコーダの深さ: 深さ=6
  • MAS ヘッドの総数: ヘッド = 16
  • MLP ディメンション: mlp_dim=2048
import torch
from torch import nn

from einops import rearrange, repeat
from einops.layers.torch import Rearrange

# helpers

def pair(t):
    return t if isinstance(t, tuple) else (t, t)
    
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):
        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.'

        num_patches = (image_height // patch_height) * (image_width // patch_width)
        patch_dim = channels * patch_height * patch_width
        assert pool in {
    
    'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)'
        ...

3.2 画像エンコード

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):
        super().__init__()
        
        ...
		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),
        )
        ...
        
    def forward(self, img):
        x = self.to_patch_embedding(img)
        ...
        

  入力画像の形状 = ( B × C × H × W ) 形状 = (B × C × H × W)大丈夫_ _ _=( B×C×H×W )、最初に( B × ( h × w ) × ( p 1 × p 2 × C ) ) (B × (h × w) × (p_1 × p_2 × C)) に変形します。( B×( h××( p1×p2×C ))、ここで、H、W はピクチャの元の幅と高さ、p 1 、p 2 p_1、p_2p1p2画像パッチのサイズ、h、wh、whw は画像パッチの数です。次に、線形レイヤーを使用して、指定した寸法に変換します。

3.3 クラストークンの追加

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):
        super().__init__()
        
        ...
		self.cls_token = nn.Parameter(torch.randn(1, 1, dim))
        ...
        
    def forward(self, img):
        x = self.to_patch_embedding(img)
        b, n, _ = x.shape

        cls_tokens = repeat(self.cls_token, '1 1 d -> b 1 d', b = b)
        x = torch.cat((cls_tokens, x), dim=1)
        ...

  まずクラス トークンを B にコピーします。B はバッチ サイズです。次に、パッチシーケンスの前に接続します。

3.4 位置コーディング

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):
        super().__init__()
        
        ...
		self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim))
        ...
        
    def forward(self, img):
        x = self.to_patch_embedding(img)
        b, n, _ = x.shape

        cls_tokens = repeat(self.cls_token, '1 1 d -> b 1 d', b = b)
        x = torch.cat((cls_tokens, x), dim=1)
        x += self.pos_embedding[:, :(n + 1)]
        ...
    

3.5 変圧器


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)

class 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)

class 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.dropout = nn.Dropout(dropout)

        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):
        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 = self.heads), qkv)

        dots = torch.matmul(q, k.transpose(-1, -2)) * self.scale

        attn = self.attend(dots)
        attn = self.dropout(attn)

        out = torch.matmul(attn, v)
        out = rearrange(out, 'b h n d -> b n (h d)')
        return self.to_out(out)

class 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

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):
        super().__init__()
        
        ...
		self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout)
        ...
        
    def forward(self, img):
        x = self.to_patch_embedding(img)
        b, n, _ = x.shape

        cls_tokens = repeat(self.cls_token, '1 1 d -> b 1 d', b = b)
        x = torch.cat((cls_tokens, x), dim=1)
        x += self.pos_embedding[:, :(n + 1)]

		x = self.transformer(x)
        ...
        

3.6 MLP層

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):
        super().__init__()
        
        ...
		self.mlp_head = nn.Sequential(
            nn.LayerNorm(dim),
            nn.Linear(dim, num_classes)
        )
        ...
        
    def forward(self, img):
        ...
		x = self.transformer(x)

        x = x[:, 0]
        out = self.mlp_head(x)
        return out
    

3.7 コード全体

コードのダウンロード アドレス:
https://download.csdn.net/download/qq_39707285/87405676

おすすめ

転載: blog.csdn.net/qq_39707285/article/details/128811927