LDA (Latent Dirichlet Allocation): Principles, Examples, and Python Implementation

LDA (Latent Dirichlet Allocation) : Guide Complet — Principes, Exemples et Implémentation Python

LDA (Latent Dirichlet Allocation): Complete Guide — Principles, Examples, and Python Implementation

Summary

LDA (Latent Dirichlet Allocation) is a generative probabilistic model used for topic modeling in natural language processing (NLP). It enables automatic discovery of latent themes present in a document corpus, without any manual annotation. Each document is represented as a probabilistic mixture of several topics, and each topic is itself a probability distribution over the vocabulary. LDA is based on the Dirichlet distribution and uses approximate inference methods such as Gibbs sampling or variational Bayes to estimate hidden parameters from observed data.


Mathematical Principle: The Generative Model

LDA is a Bayesian generative model. Its fundamental idea is as follows: each document was “generated” by a random process that mixes several topics according to certain proportions. To understand LDA, one must describe this generation process in reverse: we observe the documents, and we attempt to infer a posteriori the topic structure that produced them.

The Generative Process

Here is the complete generative model of LDA, document by document:

  1. Generate word distributions per topic: for each topic k ∈ {1, …, K}, draw a distribution over words
    φ_k ~ Dirichlet(β)
    where β is the concentration parameter (often a symmetric vector β·1).
  2. For each document d ∈ {1, …, D}:
    – Draw the document’s topic distribution:
    θ_d ~ Dirichlet(α)
    where α is the prior concentration parameter on topic mixtures.
    – For each word n in document d (total of N_d words):

    1. Draw a topic assignment:
      z_{d,n} ~ Multinomial(θ_d)
    2. Draw the observed word:
      w_{d,n} ~ Multinomial(φ_{z_{d,n}})

The Probabilistic Graph

LDA has a conditional structure represented by a graphical model:

  • α (hyperparameter) → θ_d (document topic distribution) → z_{d,n} (assignment of each word)
  • β (hyperparameter) → φ_k (topic word distribution) → w_{d,n} (observed word)

The only observed variables are the words w_{d,n}. Everything else — the distributions θd, φ_k and the assignments z{d,n} — is latent and must be inferred.

Inference: Gibbs Sampling and Variational Bayes

Exact computation of the posterior distribution p(θ, φ, z | w, α, β) is intractable for realistically sized corpora, because it requires integrating over a gigantic combinatorial space (each word in each document can belong to any topic). Two approximation methods are commonly used:

Collapsed Gibbs Sampling (CGS) — This is the historical method. We iterate over each word in each document, and resample its topic assignment z_{d,n} conditionally on all other assignments:

p(z_{d,n} = k | z_{¬(d,n)}, w, α, β) ∝
    (n_{d,k}^{¬(d,n)} + α_k) × (n_{k,w}^{¬(d,n)} + β_w) / (n_k^{¬(d,n)} + Σ β)

where n_{d,k} is the number of words in document d assigned to topic k, and n_{k,w} is the number of times word w appears in topic k.

Variational Bayes (VBI) — Introduced by Blei et al. (2003), this method constructs a variational distribution q(θ, φ, z) that minimizes the Kullback-Leibler divergence with the true posterior distribution. It is an EM (Expectation-Maximization) type algorithm that alternates between updating variational parameters and the lower bound (ELBO). sklearn.LatentDirichletAllocation uses variational Bayes by default.


Intuition: LDA Explained Simply

Imagine you are a chef and you taste a dish without knowing the recipe. Your task is to guess which ingredients were used and in what proportions.

In this analogy:
– The dish is a document.
– The basic ingredients (salt, tomatoes, basil, cheese…) are the topics.
– Each dish is a mixture of several ingredients in different proportions: a pizza contains a lot of tomato and cheese, a little basil; a margherita contains mostly mozzarella and tomato.
– Similarly, a news document about energy contains a little “politics” topic, a little “technology” topic, and a big “environment” topic.

The goal of LDA is exactly this: analyze a large number of “dishes” (documents) to discover what the fundamental ingredients (topics) are and reconstruct the recipe (topic mixture) of each document.

And like the chef who must guess the recipe without knowing it, LDA knows neither the topics nor their proportions in advance. It discovers them automatically by observing word co-occurrences across all documents. If the words “vote”, “ballot”, and “parliament” always appear together, LDA groups these words into a “politics” topic.


Python Implementation of LDA

1. LDA with scikit-learn (LatentDirichletAllocation)

Scikit-learn offers an efficient implementation of variational Bayes for LDA. It is the most accessible approach to get started.

from sklearn.decomposition import LatentDirichletAllocation
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np

