Dylan Deshler commited on
Commit
c9477d8
·
1 Parent(s): f5e4044

autoregressive

Browse files
Files changed (2) hide show
  1. backend.py +27 -17
  2. real_backend.py +19 -2
backend.py CHANGED
@@ -79,25 +79,35 @@ N_CHUNKS = 24 # fixed sequence length (measures) -- matches training
79
  CONTINUE_MEASURES = 8
80
 
81
  # Noise level the already-written measures are held at while the new ones are
82
- # sampled. 0.0 hands the past over clean, which is both the Diffusion Forcing
83
- # reference implementation's shipped default (its scheduling matrix pads history
84
- # with zeros; the `stabilization` variable exists but is set to 0) and the right
85
- # default here.
86
  #
87
- # The DF paper only says a small nonzero level 0<k<<K can stabilize rollout, with
88
- # no number. The concrete win reported elsewhere -- GameNGen's noise-augmented
89
- # context, max level 0.7 over 10 buckets -- is a TRAINING-time intervention, and
90
- # that paper notes results are already much improved at inference with no added
91
- # noise. This model gets that training for free: FM.loss draws an independent
92
- # t ~ sigmoid(randn) per measure, so it has already seen every measure at every
93
- # noise level and is calibrated for a past it should not fully trust.
94
  #
95
- # So raise this only if repeated Continues visibly drift or harden into a loop.
96
- # Useful range is 0.1-0.2, NOT lower: under sigmoid(randn) only 0.005% of training
97
- # tokens landed below t=0.02 (vs 1.4% below 0.1, 8.3% below 0.2), so very small
98
- # levels are the undertrained tail rather than a gentler version of the same
99
- # thing. Above ~0.3 the context starts genuinely washing out.
100
- CONTINUE_CTX_NOISE = 0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  # Vanilla history guidance (HG-v) weight for Continue. CFG steers toward the
103
  # conditioning signals; this steers toward THE PAST:
 
79
  CONTINUE_MEASURES = 8
80
 
81
  # Noise level the already-written measures are held at while the new ones are
82
+ # sampled.
 
 
 
83
  #
84
+ # NOT 0.0, even though clean history is what the Diffusion Forcing reference
85
+ # implementation ships. That implementation trains on DISCRETE noise indices
86
+ # drawn uniformly over [0, K), so index 0 -- perfectly clean -- is an ordinary
87
+ # training input for it. This model does not: FM.loss draws
 
 
 
88
  #
89
+ # t = torch.sigmoid(torch.randn(B, T))
90
+ #
91
+ # a continuous logit-normal, which has ZERO density at 0 and never gets near it.
92
+ # Over a 300k-step run (grad_accum 4 x batch 32 x 24 measures ~= 9.2e8 draws) the
93
+ # smallest t the model ever saw is about 0.0025, and t < 0.001 never occurred at
94
+ # all. t=0.0 is therefore off the end of the training support, and t is not a
95
+ # passive tag: it feeds the per-measure AdaLN modulation (`t = t + style + bpm`),
96
+ # so an out-of-distribution value on the 16 context measures corrupts their
97
+ # features and, through attention, the free measures with them.
98
+ #
99
+ # 0.05 is the smallest value that is comfortably supported -- roughly 1.5e6
100
+ # training draws landed below it -- while leaving the context essentially intact
101
+ # (alpha=0.95, sigma=0.05). Draws below each level over that run:
102
+ #
103
+ # t < 0.001 0 t < 0.05 1,490,876
104
+ # t < 0.01 1,993 t < 0.10 12,904,433
105
+ #
106
+ # Raising it toward 0.1-0.2 is also well supported and is the knob to reach for
107
+ # if repeated Continues drift or harden into a loop (Diffusion Forcing's
108
+ # stabilization result: a model that half-distrusts its own past compounds error
109
+ # more slowly). Above ~0.3 the context genuinely washes out.
110
+ CONTINUE_CTX_NOISE = 0.05
111
 
112
  # Vanilla history guidance (HG-v) weight for Continue. CFG steers toward the
113
  # conditioning signals; this steers toward THE PAST:
real_backend.py CHANGED
@@ -295,10 +295,27 @@ def fm_df_sample(
295
  v_none = _predict(x_none, t_none)
296
  v = v_none + omega * (v - v_none)
297
 
298
- x_t = x_t + v * (t_next - t_cur)
 
 
 
 
 
 
 
299
  # the Euler step moved the context too; put it back untouched.
300
  x_t = torch.where(keep, ctx_x, x_t)
301
- return x_t
 
 
 
 
 
 
 
 
 
 
302
 
303
 
304
  def _check_stage(m, stage, path, model_args):
 
295
  v_none = _predict(x_none, t_none)
296
  v = v_none + omega * (v - v_none)
297
 
298
+ # (t_cur - t_next), NOT (t_next - t_cur). The net predicts -dx_t/dt
299
+ # (FM.loss's target is A(t)x + B(t)noise == x - (1-sigma_min)noise,
300
+ # commented "-dxt/dt"), and t runs 1 -> 0, so the descending step
301
+ # dt = t_next - t_cur is negative and the two negatives cancel:
302
+ # dx = (dx_t/dt) * dt = (-v) * (t_next - t_cur) = v * (t_cur - t_next)
303
+ # This is exactly what FMEulerSampler.sample does. Getting it
304
+ # backwards integrates the flow the wrong way and returns noise.
305
+ x_t = x_t + v * (t_cur - t_next)
306
  # the Euler step moved the context too; put it back untouched.
307
  x_t = torch.where(keep, ctx_x, x_t)
308
+
309
+ # Hand back the CLEAN context, not ctx_x. ctx_x is an input the model reads;
310
+ # it is not an answer. The caller stores this whole tensor as tr.latents and
311
+ # the next continuation slices its context out of it, so returning the noised
312
+ # copy would re-noise the same measures on every press: after k continues the
313
+ # oldest surviving measure has been scaled by alpha(ctx_noise)^k with k
314
+ # independent noise draws stacked on it (0.95^k at the 0.05 default -- 0.77
315
+ # by the fifth press) and has drifted well off the latent manifold. That
316
+ # decays into mush no matter how good the sampler is. It also means the
317
+ # re-decoded context measures are the real ones rather than degraded ones.
318
+ return torch.where(keep, known_x0, x_t)
319
 
320
 
321
  def _check_stage(m, stage, path, model_args):