Erstellen eines Vision Transformers von Grund auf in PyTorch | von Akshay Ballal | Juni 2023

0
27


Aufschlüsselung der Architektur und Implementierungsschritte für die Bildklassifizierung auf dem neuesten Stand der Technik

Bild generiert durch Secure Diffusion 2.1

In den letzten Jahren wurde der Bereich der Laptop Imaginative and prescient durch das Aufkommen von Transformatormodellen revolutioniert. Ursprünglich für die Verarbeitung natürlicher Sprache konzipiert, haben sich Transformer auch bei der Erfassung räumlicher Abhängigkeiten in visuellen Daten als äußerst leistungsfähig erwiesen. Der Imaginative and prescient Transformer (ViT) ist ein Paradebeispiel hierfür und stellt eine neuartige Architektur vor, die bei verschiedenen Bildklassifizierungsaufgaben modernste Leistung erzielt.

In diesem Artikel begeben wir uns auf die Reise, um mit PyTorch unseren eigenen Imaginative and prescient Transformer zu bauen. Indem wir die Implementierung Schritt für Schritt aufschlüsseln, möchten wir ein umfassendes Verständnis der ViT-Architektur vermitteln und es Ihnen ermöglichen, ihr Innenleben klar zu erfassen. Natürlich könnten wir immer die in PyTorch integrierte Implementierung des Imaginative and prescient Transformer-Modells verwenden, aber was ist der Spaß daran?

Für das Verständnis der Imaginative and prescient Transformer-Architektur ist es wichtig, sie von Grund auf neu zu erstellen. In den folgenden Abschnitten werden wir jede Komponente des ViT-Modells analysieren und ihren Zweck erläutern. Wir beginnen mit der Patch-Embedding-Ebene, die dafür verantwortlich ist, das Eingabebild in kleinere Patches zu unterteilen und diese in ein Vektorformat einzubetten. Anschließend werden wir den Multi-Head Self Consideration Block untersuchen, der es dem Modell ermöglicht, globale und lokale Beziehungen innerhalb der Patches zu erfassen.

Um die Imaginative and prescient Transformer-Architektur zu verstehen, muss sie von Grund auf neu erstellt werden. In den folgenden Abschnitten werden wir jede Komponente des ViT-Modells analysieren und ihren Zweck erläutern. Wir beginnen mit der Patch-Embedding-Ebene, die das Eingabebild in kleinere Patches unterteilt und diese in ein Vektorformat einbettet. Anschließend untersuchen wir den Multi-Head Self Consideration Block, der es dem Modell ermöglicht, globale und lokale Beziehungen innerhalb der Patches zu erfassen.

Darüber hinaus befassen wir uns mit dem Machine Studying Perceptron Block, einer entscheidenden Komponente für die Erfassung hierarchischer Darstellungen der Eingabedaten. Durch den Zusammenbau dieser Komponenten konstruieren wir den Transformer Block, den Kernbaustein des Imaginative and prescient Transformer.

Schließlich bringen wir alles zusammen, indem wir das ViT-Modell erstellen und dabei die sorgfältig gefertigten Komponenten verwenden. Mit dem fertigen Modell haben Sie die Freiheit, zu experimentieren, Feinabstimmungen vorzunehmen und sein Potenzial für verschiedene Laptop-Imaginative and prescient-Aufgaben auszuschöpfen.

Am Ende dieses Beitrags verfügen Sie über ein solides Verständnis der Imaginative and prescient Transformer-Architektur und ihrer PyTorch-Implementierung. Dieses Wissen ermöglicht es Ihnen, das Modell an Ihre spezifischen Anforderungen anzupassen und zu erweitern oder sich sogar an fortgeschrittene Laptop-Imaginative and prescient-Anwendungen zu wagen. Lassen Sie uns gemeinsam auf diese spannende Reise gehen. Lasst uns beginnen.

Hinweis: Der Hyperlink zum Colab Pocket book befindet sich am Ende des Artikels

!pip set up -q torchinfo
import torch
from torch import nn
from torchinfo import abstract

1.1. Laden Sie die Daten aus dem Web herunter (hierfür handelt es sich um eine ZIP-Datei).
1.2. Extrahieren Sie die ZIP-Datei
1.3. Löschen Sie die ZIP-Datei

import requests
from pathlib import Path
import os
from zipfile import ZipFile

