HyperAI超神経
Back to Headlines

ビジョントランスフォーマー(ViT)の実装とCIFAR-10での結果:CNNに代わる新時代 ビジョントランスフォーマー(ViT)の基本から実装まで:CIFAR-10での性能評価 ビジョントランスフォーマー(ViT)入門:CNNを凌駕する画像認識モデル ビジョントランスフォーマー(ViT)の解剖:CIFAR-10データセットでの実装と訓練 ビジョントランスフォーマー(ViT):CNNの限界を超える新しいアプローチ MIT学生が解説:ビジョントランスフォーマー(ViT)の構築とCIFAR-10での訓練 ビジョントランスフォーマー(ViT)の実践ガイド:CIFAR-10での性能と分析 ビジョントランスフォーマー(ViT)の全解説:CNNと比較してみよう ビジョントランスフォーマー(ViT)の実装方法:CIFAR-10での訓練結果 ビジョントランスフォーマー(ViT):CNNの代わりに注目される理由と実践

1日前

ピクセルから予測へ:画像用トランスフォーマーの構築 CNN(畳み込みニューラルネットワーク)がコンピュータビジョンの中心的存在だったが、それが障害になっている可能性があることに注目が集まった。2020年、Googleの研究者チームは大胆な問いかけをした。「畳み込みを使わずに、世界クラスの画像モデルは作れるだろうか?」彼らの提案したビジョントランスフォーマー(ViT)は、画像をテキストのように処理することで、 attention 機能を活用している。 1. 背景と直感的理解 1.1 NLPにおけるリカレントモデルからトランスフォーマーへの移行 2017年以前、NLP(自然言語処理)はRNNやLSTMによって主導されていた。しかし、これらのモデルはパラレル化が難しく、長いシーケンスでは初期のトークンの情報を維持するのが困難だった。2017年に、Googleの研究者は「Attention Is All You Need」という論文で、 attention に基づく新しいアーキテクチャ「トランスフォーマー」を紹介した。この仕組みは、各トークンがシーケンス内の全てのトークンと直接通信することができるため、効率性と大域的理解能力が向上した。その後、トランスフォーマーはBERT、GPT、T5などの大規模モデルの基盤となった。 1.2 アテンションは畳み込みを置き換えることができるか? 2020年、Dosovitskiyらは論文「An Image Is Worth 16×16 Words」で、画像をテキストのように扱う大胆なアイデアを提起した。彼らは画像をパッチに分割し、標準的なトランスフォーマーに入力することで、大規模データセットを用いなくても競争力のある結果を得られることが示された。ただし、初期のViTは大規模データセットが必要だったが、次第に改善され、現代のコンピュータビジョンにおいて核心的なコンポーネントとなっている。 2. ビジョントランスフォーマーの仕組み 2.1 パッチ埋め込み 画像は通常、2Dグリッドのピクセルで構成されているが、トランスフォーマーは1Dシーケンスとして入力を処理する。そのため、ViTは画像を16×16ピクセルの非重複正方形パッチに分割し、それぞれを1Dベクトルに平坦化して固定サイズの埋め込みに入れ込む。 2.2 クラステークンと位置埋め込み トランスフォーマーは並べ替えに対する不変性を持つため、順序を理解するために位置埋め込みが必要である。また、クラステークン [CLS] が前部に配置され、このトークンがシーケンス全体の情報を集約する役割を担う。 2.3 マルチヘッド自己注意(MHSA) ViTの核心部分はMHSAで、これにより各パッチ間の関連性が空間的な距離に関係なく理解できる。入力は複数の「ヘッド」に分割され、各ヘッドは異なる特徴に-focusedする。例えば、あるヘッドはエッジ、別のヘッドはテクスチャ、さらに別のヘッドは空間配列に-focusedする。出力は結合されて元の埋め込み空間に戻される。 2.4 トランスフォーマーエンコーダー エンコーダーブロックはViTの基本単位で、自己注意とMLP(多層パーセプトロン)レイヤーを残差接続と共に積み重ねることで構成される。この設計により、モデルは大域的な推論を行う一方で、稳定性を保つ。 2.5 分類ヘッド 全てのトランスフォーマーブロックを通過した後、[CLS]トークンの最終埋め込みが画像全体の要約表現として使用される。このベクトルを線形レイヤーを通じてクラスの確率を出力することで分類を行う。 3. 実装ガイド MITの理工学部生がViTをスクラッチで実装した歩みを追う。 3.1 パッチ埋め込み 画像パッチを系列の埋め込みに変換するために、Conv2dレイヤーを使用する。これは効率的で、逆伝播も簡単に行える。 ```python class PatchEmbed(nn.Module): def init(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768): super().init() self.img_size = img_size self.patch_size = patch_size self.num_patches = (img_size // patch_size) ** 2 self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) def forward(self, x): x = self.proj(x) x = x.flatten(2) x = x.transpose(1, 2) return x ``` 3.2 クラステークンと位置埋め込み ViTの入力をトランスフォーマーエンコーダーに適切な形状にして返すモジュールを定義する。 ```python class ViTEmbed(nn.Module): def init(self, num_patches, embed_dim): super().init() self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim)) def forward(self, x): batch_size = x.shape[0] cls_tokens = self.cls_token.expand(batch_size, -1, -1) x = torch.cat((cls_tokens, x), dim=1) x = x + self.pos_embed return x ``` 3.3 マルチヘッド自己注意 MHSAを実装する。各トークンはQ(クエリ)、K(キー)、V(バリュー)ベクトルに投影され、複数のヘッドで同時に対応する。 ```python class MyMultiheadAttention(nn.Module): def init(self, embed_dim, num_heads): super().init() assert embed_dim % num_heads == 0, "embed_dim must be divisible by num_heads" self.embed_dim = embed_dim self.num_heads = num_heads self.head_dim = embed_dim // num_heads self.q_proj = nn.Linear(embed_dim, embed_dim) self.k_proj = nn.Linear(embed_dim, embed_dim) self.v_proj = nn.Linear(embed_dim, embed_dim) self.out_proj = nn.Linear(embed_dim, embed_dim) def forward(self, x): B, T, C = x.shape Q = self.q_proj(x) K = self.k_proj(x) V = self.v_proj(x) def split_heads(tensor): return tensor.view(B, T, self.num_heads, self.head_dim).transpose(1, 2) Q = split_heads(Q) K = split_heads(K) V = split_heads(V) scores = torch.matmul(Q, K.transpose(-2, -1)) / self.head_dim ** 0.5 attn = torch.softmax(scores, dim=-1) out = torch.matmul(attn, V) out = out.transpose(1, 2).contiguous().view(B, T, C) return self.out_proj(out) ``` 3.4 トランスフォーマーエンコーダー エンコーダーブロックを構築する。自己注意とMLPレイヤーを残差接続と共に積み重ねる。 ```python class TransformerBlock(nn.Module): def init(self, embed_dim, num_heads, mlp_ratio=4.0): super().init() self.norm1 = nn.LayerNorm(embed_dim) self.attn = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True) self.norm2 = nn.LayerNorm(embed_dim) self.mlp = nn.Sequential( nn.Linear(embed_dim, int(embed_dim * mlp_ratio)), nn.GELU(), nn.Linear(int(embed_dim * mlp_ratio), embed_dim) ) def forward(self, x): x = x + self.attn(self.norm1(x), self.norm1(x), self.norm1(x))[0] x = x + self.mlp(self.norm2(x)) return x ``` 3.5 すべてをつなぎ合わせる SimpleViTモデルを構築する。 ```python class SimpleViT(nn.Module): def init(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, depth=12, num_heads=12, num_classes=1000): super().init() self.patch_embed = PatchEmbed(img_size, patch_size, in_chans, embed_dim) num_patches = (img_size // patch_size) ** 2 self.vit_embed = ViTEmbed(num_patches, embed_dim) self.blocks = nn.Sequential(*[ TransformerBlock(embed_dim, num_heads) for _ in range(depth) ]) self.norm = nn.LayerNorm(embed_dim) self.head = nn.Linear(embed_dim, num_classes) def forward(self, x): x = self.patch_embed(x) x = self.vit_embed(x) x = self.blocks(x) x = self.norm(x) return self.head(x[:, 0]) ``` 4. ViTの訓練 4.1 データセット: CIFAR-10 CIFAR-10データセット(60,000枚の32×32ピクセルの画像、10クラス)を使用してViTを訓練した。各画像の小さなサイズが訓練に有利だった。 4.2 モデル設定: CIFAR-10向けのViTアダプテーション 元のViTはImageNetのような大規模データセットを想定していたが、CIFAR-10では以下のような調整が必要だった: - 画像サイズ:32×32ピクセル - パッチサイズ:4×4ピクセル → 64トークン - 埋め込み次元:192 - トランスフォーマーブロック数:6 - ヘッド数:3 4.3 訓練セットアップ 訓練は以下の設定で行った: - 最適化アルゴリズム:Adam - 学習率:0.001 - バッチサイズ:64 訓練は約30秒毎epochで、合計15分程度で完了した。 4.4 結果 30epochの訓練後、60%程度の精度が達成された。モデルは猫やかえるなどの classe を正確に特定できたが、船と飛行機などの視覚的に似ている classe に苦戦した。クラスごとの accuracy は、特に犬や自動車での高精度が目立った。 業界関係者はViTの導入により、コンピュータビジョンの将来性が大きく開かれたと評価している。MITの研究者のこのプロジェクトを通じて、ViTの仕組みと実装方法がより詳しく理解されるようになった。SimpleViTの実装は、PyTorchで完全にスクラッチから始めて行われたもので、将来のさらなる発展にも貢献することが期待されている。

Related Links