LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 196 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/9
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningBayesian Inference from EvidenceStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/15
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageStatistical Learning and GeneralizationConformal Prediction and UncertaintyClustering and PCAProbabilistic Generative ModelsCore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingCausal Inference and CounterfactualsPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/25
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernancePrivacy-Preserving Machine LearningData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/17
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningTraining Run OperationsReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/31
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceAccelerator Architecture Field GuideCorrect GPU Profiling and BenchmarkingGPU Kernel Performance Engineering LabGPU Parallel Primitives LabGPU Compilers and Kernel LanguagesGEMM From Naive CUDA to Tensor CoresLow-Precision GPU ProgrammingGPU Runtime Execution LabAI-Generated Kernel EngineeringModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingGPU Failure OperationsDistributed Inference Data PlaneServing Benchmark EngineeringA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnML Algorithms & EvaluationProbabilistic Generative Models
🏛️MediumModel Architecture

Probabilistic Generative Models

Fit a Gaussian mixture from scratch, calculate soft responsibilities, trace expectation-maximization via ELBO lower-bound ascent, and bridge to Variational Autoencoders.

18 min read
Learning path
Step 40 of 196 in the full curriculum
Clustering and PCACore Retrieval Algorithms

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

A latency dashboard reports a mean of 60 milliseconds across production traffic. The operations dashboard displays a green status, but when you inspect the raw request logs [28, 30, 32, 86, 90, 94], not a single request actually took 60 ms. Three queries finished in roughly 30 ms (in-memory cache hits), while the remaining three stalled near 90 ms (unindexed disk queries).

Fitting a single standard Gaussian to this sample produces a bell curve centered at 60 ms: the exact region where the probability of observing an actual request is near zero. A hard clustering algorithm like K-means assigns each point to a nearest centroid, but it can't quantify how confident it feels about boundary observations. A Gaussian mixture model resolves this by modeling the data as a combination of multiple hidden sub-populations, keeping uncertain membership visible through probability distributions.[1]Reference 1Pattern Recognition and Machine Learning.https://www.microsoft.com/en-us/research/publication/pattern-recognition-machine-learning/

Density chart on a 0 to 120 ms latency scale: the fitted mixture peaks near 30 and 90 ms, while the single Gaussian peaks at the shared mean of 60 ms. Individual fast and slow component densities show the underlying mixture decomposition. A rug strip marks the six observations at 28, 30, 32, 86, 90, and 94 ms. Three summary cards detail the component parameters and the soft responsibility split at 50 ms.
Both fitted models have mean 60 ms. The mixture puts its density peaks near the observed groups while showing individual fast and slow components. The rug shows the six measurements; curves show model densities, not extra points.

Generative versus discriminative modeling

Machine learning systems answer two distinct modeling questions depending on whether they estimate conditional or joint distributions.

Discriminative models estimate the conditional distribution P(Y∣X)P(Y \mid X)P(Y∣X). Given observed features XXX (like request latency or query size), a discriminative classifier determines the probability of class label YYY (such as cache hit versus database fallback). Discriminative models focus strictly on the decision boundary separating classes. If you feed them corrupted inputs or impossible latencies like -500 ms, they still output confident class probabilities because they never modeled what valid inputs look like. They can't generate new data points, evaluate input plausibility, or identify missing features.

Generative models estimate how data came to exist in the world. They learn either the joint distribution P(X,Y)=P(Y)P(X∣Y)P(X, Y) = P(Y)P(X \mid Y)P(X,Y)=P(Y)P(X∣Y) or, in the unsupervised setting, the marginal data distribution P(X)P(X)P(X). By capturing the data-generating mechanism, a generative model evaluates whether a new observation looks probable under normal traffic, generates synthetic samples by sampling from the learned distribution, and accounts for unobserved mechanisms.

In real-world request logs, we don't have labeled class tags YYY telling us which infrastructure component served each query. We only observe raw latencies XXX. To explain multi-modal observations without human labels, generative models introduce unobserved hidden variables: latent variables.

Latent variables and the intractable log-sum

Start with the six observed request latencies: [28, 30, 32, 86, 90, 94]. Their arithmetic mean is 60 ms, but the numbers naturally cluster into two distinct groups.

RequestLatencyPlausible hidden mechanism
A28 msIn-memory cache hit
B30 msIn-memory cache hit
C32 msIn-memory cache hit
D86 msDatabase disk scan
E90 msDatabase disk scan
F94 msDatabase disk scan

A probabilistic generative model formulates a two-stage story for each observation:

Diagram showing Select latent component Z = k, Component distribution N(mu_k, sigma_k^2), and Sample observed latency X.
Select latent component Z = k, Component distribution N(mu_k, sigma_k^2), and Sample observed latency X.