# Outline the URL for the zip file
url = "https://github.com/mrdbourke/pytorch-deep-learning/uncooked/fundamental/information/pizza_steak_sushi.zip"
# Ship a GET request to obtain the file
response = requests.get(url)
# Outline the trail to the information listing
data_path = Path("information")
# Outline the trail to the picture listing
image_path = data_path / "pizza_steak_sushi"
# Test if the picture listing already exists
if image_path.is_dir():
print(f"{image_path} listing exists.")
else:
print(f"Didn't discover {image_path} listing, creating one...")
image_path.mkdir(dad and mom=True, exist_ok=True)
# Write the downloaded content material to a zipper file
with open(data_path / "pizza_steak_sushi.zip", "wb") as f:
f.write(response.content material)
# Extract the contents of the zip file to the picture listing
with ZipFile(data_path / "pizza_steak_sushi.zip", "r") as zipref:
zipref.extractall(image_path)
# Take away the downloaded zip file
os.take away(data_path / 'pizza_steak_sushi.zip')

  1. Ändern Sie die Größe der Bilder mit Resize() auf 224. Wir wählen die Größe des Bildes auf 224, basierend auf dem ViT-Papier
  2. In Tensor umwandeln mit ToTensor()
from torchvision.transforms import Resize, Compose, ToTensor

# Outline the train_transform utilizing Compose
train_transform = Compose([ Resize((224, 224)), ToTensor()])
# Outline the test_transform utilizing Compose
test_transform = Compose([ Resize((224, 224)), ToTensor()])

Wir können die ImageFolder DataSet-Bibliothek von PyTorch verwenden, um unsere Datensätze zu erstellen. Damit ImageFolder funktioniert, muss Ihr Datenordner folgendermaßen strukturiert sein.

information
└── pizza_steak_sushi
├── take a look at
│ ├── pizza
│ ├── steak
│ └── sushi
└── practice
├── pizza
├── steak
└── sushi

All die pizza Die Bilder befinden sich für alle Klassen, die Sie haben, im Pizza-Ordner der Unterordner „Coaching“, „Take a look at“ usw.

Es gibt zwei nützliche Methoden, die Sie für die erstellte Datei aufrufen können training_dataset Und test_dataset

  1. training_dataset.lessons das gibt ['pizza', 'steak', 'sushi']
  2. training_dataset.class_to_idx das gibt {'pizza': 0, 'steak': 1, 'sushi': 2}
from torchvision.datasets import ImageFolder
from torch.utils.information import DataLoader

BATCH_SIZE = 32

# Outline the information listing
data_dir = Path("information/pizza_steak_sushi")

# Create the coaching dataset utilizing ImageFolder
training_dataset = ImageFolder(root=data_dir / "practice", remodel=train_transform)

# Create the take a look at dataset utilizing ImageFolder
test_dataset = ImageFolder(root=data_dir / "take a look at", remodel=test_transform)

# Create the coaching dataloader utilizing DataLoader
training_dataloader = DataLoader(
dataset=training_dataset,
shuffle=True,
batch_size=BATCH_SIZE,
num_workers=2
)

# Create the take a look at dataloader utilizing DataLoader
test_dataloader = DataLoader(
dataset=test_dataset,
shuffle=False,
batch_size=BATCH_SIZE,
num_workers=2
)

Wir können einige Bilder von Trainingsdatensätzen visualisieren und ihre Beschriftungen sehen

import matplotlib.pyplot as plt
import random

num_rows = 5
num_cols = num_rows

# Create a determine with subplots
fig, axs = plt.subplots(num_rows, num_cols, figsize=(10, 10))

# Iterate over the subplots and show random photographs from the coaching dataset
for i in vary(num_rows):
for j in vary(num_cols):
# Select a random index from the coaching dataset
image_index = random.randrange(len(training_dataset))

# Show the picture within the subplot
axs[i, j].imshow(training_dataset[image_index][0].permute((1, 2, 0)))

# Set the title of the subplot because the corresponding class identify
axs[i, j].set_title(training_dataset.lessons[training_dataset[image_index][1]], shade="white")

# Disable the axis for higher visualization
axs[i, j].axis(False)

# Set the tremendous title of the determine
fig.suptitle(f"Random {num_rows * num_cols} photographs from the coaching dataset", fontsize=16, shade="white")

