PyTorch Transformer-Modell erstellen
Transformer ist eines der leistungsstärksten Modelle im modernen maschinellen Lernen.
Das Transformer-Modell ist eine Deep-Learning-Architektur, die auf dem Self-Attention-Mechanismus basiert. Es hat den Bereich der Verarbeitung natürlicher Sprache (NLP) revolutioniert und ist zur Grundlage moderner Deep-Learning-Modelle wie BERT, GPT usw. geworden.
Transformer ist eine Kernarchitektur im modernen NLP. Mit seiner leistungsstarken Modellierungsfähigkeit für weitreichende Abhängigkeiten und effizienten Vorteilen bei der parallelen Berechnung übertrifft es traditionelle Long Short-Term Memory (LSTM)-Netzwerke bei Aufgaben wie Sprachübersetzung und Textzusammenfassung.
Wenn du Transformer noch nicht verstehst, kannst du nachlesen:Einführung in das Transformer-Modell。
Erstellen eines Transformer-Modells mit PyTorch
Die Schritte zum Erstellen eines Transformer-Modells sind wie folgt:
1. Importiere die notwendigen Bibliotheken und Module
Importiere die PyTorch-Kernbibliothek, das Modul für neuronale Netzwerke, das Optimierer-Modul, Datenverarbeitungswerkzeuge sowie Mathematik- und Objektkopiermodule, um Unterstützung für die Definition der Modellarchitektur, die Verwaltung von Daten und den Trainingsprozess zu bieten.
import torch import torch.nn as nn import torch.optim as optim import torch.utils.data as data import math import copy
Beschreibung:
torch: PyTorchs Kernbibliothek, verwendet für Tensoroperationen und automatische Differenzierung.torch.nn: PyTorchs Modul für neuronale Netzwerke, das verschiedene Schichten und Verlustfunktionen enthält.torch.optim: Optimierungsalgorithmus-Modul, wie Adam, SGD usw.math: Mathematikfunktionsbibliothek, verwendet zur Berechnung von Quadratwurzeln usw.copy: Wird zum tiefen Kopieren von Objekten verwendet.
Grundlegende Bausteine definieren: Multi-Head-Attention, positionsweises Feed-Forward-Netzwerk, Positionskodierung
Multi-Head-AttentionDurch die Berechnung der Beziehung zwischen jedem Positionspaar in der Sequenz über mehrere "Attention-Heads" kann sie verschiedene Merkmale und Muster der Eingabesequenz erfassen.
Die Klasse MultiHeadAttention kapselt den in Transformer-Modellen üblicherweise verwendeten Multi-Head-Attention-Mechanismus. Sie ist dafür verantwortlich, die Eingabe in mehrere Attention-Heads aufzuteilen, auf jeden Head Attention anzuwenden und dann die Ergebnisse zu kombinieren, sodass das Modell verschiedene Beziehungen in den Eingabedaten auf unterschiedlichen Skalen erfassen und seine Ausdruckskraft verbessern kann.
Beispiel
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0, "d_model muss durch num_heads teilbar sein"
self.d_model = d_model # Modell-Dimension (z. B. 512)
self.num_heads = num_heads # Anzahl der Attention-Heads (z. B. 8)
self.d_k = d_model // num_heads # Dimension jedes Heads (z. B. 64)
# Lineare Transformationsschichten definieren (ohne Bias)
self.W_q = nn.Linear(d_model, d_model) # Query-Transformation
self.W_k = nn.Linear(d_model, d_model) # Key-Transformation
self.W_v = nn.Linear(d_model, d_model) # Value-Transformation
self.W_o = nn.Linear(d_model, d_model) # Output-Transformation
def scaled_dot_product_attention(self, Q, K, V, mask=Keine):
"""
Skalierte Skalarprodukt-Attention berechnen
Eingabeform:
Q: (batch_size, num_heads, seq_length, d_k)
K, V: wie Q
Ausgabeform: (batch_size, num_heads, seq_length, d_k)
"""
# Attention-Scores berechnen (Skalarprodukt von Q und K)
attn_scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
# Maske anwenden (z. B. Padding-Maske oder Maske für zukünftige Informationen)
if mask is not None:
attn_scores = attn_scores.masked_fill(mask == 0, -1e9)
# Attention-Gewichte berechnen (Softmax-Normalisierung)
attn_probs = torch.softmax(attn_scores, dim=-1)
# Gewichtete Summe der Value-Vektoren
output = torch.matmul(attn_probs, V)
return output
def split_heads(self, x):
"""
Teile den Eingabe-Tensor in mehrere Heads auf
Eingabeform: (batch_size, seq_length, d_model)
Ausgabeform: (batch_size, num_heads, seq_length, d_k)
"""
batch_size, seq_length, d_model = x.size()
return x.view(batch_size, seq_length, self.num_heads, self.d_k).transpose(1, 2)
def combine_heads(self, x):
"""
Führe die Ausgaben mehrerer Heads wieder zur ursprünglichen Form zusammen
Eingabeform: (batch_size, num_heads, seq_length, d_k)
Ausgabeform: (batch_size, seq_length, d_model)
"""
batch_size, _, seq_length, d_k = x.size()
return x.transpose(1, 2).contiguous().view(batch_size, seq_length, self.d_model)
def forward(self, Q, K, V, mask=Keine):
"""
Vorwärtspropagierung
Eingabeform: Q/K/V: (batch_size, seq_length, d_model)
Ausgabeform: (batch_size, seq_length, d_model)
"""
# Lineare Transformation und Aufteilung in mehrere Köpfe
Q = self.split_heads(self.W_q(Q)) # (batch, heads, seq_len, d_k)
K = self.split_heads(self.W_k(K))
V = self.split_heads(self.W_v(V))
# Attention berechnen
attn_output = self.scaled_dot_product_attention(Q, K, V, mask)
# Köpfe zusammenführen und Ausgabetransformation anwenden
output = self.W_o(self.combine_heads(attn_output))
return output
Beschreibung:
Mehrköpfiger Aufmerksamkeitsmechanismus: Teile die Eingabe in mehrere Köpfe auf, jeder Kopf berechnet die Aufmerksamkeit unabhängig, und führe schließlich die Ergebnisse zusammen.
Skalierte Skalarprodukt-Aufmerksamkeit: Berechne das Skalarprodukt von Abfragen und Schlüsseln, skaliere es, verwende Softmax, um Aufmerksamkeitsgewichte zu berechnen, und bilde schließlich eine gewichtete Summe der Werte.
Maske: Wird verwendet, um ungültige Positionen zu maskieren (z. B. aufgefüllte Teile).
Positionsweises Feed-Forward-Netzwerk
Beispiel
def __init__(self, d_model, d_ff):
super(PositionWiseFeedForward, self).__init__()
self.fc1 = nn.Linear(d_model, d_ff) # Erste vollständig verbundene Schicht
self.fc2 = nn.Linear(d_ff, d_model) # Zweite vollständig verbundene Schicht
self.relu = nn.ReLU() # Aktivierungsfunktion
def forward(self, x):
# Berechnung des Feed-Forward-Netzwerks
return self.fc2(self.relu(self.fc1(x)))
Feed-Forward-Netzwerk:Es besteht aus zwei vollständig verbundenen Schichten und einer ReLU-Aktivierungsfunktion, die verwendet wird, um die Ausgabe des Aufmerksamkeitsmechanismus weiterzuverarbeiten.
Positionale Codierung
Positionskodierung wird verwendet, um Positionsinformationen jedes Tokens in der Eingabesequenz einzufügen.
Verwenden Sie Sinus- und Kosinusfunktionen unterschiedlicher Frequenzen, um Positionskodierungen zu erzeugen.
Beispiel
def __init__(self, d_model, max_seq_length):
super(PositionalEncoding, self).__init__()
pe = torch.zeros(max_seq_length, d_model) # Initialisiere die Positionskodierungsmatrix
position = torch.arange(0, max_seq_length, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term) # Sinusfunktion für gerade Positionen verwenden
pe[:, 1::2] = torch.cos(position * div_term) # Kosinusfunktion für ungerade Positionen verwenden
self.register_buffer('pe', pe.unsqueeze(0)) # Als Puffer registrieren
def forward(self, x):
# Positionskodierung zur Eingabe hinzufügen
return x + self.pe[:, :x.size(1)]
Encoder-Block erstellen (Encoder-Schicht)

