Phase 05 - Lesson 15
Modelado de Tópicos — LDA y BERTopic
LDA: los documentos son mezclas de tópicos, los tópicos son distribuciones sobre palabras. BERTopic: los documentos se agrupan en el espacio de embeddings, los agrupamientos son tópicos. Mismo objetivo, primitivas diferentes.
Tipo: Aprender Lenguajes: Python Prerrequisitos: Fase 5 · 02 (BoW + TF-IDF), Fase 5 · 03 (Word2Vec) Tiempo: ~45 minutos
El Problema
Tienes 10.000 tickets de soporte al cliente, 50.000 artículos de noticias o 200.000 tweets. Necesitas saber de qué trata la colección sin leerla. No tienes categorías etiquetadas. Ni siquiera sabes cuántas categorías existen.
El modelado de tópicos responde a eso sin supervisión. Dale un corpus y recibe de vuelta un pequeño conjunto de tópicos coherentes y, para cada documento, una distribución sobre esos tópicos.
Dos familias algorítmicas dominan. LDA (2003) trata cada documento como una mezcla de tópicos latentes y cada tópico como una distribución sobre palabras. La inferencia es bayesiana. Todavía se usa en producción cuando necesitas asignaciones de tópicos con pertenencia mixta y distribuciones de probabilidad explicables a nivel de palabra.
BERTopic (2020) codifica documentos con BERT, reduce la dimensionalidad con UMAP, agrupa con HDBSCAN y extrae palabras de tópicos mediante TF-IDF basado en clases. Gana con texto corto, redes sociales y cualquier cosa donde la similitud semántica importe más que la superposición de palabras. Un documento recibe un solo tópico, lo que es una limitación para contenido de formato largo.
Esta lección construye intuición para ambos y señala cuál elegir para un corpus dado.
El Concepto
Historia generativa de LDA. Cada tópico es una distribución sobre palabras. Cada documento es una mezcla de tópicos. Para generar una palabra en un documento, muestrea un tópico de la mezcla del documento y luego muestrea una palabra de la distribución de ese tópico. La inferencia invierte esto: dadas las palabras observadas, infiere la distribución de tópicos por documento y la distribución de palabras por tópico. El muestreo de Gibbs colapsado o el Bayes variacional hacen la matemática.
Salida principal de LDA:
doc_topic: matriz(n_docs, n_topics), cada fila suma 1 (mezcla de tópicos del documento).topic_word: matriz(n_topics, vocab_size), cada fila suma 1 (distribución de palabras del tópico).
Pipeline de BERTopic.
- Codifica cada documento con un sentence transformer (por ejemplo,
all-MiniLM-L6-v2). Vectores de 384 dimensiones. - Reduce la dimensionalidad con UMAP a ~5 dimensiones. Los embeddings de BERT tienen una dimensionalidad demasiado alta para el agrupamiento.
- Agrupa con HDBSCAN. Basado en densidad, produce agrupamientos de tamaño variable y una etiqueta de "outlier".
- Para cada agrupamiento, calcula el TF-IDF basado en clases sobre los documentos del agrupamiento para extraer las palabras principales.
La salida es un tópico por documento (más una etiqueta de outlier -1). Opcionalmente, una pertenencia suave mediante el vector de probabilidad de HDBSCAN.
Constrúyelo
Paso 1: LDA con 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)}")
Observa: stopwords eliminadas, min_df y max_df filtran términos raros y ubicuos, CountVectorizer (no TfidfVectorizer) porque LDA espera conteos brutos.
Paso 2: BERTopic (producción)
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]}")
El filtro sobre Topic != -1 descarta el cubo de outliers de BERTopic (documentos que HDBSCAN no pudo agrupar). El min_topic_size controla el tamaño mínimo de agrupamiento de HDBSCAN; el valor por defecto de la biblioteca BERTopic es 10. Este ejemplo lo establece explícitamente en 15 para la escala de la lección. Para corpus de más de 10.000 documentos, auméntalo a 50 o 100.
Paso 3: evaluación
Ambos métodos producen palabras de tópicos. La cuestión es si esas palabras son coherentes.
- Coherencia de tópicos (c_v). Combina la NPMI (información mutua puntual normalizada) de pares de palabras principales sobre contextos de ventana deslizante, agrega las puntuaciones en vectores de tópicos y compara esos vectores mediante similitud del coseno. Cuanto más alta, mejor. Usa
gensim.models.CoherenceModelconcoherence="c_v". - Diversidad de tópicos. Fracción de palabras únicas entre las palabras principales de todos los tópicos. Cuanto más alta, mejor (los tópicos no se superponen).
- Inspección cualitativa. Lee las palabras principales de cada tópico. ¿Nombran algo real? El juicio humano sigue siendo la última línea de defensa.
Cuándo elegir cuál
| Situación | Elige |
|---|---|
| Texto corto (tweets, reseñas, titulares) | BERTopic |
| Documentos largos con mezclas de tópicos | LDA |
| Sin GPU / cómputo limitado | LDA o NMF |
| Necesidad de distribuciones multitópico a nivel de documento | LDA |
| Integración con LLM para etiquetado de tópicos | BERTopic (soporte directo) |
| Despliegue en edge con recursos restringidos | LDA |
| Máxima coherencia semántica | BERTopic |
La mayor consideración práctica es la longitud del documento. Los embeddings de BERT truncan; los conteos de LDA funcionan con cualquier longitud. Para documentos más largos que el contexto del modelo de embeddings, o divide en fragmentos + agrega o usa LDA.
Úsalo
El stack de 2026:
- BERTopic. Predeterminado para texto corto y cualquier cosa donde la semántica importe.
gensim.models.LdaModel. LDA clásico para producción, maduro, probado en batalla.sklearn.decomposition.LatentDirichletAllocation. LDA fácil para experimentos.- NMF. Factorización de matrices no negativas. Alternativa rápida a LDA, calidad comparable en texto corto.
- Top2Vec. Diseño similar a BERTopic. Comunidad más pequeña pero bueno en algunos benchmarks.
- FASTopic. Más nuevo, más rápido que BERTopic en corpus muy grandes.
- Etiquetado basado en LLM. Ejecuta cualquier agrupamiento y luego pídele a un modelo que nombre cada agrupamiento.
Entrégalo
Guarda 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.
Ejercicios
- Fácil. Ajusta LDA con 5 tópicos en el conjunto de datos 20 Newsgroups. Imprime las 10 palabras principales por tópico. Etiqueta cada tópico a mano. ¿El algoritmo encontró las categorías reales?
- Medio. Ajusta BERTopic en el mismo subconjunto de 20 Newsgroups. Compara el número de tópicos encontrados, las palabras principales y la coherencia cualitativa frente a LDA. ¿Cuál revela las categorías reales de forma más limpia?
- Difícil. Calcula la coherencia c_v tanto para LDA como para BERTopic en tu corpus. Ejecuta cada uno con 5, 10, 20 y 50 tópicos. Grafica la coherencia vs el número de tópicos. Informa qué método es más estable a lo largo de los números de tópicos.
Términos Clave
| Término | Lo que la gente dice | Lo que realmente significa |
|---|---|---|
| Tópico | Algo de lo que trata el corpus | Una distribución de probabilidad sobre palabras (LDA) o un agrupamiento de documentos similares (BERTopic). |
| Pertenencia mixta | Un documento abarca varios tópicos | LDA asigna a cada documento una distribución sobre todos los tópicos. |
| UMAP | Reducción de dimensionalidad | Aprendizaje de variedad (manifold) que preserva la estructura local; usado en BERTopic. |
| HDBSCAN | Agrupamiento por densidad | Encuentra agrupamientos de tamaño variable; produce etiqueta de "ruido" (-1) para outliers. |
| Coherencia c_v | Métrica de calidad de tópicos | Información mutua puntual promedio de las palabras principales del tópico dentro de ventanas deslizantes. |
Lectura Adicional
- Blei, Ng, Jordan (2003). Latent Dirichlet Allocation — el artículo de LDA.
- Grootendorst (2022). BERTopic: Neural topic modeling with a class-based TF-IDF procedure — el artículo de BERTopic.
- Röder, Both, Hinneburg (2015). Exploring the Space of Topic Coherence Measures — el artículo que introdujo el c_v y sus parientes.
- Documentación de BERTopic — la referencia de producción. Excelentes ejemplos.