single
Challenge: Building a Transformer Text Classifier
Swipe um das Menü anzuzeigen
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100import numpy as np # Simple tokenizer: splits text into lowercase words def tokenize(text): return text.lower().split() # Build vocabulary from training texts def build_vocab(texts): vocab = {} idx = 1 # 0 reserved for padding for text in texts: for word in tokenize(text): if word not in vocab: vocab[word] = idx idx += 1 return vocab # Convert text to sequence of word indices def text_to_sequence(text, vocab, max_len): seq = [vocab.get(word, 0) for word in tokenize(text)] if len(seq) < max_len: seq += [0] * (max_len - len(seq)) else: seq = seq[:max_len] return np.array(seq) # Sinusoidal positional encoding def positional_encoding(seq_len, d_model): pos = np.arange(seq_len)[:, np.newaxis] i = np.arange(d_model)[np.newaxis, :] angle_rates = 1 / np.power(10000, (2 * (i // 2)) / np.float32(d_model)) angle_rads = pos * angle_rates pe = np.zeros((seq_len, d_model)) pe[:, 0::2] = np.sin(angle_rads[:, 0::2]) pe[:, 1::2] = np.cos(angle_rads[:, 1::2]) return pe # Simple self-attention layer def self_attention(X): d_k = X.shape[-1] Q = X K = X V = X scores = Q @ K.T / np.sqrt(d_k) weights = softmax(scores) return weights @ V def softmax(x): e_x = np.exp(x - np.max(x, axis=-1, keepdims=True)) return e_x / np.sum(e_x, axis=-1, keepdims=True) # Feed-forward layer def feed_forward(X, W1, b1, W2, b2): return np.maximum(0, X @ W1 + b1) @ W2 + b2 # Minimal Transformer encoder block def transformer_encoder(X, pe, W1, b1, W2, b2): X_pe = X + pe attn_out = self_attention(X_pe) ff_out = feed_forward(attn_out, W1, b1, W2, b2) return ff_out # Classifier class TransformerClassifier: def __init__(self, vocab_size, d_model, max_len, num_classes): self.d_model = d_model self.max_len = max_len self.embeddings = np.random.randn(vocab_size + 1, d_model) * 0.01 self.W1 = np.random.randn(d_model, d_model) * 0.01 self.b1 = np.zeros(d_model) self.W2 = np.random.randn(d_model, d_model) * 0.01 self.b2 = np.zeros(d_model) self.Wc = np.random.randn(d_model, num_classes) * 0.01 self.bc = np.zeros(num_classes) self.pe = positional_encoding(max_len, d_model) def forward(self, x_seq): X = self.embeddings[x_seq] encoded = transformer_encoder(X, self.pe, self.W1, self.b1, self.W2, self.b2) pooled = np.mean(encoded, axis=0) # Simple mean pooling logits = pooled @ self.Wc + self.bc probs = softmax(logits) return probs # Example usage: texts = ["I love machine learning", "Transformers are powerful", "Deep learning is cool"] labels = [0, 1, 0] # 2 classes: 0, 1 vocab = build_vocab(texts) max_len = 6 d_model = 8 num_classes = 2 clf = TransformerClassifier(len(vocab), d_model, max_len, num_classes) # Predict class probabilities for a new text test_text = "I love transformers" test_seq = text_to_sequence(test_text, vocab, max_len) probs = clf.forward(test_seq) print("Predicted class probabilities:", probs)
Hier ist eine minimale Implementierung eines auf Transformern basierenden Textklassifikators, der ausschließlich numpy und grundlegende Python-Funktionen verwendet. Dieser Code zeigt, wie die wesentlichen Bestandteile eines Transformers für eine Textklassifizierungsaufgabe ohne Deep-Learning-Bibliotheken zusammengesetzt werden können.
Der Prozess beginnt damit, Rohtext in ein für die Berechnung geeignetes Format umzuwandeln. Die Funktion tokenize zerlegt den Text in Kleinbuchstaben-Wörter:
def tokenize(text):
return text.lower().split()
Anschließend erstellt build_vocab eine Abbildung von jedem einzigartigen Wort auf eine eindeutige Ganzzahl:
def build_vocab(texts):
vocab = {}
idx = 1 # 0 reserved for padding
for text in texts:
for word in tokenize(text):
if word not in vocab:
vocab[word] = idx
idx += 1
return vocab
Die Funktion text_to_sequence wandelt jeden Eingabesatz in eine Sequenz von Wortindizes fester Länge um, wobei bei Bedarf aufgefüllt oder abgeschnitten wird:
def text_to_sequence(text, vocab, max_len):
seq = [vocab.get(word, 0) for word in tokenize(text)]
if len(seq) < max_len:
seq += [0] * (max_len - len(seq))
else:
seq = seq[:max_len]
return seq
Um dem Modell die Reihenfolge der Wörter zu vermitteln, erzeugt positional_encoding eine Matrix aus sinusförmigen Werten, die zu den Wort-Embeddings addiert werden kann. Dieser Schritt ist entscheidend, da Transformer im Gegensatz zu rekurrenten Netzen Sequenzen nicht von Natur aus in Reihenfolge verarbeiten:
def positional_encoding(seq_len, d_model):
pos = np.arange(seq_len)[:, np.newaxis]
i = np.arange(d_model)[np.newaxis, :]
angle_rates = 1 / np.power(10000, (2 * (i // 2)) / np.float32(d_model))
angle_rads = pos * angle_rates
pe = np.zeros((seq_len, d_model))
pe[:, 0::2] = np.sin(angle_rads[:, 0::2])
pe[:, 1::2] = np.cos(angle_rads[:, 1::2])
return pe
Der Self-Attention-Mechanismus, implementiert in self_attention, ermöglicht es dem Modell, die Bedeutung jedes Wortes im Kontext der gesamten Sequenz zu gewichten. Dies geschieht, indem Ähnlichkeitswerte zwischen allen Wortpaaren berechnet, die softmax-Funktion angewendet und anschließend für jedes Wort eine neue Repräsentation als gewichtete Summe aller Wörter der Sequenz erstellt wird:
def self_attention(X):
d_k = X.shape[-1]
Q = X
K = X
V = X
scores = Q @ K.T / np.sqrt(d_k)
weights = softmax(scores)
return weights @ V
def softmax(x):
e_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
return e_x / np.sum(e_x, axis=-1, keepdims=True)
Nach der Self-Attention verarbeitet ein einfaches Feed-Forward-Netzwerk (feed_forward) die Sequenz weiter:
def feed_forward(X, W1, b1, W2, b2):
return np.maximum(0, X @ W1 + b1) @ W2 + b2
Die Funktion transformer_encoder kombiniert diese Schritte, indem sie Positional Encoding, Self-Attention und das Feed-Forward-Netzwerk nacheinander anwendet:
def transformer_encoder(X, pe, W1, b1, W2, b2):
X_pe = X + pe
attn_out = self_attention(X_pe)
ff_out = feed_forward(attn_out, W1, b1, W2, b2)
return ff_out
Die Klasse TransformerClassifier fasst alles zusammen. Sie initialisiert zufällige Wort-Embeddings, Layer-Gewichte und Positional Encodings. In der Methode forward werden die Embeddings für jedes Token nachgeschlagen, der Encoder-Block angewendet, die Ausgaben über die Sequenz gemittelt (Pooling) und das Ergebnis durch eine finale lineare Schicht geleitet, um Klassenwahrscheinlichkeiten zu erzeugen:
class TransformerClassifier:
def __init__(self, vocab_size, d_model, max_len, num_classes):
self.d_model = d_model
self.max_len = max_len
self.embeddings = np.random.randn(vocab_size + 1, d_model) * 0.01
self.W1 = np.random.randn(d_model, d_model) * 0.01
self.b1 = np.zeros(d_model)
self.W2 = np.random.randn(d_model, d_model) * 0.01
self.b2 = np.zeros(d_model)
self.Wc = np.random.randn(d_model, num_classes) * 0.01
self.bc = np.zeros(num_classes)
self.pe = positional_encoding(max_len, d_model)
def forward(self, x_seq):
X = self.embeddings[x_seq]
encoded = transformer_encoder(X, self.pe, self.W1, self.b1, self.W2, self.b2)
pooled = np.mean(encoded, axis=0) # Simple mean pooling
logits = pooled @ self.Wc + self.bc
probs = softmax(logits)
return probs
Am Ende des Codes ist ein Beispiel zu sehen, bei dem ein kleiner Satz von Trainings-Texten verwendet wird, um das Vokabular zu erstellen und den Klassifikator einzurichten. Wenn ein neuer Satz eingegeben wird, sagt das Modell die Klassenwahrscheinlichkeiten voraus und zeigt so, wie die minimale Transformer-Pipeline vom Rohtext bis zur Vorhersage funktioniert:
texts = ["I love machine learning", "Transformers are powerful", "Deep learning is cool"]
labels = [0, 1, 0] # 2 classes: 0, 1
vocab = build_vocab(texts)
max_len = 6
d_model = 8
num_classes = 2
clf = TransformerClassifier(len(vocab), d_model, max_len, num_classes)
test_text = "I love transformers"
test_seq = text_to_sequence(test_text, vocab, max_len)
probs = clf.forward(test_seq)
print("Predicted class probabilities:", probs)
Wischen, um mit dem Codieren zu beginnen
Schrittweise Entwicklung eines minimalen Transformer-Textklassifikators:
- Definition einer Funktion
tokenize, die einen String entgegennimmt und eine Liste von Wörtern in Kleinbuchstaben zurückgibt; - Definition einer Funktion
build_vocab, die eine Liste von Texten entgegennimmt und ein Wörterbuch zurückgibt, das jedes einzigartige Wort einer ganzzahligen Indexnummer zuordnet (Indexierung beginnt bei 1, 0 ist für Padding reserviert); - Definition einer Funktion
text_to_sequence, die einen Text mithilfe eines Vokabulars in eine Liste von Wortindizes umwandelt und auf eine feste Länge auffüllt oder abschneidet.
Testen Sie Ihre Funktionen mit:
tokenize("Hello World!")sollte["hello", "world!"]zurückgeben;build_vocab(["Hello world", "world of NLP"])sollte ein Wörterbuch zurückgeben, das Wörter eindeutigen Indizes zuordnet (z. B.{"hello": 1, "world": 2, "of": 3, "nlp": 4});text_to_sequence("Hello NLP", vocab, 4)sollte eine Liste von 4 Indizes zurückgeben, bei Bedarf mit Nullen aufgefüllt.
Lösung
Danke für Ihr Feedback!
single
Fragen Sie AI
Fragen Sie AI
Fragen Sie alles oder probieren Sie eine der vorgeschlagenen Fragen, um unser Gespräch zu beginnen