AI Knowledge Tree

Topic models: LDA and NMF

Build LDA's plate model, understand Gibbs sampling as the inference engine, and use NMF as a deterministic matrix-factorization alternative.

⏱ 30 min

A document is a mixture of topics; each topic is a distribution over words. Topic models discover these latent themes from a corpus without supervision, grouping words that tend to appear together and assigning each document a mixture of those groups.

The two canonical approaches are Latent Dirichlet Allocation (LDA), a probabilistic graphical model, and Non-negative Matrix Factorization (NMF), a deterministic matrix decomposition[2].

Latent Dirichlet Allocation

The generative story

LDA assumes documents are generated by the following process[1]:

  1. For each document dd, draw a topic mixture θd∼Dir(α)\theta_d \sim \text{Dir}(\alpha).
  2. For each of the KK topics kk, draw a word distribution ϕk∼Dir(β)\phi_k \sim \text{Dir}(\beta).
  3. For each word position nn in document dd: a. Draw a topic assignment zdn∼Categorical(θd)z_{dn} \sim \text{Categorical}(\theta_d). b. Draw a word wdn∼Categorical(ϕzdn)w_{dn} \sim \text{Categorical}(\phi_{z_{dn}}).

The Dirichlet priors α\alpha and β\beta control sparsity: small α\alpha produces documents that concentrate on few topics; small β\beta produces topics that concentrate on few words.

Plate notation

Shaded nodes (ww) are observed; all others are latent.

Inference: collapsed Gibbs sampling

Exact posterior inference is intractable; the standard approach is collapsed Gibbs sampling, which integrates out θ\theta and ϕ\phi analytically and samples only the topic assignments z\mathbf{z}.

The conditional posterior for a single assignment zdn=kz_{dn} = k (given all others):

p(zdn=k∣z−dn,w)∝ndk−dn+αnd−dn+Kα⏟document-topic affinity⋅nkw−dn+βnk−dn+Vβ⏟topic-word affinity,p(z_{dn} = k \mid \mathbf{z}_{-dn}, \mathbf{w}) \propto \underbrace{\frac{n_{dk}^{-dn} + \alpha}{n_d^{-dn} + K\alpha}}_{\text{document-topic affinity}} \cdot \underbrace{\frac{n_{kw}^{-dn} + \beta}{n_k^{-dn} + V\beta}}_{\text{topic-word affinity}},

where ndk−dnn_{dk}^{-dn} is the number of words in document dd assigned to topic kk (excluding position nn), and nkw−dnn_{kw}^{-dn} is the number of times word ww is assigned to topic kk (excluding position nn).

Each iteration cycles over every word in the corpus and resamples its topic assignment. After burn-in, the averaged counts estimate θ\theta and ϕ\phi.

Reading the output

After convergence:

  • ϕk\phi_k: distribution over vocabulary for topic kk. The top-10 words characterize the topic.
  • θd\theta_d: distribution over topics for document dd.
import numpy as np
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.feature_extraction.text import CountVectorizer

docs = [
    "The president signed the new healthcare bill into law today.",
    "Stock markets rose sharply on strong earnings reports.",
    "Scientists discover a new species of deep-sea fish.",
    "Senate debates immigration reform and border security funding.",
    "Tech company reports record profits driven by cloud services.",
    "Marine biologists study coral reef bleaching in the Pacific.",
]

vectorizer = CountVectorizer(stop_words="english")
X = vectorizer.fit_transform(docs)
vocab = vectorizer.get_feature_names_out()

lda = LatentDirichletAllocation(n_components=3, random_state=0, max_iter=100)
lda.fit(X)

for k, phi in enumerate(lda.components_):
    top_words = [vocab[i] for i in phi.argsort()[-5:][::-1]]
    print(f"Topic {k}: {', '.join(top_words)}")

Non-negative Matrix Factorization

NMF factorizes the document-term matrix X∈R≥0D×V\mathbf{X} \in \mathbb{R}^{D \times V}_{\ge 0} into two non-negative factors:

X≈WH,W∈R≥0D×K,H∈R≥0K×V.\mathbf{X} \approx \mathbf{W} \mathbf{H}, \quad \mathbf{W} \in \mathbb{R}^{D \times K}_{\ge 0},\quad \mathbf{H} \in \mathbb{R}^{K \times V}_{\ge 0}.
  • Hk,:\mathbf{H}_{k,:}: the word distribution for topic kk (analogous to ϕk\phi_k in LDA).
  • Wd,:\mathbf{W}_{d,:}: the topic weight vector for document dd (analogous to θd\theta_d).

Non-negativity forces additive, parts-based decompositions: topics can only combine positively, which produces interpretable components.

