Phase 05 - Lesson 15
Modelagem de Tópicos — LDA e BERTopic
LDA: documentos são misturas de tópicos, tópicos são distribuições sobre palavras. BERTopic: documentos se agrupam no espaço de embeddings, os agrupamentos são tópicos. Mesmo objetivo, primitivas diferentes.
Tipo: Aprender Linguagens: Python Pré-requisitos: Fase 5 · 02 (BoW + TF-IDF), Fase 5 · 03 (Word2Vec) Tempo: ~45 minutos
O Problema
Você tem 10.000 tickets de suporte ao cliente, 50.000 artigos de notícias ou 200.000 tweets. Você precisa saber sobre o que a coleção trata sem lê-la. Você não tem categorias rotuladas. Você nem sabe quantas categorias existem.
A modelagem de tópicos responde a isso sem supervisão. Dê a ela um corpus e receba de volta um pequeno conjunto de tópicos coerentes e, para cada documento, uma distribuição sobre esses tópicos.
Duas famílias algorítmicas dominam. O LDA (2003) trata cada documento como uma mistura de tópicos latentes e cada tópico como uma distribuição sobre palavras. A inferência é bayesiana. Ele ainda é usado em produção quando você precisa de atribuições de tópicos com pertencimento misto e distribuições de probabilidade explicáveis no nível das palavras.
O BERTopic (2020) codifica documentos com BERT, reduz a dimensionalidade com UMAP, agrupa com HDBSCAN e extrai palavras de tópicos via TF-IDF baseado em classes. Ele se sai melhor com texto curto, redes sociais e qualquer coisa em que a similaridade semântica importe mais do que a sobreposição de palavras. Um documento recebe um único tópico, o que é uma limitação para conteúdo de formato longo.
Esta lição constrói intuição para ambos e indica qual escolher para um dado corpus.
O Conceito
História generativa do LDA. Cada tópico é uma distribuição sobre palavras. Cada documento é uma mistura de tópicos. Para gerar uma palavra em um documento, amostre um tópico da mistura do documento e, em seguida, amostre uma palavra da distribuição desse tópico. A inferência inverte isso: dadas as palavras observadas, infira a distribuição de tópicos por documento e a distribuição de palavras por tópico. A amostragem de Gibbs colapsada ou o Bayes variacional fazem a matemática.
Saída principal do LDA:
doc_topic: matriz(n_docs, n_topics), cada linha soma 1 (mistura de tópicos do documento).topic_word: matriz(n_topics, vocab_size), cada linha soma 1 (distribuição de palavras do tópico).
Pipeline do BERTopic.
- Codifique cada documento com um sentence transformer (por exemplo,
all-MiniLM-L6-v2). Vetores de 384 dimensões. - Reduza a dimensionalidade com UMAP para ~5 dimensões. Os embeddings do BERT têm dimensionalidade alta demais para agrupamento.
- Agrupe com HDBSCAN. Baseado em densidade, produz agrupamentos de tamanho variável e um rótulo de "outlier".
- Para cada agrupamento, calcule o TF-IDF baseado em classes sobre os documentos do agrupamento para extrair as principais palavras.
A saída é um tópico por documento (mais um rótulo de outlier -1). Opcionalmente, um pertencimento suave via o vetor de probabilidade do HDBSCAN.
Construa
Passo 1: LDA com scikit-learn
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
import numpy as np
def fit_lda(documents, n_topics=5, max_features=1000):
cv = CountVectorizer(
max_features=max_features,
stop_words="english",
min_df=2,
max_df=0.9,
)
X = cv.fit_transform(documents)
lda = LatentDirichletAllocation(
n_components=n_topics,
random_state=42,
max_iter=50,
learning_method="online",
)
doc_topic = lda.fit_transform(X)
feature_names = cv.get_feature_names_out()
return lda, cv, doc_topic, feature_names
def print_top_words(lda, feature_names, n_top=10):
for idx, topic in enumerate(lda.components_):
top_idx = np.argsort(-topic)[:n_top]
words = [feature_names[i] for i in top_idx]
print(f"topic {idx}: {' '.join(words)}")
Observe: stopwords removidas, min_df e max_df filtram termos raros e onipresentes, CountVectorizer (não TfidfVectorizer) porque o LDA espera contagens brutas.
Passo 2: BERTopic (produção)
from bertopic import BERTopic
topic_model = BERTopic(
embedding_model="sentence-transformers/all-MiniLM-L6-v2",
min_topic_size=15,
verbose=True,
)
topics, probs = topic_model.fit_transform(documents)
info = topic_model.get_topic_info()
print(info.head(20))
valid_topics = info[info["Topic"] != -1]["Topic"].tolist()
for topic_id in valid_topics[:5]:
print(f"topic {topic_id}: {topic_model.get_topic(topic_id)[:10]}")
O filtro em Topic != -1 descarta o balde de outliers do BERTopic (documentos que o HDBSCAN não conseguiu agrupar). O min_topic_size controla o tamanho mínimo de agrupamento do HDBSCAN; o padrão da biblioteca BERTopic é 10. Este exemplo o define explicitamente como 15 para a escala da lição. Para corpora com mais de 10.000 documentos, aumente para 50 ou 100.
Passo 3: avaliação
Ambos os métodos produzem palavras de tópicos. A questão é se essas palavras são coerentes.
- Coerência de tópicos (c_v). Combina a NPMI (informação mútua pontual normalizada) de pares de palavras principais sobre contextos de janela deslizante, agrega as pontuações em vetores de tópicos e compara esses vetores via similaridade de cosseno. Quanto maior, melhor. Use
gensim.models.CoherenceModelcomcoherence="c_v". - Diversidade de tópicos. Fração de palavras únicas entre as principais palavras de todos os tópicos. Quanto maior, melhor (os tópicos não se sobrepõem).
- Inspeção qualitativa. Leia as principais palavras de cada tópico. Elas nomeiam algo real? O julgamento humano ainda é a última linha de defesa.
Quando escolher qual
| Situação | Escolha |
|---|---|
| Texto curto (tweets, avaliações, manchetes) | BERTopic |
| Documentos longos com misturas de tópicos | LDA |
| Sem GPU / poder computacional limitado | LDA ou NMF |
| Necessidade de distribuições multitópico no nível do documento | LDA |
| Integração com LLM para rotulagem de tópicos | BERTopic (suporte direto) |
| Implantação em edge com recursos restritos | LDA |
| Coerência semântica máxima | BERTopic |
A maior consideração prática é o comprimento do documento. Os embeddings do BERT truncam; as contagens do LDA funcionam com qualquer comprimento. Para documentos maiores que o contexto do modelo de embeddings, ou divida em pedaços + agregue ou use LDA.
Use
A stack de 2026:
- BERTopic. Padrão para texto curto e qualquer coisa em que a semântica importe.
gensim.models.LdaModel. LDA clássico para produção, maduro, testado em batalha.sklearn.decomposition.LatentDirichletAllocation. LDA fácil para experimentos.- NMF. Fatoração de matriz não negativa. Alternativa rápida ao LDA, qualidade comparável em texto curto.
- Top2Vec. Design semelhante ao BERTopic. Comunidade menor, mas bom em alguns benchmarks.
- FASTopic. Mais novo, mais rápido que o BERTopic em corpora muito grandes.
- Rotulagem baseada em LLM. Execute qualquer agrupamento e depois peça a um modelo para nomear cada agrupamento.
Entregue
Salve como outputs/skill-topic-picker.md:
---
name: topic-picker
description: Pick LDA or BERTopic for a corpus. Specify library, knobs, evaluation.
version: 1.0.0
phase: 5
lesson: 15
tags: [nlp, topic-modeling]
---
Given a corpus description (document count, avg length, domain, language, compute budget), output:
1. Algorithm. LDA / NMF / BERTopic / Top2Vec / FASTopic. One-sentence reason.
2. Configuration. Number of topics: `recommended = max(5, round(sqrt(n_docs)))`, clamped to 200 for corpora under 40,000 docs; permit >200 only when the corpus is genuinely large (>40k) and note the increased compute cost. `min_df` / `max_df` filters and embedding model for neural approaches also belong here.
3. Evaluation. Topic coherence (c_v) via `gensim.models.CoherenceModel`, topic diversity, and a 20-sample human read.
4. Failure mode to probe. For LDA, "junk topics" absorbing stopwords and frequent terms. For BERTopic, the -1 outlier cluster swallowing ambiguous documents.
Refuse BERTopic on documents longer than the embedding model's context window without a chunking strategy. Refuse LDA on very short text (tweets, reviews under 10 tokens) as coherence collapses. Flag any n_topics choice below 5 as likely wrong; flag >200 on corpora under 40k docs as likely over-splitting.
Exercícios
- Fácil. Ajuste o LDA com 5 tópicos no conjunto de dados 20 Newsgroups. Imprima as 10 principais palavras por tópico. Rotule cada tópico manualmente. O algoritmo encontrou as categorias reais?
- Médio. Ajuste o BERTopic no mesmo subconjunto de 20 Newsgroups. Compare o número de tópicos encontrados, as principais palavras e a coerência qualitativa em relação ao LDA. Qual revela as categorias reais de forma mais limpa?
- Difícil. Calcule a coerência c_v tanto para o LDA quanto para o BERTopic no seu corpus. Execute cada um com 5, 10, 20 e 50 tópicos. Trace a coerência vs o número de tópicos. Relate qual método é mais estável entre os números de tópicos.
Termos-Chave
| Termo | O que as pessoas dizem | O que realmente significa |
|---|---|---|
| Tópico | Algo sobre o que o corpus trata | Uma distribuição de probabilidade sobre palavras (LDA) ou um agrupamento de documentos semelhantes (BERTopic). |
| Pertencimento misto | Um documento abrange vários tópicos | O LDA atribui a cada documento uma distribuição sobre todos os tópicos. |
| UMAP | Redução de dimensionalidade | Aprendizado de variedade (manifold) que preserva a estrutura local; usado no BERTopic. |
| HDBSCAN | Agrupamento por densidade | Encontra agrupamentos de tamanho variável; produz rótulo de "ruído" (-1) para outliers. |
| Coerência c_v | Métrica de qualidade de tópicos | Informação mútua pontual média das principais palavras do tópico dentro de janelas deslizantes. |
Leitura Complementar
- Blei, Ng, Jordan (2003). Latent Dirichlet Allocation — o artigo do LDA.
- Grootendorst (2022). BERTopic: Neural topic modeling with a class-based TF-IDF procedure — o artigo do BERTopic.
- Röder, Both, Hinneburg (2015). Exploring the Space of Topic Coherence Measures — o artigo que introduziu o c_v e seus parentes.
- Documentação do BERTopic — a referência de produção. Excelentes exemplos.