First, nature selects an unobserved latent component zi=k∈{1,…,K}z_i = k \in \{1, \dots, K\}zi​=k∈{1,…,K} with prior probability P(zi=k)=πkP(z_i = k) = \pi_kP(zi​=k)=πk​, where ∑k=1Kπk=1\sum_{k=1}^K \pi_k = 1∑k=1K​πk​=1 and πk≥0\pi_k \ge 0πk​≥0. Second, conditioned on that choice, nature samples the observable latency xix_ixi​ from that component's density p(xi∣zi=k,θ)=N(xi∣μk,σk2)p(x_i \mid z_i = k, \theta) = \mathcal{N}(x_i \mid \mu_k, \sigma_k^2)p(xi​∣zi​=k,θ)=N(xi​∣μk​,σk2​).

Because the request log only records the observed values X={x1,…,xn}X = \{x_1, \dots, x_n\}X={x1​,…,xn​} while the latent choices Z={z1,…,zn}Z = \{z_1, \dots, z_n\}Z={z1​,…,zn​} remain hidden, evaluating the probability of an observed point requires marginalizing over all possible latent assignments:

p(xi∣θ)=∑k=1Kp(xi,zi=k∣θ)=∑k=1Kπk N(xi∣μk,σk2).p(x_i \mid \theta) = \sum_{k=1}^{K} p(x_i, z_i = k \mid \theta) = \sum_{k=1}^{K} \pi_k\,\mathcal{N}(x_i \mid \mu_k, \sigma_k^2).p(xi​∣θ)=k=1∑K​p(xi​,zi​=k∣θ)=k=1∑K​πk​N(xi​∣μk​,σk2​).

Assuming requests arrive independently, maximum likelihood estimation aims to find parameters θ={πk,μk,σk2}k=1K\theta = \{\pi_k, \mu_k, \sigma_k^2\}_{k=1}^Kθ={πk​,μk​,σk2​}k=1K​ that maximize the joint probability of all observed data. Taking the logarithm converts the product of independent probabilities into a sum:

ℓ(θ)=log⁡p(X∣θ)=∑i=1nlog⁡(∑k=1Kπk N(xi∣μk,σk2)).\ell(\theta) = \log p(X \mid \theta) = \sum_{i=1}^{n} \log \left( \sum_{k=1}^{K} \pi_k\,\mathcal{N}(x_i \mid \mu_k, \sigma_k^2) \right).ℓ(θ)=logp(X∣θ)=i=1∑n​log(k=1∑K​πk​N(xi​∣μk​,σk2​)).

Notice the mathematical barrier: the summation across components sits inside the logarithm.

In standard maximum likelihood without latent variables, taking the logarithm moves directly onto exponential family densities. The log cancels the exponential term, creating simple linear or quadratic expressions whose derivatives set cleanly to zero. With latent variables, the sum inside the log prevents the logarithm from reaching individual Gaussian terms. All parameters across all components become nonlinearly coupled. Setting ∇θℓ(θ)=0\nabla_\theta \ell(\theta) = 0∇θ​ℓ(θ)=0 yields no closed-form analytical solution. If ZZZ were continuous rather than discrete, that inner sum would become an intractable high-dimensional integral.

Soft responsibilities in Gaussian mixtures

If we knew which latent component generated each request, fitting each Gaussian would be straightforward: compute the sample mean and variance for each group separately. If we already had the true parameters θ\thetaθ, assigning requests to components would also be straightforward Bayes' rule.

Because we have neither, we start by evaluating provisional parameters. Consider two provisional components: a fast component at μ1=30\mu_1 = 30μ1​=30 ms, a slow component at μ2=90\mu_2 = 90μ2​=90 ms, equal standard deviations σ1=σ2=20\sigma_1 = \sigma_2 = 20σ1​=σ2​=20 ms, and equal prior weights π1=π2=0.5\pi_1 = \pi_2 = 0.5π1​=π2​=0.5.

Now evaluate a boundary request that took 50 ms. The fast Gaussian's density at 50 ms is roughly 0.01210 per ms; the slow Gaussian's density is roughly 0.00270 per ms. Weighting each contribution by prior probability 0.5 gives 0.00605 and 0.00135.

Normalizing these two numbers yields the posterior probability that component kkk generated observation xix_ixi​, known as the component's responsibility γik\gamma_{ik}γik​:[1]Reference 1Pattern Recognition and Machine Learning.https://www.microsoft.com/en-us/research/publication/pattern-recognition-machine-learning/

γik=P(zi=k∣xi,θ)=πk N(xi∣μk,σk2)∑j=1Kπj N(xi∣μj,σj2).\gamma_{ik} = P(z_i = k \mid x_i, \theta) = \frac{\pi_k\,\mathcal{N}(x_i \mid \mu_k, \sigma_k^2)}{\sum_{j=1}^{K} \pi_j\,\mathcal{N}(x_i \mid \mu_j, \sigma_j^2)}.γik​=P(zi​=k∣xi​,θ)=∑j=1K​πj​N(xi​∣μj​,σj2​)πk​N(xi​∣μk​,σk2​)​.