Objective: minimize the Frobenius reconstruction error with a multiplicative update:

min⁡W,H≥0∥X−WH∥F2.\min_{\mathbf{W}, \mathbf{H} \ge 0} \|\mathbf{X} - \mathbf{W}\mathbf{H}\|_F^2.

Multiplicative update rules (Lee and Seung, 2001):

H←H⊙W⊤XW⊤WH,W←W⊙XH⊤WHH⊤.\mathbf{H} \leftarrow \mathbf{H} \odot \frac{\mathbf{W}^\top \mathbf{X}}{\mathbf{W}^\top \mathbf{W} \mathbf{H}}, \quad \mathbf{W} \leftarrow \mathbf{W} \odot \frac{\mathbf{X} \mathbf{H}^\top}{\mathbf{W} \mathbf{H} \mathbf{H}^\top}.

Division and multiplication are elementwise. These updates guarantee non-negativity is preserved and the objective never increases.

from sklearn.decomposition import NMF

nmf = NMF(n_components=3, random_state=0, max_iter=500)
W = nmf.fit_transform(X)
H = nmf.components_

for k in range(3):
    top_words = [vocab[i] for i in H[k].argsort()[-5:][::-1]]
    print(f"NMF topic {k}: {', '.join(top_words)}")

LDA vs NMF

PropertyLDANMF
FrameworkProbabilistic graphical modelMatrix factorization
InferenceMCMC (Gibbs) or variational EMMultiplicative updates
InterpretabilityPrincipled uncertaintySimpler, faster
Topic coherenceOften betterComparable on short docs
ScalabilitySlowerFaster

Exercises

Exercise 1. In collapsed Gibbs sampling, the unnormalized resampling weight for assigning word ww in document dd to topic kk is the product of a document-topic affinity and a topic-word affinity. A corpus has K=2K = 2 topics, V=5V = 5 vocabulary words, hyperparameters α=0.1\alpha = 0.1, β=0.1\beta = 0.1. Excluding the current word, document dd has counts nd,0−dn=4n_{d,0}^{-dn} = 4, nd,1−dn=0n_{d,1}^{-dn} = 0 (so nd−dn=4n_d^{-dn} = 4); the current word ww has topic counts n0,w−dn=3n_{0,w}^{-dn} = 3, n1,w−dn=0n_{1,w}^{-dn} = 0, with topic totals n0−dn=20n_0^{-dn} = 20, n1−dn=20n_1^{-dn} = 20. Compute the unnormalized weight for each topic and the resulting probability of topic 0.

Solution

Step 1. Apply the formula ndk−dn+αnd−dn+Kα⋅nkw−dn+βnk−dn+Vβ\frac{n_{dk}^{-dn} + \alpha}{n_d^{-dn} + K\alpha} \cdot \frac{n_{kw}^{-dn} + \beta}{n_k^{-dn} + V\beta} for topic 0. Document term: 4+0.14+2(0.1)=4.14.2\frac{4 + 0.1}{4 + 2(0.1)} = \frac{4.1}{4.2}. Word term: 3+0.120+5(0.1)=3.120.5\frac{3 + 0.1}{20 + 5(0.1)} = \frac{3.1}{20.5}.

weight0=4.14.2⋅3.120.5=0.9762⋅0.1512=0.1476.\text{weight}_0 = \frac{4.1}{4.2} \cdot \frac{3.1}{20.5} = 0.9762 \cdot 0.1512 = 0.1476.

Step 2. For topic 1: document term 0+0.14.2=0.14.2\frac{0 + 0.1}{4.2} = \frac{0.1}{4.2}, word term 0+0.120+0.5=0.120.5\frac{0 + 0.1}{20 + 0.5} = \frac{0.1}{20.5}.

weight1=0.14.2⋅0.120.5=0.02381⋅0.004878=0.0001161.\text{weight}_1 = \frac{0.1}{4.2} \cdot \frac{0.1}{20.5} = 0.02381 \cdot 0.004878 = 0.0001161.

Step 3. Normalize:

p(z=0)=0.14760.1476+0.0001161≈0.99921.p(z = 0) = \frac{0.1476}{0.1476 + 0.0001161} \approx 0.99921.

The word is almost certainly assigned to topic 0: both the document (already dominated by topic 0) and the topic-word counts (the word has been seen in topic 0, never in topic 1) pull the same way, and the small α,β\alpha, \beta priors leave only a vanishing chance for topic 1.

