Ruurd commited on
Commit
35914ff
·
verified ·
1 Parent(s): a9be260

Decouple PMI weight from sequence sampling temperature

Browse files
Files changed (3) hide show
  1. README.md +22 -0
  2. app.py +11 -3
  3. src/diffusion_lm/bigram.py +22 -4
README.md CHANGED
@@ -87,3 +87,25 @@ and reset each block/iteration. Gamma still subtracts gamma*C in log-score units
87
  this is not a percentage-parameter remapping. Gamma=0 restores unpenalized results
88
  for the selected maximum-path or sampling mode. Ordinary decoding ignores these
89
  checkboxes; the original temperature weighting is unchanged.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  this is not a percentage-parameter remapping. Gamma=0 restores unpenalized results
88
  for the selected maximum-path or sampling mode. Ordinary decoding ignores these
89
  checkboxes; the original temperature weighting is unchanged.
90
+
91
+
92
+ ## Independent temperature and bigram weight
93
+
94
+ The LLaDA Space now uses S(y) = sum log p_t(y_t) + w sum PMI - gamma sum C.
95
+ **Bigram weight** ranges from 0 to 5, default 1. A weight of 1 gives equal
96
+ coefficients, not necessarily equal numerical influence. Weight 0 disables PMI
97
+ but does not disable repetition penalties. The previous fixed 0.5*temperature
98
+ PMI coefficient described above is superseded for this Space.
99
+
100
+ **Sample bigram-guided sequences** samples P(y) proportional to exp(S(y)/T).
101
+ Temperature controls the entropy of the whole sequence score, including repetition,
102
+ without changing the ratios of score coefficients. T=0 selects the maximum path;
103
+ positive T is floored to 1e-6 for numerical stability. Maximum-path Viterbi ignores
104
+ temperature. Raw full-vocabulary diffusion probabilities still determine expected
105
+ repetition counts and retention/APD confidence. Default T is now 1, w is 1,
106
+ matching the former T=2 PMI coefficient and sequence distribution. Ordinary
107
+ diffusion retains its usual temperature behavior. No extra model forwards.
108
+
109
+ Unseen pairs have a neutral PMI correction of zero, never a forbidden transition.
110
+ Conditional scoring instead uses smoothed finite probabilities. Expected repetition
111
+ counts remain the default; the optional candidate-set count mode is unchanged.
app.py CHANGED
@@ -33,7 +33,7 @@ PMI_MIN_COUNT = 1
33
  PMI_CLIP = 2.0
34
  BIGRAM_ENDING_DELAY_STRENGTH = 10.0
35
  # Preserve the candidate pool and balance used in the matched Viterbi tests.
36
- DEFAULT_TEMPERATURE = 2.0 if BIGRAM_ENABLED else 0.7
37
  DEFAULT_TOP_K = 16 if BIGRAM_ENABLED else 3
38
 
39
  MODEL_REPO_ID = os.getenv("MODEL_REPO_ID", SPACE["model_repo_id"])
@@ -125,6 +125,7 @@ def generate(
125
  adaptive_parallel_decoding: bool = False,
126
  sequence_sampling: bool = False,
127
  count_all_candidate_bigrams: bool = False,
 
128
  ):
129
  """Stream iterative masked-diffusion generation from the fixed model."""
130
  question = question.strip() or "What do you know about Amsterdam?"
