Quando comecei a trabalhar com dados de texto anos atrás, todo o conceito de incorporações parecia desnecessariamente complexo. Fiquei confortável com minhas abordagens de saco de palavras e vetores TF-IDF simples.
Eu estava trabalhando com comentários de produtos, e meus modelos tradicionais continuaram classificando comentários com sarcasmo ou linguagem nuancada.O problema ficou claro: meus modelos não entenderam que "este produto está doente" poderia realmente ser positivo ou que "funcionou exatamente como esperado" poderia ser neutro ou negativo dependendo do contexto.
Eles não são apenas uma nova técnica fantástica – eles abordam limitações fundamentais em como as máquinas entendem a linguagem.
The Old Days: Life Before Embeddings
Os velhos dias: a vida antes dos embriõesVamos entender a história de usar representações de texto antes de inserções sem entrar nos detalhes desses métodos e abordagens.
One-Hot Encoding
Representava cada palavra como um vetor escasso com todos os zeros, exceto um único "1" na posição correspondente a essa palavra em um vocabulário. Representava palavras como vetores maciços e escassos, onde cada palavra tinha sua própria dimensão. Se o seu vocabulário tinha 100.000 palavras (o que é modesto), cada vetor de palavra tinha 99,999 zeros e um único 1. Essas representações nos disseram absolutamente nada de significado. As palavras "excelente" e "fantastic" eram matematicamente tão diferentes quanto "excelente" e "terrível" - completamente ausentes das relações semânticas óbvias.
"gato" → [1, 0, 0, 0, ..., 0] (posição 5432 no vocabulário) "cão" → [0, 1, 0, 0, ..., 0] (posição 8921 no vocabulário)
Limitações
- Não
- Explosão de dimensionalidade: os vetores tinham tantas dimensões quanto o tamanho do vocabulário (frequentemente mais de 100.000) Não
- Sem relações semânticas: "gato" e "gato" eram tão diferentes quanto "gato" e "avião" (todos equidistantes) Não
- Ineficiência computacional: Multiplicar essas matrizes escassas foi extremamente intensivo em recursos Não
- Sem generalização: o sistema não conseguiu entender palavras fora de seu vocabulário original Não
Bag-of-Words Approach
Ele contava as ocorrências de palavras em documentos, às vezes ponderadas pela sua importância. Tratava as palavras em documentos como coleções de palavras não classificadas, eliminando completamente a ordem de palavras. "O cão morde o homem" e "O homem morde o cão" teriam representações idênticas.
Documento: "O gato sentado no tapete" BoW: {"o": 2, "gato": 1, "sat": 1, "on": 1, "mat": 1}
As limitações:
- Não
- Perda de ordem de palavras: "Cão morde homem" e "Homem morde cão" tinham representações idênticas Não
- Vectores de alta dimensão: ainda são necessários vetores de tamanho de vocabulário Não
- Semântica: Sinônimos foram representados como características completamente diferentes Não
- Sem significado contextual: cada palavra tinha uma representação fixa, independentemente do contexto Não
N-grams
To capture some word order, we started using n-grams (sequences of n consecutive words). It looked at sequences of N consecutive words to capture some contextual information.
Com unigrams (palavras únicas), você pode ter um vocabulário de 100.000. Com bigrams (pares de palavras), de repente você está olhando para milhões de recursos potenciais. Com trigrams? bilhões, teoricamente. Mesmo com corte agressivo, a dimensionalidade tornou-se incontrolável.
Limitations:
As limitações:- Não
- Explosão combinatória: o número de n-gramas possíveis cresce exponencialmente Não
- Escassez de dados: a maioria dos n-gramas possíveis nunca aparecem nos dados de treinamento Não
- Janela de contexto limitada: apenas relações capturadas dentro de janelas pequenas (tipicamente 2-5 palavras) Não
TF-IDF (Term Frequency-Inverse Document Frequency)
O TF-IDF melhorou as coisas ponderando palavras com base em quão importantes elas eram para um documento específico em relação ao corpus.
Limitations:
As limitações:Sem significado semântico: é o número e a frequência das palavras que determinam a importância de seu uso.
The Embedding Revolution: What Changed?
A Revolução Civil: o que mudou?A transição para os embeddings não foi apenas uma melhoria incremental; foi uma mudança de paradigma na forma como representamos a linguagem.
Meaning Through Context
A visão fundamental por trás das incorporações é enganosamente simples: palavras que aparecem em contextos semelhantes provavelmente têm significados semelhantes.Se você ver "cão" e "gato" aparecendo em torno dos mesmos tipos de palavras ("animal de estimação", "alimento", "forro"), eles provavelmente estão semanticamente relacionados.
Os primeiros modelos de incorporação como o Word2Vec capturaram isso treinando redes neurais para prever:
- Não
- Uma palavra baseada em seu contexto circundante (Saco contínuo de palavras) Não
- O contexto circundante baseado em uma palavra (Skip-gram) Não
Os pesos de camada ocultos desses modelos tornaram-se nossos vetores de palavras, codificando relações semânticas nas propriedades geométricas do espaço vetorial.
Quando eu comecei a traçar os vetores de palavras e vi que "rei" - "homem" + "mulher" ≈ "rei", eu sabia que estávamos em algo revolucionário.
Os primeiros modelos como Word2Vec e GloVe deram a cada palavra um único vetor, independentemente do contexto.
"I need to bank the money" vs. "I'll meet you by the river bank"
Modelos como BERT e GPT resolveram isso gerando diferentes inserções para a mesma palavra dependendo do seu contexto circundante.
Então, primeiro vamos entender o que são os embeddings e como eles transformaram a NLP e abordaram as limitações das abordagens anteriores.
What Are Embeddings?
O que são Embeddings?Os embeddings são representações numéricas de dados (texto, imagens, áudio, etc.) em um espaço vetorial contínuo.Para o texto, os embeddings capturam relações semânticas entre palavras ou documentos, permitindo que as máquinas entendam o significado de uma forma que é matematicamente processável.
Key Concepts:
- Não
- Vectores: listas ordenadas de números que representam um ponto no espaço multidimensional Não
- Dimensões: O número de valores em cada vetor (por exemplo, 768-dim, 1024-dim) Não
- Espaço vetorial: o espaço matemático onde existem embeddings Não
- Semântica: medida pela distância ou ângulo entre os vectores (mais próximo = mais semelhante) Não
What Do Dimensions Represent?
Cada dimensão em um vetor de incorporação representa um recurso ou aspecto aprendido dos dados. Ao contrário da engenharia de características clássica, onde os seres humanos definem o que cada dimensão significa, em modelos modernos de incorporação:
- Não
- Dimensões emergem durante o treinamento para representar "conceitos" abstratos Não
- As dimensões individuais muitas vezes carecem de significado humano-interpretável específico Não
- O vetor completo, no entanto, captura informação semântica holisticamente Não
- Algumas dimensões podem corresponder ao sentimento, formalidade, tópico ou sintaxe, mas a maioria representa combinações complexas de características. Não
Why We Need Embeddings
Os computadores basicamente trabalham com números, não palavras.Quando processamos linguagem, precisamos converter texto em representações numéricas que:
- Não
- Capturar relações semânticas – conceitos semelhantes devem ter representações semelhantes Não
- Manter o significado contextual – A mesma palavra pode significar coisas diferentes em contextos diferentes Não
- Ativar operações matemáticas – como encontrar semelhanças ou executar analogias Não
- Trabalhar de forma eficiente em escala – Processar grandes volumes de texto sem explosão computacional Não
Os embeddings resolvem esses problemas representando palavras, frases ou documentos como vetores densos em um espaço contínuo onde as relações semânticas são preservadas como relações geométricas.
Fundamentos de Embeddings
Representação do vetor dense
Em vez de vetores escassos com milhares ou milhões de dimensões, os embeddings usam algumas centenas de dimensões densas, onde cada dimensão contribui para o significado.
"cat" → [0.2, -0.4, 0.1, -0.8, ..., 0.3] (300 dimensions)
"kitten" → [0.19, -0.38, 0.15, -0.75, ..., 0.29] (similar to "cat")
Isso torna as ordens de computação de magnitude mais eficientes, permitindo uma representação semântica mais rica.
Semântica Distributiva
Os embeddings são construídos sobre o princípio de que "você saberá uma palavra pela empresa que mantém" (J.R. Firth). Ao analisar quais palavras aparecem em contextos similares, os embeddings capturam relações semânticas automaticamente.
Por exemplo, "rei" e "rei" terão contextos semelhantes, então eles terão inserções semelhantes, embora raramente apareçam na mesma posição exata.
Propriedades matemáticas
Os espaços incorporados têm propriedades matemáticas notáveis:
vector("king") - vector("man") + vector("woman") ≈ vector("queen")
Isso permite raciocínio analógico e operações semânticas diretamente no espaço vetorial.
Transferência de aprendizagem
Os embeddings pré-treinados capturam conhecimentos gerais de linguagem que podem ser ajustados para tarefas específicas, reduzindo drasticamente os dados necessários para novas aplicações.
Entendimento Contextual
As incorporações contextuais modernas (como as da BERT, GPT, etc.) representam a mesma palavra de forma diferente com base no contexto:
"I'll deposit money in the bank" → "bank" relates to finance
"I'll sit by the river bank" → "bank" relates to geography
With all the knowledge about the history and understanding of embeddings, it’s time to get down to using them.
Use os Modelos LLM/SLM para gerar embeddings
Várias equipes de pesquisa desenvolveram modelos de incorporação treinados em vários conjuntos de dados que abrangem vários idiomas e domínios. Esta diversidade resulta em modelos com vocabulários muito diferentes e capacidades de compreensão semântica. Por exemplo, modelos treinados predominantemente em literatura científica inglesa codificarão conceitos técnicos de forma diferente daqueles treinados em conteúdo de mídia social multilíngue. Esta especialização permite que os profissionais selecionem modelos de incorporação que melhor se alinham com seus casos de uso específicos.
A implementação prática de embeddings foi muito simplificada por bibliotecas como o pacote SentenceTransformer da Hugging Face, que fornece um SDK abrangente para trabalhar com vários modelos de embedding. Da mesma forma, o SDK da OpenAI oferece acesso direto aos seus modelos de embedding, que mostraram desempenho impressionante em muitos benchmarks. Essas ferramentas, e há muitos mais, têm acesso democratizado a tecnologias de embedding de última geração, permitindo que os desenvolvedores integram a compreensão semântica em aplicações sem ter que treinar modelos a partir do zero.
Os modelos, por causa deste artigo, devem ser tratados como uma caixa preta que toma sentenças como uma entrada e retorna sua representação vetorial correspondente.
Usando a SentenceTransformers Library para Embeddings
A maneira mais simples de gerar embeddings usando SentenceTransformer:
from sentence_transformers import SentenceTransformer
# Load a pre-trained model
model = SentenceTransformer('all-MiniLM-L6-v2') # 384 dimensions
# Generate embeddings
texts = ["This is an example sentence", "Each sentence becomes a vector"]
embeddings = model.encode(texts)
print(f"Shape: {embeddings.shape}") # (2, 384)
max_seq_length = model.tokenizer.model_max_length
print(max_seq_length) # 256
O modelo “all-MiniLM-L6-v2” disponível da HuggingFace tem 384 dimensões. Isso significa que ele pode capturar 384 características ou nuances para uma determinada palavra ou frase. O comprimento de sequência deste modelo é de 256 tokens. As sentenças são divididas em palavras e palavras em tokens pelo tokenizador durante o processo de incorporação. O número de tokens gerados para uma frase é geralmente 25% a 40% mais do que o número de palavras na frase.
O comprimento da sequência denota o número de tokens que podem ser processados pelo modelo como entrada dada. Qualquer coisa menos é padded para torná-lo 256 em comprimento, e qualquer coisa mais é descartado.
O método de codificação da classe SentenceTransformer é um envelope no modo de inferência PyTorch para usar o modelo.
from sentence_transformers import SentenceTransformer
import torch
# Load the model directly with SentenceTransformer
model = SentenceTransformer("sentence-transformers/msmarco-distilbert-base-tas-b")
# Input text
texts = ["This is an example sentence", "Each sentence becomes a vector"]
# Get embedding directly
with torch.no_grad():
embedding = model.encode(texts, convert_to_tensor=True)
print(embedding)
Aqui, a função torch.no_grad garante que não sejam calculados gradientes durante a propagação traseira.
Outra maneira mais genérica de gerar embeddings usando o PyTorch:
# Load the model
model = AutoModel.from_pretrained("sentence-transformers/msmarco-distilbert-base-tas-b")
# Get the tokenizer
tokenizer = AutoTokenizer.from_pretrained("sentence-transformers/msmarco-distilbert-base-tas-b")
# Tokenize input
text = ["This is an example sentence"]
encoded_input = tokenizer(text, padding=True, truncation=True, return_tensors='pt')
# Get embedding of the [CLS] token
with torch.no_grad():
outputs = model(**encoded_input, return_dict=True)
cls_embedding = outputs.last_hidden_state[:, 0]
print(cls_embedding)
A diferença entre este e fragmentos de código anteriores é que a função de codificação foi substituída por usar tokenizer e modelo explicitamente.
Outra diferença é que estamos usando outputs.last_hidden_state[:, 0] para recuperar o vetor relacionado ao token CLS. Este token especial CLS é adicionado a cada sentença no início de cada sentença, e contém informações acumuladas sobre a sentença inteira.
Deve-se notar que esta abordagem de adição de um token CLS é aplicável apenas a certas arquiteturas baseadas em transformadores, e isso inclui BERT e suas variantes e transformadores baseados apenas em codificadores.
Best for:Tarefas de Classificação e Previsão de Nível de Sequência
Why they work:O token [CLS] nos modelos de estilo BERT é especificamente treinado para agregar informações de toda a sequência durante o pré-treinamento.
When to choose:
- Não
- Quando se utilizam modelos BERT, RoBERTa ou similares para a classificação Não
- Quando você precisa de um único vetor que represente uma seqüência inteira Não
- Quando sua tarefa subsequente envolve prever uma propriedade de todo o texto Não
O método CLS usado é apenas um dos métodos para capturar as incorporações para uma sentença.
Mean Pooling
Tomar a média de todas as incorporações de token é surpreendentemente eficaz para muitas tarefas.É o meu método go-to quando estou usando incorporações para tarefas de semelhança ou recuperação.
Best for:Semântica de semelhança, recuperação e representações de propósito geral.
Why it works:Ao medir em todas as representações de token, o pooling médio capta o conteúdo semântico coletivo enquanto reduz o ruído.
When to choose:
- Não
- Para aplicações de semelhança de documentos ou de pesquisa semântica Não
- Quando você precisa de representações robustas que não são dominadas por nenhum token único
- Quando o teste empírico mostra que ele supera outros métodos (muitas vezes faz para tarefas de semelhança) Não
import torch
from transformers import AutoTokenizer, AutoModel
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# Tokenize input
texts = ["This is an example sentence", "Each sentence becomes a vector"]
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
# Mean pooling
with torch.no_grad():
outputs = model(**inputs)
# Get attention mask to ignore padding tokens
attention_mask = inputs['attention_mask']
# Sum token embeddings and divide by the number of tokens
sum_embeddings = torch.sum(outputs.last_hidden_state * attention_mask.unsqueeze(-1), dim=1)
count_tokens = torch.sum(attention_mask, dim=1, keepdim=True)
mean_embeddings = sum_embeddings / count_tokens
print(f"Shape: {mean_embeddings.shape}") # (2, 768)
Max Pooling
Max pooling leva o valor máximo para cada dimensão em todos os tokens.É surpreendentemente bom em capturar recursos importantes, independentemente de onde eles aparecem no texto.
Best for:Detecção de recursos e tarefas de extração de informações
Why it works:O Max pooling seleciona a ativação mais forte para cada dimensão em todos os tokens, capturando efetivamente os recursos mais proeminentes, independentemente de onde eles aparecem no texto.
When to choose:
- Não
- Quando características específicas importam mais do que sua frequência ou posição Não
- Ao procurar a presença de conceitos ou entidades particulares Não
- Ao lidar com textos longos onde sinais importantes podem ser diluídos na média Não
import torch
from transformers import AutoTokenizer, AutoModel
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# Tokenize input
texts = ["This is an example sentence", "Each sentence becomes a vector"]
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
# Max pooling
with torch.no_grad():
outputs = model(**inputs)
# Create a mask to ignore padding tokens for max pooling
attention_mask = inputs['attention_mask'].unsqueeze(-1)
# Replace padding token representations with -inf so they're never selected as max
token_embeddings = outputs.last_hidden_state.masked_fill(attention_mask == 0, -1e9)
# Take max over token dimension
max_embeddings = torch.max(token_embeddings, dim=1)[0]
print(f"Shape: {max_embeddings.shape}") # (2, 768)
Weighted Mean Pooling
O método de agrupamento ponderado tenta dar mais peso a tokens mais importantes com base na posição (por exemplo, dando mais peso a tokens posteriores).
Best for:Tarefas onde diferentes partes da entrada têm importância diferente
Why it works:O agrupamento ponderado permite que você enfatize certos tokens com base em sua posição, pontuação de atenção ou outras métricas de relevância.
When to choose:
- Quando a ordem de sequência importa (por exemplo, dando mais peso a tokens posteriores) Não
- Quando certos tokens são inerentemente mais informativos (por exemplo, núcleos e verbos versus artigos) Não
- Quando você tem uma importância heurística específica que faz sentido para sua tarefa Não
import torch
import torch.nn.functional as F
from transformers import AutoTokenizer, AutoModel
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# Tokenize input
texts = ["This is an example sentence", "Each sentence becomes a vector"]
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
# Weighted mean pooling - more weight to later tokens
with torch.no_grad():
outputs = model(**inputs)
# Get token embeddings and attention mask
token_embeddings = outputs.last_hidden_state
attention_mask = inputs['attention_mask']
# Create position-based weights (later positions get higher weights)
input_lengths = torch.sum(attention_mask, dim=1).unsqueeze(-1)
position_indices = torch.arange(token_embeddings.size(1)).unsqueeze(0).expand_as(attention_mask)
position_weights = position_indices.float() / input_lengths.float()
position_weights = position_weights * attention_mask
# Normalize weights to sum to 1
position_weights = position_weights / torch.sum(position_weights, dim=1, keepdim=True)
# Apply weights and sum
weighted_embeddings = torch.sum(token_embeddings * position_weights.unsqueeze(-1), dim=1)
print(f"Shape: {weighted_embeddings.shape}") # (2, 768)
Última polêmica
O último token pooling é uma técnica para criar um único vetor de incorporação a partir de uma sequência de incorporações de token selecionando apenas a representação do token final.
Best for:Modelos Autoregressivos e Processamento Sequencial
Why it works:Em modelos de esquerda para direita, como o GPT, o token final contém o contexto acumulado de toda a sequência, tornando-o rico em informações para certas tarefas.
When to choose:
- Não
- Ao usar o GPT ou outros modelos exclusivos para decodificadores Não
- Quando se trabalha com tarefas que dependem fortemente do contexto anterior Não
- Para geração de texto ou tarefas de conclusão Não
import torch
from transformers import AutoTokenizer, AutoModel
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# Tokenize input
texts = ["This is an example sentence", "Each sentence becomes a vector"]
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
# Last token pooling
with torch.no_grad():
outputs = model(**inputs)
# Get the last non-padding token for each sequence
attention_mask = inputs['attention_mask']
last_token_indices = torch.sum(attention_mask, dim=1) - 1
batch_indices = torch.arange(attention_mask.size(0))
# Extract the last token embedding for each sequence
last_token_embeddings = outputs.last_hidden_state[batch_indices, last_token_indices]
print(f"Shape: {last_token_embeddings.shape}") # (2, 768)
Existem muitas outras maneiras, e esses métodos podem ser combinados juntos, bem como para criar métodos personalizados.Este foi apenas o começo para entender embeddings como um conceito e implementação básica para obter embeddings usando técnicas diferentes.
Looking Forward: Where Embeddings Are Headed
Olhando para a frente: para onde os embriões estão indoO espaço de incorporação (pun intended) continua a evoluir:
- Não
- Os embeddings multimodais estão quebrando barreiras entre texto, imagens, áudio e vídeo. Modelos como o CLIP e o DALL-E usam embeddings para criar um espaço semântico compartilhado entre diferentes modalidades. Não
- Arquiteturas mais eficientes, como MobileBERT e DistilBERT, estão permitindo a utilização de incorporações poderosas em dispositivos de borda com recursos limitados. Não
- Embeddings específicos de domínio pré-treinados em corpora especializada estão impulsionando o estado da arte em campos como medicina, direito e finanças. Não
Estou particularmente animado com as incorporações conscientes da composição que capturam melhor como o significado é construído a partir de unidades menores, o que poderia finalmente resolver desafios de longa data com negação e frases de composição.
Final Thoughts
Pensamentos finaisOs embeddings não são apenas outra técnica de NLP – eles são uma mudança fundamental na forma como as máquinas entendem e processam a linguagem. Eles nos mudaram de tratar o texto como símbolos arbitrários para capturar a rica e complexa rede de significados e relações que os humanos entendem intuitivamente.
Seja qual for a tarefa de NLP em que você esteja trabalhando, as chances são de que as incorporações aplicadas cuidadosamente possam torná-lo melhor.A chave é entender não apenas como gerá-las, mas quando e por que usar diferentes abordagens.
E se você ainda estiver usando saco de palavras ou codificação one-hot para análise de texto...
Há um mundo inteiro de possibilidades esperando por você.