Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Impara Sfida: Costruire un Classificatore di Testo con Transformer | Applicare i Transformers ai Compiti NLP
Transformer per l'elaborazione del linguaggio naturale
Sezione 3. Capitolo 2
single

single

bookSfida: Costruire un Classificatore di Testo con Transformer

Scorri per mostrare il menu

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
import 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)
copy

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)
Compito

Scorri per iniziare a programmare

Costruire passo dopo passo un classificatore di testo minimale basato su Transformer:

  1. Scrivere una funzione tokenize che prenda una stringa e restituisca una lista di parole in minuscolo;
  2. Scrivere una funzione build_vocab che 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);
  3. Scrivere una funzione text_to_sequence che 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

Switch to desktopCambia al desktop per esercitarti nel mondo realeContinua da dove ti trovi utilizzando una delle opzioni seguenti
Tutto è chiaro?

Come possiamo migliorarlo?

Grazie per i tuoi commenti!

Sezione 3. Capitolo 2
single

single

Chieda ad AI

expand

Chieda ad AI

ChatGPT

Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione

some-alt