# Set the background shade of the determine as black
fig.set_facecolor(shade='black')

# Show the plot
plt.present()

Zufällige Trainingsdatensatzbilder

Nehmen wir uns nun etwas Zeit, um die Imaginative and prescient Transformer-Architektur zu verstehen. Dies ist der Hyperlink zum Authentic-Imaginative and prescient-Transformer-Papier: https://arxiv.org/abs/2010.11929.

Unten sehen Sie die im Bild vorgeschlagene Architektur.

Der Imaginative and prescient Transformer (ViT) ist eine Artwork Transformer-Architektur, die für Bildverarbeitungsaufgaben entwickelt wurde. Im Gegensatz zu herkömmlichen Transformern, die mit Sequenzen von Worteinbettungen arbeiten, arbeitet ViT mit Sequenzen von Bildeinbettungen. Mit anderen Worten: Es zerlegt ein Eingabebild in Patches und behandelt sie als eine Folge lernbarer Einbettungen.

Was ViT im Großen und Ganzen tut, ist Folgendes:

1. Erstellt Patch-Einbettungen

2. Leitet Einbettungen durch Transformatorblöcke:

  • Die Patch-Einbettungen werden zusammen mit dem Klassifizierungstoken durch mehrere Transformer-Blöcke geleitet.
  • Jeder Transformer-Block besteht aus einem MultiHead Self-Consideration Block (MSA Block) und einem Multi-Layer Perceptron Block (MLP Block).
  • Zwischen dem Eingang des Transformer-Blocks und dem Eingang des MSA-Blocks sowie zwischen dem Eingang des MLP-Blocks und dem Ausgang des MLP-Blocks werden Sprungverbindungen hergestellt. Diese Sprungverbindungen tragen dazu bei, das Downside des verschwindenden Gradienten zu mildern, wenn weitere Transformer-Blöcke hinzugefügt werden.

3. Führt durch Einstufung:

  • Die endgültige Ausgabe der Transformer-Blöcke wird durch einen MLP-Block geleitet.
  • Das Klassifizierungstoken, das Informationen über die Klasse des Eingabebilds enthält, wird für Vorhersagen verwendet.

Wir werden uns im Element mit jedem dieser Schritte befassen, beginnend mit dem entscheidenden Prozess der Erstellung von Patch-Einbettungen.

Für das ViT-Papier müssen wir die folgenden Funktionen für das Bild ausführen, bevor wir es an die MultiHead Self Consideration Transformer-Schicht übergeben

1. Konvertieren Sie das Bild in Patches der Größe 16 x 16.
2. Betten Sie jeden Patch in 768 Dimensionen ein. So wird jeder Patch zu einem „[1 x 768] ` Vektor. Für jedes Bild gibt es N = HxB/P² Anzahl an Patches. Dadurch entsteht ein Bild mit der Type „[14 x 14 x 768]`
3. Reduzieren Sie das Bild entlang eines einzelnen Vektors. Dies ergibt ein `[196 x 768]`Matrix, das ist unsere Bildeinbettungssequenz.
4. Stellen Sie die Klassen-Token-Einbettungen der obigen Ausgabe voran
5. Fügen Sie die Positionseinbettungen zum Klassentoken und den Bildeinbettungen hinzu.

Patch-Einbettungsanimation

In Code:

PATCH_SIZE = 16
IMAGE_WIDTH = 224
IMAGE_HEIGHT = IMAGE_WIDTH
IMAGE_CHANNELS = 3
EMBEDDING_DIMS = IMAGE_CHANNELS * PATCH_SIZE**2
NUM_OF_PATCHES = int((IMAGE_WIDTH * IMAGE_HEIGHT) / PATCH_SIZE**2)

#the picture width and picture top needs to be divisible by patch dimension. This can be a verify to see that.

assert IMAGE_WIDTH % PATCH_SIZE == 0 and IMAGE_HEIGHT % PATCH_SIZE ==0 , print("Picture Width will not be divisible by patch dimension")

Schritt 4.1 Konvertieren des Bildes in Patches von 16 x 16 und Erstellen eines Einbettungsvektors für jeden Patch der Größe 768.

Dies kann durch die Verwendung einer Conv2D-Ebene mit einer Kernelgröße gleich patch_size und einem Schritt gleich patch_size erreicht werden

