Spaces:
Running on Zero
Running on Zero
Deploy BYOD-Llama-3.1-8B full-precision demo
Browse files- app.py +35 -12
- src/diffusion_lm/.DS_Store +0 -0
- 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 |
-
|
|
|
|
| 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 |
-
|
|
|
|
| 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 |
-
|
| 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 |
-
|
|
|
|
|
|
|
| 74 |
freeze_retained_tokens=True,
|
| 75 |
repetition_penalty=1.0,
|
| 76 |
eos_eot_prediction_penalty=1.0,
|
| 77 |
-
include_pre_remask_prediction=
|
| 78 |
block_length=block_length,
|
| 79 |
):
|
| 80 |
-
|
| 81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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=
|
| 108 |
num_steps = gr.Slider(1, 512, value=64, step=1, label="Denoising steps")
|
| 109 |
-
block_length = gr.Slider(16, 512, value=
|
| 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 |
-
|
| 114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
| 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(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 =
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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'>
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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,
|