Ruurd commited on
Commit
4f71bcc
·
verified ·
1 Parent(s): e2402e0

Deploy BYOD-Llama-3.1-8B full-precision demo

Browse files
Files changed (3) hide show
  1. app.py +35 -12
  2. src/diffusion_lm/.DS_Store +0 -0
  3. src/diffusion_lm/inference.py +38 -11
app.py CHANGED
@@ -4,6 +4,7 @@ from __future__ import annotations
4
  import json
5
  import os
6
  import sys
 
7
  from pathlib import Path
8
 
9
  import gradio as gr
@@ -35,9 +36,11 @@ def _duration(*args) -> int:
35
  """Reserve enough GPU time for the requested number of denoising steps."""
36
  try:
37
  steps = int(args[3])
 
38
  except (IndexError, TypeError, ValueError):
39
  steps = 64
40
- return min(300, max(30, steps * 2))
 
41
 
42
 
43
  @spaces.GPU(size="large", duration=_duration)
@@ -50,12 +53,13 @@ def generate(
50
  temperature: float,
51
  top_k: int,
52
  seed: int,
53
- show_trajectory: bool,
 
54
  ):
55
  """Stream iterative masked-diffusion generation from the fixed model."""
56
  question = question.strip() or "What do you know about Amsterdam?"
57
  block_length = min(int(block_length), int(max_new_tokens))
58
- latest = ("", "Starting…", "")
59
  for text, status, trajectory_html in denoise_stream(
60
  SESSION,
61
  question=question,
@@ -70,15 +74,21 @@ def generate(
70
  confidence_guided=True,
71
  proportional_unmask=False,
72
  early_stopping=False,
73
- confidence_eos_eot_inf=True,
 
 
74
  freeze_retained_tokens=True,
75
  repetition_penalty=1.0,
76
  eos_eot_prediction_penalty=1.0,
77
- include_pre_remask_prediction=show_trajectory,
78
  block_length=block_length,
79
  ):
80
- latest = (text, status, trajectory_html if show_trajectory else "")
81
- yield latest
 
 
 
 
82
 
83
 
84
  with gr.Blocks(title=f"{DISPLAY_NAME} · masked diffusion") as demo:
@@ -104,14 +114,26 @@ with gr.Blocks(title=f"{DISPLAY_NAME} · masked diffusion") as demo:
104
  value="You are a helpful assistant.",
105
  lines=2,
106
  )
107
- max_new_tokens = gr.Slider(16, 512, value=128, step=16, label="New tokens")
108
  num_steps = gr.Slider(1, 512, value=64, step=1, label="Denoising steps")
109
- block_length = gr.Slider(16, 512, value=128, step=16, label="Block length")
110
  temperature = gr.Slider(0.0, 2.0, value=0.7, step=0.05, label="Temperature")
111
  top_k = gr.Slider(1, 100, value=3, step=1, label="Top-k")
112
  seed = gr.Number(value=1234, precision=0, label="Seed")
113
- show_trajectory = gr.Checkbox(value=False, label="Show inference trajectory")
114
- trajectory = gr.HTML(label="Token trajectory", visible=True)
 
 
 
 
 
 
 
 
 
 
 
 
115
  gr.Markdown(
116
  "The first request may take longer while the base model and adapter are loaded. "
117
  f"[Model card](https://huggingface.co/{MODEL_REPO_ID}) · "
@@ -127,7 +149,8 @@ with gr.Blocks(title=f"{DISPLAY_NAME} · masked diffusion") as demo:
127
  temperature,
128
  top_k,
129
  seed,
130
- show_trajectory,
 
131
  ]
132
  run.click(generate, inputs=inputs, outputs=[output, status, trajectory])
133
  question.submit(generate, inputs=inputs, outputs=[output, status, trajectory])
 
4
  import json
5
  import os
6
  import sys
7
+ import time
8
  from pathlib import Path
9
 
10
  import gradio as gr
 
36
  """Reserve enough GPU time for the requested number of denoising steps."""
37
  try:
38
  steps = int(args[3])
39
+ pause_per_step = float(args[8])
40
  except (IndexError, TypeError, ValueError):
41
  steps = 64