CNN und Flatten werden auf das Bild angewendet
conv_layer = nn.Conv2d(in_channels = IMAGE_CHANNELS, 
out_channels = EMBEDDING_DIMS,
kernel_size = PATCH_SIZE,
stride = PATCH_SIZE)

Wir können ein zufälliges Bild an die Faltungsschicht übergeben und sehen, was passiert

random_images, random_labels = subsequent(iter(training_dataloader))
random_image = random_images[0]

# Create a brand new determine
fig = plt.determine(1)

# Show the random picture
plt.imshow(random_image.permute((1, 2, 0)))

# Disable the axis for higher visualization
plt.axis(False)

# Set the title of the picture
plt.title(training_dataset.lessons[random_labels[0]], shade="white")

# Set the background shade of the determine as black
fig.set_facecolor(shade="black")

Nehmen Sie ein zufälliges Bild aus dem Datensatz

Wir müssen die Type ändern [1, 14, 14, 768] und reduzieren Sie die Ausgabe auf [1, 196, 768]

# Go the picture by means of the convolution layer
image_through_conv = conv_layer(random_image.unsqueeze(0))
print(f'Form of embeddings by means of the conv layer -> {checklist(image_through_conv.form)} <- [batch_size, num_of_patch_rows,num_patch_cols embedding_dims]')

# Permute the size of image_through_conv to match the anticipated form
image_through_conv = image_through_conv.permute((0, 2, 3, 1))

# Create a flatten layer utilizing nn.Flatten
flatten_layer = nn.Flatten(start_dim=1, end_dim=2)

# Go the image_through_conv by means of the flatten layer
image_through_conv_and_flatten = flatten_layer(image_through_conv)

# Print the form of the embedded picture
print(f'Form of embeddings by means of the flatten layer -> {checklist(image_through_conv_and_flatten.form)} <- [batch_size, num_of_patches, embedding_dims]')

# Assign the embedded picture to a variable
embedded_image = image_through_conv_and_flatten

Form of embeddings by means of the conv layer -> [1, 768, 14, 14] <- [batch_size, num_of_patch_rows,num_patch_cols embedding_dims]
Form of embeddings by means of the flatten layer -> [1, 196, 768] <- [batch_size, num_of_patches, embedding_dims]

4.2. Voranstellen der Klassen-Token-Einbettung und Hinzufügen der Positionseinbettungen

class_token_embeddings = nn.Parameter(torch.rand((1, 1,EMBEDDING_DIMS), requires_grad  = True))
print(f'Form of class_token_embeddings --> {checklist(class_token_embeddings.form)} <-- [batch_size, 1, emdedding_dims]')

embedded_image_with_class_token_embeddings = torch.cat((class_token_embeddings, embedded_image), dim = 1)
print(f'nShape of picture embeddings with class_token_embeddings --> {checklist(embedded_image_with_class_token_embeddings.form)} <-- [batch_size, num_of_patches+1, embeddiing_dims]')

position_embeddings = nn.Parameter(torch.rand((1, NUM_OF_PATCHES+1, EMBEDDING_DIMS ), requires_grad = True ))
print(f'nShape of position_embeddings --> {checklist(position_embeddings.form)} <-- [batch_size, num_patches+1, embeddings_dims]')

final_embeddings = embedded_image_with_class_token_embeddings + position_embeddings
print(f'nShape of final_embeddings --> {checklist(final_embeddings.form)} <-- [batch_size, num_patches+1, embeddings_dims]')

Form of `class_token_embeddings` --> `[1, 1, 768]` <-- `[batch_size, 1, emdedding_dims]`

Form of `picture embeddings with class_token_embeddings` --> `[1, 197, 768]` <-- `[batch_size, num_of_patches+1, embeddiing_dims]`

Form of `position_embeddings` --> `[1, 197, 768]` <-- `[batch_size, num_patches+1, embeddings_dims]`

Form of `final_embeddings` --> `[1, 197, 768]` <-- `[batch_size, num_patches+1, embeddings_dims]`

Fügen Sie den PatchEmbeddingLayer zusammen

Wir werden von PyTorch erben nn.Module um unsere benutzerdefinierte Ebene zu erstellen, die ein Bild aufnimmt und die Patch-Einbettungen auswirft, die aus den Bildeinbettungen, Klassentoken-Einbettungen und Positionseinbettungen bestehen.