@@ -136,11 +137,13 @@ def generate(
136
  raise gr.Error("Bigram Viterbi is not enabled for this Space.")
137
  try:
138
  selector = BigramViterbiSelector(
139
- _bigram_scorer(bigram_model_path).to(SESSION.device), 0.5,
 
140
  scoring_method=("pmi" if remasking_strategy == "PMI Viterbi + Repetition Penalty" else
141
  {"Conditional bigram": "conditional", "PMI bigram": "pmi"}.get(bigram_scoring_method, "")),
142
  min_count=int(pmi_min_count), pmi_clip=float(pmi_clip),
143
  sequence_sampling=bool(sequence_sampling),
 
144
  remasking_confidence={"Diffusion probability": "diffusion", "Viterbi path margin": "viterbi_margin"}.get(bigram_remasking_confidence, ""),
145
  )
146
  if remasking_strategy == "PMI Viterbi + Repetition Penalty":
@@ -240,7 +243,11 @@ with gr.Blocks(title=f"{DISPLAY_NAME} · masked diffusion") as demo:
240
  num_steps = gr.Slider(1, 512, value=64, step=1, label="Denoising steps")
241
  block_length = gr.Slider(1, 512, value=64, step=1, label="Block length")
242
  temperature = gr.Slider(0.0, 2.0, value=DEFAULT_TEMPERATURE, step=0.05, label="Temperature",
243
- info="In Viterbi, lower values favor diffusion predictions over bigrams; 0 ignores bigram scores. Viterbi remains deterministic." if BIGRAM_ENABLED else None)
 
 
 
 
244
  top_k = gr.Slider(1, 100, value=DEFAULT_TOP_K, step=1, label="Top-k",
245
  info="Number of candidate tokens per position for sampling or Viterbi. At 1, only the model’s highest-scoring token is available." if BIGRAM_ENABLED else None)
246
  pause_per_step = gr.Slider(
@@ -361,6 +368,7 @@ with gr.Blocks(title=f"{DISPLAY_NAME} · masked diffusion") as demo:
361
  adaptive_parallel_decoding,
362
  sequence_sampling,
363
  count_all_candidate_bigrams,
 
364
  ]
365
  def update_ar_controls(strategy, apd=False):
366
  """Expose AR-only controls and clear revision when another strategy is selected."""
 
33
  PMI_CLIP = 2.0
34
  BIGRAM_ENDING_DELAY_STRENGTH = 10.0
35
  # Preserve the candidate pool and balance used in the matched Viterbi tests.
36
+ DEFAULT_TEMPERATURE = (1.0 if SPACE.get("model_backend") == "llada" else 2.0) if BIGRAM_ENABLED else 0.7
37
  DEFAULT_TOP_K = 16 if BIGRAM_ENABLED else 3
38
 
39
  MODEL_REPO_ID = os.getenv("MODEL_REPO_ID", SPACE["model_repo_id"])
 
125
  adaptive_parallel_decoding: bool = False,
126
  sequence_sampling: bool = False,
127
  count_all_candidate_bigrams: bool = False,
128
+ bigram_weight: float = 1.0,
129
  ):
130
  """Stream iterative masked-diffusion generation from the fixed model."""
131
  question = question.strip() or "What do you know about Amsterdam?"
 
137
  raise gr.Error("Bigram Viterbi is not enabled for this Space.")
138
  try:
139
  selector = BigramViterbiSelector(
140
+ _bigram_scorer(bigram_model_path).to(SESSION.device),
141
+ float(bigram_weight) if getattr(SESSION, "llada", False) else 0.5,
142
  scoring_method=("pmi" if remasking_strategy == "PMI Viterbi + Repetition Penalty" else
143
  {"Conditional bigram": "conditional", "PMI bigram": "pmi"}.get(bigram_scoring_method, "")),
144
  min_count=int(pmi_min_count), pmi_clip=float(pmi_clip),
145
  sequence_sampling=bool(sequence_sampling),
146
+ independent_temperature=bool(getattr(SESSION, "llada", False)),
147
  remasking_confidence={"Diffusion probability": "diffusion", "Viterbi path margin": "viterbi_margin"}.get(bigram_remasking_confidence, ""),
148
  )
149
  if remasking_strategy == "PMI Viterbi + Repetition Penalty":
 
243
  num_steps = gr.Slider(1, 512, value=64, step=1, label="Denoising steps")
244
  block_length = gr.Slider(1, 512, value=64, step=1, label="Block length")
245
  temperature = gr.Slider(0.0, 2.0, value=DEFAULT_TEMPERATURE, step=0.05, label="Temperature",
246
+ info=("For sequence sampling: higher values increase randomness; 0 selects the best path. Bigram weight controls PMI influence. Maximum-path Viterbi ignores temperature."
247
+ if SESSION.llada else "In Viterbi, lower values favor diffusion predictions over bigrams; 0 ignores bigram scores. Viterbi remains deterministic.") if BIGRAM_ENABLED else None)
248
+ bigram_weight = gr.Slider(0.0, 5.0, value=1.0, step=0.05, label="Bigram weight",
249
+ visible=SESSION.llada and BIGRAM_ENABLED,
250
+ info="PMI coefficient: 0 disables PMI; 1 gives equal diffusion and PMI score coefficients. Independent of temperature.")
251
  top_k = gr.Slider(1, 100, value=DEFAULT_TOP_K, step=1, label="Top-k",
252
  info="Number of candidate tokens per position for sampling or Viterbi. At 1, only the model’s highest-scoring token is available." if BIGRAM_ENABLED else None)