For our 50 ms request, the fast responsibility is 0.00605/(0.00605+0.00135)≈0.8180.00605 / (0.00605 + 0.00135) \approx 0.8180.00605/(0.00605+0.00135)≈0.818, and the slow responsibility is 0.00135/0.00740≈0.1820.00135 / 0.00740 \approx 0.1820.00135/0.00740≈0.182. The next code snippet calculates these values without intermediate rounding:

calculate-mixture-responsibilities.py
1from math import exp, pi, sqrt 2 3def gaussian_density(value: float, mean: float, deviation: float) -> float: 4 exponent = -((value - mean) ** 2) / (2 * deviation**2) 5 return exp(exponent) / (sqrt(2 * pi) * deviation) 6 7latency = 50.0 8weighted_fast = 0.5 * gaussian_density(latency, mean=30.0, deviation=20.0) 9weighted_slow = 0.5 * gaussian_density(latency, mean=90.0, deviation=20.0) 10total = weighted_fast + weighted_slow 11 12fast = weighted_fast / total 13slow = weighted_slow / total 14assert abs(fast + slow - 1.0) < 1e-12 15print(f"fast responsibility: {fast:.3f}") 16print(f"slow responsibility: {slow:.3f}")
Output
1fast responsibility: 0.818 2slow responsibility: 0.182

The fast component receives greater responsibility here because its mean is closer and variances are equal. If components had unequal spreads or priors, the closer mean wouldn't automatically win.

Contrast this soft probabilistic assignment with hard K-means clustering from Clustering, PCA, and Representation Learning. K-means forces an absolute 0 or 1 choice based on Euclidean distance, discarding ambiguity. Mathematically, K-means is the limiting case of a Gaussian mixture model where all component covariances are identical and spherical (Σk=σ2I\Sigma_k = \sigma^2 IΣk​=σ2I) as variance shrinks to zero (σ2→0\sigma^2 \to 0σ2→0). In that zero-variance limit, the softmax posterior collapses into a hard argmax. By retaining non-zero variance, a Gaussian mixture preserves the real uncertainty: a 50 ms latency leans fast, but still carries an 18.2% probability of being an unusually speedy database query.

Responsibilities are discrete probabilities summing to one for each observation (∑kγik=1\sum_k \gamma_{ik} = 1∑k​γik​=1). In contrast, p(x∣θ)p(x \mid \theta)p(x∣θ) is a continuous probability density, whose values can exceed 1.0 for narrow spreads and whose integral over an interval gives probability.

A 50-millisecond request receives fast-component responsibility 0.818. What happens to the remaining 0.182, and does the larger number prove that an in-memory cache handled the request?

Answer

The remaining 0.182 belongs to the slow component, so both responsibilities sum to exactly one. The 0.818 score is a conditional probability under the assumed statistical model; no hardware traces or queue states were measured, so it doesn't establish physical causation.

Expectation-Maximization as lower-bound ascent

We need responsibilities to learn parameters, but we need parameters to calculate responsibilities. The Expectation-Maximization (EM) algorithm, introduced by Dempster, Laird, and Rubin (1977)[2]Reference 2Maximum Likelihood from Incomplete Data via the EM Algorithmhttps://doi.org/10.1111/j.2517-6161.1977.tb01600.x, resolves this mutual dependency by alternating two coordinated steps.

Rather than attacking the non-convex log-sum directly, EM introduces an arbitrary probability distribution q(Z)q(Z)q(Z) over the latent variables and constructs a tractable lower bound: the Evidence Lower Bound (ELBO).

Let's derive the lower bound through Jensen's inequality. For any concave function fff, Jensen's inequality guarantees that the function of an expectation is at least the expectation of the function: f(E[Y])≥E[f(Y)]f(\mathbb{E}[Y]) \ge \mathbb{E}[f(Y)]f(E[Y])≥E[f(Y)]. Because the logarithm function is strictly concave, multiplying and dividing by q(Z)q(Z)q(Z) yields:

log⁡p(X∣θ)=log⁡∑Zq(Z)p(X,Z∣θ)q(Z)=log⁡Eq[p(X,Z∣θ)q(Z)]≥Eq[log⁡p(X,Z∣θ)q(Z)].\log p(X \mid \theta) = \log \sum_{Z} q(Z) \frac{p(X, Z \mid \theta)}{q(Z)} = \log \mathbb{E}_q \left[ \frac{p(X, Z \mid \theta)}{q(Z)} \right] \ge \mathbb{E}_q \left[ \log \frac{p(X, Z \mid \theta)}{q(Z)} \right].logp(X∣θ)=logZ∑​q(Z)q(Z)p(X,Z∣θ)​=logEq​[q(Z)p(X,Z∣θ)​]≥Eq​[logq(Z)p(X,Z∣θ)​].

Expanding that expectation defines the Evidence Lower Bound L(q,θ)\mathcal{L}(q, \theta)L(q,θ):[3]Reference 3CS229 Machine Learning Notes: The EM Algorithmhttps://cs229.stanford.edu/notes2022fall/main_notes.pdf

