Download lecture_4/README.md from ChatterjeeLab/CIS6270: direct link, hf CLI and curl.
- Browser
- Download file 10.5 kB
-
https://huggingface.co/ChatterjeeLab/CIS6270/resolve/main/lecture_4/README.md
- Command line
-
hf download hf://ChatterjeeLab/CIS6270/lecture_4/README.md
-
curl -L -o README.md https://huggingface.co/ChatterjeeLab/CIS6270/resolve/main/lecture_4/README.md
Lecture 4: Discrete Diffusion
Chapter 4 keeps every state inside a finite vocabulary and replaces the continuous displacement of
Chapter 3 by a decision about which tokens to erase, reveal or replace. The chapter builds masked
diffusion by hand from a survival schedule, then rebuilds it as one member of a family: a rate
matrix fixes what a token can do in the next instant, the master equation follows the marginals,
the matrix exponential gives the corruption kernel at any time, and time reversal turns the forward
rates into the rates that generate. Seven published models come out of that family by changing the
rate matrix, the time variable, what the network predicts, or the state space itself, and a
further group keeps the rates and changes the order in which positions are filled, the weighting
of the loss, or the distribution to sample from. Every file here runs on the shared synthetic DNA
of cis6270.data, with t = 0 clean and t = 1 all masks, survival probability alpha_t, and
rates R_t(x, y) in the row convention p_dot = p R.
The files
| File | Command | What it trains and generates |
|---|---|---|
ctmc.py |
python lecture_4/ctmc.py |
Trains no network, and works every number of the CTMC sections: the two rules, one row for DNA, waiting times, the master equation, the matrix exponential, the R_pi family, eigenvalues, detailed balance and time reversal. |
mdlm.py |
python lecture_4/mdlm.py |
Trains the denoiser on the weighted cross-entropy at masked positions, generates DNA from all masks. The chapter's thirty-line model. |
uniform.py |
python lecture_4/uniform.py |
Trains against the rate loss under uniform replacement, generates by tau-leaping from uniformly random tokens. |
d3pm.py |
python lecture_4/d3pm.py |
Trains against the discrete-time variational bound plus the auxiliary cross-entropy, generates by T ancestral steps. |
sedd.py |
python lecture_4/sedd.py |
Trains a network to output the concrete score through denoising score entropy, generates from the predicted ratios. |
block.py |
python lecture_4/block.py |
Trains masked diffusion inside blocks with a clean prefix, generates block by block. --block moves the dial between MDLM and autoregression. |
simplex.py |
python lecture_4/simplex.py |
Trains a denoiser that reads a Dirichlet belief at every position, generates by thinning the belief, drawing an innovation and mixing the two. |
guidance.py |
python lecture_4/guidance.py |
Trains a label-dropout denoiser and a classifier on noisy sequences, then samples unguided, with exact classifier guidance, with its gradient approximation, and classifier free. |
planner.py |
python lecture_4/planner.py |
Trains with the planner weights inside the bound, samples the same weights in a uniform order and in the planner's order with a remasking corrector. |
search.py |
python lecture_4/search.py |
Trains masked diffusion, then searches the tree of partial sequences under the h-transform and keeps a Pareto archive over two objectives. |
Two constructions of the chapter have no file here, and the chapter develops both in full.
Per-token schedules, MD4 and GenMD4, give every clean token its own rate beta_a(t) and change
nothing but the weight, which becomes w_a(t) = -alpha_dot_t(a)/(1 - alpha_t(a)) inside the sum
over tokens. A2D2 lets the length change by adding an insertion jump beside the unmasking jump,
which enlarges the state space and keeps the master equation, the reversal and the rate loss
exactly as they are.
Every file takes the shared flags of cis6270.runner.common_parser, so --steps, --samples,
--sample-steps, --seed and --quiet mean the same thing everywhere. ctmc.py works through
rates the chapter writes down, so its --steps is the number of Euler steps in the
matrix-exponential comparison and its --samples the number of simulated paths. d3pm.py takes
T from --timesteps, since the same number fixes the corruption and the ancestral sampler.
search.py reads --sample-steps as the length of one rollout, which is short because we score
every leaf of the tree by its own rollouts.
What a default run produces
We took every number below from --seed 0 on a CPU. The worked numbers are exact and repeat on any
machine; the trained numbers move a little with the hardware's floating point, and the sample
statistics move with the seed. The training data have GC fraction 0.577 and motif fraction 0.777,
and a sampler drawing its bases uniformly would land near 0.05 on the motif, since ACGT fits in a
length-16 sequence at thirteen places and each one has probability 1/256.
| File | Worked numbers it prints | Trained numbers |
|---|---|---|
ctmc.py |
exit rate 4 out of A, P(A, A) = 0.6 at h = 0.1, Euler step (0.16, 0.22, 0.28, 0.34), exp(tau R_pi) at 0.625 and 0.125, Euler products 0.480, 0.570, 0.616, reverse rates 2 and 0.5 out of C |
none |
mdlm.py |
r = 0.5, reverse law (0.3, 0.2, 0, 0, 0.5), w(0.5) = 2, loss term 2.0996, rate error 1.0217 |
bound 1.25 nats per token, GC 0.56, motif 0.69 |
uniform.py |
kernel 0.625 and 0.125, clean factor 5, posterior (0.375, 0.625), conditional rates 5 and 0.2, posterior average 2, model rates 2.76 and 1.16, rate error 0.754 |
rate loss 19.1 nats per sequence, GC 0.56, motif 0.50 |
d3pm.py |
alpha_bar_2 = 0.56, Q_bar_2 at 0.67 and 0.11, the step-1 posterior (0.5795, 0.3523, 0.0341, 0.0341), Gaussian row 0.4088 and 0.0912, exp(0.3 R) first row (0.6747, 0.1964, 0.1122, 0.0167) |
bound 18.2 nats per sequence, GC 0.57, motif 0.72 |
sedd.py |
target ratio 2, score entropy 0.0107, 0.3863, 0.1891 |
score entropy 18.8 nats per sequence, mean predicted ratio 1.26 toward a conditional target of 5, GC 0.55, motif 0.33 |
block.py |
worked bound 2.7567 nats on ACGTAC, integral of w(t)(1 - alpha_t) = 1 |
bound 1.21 nats per token at L' = 4 and 1.29 at L' = 1, GC 0.57, motif 0.67 |
simplex.py |
c_t = 8 and c_s = 20, parameters (5, 1, 1, 1) and (17, 1, 1, 1), standard deviation 0.161 on A, increment (12, 0, 0, 0), W ~ Beta(8, 12) with mean 0.4, simulated standard deviation 0.0779 against the exact 0.0779 |
cross-entropy 0.42 nats per token, GC 0.57, motif 0.34 |
guidance.py |
h = (2, 1, 0.5, 0.5), guided rates (1, 1.5, 0.5), at gamma = 2 rates (0.5, 4.5, 0.5) with exit rate 5.5 and share 0.8182 on G |
GC 0.58 unguided, 0.68 with the exact classifier, 0.63 with the gradient approximation, 0.74 classifier free |
planner.py |
reveals positions 3 and 6, remasks position 5, both corrector fluxes 1, PAPL weights (0.4, 2.0, 0.6, 1.0), inner sums 2.9 and 2.12 |
motif 0.61 in a uniform order against 0.86 in the planner's order, GC 0.58 and 0.51 |
search.py |
h(z) = 0.35, h(z') = 0.75, factor 2.143, a rate of 1.2 becomes 2.571, the archive keeps P1 and P2 |
144 rollouts scored, an archive of 3 sequences, best GC 0.88 and best motif match 1.0 |
On one core, we get about two seconds from a default run of ctmc.py, and between thirty and
forty seconds of processor time from every other file. With --steps 20, each file runs in a few
seconds.
mdlm.py --steps 3000 lowers the bound from 1.25 to 1.22 nats per token in about three times the
time, and leaves the motif fraction inside the spread it already has across seeds, which at seeds
0, 1 and 2 is 0.69, 0.45 and 0.72. The one-position bound of block.py --block 1 is the exact
autoregressive log-likelihood with no slack, and at this budget it still measures above the
L' = 4 bound, because the estimator draws one block of sixteen and multiplies by sixteen, which
is sixteen times noisier at the same number of draws, and because a denoiser restricted to a left
prefix has a harder prediction problem than one that reads both sides.
sedd.py sits below the other samplers on the motif because its sampler stops at the time floor,
as the list below says. simplex.py --sample-steps 256 raises its motif fraction from 0.34 to
0.38, so the reverse grid accounts for only a small part of what holds that number down.
What is simplified
- The time weight
w(t) = 1/tis unbounded att = 0, so every file draws its times above a small floor. The chapter's integrand is correct as written; the floor keeps one draw from dominating a batch. - The samplers that use tau-leaping rescale a step whose total jump probability would exceed one.
uniform.pythen finishes with the exact posterior step down tot = 0that the chapter derives for thepifamily.sedd.pypredicts ratios, with no posterior over clean tokens to step through, so it stops at the time floor, wherealpha_t = 0.92leaves about eight percent of the positions redrawn frompi. uniform.pyandsedd.pyuse the constant schedulebeta(t) = 4, which is the schedule of the chapter's worked example and reachesalpha_t = 1/2att = (log 2)/4. Its survival probability att = 1is0.018, and that residue is theKL(q_1 || p_1)term of the bound.d3pm.pysamples one step index per sequence and multiplies byT, which estimates the sum over steps without evaluating all of it.block.pysamples one block per sequence and multiplies by the number of blocks, for the same reason. To train a block of lengthL'we hide the suffix behind the mask in place of a block-causal attention mask.simplex.pyholds the concentration constant atgamma = 1, realizes the sum over clean tokens by one categorical draw as the chapter does, and reads the final token as the corner the belief has reached. Its loss is the chapter's cross-entropy, so its nats per token are the prediction error of the denoiser at the sampled times, and the bounds above are on a likelihood.guidance.pyuses a mean-pooling classifier on one-hot inputs, because we need a derivative with respect to the sequence to approximate the gradient, and the label of this data is a thresholded mean over positions.planner.pyreveals the number of positions the schedule sets at each step, which is the deterministic version of the chapter's independent reveal draws, and remasks a fixed count.search.pyscores--sample-stepsrollout steps per completion and expands a fixed number of children, so the tree stays small enough to build in a few seconds.