Encoder-Schicht:Enthält einen Self-Attention-Mechanismus und ein Feed-Forward-Netzwerk, wobei auf jede Unterschicht Residualverbindungen und Layer-Normalisierung folgen.
Beispiel
def __init__(self, d_model, num_heads, d_ff, dropout):
super(EncoderLayer, self).__init__()
self.self_attn = MultiHeadAttention(d_model, num_heads) # Self-Attention-Mechanismus
self.feed_forward = PositionWiseFeedForward(d_model, d_ff) # Feed-Forward-Netzwerk
self.norm1 = nn.LayerNorm(d_model) # Layer-Normalisierung
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout) # Dropout
def forward(self, x, mask):
# Self-Attention-Mechanismus
attn_output = self.self_attn(x, x, x, mask)
x = self.norm1(x + self.dropout(attn_output)) # Residualverbindung und Layer-Normalisierung
# Feed-Forward-Netzwerk
ff_output = self.feed_forward(x)
x = self.norm2(x + self.dropout(ff_output)) # Restverbindung und Layer-Normalisierung
return x
Decoder-Modul erstellen

Decoder-Schicht:Enthält einen Self-Attention-Mechanismus, einen Cross-Attention-Mechanismus und ein Feed-Forward-Netzwerk, wobei auf jede Unterschicht Restverbindungen und Layer-Normalisierung folgen.
Beispiel
def __init__(self, d_model, num_heads, d_ff, dropout):
super(DecoderLayer, self).__init__()
self.self_attn = MultiHeadAttention(d_model, num_heads) # Self-Attention-Mechanismus
self.cross_attn = MultiHeadAttention(d_model, num_heads) # Cross-Attention-Mechanismus
self.feed_forward = PositionWiseFeedForward(d_model, d_ff) # Feed-Forward-Netzwerk
self.norm1 = nn.LayerNorm(d_model) # Layer-Normalisierung
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout) # Dropout
def forward(self, x, enc_output, src_mask, tgt_mask):
# Self-Attention-Mechanismus
attn_output = self.self_attn(x, x, x, tgt_mask)
x = self.norm1(x + self.dropout(attn_output)) # Residualverbindung und Layer-Normalisierung
# Cross-Attention-Mechanismus
attn_output = self.cross_attn(x, enc_output, enc_output, src_mask)
x = self.norm2(x + self.dropout(attn_output)) # Residualverbindung und Layer-Normalisierung
# Feed-Forward-Netzwerk
ff_output = self.feed_forward(x)
x = self.norm3(x + self.dropout(ff_output)) # Residualverbindung und Layer-Normalisierung
return x
Erstelle das vollständige Transformer-Modell