253
  pause_per_step = gr.Slider(
 
368
  adaptive_parallel_decoding,
369
  sequence_sampling,
370
  count_all_candidate_bigrams,
371
+ bigram_weight,
372
  ]
373
  def update_ar_controls(strategy, apd=False):
374
  """Expose AR-only controls and clear revision when another strategy is selected."""
src/diffusion_lm/bigram.py CHANGED
@@ -205,7 +205,7 @@ def expected_bigram_repetition_penalties(token_ids, probabilities, vocab_size, s
205
 
206
 
207
  class BigramViterbiSelector:
208
- def __init__(self, scorer, weight=0.5, *, scoring_method="conditional", min_count=5, pmi_clip=5.0, remasking_confidence="diffusion", sequence_sampling=False):
209
  if not math.isfinite(weight) or weight < 0:
210
  raise ValueError("Bigram weight must be finite and nonnegative")
211
  BigramScorer.validate_scoring(scoring_method, min_count, pmi_clip)
@@ -213,6 +213,7 @@ class BigramViterbiSelector:
213
  raise ValueError("Remasking confidence must be 'diffusion' or 'viterbi_margin'")
214
  self.remasking_confidence = remasking_confidence
215
  self.sequence_sampling = bool(sequence_sampling)
 
216
  self.last_path_margins = None
217
  self.scorer, self.weight = scorer, float(weight)
218
  self.scoring_method, self.min_count, self.pmi_clip = scoring_method, min_count, float(pmi_clip)
@@ -243,7 +244,7 @@ class BigramViterbiSelector:
243
  return token_ids.gather(1, path[:, None]).squeeze(1)
244
 
245
  @staticmethod
246
- def sample_sequence(token_ids, emissions, transitions, weight=1.0):
247
  """Exact forward-filter/backward sampling from exp(total path score).
248
 
249
  No score perturbations or model calls. Centering each forward row
@@ -255,7 +256,18 @@ class BigramViterbiSelector:
255
  length, k = emissions.shape
256
  if transitions.shape != (length - 1, k, k):
257
  raise ValueError("Expected transitions [B-1,K,K]")
 
 
 
 
 
 
 
 
258
  edges = transitions.float() * weight if weight else torch.zeros_like(transitions, dtype=torch.float32)
 
 
 
259
  forward = torch.empty_like(emissions, dtype=torch.float32)
260
  row = emissions[0].float()
261
  forward[0] = row - row.max()
@@ -321,6 +333,9 @@ class BigramViterbiSelector:
321
  raw log probabilities + (weight * T) * transitions. The latter avoids
322
  division by tiny T and has the exact greedy limit at T=0. Margins use
323
  these common rescaled units, preserving their retention ranking.
 
 
 
324
  Returned diffusion confidence always uses raw full-vocabulary p_t.
325
  Optional repetition penalties subtract gamma*C(a,b) directly from each
326
  edge, independently of the PMI weight and temperature. Expected counts
@@ -336,7 +351,7 @@ class BigramViterbiSelector:
336
  temperature = float(temperature)
337
  if not math.isfinite(temperature) or temperature < 0:
338
  raise ValueError("temperature must be finite and nonnegative")
339
- weight = self.weight * temperature
340
  if not math.isfinite(weight):
341
  raise ValueError("temperature times bigram weight must be finite")
342
  self.last_path_margins = None
@@ -388,7 +403,10 @@ class BigramViterbiSelector:
388
  if repetition_penalty_strength:
389
  edges = edges - penalties
390
  decoder = self.sample_sequence if self.sequence_sampling else self.decode
391
- selected = decoder(candidates, emissions, edges, 1.0)
 
 
 
392
  if self.remasking_confidence == "viterbi_margin":
393
  self.last_path_margins = self.path_margins(candidates, emissions, edges, selected, 1.0)
394
  elif self.remasking_confidence == "viterbi_margin":
 
205
 
206
 
207
  class BigramViterbiSelector:
208
+ def __init__(self, scorer, weight=0.5, *, scoring_method="conditional", min_count=5, pmi_clip=5.0, remasking_confidence="diffusion", sequence_sampling=False, independent_temperature=False):
209
  if not math.isfinite(weight) or weight < 0:
210
  raise ValueError("Bigram weight must be finite and nonnegative")
211
  BigramScorer.validate_scoring(scoring_method, min_count, pmi_clip)
 
213
  raise ValueError("Remasking confidence must be 'diffusion' or 'viterbi_margin'")
214
  self.remasking_confidence = remasking_confidence
215
  self.sequence_sampling = bool(sequence_sampling)
216
+ self.independent_temperature = bool(independent_temperature)
217
  self.last_path_margins = None
218
  self.scorer, self.weight = scorer, float(weight)
219
  self.scoring_method, self.min_count, self.pmi_clip = scoring_method, min_count, float(pmi_clip)
 
244
  return token_ids.gather(1, path[:, None]).squeeze(1)
245
 
246
  @staticmethod
247
+ def sample_sequence(token_ids, emissions, transitions, weight=1.0, temperature=1.0):
248
  """Exact forward-filter/backward sampling from exp(total path score).
249
 
250
  No score perturbations or model calls. Centering each forward row
 
256
  length, k = emissions.shape
257
  if transitions.shape != (length - 1, k, k):
258
  raise ValueError("Expected transitions [B-1,K,K]")
259
+ temperature = float(temperature)
260
+ if not math.isfinite(temperature) or temperature < 0:
261
+ raise ValueError("Sequence temperature must be finite and nonnegative")
262
+ if temperature == 0:
263
+ return BigramViterbiSelector.decode(token_ids, emissions, transitions, weight)
264
+ # Global path temperature changes entropy without changing the relative
265
+ # diffusion/PMI/repetition weights. Tiny positive T approaches argmax.
266
+ scale = max(temperature, 1e-6)
267
  edges = transitions.float() * weight if weight else torch.zeros_like(transitions, dtype=torch.float32)
268
+ if edges.numel():
269
+ edges = (edges - edges.amax(dim=(1, 2), keepdim=True)) / scale
270
+ emissions = (emissions.float() - emissions.float().amax(-1, keepdim=True)) / scale
271
  forward = torch.empty_like(emissions, dtype=torch.float32)
272
  row = emissions[0].float()
273
  forward[0] = row - row.max()
 
333
  raw log probabilities + (weight * T) * transitions. The latter avoids
334
  division by tiny T and has the exact greedy limit at T=0. Margins use
335
  these common rescaled units, preserving their retention ranking.
336
+ With independent_temperature=True, weight directly multiplies PMI and
337
+ temperature only sets the sequence sampling distribution exp(S/T).
338
+ Maximum-path decoding is temperature-independent; T=0 samples its argmax.
339
  Returned diffusion confidence always uses raw full-vocabulary p_t.
340
  Optional repetition penalties subtract gamma*C(a,b) directly from each
341
  edge, independently of the PMI weight and temperature. Expected counts
 
351
  temperature = float(temperature)
352
  if not math.isfinite(temperature) or temperature < 0:
353
  raise ValueError("temperature must be finite and nonnegative")
354
+ weight = self.weight if self.independent_temperature else self.weight * temperature
355
  if not math.isfinite(weight):
356
  raise ValueError("temperature times bigram weight must be finite")
357
  self.last_path_margins = None
 
403
  if repetition_penalty_strength:
404
  edges = edges - penalties
405
  decoder = self.sample_sequence if self.sequence_sampling else self.decode
406
+ if self.sequence_sampling and self.independent_temperature:
407
+ selected = decoder(candidates, emissions, edges, 1.0, temperature=temperature)
408
+ else:
409
+ selected = decoder(candidates, emissions, edges, 1.0)
410
  if self.remasking_confidence == "viterbi_margin":
411
  self.last_path_margins = self.path_margins(candidates, emissions, edges, selected, 1.0)
412
  elif self.remasking_confidence == "viterbi_margin":