L(q,θ)≡∑Zq(Z)log⁡p(X,Z∣θ)q(Z).\mathcal{L}(q, \theta) \equiv \sum_{Z} q(Z) \log \frac{p(X, Z \mid \theta)}{q(Z)}.L(q,θ)≡Z∑​q(Z)logq(Z)p(X,Z∣θ)​.

We can also express the relationship as an exact algebraic decomposition:

log⁡p(X∣θ)=L(q,θ)+DKL(q(Z)∥p(Z∣X,θ)),\log p(X \mid \theta) = \mathcal{L}(q, \theta) + D_{\mathrm{KL}}(q(Z) \parallel p(Z \mid X, \theta)),logp(X∣θ)=L(q,θ)+DKL​(q(Z)∥p(Z∣X,θ)),

where DKL(q∥p)D_{\mathrm{KL}}(q \parallel p)DKL​(q∥p) is the Kullback-Leibler divergence measuring the divergence between our distribution q(Z)q(Z)q(Z) and the true posterior p(Z∣X,θ)p(Z \mid X, \theta)p(Z∣X,θ):

DKL(q(Z)∥p(Z∣X,θ))=∑Zq(Z)log⁡q(Z)p(Z∣X,θ).D_{\mathrm{KL}}(q(Z) \parallel p(Z \mid X, \theta)) = \sum_{Z} q(Z) \log \frac{q(Z)}{p(Z \mid X, \theta)}.DKL​(q(Z)∥p(Z∣X,θ))=Z∑​q(Z)logp(Z∣X,θ)q(Z)​.

Because KL divergence is always non-negative (DKL≥0D_{\mathrm{KL}} \ge 0DKL​≥0) and equals zero if and only if q(Z)=p(Z∣X,θ)q(Z) = p(Z \mid X, \theta)q(Z)=p(Z∣X,θ), L(q,θ)\mathcal{L}(q, \theta)L(q,θ) forms a valid lower bound on the true marginal log likelihood log⁡p(X∣θ)\log p(X \mid \theta)logp(X∣θ).

Geometric visualization of the Expectation-Maximization algorithm. A true marginal log-likelihood curve ell(theta) is bounded from below by concave Evidence Lower Bound (ELBO) functions. At step t, the E-step tightens the bound so it touches ell(theta) at theta^(t) with zero KL divergence. The M-step moves horizontally to maximize the bound at theta^(t+1). The subsequent E-step lifts the bound to touch the true likelihood again, guaranteeing monotonic ascent.
Expectation-Maximization lower-bound ascent geometry. The E-step eliminates the KL divergence gap, bringing the lower bound into tangent contact with the true log likelihood. The M-step optimizes along the bound, ensuring each full cycle never decreases marginal likelihood.

The algorithm climbs this geometry through coordinate ascent:

  1. E-step (Expectation / Bound Tightening): Hold model parameters fixed at θ(t)\theta^{(t)}θ(t). Set q(Z)=p(Z∣X,θ(t))q(Z) = p(Z \mid X, \theta^{(t)})q(Z)=p(Z∣X,θ(t)), computing the conditional responsibilities γik\gamma_{ik}γik​. This forces DKL(q∥p)=0D_{\mathrm{KL}}(q \parallel p) = 0DKL​(q∥p)=0, causing the lower bound L(q,θ)\mathcal{L}(q, \theta)L(q,θ) to touch the true log likelihood curve tangentially at θ(t)\theta^{(t)}θ(t):

    L(q,θ(t))=log⁡p(X∣θ(t)).\mathcal{L}(q, \theta^{(t)}) = \log p(X \mid \theta^{(t)}).L(q,θ(t))=logp(X∣θ(t)).
  2. M-step (Maximization / Parameter Optimization): Freeze q(Z)q(Z)q(Z) and maximize L(q,θ)\mathcal{L}(q, \theta)L(q,θ) with respect to θ\thetaθ:

    θ(t+1)=arg⁡max⁡θL(q,θ)=arg⁡max⁡θ∑Zq(Z)log⁡p(X,Z∣θ).\theta^{(t+1)} = \arg\max_\theta \mathcal{L}(q, \theta) = \arg\max_\theta \sum_{Z} q(Z) \log p(X, Z \mid \theta).θ(t+1)=argθmax​L(q,θ)=argθmax​Z∑​q(Z)logp(X,Z∣θ).

    Because the logarithm now operates directly on the complete-data joint probability p(X,Z∣θ)p(X, Z \mid \theta)p(X,Z∣θ), the sum inside the log is gone! For Gaussian components, this yields exact closed-form updates.

  3. Monotonicity guarantee: Combining both steps confirms that likelihood never decreases:[2]Reference 2Maximum Likelihood from Incomplete Data via the EM Algorithmhttps://doi.org/10.1111/j.2517-6161.1977.tb01600.x

    log⁡p(X∣θ(t+1))≥L(q,θ(t+1))≥L(q,θ(t))=log⁡p(X∣θ(t)).\log p(X \mid \theta^{(t+1)}) \ge \mathcal{L}(q, \theta^{(t+1)}) \ge \mathcal{L}(q, \theta^{(t)}) = \log p(X \mid \theta^{(t)}).logp(X∣θ(t+1))≥L(q,θ(t+1))≥L(q,θ(t))=logp(X∣θ(t)).

