single
Desafio: Construindo um Classificador de Texto com Transformer
Deslize para mostrar o 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)
Você acabou de ver uma implementação mínima de um classificador de texto baseado em Transformer utilizando apenas numpy e recursos básicos do Python. Este código demonstra como as partes essenciais de um Transformer podem ser combinadas para uma tarefa de classificação de texto sem depender de bibliotecas de deep learning.
O processo começa convertendo o texto bruto em um formato adequado para computação. A função tokenize divide o texto em palavras minúsculas:
def tokenize(text):
return text.lower().split()
Em seguida, build_vocab cria um mapeamento de cada palavra única para um inteiro ú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
A função text_to_sequence transforma cada sentença de entrada em uma sequência de índices de palavras de comprimento fixo, preenchendo ou truncando conforme necessário:
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 ao modelo consciência da ordem das palavras, positional_encoding gera uma matriz de valores senoides que pode ser somada aos embeddings das palavras. Esta etapa é crucial porque, ao contrário das redes recorrentes, Transformers não processam sequências em ordem de forma inerente:
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
O mecanismo de self-attention, implementado em self_attention, permite que o modelo avalie a importância de cada palavra no contexto de toda a sequência. Isso é feito calculando escores de similaridade entre todos os pares de palavras, aplicando a função softmax para obter pesos de atenção e, em seguida, criando uma nova representação para cada palavra como uma soma ponderada de todas as palavras da sequência:
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)
Após o self-attention, uma rede feed-forward simples (feed_forward) processa ainda mais a sequência:
def feed_forward(X, W1, b1, W2, b2):
return np.maximum(0, X @ W1 + b1) @ W2 + b2
A função transformer_encoder combina essas etapas, aplicando codificação posicional, self-attention e a rede feed-forward em sequência:
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
A classe TransformerClassifier integra tudo. Ela inicializa embeddings de palavras aleatórios, pesos das camadas e codificações posicionais. No método forward, busca os embeddings para cada token, aplica o bloco codificador, faz o pooling das saídas por média ao longo da sequência e passa o resultado por uma camada linear final para produzir as probabilidades das classes:
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
Você pode ver um exemplo ao final do código, onde um pequeno conjunto de textos de treinamento é usado para construir o vocabulário e configurar o classificador. Quando uma nova sentença é fornecida, o modelo prevê as probabilidades das classes, mostrando como o pipeline mínimo do Transformer funciona desde o texto bruto até a predição:
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)
Deslize para começar a programar
Construa um classificador de texto minimalista com Transformer, passo a passo:
- Escreva uma função
tokenizeque recebe uma string e retorna uma lista de palavras em minúsculas; - Escreva uma função
build_vocabque recebe uma lista de textos e retorna um dicionário mapeando cada palavra única para um índice inteiro (comece a indexação em 1, reserve 0 para padding); - Escreva uma função
text_to_sequenceque converte um texto em uma lista de índices de palavras usando um vocabulário e faz o padding/truncamento para um comprimento fixo.
Teste suas funções com:
tokenize("Hello World!")deve retornar["hello", "world!"];build_vocab(["Hello world", "world of NLP"])deve retornar um dicionário mapeando palavras para índices únicos (por exemplo,{"hello": 1, "world": 2, "of": 3, "nlp": 4});text_to_sequence("Hello NLP", vocab, 4)deve retornar uma lista de 4 índices, preenchida com zeros se necessário.
Solução
Obrigado pelo seu feedback!
single
Pergunte à IA
Pergunte à IA
Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo