1import torch
2import numpy as np
3from torch import nn
4import torch.nn.functional as F
5from tqdm import tqdm
6import pandas as pd
7import os
8import random
9import copy
10import math
11from transformers import AutoTokenizer, AutoModel, AutoModelForCausalLM, AutoConfig
12
13from typing import Optional, Dict, Any, Tuple, List
14
15def add_gumbel_noise(logits, temperature):
16 '''
17 The Gumbel max is a method for sampling categorical distributions.
18 According to arXiv:2409.02908, for MDM, low-precision Gumbel Max improves perplexity score but reduces generation quality.
19 Thus, we use float64.
20 '''
21 if temperature == 0:
22 return logits
23 logits = logits.to(torch.float64)
24 noise = torch.rand_like(logits, dtype=torch.float64)
25 gumbel_noise = (- torch.log(noise)) ** temperature
26 return logits.exp() / gumbel_noise
27
28
29import torch
30import torch.nn.functional as F
31
32
33@ torch.no_grad()
34def generate_refusion(model, tokenizer, prompt, gen_length=128, temperature=0., mask_id=151670, slot_size=8,
35 model_path='', serial_num_blocks=2, slot_threshold=0.9, token_threshold=0.9):
36
37 slot_threshold = slot_threshold
38 token_threshold = token_threshold
39 sum_TPF = 0.0
40 forward_count = 0
41
42 eos_token_id = tokenizer.eos_token_id
43 batch_size = 1
44 prompt_len = prompt.shape[1]
45 device = model.device
46
47 gen_pad_len = (slot_size - (gen_length % slot_size)) % slot_size
48 gen_length = gen_length + gen_pad_len
49 gen_x = torch.full((batch_size, gen_length), mask_id, dtype=torch.long, device=device)
50
51 prompt_pos_ids = torch.arange(prompt_len, dtype=torch.long, device=device).unsqueeze(0)
52 gen_pos_ids = torch.arange(prompt_len, prompt_len + gen_length, dtype=torch.long, device=device).unsqueeze(0)
53
54 cur_x = prompt.clone()
55 cur_pos = prompt_pos_ids.clone()
56
57 cur_slot_size = slot_size
58
59 eos_flag = False
60 block_length = gen_length // serial_num_blocks
61
62 past_key_values = None
63
64
65 for serial_num_block in range(serial_num_blocks):
66
67 # block level
68 cur_gen_x = gen_x[:, serial_num_block*block_length:(serial_num_block+1)*block_length] # (batch_size, block_length)
69 cur_gen_pos_ids = gen_pos_ids[:, serial_num_block*block_length:(serial_num_block+1)*block_length] # (batch_size, block_length)
70
71 cur_gen_blocks_x = cur_gen_x.reshape(batch_size, -1, cur_slot_size)
72 cur_gen_blocks_pos_ids = cur_gen_pos_ids.reshape(batch_size, -1, cur_slot_size)
73
74 # slot level generation
75 while cur_gen_blocks_x.numel() > 0:
76 cur_gen_blocks_x = cur_gen_blocks_x.reshape(batch_size, -1, cur_slot_size)
77 cur_gen_blocks_pos_ids = cur_gen_blocks_pos_ids.reshape(batch_size, -1, cur_slot_size)
78
79 flat_gen_blocks_x = cur_gen_blocks_x.view(batch_size, -1)
80 flat_gen_blocks_pos_ids = cur_gen_blocks_pos_ids.view(batch_size, -1)
81
82 prefix_block_tag = False
83
84 # MDM
85 if past_key_values is None:
86 input_x = torch.cat((cur_x, flat_gen_blocks_x), dim=1)
87 input_pos_ids = torch.cat((cur_pos, flat_gen_blocks_pos_ids), dim=1)
88 outputs = model(
89 input_ids=input_x,
90 position_ids=input_pos_ids,
91 past_key_values=past_key_values,
92 use_cache=True
93 )
94 else:
95 outputs = model(
96 input_ids=flat_gen_blocks_x,
97 position_ids=flat_gen_blocks_pos_ids,
98 past_key_values=past_key_values,
99 use_cache=True
100 )
101
102 logits = outputs.logits
103
104 gen_logits = logits[:, -flat_gen_blocks_x.shape[1]:, :]
105
106 past_key_values = outputs.past_key_values
107 past_key_values.crop(cur_x.shape[1])
108 assert cur_x.shape[-1] == past_key_values[0][0].shape[-2]
109
110 logits_with_noise = add_gumbel_noise(gen_logits, temperature=temperature)
111 x0_gen = torch.argmax(logits_with_noise, dim=-1)
112 x0_gen_blocks = x0_gen.view(batch_size, -1, cur_slot_size)
113
114 p_softmax = F.softmax(gen_logits, dim=-1)
115 x0_p_softmax = torch.gather(p_softmax, dim=-1, index=torch.unsqueeze(x0_gen, -1)).squeeze(-1)
116
117 x0_p_softmax_blocks = x0_p_softmax.view(batch_size, -1, cur_slot_size)
118 block_confidence_softmax = x0_p_softmax_blocks[:,:,0] # (bsz, num_slots)
119
120 is_confident_block = block_confidence_softmax > slot_threshold
121 counts_block = torch.sum(is_confident_block, dim=1).item()
122 topk_indices_relative = is_confident_block[0].nonzero(as_tuple=True)[0]
123
124 if counts_block <= 0:
125 counts_block = 1
126 _, topk_indices_relative = torch.topk(block_confidence_softmax.squeeze(0), k=1)
127
128 # choose slot
129 topk_indices_relative, _ = torch.sort(topk_indices_relative)
130
131 chosen_gen_blocks = x0_gen_blocks[0, topk_indices_relative, :]
132 chosen_position_ids = cur_gen_blocks_pos_ids[0, topk_indices_relative, :]
133 chosen_p_softmax_blocks = x0_p_softmax_blocks[0, topk_indices_relative, :]
134
135
136 # Global Verification
137 outputs = model(
138 input_ids=chosen_gen_blocks.reshape(1, -1),
139 position_ids=chosen_position_ids.reshape(1, -1),
140 past_key_values=past_key_values,
141 use_cache=True,
142 )
143
144 AR_logits = outputs.logits #[1, len, vocab_len]
145 AR_logits = torch.cat([AR_logits[:,:1], AR_logits[:, :-1]], dim=1)
146 AR_p_softmax = F.softmax(AR_logits, dim=-1) #[1, len, 1]
147 AR_x0_p_softmax = torch.gather(AR_p_softmax, dim=-1, index=torch.unsqueeze(chosen_gen_blocks.reshape(1, -1), -1)).squeeze(-1) #[1, len]
148 AR_x0_p_softmax_blocks = AR_x0_p_softmax.reshape(-1, cur_slot_size)
149 chosen_p_softmax_blocks[:,1:] = AR_x0_p_softmax_blocks[:,1:]
150
151
152 prob_mask = chosen_p_softmax_blocks > token_threshold
153 prob_mask[:, 0] = 1
154 tag_blocks = torch.cumprod(prob_mask.int(), dim=-1)
155
156 tag_tokens = torch.cumprod(prob_mask.int().reshape(1, -1), dim=-1)
157 prefix_len = torch.sum(tag_tokens, dim=-1)
158 flat_chosen_gen_blocks = chosen_gen_blocks.reshape(1, -1)
159 confident_prefix_tokens = flat_chosen_gen_blocks[:, :prefix_len]
160
161 if prefix_len > 0:
162 is_eos_in_prefix = (confident_prefix_tokens.squeeze(0) == eos_token_id)
163 eos_found_flag = torch.any(is_eos_in_prefix)
164
165 remain_indices = []
166
167 indices_to_remove = set()
168
169 if eos_found_flag:
170 first_eos_pos_tensor = torch.argmax(is_eos_in_prefix.int())
171
172 eos_block_pos = first_eos_pos_tensor // cur_slot_size + 1
173 eos_token_pos = first_eos_pos_tensor - (first_eos_pos_tensor // cur_slot_size) * cur_slot_size
174
175 eos_block = topk_indices_relative[eos_block_pos-1].item()
176
177 remain_indices.extend(topk_indices_relative[:eos_block_pos].tolist())
178
179 topk_indices_relative = torch.tensor([], device=device)
180
181 eos_flag = True
182
183 indices_after_eos = list(range(eos_block, cur_gen_blocks_x.shape[1]))
184 indices_to_remove.update(indices_after_eos)
185
186 elif (prefix_len // cur_slot_size) > 0:
187 num_prefix_blocks = prefix_len // cur_slot_size
188 remain_indices.extend(topk_indices_relative[:num_prefix_blocks].tolist())
189
190 topk_indices_relative = topk_indices_relative[num_prefix_blocks:]
191 tag_blocks = tag_blocks[num_prefix_blocks:]
192
193 if len(remain_indices) > 0:
194
195 indices_to_remove.update(remain_indices)
196
197 token_indices = []
198
199 for i_idx, b_idx in enumerate(remain_indices):
200 start_index = b_idx * cur_slot_size
201
202 current_block_len = cur_slot_size
203 # If EOS exists and this is the last slot, then adjust the length.
204 if eos_found_flag and i_idx == len(remain_indices) - 1:
205 current_block_len = eos_token_pos + 1
206
207
208 end_index = start_index + current_block_len
209 block_range = torch.arange(start_index, end_index, dtype=torch.long, device=device)
210
211 token_indices.append(block_range)
212
213 full_token_indices = torch.cat(token_indices)
214
215 cur_x = torch.cat((cur_x, x0_gen[:, full_token_indices]), dim=1)
216 cur_pos = torch.cat((cur_pos, flat_gen_blocks_pos_ids[:, full_token_indices]), dim=1)
217
218 past_key_values = outputs.past_key_values
219 past_key_values.crop(cur_x.shape[1])
220
221 assert cur_x.shape[-1] == past_key_values[0][0].shape[-2]
222
223 prefix_block_tag = True
224
225 sum_TPF += cur_slot_size * len(remain_indices) / 2
226 forward_count += 1
227
228 if prefix_block_tag == True:
229 keep_mask = torch.ones(cur_gen_blocks_x.shape[1], dtype=torch.bool, device=device)
230 keep_mask[list(indices_to_remove)] = False
231 cur_gen_blocks_x = cur_gen_blocks_x[:, keep_mask, :]
232 cur_gen_blocks_pos_ids = cur_gen_blocks_pos_ids[:, keep_mask, :]
233
234 continue
235
236 elif prefix_block_tag == False:
237 past_key_values = outputs.past_key_values
238 past_key_values.crop(cur_x.shape[1])
239 assert cur_x.shape[-1] == past_key_values[0][0].shape[-2]
240
241 indices_to_remove = set(topk_indices_relative.tolist())
242
243 current_speculative_blocks = chosen_gen_blocks.clone()
244 accepted_prefix_len = 0
245 eos_found_in_loop = False
246
247 if past_key_values is not None and counts_block > 1:
248 past_key_values.batch_repeat_interleave(counts_block)
249
250 for loop_iter in range(cur_slot_size):
251 if not torch.any(tag_blocks == 0):
252 break
253
254 input_tokens = current_speculative_blocks[:, accepted_prefix_len:]
255 input_pos = chosen_position_ids[:, accepted_prefix_len:]
256
257 current_tags = tag_blocks[:, accepted_prefix_len:]
258 masked_input_tokens = torch.where(current_tags.bool(), input_tokens, mask_id)
259
260 # Prediction
261 draft_len = past_key_values[0][0].shape[2]
262 draft_outputs = model(
263 input_ids=masked_input_tokens,
264 position_ids=input_pos,
265 past_key_values=past_key_values,
266 use_cache=False,
267 )
268 past_key_values.crop(draft_len)
269 draft_logits = draft_outputs.logits
270 proposed_tokens = torch.argmax(draft_logits, dim=-1)
271
272 input_tokens = torch.where(current_tags.bool(), input_tokens, proposed_tokens)
273 current_speculative_blocks[:, accepted_prefix_len:] = input_tokens
274
275 # Verification
276 verify_outputs = model(
277 input_ids=input_tokens,
278 position_ids=input_pos,
279 past_key_values=past_key_values,
280 use_cache=True,
281 )
282 verify_logits = verify_outputs.logits
283 verify_logits = torch.cat([verify_logits[:,:1], verify_logits[:, :-1]], dim=1)
284
285 verify_probs = F.softmax(verify_logits, dim=-1)
286 gathered_probs = torch.gather(verify_probs, -1, input_tokens.unsqueeze(-1)).squeeze(-1)
287
288 prob_mask = gathered_probs > token_threshold
289
290 # Keep at least one token
291 update_tag_blocks = F.pad(tag_blocks[:, accepted_prefix_len:], (1, 0), value=1)[:, :-1]
292
293 prob_mask[update_tag_blocks == 1] = True
294
295 new_tags = torch.cumprod(prob_mask.int(), dim=-1)
296 tag_blocks[:, accepted_prefix_len:] = new_tags
297
298 newly_verified_mask = (tag_blocks[:, accepted_prefix_len:] == 1)
299 is_eos_in_new = (current_speculative_blocks[:, accepted_prefix_len:] == eos_token_id) & newly_verified_mask
300
301 if torch.any(is_eos_in_new):
302 eos_found_in_loop = True
303 first_eos_block_idx = torch.where(torch.any(is_eos_in_new, dim=1))[0][0].item()
304
305 current_speculative_blocks = current_speculative_blocks[:first_eos_block_idx+1]
306 tag_blocks = tag_blocks[:first_eos_block_idx+1]
307 tag_blocks[first_eos_block_idx] = 1
308 chosen_position_ids = chosen_position_ids[:first_eos_block_idx+1]
309 topk_indices_relative = topk_indices_relative[:first_eos_block_idx+1]
310 if verify_outputs.past_key_values is not None:
311 verify_outputs.past_key_values.batch_select_minibatch(first_eos_block_idx + 1)
312
313 current_tags = tag_blocks[:, accepted_prefix_len:]
314 len_per_block = torch.sum(current_tags, dim=1)
315 newly_accepted_len = torch.min(len_per_block).item()
316 if newly_accepted_len > 0:
317 if torch.any(tag_blocks == 0):
318 accepted_prefix_len = accepted_prefix_len + newly_accepted_len - 1
319 else:
320 accepted_prefix_len = accepted_prefix_len + newly_accepted_len
321 past_key_values = verify_outputs.past_key_values
322 if past_key_values is not None:
323 past_key_values.crop(cur_x.shape[1] + accepted_prefix_len)
324
325 sum_TPF += (cur_slot_size * counts_block) / (loop_iter * 2 + 2)
326 forward_count += 1
327
328 ar_kv_cache = tuple(
329 (
330 layer_past[0][:, :, -cur_slot_size:, :], # key
331 layer_past[1][:, :, -cur_slot_size:, :] # value
332 )
333 for layer_past in past_key_values
334 )
335
336
337 past_key_values.crop(cur_x.shape[1])
338 past_key_values.batch_select_indices(torch.tensor([0]).to(device))
339
340 eos_mask = (current_speculative_blocks == eos_token_id) # (k*cur_slot_size)
341 keep_mask = (torch.cumsum(eos_mask.flatten().int(), dim=-1) - eos_mask.flatten().int()) == 0
342 kept_tokens = current_speculative_blocks.flatten()[keep_mask].reshape(batch_size, -1)
343 kept_pos_ids = chosen_position_ids.flatten()[keep_mask].reshape(batch_size, -1)
344
345 # update KV cache
346 if kept_tokens.numel() > 0 and ar_kv_cache is not None:
347 new_past = []
348 for i, (key, val) in enumerate(ar_kv_cache):
349 num_heads, _, head_dim = key.shape[1], key.shape[2], key.shape[3]
350
351 flat_key = key.permute(1, 0, 2, 3).reshape(1, num_heads, -1, head_dim)
352 flat_val = val.permute(1, 0, 2, 3).reshape(1, num_heads, -1, head_dim)
353
354 kept_key = flat_key[:, :, keep_mask, :]
355 kept_val = flat_val[:, :, keep_mask, :]
356
357 new_past.append((kept_key, kept_val))
358
359 kept_kv = tuple(new_past)
360
361 past_key_values.full_update(kept_kv)
362
363 cur_x = torch.cat((cur_x, kept_tokens), dim=1)
364 cur_pos = torch.cat((cur_pos, kept_pos_ids), dim=1)
365
366 assert cur_x.shape[-1] == past_key_values[0][0].shape[-2]
367
368 if eos_found_in_loop:
369 indices_after_eos = list(range(first_eos_block_idx, cur_gen_blocks_x.shape[1]))
370 indices_to_remove.update(indices_after_eos)
371 eos_flag = True
372
373 keep_mask = torch.ones(cur_gen_blocks_x.shape[1], dtype=torch.bool, device=device)
374 keep_mask[list(indices_to_remove)] = False
375 cur_gen_blocks_x = cur_gen_blocks_x[:, keep_mask, :]
376 cur_gen_blocks_pos_ids = cur_gen_blocks_pos_ids[:, keep_mask, :]
377
378 if eos_flag:
379 break
380
381 _, re_mask_indices = torch.sort(cur_pos, dim=-1)
382 x = torch.gather(cur_x, dim=-1, index=re_mask_indices)
383
384 TPF = sum_TPF / forward_count
385
386 return x, TPF
387
388
389
390def main():
391 device = 'cuda'
392
393 model_path = "ReFusion"
394 model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True, torch_dtype=torch.bfloat16).to(device).eval()
395 tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
396
397 prompt = "You are an expert Python programmer. Your task is to write a single Python function to solve the problem described below, and here is your task: Write a function to sum all amicable numbers from 1 to a specified number.\n\nDirectly after the '[BEGIN]' marker, you must write only the Python code for the function. Do not provide any explanations, comments, or introductory text. The function must include the 'def' line, its arguments, the function body, and a 'return' statement. Your code should pass these tests:\n\nassert amicable_numbers_sum(999)==504\nassert amicable_numbers_sum(9999)==31626\nassert amicable_numbers_sum(99)==0\n[BEGIN]\n"
398
399 m = [{"role": "user", "content": prompt}, ]
400 prompt = tokenizer.apply_chat_template(m, add_generation_prompt=True, tokenize=False, enable_thinking=True)
401
402 print(prompt)
403
404 input_ids = tokenizer(prompt)['input_ids']
405 input_ids = torch.tensor(input_ids).to(device).unsqueeze(0)
406
407 out, TPF = generate_refusion(model, tokenizer, input_ids, gen_length=512, temperature=0., mask_id=151670, slot_size=4, model_path=model_path, serial_num_blocks=32, slot_threshold=0.6, token_threshold=0.3)
408 print(tokenizer.batch_decode(out[:, input_ids.shape[1]:], skip_special_tokens=True)[0])
409 print("---------TPF:", TPF)
410
411
412if __name__ == '__main__':
413 main()