Representação de Texto
Tudo o que fizemos até agora supôs que os dados chegam como uma tabela numérica. Texto não chega. Esta aula constrói a ponte — de strings brutas a vetores — de que toda aplicação de texto depende, e prepara o terreno para Modelagem de Tópicos & BERTopic.
A pergunta central: como transformar um documento em um vetor para que ferramentas geométricas (distâncias, agrupamento, classificadores) se apliquem?
Tokenização e o vocabulário
O primeiro passo é dividir o texto em unidades — tokens (palavras, subpalavras ou caracteres) — e construir um vocabulário: o conjunto de tokens distintos em todo o corpus. Normalizações comuns: minúsculas, remover pontuação, opcionalmente remover stop words ("o", "de", "e" — palavras frequentes que carregam pouco conteúdo) e reduzir palavras a radicais (stems) ou lemas ("correndo" → "correr").
Bag-of-Words
A representação clássica (descendente direta da recuperação de informação dos anos 1950): representar um documento por suas contagens de palavras, ignorando totalmente a ordem.
from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer()
X = vec.fit_transform(corpus) # matriz esparsa: n_docs × |V|
"Bag" (saco) é literal: "o cachorro mordeu o homem" e "o homem mordeu o cachorro" recebem vetores idênticos. A ordem das palavras — e, portanto, a maior parte da sintaxe — é descartada.
TF-IDF
Contagens brutas dão peso demais a palavras comuns. O TF-IDF (frequência do termo × frequência inversa nos documentos, Spärck Jones 1972) reescala cada contagem por quão distintiva a palavra é no corpus:
onde \(n\) é o número de documentos e \(\text{df}(t)\) é o número de documentos que contêm \(t\). Uma palavra que aparece em todos os documentos (idf ≈ 1 após a suavização) é descontada; uma palavra concentrada em poucos documentos é amplificada. O scikit-learn então normaliza cada vetor de documento para comprimento unitário.
| ate | cat | chased | cheese | data | dog | from | learn | learning | machine | models | mouse | the | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| doc1 | 0.00 | 0.40 | 0.40 | 0.00 | 0.00 | 0.51 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.65 |
| doc2 | 0.00 | 0.42 | 0.42 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.42 | 0.68 |
| doc3 | 0.57 | 0.00 | 0.00 | 0.57 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.45 | 0.37 |
| doc4 | 0.00 | 0.00 | 0.00 | 0.00 | 0.41 | 0.00 | 0.41 | 0.41 | 0.41 | 0.41 | 0.41 | 0.00 | 0.00 |
Note como "the" — presente em três de quatro documentos — recebe pesos baixos, enquanto palavras distintivas como "cheese" e "learning" pontuam alto em seus documentos. Documentos são comparados com similaridade de cosseno:
n-gramas: comprando de volta um pouco de ordem
Contar sequências de \(n\) tokens consecutivos (bigramas: "not good", "machine learning") recupera a ordem local das palavras:
O custo: o vocabulário — e a dimensionalidade — explode combinatoriamente.
Os limites das representações esparsas
Vetores de bag-of-words/TF-IDF são esparsos (majoritariamente zeros), de alta dimensão (\(|V|\) pode passar de 10⁵) e — crucialmente — tratam as palavras como símbolos atômicos:
- "carro" e "automóvel" são dimensões ortogonais: similaridade zero, apesar de sinônimos;
- "banco" (rio) e "banco" (finanças) são a mesma dimensão: o contexto é invisível;
- um documento sobre cachorros e outro sobre filhotes podem não compartilhar vocabulário e serem julgados não relacionados.
Essas são exatamente as falhas que motivaram os embeddings densos.
Embeddings densos
Word embeddings — a intuição do word2vec
O word2vec (Mikolov et al., 2013) aprende um vetor denso (~300 dimensões) por palavra treinando uma rede rasa para prever palavras a partir de seus contextos. A hipótese distribucional faz a mágica: palavras que aparecem em contextos semelhantes recebem vetores semelhantes. Sinônimos ficam próximos, e as direções codificam relações — o famoso
GloVe (2014) e fastText (2016) refinam a ideia. Limitação: um vetor por palavra, então "banco" ainda tem um único sentido, uma média sobre todos os seus usos.
Sentence embeddings — contextuais e de documento inteiro
Modelos transformer (BERT, 2018) produzem embeddings contextuais — o vetor de "banco" difere em "banco do rio" vs "empréstimo do banco". O Sentence-BERT (Reimers & Gurevych, 2019) faz o fine-tuning desses modelos para que uma frase inteira ou um documento curto mapeie para um único vetor denso (~384–768 dimensões) onde similaridade de cosseno ≈ similaridade semântica:
# pip install sentence-transformers
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
emb = model.encode(["The dog chased the cat",
"A hound pursued a feline",
"Interest rates rose again"])
# emb.shape == (3, 384); emb[0]·emb[1] alto, emb[0]·emb[2] baixo
As duas primeiras frases quase não compartilham vocabulário, mas recebem vetores altamente similares — exatamente o que o TF-IDF não consegue fazer.
Esparso vs denso: quando usar cada um
| Esparso (BoW / TF-IDF) | Denso (embeddings) | |
|---|---|---|
| Dimensionalidade | ( | V |
| Sinônimos | ortogonais (perdidos) | vetores próximos |
| Polissemia | confundida | tratada (modelos contextuais) |
| Interpretabilidade | alta — as dimensões são palavras | baixa — as dimensões são abstratas |
| Custo computacional | trivial | precisa de um modelo pré-treinado |
| Ótimo para | busca por palavra-chave, baselines lineares | busca semântica, agrupamento, BERTopic |
Para onde isso leva
Próxima aula: representar documentos densamente, reduzir com UMAP, agrupar com HDBSCAN e descrever cada agrupamento com uma variante de TF-IDF. Representações esparsas e densas trabalhando juntas — esse pipeline é o BERTopic.