# Data: small document corpus
documents = [
    "Citizens' voting influences the composition of parliament.",
    "Artificial intelligence is revolutionizing the technology sector.",
    "Listed stocks reach historic highs.",
    "Legislative elections reshape the political landscape.",
    "Machine learning transforms the digital industry.",
    "The stock market reacts to ECB announcements.",
    "The government proposes a new tax reform.",
    "Deep neural networks surpass classical methods.",
    "Investors fear a correction in the financial market.",
    "The president announces measures for public education."
]

# Step 1: Vectorization (bag-of-words)
vectorizer = CountVectorizer(
    max_features=500,       # Limit vocabulary
    stop_words=None,         # Adapt according to language
    min_df=1,
    max_df=0.95             # Eliminate overly frequent words
)
X = vectorizer.fit_transform(documents)
vocab = vectorizer.get_feature_names_out()

# Step 2: Apply LDA
n_topics = 3
lda = LatentDirichletAllocation(
    n_components=n_topics,
    learning_method='batch',    # 'batch' or 'online'
    learning_decay=0.7,
    doc_topic_prior=None,       # Auto (1/n_topics)
    topic_word_prior=None,      # Auto (1/n_topics)
    max_iter=20,
    random_state=42,
    n_jobs=-1,                  # Parallelization
    verbose=1
)

# Fitting
doc_topic_dist = lda.fit_transform(X)

# Step 3: Display topics
def display_topics(model, feature_names, n_top_words=8):
    for idx, topic in enumerate(model.components_):
        print(f"\nTopic {idx + 1}:")
        top_words_idx = topic.argsort()[:-n_top_words - 1:-1]
        top_words = [feature_names[i] for i in top_words_idx]
        print("  " + ", ".join(top_words))

display_topics(lda, vocab, n_top_words=6)

2. Document Distribution Across Topics

Once the model is trained, we can examine the thematic distribution of each document:

# Proportion of each topic in each document
print("Topic distribution per document:")
for d_idx, dist in enumerate(doc_topic_dist):
    dominant_topic = dist.argmax()
    proportions = ", ".join(f"T{i}={v:.2f}" for i, v in enumerate(dist))
    print(f"  Doc {d_idx + 1}: {proportions} -> Dominant topic: T{dominant_topic + 1}")

3. Choosing the Number of Topics with Perplexity

Perplexity measures the quality of a probabilistic model: the lower it is, the better the model generalizes. It is the classic tool for choosing K (the number of topics).

import matplotlib.pyplot as plt

perplexities = []
topic_range = range(2, 8)

for k in topic_range:
    lda_k = LatentDirichletAllocation(
        n_components=k,
        random_state=42,
        max_iter=20,
        verbose=0
    )
    lda_k.fit(X)
    perplexity = lda_k.perplexity(X)
    perplexities.append(perplexity)
    print(f"K={k}: perplexity = {perplexity:.2f}")

plt.figure(figsize=(8, 5))
plt.plot(topic_range, perplexities, 'bo-')
plt.xlabel("Number of topics (K)")
plt.ylabel("Perplexity")
plt.title("Perplexity as a function of the number of topics")
plt.grid(True, alpha=0.3)
plt.show()

4. Interactive Visualization with pyLDAvis

# pip install pyLDAvis gensim
import gensim.corpora as corpora
import gensim
import pyLDAvis
import pyLDAvis.gensim_models as gensimvis

# Create dictionary and corpus in gensim format
texts = [doc.lower().split() for doc in documents]
dictionary = corpora.Dictionary(texts)
corpus = [dictionary.doc2bow(text) for text in texts]

# Train gensim LDA
lda_gensim = gensim.models.LdaModel(
    corpus,
    id2word=dictionary,
    num_topics=3,
    random_state=42,
    passes=20
)

# Visualize with pyLDAvis
vis = gensimvis.prepare(lda_gensim, corpus, dictionary)
pyLDAvis.display(vis)

pyLDAvis offers an interactive visualization: a 2D scatter plot of topics (semantic proximity via MDS) and, on clicking a topic, a histogram of the most associated words. It is the reference tool for exploring and interpreting LDA results.


LDA Hyperparameters

The choice of hyperparameters profoundly influences the quality of the discovered topics:

Hyperparameter Description Typical Value
n_components Number of topics K 5–100 depending on corpus size
learning_method 'batch' (classical VBI) or 'online' (incremental VBI) 'online' for large corpora
learning_decay Learning rate for the online method 0.5–0.9 (default: 0.7)
doc_topic_prior (α) Controls topic dispersion per document 1/K (default if None: sparse α)
topic_word_prior (β) Controls word dispersion per topic 1/K (default if None: concentrated φ)
max_iter Maximum number of iterations 10–50 (monitor convergence via log-likelihood)
evaluate_every Perplexity evaluation frequency 0–10