42
+ pause_per_step = 0.0
43
+ return min(300, max(30, round(steps * (2.0 + pause_per_step))))
44
 
45
 
46
  @spaces.GPU(size="large", duration=_duration)
 
53
  temperature: float,
54
  top_k: int,
55
  seed: int,
56
+ pause_per_step: float,
57
+ delay_eos_eot: bool,
58
  ):
59
  """Stream iterative masked-diffusion generation from the fixed model."""
60
  question = question.strip() or "What do you know about Amsterdam?"
61
  block_length = min(int(block_length), int(max_new_tokens))
62
+ first_step = True
63
  for text, status, trajectory_html in denoise_stream(
64
  SESSION,
65
  question=question,
 
74
  confidence_guided=True,
75
  proportional_unmask=False,
76
  early_stopping=False,
77
+ # This delays retention of predicted endings; it does not alter their
78
+ # sampling probability. Keep it optional so answers can end naturally.
79
+ confidence_eos_eot_inf=bool(delay_eos_eot),
80
  freeze_retained_tokens=True,
81
  repetition_penalty=1.0,
82
  eos_eot_prediction_penalty=1.0,
83
+ include_pre_remask_prediction=False,
84
  block_length=block_length,
85
  ):
86
+ if not first_step and float(pause_per_step) > 0:
87
+ # The sleep occurs inside this one decorated generator invocation,
88
+ # so ZeroGPU remains allocated for the entire denoising run.
89
+ time.sleep(float(pause_per_step))
90
+ first_step = False
91
+ yield text, status, trajectory_html
92
 
93
 
94
  with gr.Blocks(title=f"{DISPLAY_NAME} · masked diffusion") as demo:
 
114
  value="You are a helpful assistant.",
115
  lines=2,
116
  )
117
+ max_new_tokens = gr.Slider(16, 512, value=64, step=16, label="New tokens")
118
  num_steps = gr.Slider(1, 512, value=64, step=1, label="Denoising steps")
119
+ block_length = gr.Slider(16, 512, value=64, step=16, label="Block length")
120
  temperature = gr.Slider(0.0, 2.0, value=0.7, step=0.05, label="Temperature")
121
  top_k = gr.Slider(1, 100, value=3, step=1, label="Top-k")
122
  seed = gr.Number(value=1234, precision=0, label="Seed")
123
+ pause_per_step = gr.Slider(
124
+ 0.0,
125
+ 1.0,
126
+ value=0.0,
127
+ step=0.05,
128
+ label="Pause between denoising steps (seconds)",
129
+ info="Slows the visualization while keeping one GPU allocation for the full run.",
130
+ )
131
+ delay_eos_eot = gr.Checkbox(
132
+ value=False,
133
+ label="Delay EOS/EOT retention (longer answers)",
134
+ info="Preferentially re-masks predicted endings; it does not lower their prediction probability.",
135
+ )
136
+ trajectory = gr.HTML(label="Live confidence-colored denoising")
137
  gr.Markdown(
138
  "The first request may take longer while the base model and adapter are loaded. "
139
  f"[Model card](https://huggingface.co/{MODEL_REPO_ID}) · "
 
149
  temperature,
150
  top_k,
151
  seed,
152
+ pause_per_step,
153
+ delay_eos_eot,
154
  ]
155
  run.click(generate, inputs=inputs, outputs=[output, status, trajectory])
156
  question.submit(generate, inputs=inputs, outputs=[output, status, trajectory])
src/diffusion_lm/.DS_Store CHANGED
Binary files a/src/diffusion_lm/.DS_Store and b/src/diffusion_lm/.DS_Store differ
 
