single
Challenge: Het Bouwen van een Transformer-Tekstclassificator
Veeg om het menu te tonen
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)
Je hebt zojuist een minimale implementatie gezien van een Transformer-gebaseerde tekstclassificator met alleen numpy en kernfunctionaliteit van Python. Deze code laat zien hoe de essentiële onderdelen van een Transformer kunnen worden samengebracht voor een tekstclassificatietaak zonder afhankelijk te zijn van deep learning-bibliotheken.
Het proces begint met het omzetten van ruwe tekst naar een formaat dat geschikt is voor berekeningen. De functie tokenize splitst de tekst in kleine letters woorden:
def tokenize(text):
return text.lower().split()
Vervolgens maakt build_vocab een mapping van elk uniek woord naar een uniek geheel getal:
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
De functie text_to_sequence zet elke invoerzinsnede om naar een sequentie van vaste lengte met woordindexen, waarbij zo nodig wordt opgevuld of afgekapt:
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
Om het model bewust te maken van de volgorde van woorden, genereert positional_encoding een matrix van sinusoïdale waarden die bij de woordembeddings kan worden opgeteld. Deze stap is cruciaal omdat, in tegenstelling tot recurrente netwerken, Transformers niet inherent sequentieel verwerken:
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
Het self-attention-mechanisme, geïmplementeerd in self_attention, stelt het model in staat het belang van elk woord te wegen in de context van de hele sequentie. Dit gebeurt door gelijkenisscores te berekenen tussen alle woordparen, de softmax-functie toe te passen om attention-gewichten te verkrijgen, en vervolgens een nieuwe representatie voor elk woord te creëren als een gewogen som van alle woorden in de sequentie:
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)
Na self-attention verwerkt een eenvoudige feed-forward-netwerk (feed_forward) de sequentie verder:
def feed_forward(X, W1, b1, W2, b2):
return np.maximum(0, X @ W1 + b1) @ W2 + b2
De functie transformer_encoder combineert deze stappen door positionele codering, self-attention en het feed-forward-netwerk achtereenvolgens toe te passen:
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
De klasse TransformerClassifier brengt alles samen. Deze initialiseert willekeurige woordembeddings, laaggewichten en positionele coderingen. In de forward-methode worden de embeddings voor elke token opgezocht, wordt het encoderblok toegepast, worden de uitkomsten gemiddeld over de sequentie (pooling), en wordt het resultaat door een laatste lineaire laag gehaald om klassekansen te produceren:
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
Aan het einde van de code zie je een voorbeeld waarbij een kleine set trainingsteksten wordt gebruikt om het vocabulaire op te bouwen en de classificator te initialiseren. Wanneer je een nieuwe zin invoert, voorspelt het model klassekansen, waarmee wordt getoond hoe de minimale Transformer-pijplijn werkt van ruwe tekst tot voorspelling:
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)
Veeg om te beginnen met coderen
Bouw stap voor stap een minimale Transformer-tekstclassificator:
- Schrijf een functie
tokenizedie een string neemt en een lijst van kleine letters als woorden retourneert; - Schrijf een functie
build_vocabdie een lijst van teksten neemt en een woordenboek retourneert dat elk uniek woord aan een geheelgetal-index koppelt (begin met indexeren vanaf 1, reserveer 0 voor padding); - Schrijf een functie
text_to_sequencedie een tekst omzet naar een lijst van woordindices met behulp van een vocabulaire en deze opvult of afkapt tot een vaste lengte.
Test uw functies met:
tokenize("Hello World!")moet["hello", "world!"]retourneren;build_vocab(["Hello world", "world of NLP"])moet een woordenboek retourneren dat woorden aan unieke indices koppelt (bijvoorbeeld{"hello": 1, "world": 2, "of": 3, "nlp": 4});text_to_sequence("Hello NLP", vocab, 4)moet een lijst van 4 indices retourneren, indien nodig opgevuld met nullen.
Oplossing
Bedankt voor je feedback!
single
Vraag AI
Vraag AI
Vraag wat u wilt of probeer een van de voorgestelde vragen om onze chat te starten.