Note Wisdom
These notes walk through Stanford CME296 Lecture 3 on flow matching: the transport framing, vector fields and the continuity equation, the conditional-flow-matching loss, and Reflow. They also flag which derivations the lecturer skipped, so you know where to read further.
Institution: Stanford
Original Course: Stanford CME296 Diffusion & Large Vision Models | Spring 2026 | Lecture 3 - Flow matching
Instructor Bio: This lecture is co-delivered by **Afshine Amidi** and **Shervine Amidi**, adjunct faculty at Stanford University’s Institute for Computational and Mathematical Engineering (ICME). Afshine Amidi earned his engineering degree from École Centrale Paris and a Master of Science in Operations Research from the Massachusetts Institute of Technology, where he worked under Prof. Dimitris Bertsimas and received the Jean Gaillard Scholarship and MIT Dean’s Fellowship. He has held professional roles at Amazon and McKinsey & Company, leading AI and business strategy initiatives, and co-authors widely adopted technical learning guides on machine learning, algorithms, and transformer models. Shervine Amidi holds B.S. and M.S. degrees in engineering from École Centrale Paris, as well as an M.S. in Computational and Mathematical Engineering from Stanford University. He has conducted computer vision research at the Stanford Vision Lab and the École Centrale Paris Visual Computing Center. Currently a Senior Machine Learning Engineer at Netflix, he previously worked at Google DeepMind on the Gemini team, Google Assistant, and Uber’s data science division. He has served as a teaching assistant for multiple core Stanford CS courses and has been an adjunct lecturer at the university since 2021.
Course Description: This lecture presents flow-based generative modeling as a distinct and rapidly evolving paradigm for generative vision. It covers the historical development of flow models, the design rationale behind the flow matching objective, conditional flow matching for controlled generation, end-to-end training and inference workflows for flow models, rectified flow formulations, and a systematic comparative analysis of flow matching, diffusion models, and score matching across training stability, sampling speed, and sample quality.
If you missed the third session of Stanford's CME296, the short version is that the class finally met flow matching, the paradigm the lecturer called "quite trendy these days." He framed it as the third lens on a single problem the course has been circling since week one: how do you start somewhere easy to sample from and end up somewhere that looks like your data. Lecture one did that with DDPM, lecture two with score matching, and this one does it by transporting density directly. What I appreciated most is that he kept saying out loud when he was skipping a derivation, which tells you exactly where the soft spots are.
The session runs close to an hour and fifty minutes and splits into two voices. The first speaker builds the whole theory from notation to the final loss; a second speaker (whom the first later hands off to, and who refers back to him as Ashvin) takes over for the visual, practical half on curved trajectories and Reflow before closing with a comparison across all three paradigms.
The first ten minutes are a recap, and they're worth sitting through if you were foggy on weeks one and two. The DDPM story: take clean images, corrupt them gradually with a process q that adds weighted Gaussian noise, then learn a reverse process p-theta that undoes it. The training objective came from maximizing the likelihood of the training data, and after working through a lower bound the whole thing collapsed into an L2 regression on the added noise (2:23).
The score-matching story started one level up, in the geometry of the space itself: learn the gradient of the log density, treat it as a compass pointing toward high-density regions, and walk there with Langevin sampling, noise term included for diversity. Both views ended in an L2 loss, and both turned out to be connected, because both involve adding noise. Unifying them meant going continuous and writing the forward process as an SDE with a drift term (deterministic movement) and a diffusion term (the stochasticity). Reversing it, via a result from the 1980s, requires knowing the score.
Then the warning I'd flag for anyone reading papers alongside the course. In the diffusion and score-matching world, t = 0 was clean data and capital T was noise. Here it flips: p₀ is the easy distribution you start from, usually a standard Gaussian, and p₁ is the target, your actual images (9:30). The lecturer's justification is pure pragmatism — the flow matching literature uses this convention, and keeping the old one would make every paper you read confusing. He also notes it isn't universal; some papers reverse it anyway. Small thing, but it's the kind of sign error that quietly ruins an afternoon.
The framing word for the day is transport. Not "denoise," not "climb the density gradient" — move an entire probability density, all of which sums to one, from one region of space to another.
Four pieces of notation do most of the work:
The vector field is the star, and since it's also a vector, the natural question is how it differs from the score we spent last week on. The analogy he offered is the one I'd write on my hand before an exam. Picture self-driving cars scattered in a Gaussian blob in the middle of the desert, and you want them arranged like your target distribution. The velocity is the instruction you broadcast to each car at each location and time — and if the field weren't time-dependent you could think of it as a fixed highway network with speed limits. Since it is time-dependent, the same spot can carry different instructions at t₁ and t₂. The score, by contrast, is a compass in the dashboard showing which way the crowd is (17:16). Both are vectors. They are not the same vector.
Two equations then carry the rest of the theory. The first is the micro view: dx/dt = u_t(x), starting from x₀. Here he paused for a side condition that will matter later — the trajectory is unique for a given initial condition if the vector field is Lipschitz continuous (20:02). A short poll of the room came back mostly blank, so he defined it: there's some constant m such that the distance between f(x) and f(y) never exceeds m times the distance between x and y. Continuous, but not wildly so. His counterexample is genuinely illuminating — take u(x) = √x on x ≥ 0 and start at x(0) = 0. Then x(t) = 0 is a solution, and so is x(t) = t²/4. Same starting point, two different futures, because the square root misbehaves at zero.
The second equation is the macro view, and he built it rather than asserting it, which I found the most teachable stretch of the lecture (24:47 onward). If you transport a density from one place to another, you don't want to invent or destroy mass along the way. So for a tiny region of space, the change in density over time should equal what flows in minus what flows out. Left-hand side: a partial derivative of p_t with respect to time. Right-hand side: something we need to invent.
Call that something the divergence. Rather than write the formula first, he asked the room to think about what "more leaving than arriving" looks like in one dimension. If the arrows along a line start short and grow longer, stuff is leaving. If they shrink, or point inward from both sides, stuff is accumulating. In both sketches, the thing that tracks the sign is the derivative of the field along that coordinate: positive when more exits, negative when more enters. That's the whole intuition. Extrapolate to multiple dimensions and the divergence becomes the sum of those partial derivatives — a scalar, positive for net outflow, negative for net inflow. He was explicit that this is not a proof, just the shape of the idea.
Then a second question: what field do we take the divergence of? Not the vector field alone. His example was two locations with identical velocities but different densities — the denser region obviously ships more mass. The quantity that carries density around is the probability flux, the scalar density p_t(x) times the vector field u_t(x). Put it together and you get the continuity equation: the time derivative of the density equals minus the divergence of that flux (35:07).
The payoff is a phrase you'll see constantly in papers, which he read aloud and then decoded: when someone says a vector field generates a probability path, they simply mean the pair satisfies the continuity equation. The two views then link up cleanly. Sample x₀ from p₀, let that particle ride the vector field, and — provided the field generates the path — your x_t at each moment is distributed according to p_t, and x₁ is distributed according to p₁. That's the whole point of the exercise, and he repeated it at least three times in different words, which suggests he knew it was the load-bearing idea.
Now the practical problem: find u_t. He detoured through the historical answer first. Continuous normalizing flows (44:46) trained by maximum likelihood — you can rewrite the continuity equation to expose log p_t(x), compute log p₀(x₀) analytically because it's Gaussian, and connect the two ends by integrating the divergence of the vector field. Elegant, and useless in practice: you'd have to run a numerical solver inside every training step. There is, by his account, an actual snail emoji on that slide (46:07). He was explicit that the method is about eight years old, mentioned for historical purposes only, and that this is the motivation for needing something else.
That something else is the cleverest stretch of the lecture, and it works by making the problem easier and then undoing the simplification.
Step one: one point instead of a dataset. p₁ might be horribly complicated, but it's built from individual training examples. So pretend the target is a single point, a Dirac delta — the simplest distribution there is, deterministic, zero everywhere except at that one location where it spikes to infinity while still integrating to one (48:09).
Step two: propose a simple path to it. Take the standard Gaussian at t = 0, shrink it, and slide it toward x₁ until it becomes that point. Concretely, the conditional path p_t(x | x₁) is Gaussian with mean t·x₁ and variance (1 − t)²·I. At t = 0 you recover the standard normal; at t = 1 the variance goes to zero and you get the Dirac. Valid path.
Step three: find its vector field. He just hands you this one — u_t(x | x₁) = (x₁ − x)/(1 − t) (53:03) — and says you can verify it generates the conditional path by plugging both into the continuity equation. The sanity check he did offer is memorable: as t approaches 1, if you're anywhere other than x₁, that expression explodes. Which makes sense, because at t = 1 the target is a single deterministic point and you had better hurry.
Step four: the simplification that pays for everything. If x_t is actually drawn from that conditional Gaussian, you can write it as x_t = t·x₁ + (1 − t)·x₀ with x₀ standard normal (mean plus variance times a Gaussian draw). Substitute that into the formula above, and the algebra collapses: the conditional velocity at such a point is just x₁ − x₀ (58:31). A straight-line displacement. Someone asked why this mattered, and he promised to cash the check in fifteen minutes.
Step five: reassemble the dataset. Run that construction for every training point and average the paths. With two data points you get a mixture of two Gaussians; with a full training set you get the marginal probability path, which he verified does start at p₀ and end at the data distribution, so it's a legitimate route (1:02:24).
Step six: average the velocities correctly. You can't just average the conditional vector fields, you have to weight them — by the conditional path times the data density over the marginal path. He flagged the expression as scary-looking and then defused it: squint and it's Bayes' rule. At a given location x, each conditional velocity gets weighted by how likely that x₁ is to be your destination given where you currently are. In other words, the marginal vector field is a posterior mean of conditional velocities, a weighted consensus of "where am I probably headed from here."
And then the announcement he'd been building toward (1:08:54): this aggregated field really does generate the marginal probability path. He didn't derive it — you'd plug both into the continuity equation and recognize the divergence — but the consequence is the one that matters. Sample from the Gaussian, follow this field, and at t = 1 you land in the data distribution.
The flow matching loss itself is the squared distance between your learned field and that marginal field, averaged over time and over points drawn from the marginal path. Still intractable, because the marginal field is not something you can write down. So the move is to swap in the conditional version: average over time, over a training point x₁, and over x drawn from the conditional path, comparing your network's output to x₁ − x₀.
Why are these equivalent (1:11:16)? He reused the trick from score matching. Expand the squared distance into three terms: the learned field squared, minus twice the dot product, plus the target squared. The first term is identical in both losses. The last doesn't involve the parameters at all, so it's irrelevant to optimization. Only the cross term needs to match. And it does: write the conditional expectation as an integral over the data density times the conditional path, multiply and divide by the marginal density, and the construction from step six pops out — leaving exactly the cross term from the intractable loss. Same objective, same gradients, wildly easier to compute.
The final training recipe is almost embarrassingly plain. Sample noise x₀, sample an image x₁ from the training set, sample a time step, build the interpolated x_t, and ask your network, given x_t and t, to predict x₁ − x₀. Backpropagate. Inference is the mirror image: draw from a standard Gaussian and numerically integrate the ODE, Euler-style, step by step.
One loose thread he tied up himself: if we're matching velocity, why is it called flow matching? Two reasons, and he labeled them a personal take. Historically, these models already had "flow" in their names, including continuous normalizing flows, so partly it's branding. But there's a real reason too — if the learned vector field is Lipschitz, trajectories are unique, which gives you a one-to-one correspondence between a velocity field and a flow, so matching one really is matching the other. Whether the learned field is Lipschitz was deferred to lecture five; the gist is that a network of matrix multiplications and smooth activations is.
The second speaker started from a picture that complicates the clean story above. Draw a straight line from x₀ to x₁ for one training pair, then another pair, and suppose the lines cross — at the same location and roughly the same time. The loss can only fit one vector there, so the MSE forces an average. The trajectories you taught are not the trajectories you get; paths come out curved. Curved paths cost you at inference, because Euler estimates velocity at the start of a step and needs several steps to follow a bend, and step count is exactly the NFE budget you were trying to shrink (1:35:05). He also shut the door on a shortcut: in the diffusion setting you can assume the drift is linear in x, which is what makes DPM-solver-style tricks work. Here you're learning a nonlinear velocity field directly, so no such luck (1:36:08).
Reflow is the fix (1:36:30). Take the pairs you actually get by integrating the trained model — same Gaussian starting points, new endpoints — and retrain on those, because by construction the field now carries each start directly to its end. Do it repeatedly and the paths straighten, at which point a handful of Euler steps suffice. He called the first pass a "one rectified flow" model, and he was candid that one pass is usually plenty.
Two honest caveats came with it, and they're the part I'd push on if I were in the room. First, does reflow still land you in the data distribution? The justification he sketched is that the new and old probability flows obey the same continuity equation, and with the same initial condition a uniqueness argument carries it through — his words were "just hand-waving the proof here." Second, even granting the theory, two error sources creep in: discretization error from the solver and approximation error from a network that certainly hasn't learned the field perfectly. That's why you don't reflow many times, and why the whole thing is a trade of some quality for much faster sampling. His own summary — "we're happy, but not quite" — felt like the most honest sentence of the day.
The closing comparison (1:43:20) is the part I'd screenshot. Forward process: discrete-time diffusion for DDPM, an SDE once you take the limit, and for flow matching a direct interpolation between noise and image, with the time convention flipped. What you fit: noise in the diffusion view, the score in the score-matching view (with a clean relationship between the two), and the velocity field here, chosen so the ODE you integrate at the end is as simple as possible. Generation: the first two came in stochastic flavors with deterministic counterparts; flow matching has no such split because it's deterministic from the start. For anyone who wants the unifying theory, he recommended the stochastic interpolants paper, out of scope for the course, whose punchline is that of noise, score, and velocity, knowing any two determines the third (1:46:56).
As a listener, three things left me unsatisfied, none of them fatal. The marginal probability path sits in the denominator of both the velocity aggregation and the intractable loss, and we were never told it's computable — quite the opposite, since sidestepping it is the entire point of the conditional loss; I'd have liked that tension named explicitly instead of left for us to notice. Two separate claims were verified by "plug it into the continuity equation" and then skipped for time, and the Lipschitz argument was deferred by two lectures, so a decent slice of the day's rigor was on credit. And the reflow guarantee rests on a proof the lecturer himself described as hand-waving, with quality left as a function of errors nobody quantified. None of that makes the lecture bad — the construction from a single Dirac target up to a full dataset is one of the more satisfying pieces of exposition I've sat through — but if you're planning to implement this, the skipped derivations are where I'd spend my reading time.
Content Disclaimer:
This article is for general reference only and does not constitute professional R&D guidance, production process advice or quality certification. All material performance data has specific test premises; readers should verify parameters against actual equipment and working conditions.
All contents below are exclusive to the paid Word file, NOT available on this web page