Jensen's inequality ensures the ELBO never exceeds true likelihood. Maximizing that bound over θ\thetaθ in the M-step guarantees progress, while the E-step closes the divergence gap at the current parameter. Together, these properties ensure monotonic non-decrease toward a stationary point, though they don't guarantee reaching the global optimum.

Closed-form updates and numerical stability

For Gaussian mixture models, maximizing ∑Zq(Z)log⁡p(X,Z∣θ)\sum_Z q(Z) \log p(X, Z \mid \theta)∑Z​q(Z)logp(X,Z∣θ) produces intuitive weighted updates for each component kkk:

Nk=∑i=1nγik,πknew=Nkn,μknew=∑i=1nγikxiNk,(σk2)new=∑i=1nγik(xi−μknew)2Nk.N_k = \sum_{i=1}^{n} \gamma_{ik}, \qquad \pi_k^{\text{new}} = \frac{N_k}{n}, \qquad \mu_k^{\text{new}} = \frac{\sum_{i=1}^{n} \gamma_{ik} x_i}{N_k}, \qquad (\sigma_k^2)^{\text{new}} = \frac{\sum_{i=1}^{n} \gamma_{ik} (x_i - \mu_k^{\text{new}})^2}{N_k}.Nk​=i=1∑n​γik​,πknew​=nNk​​,μknew​=Nk​∑i=1n​γik​xi​​,(σk2​)new=Nk​∑i=1n​γik​(xi​−μknew​)2​.

Here NkN_kNk​ represents the effective number of observations assigned to component kkk. The denominator for variance is NkN_kNk​ rather than Nk−1N_k - 1Nk​−1 because this is maximum likelihood estimation, not an unbiased sample variance calculation.

When implementing these updates in software, direct calculation of tiny Gaussian densities leads to floating-point underflow. For points far from a mean, raw evaluation of exp⁡(−0.5(x−μ)2/σ2)\exp(-0.5 (x - \mu)^2 / \sigma^2)exp(−0.5(x−μ)2/σ2) produces zero. When every component underflows, computing γik=0/0\gamma_{ik} = 0 / 0γik​=0/0 results in NaN.

The log-sum-exp trick protects calculations by operating in log space:

log⁡∑k=1Kexp⁡(sk)=smax⁡+log⁡(∑k=1Kexp⁡(sk−smax⁡)),\log \sum_{k=1}^{K} \exp(s_k) = s_{\max} + \log \left( \sum_{k=1}^{K} \exp(s_k - s_{\max}) \right),logk=1∑K​exp(sk​)=smax​+log(k=1∑K​exp(sk​−smax​)),

where smax⁡=max⁡ksks_{\max} = \max_k s_ksmax​=maxk​sk​. Subtracting the maximum log score ensures that the largest term evaluates to exp⁡(0)=1.0\exp(0) = 1.0exp(0)=1.0, preventing underflow during normalization.

The next snippet fits our six observed latencies [28, 30, 32, 86, 90, 94] starting from provisional means at 25 and 95 ms with initial variances of 900 ms²:

fit-two-component-mixture.py
1from math import exp, log, pi 2 3values = [28.0, 30.0, 32.0, 86.0, 90.0, 94.0] 4 5def logsumexp(scores): 6 largest = max(scores) 7 return largest + log(sum(exp(score - largest) for score in scores)) 8 9def log_scores(value, model): 10 weights, means, variances = model 11 return [ 12 log(weight) - 0.5 * (log(2 * pi * variance) + (value - mean)**2 / variance) 13 for weight, mean, variance in zip(weights, means, variances) 14 ] 15 16def log_likelihood(data, model): 17 return sum(logsumexp(log_scores(value, model)) for value in data) 18 19def fit_mixture(data, start_means, steps=8, variance_floor=1.0): 20 assert data and start_means and 0 < variance_floor <= 900.0 21 count_components = len(start_means) 22 model = ([1.0 / count_components] * count_components, 23 list(start_means), [900.0] * count_components) 24 history = [log_likelihood(data, model)] 25 for _ in range(steps): 26 responsibilities = [] 27 for value in data: 28 scores = log_scores(value, model) 29 normalizer = logsumexp(scores) 30 row = [exp(score - normalizer) for score in scores] 31 assert abs(sum(row) - 1.0) < 1e-10 32 responsibilities.append(row) 33 34 weights, means, variances = [], [], [] 35 for component in range(count_components): 36 memberships = [row[component] for row in responsibilities] 37 effective_count = sum(memberships) 38 if effective_count < 1e-12: 39 raise ValueError("Numerically empty component: try another initialization") 40 mean = sum(r * x for r, x in zip(memberships, data)) / effective_count 41 variance = sum(r * (x - mean)**2 for r, x in zip(memberships, data)) / effective_count 42 weights.append(effective_count / len(data)) 43 means.append(mean) 44 variances.append(max(variance, variance_floor)) 45 model = (weights, means, variances) 46 history.append(log_likelihood(data, model)) 47 48 assert all(new >= old - 1e-10 for old, new in zip(history, history[1:])) 49 return model, history 50 51model, history = fit_mixture(values, [25.0, 95.0]) 52print("log likelihood:", [round(value, 3) for value in history]) 53for index, (weight, mean, variance) in enumerate(zip(*model), start=1): 54 print(f"component {index}: weight={weight:.3f}, mean={mean:.1f} ms, variance={variance:.3f} ms²")
Output
1log likelihood: [-29.62, -27.056, -20.641, -17.694, -17.694, -17.694, -17.694, -17.694, -17.694] 2component 1: weight=0.500, mean=30.0 ms, variance=2.667 ms² 3component 2: weight=0.500, mean=90.0 ms, variance=10.667 ms²

