Modelagem de Tópicos & BERTopic
A modelagem de tópicos responde a uma pergunta enganosamente simples: dados milhares de documentos, sobre o que eles falam? É não supervisionada — sem rótulos, sem categorias predefinidas — o que a torna a irmã, no mundo do texto, do agrupamento. Aplicações: minerar avaliações de clientes e tíquetes de suporte, organizar arquivos de notícias, monitorar redes sociais, explorar a literatura científica.
Esta aula apresenta a abordagem clássica (LDA) e então o BERTopic — um pipeline moderno montado quase inteiramente a partir de técnicas que você já aprendeu neste curso.
Modelagem de tópicos clássica: LDA
A Latent Dirichlet Allocation (Blei, Ng & Jordan, 2003) é um modelo generativo probabilístico construído sobre contagens de bag-of-words. Ela supõe que cada documento foi "escrito" por um processo aleatório:
- cada tópico é uma distribuição de probabilidade sobre o vocabulário (o tópico "esportes": alta probabilidade para jogo, time, placar...);
- cada documento é uma mistura de tópicos (70% esportes, 30% finanças);
- cada palavra de um documento é gerada sorteando primeiro um tópico da mistura do documento e depois uma palavra desse tópico.
Ajustar a LDA inverte o processo: dadas apenas as palavras observadas, inferir as distribuições tópico–palavra e as misturas documento–tópico.
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
X = CountVectorizer(max_df=0.9, min_df=5, stop_words='english').fit_transform(docs)
lda = LatentDirichletAllocation(n_components=10, random_state=0).fit(X)
A LDA serviu o campo por duas décadas, mas herda toda limitação do bag-of-words:
- ordem das palavras e contexto são ignorados; sinônimos são dimensões não relacionadas;
- o número de tópicos \(k\) precisa ser fixado de antemão;
- textos curtos (tweets, títulos de tíquetes) dão contagens muito esparsas — a LDA tem dificuldade;
- os tópicos costumam ser difíceis de interpretar sem muito pré-processamento (listas de stop words, stemming, ajuste).
BERTopic: modelagem de tópicos sobre embeddings
O BERTopic (Grootendorst, 2022) substitui a história generativa por uma geométrica: representar documentos de modo que a similaridade semântica seja proximidade espacial e então encontrar as regiões densas. O pipeline é uma composição das três últimas aulas:
flowchart LR
A[Documentos] --> B["1. Sentence embeddings<br><small>SBERT — Representação de Texto</small>"]
B --> C["2. Reduzir dimensões<br><small>UMAP — Redução de Dimensionalidade</small>"]
C --> D["3. Agrupar<br><small>HDBSCAN — Agrupamento</small>"]
D --> E["4. Descrever tópicos<br><small>c-TF-IDF — esta aula</small>"] Passo 1 — Representar (embed). Cada documento vira um vetor denso via um sentence-transformer. Documentos sobre reembolsos e dinheiro de volta ficam próximos mesmo com vocabulário disjunto — a vantagem decisiva sobre a LDA.
Passo 2 — Reduzir. Embeddings têm 384–768 dimensões; o agrupamento por densidade sofre nesse espaço (a maldição da dimensionalidade). O UMAP comprime para ~5 dimensões preservando a estrutura de vizinhança.
Passo 3 — Agrupar. O HDBSCAN encontra agrupamentos de forma e densidade variáveis — e, crucialmente, não força todo documento para dentro de um tópico: documentos que não se encaixam em lugar nenhum viram outliers (tópico −1) em vez de poluir tópicos reais. O número de tópicos emerge dos dados; você nunca define \(k\).
Passo 4 — Descrever. Cada agrupamento precisa de um rótulo legível por humanos. O BERTopic concatena todos os documentos de um agrupamento em um pseudodocumento e aplica o TF-IDF baseado em classe:
onde \(A\) é o número médio de palavras por classe e \(\text{tf}(t)\) é a frequência de \(t\) em todas as classes. É o TF-IDF que você conhece aplicado no nível do agrupamento: palavras frequentes neste tópico mas raras nos outros ficam no topo — essas se tornam as palavras-chave do tópico.
Por que vale a pena estudar este design
O BERTopic é um estudo de caso em composição: quatro componentes clássicos, cada um substituível (troque o SBERT por qualquer embedder, o HDBSCAN por k-means, o c-TF-IDF por outro rotulador), montados em um sistema de ponta. Entender as partes — o que você agora faz — significa que você consegue ajustar, depurar e estender o todo.
Um exemplo básico resolvido
O BERTopic não está no ambiente de build deste site (modelos de embedding são pesados), então rode isto localmente ou no Colab — um notebook complementar é fornecido abaixo.
# pip install bertopic
from bertopic import BERTopic
from sklearn.datasets import fetch_20newsgroups
docs = fetch_20newsgroups(subset='all', remove=('headers', 'footers', 'quotes')).data
topic_model = BERTopic(language='english', verbose=True)
topics, probs = topic_model.fit_transform(docs)
topic_model.get_topic_info().head(10)
Saída típica — tópicos descobertos sem supervisão, sem \(k\) predefinido:
Topic Count Name
-1 6789 -1_the_of_to_and ← outliers (sem tópico)
0 589 0_game_team_hockey_play
1 541 1_god_jesus_bible_faith
2 480 2_car_engine_dealer_miles
3 432 3_key_encryption_chip_clipper
...
Inspecionando e usando o modelo:
topic_model.get_topic(0) # principais palavras c-TF-IDF do tópico 0
topic_model.find_topics("space exploration") # buscar tópicos semanticamente
topic_model.transform(["My car needs new brakes"]) # atribuir tópicos a novos docs
# Visualizações interativas embutidas (Plotly)
topic_model.visualize_topics() # mapa de distância entre tópicos
topic_model.visualize_barchart() # principais palavras por tópico
topic_model.visualize_heatmap() # matriz de similaridade de tópicos
Dicas práticas
- Reduzir outliers: um tópico −1 grande é normal;
topic_model.reduce_outliers(docs, topics)os reatribui ao tópico mais próximo, se desejado. - Controlar a granularidade dos tópicos com o
min_cluster_sizedo HDBSCAN (viamin_topic_size): maior → menos tópicos, mais amplos. Ou funda após o ajuste:topic_model.reduce_topics(docs, nr_topics=20). - Palavras-chave melhores: passe um
CountVectorizer(stop_words='english', ngram_range=(1,2))para melhorar os rótulos c-TF-IDF sem tocar no agrupamento. - Reprodutibilidade: o UMAP é estocástico — defina
umap_model=UMAP(random_state=42)para tópicos repetíveis. - Corpora em português / multilíngues:
BERTopic(language='multilingual')seleciona um sentence-transformer multilíngue — funciona bem em texto em português do Brasil.
LDA vs BERTopic
| LDA (2003) | BERTopic (2022) | |
|---|---|---|
| Representação | contagens de bag-of-words | sentence embeddings contextuais |
| Sinônimos/contexto | invisíveis | capturados pelo embedder |
| Número de tópicos | fixado de antemão (k) | emerge da densidade (HDBSCAN) |
| Outliers | forçados para tópicos | tópico −1 explícito |
| Textos curtos | fraco (contagens esparsas) | forte |
| Interpretabilidade | probabilidades tópico–palavra | palavras-chave c-TF-IDF + visualizações |
| Custo | barato, CPU | o passo de embedding precisa de um modelo (GPU ajuda) |
Notebook complementar
Baixe o notebook e rode no Colab ou localmente (pip install bertopic):