src/diffusion_lm/inference.py CHANGED
@@ -402,7 +402,12 @@ def load_llada_session(repo_id: str = "GSAI-ML/LLaDA-8B-Instruct", device_name:
402
  return session
403
 
404
 
405
- def _sample(logits: torch.Tensor, temperature: float, top_k: int, generator: torch.Generator | None) -> tuple[torch.Tensor, torch.Tensor]:
 
 
 
 
 
406
  """Top-k sample token IDs and return their normalized sampling confidence."""
407
  logits = logits / max(temperature, 1e-5)
408
  vocab_size = logits.shape[-1]
@@ -412,7 +417,7 @@ def _sample(logits: torch.Tensor, temperature: float, top_k: int, generator: tor
412
  picked_local = torch.multinomial(probabilities, 1, generator=generator)
413
  picked = indices.gather(-1, picked_local).squeeze(-1)
414
  confidence = probabilities.gather(-1, picked_local).squeeze(-1)
415
- return picked, confidence
416
 
417
 
418
  def _llada_gumbel_noise(logits: torch.Tensor, temperature: float) -> torch.Tensor:
@@ -800,22 +805,31 @@ def render_denoising_step(tokens: list[int], confidences: list[float], answer_st
800
  output_token_count += 1
801
  token_text = escape(tokenizer.decode([token], skip_special_tokens=False)).replace("\n", "↵ ")
802
  if token == mask_token_id:
803
- style, token_text = "background:#d1d5db;color:#111827;border-radius:3px;padding:1px 4px", "MASK"
804
- elif frozen and offset in frozen:
805
- style = "color:#1d4ed8;font-weight:700"
806
- elif retained and offset in retained:
807
- style = "color:#7c3aed;font-weight:700"
 
 
808
  else:
809
  confidence = max(0.0, min(1.0, float(confidences[offset]))) if offset < len(confidences) else 0.0
810
  hue = int(confidence * 120)
811
  style = f"color:hsl({hue},90%,30%);font-weight:{'600' if confidence > .8 else '400'}"
812
- pieces.append(f"<span style='{style}' title='position {offset}'>{token_text}</span>")
 
 
 
 
 
 
 
813
  pct = int(100 * step / max(total_steps, 1))
814
  return (f"<div style='font-family:system-ui;padding:14px;border:1px solid #d1d5db;border-radius:9px;background:#fafafa'>"
815
  f"<div style='font-weight:700;color:#2563eb;margin-bottom:7px'>Denoising step {step}/{total_steps} · {output_token_count} output tokens</div>"
816
  f"<div style='background:#e5e7eb;border-radius:4px;height:7px;margin-bottom:10px'><div style='background:#2563eb;width:{pct}%;height:7px;border-radius:4px'></div></div>"
817
  f"<div style='line-height:2;font-size:15px;white-space:pre-wrap'>{''.join(pieces)}</div>"
818
- f"<div style='font-size:11px;color:#6b7280;margin-top:8px'>Green hues indicate confidence; gray tokens are MASK; purple tokens are retained but editable; blue tokens are retained and locked.</div></div>")
819
 
820
 
821
  def decode_denoising_state(
@@ -916,7 +930,12 @@ def denoise_stream(session: InferenceSession, question: str, system_prompt: str,
916
  session.tokenizer.eos_token_id,
917
  eot_token_id,
918
  )
919
- sampled, confidence = _sample(logits, float(temperature), int(top_k), None)
 
 
 
 
 
920
  retention_confidence = confidence
921
  if confidence_eos_eot_inf:
922
  special_prediction = sampled == session.tokenizer.eos_token_id
@@ -929,6 +948,14 @@ def denoise_stream(session: InferenceSession, question: str, system_prompt: str,
929
  if freeze_retained_tokens:
930
  for offset, token in frozen.items():
931
  ids[answer_start + offset] = token
 
 
 
 
 
 
 
 
932
  predicted_text = decode_denoising_state(
933
  ids[answer_start:],
934
  session.tokenizer,
@@ -1032,7 +1059,7 @@ def denoise_stream(session: InferenceSession, question: str, system_prompt: str,
1032
  status += " · block stopped early (same prediction for 3 iterations)"
1033
  html = render_denoising_step(
1034
  ids,
1035
- confidence.tolist(),
1036
  answer_start,
1037
  session.tokenizer,
1038
  session.mask_token_id,
 
402
  return session
403
 
404
 
405
+ def _sample(
406
+ logits: torch.Tensor,
407
+ temperature: float,
408
+ top_k: int,
409
+ generator: torch.Generator | None,
410
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
411
  """Top-k sample token IDs and return their normalized sampling confidence."""
412
  logits = logits / max(temperature, 1e-5)
413
  vocab_size = logits.shape[-1]
 
417
  picked_local = torch.multinomial(probabilities, 1, generator=generator)
418
  picked = indices.gather(-1, picked_local).squeeze(-1)
419
  confidence = probabilities.gather(-1, picked_local).squeeze(-1)
420
+ return picked, confidence, indices, probabilities
421
 
422
 
423
  def _llada_gumbel_noise(logits: torch.Tensor, temperature: float) -> torch.Tensor:
 
805
  output_token_count += 1
806
  token_text = escape(tokenizer.decode([token], skip_special_tokens=False)).replace("\n", "↵ ")
807
  if token == mask_token_id:
808
+ style, token_text = (
809
+ "display:inline-block;background:#d1d5db;color:#4b5563;"
810
+ "border:1px solid #9ca3af;border-radius:4px;padding:0 4px;"
811
+ "font-size:.78em;line-height:1.45;margin:0 1px;vertical-align:baseline",
812
+ "mask",
813
+ )
814
+ title = f"position {offset} · masked"
815
  else:
816
  confidence = max(0.0, min(1.0, float(confidences[offset]))) if offset < len(confidences) else 0.0
817
  hue = int(confidence * 120)
818
  style = f"color:hsl({hue},90%,30%);font-weight:{'600' if confidence > .8 else '400'}"
819
+ title = f"position {offset} · sampling probability {confidence:.1%}"
820
+ if frozen and offset in frozen:
821
+ style += ";border-bottom:2px solid #2563eb"
822
+ title += " · retained and locked"
823
+ elif retained and offset in retained:
824
+ style += ";border-bottom:2px solid #7c3aed"
825
+ title += " · retained and editable"
826
+ pieces.append(f"<span style='{style}' title='{title}'>{token_text}</span>")
827
  pct = int(100 * step / max(total_steps, 1))
828
  return (f"<div style='font-family:system-ui;padding:14px;border:1px solid #d1d5db;border-radius:9px;background:#fafafa'>"
829
  f"<div style='font-weight:700;color:#2563eb;margin-bottom:7px'>Denoising step {step}/{total_steps} · {output_token_count} output tokens</div>"
830
  f"<div style='background:#e5e7eb;border-radius:4px;height:7px;margin-bottom:10px'><div style='background:#2563eb;width:{pct}%;height:7px;border-radius:4px'></div></div>"
831
  f"<div style='line-height:2;font-size:15px;white-space:pre-wrap'>{''.join(pieces)}</div>"
832
+ f"<div style='font-size:11px;color:#6b7280;margin-top:8px'>Red-to-green text indicates lower-to-higher sampling probability; gray chips are masks; purple underlines indicate retained/editable tokens; blue underlines indicate retained/locked tokens. Hover for details.</div></div>")
833
 
834
 
835
  def decode_denoising_state(
 
930
  session.tokenizer.eos_token_id,
931
  eot_token_id,
932
  )
933
+ sampled, confidence, candidate_ids, candidate_probabilities = _sample(
934
+ logits,
935
+ float(temperature),
936
+ int(top_k),
937
+ None,
938
+ )
939
  retention_confidence = confidence
940
  if confidence_eos_eot_inf:
941
  special_prediction = sampled == session.tokenizer.eos_token_id
 
948
  if freeze_retained_tokens:
949
  for offset, token in frozen.items():
950
  ids[answer_start + offset] = token
951
+ display_confidence = confidence.clone()
952
+ if freeze_retained_tokens:
953
+ # The sampled token may be overwritten by a previously locked one.
954
+ # Report the probability of the token actually displayed, rather
955
+ # than the confidence of the discarded sample.
956
+ for offset, token in frozen.items():
957
+ matches = candidate_ids[offset] == int(token)
958
+ display_confidence[offset] = candidate_probabilities[offset][matches].sum()
959
  predicted_text = decode_denoising_state(
960
  ids[answer_start:],
961
  session.tokenizer,
 
1059
  status += " · block stopped early (same prediction for 3 iterations)"
1060
  html = render_denoising_step(
1061
  ids,
1062
+ display_confidence.tolist(),
1063
  answer_start,
1064
  session.tokenizer,
1065
  session.mask_token_id,