single
Sfida: Costruire un Classificatore di Testo con Transformer
Scorri per mostrare il menu
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)
Hai appena visto una implementazione minimale di un classificatore di testo basato su Transformer utilizzando solo numpy e le funzionalità base di Python. Questo codice mostra come le parti essenziali di un Transformer possano essere assemblate per un compito di classificazione del testo senza fare affidamento su librerie di deep learning.
Il processo inizia convertendo il testo grezzo in un formato adatto al calcolo. La funzione tokenize suddivide il testo in parole minuscole:
def tokenize(text):
return text.lower().split()
Successivamente, build_vocab crea una mappatura da ogni parola unica a un intero unico:
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
La funzione text_to_sequence trasforma ogni frase di input in una sequenza di lunghezza fissa di indici di parole, aggiungendo padding o troncando se necessario:
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
Per fornire al modello la consapevolezza dell'ordine delle parole, positional_encoding genera una matrice di valori sinusoidali che può essere sommata agli embedding delle parole. Questo passaggio è cruciale perché, a differenza delle reti ricorrenti, i Transformer non processano intrinsecamente le sequenze in ordine:
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
Il meccanismo di self-attention, implementato in self_attention, consente al modello di pesare l'importanza di ogni parola nel contesto dell'intera sequenza. Questo viene ottenuto calcolando i punteggi di similarità tra tutte le coppie di parole, applicando la funzione softmax per ottenere i pesi di attenzione e creando quindi una nuova rappresentazione per ogni parola come somma pesata di tutte le parole nella sequenza:
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)
Dopo la self-attention, una semplice rete feed-forward (feed_forward) elabora ulteriormente la sequenza:
def feed_forward(X, W1, b1, W2, b2):
return np.maximum(0, X @ W1 + b1) @ W2 + b2
La funzione transformer_encoder combina questi passaggi, applicando in sequenza la codifica posizionale, la self-attention e la rete feed-forward:
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
La classe TransformerClassifier unisce tutti i componenti. Inizializza gli embedding delle parole, i pesi dei layer e le codifiche posizionali in modo casuale. Nel metodo forward, recupera gli embedding per ogni token, applica il blocco encoder, aggrega le uscite tramite media sulla sequenza e passa il risultato attraverso un layer lineare finale per produrre le probabilità di classe:
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
Puoi vedere un esempio alla fine del codice dove un piccolo set di testi di addestramento viene utilizzato per costruire il vocabolario e impostare il classificatore. Quando inserisci una nuova frase, il modello predice le probabilità di classe, mostrando come funziona la pipeline minimale del Transformer dal testo grezzo alla previsione:
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)
Scorri per iniziare a programmare
Costruire passo dopo passo un classificatore di testo minimale basato su Transformer:
- Scrivere una funzione
tokenizeche prenda una stringa e restituisca una lista di parole in minuscolo; - Scrivere una funzione
build_vocabche prenda una lista di testi e restituisca un dizionario che mappa ogni parola unica a un indice intero (iniziare l'indicizzazione da 1, riservando 0 per il padding); - Scrivere una funzione
text_to_sequenceche converta un testo in una lista di indici di parole utilizzando un vocabolario e aggiunga/riduca la lunghezza a un valore fisso.
Testare le funzioni con:
tokenize("Hello World!")deve restituire["hello", "world!"];build_vocab(["Hello world", "world of NLP"])deve restituire un dizionario che mappa le parole a indici unici (ad esempio,{"hello": 1, "world": 2, "of": 3, "nlp": 4});text_to_sequence("Hello NLP", vocab, 4)deve restituire una lista di 4 indici, completata con zeri se necessario.
Soluzione
Grazie per i tuoi commenti!
single
Chieda ad AI
Chieda ad AI
Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione