Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Apprendre Défi : Construire un Classificateur de Texte avec un Transformer | Application des Transformers aux Tâches NLP
Transformers pour le Traitement du Langage Naturel
Section 3. Chapitre 2
single

single

bookDéfi : Construire un Classificateur de Texte avec un Transformer

Glissez pour afficher le 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

Vous venez de découvrir une implémentation minimale d’un classificateur de texte basé sur un Transformer, utilisant uniquement numpy et les fonctionnalités de base de Python. Ce code illustre comment assembler les éléments essentiels d’un Transformer pour une tâche de classification de texte, sans recourir à des bibliothèques de deep learning.

Le processus commence par la conversion du texte brut en un format adapté au calcul. La fonction tokenize découpe le texte en mots minuscules :

def tokenize(text):
    return text.lower().split()

Ensuite, build_vocab crée une correspondance entre chaque mot unique et un entier unique :

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 fonction text_to_sequence transforme chaque phrase d’entrée en une séquence d’indices de mots de longueur fixe, en complétant ou tronquant si nécessaire :

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

Pour donner au modèle la notion d’ordre des mots, positional_encoding génère une matrice de valeurs sinusoïdales à ajouter aux embeddings de mots. Cette étape est cruciale car, contrairement aux réseaux récurrents, les Transformers ne traitent pas intrinsèquement les séquences dans l’ordre :

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

Le mécanisme d’auto-attention, implémenté dans self_attention, permet au modèle de pondérer l’importance de chaque mot dans le contexte de toute la séquence. Cela se fait en calculant des scores de similarité entre toutes les paires de mots, en appliquant la fonction softmax pour obtenir des poids d’attention, puis en créant une nouvelle représentation pour chaque mot comme somme pondérée de tous les mots de la séquence :

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)

Après l’auto-attention, un réseau feed-forward simple (feed_forward) traite davantage la séquence :

def feed_forward(X, W1, b1, W2, b2):
    return np.maximum(0, X @ W1 + b1) @ W2 + b2

La fonction transformer_encoder combine ces étapes, en appliquant l’encodage positionnel, l’auto-attention et le réseau feed-forward à la suite :

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 regroupe l’ensemble. Elle initialise des embeddings de mots aléatoires, les poids des couches et les encodages positionnels. Dans la méthode forward, elle récupère les embeddings pour chaque token, applique le bloc encodeur, effectue un pooling par moyenne sur la séquence, puis passe le résultat dans une couche linéaire finale pour produire les probabilités de 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

Un exemple est présenté à la fin du code, où un petit ensemble de textes d’entraînement est utilisé pour construire le vocabulaire et initialiser le classificateur. Lorsqu’une nouvelle phrase est saisie, le modèle prédit les probabilités de classe, illustrant ainsi le fonctionnement du pipeline Transformer minimal, du texte brut à la prédiction :

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)
Tâche

Glissez pour commencer à coder

Construire un classificateur de texte minimal basé sur Transformer étape par étape :

  1. Écrire une fonction tokenize qui prend une chaîne de caractères et retourne une liste de mots en minuscules ;
  2. Écrire une fonction build_vocab qui prend une liste de textes et retourne un dictionnaire associant chaque mot unique à un indice entier (commencer l'indexation à 1, réserver 0 pour le padding) ;
  3. Écrire une fonction text_to_sequence qui convertit un texte en une liste d'indices de mots à l'aide d'un vocabulaire et effectue un padding/troncature à une longueur fixe.

Testez vos fonctions avec :

  • tokenize("Hello World!") doit retourner ["hello", "world!"] ;
  • build_vocab(["Hello world", "world of NLP"]) doit retourner un dictionnaire associant les mots à des indices uniques (par exemple, {"hello": 1, "world": 2, "of": 3, "nlp": 4}) ;
  • text_to_sequence("Hello NLP", vocab, 4) doit retourner une liste de 4 indices, complétée par des zéros si nécessaire.

Solution

Switch to desktopPassez à un bureau pour une pratique réelleContinuez d'où vous êtes en utilisant l'une des options ci-dessous
Tout était clair ?

Comment pouvons-nous l'améliorer ?

Merci pour vos commentaires !

Section 3. Chapitre 2
single

single

Demandez à l'IA

expand

Demandez à l'IA

ChatGPT

Posez n'importe quelle question ou essayez l'une des questions suggérées pour commencer notre discussion

some-alt