Exercise 2. NMF minimizes ∥X−WH∥F2\|\mathbf{X} - \mathbf{W}\mathbf{H}\|_F^2 with the multiplicative update H←H⊙W⊤XW⊤WH\mathbf{H} \leftarrow \mathbf{H} \odot \frac{\mathbf{W}^\top \mathbf{X}}{\mathbf{W}^\top \mathbf{W} \mathbf{H}}. Show that if X\mathbf{X}, W\mathbf{W}, and H\mathbf{H} start non-negative, the update keeps H\mathbf{H} non-negative, and that a fixed point of the update satisfies the stationarity condition (W⊤X)kj=(W⊤WH)kj(\mathbf{W}^\top \mathbf{X})_{kj} = (\mathbf{W}^\top \mathbf{W} \mathbf{H})_{kj} wherever Hkj>0\mathbf{H}_{kj} > 0.

Solution

Step 1. The update multiplies each entry Hkj\mathbf{H}_{kj} by the ratio (W⊤X)kj(W⊤WH)kj\frac{(\mathbf{W}^\top \mathbf{X})_{kj}}{(\mathbf{W}^\top \mathbf{W} \mathbf{H})_{kj}}. With X≥0\mathbf{X} \ge 0 and W≥0\mathbf{W} \ge 0, the numerator W⊤X\mathbf{W}^\top \mathbf{X} is a product of non-negative matrices, so it is non-negative; likewise the denominator W⊤WH≥0\mathbf{W}^\top \mathbf{W} \mathbf{H} \ge 0. A non-negative Hkj\mathbf{H}_{kj} times a non-negative ratio stays non-negative, so non-negativity is preserved at every iteration.

Step 2. At a fixed point, the update leaves H\mathbf{H} unchanged: Hkj=Hkj⋅(W⊤X)kj(W⊤WH)kj\mathbf{H}_{kj} = \mathbf{H}_{kj} \cdot \frac{(\mathbf{W}^\top \mathbf{X})_{kj}}{(\mathbf{W}^\top \mathbf{W} \mathbf{H})_{kj}}.

Step 3. Wherever Hkj>0\mathbf{H}_{kj} > 0, divide both sides by Hkj\mathbf{H}_{kj} to get 1=(W⊤X)kj(W⊤WH)kj1 = \frac{(\mathbf{W}^\top \mathbf{X})_{kj}}{(\mathbf{W}^\top \mathbf{W} \mathbf{H})_{kj}}, i.e. (W⊤X)kj=(W⊤WH)kj(\mathbf{W}^\top \mathbf{X})_{kj} = (\mathbf{W}^\top \mathbf{W} \mathbf{H})_{kj}. This is exactly the first-order stationarity (KKT) condition: the gradient of the Frobenius objective with respect to Hkj\mathbf{H}_{kj} is −2(W⊤X−W⊤WH)kj-2(\mathbf{W}^\top \mathbf{X} - \mathbf{W}^\top \mathbf{W}\mathbf{H})_{kj}, which vanishes on the support of H\mathbf{H} at a fixed point.

Exercise 3 (code). Implement NMF with the multiplicative updates in pure numpy on a small non-negative document-term matrix. Run 200 iterations and confirm the factors stay non-negative and the Frobenius reconstruction error decreases monotonically.

Solution
python · numpy
import numpy as np

rng = np.random.default_rng(0)
D, V, K = 6, 8, 3
# A non-negative document-term matrix built from latent parts so structure exists
W_true = rng.random((D, K))
H_true = rng.random((K, V))
X = W_true @ H_true

W = rng.random((D, K)) + 1e-3
H = rng.random((K, V)) + 1e-3
eps = 1e-9

errors = []
for _ in range(200):
    H = H * (W.T @ X) / (W.T @ W @ H + eps)
    W = W * (X @ H.T) / (W @ H @ H.T + eps)
    errors.append(np.linalg.norm(X - W @ H))

errors = np.array(errors)
assert (W >= 0).all() and (H >= 0).all()         # non-negativity preserved
# Error is non-increasing (allow a tiny numerical slack from the eps guard)
assert (np.diff(errors) <= 1e-6).all()
assert errors[-1] < errors[0]
print("start err:", round(errors[0], 4), " end err:", round(errors[-1], 6))

The multiplicative updates keep both factors in the non-negative orthant and drive the reconstruction error down step by step, the empirical face of the monotone-decrease guarantee.

Both produce interpretable topics when KK is in the range 10-100 and documents are reasonably long. Modern LLMs have largely replaced topic models for semantic search (embeddings from E2 are more expressive), but topic models remain useful for exploratory analysis at scale with no labeled data.


References

  1. [1]Blei, D. M., Ng, A. Y., & Jordan, M. I. (2003). "Latent Dirichlet allocation". JMLR, 3, 993-1022. The generative model and variational inference for LDA.↩
  2. [2]Jurafsky, D., & Martin, J. H. (2024). Speech and Language Processing (3rd ed. draft). Topic models and vector-space document representations.↩