class PatchEmbeddingLayer(nn.Module):
def __init__(self, in_channels, patch_size, embedding_dim,):
tremendous().__init__()
self.patch_size = patch_size
self.embedding_dim = embedding_dim
self.in_channels = in_channels
self.conv_layer = nn.Conv2d(in_channels=in_channels, out_channels=embedding_dim, kernel_size=patch_size, stride=patch_size)
self.flatten_layer = nn.Flatten(start_dim=1, end_dim=2)
self.class_token_embeddings = nn.Parameter(torch.rand((BATCH_SIZE, 1, EMBEDDING_DIMS), requires_grad=True))
self.position_embeddings = nn.Parameter(torch.rand((1, NUM_OF_PATCHES + 1, EMBEDDING_DIMS), requires_grad=True))

def ahead(self, x):
output = torch.cat((self.class_token_embeddings, self.flatten_layer(self.conv_layer(x).permute((0, 2, 3, 1)))), dim=1) + self.position_embeddings
return output

patch_embedding_layer = PatchEmbeddingLayer(in_channels=IMAGE_CHANNELS, patch_size=PATCH_SIZE, embedding_dim=IMAGE_CHANNELS * PATCH_SIZE ** 2)

patch_embeddings = patch_embedding_layer(random_images)
patch_embeddings.form

    torch.Measurement([32, 197, 768])

Hier haben wir einen Stapel zufälliger Bilder durch die Patch-Einbettungsebenen geleitet und wie erwartet eine Ausgabe der Type (32, 197, 768) erhalten. Jetzt können wir auch die Zusammenfassung der von uns erstellten Ebene sehen.

Zusammenfassung der Patch-Einbettungsebene

MSA-Block verstehen

Als ersten Schritt beim Zusammenstellen unseres Transformatorblocks für das Imaginative and prescient Transformer-Modell erstellen wir einen MultiHead-Selbstaufmerksamkeitsblock.

Nehmen wir uns einen Second Zeit, um den MSA-Block zu verstehen. Der MSA-Block selbst besteht aus einer LayerNorm-Schicht und der Multi-Head-Consideration-Schicht. Die Layernorm-Ebene normalisiert im Wesentlichen unsere Patch-Einbettungsdaten über die gesamte Einbettungsdimension. Die Multi-Head Consideration-Schicht nimmt die Eingabedaten nämlich als drei Formen lernbarer Vektoren auf Anfrage, Style, Und Wertzusammen bekannt als qkv Vektoren. Diese Vektoren bilden zusammen die Beziehung zwischen jedem Patch der Eingabesequenz und jedem anderen Patch in derselben Sequenz (daher der Identify **Selbstaufmerksamkeit**).

Unsere Eingabeform für den MSA-Block ist additionally die Type unserer Patch-Einbettungen, die wir mit PatchEmbeddingLayer -> ` erstellt haben[batch_size, sequence_length, embedding_dimensions]`. Und die Ausgabe der MSA-Ebene hat dieselbe Type wie die Eingabe.

MSA-Blockcode

Beginnen wir nun mit dem Schreiben von Code für unseren MSA-Block. Dies wird kurz sein, da PyTorch über eine vorgefertigte Implementierung von verfügt LayerNorm und das MultiHeadAttention-Ebene. Wir müssen nur die richtigen Argumente liefern, die zu unserer Architektur passen. Die verschiedenen Parameter, die für unseren MSA-Block erforderlich sind, finden Sie in dieser Tabelle aus dem Authentic-ViT-Papier.

Tabelle aus dem Authentic-ViT-Papier. Wir werden hauptsächlich am ersten Modell arbeiten.
class MultiHeadSelfAttentionBlock(nn.Module):
def __init__(self,
embedding_dims = 768, # Hidden Measurement D within the ViT Paper Desk 1
num_heads = 12, # Heads within the ViT Paper Desk 1
attn_dropout = 0.0 # Default to Zero as there is no such thing as a dropout for the the MSA Block as per the ViT Paper
):
tremendous().__init__()

self.embedding_dims = embedding_dims
self.num_head = num_heads
self.attn_dropout = attn_dropout

self.layernorm = nn.LayerNorm(normalized_shape = embedding_dims)

self.multiheadattention = nn.MultiheadAttention(num_heads = num_heads,
embed_dim = embedding_dims,
dropout = attn_dropout,
batch_first = True,
)

