Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprende Challenge: Construcción de un Clasificador de Texto con Transformers | Aplicando Transformers a Tareas de PLN
Transformers para Procesamiento de Lenguaje Natural
Sección 3. Capítulo 2
single

single

bookChallenge: Construcción de un Clasificador de Texto con Transformers

Desliza para mostrar el menú

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

Acabas de ver una implementación mínima de un clasificador de texto basado en Transformer utilizando únicamente numpy y funciones básicas de Python. Este código demuestra cómo se pueden ensamblar las partes esenciales de un Transformer para una tarea de clasificación de texto sin depender de bibliotecas de aprendizaje profundo.

El proceso comienza convirtiendo el texto sin procesar en un formato adecuado para el cálculo. La función tokenize divide el texto en palabras en minúsculas:

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

A continuación, build_vocab crea una asignación de cada palabra única a un entero único:

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 función text_to_sequence convierte cada oración de entrada en una secuencia de longitud fija de índices de palabras, rellenando o truncando según sea necesario:

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

Para dar al modelo conciencia del orden de las palabras, positional_encoding genera una matriz de valores sinusoidales que se pueden sumar a las incrustaciones de palabras. Este paso es crucial porque, a diferencia de las redes recurrentes, los Transformers no procesan secuencias en orden de forma inherente:

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

El mecanismo de self-attention, implementado en self_attention, permite que el modelo valore la importancia de cada palabra en el contexto de toda la secuencia. Esto se logra calculando puntuaciones de similitud entre todos los pares de palabras, aplicando la función softmax para obtener los pesos de atención y luego creando una nueva representación para cada palabra como una suma ponderada de todas las palabras de la secuencia:

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)

Después de la self-attention, una red feed-forward simple (feed_forward) procesa aún más la secuencia:

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

La función transformer_encoder combina estos pasos, aplicando codificación posicional, self-attention y la red feed-forward en secuencia:

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 clase TransformerClassifier integra todos los componentes. Inicializa aleatoriamente las incrustaciones de palabras, los pesos de las capas y las codificaciones posicionales. En el método forward, busca las incrustaciones para cada token, aplica el bloque codificador, agrupa las salidas promediando a lo largo de la secuencia y pasa el resultado por una capa lineal final para producir probabilidades de clase:

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

Puedes ver un ejemplo al final del código donde se utiliza un pequeño conjunto de textos de entrenamiento para construir el vocabulario y configurar el clasificador. Cuando introduces una nueva oración, el modelo predice las probabilidades de clase, mostrando cómo funciona el flujo mínimo de un Transformer desde el texto sin procesar hasta la predicción:

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

Desliza para comenzar a programar

Construcción paso a paso de un clasificador de texto mínimo basado en Transformer:

  1. Función tokenize que recibe una cadena y devuelve una lista de palabras en minúsculas;
  2. Función build_vocab que recibe una lista de textos y devuelve un diccionario que asigna a cada palabra única un índice entero (comenzando desde 1, reservando el 0 para padding);
  3. Función text_to_sequence que convierte un texto en una lista de índices de palabras utilizando un vocabulario y rellena/trunca a una longitud fija.

Prueba tus funciones con:

  • tokenize("Hello World!") debe devolver ["hello", "world!"];
  • build_vocab(["Hello world", "world of NLP"]) debe devolver un diccionario que asigne palabras a índices únicos (por ejemplo, {"hello": 1, "world": 2, "of": 3, "nlp": 4});
  • text_to_sequence("Hello NLP", vocab, 4) debe devolver una lista de 4 índices, rellenando con ceros si es necesario.

Solución

Switch to desktopCambia al escritorio para practicar en el mundo realContinúe desde donde se encuentra utilizando una de las siguientes opciones
¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 3. Capítulo 2
single

single

Pregunte a AI

expand

Pregunte a AI

ChatGPT

Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla

some-alt