Views
No views yet

pip install torch transformers accelerate[!NOTE] We recommend settingenable_thinking=Falsewhen using the model to ensure stable behavior and reproducible results.
1import math
2import copy
3
4import torch
5import torch.nn.functional as F
6from transformers import AutoTokenizer, AutoModelForMaskedLM
7
8
9def add_gumbel_noise(logits, temperature):
10 if temperature == 0:
11 return logits
12 logits = logits.to(torch.float64)
13 noise = torch.rand_like(logits, dtype=torch.float64)
14 g = (-torch.log(noise)) ** temperature
15 return logits.exp() / g
16
17
18def get_num_transfer_tokens(mask_index, steps):
19 mask_num = mask_index.sum(dim=1, keepdim=True)
20 base = mask_num // steps
21 rem = mask_num % steps
22 out = torch.zeros(mask_num.size(0), steps, device=mask_index.device, dtype=torch.long) + base
23 for i in range(mask_num.size(0)):
24 out[i, : rem[i]] += 1
25 return out
26
27
28def build_staircase_attention_mask(x, block_size, pad_id):
29 B, T = x.shape
30 device = x.device
31
32 valid = x != pad_id
33 pos_raw = torch.cumsum(valid.long(), dim=-1)
34 position_ids = torch.where(valid, pos_raw - 1, torch.zeros_like(pos_raw)).long()
35
36 col = torch.arange(T, device=device)
37 block_ids = (col // block_size).view(1, T).expand(B, T)
38 block_ids = torch.where(valid, block_ids, torch.full_like(block_ids, -1))
39
40 q = block_ids.view(B, 1, T, 1)
41 k = block_ids.view(B, 1, 1, T)
42 attn = (k <= q) & (q >= 0) & (k >= 0)
43
44 return attn, position_ids
45
46
47def diffusion_step_block(logits, x_block, mask_block, num_transfer, temperature, remasking):
48 B, L, _ = logits.shape
49 if not mask_block.any():
50 return x_block
51
52 noisy = add_gumbel_noise(logits, temperature)
53 x0 = noisy.argmax(dim=-1)
54
55 if remasking == "low_confidence":
56 p = F.softmax(logits, dim=-1)
57 conf = p.gather(-1, x0.unsqueeze(-1)).squeeze(-1)
58 elif remasking == "random":
59 conf = torch.rand((B, L), device=logits.device)
60 else:
61 raise ValueError(remasking)
62
63 x0 = torch.where(mask_block, x0, x_block)
64 neg_inf = torch.full_like(conf, -float("inf"))
65 conf = torch.where(mask_block, conf, neg_inf)
66
67 commit = torch.zeros_like(x_block, dtype=torch.bool)
68 for i in range(B):
69 k = int(num_transfer[i].item())
70 if k > 0:
71 valid = (conf[i] > -float("inf")).sum().item()
72 k = min(k, valid)
73 _, idx = torch.topk(conf[i], k)
74 commit[i, idx] = True
75
76 out = x_block.clone()
77 out[commit] = x0[commit]
78 return out
79
80
81@torch.no_grad()
82def generate(
83 model,
84 tokenizer,
85 prompt,
86 steps=128,
87 max_new_tokens=128,
88 block_size=32,
89 temperature=0.0,
90 cfg_scale=0.0,
91 remasking="low_confidence",
92):
93 device = model.device
94 mask_id = tokenizer.mask_token_id
95 pad_id = tokenizer.pad_token_id
96 if pad_id is None:
97 pad_id = tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.mask_token_id
98
99 if isinstance(prompt, torch.Tensor):
100 x = prompt.to(device).long()
101 else:
102 if isinstance(prompt[0], (list, tuple)):
103 max_len = max(len(p) for p in prompt)
104 x = torch.full((len(prompt), max_len), pad_id, device=device, dtype=torch.long)
105 for i, p in enumerate(prompt):
106 x[i, : len(p)] = torch.tensor(p, device=device)
107 else:
108 x = torch.tensor(prompt, device=device).long()
109 if x.dim() == 1:
110 x = x.unsqueeze(0)
111
112 B = x.size(0)
113 finished = torch.zeros(B, dtype=torch.bool, device=device)
114
115 num_blocks = math.ceil(max_new_tokens / block_size)
116 steps_per_block = math.ceil(steps / num_blocks)
117 generated = 0
118
119 while generated < max_new_tokens:
120 if finished.all():
121 break
122 T_prefix = x.size(1)
123 offset = T_prefix % block_size
124 room = block_size if offset == 0 else block_size - offset
125 cur_len = min(room, max_new_tokens - generated)
126 if cur_len <= 0:
127 break
128
129 attn_pfx, pos_pfx = build_staircase_attention_mask(x, block_size, pad_id)
130
131 out = model(x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
132 cond_past = out.past_key_values
133
134 if cfg_scale > 0:
135 un_x = x.clone()
136 un_x[:] = mask_id
137 out_un = model(un_x, attention_mask=attn_pfx, position_ids=pos_pfx, use_cache=True)
138 uncond_past = out_un.past_key_values
139 else:
140 uncond_past = None
141
142 block = torch.full((B, cur_len), mask_id, device=device, dtype=torch.long)
143 block[finished] = pad_id
144 x = torch.cat([x, block], dim=1)
145 T_total = x.size(1)
146
147 block_mask = x[:, -cur_len:] == mask_id
148 num_transfer = get_num_transfer_tokens(block_mask, steps_per_block)
149 eff_steps = num_transfer.size(1)
150
151 full_attn, full_pos = build_staircase_attention_mask(x, block_size, pad_id)
152 attn_blk = full_attn[:, :, T_prefix:T_total, :]
153 pos_blk = full_pos[:, T_prefix:T_total]
154
155 for t in range(eff_steps):
156 x_blk = x[:, T_prefix:T_total]
157 m_blk = x_blk == mask_id
158
159 cond_logits = model(
160 x_blk, attention_mask=attn_blk, position_ids=pos_blk,
161 past_key_values=copy.deepcopy(cond_past), use_cache=False
162 ).logits
163
164 logits = cond_logits
165 if cfg_scale > 0:
166 un_logits = model(
167 x_blk, attention_mask=attn_blk, position_ids=pos_blk,
168 past_key_values=copy.deepcopy(uncond_past), use_cache=False
169 ).logits
170 logits = un_logits + (cfg_scale + 1.0) * (cond_logits - un_logits)
171
172 x_blk_new = diffusion_step_block(
173 logits, x_blk, m_blk, num_transfer[:, t], temperature, remasking
174 )
175 x[:, T_prefix:T_total] = x_blk_new
176 if tokenizer.eos_token_id is not None:
177 finished |= (x_blk_new == tokenizer.eos_token_id).any(dim=1)
178 if finished.all():
179 break
180
181 generated += cur_len
182 if finished.all():
183 break
184
185 return x
186
187
188device = "cuda" if torch.cuda.is_available() else "cpu"
189model = AutoModelForMaskedLM.from_pretrained("dllm-hub/Qwen3-0.6B-diffusion-bd3lm-v0.1", dtype=torch.bfloat16, trust_remote_code=True).to(device).eval()
190tokenizer = AutoTokenizer.from_pretrained("dllm-hub/Qwen3-0.6B-diffusion-bd3lm-v0.1", trust_remote_code=True)
191
192prompts = [
193 [
194 {"role": "system", "content": "You are a helpful AI assistant."},
195 {"role": "user", "content": "Implement a DFS traversal in Python with clear inline comments."},
196 ],
197 [
198 {"role": "system", "content": "You are a helpful AI assistant."},
199 {"role": "user", "content": "Lily can run 12 kilometers per hour for 4 hours. After that, she runs 6 kilometers per hour. How many kilometers can she run in 10 hours?"},
200 ],
201]
202
203encoded = [tokenizer.apply_chat_template(m, add_generation_prompt=True, tokenize=True, enable_thinking=False) for m in prompts]
204prompt_lens = [len(e) for e in encoded]
205max_len = max(prompt_lens)
206pad_id = tokenizer.pad_token_id
207if pad_id is None:
208 pad_id = tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.mask_token_id
209input_ids = torch.full((len(encoded), max_len), pad_id, dtype=torch.long)
210for i, ids in enumerate(encoded):
211 input_ids[i, : len(ids)] = torch.tensor(ids, dtype=torch.long)
212input_ids = input_ids.to(device)
213
214max_new_tokens = 256
215text = generate(
216 model,
217 tokenizer,
218 input_ids,
219 steps=256,
220 max_new_tokens=max_new_tokens,
221 block_size=32,
222 temperature=0.0,
223 cfg_scale=0.0,
224 remasking="low_confidence",
225)
226
227new_tokens = [text[i, prompt_lens[i] : prompt_lens[i] + max_new_tokens].tolist() for i in range(len(prompt_lens))]
228for idx, decoded in enumerate(tokenizer.batch_decode(new_tokens, skip_special_tokens=False)):
229 print(f"
230[Sample {idx}]")
231 print(decoded)| Parameter | Description | Default |
|---|---|---|
max_new_tokens | Number of tokens to generate | 256 |
steps | Number of diffusion denoising iterations | 256 |
temperature | Sampling temperature; set to 0.0 for deterministic generation | 0.0 |
block_size | Token block size used during iterative denoising | 32 |
cfg_scale | Classifier-free guidance scale controlling instruction adherence (higher = more deterministic) | 0.0 |
remasking | Strategy for re-masking during each denoising step (random or low_confidence) | low_confidence |
1python -u examples/a2d/bd3lm/chat.py \
2 --model_name_or_path dllm-hub/Qwen3-0.6B-diffusion-bd3lm-v0.1 \
3 --chat_template True --block_size 32 --remasking low_confidence --steps 256 --max_new_tokens 256| Model | GSM8K | MATH | BBH | MMLU‑Pro | Hellaswag | MMLU | HumanEval | MBPP |
|---|---|---|---|---|---|---|---|---|
Qwen3-0.6B-diffusion-bd3lm-v0.1 (evaluated) | 46.6 | 13.9 | 27.0 | 14.1 | 40.0 | 38.8 | 47.6 | 32.0 |
Qwen3-0.6B-diffusion-mdlm-v0.1 (evaluated) | 29.8 | 8.8 | 27.0 | 17.6 | 42.1 | 40.0 | 30.5 | 29.2 |
Qwen3-0.6B-Base (reported) | 59.6 | 32.4 | 41.5 | 24.7 | 47.4 | 52.8 | 32.3 | 36.6 |
Qwen2.5-0.5B (reported) | 41.6 | 19.5 | 20.3 | 15.7 | 52.1 | 47.5 | 30.5 | 39.3 |
1bash examples/a2d/bd3lm/eval.sh \
2 --model_name_or_path dllm-hub/Qwen3-0.6B-diffusion-bd3lm-v0.11@misc{zhou2026dllm,
2 title={dLLM: Simple Diffusion Language Modeling},
3 author={Zhanhui Zhou and Lingjie Chen and Hanghang Tong and Dawn Song},
4 year={2026},
5 eprint={2602.22661},
6 archivePrefix={arXiv},
7 primaryClass={cs.CL},
8 url={https://arxiv.org/abs/2602.22661},
9}