The model settles on component means at 30.0 and 90.0 ms, with variances of 8/3 (2.667 ms²) and 32/3 (10.667 ms²). The slow component's variance is four times larger because observations at [86, 90, 94] spread twice as far from their center as [28, 30, 32] do from theirs.

The variance_floor safeguard prevents an optimization pathology known as covariance collapse. If a component places its mean on a single point with zero variance, its likelihood spikes toward infinity. Imposing a floor keeps the optimization bounded.[4]Reference 4Gaussian mixture modelshttps://scikit-learn.org/stable/modules/mixture.html

The log likelihood stops changing after four iterations. Does that prove the fitted parameters reached the global maximum?

Answer

No. EM guarantees monotonic improvement to a local stationary point, not the global optimum. Another initialization might converge to a distinct mode with higher likelihood.

Diagnosing failure modes: singularities and symmetric traps

Production systems encounter two classic failure modes when fitting probabilistic mixture models.

The first failure mode is covariance collapse (singularities). In standard single-Gaussian maximum likelihood, the likelihood function is strictly bounded. In a Gaussian mixture, however, the likelihood surface contains singularities. If component kkk assigns its center μk\mu_kμk​ directly to a single observation xjx_jxj​ while its variance shrinks toward zero (σk2→0\sigma_k^2 \to 0σk2​→0), the Gaussian density N(xj∣xj,σk2)=(2πσk2)−1/2→∞\mathcal{N}(x_j \mid x_j, \sigma_k^2) = (2\pi\sigma_k^2)^{-1/2} \to \inftyN(xj​∣xj​,σk2​)=(2πσk2​)−1/2→∞. The overall log likelihood shoots toward positive infinity. This doesn't reflect a good fit; it's a degenerate singularity where one component collapses into an infinitely sharp spike. In production code, always regularize covariances by adding a diagonal ridge term (such as scikit-learn's reg_covar) or clamping variances with a minimum floor.[5]Reference 5GaussianMixturehttps://scikit-learn.org/stable/modules/generated/sklearn.mixture.GaussianMixture.html

The second failure mode is the symmetric initialization trap. If both components start with identical means, variances, and weights (such as μ1=μ2=60.0\mu_1 = \mu_2 = 60.0μ1​=μ2​=60.0 ms), every single observation receives identical responsibilities: γi1=γi2=0.5\gamma_{i1} = \gamma_{i2} = 0.5γi1​=γi2​=0.5. The subsequent M-step updates both components to the identical pooled sample mean (60.0 ms) and pooled sample variance (878.7 ms²). Because the responsibilities never differentiate, exact EM steps can't break symmetry.

The next snippet verifies this behavior by testing both starting points against an illustrative validation set [29, 31, 88, 92]:

compare-mixture-initializations.py
1validation = [29.0, 31.0, 88.0, 92.0] 2for start in ([25.0, 95.0], [60.0, 60.0]): 3 fitted, trace = fit_mixture(values, start) 4 validation_score = log_likelihood(validation, fitted) / len(validation) 5 print(f"start={start}: means={[round(mean, 1) for mean in fitted[1]]}, " 6 f"train={trace[-1]:.3f}, validation/point={validation_score:.3f}")
Output
1start=[25.0, 95.0]: means=[30.0, 90.0], train=-17.694, validation/point=-2.637 2start=[60.0, 60.0]: means=[60.0, 60.0], train=-28.943, validation/point=-4.822

The separated initialization reaches a training log likelihood of -17.694 and a validation score of -2.637 per point. The symmetric initialization remains trapped at the pooled mean 60.0 ms, scoring an inferior -28.943 on training data and -4.822 on validation traffic. Always initialize GMM components with dispersed seeds, such as K-means++ centers or multiple random restarts (n_init=10).

Comparing models with different numbers of components (KKK) requires held-out validation likelihood or information criteria (AIC/BIC). Adding more components always increases training likelihood, but risks overfitting noise or creating degenerate single-point components.

To evaluate model fidelity beyond scalar likelihood scores, draw synthetic samples from the fitted mixture: choose component kkk with probability πk\pi_kπk​, then draw x∼N(μk,σk2)x \sim \mathcal{N}(\mu_k, \sigma_k^2)x∼N(μk​,σk2​). Compare histograms of synthetic points against holdout traffic. If the simulated data misses the bimodal separation or generates negative latencies, the Gaussian assumption itself may need revision (for instance, adopting log-normal or Gamma component densities).[6]Reference 6Posterior Predictive Checkshttps://mc-stan.org/docs/stan-users-guide/posterior-predictive-checks.html

Bridge to modern deep learning: Variational Autoencoders as amortized EM

In a Gaussian mixture model, the latent variable ZZZ is a discrete index k∈{1,…,K}k \in \{1, \dots, K\}k∈{1,…,K}. Because KKK is small, calculating the exact posterior responsibility γik=P(Z=k∣X)\gamma_{ik} = P(Z = k \mid X)γik​=P(Z=k∣X) in the E-step is fast and exact.

Modern generative AI takes this formulation into high dimensions. What happens when:

  1. The latent representation z∈Rdz \in \mathbb{R}^dz∈Rd is a continuous, multi-dimensional vector (such as a 128-dimensional embedding representing an image or audio clip)?
  2. The generative process pθ(x∣z)p_\theta(x \mid z)pθ​(x∣z) is parameterized by a deep neural network (the decoder)?

Now marginal likelihood faces double intractability:

  • Marginalizing over continuous latent space requires an intractable high-dimensional integral: pθ(x)=∫pθ(x,z) dzp_\theta(x) = \int p_\theta(x, z)\,dzpθ​(x)=∫pθ​(x,z)dz.
  • The exact posterior pθ(z∣x)=pθ(x,z)/pθ(x)p_\theta(z \mid x) = p_\theta(x, z) / p_\theta(x)pθ​(z∣x)=pθ​(x,z)/pθ​(x) is impossible to compute analytically, ruling out an exact classical E-step.

Kingma and Welling (2014)[7]Reference 7Auto-Encoding Variational Bayes.https://arxiv.org/abs/1312.6114 solved this in the Variational Autoencoder (VAE) framework through amortized variational EM:

Classical EM (GMM)Variational Autoencoder (VAE)
Discrete latent index z∈{1,…,K}z \in \{1, \dots, K\}z∈{1,…,K}Continuous latent vector z∈Rdz \in \mathbb{R}^dz∈Rd
Closed-form linear decoder parameters θ={πk,μk,σk2}\theta = \{\pi_k, \mu_k, \sigma_k^2\}θ={πk​,μk​,σk2​}Deep neural network decoder parameters θ\thetaθ
Exact E-step: analytical posterior γik=P(zi=k∣xi)\gamma_{ik} = P(z_i = k \mid x_i)γik​=P(zi​=k∣xi​)Variational E-step: neural encoder qϕ(z∣x)q_\phi(z \mid x)qϕ​(z∣x) approximates posterior
Separate responsibility vector per observation iiiAmortized inference: single shared encoder network handles any input
M-step: closed-form parameter updatesM-step: gradient ascent on ELBO via backpropagation

Rather than optimizing a separate variational distribution for every data point, an encoder network with weights ϕ\phiϕ amortizes inference by predicting posterior parameters μϕ(x)\mu_\phi(x)μϕ​(x) and σϕ(x)\sigma_\phi(x)σϕ​(x) in a single forward pass.

The training objective is the exact same Evidence Lower Bound:

L(ϕ,θ;x)=Eqϕ(z∣x)[log⁡pθ(x∣z)]−DKL(qϕ(z∣x)∥p(z)).\mathcal{L}(\phi, \theta; x) = \mathbb{E}_{q_\phi(z \mid x)} [\log p_\theta(x \mid z)] - D_{\mathrm{KL}}(q_\phi(z \mid x) \parallel p(z)).L(ϕ,θ;x)=Eqϕ​(z∣x)​[logpθ​(x∣z)]−DKL​(qϕ​(z∣x)∥p(z)).

The first term acts as the M-step reconstruction objective, training the decoder to reconstruct input xxx from latent code zzz. The second term acts as the E-step regularizer, penalizing the divergence between the approximate posterior qϕ(z∣x)q_\phi(z \mid x)qϕ​(z∣x) and standard Gaussian prior p(z)=N(0,I)p(z) = \mathcal{N}(0, I)p(z)=N(0,I).

To backpropagate through stochastic latent variables, Kingma and Welling introduced the reparameterization trick: sample external noise ϵ∼N(0,I)\epsilon \sim \mathcal{N}(0, I)ϵ∼N(0,I) and compute z=μϕ(x)+σϕ(x)⊙ϵz = \mu_\phi(x) + \sigma_\phi(x) \odot \epsilonz=μϕ​(x)+σϕ​(x)⊙ϵ. Because randomness enters as an independent input, gradients flow smoothly through μϕ\mu_\phiμϕ​ and σϕ\sigma_\phiσϕ​ to train encoder and decoder end-to-end. The 1977 principle of optimizing marginal likelihood by tightening and climbing a lower bound directly powers modern latent variable architectures, from VAEs to latent diffusion models.

Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.Why can't a purely discriminative model P(Y | X) reliably identify whether an input query is out-of-distribution noise?

Correct answer: Discriminative models model class boundaries given X, without estimating the input data distribution P(X).

A discriminative model only learns P(Y | X). Because it never models the marginal distribution P(X), it will output confident predictions even when evaluated on impossible or anomalous inputs.

2.Two components contribute weighted densities 0.006 and 0.002 at an observation. What is the first component's responsibility?

Correct answer: 0.75

Normalize the first contribution by the total: 0.006 / (0.006 + 0.002) = 0.75. A raw density contribution is not a normalized probability.

3.What mathematical structure prevents setting the gradient of marginal log-likelihood to zero in closed form for a mixture model?

Correct answer: The summation over hidden components sits inside the logarithm, coupling all parameters.

Because log(sum_k pi_k N(x | theta_k)) contains a sum inside the log, the logarithm can't distribute over individual terms, coupling parameters across all components into a non-convex objective.

4.In the Expectation-Maximization algorithm, what condition holds at the conclusion of the exact E-step?

Correct answer: The KL divergence between q(Z) and the true posterior p(Z | X, theta) equals zero, making the ELBO touch the true log-likelihood.

Setting q(Z) = p(Z | X, theta) drives the KL divergence to zero. In the decomposition log p(X) = ELBO + KL, this forces the lower bound ELBO to equal the true marginal log-likelihood at the current parameter value.

5.How does hard K-means clustering relate mathematically to Gaussian mixture modeling?

Correct answer: K-means is the limiting case of a GMM with shared spherical covariance as variance approaches zero.

When component variances approach zero in a GMM with equal spherical covariances, the softmax responsibility collapses into an argmax indicator function, assigning each point completely to its nearest Euclidean center.

6.What causes covariance collapse (a singularity) during Gaussian mixture estimation?

Correct answer: A component's mean centers on a single data point while its variance shrinks toward zero, driving density to infinity.

When a Gaussian centers on a single point and variance approaches zero, (2pisigma^2)^(-1/2) approaches infinity. This produces a degenerate singularity rather than a valid population model.

7.Both components start with identical means, variances, and weights. Why does EM fail to separate them?

Correct answer: Each observation receives identical responsibilities (0.5 for each component), so both M-step updates produce the exact same pooled mean and variance.

Symmetric initialization gives equal responsibilities to all components. The resulting M-step calculates identical pooled statistics for each component, making EM unable to break symmetry without perturbation.

8.In a Variational Autoencoder (VAE), what represents the amortized variational E-step?

Correct answer: Training a neural encoder q_phi(z | x) to output approximate posterior parameters for any input x.

In high-dimensional continuous latent models, evaluating the exact posterior is intractable. The encoder neural network amortizes inference by predicting the parameters of the variational posterior q_phi(z | x) for any input x, serving as the variational E-step.

8 questions remaining.

Next Step
Continue to Core Retrieval Algorithms

Soft assignments and likelihood expose hidden structure in unlabeled observations; retrieval now applies distance and ranking decisions to finding useful examples efficiently.

PreviousClustering and PCA
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

Pattern Recognition and Machine Learning.

Bishop, C. M. · 2006

https://www.microsoft.com/en-us/research/publication/pattern-recognition-machine-learning/

Maximum Likelihood from Incomplete Data via the EM Algorithm

Dempster, A. P., Laird, N. M., & Rubin, D. B. · 1977 · Journal of the Royal Statistical Society: Series B

https://doi.org/10.1111/j.2517-6161.1977.tb01600.x

CS229 Machine Learning Notes: The EM Algorithm

Stanford University, CS229 · 2022

https://cs229.stanford.edu/notes2022fall/main_notes.pdf

Gaussian mixture models

scikit-learn Developers · 2026

https://scikit-learn.org/stable/modules/mixture.html

GaussianMixture

scikit-learn Developers · 2026

https://scikit-learn.org/stable/modules/generated/sklearn.mixture.GaussianMixture.html

Posterior Predictive Checks

Stan Development Team · 2026

https://mc-stan.org/docs/stan-users-guide/posterior-predictive-checks.html

Auto-Encoding Variational Bayes.

Kingma, D. P., Welling, M. · 2014 · ICLR 2014

https://arxiv.org/abs/1312.6114

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.