Beispiel
def __init__(self, src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_seq_length, dropout):
super(Transformer, self).__init__()
self.encoder_embedding = nn.Embedding(src_vocab_size, d_model) # Encoder-Worteinbettung
self.decoder_embedding = nn.Embedding(tgt_vocab_size, d_model) # Decoder-Worteinbettung
self.positional_encoding = PositionalEncoding(d_model, max_seq_length) # Positionskodierung
# Encoder- und Decoder-Schichten
self.encoder_layers = nn.ModuleList([EncoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)])
self.decoder_layers = nn.ModuleList([DecoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)])
self.fc = nn.Linear(d_model, tgt_vocab_size) # Abschließende vollständig verbundene Schicht
self.dropout = nn.Dropout(dropout) # Dropout
def generate_mask(self, src, tgt):
# Quellmaske: Padding-Tokens maskieren (Padding-Index ist 0)
# Form: (batch_size, 1, 1, seq_length)
src_mask = (src != 0).unsqueeze(1).unsqueeze(2)
# Zielmaske: Padding-Tokens und zukünftige Informationen maskieren
# Form: (batch_size, 1, seq_length, 1)
tgt_mask = (tgt != 0).unsqueeze(1).unsqueeze(3)
seq_length = tgt.size(1)
# Erzeuge eine obere Dreiecksmatrix-Maske, um zu verhindern, dass während der Dekodierung zukünftige Informationen gesehen werden
nopeak_mask = (1 - torch.triu(torch.ones(1, seq_length, seq_length), diagonal=1)).bool()
tgt_mask = tgt_mask & nopeak_mask # Kombiniere die Padding-Maske und die Maske für zukünftige Informationen
return src_mask, tgt_mask
def forward(self, src, tgt):
# Masken generieren
src_mask, tgt_mask = self.generate_mask(src, tgt)
# Encoder-Teil
src_embedded = self.dropout(self.positional_encoding(self.encoder_embedding(src)))
enc_output = src_embedded
for enc_layer in self.encoder_layers:
enc_output = enc_layer(enc_output, src_mask)
# Decoder-Teil
tgt_embedded = self.dropout(self.positional_encoding(self.decoder_embedding(tgt)))
dec_output = tgt_embedded
for dec_layer in self.decoder_layers:
dec_output = dec_layer(dec_output, enc_output, src_mask, tgt_mask)
# Endausgabe
output = self.fc(dec_output)
return output
Beschreibung:
Transformer-ModellEnthält Encoder- und Decoder-Teile, die jeweils aus mehreren gestapelten Schichten zusammengesetzt sind.
MaskengenerierungWird verwendet, um ungültige Positionen und zukünftige Informationen zu maskieren.
VorwärtspropagationDurchläuft nacheinander den Encoder und Decoder und gibt schließlich über eine vollständig verbundene Schicht aus.
Beschreibung der Modellinitialisierungsparameter:
class Transformer(nn.Module):
def __init__(
self,
src_vocab_size, # 源语言词汇表大小(如英文单词数)
tgt_vocab_size, # 目标语言词汇表大小(如中文单词数)
d_model=512, # 模型维度(每个词向量的长度)
num_heads=8, # 多头注意力的头数
num_layers=6, # 编码器/解码器的堆叠层数
d_ff=2048, # 前馈网络隐藏层维度
max_seq_length=100, # 最大序列长度(用于位置编码)
dropout=0.1 # Dropout概率
):
Das PyTorch-Transformer-Modell trainieren
Zufallsdaten zum Trainieren des Modells verwenden, den Verlust berechnen und Parameter aktualisieren.
Beispiel
src_vocab_size = 5000 # Größe des Quellvokabulars
tgt_vocab_size = 5000 # Größe des Zielvokabulars
d_model = 512 # Modell-Dimension
num_heads = 8 # Anzahl der Attention-Heads
num_layers = 6 # Anzahl der Encoder- und Decoder-Schichten
d_ff = 2048 # Innere Dimension des Feed-Forward-Netzwerks
max_seq_length = 100 # Maximale Sequenzlänge
dropout = 0.1 # Dropout-Wahrscheinlichkeit
# Modell initialisieren
transformer = Transformer(src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff, max_seq_length, dropout)
# Zufällige Daten generieren
src_data = torch.randint(1, src_vocab_size, (64, max_seq_length)) # Quellsequenz
tgt_data = torch.randint(1, tgt_vocab_size, (64, max_seq_length)) # Zielsequenz
# Verlustfunktion und Optimierer definieren
criterion = nn.CrossEntropyLoss(ignore_index=0) # Ignoriere den Verlust des Padding-Teils
optimizer = optim.Adam(transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)
# Trainingsschleife
transformer.train()
for epoch in range(100):
optimizer.zero_grad() # Gradienten zurücksetzen, um Akkumulation zu verhindern
# Entferne das letzte Wort bei der Eingabe der Zielsequenz (wird verwendet, um das nächste Wort vorherzusagen)
output = transformer(src_data, tgt_data[:, :-1])
# Bei der Berechnung des Verlusts beginnt die Zielsequenz beim zweiten Wort (d. h. das nächste Wort vorhersagen)
# Ausgabeform: (batch_size, seq_length-1, tgt_vocab_size)
# Zielform: (batch_size, seq_length-1)
loss = criterion(
output.contiguous().view(-1, tgt_vocab_size),
tgt_data[:, 1:].contiguous().view(-1)
)
loss.backward() # Backpropagation
optimizer.step() # Parameter aktualisieren
print(f"Epoche: {epoch+1}, Verlust: {loss.item()}")
Modellbewertung
Evaluierungsprozess:Berechnen Sie den Verlust auf den Validierungsdaten, um die Modellleistung zu bewerten.
Beispiel
# Validierungsdaten generieren
val_src_data = torch.randint(1, src_vocab_size, (64, max_seq_length))
val_tgt_data = torch.randint(1, tgt_vocab_size, (64, max_seq_length))
# Angenommen, die Eingabe ist ein Batch englischer Sätze und ihrer entsprechenden chinesischen Übersetzungen (in Indizes umgewandelt)
# Beispieldaten:
# src_data: [[3, 14, 25, ..., 0, 0], ...] # Englische Sätze (0 ist das Padding-Token)
# tgt_data: [[5, 20, 36, ..., 0, 0], ...] # Chinesische Übersetzungen (0 ist das Padding-Token)
# Hinweis: In praktischen Anwendungen muss Text vorverarbeitet werden, z. B. Tokenisierung, Kodierung und Padding
with torch.no_grad():
val_output = transformer(val_src_data, val_tgt_data[:, :-1])
val_loss = criterion(val_output.contiguous().view(-1, tgt_vocab_size), val_tgt_data[:, 1:].contiguous().Ansicht(-1))
print(f"Validierungsverlust: {val_loss.item()}")