def ahead(self, x):
x = self.layernorm(x)
output,_ = self.multiheadattention(question=x, key=x, worth=x,need_weights=False)
return output

Testen wir unseren MSA Block

multihead_self_attention_block = MultiHeadSelfAttentionBlock(embedding_dims = EMBEDDING_DIMS,
num_heads = 12
)
print(f'Form of the enter Patch Embeddings => {checklist(patch_embeddings.form)} <= [batch_size, num_patches+1, embedding_dims ]')
print(f'Form of the output from MSA Block => {checklist(multihead_self_attention_block(patch_embeddings).form)} <= [batch_size, num_patches+1, embedding_dims ]')
Form of the enter Patch Embeddings => [32, 197, 768] <= [batch_size, num_patches+1, embedding_dims ]
Form of the output from MSA Block => [32, 197, 768] <= [batch_size, num_patches+1, embedding_dims ]

Schön, additionally scheint unser MSA-Block zu funktionieren. Weitere Informationen zum MSA-Block erhalten wir über Torchinfo.

abstract(mannequin=multihead_self_attention_block,
input_size=(1, 197, 768), # (batch_size, num_patches, embedding_dimension)
col_names=["input_size", "output_size", "num_params", "trainable"],
col_width=20,
row_settings=["var_names"])
Zusammenfassung des MSA-Blocks

Den MLP-Block verstehen

Der Machine Studying Perceptron (MLP)-Block im Transformator ist eine Kombination aus einer vollständig verbundenen Schicht (auch als lineare Schicht oder dichte Schicht bezeichnet) und einer nichtlinearen Schicht. Im Fall von ViT ist die nichtlineare Schicht eine GeLU-Schicht.

Der Transformator implementiert außerdem eine Dropout-Schicht, um eine Überanpassung zu reduzieren. Der MLP-Block sieht additionally in etwa so aus:

Eingabe → Linear → GeLU → Dropout → Linear → Dropout

Dem Papier zufolge skaliert die erste lineare Ebene die Einbettungsabmessungen auf die 3072-Abmessungen (für den ViT-16/Base). Der Dropout ist auf 0,1 eingestellt und die zweite lineare Ebene verkleinert die Abmessungen wieder auf die Einbettungsabmessungen.

MLP-Blockcode

Lassen Sie uns nun unseren MLP-Block zusammenbauen. Dem ViT-Papier zufolge wird die Ausgabe des MSA-Blocks, die zur Eingabe des MSA-Blocks hinzugefügt wird (gekennzeichnet durch die Sprung-/Restverbindung in der Abbildung der Modellarchitektur), als Eingabe an den MLP-Block weitergeleitet. Alle Ebenen werden von der PyTorch-Bibliothek bereitgestellt. Wir müssen es nur zusammenbauen.

class MachineLearningPerceptronBlock(nn.Module):
def __init__(self, embedding_dims, mlp_size, mlp_dropout):
tremendous().__init__()
self.embedding_dims = embedding_dims
self.mlp_size = mlp_size
self.dropout = mlp_dropout

self.layernorm = nn.LayerNorm(normalized_shape = embedding_dims)
self.mlp = nn.Sequential(
nn.Linear(in_features = embedding_dims, out_features = mlp_size),
nn.GELU(),
nn.Dropout(p = mlp_dropout),
nn.Linear(in_features = mlp_size, out_features = embedding_dims),
nn.Dropout(p = mlp_dropout)
)

def ahead(self, x):
return self.mlp(self.layernorm(x))

Testen wir unseren MLP-Block

mlp_block = MachineLearningPerceptronBlock(embedding_dims = EMBEDDING_DIMS,
mlp_size = 3072,
mlp_dropout = 0.1)

abstract(mannequin=mlp_block,
input_size=(1, 197, 768), # (batch_size, num_patches, embedding_dimension)
col_names=["input_size", "output_size", "num_params", "trainable"],
col_width=20,
row_settings=["var_names"])

Zusammenfassung des MLP-Blocks

Erstaunlich, es sieht so aus, als ob der MLP-Block auch wie erwartet funktioniert.

class TransformerBlock(nn.Module):
def __init__(self, embedding_dims = 768,
mlp_dropout=0.1,
attn_dropout=0.0,
mlp_size = 3072,
num_heads = 12,
):
tremendous().__init__()