The Impact of α (alpha)

  • Low α (< 1/K): each document concentrates on few topics (sharp distribution).
  • High α (> 1/K): each document contains many topics (uniform distribution).
  • Practical rule: start with α = 50/K and β = 0.1 for short documents; adjust according to coherence.

The Impact of β (beta)

  • Low β: each topic uses few distinctive words (precise but potentially fragile topics).
  • High β: words are evenly distributed across topics (less distinctive topics).
  • The default value 1/K generally works well to start with.

Advantages of LDA

  1. Direct interpretability: Each topic is a weighted list of human-readable words. This is a major asset for exploratory analysis and communicating results.
  2. Soft representation: Unlike hard clustering (k-means), a document can belong simultaneously to several topics with degrees of membership. This reflects the reality of texts that often deal with multiple subjects.
  3. Solid theoretical foundation: The Bayesian generative model offers a rigorous probabilistic framework with statistical guarantees on inference.
  4. Unsupervised: No manual annotation is needed. LDA discovers thematic structures from raw data.
  5. Generalization: The model can infer the topic distribution for new documents not seen during training (transform in scikit-learn).
  6. Scalability: The online LDA variant enables processing corpora of millions of documents without loading all data into memory.

Limitations of LDA

  1. Word independence (bag-of-words): LDA ignores word order and syntax. Contextual embedding models (BERT, Word2Vec) capture fine-grained semantics better.
  2. Choice of K: There is no reliable automatic method to determine the optimal number of topics. Perplexity and coherence provide indications, but the choice remains semi-empirical.
  3. Imperfect semantic coherence: Automatic topics do not always correspond to categories intuitive to a human. A topic may mix concepts with no apparent connection.
  4. Exchangeability assumption: LDA assumes words are exchangeable within a document. This assumption is strong and models neither narrative structure nor relationships between words.
  5. Sensitivity to preprocessing: The result depends heavily on cleaning (stop-words, lemmatization, stemming, removal of rare words). A poorly calibrated NLP pipeline produces unusable topics.
  6. No hierarchy: Standard LDA does not model the natural hierarchy of topics (a “sports” topic contains subtopics “football”, “tennis”). For this, extensions like hLDA (hierarchical LDA) are needed.
  7. Sensitivity to random initializations: Different random seeds produce substantially different topics. It is recommended to run LDA several times and retain the result with the best log-likelihood.

4 Concrete Use Cases

Use Case 1: Exploration of News Corpora

A media outlet has 50,000 articles published over 10 years. LDA enables automatic discovery of the major editorial lines, identification of emerging new trends, and visualization of the newspaper’s thematic evolution over time. Combined with temporal segmentation, one obtains a tracking of the emergence and decline of each subject.

Use Case 2: Customer Ticket Triage and Routing

A company receives 3,000 support tickets per week. LDA automatically identifies the major problem categories (billing, technical bug, feature request, commercial question). Each ticket is assigned its topic distribution, enabling automatic routing to the competent department without prior manual labeling.

Use Case 3: Bibliographic Research Analysis

A researcher wants to quickly understand the research directions of a scientific field. By applying LDA to 10,000 article abstracts extracted from PubMed or arXiv, they identify the dominant scientific currents, spot under-explored areas (weak topics = research opportunities), and map the evolution of themes over several years.

Use Case 4: Opinion Analysis and Competitive Intelligence

By analyzing 100,000 customer reviews on e-commerce platforms, LDA reveals the thematic axes of satisfaction and dissatisfaction (product quality, delivery, customer service, value for money, design). Applying the same model to competitors’ reviews yields a comparative mapping of perceived strengths and weaknesses in the market.


Best Practices for Successful LDA

  1. Aggressive text cleaning: Remove stop-words, apply lemmatization, filter out overly rare words (min_df ≥ 2) and overly frequent words (max_df ≤ 0.95).
  2. Test multiple values of K: Run LDA for K ∈ {5, 10, 20, 50, 100} and compare perplexity as well as topic coherence (measured with C_v or UMass).
  3. Run multiple seeds: Launch 5–10 runs with different seeds and retain the one that maximizes log-likelihood.
  4. Validate with a human: A topic is only useful if it is interpretable. Ask a domain expert to read the top words of each topic and judge their relevance.
  5. Combine with other methods: LDA alone can be fragile. Combine it with NMF (Non-negative Matrix Factorization) to validate topic stability, or with Word2Vec to enrich the semantic representation.

See Also

About Salah YAHIAOUI

Salah YAHIAOUI publie sur The Pingouin des tutoriels Python et des décryptages sur l’intelligence artificielle. Les cas pratiques récents précisent leurs données, leurs tests et leurs limites. À propos, méthode éditoriale et contact.

View all posts by Salah YAHIAOUI →