HyperAI
Back to Headlines

De la Pixelisation à la Prédiction : Construire un Transformer pour les Images

il y a un jour

Du Pixel à la Prédiction : Construction d'un Transformers pour les_images En 2020, une équipe de chercheurs de Google a posé la question audacieuse : peuvent-nous abandonner toute utilisation de convolutions et encore construire des modèles d'images de classe mondiale ? Leur réponse, le Vision Transformer (ViT), a ouvert une nouvelle ère dans l'apprentissage profond. 1. Contexte et Intuition 1.1 De Modèles Récurrents à l'Ascension des Transformers en NLP Avant 2017, le traitement du langage naturel (NLP) était dominé par les RNNs et les LSTMs, qui étaient utilisés pour des tâches comme la traduction automatique et le modèle de langage. Mais ces modèles avaient des limitations fondamentales : ils traitaient les séquences de manière séquentielle, rendant difficile la parallélisation de l'entraînement, et ils peinaient à conserver des informations au fur et à mesure que les séquences se lengthaient. Ces contraintes entravaient leur capacité à échelonner, en particulier pour des tâches nécessitant une compréhension globale du langage. En 2017, les chercheurs de Google ont introduit le Transformers, avec leur article "Attention Is All You Need". Ce nouveau modèle était basé sur le mécanisme d'auto-attention, permettant à chaque token de considérer directement tous les autres tokens de la séquence. Cette approche a éliminé la nécessité de la récurrence et a résolu les principaux problèmes des RNNs. Dans les deux ans suivants, les Transformers ont entièrement pris le relais des RNNs dans le NLP, se révélant plus efficaces et mieux adaptés pour modéliser les dépendances sur de longues distances. 1.2 Peut-on Remplacer la Convolution par de l'Attention ? Les Convolutional Neural Networks (CNNs) étaient alors la norme en vision par ordinateur (computer vision), mais elles souffraient d'limitations similaires. Les convolutions sont essentiellement locales, rendant difficile la capture de dépendances sur de longue distance. Elles dépendent également fortement de structures spatiales et nécessitent un soin particulier pour l'ingénierie des caractéristiques. Devant ces défis, les chercheurs de Google ont proposé une alternative audacieuse : traiter une image comme une phrase en divisant celle-ci en des patches de 16x16 pixels, puis les alimenter dans un Transformers. Bien que les premiers ViT nécessitaient des ensembles de données massifs pour rivaliser avec les CNNs, cette approche a prouvé que les modèles basés sur l'attention pouvaient fonctionner pour la vision, tout comme pour le langage. 2. Comment Fonctionnent les Vision Transformers 2.1 Embedding de Patches Les Transformers ont été conçus pour traiter des séquences, comme des phrases composées de tokens. Or, les images sont des grilles bidimensionnelles de pixels. Pour les alimenter dans un Transformers, le Vision Transformer divise l'image en patches de taille fixe (par exemple, 16x16 pixels) qui sont ensuite aplatis en vecteurs 1D et projetés linéairement dans un espace d'embedding de taille fixe. Par analogie : Un tokenizer transforme une phrase en une séquence de vecteurs de mots, tandis qu'un ViT transforme une image en une séquence de vecteurs de patches. 2.2 Jeton de Classe et Embeddings Positionnels Pour que les Transformers puissent traiter correctement des séquences d'images, il faut ajouter deux éléments cruciaux : - Un jeton spécial [CLS] pour agrégier les informations globales. - Des embeddings positionnels pour coder la structure spatiale. Le jeton [CLS] est préfixé à la séquence de patches et participe, au cours de l'auto-attention, à l'intégration d'informations de tous les patches. Les embeddings positionnels, quant à eux, sont ajoutés à chaque jeton de la séquence pour donner au modèle une conscience spatiale. 2.3 Multi-Head Self-Attention (MHSA) Au cœur du Vision Transformer se trouve le mécanisme de multi-head self-attention (MHSA), qui lui permet de comprendre les relations entre les patches, indépendamment de leur distance spatiale. Au lieu d'utiliser une seule fonction d'attention, le MHSA divise l'entrée en plusieurs "têtes". Chaque tête apprend à se concentrer sur différents aspects de l'entrée, comme les contours, les textures ou la disposition spatiale, avant de concaténer leurs sorties et de les projeter de retour dans l'espace d'embedding original. Le MHSA fonctionne en projetant chaque jeton d'entrée en trois vecteurs : la requête (Q), la clé (K) et la valeur (V). L'attention est calculée en parallèle sur toutes les têtes, puis appliquée aux valeurs, et enfin les sorties sont combinées. 2.4 Bloc Encodeur Transformer Une fois que nous avons l'auto-attention, elle est encapsulée dans un bloc plus large, le bloc de l'encodeur Transformer. Ce bloc est le module fondamental des ViT et des Transformers NLP. Il combine : - Une normalisation avant l'attention (LayerNorm pré-norm). - Une multi-head self-attention appliquée à l'entrée normalisée. - Une connexion résiduelle pour ajouter la sortie de l'attention à l'entrée. - Une autre normalisation, suivie d'une petite couche de réseau de neurones multicouches (MLP). - Une autre connexion résiduelle pour ajouter la sortie de l'MLP. Cette structure est répétée à travers toutes les couches de l'encodeur Transformer. 2.5 Tête de Classification Après avoir passé l'entrée à travers plusieurs couches de l'encodeur Transformer, le modèle doit produire une prédiction finale. C'est la tête de classification qui s'en charge. Pendant l'étape d'embedding, un jeton spécial [CLS] a été ajouté en début de séquence pour agrégir les informations de tous les patches d'image. Après toutes les couches de l'encodeur, l'embedding final du jeton [CLS] sert de représentation résumée de l'ensemble de l'image. Ce vecteur est ensuite passé à une simple couche linéaire pour générer les logits de classe. 3. Guide d'Implémentation Nous allons implémenter les modules clés d'un Vision Transformer à partir de zéro. 3.1 Embedding de Patches Pour convertir les patches d'image en une séquence d'embeddings, nous utilisons une couche Conv2D avec un noyau et un stride égaux à la taille du patch. Cela extrait des patches non chevauchants et applique une projection linéaire apprise, en une seule opération. ```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): B, C, H, W = x.shape x = self.proj(x) # [B, embed_dim, H/patch, W/patch] x = x.flatten(2) # [B, embed_dim, num_patches] x = x.transpose(1, 2) # [B, num_patches, embed_dim] return x ``` 3.2 Jeton de Classe et Embeddings Positionnels Ce module définit un jeton de classe appris [CLS] et des embeddings positionnels pour chaque jeton, y compris le [CLS]. Cela produit une séquence prête pour l'encodeur Transformer. ```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)) # [1, 1, D] self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim)) # [1, N+1, D] def forward(self, x): batch_size = x.shape[0] cls_tokens = self.cls_token.expand(batch_size, -1, -1) # [B, 1, D] x = torch.cat((cls_tokens, x), dim=1) # [B, N+1, D] x = x + self.pos_embed # [B, N+1, D] return x ``` 3.3 Multi-Head Self-Attention Nous mettons en place le mécanisme MHSA. Chaque jeton d'entrée est projeté en une requête (Q), une clé (K) et une valeur (V). L'attention est calculée en parallèle sur plusieurs têtes, puis les sorties sont concaténées et projetées de retour dans l'espace d'embedding original. ```python class MyMultiheadAttention(nn.Module): def init(self, embed_dim, num_heads): super().init() assert embed_dim % num_heads == 0, "embed_dim doit être divisible par 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 # [batch, seq_len, embed_dim] 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)) # [B, heads, T, T] scores /= self.head_dim ** 0.5 attn = torch.softmax(scores, dim=-1) out = torch.matmul(attn, V) # [B, heads, T, head_dim] out = out.transpose(1, 2).contiguous().view(B, T, C) return self.out_proj(out) ``` 3.4 Bloc Encodeur Transformer Le bloc de l'encodeur Transformer encapsule l'auto-attention et la couche MLP avec des connexions résiduelles pour assurer la stabilité et la transformation des représentations. ```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 Rassemblement des Composants Maintenant que nous avons tous les composants clés du Vision Transformer, il est temps de les assembler en un modèle complet. ```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. Entraînement du ViT 4.1 Jeu de Données : CIFAR-10 Nous avons entraîné notre Vision Transformer sur CIFAR-10, une base de données référente comportant 60 000 images réparties en 10 classes (avions, chats, bateaux, etc.). Chaque image est de taille 32x32 pixels, ce qui rend CIFAR-10 : - Rapidement accessible pour l'entraînement. - Propice aux tests de simplicité modélisatrice. 4.2 Configuration du Modèle : Adaptation du ViT à CIFAR-10 Les ViT sont originellement conçus pour des ensembles de données à grande échelle comme ImageNet. Pour rendre l'entraînement faisable sur CIFAR-10 avec des ressources limitées, nous avons ajusté la configuration du modèle : python model = SimpleViT( img_size=32, # Images de taille 32x32 patch_size=4, # Patches de 4x4 pixels → 64 tokens in_chans=3, embed_dim=192, # Réduction de la dimension d'embedding depth=6, # Moins de couches Transformer num_heads=3, # Divisible par la dimension d'embedding num_classes=10 # Pour les 10 classes de CIFAR-10 ).to(device) 4.3 Configuration de l'Entraînement Le modèle a été entraîné en utilisant : Une perte de croissance logarithmique (crossEntropyLoss). L'optimizer Adam. Une taille de lot (batch size) de 128. L'entraînement était efficace, avec environ 30 secondes par epoch, grâce à l'utilisation d'une GPU et à l'optimisation des hyperparamètres. 4.3 Résultats Après 30 epochs, totalisant environ 15 minutes d'entraînement sur une GPU, le modèle a atteint une précision d'environ 60% sur l'ensemble de test CIFAR-10. C'est un solide point de départ, compte tenu de la simplicité du modèle et de la taille relativement petite de l'ensemble de données. Progression de l'Apprentissage Les figures ci-dessous montrent l'évolution de la perte d'entraînement et de la précision sur l'ensemble de test au fil des epochs. Figure 6 : Évolution de la perte d'entraînement et de la précision sur l'ensemble de test sur 30 epochs. Exemples de Predicitions Voici quelques exemples de sorties du modèle. Bien qu'il ait identifié correctement de nombreuses images (comme des chats et des grenouilles), il a eu des difficultés avec les classes visuellement similaires (par exemple, confondre un bateau avec un avion). Figure 7 : Exemples de prédictions sur des images de CIFAR-10. Précision Par Classe Le graphique ci-dessous montre la performance du modèle sur chacune des 10 classes. Notamment : Figure 8 : Précision par classe. Evaluation de l'Événement par des Professionnels de l'Industrie L'introduction du Vision Transformer a suscité un fort engouement et a ouvert de nouvelles voies de recherche en vision par ordinateur. De nombreux experts dans le domaine estiment que le ViT est une avancée significative, permettant une meilleure généralisation et des performances compétitives même avec des ensembles de données plus petits. Projet de l'Entreprise Google, à travers sa division de recherche, est à l'avant-garde des innovations en apprentissage profond. Le travail sur le Vision Transformer illustre clairement leur capacité à repenser les paradigmes existants et à ouvrir de nouvelles perspectives. Ce projet a déjà inspiré de nombreuses applications pratiques et continue à influencer le développement de modèles basés sur l'attention dans le domaine de la vision par ordinateur. Ce résumé offre une vue d'ensemble concise et précise de l'architecture, de l'implémentation et des résultats du Vision Transformer, adaptant son contenu pour le rendre accessible et engageant pour un public technologique.

Related Links