self.msa_block = MultiHeadSelfAttentionBlock(embedding_dims = embedding_dims,
num_heads = num_heads,
attn_dropout = attn_dropout)

self.mlp_block = MachineLearningPerceptronBlock(embedding_dims = embedding_dims,
mlp_size = mlp_size,
mlp_dropout = mlp_dropout,
)

def ahead(self,x):
x = self.msa_block(x) + x
x = self.mlp_block(x) + x

return x

Testen des Transformatorblocks

transformer_block = TransformerBlock(embedding_dims = EMBEDDING_DIMS,
mlp_dropout = 0.1,
attn_dropout=0.0,
mlp_size = 3072,
num_heads = 12)

print(f'Form of the enter Patch Embeddings => {checklist(patch_embeddings.form)} <= [batch_size, num_patches+1, embedding_dims ]')
print(f'Form of the output from Transformer Block => {checklist(transformer_block(patch_embeddings).form)} <= [batch_size, num_patches+1, embedding_dims ]')

Form of the enter Patch Embeddings => [32, 197, 768] <= [batch_size, num_patches+1, embedding_dims ]
Form of the output from Transformer Block => [32, 197, 768] <= [batch_size, num_patches+1, embedding_dims ]
Zusammenfassung des Transformatorblocks

Lassen Sie uns abschließend unser ViT-Modell zusammenstellen. Es wird so einfach sein wie das Kombinieren dessen, was wir bisher gemacht haben. Eine kleine Ergänzung wird die Klassifizierungsebene sein, die wir hinzufügen werden. In ViT ist die Klassifikatorebene eine einfache lineare Ebene mit Ebenennormalisierung. Die Klassifizierung erfolgt anhand des nullten Indexes des Ausgangs des Transformators.

class ViT(nn.Module):
def __init__(self, img_size = 224,
in_channels = 3,
patch_size = 16,
embedding_dims = 768,
num_transformer_layers = 12, # from desk 1 above
mlp_dropout = 0.1,
attn_dropout = 0.0,
mlp_size = 3072,
num_heads = 12,
num_classes = 1000):
tremendous().__init__()

self.patch_embedding_layer = PatchEmbeddingLayer(in_channels = in_channels,
patch_size=patch_size,
embedding_dim = embedding_dims)

self.transformer_encoder = nn.Sequential(*[TransformerBlock(embedding_dims = embedding_dims,
mlp_dropout = mlp_dropout,
attn_dropout = attn_dropout,
mlp_size = mlp_size,
num_heads = num_heads) for _ in range(num_transformer_layers)])

self.classifier = nn.Sequential(nn.LayerNorm(normalized_shape = embedding_dims),
nn.Linear(in_features = embedding_dims,
out_features = num_classes))

def ahead(self, x):
return self.classifier(self.transformer_encoder(self.patch_embedding_layer(x))[:, 0])

abstract(mannequin=vit,
input_size=(BATCH_SIZE, 3, 224, 224), # (batch_size, num_patches, embedding_dimension)
col_names=["input_size", "output_size", "num_params", "trainable"],
col_width=20,
row_settings=["var_names"])
Zusammenfassung des ViT-Modells

Das ist es. Jetzt können Sie dieses Modell wie jedes andere Modell in PyTorch trainieren. Lassen Sie mich wissen, wie es für Sie funktioniert. Ich hoffe, diese Schritt-für-Schritt-Anleitung hat Ihnen geholfen, den Imaginative and prescient Transformer zu verstehen und Sie dazu inspiriert, tiefer in die Welt der Laptop Imaginative and prescient und Transformer-Modelle einzutauchen. Mit dem gewonnenen Wissen sind Sie bestens gerüstet, um die Grenzen des Laptop Imaginative and prescient zu erweitern und das Potenzial dieser bahnbrechenden Architekturen auszuschöpfen.

Additionally machen Sie weiter, bauen Sie auf dem auf, was Sie gelernt haben, und lassen Sie Ihrer Fantasie freien Lauf, während Sie den Imaginative and prescient Transformer nutzen, um spannende visuelle Herausforderungen zu meistern. Viel Spaß beim Codieren!

Colab-Notizbuch: https://colab.research.google.com/drive/1JdmFNGCqCIcmN3jvfs_gFd1cCwsFQr4k?usp=sharing



Source link

HINTERLASSEN SIE EINE ANTWORT

Please enter your comment!
Please enter your name here